diff --git a/.env.example b/.env.example index aa5efdc..5b6781a 100644 --- a/.env.example +++ b/.env.example @@ -5,3 +5,5 @@ GOERLI_RPC= GOERLI_DEPLOYER_PK= ETHERSCAN_API_KEY= + +OPTIMISM_RPC= \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 010bb44..7f5c524 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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 diff --git a/foundry.toml b/foundry.toml index 3f6d2a0..f2d2201 100644 --- a/foundry.toml +++ b/foundry.toml @@ -31,4 +31,5 @@ src = 'solidity/interfaces/' runs = 1000 [rpc_endpoints] -mainnet = "${MAINNET_RPC}" \ No newline at end of file +mainnet = "${MAINNET_RPC}" +optimism = "${OPTIMISM_RPC}" \ No newline at end of file diff --git a/solidity/examples/AliceGovernor.sol b/solidity/examples/AliceGovernor.sol index 2c2488d..e8e1691 100644 --- a/solidity/examples/AliceGovernor.sol +++ b/solidity/examples/AliceGovernor.sol @@ -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) { diff --git a/solidity/test/integration/Delegation.t.sol b/solidity/test/integration/Delegation.t.sol new file mode 100644 index 0000000..779863d --- /dev/null +++ b/solidity/test/integration/Delegation.t.sol @@ -0,0 +1,152 @@ +// 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_Delegation is IntegrationBase { + function test_AllVotersDelegateToProposer() public { + // AllVoters delegates to proposer + 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]; + + for (uint256 _j = 0; _j < governor.proposalTypes().length; _j++) { + uint8 proposalType = governor.proposalTypes()[_j]; + assertEq(rabbitToken.getVotes(holder, proposalType), 0); + } + } + + for (uint256 _j = 0; _j < governor.proposalTypes().length; _j++) { + uint8 proposalType = governor.proposalTypes()[_j]; + assertEq(rabbitToken.getVotes(proposer, proposalType), INITIAL_VOTERS_BALANCE * VOTERS_NUMBER); + } + } + + function test_AllVotersDelegateByProposalType() public { + uint8[] memory proposalTypes = governor.proposalTypes(); + + 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); + } + } +} diff --git a/solidity/test/integration/IntegrationBase.t.sol b/solidity/test/integration/IntegrationBase.t.sol new file mode 100644 index 0000000..8c2765c --- /dev/null +++ b/solidity/test/integration/IntegrationBase.t.sol @@ -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 + ); +} diff --git a/solidity/test/integration/Propose.t.sol b/solidity/test/integration/Propose.t.sol new file mode 100644 index 0000000..1645691 --- /dev/null +++ b/solidity/test/integration/Propose.t.sol @@ -0,0 +1,171 @@ +// 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 { + event ProposalCanceled(uint256 _proposalId); + + 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, uint256 _againstVoters) internal { + for (uint256 _i = 0; _i < VOTERS_NUMBER; _i++) { + address _holder = holders[_i]; + vm.prank(_holder); + + // for 60% , against 40% + uint8 _support = _forVoters > _i ? 1 : _forVoters + _againstVoters > _i ? 0 : 2; + // Vote + governor.castVote(_proposalId, _support); + } + } + + 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, _againstVoters); + + (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, _againstVoters); + + (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); + } + + function test_ProposalEven() public { + uint256 _proposalId = _propose(); + + _mineBlocks(governor.votingDelay() + 1); + + uint256 _againstVoters = VOTERS_NUMBER / 2; + uint256 _forVoters = VOTERS_NUMBER - _againstVoters; + + _vote(_proposalId, _forVoters, _againstVoters); + + (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); + } + + function test_ProposalSucceedWithAbstentions() public { + uint256 _proposalId = _propose(); + + _mineBlocks(governor.votingDelay() + 1); + + uint256 _abstainVoters = VOTERS_NUMBER / 2 + 1; + uint256 _againstVoters = (VOTERS_NUMBER - _abstainVoters) / 2 - 1; + uint256 _forVoters = VOTERS_NUMBER - _abstainVoters - _againstVoters; + + _vote(_proposalId, _forVoters, _againstVoters); + + (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, INITIAL_VOTERS_BALANCE * _abstainVoters); + assertEq(_votes, INITIAL_VOTERS_BALANCE * VOTERS_NUMBER); + + // End voting period + _mineBlocks(governor.votingPeriod()); + + assertEq(uint256(governor.state(_proposalId)), 4); + } + + function test_ProposeAndCancel() public { + 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.startPrank(holders[0]); + + // Propose + uint256 _proposalId = governor.propose(0, _targets, _values, _calldatas, _description); + + _expectEmit(address(governor)); + emit ProposalCanceled(_proposalId); + + // Cancel proposal + governor.cancel(0, _targets, _values, _calldatas, keccak256(bytes(_description))); + + vm.stopPrank(); + + _mineBlocks(governor.votingDelay() + 1); + + vm.prank(holders[1]); + + vm.expectRevert(abi.encodeWithSelector(IWonderGovernor.GovernorUnexpectedProposalState.selector, _proposalId, 2, 2)); + governor.castVote(_proposalId, 1); + } +} diff --git a/solidity/test/unit/WonderGovernor.t.sol b/solidity/test/unit/WonderGovernor.t.sol index fa480e7..a5badb0 100644 --- a/solidity/test/unit/WonderGovernor.t.sol +++ b/solidity/test/unit/WonderGovernor.t.sol @@ -11,6 +11,8 @@ import {IWonderVotes} from 'interfaces/governance/utils/IWonderVotes.sol'; import {IWonderGovernor} from 'interfaces/governance/IWonderGovernor.sol'; import {WonderVotes} from 'contracts/governance/utils/WonderVotes.sol'; +import {TestExtended} from '../utils/TestExtended.sol'; + contract GovernorForTest is AliceGovernor { constructor(address _wonderToken) AliceGovernor(_wonderToken) {} @@ -19,7 +21,7 @@ contract GovernorForTest is AliceGovernor { } } -contract BaseTest is Test { +contract BaseTest is TestExtended { address deployer = makeAddr('deployer'); address hatter = makeAddr('hatter'); address cat = makeAddr('cat'); @@ -45,10 +47,6 @@ contract BaseTest is Test { vm.stopPrank(); } - function _expectEmit(address _contract) internal { - vm.expectEmit(true, true, true, true, _contract); - } - function _createProposal( uint8 _proposalType, address _target, @@ -369,6 +367,7 @@ contract Unit_CastVote is BaseTest { (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); + assertEq(_votes, _voterVotes); assertEq(_forVotes, _voterVotes); assertEq(_againstVotes, 0); assertEq(_abstainVotes, 0); @@ -400,6 +399,7 @@ contract Unit_CastVote is BaseTest { (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); + assertEq(_votes, _voterVotes); assertEq(_forVotes, 0); assertEq(_againstVotes, _voterVotes); assertEq(_abstainVotes, 0); @@ -431,6 +431,7 @@ contract Unit_CastVote is BaseTest { (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); + assertEq(_votes, _voterVotes); assertEq(_forVotes, 0); assertEq(_againstVotes, 0); assertEq(_abstainVotes, _voterVotes); @@ -528,6 +529,7 @@ contract Unit_CastVoteWithReason is BaseTest { (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); + assertEq(_votes, _voterVotes); assertEq(_forVotes, _voterVotes); assertEq(_againstVotes, 0); assertEq(_abstainVotes, 0); @@ -560,6 +562,7 @@ contract Unit_CastVoteWithReason is BaseTest { (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); + assertEq(_votes, _voterVotes); assertEq(_forVotes, 0); assertEq(_againstVotes, _voterVotes); assertEq(_abstainVotes, 0); @@ -592,6 +595,7 @@ contract Unit_CastVoteWithReason is BaseTest { (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); + assertEq(_votes, _voterVotes); assertEq(_forVotes, 0); assertEq(_againstVotes, 0); assertEq(_abstainVotes, _voterVotes); @@ -696,6 +700,7 @@ contract Unit_CastVoteWithReasonAndParams is BaseTest { (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); + assertEq(_votes, _voterVotes); assertEq(_forVotes, _voterVotes); assertEq(_againstVotes, 0); assertEq(_abstainVotes, 0); @@ -730,6 +735,7 @@ contract Unit_CastVoteWithReasonAndParams is BaseTest { (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); + assertEq(_votes, _voterVotes); assertEq(_forVotes, 0); assertEq(_againstVotes, _voterVotes); assertEq(_abstainVotes, 0); @@ -764,6 +770,7 @@ contract Unit_CastVoteWithReasonAndParams is BaseTest { (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); + assertEq(_votes, _voterVotes); assertEq(_forVotes, 0); assertEq(_againstVotes, 0); assertEq(_abstainVotes, _voterVotes); diff --git a/solidity/test/unit/WonderVotes.t.sol b/solidity/test/unit/WonderVotes.t.sol index 8b7c1c8..fb8ee85 100644 --- a/solidity/test/unit/WonderVotes.t.sol +++ b/solidity/test/unit/WonderVotes.t.sol @@ -12,6 +12,8 @@ import {RabbitToken} from 'examples/RabbitToken.sol'; import {MockAliceGovernor} from '../smock/examples/MockAliceGovernor.sol'; import {AliceGovernor} from 'examples/AliceGovernor.sol'; +import {TestExtended} from '../utils/TestExtended.sol'; + contract WonderVotesForTest is RabbitToken { constructor(AliceGovernor _governor) RabbitToken(_governor) {} @@ -24,7 +26,7 @@ contract WonderVotesForTest is RabbitToken { } } -contract BaseTest is Test { +contract BaseTest is TestExtended { address deployer = makeAddr('deployer'); address hatter = makeAddr('hatter'); address cat = makeAddr('cat'); @@ -51,10 +53,6 @@ contract BaseTest is Test { vm.stopPrank(); } - - function _expectEmit(address _contract) internal { - vm.expectEmit(true, true, true, true, _contract); - } } contract Unit_Delegate_Simple is BaseTest { @@ -290,7 +288,7 @@ contract Unit_Delegate_SmartAndPartial is BaseTest { function test_Minting_SmartAndPartialDelegation_Before(uint128 _amount) public { uint8[] memory _proposalTypes = rabbitToken.proposalTypes(); - // To simply we will divide the voting power into 2 delegates 50% each + // To simplify we will divide the voting power into 2 delegates 50% each // We can add a more complex test of this further uint256 _weightNormalizer = rabbitToken.weightNormalizer(); uint256 _weight = _weightNormalizer / 2; @@ -336,8 +334,7 @@ contract Unit_Delegate_SmartAndPartial is BaseTest { uint8[] memory _proposalTypes = rabbitToken.proposalTypes(); - // To simply we will divide the voting power into 2 delegates 50% each - // We can add a more complex test of this further + // 50% each uint256 _weightNormalizer = rabbitToken.weightNormalizer(); uint256 _weight = _weightNormalizer / 2; @@ -392,8 +389,7 @@ contract Unit_Delegate_SmartAndPartial is BaseTest { uint8[] memory _proposalTypes = rabbitToken.proposalTypes(); WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount); - // To simply we will divide the voting power into 2 delegates 50% each - // We can add a more complex test of this further + // 50% each uint256 _weightNormalizer = rabbitToken.weightNormalizer(); uint256 _weight = _weightNormalizer / 2; @@ -436,7 +432,7 @@ contract Unit_Delegate_SmartAndPartial is BaseTest { function test_Revert_InvalidWeightSum_LessThan_WeighNormalizer(uint8 _proposalType, uint256 _weightSum) public { vm.assume(_proposalType < rabbitToken.proposalTypes().length); - vm.assume(_weightSum > 0 && (_weightSum > rabbitToken.weightNormalizer())); + vm.assume(_weightSum > 0 && (_weightSum < rabbitToken.weightNormalizer())); IWonderVotes.Delegate[] memory _delegatesStruct = new IWonderVotes.Delegate[](1); _delegatesStruct[0] = IWonderVotes.Delegate({account: makeAddr('delegate'), weight: _weightSum}); @@ -588,7 +584,7 @@ contract Unit_TransferVotes is BaseTest { address _account, uint8[] memory _proposalTypes ) internal returns (address[] memory, address[] memory) { - // To simply we will divide the voting power into 2 delegates 50% each + // To simplify we will divide the voting power into 2 delegates 50% each uint256 _weightNormalizer = rabbitToken.weightNormalizer(); uint256 _weight = _weightNormalizer / 2; diff --git a/solidity/test/utils/TestExtended.sol b/solidity/test/utils/TestExtended.sol new file mode 100644 index 0000000..e3dd99d --- /dev/null +++ b/solidity/test/utils/TestExtended.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import 'forge-std/Test.sol'; + +contract TestExtended is Test { + uint256 public constant BLOCK_TIME = 12 seconds; + + function _mineBlock() internal { + _mineBlocks(1); + } + + function _mineBlocks(uint256 _blocks) internal { + vm.warp(block.timestamp + _blocks * BLOCK_TIME); + vm.roll(block.number + _blocks); + } + + function _expectEmit(address _contract) internal { + vm.expectEmit(true, true, true, true, _contract); + } +}