Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add integration tests #8

Merged
merged 20 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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=
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
touch .env
echo MAINNET_RPC="${{ secrets.MAINNET_RPC }}" >> .env
echo GOERLI_RPC="${{ secrets.GOERLI_RPC }}" >> .env
echo OPTIMISM_RPC="${{ secrets.OPTIMISM_RPC }}" >> .env
cat .env

- name: Run tests
Expand Down
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
);
}
Loading