Skip to content

Commit

Permalink
test: add integration tests
Browse files Browse the repository at this point in the history
Signed-off-by: 0xRaccoon <[email protected]>
  • Loading branch information
0xRaccoon committed Dec 26, 2023
1 parent 3856d2d commit fbeccaf
Show file tree
Hide file tree
Showing 12 changed files with 361 additions and 18 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ GOERLI_RPC=
GOERLI_DEPLOYER_PK=

ETHERSCAN_API_KEY=

OPTIMISM_RPC=
3 changes: 2 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ src = 'solidity/interfaces/'
runs = 1000

[rpc_endpoints]
mainnet = "${MAINNET_RPC}"
mainnet = "${MAINNET_RPC}"
optimism = "${OPTIMISM_RPC}"
1 change: 1 addition & 0 deletions solidity/examples/AliceGovernor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ contract AliceGovernor is WonderGovernor {
uint256 _weight,
bytes memory _params
) internal virtual override {
proposalTracks[_proposalId].votes += _weight;
if (_support == 0) {
proposalTracks[_proposalId].againstVotes += _weight;
} else if (_support == 1) {
Expand Down
152 changes: 152 additions & 0 deletions solidity/test/integration/Delegation.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import './IntegrationBase.t.sol';

Check warning on line 4 in solidity/test/integration/Delegation.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

global import of path ./IntegrationBase.t.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)

Check warning on line 4 in solidity/test/integration/Delegation.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

Import './IntegrationBase.t.sol' in contract Integration_Delegation should be declared as import {contract_to_import} from './IntegrationBase.t.sol';

import {WonderVotes} from 'contracts/governance/utils/WonderVotes.sol';

Check warning on line 6 in solidity/test/integration/Delegation.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

imported name WonderVotes is not used
import {WonderGovernor} from 'contracts/governance/WonderGovernor.sol';

Check warning on line 7 in solidity/test/integration/Delegation.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

imported name WonderGovernor is not used

contract Integration_Delegation is IntegrationBase {

Check warning on line 9 in solidity/test/integration/Delegation.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

Contract name must be in CamelCase
function test_AllVotersDelegateToProposer() public {
// AllVoters delegates to proposer
for (uint256 _i = 0; _i < VOTERS_NUMBER; _i++) {
address holder = holders[_i];

Check warning on line 13 in solidity/test/integration/Delegation.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

'holder' should start with _
vm.prank(holder);
rabbitToken.delegate(proposer);
}

for (uint256 _i = 0; _i < VOTERS_NUMBER; _i++) {
address holder = holders[_i];

Check warning on line 19 in solidity/test/integration/Delegation.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

'holder' should start with _

for (uint256 _j = 0; _j < governor.proposalTypes().length; _j++) {
uint8 proposalType = governor.proposalTypes()[_j];

Check warning on line 22 in solidity/test/integration/Delegation.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

'proposalType' should start with _
assertEq(rabbitToken.getVotes(holder, proposalType), 0);
}
}

for (uint256 _j = 0; _j < governor.proposalTypes().length; _j++) {
uint8 proposalType = governor.proposalTypes()[_j];

Check warning on line 28 in solidity/test/integration/Delegation.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

'proposalType' should start with _
assertEq(rabbitToken.getVotes(proposer, proposalType), INITIAL_VOTERS_BALANCE * VOTERS_NUMBER);
}
}

function test_AllVotersDelegateByProposalType() public {
uint8[] memory proposalTypes = governor.proposalTypes();

Check warning on line 34 in solidity/test/integration/Delegation.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

'proposalTypes' should start with _

for (uint256 _i = 0; _i < proposalTypes.length; _i++) {
address _holder = holders[_i];
vm.prank(_holder);
rabbitToken.delegate(proposer, proposalTypes[_i]);
}

for (uint256 _i = 0; _i < proposalTypes.length; _i++) {
address _holder = holders[_i];
assertEq(rabbitToken.getVotes(_holder, proposalTypes[_i]), 0);
assertEq(rabbitToken.getVotes(proposer, proposalTypes[_i]), INITIAL_VOTERS_BALANCE);
}
}

function test_AllVotersDelegatePartially() public {
uint8[] memory proposalTypes = governor.proposalTypes();

// 50% of votes
uint256 _weight = rabbitToken.weightNormalizer() / 2;

IWonderVotes.Delegate memory _delegate = IWonderVotes.Delegate({account: proposer, weight: _weight});
IWonderVotes.Delegate memory _delegate2 = IWonderVotes.Delegate({account: proposer2, weight: _weight});

IWonderVotes.Delegate[] memory _delegates = new IWonderVotes.Delegate[](2);
_delegates[0] = _delegate;
_delegates[1] = _delegate2;

for (uint256 _i = 0; _i < proposalTypes.length; _i++) {
for (uint256 _j = 0; _j < VOTERS_NUMBER; _j++) {
address _holder = holders[_j];
vm.prank(_holder);
rabbitToken.delegate(_delegates, proposalTypes[_i]);
}
}

for (uint256 _i = 0; _i < proposalTypes.length; _i++) {
assertEq(rabbitToken.getVotes(proposer, proposalTypes[_i]), INITIAL_VOTERS_BALANCE * VOTERS_NUMBER / 2);
assertEq(rabbitToken.getVotes(proposer2, proposalTypes[_i]), INITIAL_VOTERS_BALANCE * VOTERS_NUMBER / 2);

for (uint256 _j = 0; _j < VOTERS_NUMBER; _j++) {
address _holder = holders[_j];
assertEq(rabbitToken.getVotes(_holder, proposalTypes[_i]), 0);
}
}
}

function test_ProposeWithDelegatedVotes() public {
address _voter1 = holders[0];

vm.prank(proposer);
rabbitToken.delegate(proposer);

// delegate to proposer
vm.prank(_voter1);
rabbitToken.delegate(proposer);

address[] memory _targets = new address[](1);
_targets[0] = address(governor);

uint256[] memory _values = new uint256[](1);
_values[0] = 1;

bytes[] memory _calldatas = new bytes[](1);
_calldatas[0] = abi.encode(0);

string memory _description = 'test proposal';

// To propose Governor controls the proposal threshold calling getPastVotes, so we need to mine a block to be able to propose
_mineBlock();

uint8[] memory _proposalTypes = governor.proposalTypes();

vm.startPrank(proposer);
for (uint256 _i = 0; _i < _proposalTypes.length; _i++) {
uint8 _proposalType = _proposalTypes[_i];

uint256 _precomputedProposalId =
governor.hashProposal(_proposalType, _targets, _values, _calldatas, keccak256(bytes(_description)));
_expectEmit(address(governor));

emit ProposalCreated(
_precomputedProposalId,
_proposalType,
address(proposer),
_targets,
_values,
new string[](1),
_calldatas,
block.number + 1,
block.number + governor.votingPeriod() + 1,
_description
);
governor.propose(_proposalType, _targets, _values, _calldatas, _description);
}
vm.stopPrank();
}

function test_AllVotersChangeDelegation() public {
uint8[] memory _proposalTypes = governor.proposalTypes();

for (uint256 _i = 0; _i < VOTERS_NUMBER; _i++) {
address _holder = holders[_i];
vm.prank(_holder);
rabbitToken.delegate(proposer);
}

for (uint256 _i = 0; _i < VOTERS_NUMBER; _i++) {
address _holder = holders[_i];
vm.prank(_holder);
rabbitToken.delegate(proposer2);
}

for (uint8 i = 0; i < _proposalTypes.length; i++) {
assertEq(rabbitToken.getVotes(proposer, _proposalTypes[i]), 0);
assertEq(rabbitToken.getVotes(proposer2, _proposalTypes[i]), INITIAL_VOTERS_BALANCE * VOTERS_NUMBER);
}
}
}
72 changes: 72 additions & 0 deletions solidity/test/integration/IntegrationBase.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// solhint-disable no-unused-import
// solhint-disable-next-line no-console
import {console} from 'forge-std/console.sol';

import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

import {IWonderVotes} from 'interfaces/governance/utils/IWonderVotes.sol';
import {IWonderGovernor} from 'interfaces/governance/IWonderGovernor.sol';

import {AliceGovernor} from 'examples/AliceGovernor.sol';
import {RabbitToken} from 'examples/RabbitToken.sol';

import {TestExtended} from '../utils/TestExtended.sol';

contract IntegrationBase is TestExtended {
uint256 public constant FORK_BLOCK = 111_361_902;

uint256 internal _initialBalance = 100_000 ether;

address public deployer = makeAddr('deployer');
address public proposer = makeAddr('proposer');
address public proposer2 = makeAddr('proposer2');

address[] public holders;

IWonderVotes public rabbitToken;
IWonderGovernor public governor;

uint256 public constant INITIAL_VOTERS_BALANCE = 100_000e18;
uint8 public constant VOTERS_NUMBER = 10;

function setUp() public virtual {
vm.createSelectFork(vm.rpcUrl('optimism'), FORK_BLOCK);

// Deploy the governance contracts
vm.startPrank(deployer);

address tokenAddress = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 1);
governor = new AliceGovernor(tokenAddress);
rabbitToken = new RabbitToken(AliceGovernor(payable(address(governor))));

vm.stopPrank();

for (uint256 i = 0; i < VOTERS_NUMBER; i++) {
address holder = makeAddr(string(abi.encodePacked('holder', i)));
holders.push(holder);
deal(tokenAddress, holder, INITIAL_VOTERS_BALANCE);
vm.prank(holder);

// start tracking votes
rabbitToken.delegate(holder);
}

_mineBlock();
}

event ProposalCreated(
uint256 proposalId,
uint8 proposalType,
address proposer,
address[] targets,
uint256[] values,
string[] signatures,
bytes[] calldatas,
uint256 voteStart,
uint256 voteEnd,
string description
);
}
87 changes: 87 additions & 0 deletions solidity/test/integration/Propose.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import './IntegrationBase.t.sol';

import {WonderVotes} from 'contracts/governance/utils/WonderVotes.sol';
import {WonderGovernor} from 'contracts/governance/WonderGovernor.sol';

contract Integration_Propose is IntegrationBase {
function _propose() internal returns (uint256 _proposalId) {
address[] memory _targets = new address[](1);
_targets[0] = address(governor);

uint256[] memory _values = new uint256[](1);
_values[0] = 1;

bytes[] memory _calldatas = new bytes[](1);
_calldatas[0] = abi.encode(0);

string memory _description = 'test proposal';

vm.prank(holders[0]);

// Propose
return governor.propose(0, _targets, _values, _calldatas, _description);
}

function _vote(uint256 _proposalId, uint256 _forVoters) internal {
for (uint256 _i = 0; _i < VOTERS_NUMBER; _i++) {
address _holder = holders[_i];
vm.prank(_holder);

// for 60% , against 40%
uint8 _vote = _forVoters > _i ? 1 : 0;
// Vote
governor.castVote(_proposalId, _vote);
}
}

function test_ProposalSucceeded() public {
uint256 _proposalId = _propose();

_mineBlocks(governor.votingDelay() + 1);

uint256 _forVoters = VOTERS_NUMBER / 2 + 1;
uint256 _againstVoters = VOTERS_NUMBER - _forVoters;

_vote(_proposalId, _forVoters);

(uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) =
AliceGovernor(payable(address(governor))).proposalTracks(_proposalId);

assertEq(_forVotes, INITIAL_VOTERS_BALANCE * _forVoters);
assertEq(_againstVotes, INITIAL_VOTERS_BALANCE * _againstVoters);
assertEq(_abstainVotes, 0);
assertEq(_votes, INITIAL_VOTERS_BALANCE * VOTERS_NUMBER);

// End voting period
_mineBlocks(governor.votingPeriod());

assertEq(uint256(governor.state(_proposalId)), 4);
}

function test_ProposalDefeated() public {
uint256 _proposalId = _propose();

_mineBlocks(governor.votingDelay() + 1);

uint256 _againstVoters = VOTERS_NUMBER / 2 + 1;
uint256 _forVoters = VOTERS_NUMBER - _againstVoters;

_vote(_proposalId, _forVoters);

(uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) =
AliceGovernor(payable(address(governor))).proposalTracks(_proposalId);

assertEq(_forVotes, INITIAL_VOTERS_BALANCE * _forVoters);
assertEq(_againstVotes, INITIAL_VOTERS_BALANCE * _againstVoters);
assertEq(_abstainVotes, 0);
assertEq(_votes, INITIAL_VOTERS_BALANCE * VOTERS_NUMBER);

// End voting period
_mineBlocks(governor.votingPeriod());

assertEq(uint256(governor.state(_proposalId)), 3);
}
}
2 changes: 1 addition & 1 deletion solidity/test/smock/SmockHelper.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

import {Test} from 'forge-std/Test.sol';

Expand Down
4 changes: 2 additions & 2 deletions solidity/test/smock/examples/MockAliceGovernor.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Test} from 'forge-std/Test.sol';
import {
Expand Down
4 changes: 2 additions & 2 deletions solidity/test/smock/examples/MockRabbitToken.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Test} from 'forge-std/Test.sol';
import {AliceGovernor, EIP712, ERC20, RabbitToken, WonderERC20Votes} from 'solidity/examples/RabbitToken.sol';
Expand Down
Loading

0 comments on commit fbeccaf

Please sign in to comment.