diff --git a/package.json b/package.json index 4ecc32f..2fb1326 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,8 @@ "lint:sol-tests": "solhint 'solidity/test/**/*.sol'", "prepare": "husky install", "test": "forge test -vvv", - "test:e2e": "forge test --match-contract E2E -vvv", - "test:gas": "forge test --match-contract Integration -vvv --gas-report", + "test:e2e": "forge test --match-contract Integration -vvv", + "test:gas": "forge test --match-contract Gas -vvv --gas-report", "test:integration": "forge test --match-contract Integration -vvv", "test:unit": "forge test --match-contract Unit -vvv", "test:unit:deep": "FOUNDRY_FUZZ_RUNS=5000 yarn test:unit" diff --git a/solidity/contracts/governance/WonderGovernor.sol b/solidity/contracts/governance/WonderGovernor.sol index 820d254..153954d 100644 --- a/solidity/contracts/governance/WonderGovernor.sol +++ b/solidity/contracts/governance/WonderGovernor.sol @@ -270,6 +270,7 @@ abstract contract WonderGovernor is address account, uint8 proposalType, uint256 timepoint, + uint256 voteStart, bytes memory params ) internal view virtual returns (uint256); @@ -304,6 +305,7 @@ abstract contract WonderGovernor is address[] memory targets, uint256[] memory values, bytes[] memory calldatas, + uint256 votesBlockNumber, string memory description ) public virtual validProposalType(proposalType) returns (uint256) { address proposer = _msgSender(); @@ -314,7 +316,7 @@ abstract contract WonderGovernor is } // check proposal threshold - uint256 proposerVotes = getVotes(proposer, proposalType, clock() - 1); + uint256 proposerVotes = getVotes(proposer, proposalType, votesBlockNumber, clock() - 1); uint256 votesThreshold = proposalThreshold(proposalType); if (proposerVotes < votesThreshold) { revert GovernorInsufficientProposerVotes(proposer, proposerVotes, votesThreshold); @@ -533,8 +535,13 @@ abstract contract WonderGovernor is /** * @dev See {IWonderGovernor-getVotes}. */ - function getVotes(address account, uint8 proposalType, uint256 timepoint) public view virtual returns (uint256) { - return _getVotes(account, proposalType, timepoint, _defaultParams()); + function getVotes( + address account, + uint8 proposalType, + uint256 timepoint, + uint256 voteStart + ) public view virtual returns (uint256) { + return _getVotes(account, proposalType, timepoint, voteStart, _defaultParams()); } /** @@ -544,17 +551,18 @@ abstract contract WonderGovernor is address account, uint8 proposalType, uint256 timepoint, + uint256 voteStart, bytes memory params ) public view virtual returns (uint256) { - return _getVotes(account, proposalType, timepoint, params); + return _getVotes(account, proposalType, timepoint, voteStart, params); } /** * @dev See {IWonderGovernor-castVote}. */ - function castVote(uint256 proposalId, uint8 support) public virtual returns (uint256) { + function castVote(uint256 proposalId, uint8 support, uint256 votesBlockNumber) public virtual returns (uint256) { address voter = _msgSender(); - return _castVote(proposalId, voter, support, ''); + return _castVote(proposalId, voter, support, votesBlockNumber, ''); } /** @@ -563,10 +571,11 @@ abstract contract WonderGovernor is function castVoteWithReason( uint256 proposalId, uint8 support, + uint256 votesBlockNumber, string calldata reason ) public virtual returns (uint256) { address voter = _msgSender(); - return _castVote(proposalId, voter, support, reason); + return _castVote(proposalId, voter, support, votesBlockNumber, reason); } /** @@ -575,11 +584,12 @@ abstract contract WonderGovernor is function castVoteWithReasonAndParams( uint256 proposalId, uint8 support, + uint256 votesBlockNumber, string calldata reason, bytes memory params ) public virtual returns (uint256) { address voter = _msgSender(); - return _castVote(proposalId, voter, support, reason, params); + return _castVote(proposalId, voter, support, reason, votesBlockNumber, params); } /** @@ -589,6 +599,7 @@ abstract contract WonderGovernor is uint256 proposalId, uint8 support, address voter, + uint256 votesBlockNumber, bytes memory signature ) public virtual returns (uint256) { bool valid = SignatureChecker.isValidSignatureNow( @@ -601,7 +612,7 @@ abstract contract WonderGovernor is revert GovernorInvalidSignature(voter); } - return _castVote(proposalId, voter, support, ''); + return _castVote(proposalId, voter, support, votesBlockNumber, ''); } /** @@ -611,6 +622,7 @@ abstract contract WonderGovernor is uint256 proposalId, uint8 support, address voter, + uint256 votesBlockNumber, string calldata reason, bytes memory params, bytes memory signature @@ -637,7 +649,7 @@ abstract contract WonderGovernor is revert GovernorInvalidSignature(voter); } - return _castVote(proposalId, voter, support, reason, params); + return _castVote(proposalId, voter, support, reason, votesBlockNumber, params); } /** @@ -650,9 +662,10 @@ abstract contract WonderGovernor is uint256 proposalId, address account, uint8 support, + uint256 votesBlockNumber, string memory reason ) internal virtual returns (uint256) { - return _castVote(proposalId, account, support, reason, _defaultParams()); + return _castVote(proposalId, account, support, reason, votesBlockNumber, _defaultParams()); } /** @@ -666,13 +679,14 @@ abstract contract WonderGovernor is address account, uint8 support, string memory reason, + uint256 votesBlockNumber, bytes memory params ) internal virtual returns (uint256) { _validateStateBitmap(proposalId, _encodeStateBitmap(ProposalState.Active)); uint8 _proposalType = _proposals[proposalId].proposalType; - uint256 weight = _getVotes(account, _proposalType, proposalSnapshot(proposalId), params); + uint256 weight = _getVotes(account, _proposalType, votesBlockNumber, proposalSnapshot(proposalId), params); _countVote(proposalId, account, support, weight, params); if (params.length == 0) { diff --git a/solidity/contracts/governance/utils/WonderVotes.sol b/solidity/contracts/governance/utils/WonderVotes.sol index 747d23d..96e6ee3 100644 --- a/solidity/contracts/governance/utils/WonderVotes.sol +++ b/solidity/contracts/governance/utils/WonderVotes.sol @@ -6,7 +6,6 @@ import {IERC6372} from '@openzeppelin/contracts/interfaces/IERC6372.sol'; import {Context} from '@openzeppelin/contracts/utils/Context.sol'; import {Nonces} from '@openzeppelin/contracts/utils/Nonces.sol'; import {EIP712} from '@openzeppelin/contracts/utils/cryptography/EIP712.sol'; -import {Checkpoints} from '@openzeppelin/contracts/utils/structs/Checkpoints.sol'; import {SafeCast} from '@openzeppelin/contracts/utils/math/SafeCast.sol'; import {ECDSA} from '@openzeppelin/contracts/utils/cryptography/ECDSA.sol'; import {Time} from '@openzeppelin/contracts/utils/types/Time.sol'; @@ -31,16 +30,19 @@ import {IWonderVotes} from 'interfaces/governance/utils/IWonderVotes.sol'; * previous example, it would be included in {ERC721-_update}). */ abstract contract WonderVotes is Context, EIP712, Nonces, IERC6372, IWonderVotes { - using Checkpoints for Checkpoints.Trace208; - bytes32 private constant DELEGATION_TYPEHASH = keccak256('Delegation(uint8 proposalType, Delegate[] delegatees,uint256 nonce,uint256 expiry)'); mapping(address account => mapping(uint8 proposalType => Delegate[])) private _delegatees; - mapping(address delegatee => mapping(uint8 proposalType => Checkpoints.Trace208)) private _delegateCheckpoints; + mapping(address delegatee => mapping(uint8 proposalType => mapping(uint48 key => Checkpoint))) private + _delegateCheckpoints; + + mapping(address delegatee => mapping(uint8 proposalType => uint48 lastUpdate)) private _delegateCheckpointsLastUpdate; + + mapping(uint48 key => uint208 value) private _totalCheckpoints; - Checkpoints.Trace208 private _totalCheckpoints; + uint48 private _totalCheckpointsLastUpdate; mapping(address account => bool) private _nonDelegableAddresses; @@ -78,7 +80,7 @@ abstract contract WonderVotes is Context, EIP712, Nonces, IERC6372, IWonderVotes * @dev Returns the current amount of votes that `account` has for the given `proposalType`. */ function getVotes(address account, uint8 proposalType) public view virtual returns (uint256) { - return _delegateCheckpoints[account][proposalType].latest(); + return _delegateCheckpoints[account][proposalType][_delegateCheckpointsLastUpdate[account][proposalType]]._value; } /** @@ -89,12 +91,15 @@ abstract contract WonderVotes is Context, EIP712, Nonces, IERC6372, IWonderVotes * * - `timepoint` must be in the past. If operating using block numbers, the block must be already mined. */ - function getPastVotes(address account, uint8 proposalType, uint256 timepoint) public view virtual returns (uint256) { - uint48 currentTimepoint = clock(); - if (timepoint >= currentTimepoint) { - revert ERC5805FutureLookup(timepoint, currentTimepoint); - } - return _delegateCheckpoints[account][proposalType].upperLookupRecent(SafeCast.toUint48(timepoint)); + function getSnapshotVotes( + address account, + uint8 proposalType, + uint256 timepoint, + uint256 voteStart + ) public view virtual returns (uint256) { + Checkpoint memory _checkPoint = _delegateCheckpoints[account][proposalType][uint48(timepoint)]; + if (_checkPoint._nextKey < voteStart && _checkPoint._nextKey > 0) revert InvalidVotesClock(uint48(timepoint)); + return _checkPoint._value; } /** @@ -114,14 +119,14 @@ abstract contract WonderVotes is Context, EIP712, Nonces, IERC6372, IWonderVotes if (timepoint >= currentTimepoint) { revert ERC5805FutureLookup(timepoint, currentTimepoint); } - return _totalCheckpoints.upperLookupRecent(SafeCast.toUint48(timepoint)); + return _totalCheckpoints[uint48(timepoint)]; } /** * @dev Returns the current total supply of votes for a given `proposalType`. */ function _getTotalSupply() internal view virtual returns (uint256) { - return _totalCheckpoints.latest(); + return _totalCheckpoints[_totalCheckpointsLastUpdate]; } /** @@ -292,11 +297,12 @@ abstract contract WonderVotes is Context, EIP712, Nonces, IERC6372, IWonderVotes */ function _transferVotingUnits(address from, address to, uint256 amount) internal virtual { if (from == address(0)) { - _push(_totalCheckpoints, _add, SafeCast.toUint208(amount)); + _totalCheckpoints[clock()] = uint208(_uncheckedAdd(_totalCheckpoints[_totalCheckpointsLastUpdate], amount)); } if (to == address(0)) { - _push(_totalCheckpoints, _subtract, SafeCast.toUint208(amount)); + _totalCheckpoints[clock()] = uint208(_uncheckedSub(_totalCheckpoints[_totalCheckpointsLastUpdate], amount)); } + _totalCheckpointsLastUpdate = clock(); uint8[] memory _proposalTypes = _getProposalTypes(); @@ -317,9 +323,18 @@ abstract contract WonderVotes is Context, EIP712, Nonces, IERC6372, IWonderVotes if (from[i].account != address(0)) { _weight = from[i].weight; uint256 _votingUnits = amount * _weight / _weightSum; - (uint256 oldValue, uint256 newValue) = - _push(_delegateCheckpoints[from[i].account][proposalType], _subtract, SafeCast.toUint208(_votingUnits)); - emit DelegateVotesChanged(from[i].account, proposalType, oldValue, newValue); + + Checkpoint storage _previousCheckpoint = _delegateCheckpoints[from[i].account][proposalType][_delegateCheckpointsLastUpdate[from[i] + .account][proposalType]]; + _previousCheckpoint._nextKey = clock(); + + uint256 _oldValue = _previousCheckpoint._value; + uint256 _newValue = _uncheckedSub(_oldValue, _votingUnits); + + _delegateCheckpoints[from[i].account][proposalType][clock()]._value = uint208(_newValue); + + _delegateCheckpointsLastUpdate[from[i].account][proposalType] = clock(); + emit DelegateVotesChanged(from[i].account, proposalType, _oldValue, _newValue); } } @@ -327,45 +342,20 @@ abstract contract WonderVotes is Context, EIP712, Nonces, IERC6372, IWonderVotes if (to[i].account != address(0)) { _weight = to[i].weight; uint256 _votingUnits = amount * _weight / _weightSum; - (uint256 oldValue, uint256 newValue) = - _push(_delegateCheckpoints[to[i].account][proposalType], _add, SafeCast.toUint208(_votingUnits)); - emit DelegateVotesChanged(to[i].account, proposalType, oldValue, newValue); - } - } - } - /** - * @dev Get number of checkpoints for `account` given a `proposalType`. - */ - function _numCheckpoints(address account, uint8 proposalType) internal view virtual returns (uint32) { - return SafeCast.toUint32(_delegateCheckpoints[account][proposalType].length()); - } + Checkpoint storage _previousCheckpoint = + _delegateCheckpoints[to[i].account][proposalType][_delegateCheckpointsLastUpdate[to[i].account][proposalType]]; + _previousCheckpoint._nextKey = clock(); - /** - * @dev Get the `pos`-th checkpoint for `account` given a `proposalType`. - */ - function _checkpoints( - address account, - uint8 proposalType, - uint32 pos - ) internal view virtual returns (Checkpoints.Checkpoint208 memory) { - return _delegateCheckpoints[account][proposalType].at(pos); - } - - function _push( - Checkpoints.Trace208 storage store, - function(uint208, uint208) view returns (uint208) op, - uint208 delta - ) private returns (uint208, uint208) { - return store.push(clock(), op(store.latest(), delta)); - } + uint256 _oldValue = _previousCheckpoint._value; + uint256 _newValue = _uncheckedAdd(_oldValue, _votingUnits); - function _add(uint208 a, uint208 b) private pure returns (uint208) { - return a + b; - } + _delegateCheckpoints[to[i].account][proposalType][clock()]._value = uint208(_newValue); - function _subtract(uint208 a, uint208 b) private pure returns (uint208) { - return a - b; + _delegateCheckpointsLastUpdate[to[i].account][proposalType] = clock(); + emit DelegateVotesChanged(to[i].account, proposalType, _oldValue, _newValue); + } + } } /** @@ -418,4 +408,16 @@ abstract contract WonderVotes is Context, EIP712, Nonces, IERC6372, IWonderVotes } _; } + + function _uncheckedAdd(uint256 a, uint256 b) internal pure returns (uint256) { + unchecked { + return a + b; + } + } + + function _uncheckedSub(uint256 a, uint256 b) internal pure returns (uint256) { + unchecked { + return a - b; + } + } } diff --git a/solidity/contracts/token/ERC20/extensions/WonderERC20Votes.sol b/solidity/contracts/token/ERC20/extensions/WonderERC20Votes.sol index 0cafdd0..7385d52 100644 --- a/solidity/contracts/token/ERC20/extensions/WonderERC20Votes.sol +++ b/solidity/contracts/token/ERC20/extensions/WonderERC20Votes.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.20; import {ERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol'; -import {Checkpoints} from '@openzeppelin/contracts/utils/structs/Checkpoints.sol'; import {WonderVotes} from 'contracts/governance/utils/WonderVotes.sol'; /** @@ -15,7 +14,7 @@ import {WonderVotes} from 'contracts/governance/utils/WonderVotes.sol'; * * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting - * power can be queried through the public accessors {getVotes} and {getPastVotes}. + * power can be queried through the public accessors {getVotes} and {getSnapshotVotes}. * * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. @@ -66,22 +65,4 @@ abstract contract WonderERC20Votes is ERC20, WonderVotes { function _getVotingUnits(address account) internal view virtual override returns (uint256) { return balanceOf(account); } - - /** - * @dev Get number of checkpoints for `account` given a `proposalType`. - */ - function numCheckpoints(address account, uint8 proposalType) public view virtual returns (uint32) { - return _numCheckpoints(account, proposalType); - } - - /** - * @dev Get the `pos`-th checkpoint for `account` given a `proposalType`. - */ - function checkpoints( - address account, - uint8 proposalType, - uint32 pos - ) public view virtual returns (Checkpoints.Checkpoint208 memory) { - return _checkpoints(account, proposalType, pos); - } } diff --git a/solidity/examples/AliceGovernor.sol b/solidity/examples/AliceGovernor.sol index e8e1691..a2c1461 100644 --- a/solidity/examples/AliceGovernor.sol +++ b/solidity/examples/AliceGovernor.sol @@ -46,9 +46,10 @@ contract AliceGovernor is WonderGovernor { address _account, uint8 _proposalType, uint256 _timepoint, + uint256 _voteStart, bytes memory _params ) internal view virtual override returns (uint256) { - return votes.getPastVotes(_account, _proposalType, _timepoint); + return votes.getSnapshotVotes(_account, _proposalType, _timepoint, _voteStart); } function clock() public view override returns (uint48) { diff --git a/solidity/interfaces/governance/IWonderGovernor.sol b/solidity/interfaces/governance/IWonderGovernor.sol index eb8663f..89d01b0 100644 --- a/solidity/interfaces/governance/IWonderGovernor.sol +++ b/solidity/interfaces/governance/IWonderGovernor.sol @@ -294,7 +294,12 @@ interface IWonderGovernor is IERC165, IERC6372 { * Note: this can be implemented in a number of ways, for example by reading the delegated balance from one (or * multiple), {ERC20Votes} tokens. */ - function getVotes(address account, uint8 proposalType, uint256 timepoint) external view returns (uint256); + function getVotes( + address account, + uint8 proposalType, + uint256 timepoint, + uint256 voteStart + ) external view returns (uint256); /** * @notice module:reputation @@ -304,6 +309,7 @@ interface IWonderGovernor is IERC165, IERC6372 { address account, uint8 proposalType, uint256 timepoint, + uint256 voteStart, bytes memory params ) external view returns (uint256); @@ -324,6 +330,7 @@ interface IWonderGovernor is IERC165, IERC6372 { address[] memory targets, uint256[] memory values, bytes[] memory calldatas, + uint256 votesBlockNumber, string memory description ) external returns (uint256 proposalId); @@ -378,7 +385,7 @@ interface IWonderGovernor is IERC165, IERC6372 { * * Emits a {VoteCast} event. */ - function castVote(uint256 proposalId, uint8 support) external returns (uint256 balance); + function castVote(uint256 proposalId, uint8 support, uint256 votesBlockNumber) external returns (uint256 balance); /** * @dev Cast a vote with a reason @@ -388,6 +395,7 @@ interface IWonderGovernor is IERC165, IERC6372 { function castVoteWithReason( uint256 proposalId, uint8 support, + uint256 votesBlockNumber, string calldata reason ) external returns (uint256 balance); @@ -399,6 +407,7 @@ interface IWonderGovernor is IERC165, IERC6372 { function castVoteWithReasonAndParams( uint256 proposalId, uint8 support, + uint256 votesBlockNumber, string calldata reason, bytes memory params ) external returns (uint256 balance); @@ -412,6 +421,7 @@ interface IWonderGovernor is IERC165, IERC6372 { uint256 proposalId, uint8 support, address voter, + uint256 votesBlockNumber, bytes memory signature ) external returns (uint256 balance); @@ -425,6 +435,7 @@ interface IWonderGovernor is IERC165, IERC6372 { uint256 proposalId, uint8 support, address voter, + uint256 votesBlockNumber, string calldata reason, bytes memory params, bytes memory signature diff --git a/solidity/interfaces/governance/utils/IWonderVotes.sol b/solidity/interfaces/governance/utils/IWonderVotes.sol index a186451..491be24 100644 --- a/solidity/interfaces/governance/utils/IWonderVotes.sol +++ b/solidity/interfaces/governance/utils/IWonderVotes.sol @@ -11,6 +11,11 @@ interface IWonderVotes { uint256 weight; } + struct Checkpoint { + uint48 _nextKey; + uint208 _value; + } + /** * @dev The signature used has expired. */ @@ -41,6 +46,8 @@ interface IWonderVotes { */ error VotesDelegationSuspended(address account); + error InvalidVotesClock(uint48 clock); + /** * @dev Emitted when an account changes their delegates. */ @@ -68,7 +75,12 @@ interface IWonderVotes { * @dev Returns the amount of votes that `account` had at a specific moment in the past for a given `proposalType`. * If the `clock()` is configured to use block numbers, this will return the value at the end of the corresponding block. */ - function getPastVotes(address account, uint8 proposalType, uint256 timepoint) external view returns (uint256); + function getSnapshotVotes( + address account, + uint8 proposalType, + uint256 votesBlockNumber, + uint256 voteStart + ) external view returns (uint256); /** * @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is diff --git a/solidity/test/gas/WonderVotes.t.sol b/solidity/test/gas/WonderVotes.t.sol new file mode 100644 index 0000000..2340a83 --- /dev/null +++ b/solidity/test/gas/WonderVotes.t.sol @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import 'forge-std/Test.sol'; + +import {WonderGovernor} from 'contracts/governance/WonderGovernor.sol'; +import {IWonderGovernor} from 'interfaces/governance/IWonderGovernor.sol'; +import {AliceGovernor} from 'examples/AliceGovernor.sol'; +import {IWonderVotes} from 'interfaces/governance/utils/IWonderVotes.sol'; +import {IWonderGovernor} from 'interfaces/governance/IWonderGovernor.sol'; +import {WonderVotes} from 'contracts/governance/utils/WonderVotes.sol'; + +import {RabbitToken} from 'examples/RabbitToken.sol'; +import {TestExtended} from '../utils/TestExtended.sol'; + +contract GovernorForTest is AliceGovernor { + constructor(address _wonderToken) AliceGovernor(_wonderToken) {} + + function getProposal(uint256 _proposalId) public view returns (ProposalCore memory) { + return _getProposal(_proposalId); + } +} + +contract WonderVotesForTest is RabbitToken { + constructor(AliceGovernor _governor) RabbitToken(_governor) {} + + function mint(address _account, uint256 _amount) public { + _mint(_account, _amount); + } + + function burn(uint256 _amount) public { + _burn(msg.sender, _amount); + } +} + +contract BaseTest is TestExtended { + address deployer = makeAddr('deployer'); + address hatter = makeAddr('hatter'); + address cat = makeAddr('cat'); + + IWonderGovernor governor; + RabbitToken rabbit; + + function _mockgetSnapshotVotes(address _account, uint8 _proposalType, uint256 _timePoint, uint256 _votes) internal { + vm.mockCall( + address(rabbit), + abi.encodeWithSelector(IWonderVotes.getSnapshotVotes.selector, _account, _proposalType, _timePoint), + abi.encode(_votes) + ); + } + + function setUp() public { + vm.startPrank(deployer); + + address tokenAddress = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 1); + governor = new GovernorForTest(tokenAddress); + rabbit = new WonderVotesForTest(AliceGovernor(payable(address(governor)))); + + vm.stopPrank(); + } + + function _createProposal( + uint8 _proposalType, + address _target, + uint256 _value, + bytes memory _calldata, + string memory _description, + uint256 _proposerVotes + ) internal returns (uint256) { + vm.assume(_proposalType < governor.proposalTypes().length); + vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); + + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + + address[] memory _targets = new address[](1); + _targets[0] = _target; + + uint256[] memory _values = new uint256[](1); + _values[0] = _value; + + bytes[] memory _calldatas = new bytes[](1); + _calldatas[0] = _calldata; + + vm.prank(hatter); + return governor.propose(_proposalType, _targets, _values, _calldatas, block.number - 1, _description); + } +} + +contract Gas_GetVotesAndTransfer is BaseTest { + event VoteCastWithParams( + address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason, bytes params + ); + + function test_bal( + uint8 _proposalType, + address _target, + uint256 _value, + bytes memory _calldata, + string memory _description, + uint256 _proposerVotes, + uint128 _voterVotes, + uint8 _previousTransfers + ) public { + vm.assume(_proposalType < governor.proposalTypes().length); + vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); + + for (uint256 i = 0; i <= _previousTransfers; i++) { + (WonderVotesForTest(address(rabbit))).mint(cat, 1); + } + + uint256 balanceBlock = block.number; + (WonderVotesForTest(address(rabbit))).mint(cat, _voterVotes); + _mineBlock(); + + uint256 _voteStart = block.number + governor.votingDelay(); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + + uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); + + for (uint256 i = 0; i <= governor.votingDelay(); i++) { + (WonderVotesForTest(address(rabbit))).mint(cat, 1); + _mineBlock(); + } + + vm.prank(cat); + governor.castVote(_proposalId, 1, balanceBlock); + + (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = + AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); + } + + function test_get_votes(uint8 _previousTransfers, uint8 _afterTransfers, uint8 _proposalStart) public { + vm.assume(_proposalStart >= _previousTransfers && _proposalStart <= _afterTransfers); + vm.prank(cat); + rabbit.delegate(cat); + + for (uint256 i = 0; i <= _previousTransfers; i++) { + (WonderVotesForTest(address(rabbit))).mint(cat, 1); + _mineBlock(); + } + + for (uint256 i = 0; i <= _afterTransfers; i++) { + (WonderVotesForTest(address(rabbit))).mint(cat, 1); + _mineBlock(); + } + + emit log_uint(rabbit.getSnapshotVotes(cat, 0, _proposalStart, _proposalStart)); + } + + function test_get_votes_with_transfer(uint8 _previousTransfers, uint8 _afterTransfers, uint8 _proposalStart) public { + vm.assume(_proposalStart >= _previousTransfers && _proposalStart <= _afterTransfers); + + vm.prank(cat); + rabbit.delegate(cat); + + vm.prank(hatter); + rabbit.delegate(hatter); + + (WonderVotesForTest(address(rabbit))).mint(hatter, uint256(_previousTransfers) + _afterTransfers); + + vm.startPrank(hatter); + for (uint256 i = 0; i < _previousTransfers; i++) { + rabbit.transfer(cat, 1); + _mineBlock(); + } + + for (uint256 i = 0; i < _afterTransfers; i++) { + rabbit.transfer(cat, 1); + _mineBlock(); + } + + vm.stopPrank(); + + emit log_uint(rabbit.getSnapshotVotes(cat, 0, _proposalStart, _proposalStart)); + } + + function test_get_votes_with_transfer_without_delegation( + uint8 _previousTransfers, + uint8 _afterTransfers, + uint8 _proposalStart + ) public { + vm.assume(_proposalStart >= _previousTransfers && _proposalStart <= _afterTransfers); + + (WonderVotesForTest(address(rabbit))).mint(hatter, uint256(_previousTransfers) + _afterTransfers); + + vm.startPrank(hatter); + for (uint256 i = 0; i < _previousTransfers; i++) { + rabbit.transfer(cat, 1); + _mineBlock(); + } + + for (uint256 i = 0; i < _afterTransfers; i++) { + rabbit.transfer(cat, 1); + _mineBlock(); + } + + vm.stopPrank(); + + vm.prank(cat); + rabbit.delegate(cat); + + emit log_uint(rabbit.getSnapshotVotes(cat, 0, _proposalStart, _proposalStart)); + } +} diff --git a/solidity/test/integration/Delegation.t.sol b/solidity/test/integration/Delegation.t.sol index a9fbc9a..fe5a286 100644 --- a/solidity/test/integration/Delegation.t.sol +++ b/solidity/test/integration/Delegation.t.sol @@ -189,7 +189,7 @@ contract Integration_Delegation is IntegrationBase { 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 + // To propose Governor controls the proposal threshold calling getSnapshotVotes, so we need to mine a block to be able to propose _mineBlock(); uint8[] memory _proposalTypes = governor.proposalTypes(); @@ -214,7 +214,7 @@ contract Integration_Delegation is IntegrationBase { block.number + governor.votingPeriod() + 1, _description ); - governor.propose(_proposalType, _targets, _values, _calldatas, _description); + governor.propose(_proposalType, _targets, _values, _calldatas, block.number - 1, _description); } vm.stopPrank(); } diff --git a/solidity/test/integration/Propose.t.sol b/solidity/test/integration/Propose.t.sol index 1645691..70f9177 100644 --- a/solidity/test/integration/Propose.t.sol +++ b/solidity/test/integration/Propose.t.sol @@ -24,7 +24,7 @@ contract Integration_Propose is IntegrationBase { vm.prank(holders[0]); // Propose - return governor.propose(0, _targets, _values, _calldatas, _description); + return governor.propose(0, _targets, _values, _calldatas, block.number - 1, _description); } function _vote(uint256 _proposalId, uint256 _forVoters, uint256 _againstVoters) internal { @@ -35,7 +35,7 @@ contract Integration_Propose is IntegrationBase { // for 60% , against 40% uint8 _support = _forVoters > _i ? 1 : _forVoters + _againstVoters > _i ? 0 : 2; // Vote - governor.castVote(_proposalId, _support); + governor.castVote(_proposalId, _support, block.number - 3); } } @@ -151,7 +151,7 @@ contract Integration_Propose is IntegrationBase { vm.startPrank(holders[0]); // Propose - uint256 _proposalId = governor.propose(0, _targets, _values, _calldatas, _description); + uint256 _proposalId = governor.propose(0, _targets, _values, _calldatas, block.number - 1, _description); _expectEmit(address(governor)); emit ProposalCanceled(_proposalId); @@ -166,6 +166,6 @@ contract Integration_Propose is IntegrationBase { vm.prank(holders[1]); vm.expectRevert(abi.encodeWithSelector(IWonderGovernor.GovernorUnexpectedProposalState.selector, _proposalId, 2, 2)); - governor.castVote(_proposalId, 1); + governor.castVote(_proposalId, 1, block.number - 1); } } diff --git a/solidity/test/smock/SmockHelper.sol b/solidity/test/smock/SmockHelper.sol deleted file mode 100644 index 7ef3520..0000000 --- a/solidity/test/smock/SmockHelper.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import {Test} from 'forge-std/Test.sol'; - -contract SmockHelper is Test { - function deployMock( - string memory _label, - bytes memory _creationCode, - bytes memory _encodedArgs - ) internal returns (address _deployed) { - bytes memory _bytecode = abi.encodePacked(_creationCode, _encodedArgs); - assembly { - mstore(0x0, _creationCode) - _deployed := create2(0, add(_bytecode, 0x20), mload(_bytecode), 'Wonderland') - } - vm.label(_deployed, _label); - vm.allowCheatcodes(_deployed); - } -} diff --git a/solidity/test/smock/examples/MockAliceGovernor.sol b/solidity/test/smock/examples/MockAliceGovernor.sol deleted file mode 100644 index 759c1a5..0000000 --- a/solidity/test/smock/examples/MockAliceGovernor.sol +++ /dev/null @@ -1,271 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import {Test} from 'forge-std/Test.sol'; -import { - Address, - AliceGovernor, - Checkpoints, - Context, - DoubleEndedQueue, - ECDSA, - EIP712, - ERC165, - IERC1155Receiver, - IERC165, - IERC6372, - IERC721Receiver, - IWonderGovernor, - IWonderVotes, - Nonces, - SafeCast, - SignatureChecker, - Time, - WonderGovernor, - WonderVotes -} from 'solidity/examples/AliceGovernor.sol'; -import 'solidity/contracts/governance/WonderGovernor.sol'; -import 'solidity/contracts/governance/utils/WonderVotes.sol'; - -contract MockAliceGovernor is AliceGovernor, Test { - constructor(address _wonderToken) AliceGovernor(_wonderToken) {} - - /// Mocked State Variables - - function set_votes(WonderVotes _votes) public { - votes = _votes; - } - - function mock_call_votes(WonderVotes _votes) public { - vm.mockCall(address(this), abi.encodeWithSignature('votes()'), abi.encode(_votes)); - } - - function set__countingMode(string memory __countingMode) public { - _countingMode = __countingMode; - } - - function set___proposalTypes(uint8[] memory ___proposalTypes) public { - __proposalTypes = ___proposalTypes; - } - - function set_receipts(uint256 _key0, address _key1, AliceGovernor.BallotReceipt memory _value) public { - receipts[_key0][_key1] = _value; - } - - function mock_call_receipts(uint256 _key0, address _key1, AliceGovernor.BallotReceipt memory _value) public { - vm.mockCall(address(this), abi.encodeWithSignature('receipts(uint256,address)', _key0, _key1), abi.encode(_value)); - } - - function set_proposalTracks(uint256 _key0, AliceGovernor.ProposalTrack memory _value) public { - proposalTracks[_key0] = _value; - } - - function mock_call_proposalTracks(uint256 _key0, AliceGovernor.ProposalTrack memory _value) public { - vm.mockCall(address(this), abi.encodeWithSignature('proposalTracks(uint256)', _key0), abi.encode(_value)); - } - - /// Mocked External Functions - - function mock_call_CLOCK_MODE(string memory _clockMode) public { - vm.mockCall(address(this), abi.encodeWithSignature('CLOCK_MODE()'), abi.encode(_clockMode)); - } - - function mock_call_COUNTING_MODE(string memory _return0) public { - vm.mockCall(address(this), abi.encodeWithSignature('COUNTING_MODE()'), abi.encode(_return0)); - } - - function mock_call_clock(uint48 _return0) public { - vm.mockCall(address(this), abi.encodeWithSignature('clock()'), abi.encode(_return0)); - } - - function mock_call_votingPeriod(uint256 _return0) public { - vm.mockCall(address(this), abi.encodeWithSignature('votingPeriod()'), abi.encode(_return0)); - } - - function mock_call_votingDelay(uint256 _return0) public { - vm.mockCall(address(this), abi.encodeWithSignature('votingDelay()'), abi.encode(_return0)); - } - - function mock_call_quorum(uint256 _timepoint, uint8 _proposalType, uint256 _return0) public { - vm.mockCall( - address(this), abi.encodeWithSignature('quorum(uint256,uint8)', _timepoint, _proposalType), abi.encode(_return0) - ); - } - - function mock_call_hasVoted(uint256 _proposalId, address _account, bool _return0) public { - vm.mockCall( - address(this), abi.encodeWithSignature('hasVoted(uint256,address)', _proposalId, _account), abi.encode(_return0) - ); - } - - function mock_call_proposalThreshold(uint8 _proposalType, uint256 _return0) public { - vm.mockCall(address(this), abi.encodeWithSignature('proposalThreshold(uint8)', _proposalType), abi.encode(_return0)); - } - - function mock_call_isValidProposalType(uint8 _proposalType, bool _return0) public { - vm.mockCall( - address(this), abi.encodeWithSignature('isValidProposalType(uint8)', _proposalType), abi.encode(_return0) - ); - } - - /// Mocked Internal Functions - - struct _getVotesOutput { - uint256 _returnParam0; - } - - mapping(bytes32 => _getVotesOutput) private _getVotesOutputs; - bytes32[] private _getVotesInputHashes; - - function mock_call__getVotes( - address _account, - uint8 _proposalType, - uint256 _timepoint, - bytes memory _params, - uint256 _returnParam0 - ) public { - bytes32 _key = keccak256(abi.encode(_account, _proposalType, _timepoint, _params)); - _getVotesOutputs[_key] = _getVotesOutput(_returnParam0); - for (uint256 _i; _i < _getVotesInputHashes.length; ++_i) { - if (_key == _getVotesInputHashes[_i]) { - return; - } - } - _getVotesInputHashes.push(_key); - } - - function _getVotes( - address _account, - uint8 _proposalType, - uint256 _timepoint, - bytes memory _params - ) internal view override returns (uint256 _returnParam0) { - bytes32 _key = keccak256(abi.encode(_account, _proposalType, _timepoint, _params)); - for (uint256 _i; _i < _getVotesInputHashes.length; ++_i) { - if (_key == _getVotesInputHashes[_i]) { - _getVotesOutput memory _output = _getVotesOutputs[_key]; - return (_output._returnParam0); - } - } - return super._getVotes(_account, _proposalType, _timepoint, _params); - } - - struct _isValidProposalTypeOutput { - bool _returnParam0; - } - - mapping(bytes32 => _isValidProposalTypeOutput) private _isValidProposalTypeOutputs; - bytes32[] private _isValidProposalTypeInputHashes; - - function mock_call__isValidProposalType(uint8 _proposalType, bool _returnParam0) public { - bytes32 _key = keccak256(abi.encode(_proposalType)); - _isValidProposalTypeOutputs[_key] = _isValidProposalTypeOutput(_returnParam0); - for (uint256 _i; _i < _isValidProposalTypeInputHashes.length; ++_i) { - if (_key == _isValidProposalTypeInputHashes[_i]) { - return; - } - } - _isValidProposalTypeInputHashes.push(_key); - } - - function _isValidProposalType(uint8 _proposalType) internal view override returns (bool _returnParam0) { - bytes32 _key = keccak256(abi.encode(_proposalType)); - for (uint256 _i; _i < _isValidProposalTypeInputHashes.length; ++_i) { - if (_key == _isValidProposalTypeInputHashes[_i]) { - _isValidProposalTypeOutput memory _output = _isValidProposalTypeOutputs[_key]; - return (_output._returnParam0); - } - } - return super._isValidProposalType(_proposalType); - } - - function mock_call__countVote( - uint256 _proposalId, - address _account, - uint8 _support, - uint256 _weight, - bytes memory _params - ) public { - vm.mockCall( - address(this), - abi.encodeWithSignature( - '_countVote(uint256,address,uint8,uint256,bytes)', _proposalId, _account, _support, _weight, _params - ), - abi.encode() - ); - } - - function _countVote( - uint256 _proposalId, - address _account, - uint8 _support, - uint256 _weight, - bytes memory _params - ) internal override { - (bool _success, bytes memory _data) = address(this).call( - abi.encodeWithSignature( - '_countVote(uint256,address,uint8,uint256,bytes)', _proposalId, _account, _support, _weight, _params - ) - ); - if (_success) return abi.decode(_data, ()); - else return super._countVote(_proposalId, _account, _support, _weight, _params); - } - - struct _quorumReachedOutput { - bool _returnParam0; - } - - mapping(bytes32 => _quorumReachedOutput) private _quorumReachedOutputs; - bytes32[] private _quorumReachedInputHashes; - - function mock_call__quorumReached(uint256 _proposalId, bool _returnParam0) public { - bytes32 _key = keccak256(abi.encode(_proposalId)); - _quorumReachedOutputs[_key] = _quorumReachedOutput(_returnParam0); - for (uint256 _i; _i < _quorumReachedInputHashes.length; ++_i) { - if (_key == _quorumReachedInputHashes[_i]) { - return; - } - } - _quorumReachedInputHashes.push(_key); - } - - function _quorumReached(uint256 _proposalId) internal view override returns (bool _returnParam0) { - bytes32 _key = keccak256(abi.encode(_proposalId)); - for (uint256 _i; _i < _quorumReachedInputHashes.length; ++_i) { - if (_key == _quorumReachedInputHashes[_i]) { - _quorumReachedOutput memory _output = _quorumReachedOutputs[_key]; - return (_output._returnParam0); - } - } - return super._quorumReached(_proposalId); - } - - struct _voteSucceededOutput { - bool _returnParam0; - } - - mapping(bytes32 => _voteSucceededOutput) private _voteSucceededOutputs; - bytes32[] private _voteSucceededInputHashes; - - function mock_call__voteSucceeded(uint256 _proposalId, bool _returnParam0) public { - bytes32 _key = keccak256(abi.encode(_proposalId)); - _voteSucceededOutputs[_key] = _voteSucceededOutput(_returnParam0); - for (uint256 _i; _i < _voteSucceededInputHashes.length; ++_i) { - if (_key == _voteSucceededInputHashes[_i]) { - return; - } - } - _voteSucceededInputHashes.push(_key); - } - - function _voteSucceeded(uint256 _proposalId) internal view override returns (bool _returnParam0) { - bytes32 _key = keccak256(abi.encode(_proposalId)); - for (uint256 _i; _i < _voteSucceededInputHashes.length; ++_i) { - if (_key == _voteSucceededInputHashes[_i]) { - _voteSucceededOutput memory _output = _voteSucceededOutputs[_key]; - return (_output._returnParam0); - } - } - return super._voteSucceeded(_proposalId); - } -} diff --git a/solidity/test/smock/examples/MockRabbitToken.sol b/solidity/test/smock/examples/MockRabbitToken.sol deleted file mode 100644 index ded229b..0000000 --- a/solidity/test/smock/examples/MockRabbitToken.sol +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import {Test} from 'forge-std/Test.sol'; -import {AliceGovernor, EIP712, ERC20, RabbitToken, WonderERC20Votes} from 'solidity/examples/RabbitToken.sol'; -import {WonderERC20Votes} from 'solidity/contracts/token/ERC20/extensions/WonderERC20Votes.sol'; -import {AliceGovernor} from 'solidity/examples/AliceGovernor.sol'; -import {EIP712} from 'node_modules/@openzeppelin/contracts/utils/cryptography/EIP712.sol'; -import {ERC20} from 'node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol'; - -contract MockRabbitToken is RabbitToken, Test { - constructor(AliceGovernor _governor) RabbitToken(_governor) {} - - /// Mocked State Variables - - function set_governor(AliceGovernor _governor) public { - governor = _governor; - } - - function mock_call_governor(AliceGovernor _governor) public { - vm.mockCall(address(this), abi.encodeWithSignature('governor()'), abi.encode(_governor)); - } - - /// Mocked External Functions - - function mock_call_proposalTypes(uint8[] memory _return0) public { - vm.mockCall(address(this), abi.encodeWithSignature('proposalTypes()'), abi.encode(_return0)); - } - - /// Mocked Internal Functions - - struct _getProposalTypesOutput { - uint8[] _returnParam0; - } - - mapping(bytes32 => _getProposalTypesOutput) private _getProposalTypesOutputs; - bytes32[] private _getProposalTypesInputHashes; - - function mock_call__getProposalTypes(uint8[] memory _returnParam0) public { - bytes32 _key = keccak256(abi.encode()); - _getProposalTypesOutputs[_key] = _getProposalTypesOutput(_returnParam0); - for (uint256 _i; _i < _getProposalTypesInputHashes.length; ++_i) { - if (_key == _getProposalTypesInputHashes[_i]) { - return; - } - } - _getProposalTypesInputHashes.push(_key); - } - - function _getProposalTypes() internal view override returns (uint8[] memory _returnParam0) { - bytes32 _key = keccak256(abi.encode()); - for (uint256 _i; _i < _getProposalTypesInputHashes.length; ++_i) { - if (_key == _getProposalTypesInputHashes[_i]) { - _getProposalTypesOutput memory _output = _getProposalTypesOutputs[_key]; - return (_output._returnParam0); - } - } - return super._getProposalTypes(); - } - - struct _maxDelegatesOutput { - uint8 _returnParam0; - } - - mapping(bytes32 => _maxDelegatesOutput) private _maxDelegatesOutputs; - bytes32[] private _maxDelegatesInputHashes; - - function mock_call__maxDelegates(uint8 _returnParam0) public { - bytes32 _key = keccak256(abi.encode()); - _maxDelegatesOutputs[_key] = _maxDelegatesOutput(_returnParam0); - for (uint256 _i; _i < _maxDelegatesInputHashes.length; ++_i) { - if (_key == _maxDelegatesInputHashes[_i]) { - return; - } - } - _maxDelegatesInputHashes.push(_key); - } - - function _maxDelegates() internal view override returns (uint8 _returnParam0) { - bytes32 _key = keccak256(abi.encode()); - for (uint256 _i; _i < _maxDelegatesInputHashes.length; ++_i) { - if (_key == _maxDelegatesInputHashes[_i]) { - _maxDelegatesOutput memory _output = _maxDelegatesOutputs[_key]; - return (_output._returnParam0); - } - } - return super._maxDelegates(); - } - - struct _validProposalTypeOutput { - bool _returnParam0; - } - - mapping(bytes32 => _validProposalTypeOutput) private _validProposalTypeOutputs; - bytes32[] private _validProposalTypeInputHashes; - - function mock_call__validProposalType(uint8 _proposalType, bool _returnParam0) public { - bytes32 _key = keccak256(abi.encode(_proposalType)); - _validProposalTypeOutputs[_key] = _validProposalTypeOutput(_returnParam0); - for (uint256 _i; _i < _validProposalTypeInputHashes.length; ++_i) { - if (_key == _validProposalTypeInputHashes[_i]) { - return; - } - } - _validProposalTypeInputHashes.push(_key); - } - - function _validProposalType(uint8 _proposalType) internal view override returns (bool _returnParam0) { - bytes32 _key = keccak256(abi.encode(_proposalType)); - for (uint256 _i; _i < _validProposalTypeInputHashes.length; ++_i) { - if (_key == _validProposalTypeInputHashes[_i]) { - _validProposalTypeOutput memory _output = _validProposalTypeOutputs[_key]; - return (_output._returnParam0); - } - } - return super._validProposalType(_proposalType); - } - - struct _weightNormalizerOutput { - uint256 _returnParam0; - } - - mapping(bytes32 => _weightNormalizerOutput) private _weightNormalizerOutputs; - bytes32[] private _weightNormalizerInputHashes; - - function mock_call__weightNormalizer(uint256 _returnParam0) public { - bytes32 _key = keccak256(abi.encode()); - _weightNormalizerOutputs[_key] = _weightNormalizerOutput(_returnParam0); - for (uint256 _i; _i < _weightNormalizerInputHashes.length; ++_i) { - if (_key == _weightNormalizerInputHashes[_i]) { - return; - } - } - _weightNormalizerInputHashes.push(_key); - } - - function _weightNormalizer() internal view override returns (uint256 _returnParam0) { - bytes32 _key = keccak256(abi.encode()); - for (uint256 _i; _i < _weightNormalizerInputHashes.length; ++_i) { - if (_key == _weightNormalizerInputHashes[_i]) { - _weightNormalizerOutput memory _output = _weightNormalizerOutputs[_key]; - return (_output._returnParam0); - } - } - return super._weightNormalizer(); - } -} diff --git a/solidity/test/unit/WonderGovernor.t.sol b/solidity/test/unit/WonderGovernor.t.sol index a5badb0..5a09965 100644 --- a/solidity/test/unit/WonderGovernor.t.sol +++ b/solidity/test/unit/WonderGovernor.t.sol @@ -6,11 +6,11 @@ import 'forge-std/Test.sol'; import {WonderGovernor} from 'contracts/governance/WonderGovernor.sol'; import {IWonderGovernor} from 'interfaces/governance/IWonderGovernor.sol'; import {AliceGovernor} from 'examples/AliceGovernor.sol'; -import {MockRabbitToken} from '../smock/examples/MockRabbitToken.sol'; import {IWonderVotes} from 'interfaces/governance/utils/IWonderVotes.sol'; import {IWonderGovernor} from 'interfaces/governance/IWonderGovernor.sol'; import {WonderVotes} from 'contracts/governance/utils/WonderVotes.sol'; +import {RabbitToken} from 'examples/RabbitToken.sol'; import {TestExtended} from '../utils/TestExtended.sol'; contract GovernorForTest is AliceGovernor { @@ -21,18 +21,30 @@ contract GovernorForTest is AliceGovernor { } } +contract WonderVotesForTest is RabbitToken { + constructor(AliceGovernor _governor) RabbitToken(_governor) {} + + function mint(address _account, uint256 _amount) public { + _mint(_account, _amount); + } + + function burn(uint256 _amount) public { + _burn(msg.sender, _amount); + } +} + contract BaseTest is TestExtended { address deployer = makeAddr('deployer'); address hatter = makeAddr('hatter'); address cat = makeAddr('cat'); IWonderGovernor governor; - MockRabbitToken rabbit; + RabbitToken rabbit; - function _mockGetPastVotes(address _account, uint8 _proposalType, uint256 _timePoint, uint256 _votes) internal { + function _mockgetSnapshotVotes(address _account, uint8 _proposalType, uint256 _timePoint, uint256 _votes) internal { vm.mockCall( address(rabbit), - abi.encodeWithSelector(IWonderVotes.getPastVotes.selector, _account, _proposalType, _timePoint), + abi.encodeWithSelector(IWonderVotes.getSnapshotVotes.selector, _account, _proposalType, _timePoint), abi.encode(_votes) ); } @@ -42,7 +54,7 @@ contract BaseTest is TestExtended { address tokenAddress = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 1); governor = new GovernorForTest(tokenAddress); - rabbit = new MockRabbitToken(AliceGovernor(payable(address(governor)))); + rabbit = new WonderVotesForTest(AliceGovernor(payable(address(governor)))); vm.stopPrank(); } @@ -58,7 +70,7 @@ contract BaseTest is TestExtended { vm.assume(_proposalType < governor.proposalTypes().length); vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); address[] memory _targets = new address[](1); _targets[0] = _target; @@ -70,7 +82,7 @@ contract BaseTest is TestExtended { _calldatas[0] = _calldata; vm.prank(hatter); - return governor.propose(_proposalType, _targets, _values, _calldatas, _description); + return governor.propose(_proposalType, _targets, _values, _calldatas, block.number - 1, _description); } } @@ -100,7 +112,7 @@ contract Unit_Propose is BaseTest { vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); // hatter will pass the proposal threshold limit - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); address[] memory _targets = new address[](1); _targets[0] = _target; @@ -129,7 +141,7 @@ contract Unit_Propose is BaseTest { ); vm.prank(hatter); - uint256 _proposeId = governor.propose(_proposalType, _targets, _values, _calldatas, _description); + uint256 _proposeId = governor.propose(_proposalType, _targets, _values, _calldatas, block.number - 1, _description); } function test_Stores_New_Proposal( @@ -143,7 +155,7 @@ contract Unit_Propose is BaseTest { vm.assume(_proposalType < governor.proposalTypes().length); vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); address[] memory _targets = new address[](1); _targets[0] = _target; @@ -155,7 +167,7 @@ contract Unit_Propose is BaseTest { _calldatas[0] = _calldata; vm.prank(hatter); - uint256 _proposeId = governor.propose(_proposalType, _targets, _values, _calldatas, _description); + uint256 _proposeId = governor.propose(_proposalType, _targets, _values, _calldatas, block.number - 1, _description); WonderGovernor.ProposalCore memory _proposal = GovernorForTest(payable(address(governor))).getProposal(_proposeId); @@ -180,7 +192,7 @@ contract Unit_Propose is BaseTest { vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); // hatter will pass the proposal threshold limit - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); address[] memory _targets = new address[](1); _targets[0] = _target; @@ -193,12 +205,12 @@ contract Unit_Propose is BaseTest { vm.expectCall( address(rabbit), - abi.encodeWithSelector(IWonderVotes.getPastVotes.selector, hatter, _proposalType, block.number - 1), + abi.encodeWithSelector(IWonderVotes.getSnapshotVotes.selector, hatter, _proposalType, block.number - 1), 1 ); vm.prank(hatter); - governor.propose(_proposalType, _targets, _values, _calldatas, _description); + governor.propose(_proposalType, _targets, _values, _calldatas, block.number - 1, _description); } function test_Revert_GovernorInvalidProposalType( @@ -209,7 +221,7 @@ contract Unit_Propose is BaseTest { ) public { vm.assume(_proposalType >= governor.proposalTypes().length); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, governor.proposalThreshold(_proposalType)); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, governor.proposalThreshold(_proposalType)); address[] memory _targets = new address[](1); _targets[0] = _target; @@ -223,7 +235,7 @@ contract Unit_Propose is BaseTest { vm.prank(hatter); vm.expectRevert(abi.encodeWithSelector(IWonderGovernor.GovernorInvalidProposalType.selector, _proposalType)); - governor.propose(_proposalType, _targets, _values, _calldatas, ''); + governor.propose(_proposalType, _targets, _values, _calldatas, block.number - 1, ''); } function test_Revert_GovernorInsufficientProposerVotes( @@ -237,7 +249,7 @@ contract Unit_Propose is BaseTest { uint256 _votesThreshold = governor.proposalThreshold(_proposalType); vm.assume(_proposerVotes < _votesThreshold); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); address[] memory _targets = new address[](1); _targets[0] = _target; @@ -255,7 +267,7 @@ contract Unit_Propose is BaseTest { IWonderGovernor.GovernorInsufficientProposerVotes.selector, hatter, _proposerVotes, _votesThreshold ) ); - governor.propose(_proposalType, _targets, _values, _calldatas, ''); + governor.propose(_proposalType, _targets, _values, _calldatas, block.number - 1, ''); } function test_Revert_GovernorInvalidProposalLength( @@ -266,7 +278,7 @@ contract Unit_Propose is BaseTest { ) public { vm.assume(_proposalType < governor.proposalTypes().length); vm.assume(_targets.length != _values.length || _targets.length != _calldatas.length || _targets.length == 0); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, governor.proposalThreshold(_proposalType)); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, governor.proposalThreshold(_proposalType)); vm.prank(hatter); vm.expectRevert( @@ -275,7 +287,7 @@ contract Unit_Propose is BaseTest { ) ); - governor.propose(_proposalType, _targets, _values, _calldatas, ''); + governor.propose(_proposalType, _targets, _values, _calldatas, block.number - 1, ''); } } @@ -296,8 +308,8 @@ contract Unit_CastVote is BaseTest { vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); vm.assume(_support < 2); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, block.number + governor.votingDelay(), _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, block.number + governor.votingDelay(), _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); @@ -307,7 +319,7 @@ contract Unit_CastVote is BaseTest { emit VoteCast(cat, _proposalId, _support, _voterVotes, ''); vm.prank(cat); - governor.castVote(_proposalId, _support); + governor.castVote(_proposalId, _support, block.number - 1); } function test_Call_GetVotes( @@ -326,19 +338,19 @@ contract Unit_CastVote is BaseTest { vm.assume(_support < 2); uint256 _voteStart = block.number + governor.votingDelay(); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, _voteStart, _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); vm.roll(block.number + governor.votingDelay() + 1); vm.expectCall( - address(rabbit), abi.encodeWithSelector(IWonderVotes.getPastVotes.selector, cat, _proposalType, _voteStart), 1 + address(rabbit), abi.encodeWithSelector(IWonderVotes.getSnapshotVotes.selector, cat, _proposalType, _voteStart), 1 ); vm.prank(cat); - governor.castVote(_proposalId, _support); + governor.castVote(_proposalId, _support, block.number - 1); } function test_Count_VoteFor( @@ -354,15 +366,15 @@ contract Unit_CastVote is BaseTest { vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); uint256 _voteStart = block.number + governor.votingDelay(); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, _voteStart, _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); vm.roll(block.number + governor.votingDelay() + 1); vm.prank(cat); - governor.castVote(_proposalId, 1); + governor.castVote(_proposalId, 1, block.number - 1); (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); @@ -386,15 +398,15 @@ contract Unit_CastVote is BaseTest { vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); uint256 _voteStart = block.number + governor.votingDelay(); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, _voteStart, _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); vm.roll(block.number + governor.votingDelay() + 1); vm.prank(cat); - governor.castVote(_proposalId, 0); + governor.castVote(_proposalId, 0, block.number - 1); (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); @@ -418,15 +430,15 @@ contract Unit_CastVote is BaseTest { vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); uint256 _voteStart = block.number + governor.votingDelay(); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, _voteStart, _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); vm.roll(block.number + governor.votingDelay() + 1); vm.prank(cat); - governor.castVote(_proposalId, 2); + governor.castVote(_proposalId, 2, block.number - 1); (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); @@ -456,8 +468,8 @@ contract Unit_CastVoteWithReason is BaseTest { vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); vm.assume(_support < 2); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, block.number + governor.votingDelay(), _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, block.number + governor.votingDelay(), _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); @@ -467,7 +479,7 @@ contract Unit_CastVoteWithReason is BaseTest { emit VoteCast(cat, _proposalId, _support, _voterVotes, _reason); vm.prank(cat); - governor.castVoteWithReason(_proposalId, _support, _reason); + governor.castVoteWithReason(_proposalId, _support, block.number - 1, _reason); } function test_Call_GetVotes( @@ -487,19 +499,19 @@ contract Unit_CastVoteWithReason is BaseTest { vm.assume(_support < 2); uint256 _voteStart = block.number + governor.votingDelay(); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, _voteStart, _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); vm.roll(block.number + governor.votingDelay() + 1); vm.expectCall( - address(rabbit), abi.encodeWithSelector(IWonderVotes.getPastVotes.selector, cat, _proposalType, _voteStart), 1 + address(rabbit), abi.encodeWithSelector(IWonderVotes.getSnapshotVotes.selector, cat, _proposalType, _voteStart), 1 ); vm.prank(cat); - governor.castVoteWithReason(_proposalId, _support, _reason); + governor.castVoteWithReason(_proposalId, _support, block.number - 1, _reason); } function test_Count_VoteFor( @@ -516,15 +528,15 @@ contract Unit_CastVoteWithReason is BaseTest { vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); uint256 _voteStart = block.number + governor.votingDelay(); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, _voteStart, _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); vm.roll(block.number + governor.votingDelay() + 1); vm.prank(cat); - governor.castVoteWithReason(_proposalId, 1, _reason); + governor.castVoteWithReason(_proposalId, 1, block.number - 1, _reason); (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); @@ -549,15 +561,15 @@ contract Unit_CastVoteWithReason is BaseTest { vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); uint256 _voteStart = block.number + governor.votingDelay(); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, _voteStart, _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); vm.roll(block.number + governor.votingDelay() + 1); vm.prank(cat); - governor.castVoteWithReason(_proposalId, 0, _reason); + governor.castVoteWithReason(_proposalId, 0, block.number - 1, _reason); (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); @@ -582,15 +594,15 @@ contract Unit_CastVoteWithReason is BaseTest { vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType)); uint256 _voteStart = block.number + governor.votingDelay(); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, _voteStart, _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); vm.roll(block.number + governor.votingDelay() + 1); vm.prank(cat); - governor.castVoteWithReason(_proposalId, 2, _reason); + governor.castVoteWithReason(_proposalId, 2, block.number - 1, _reason); (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); @@ -624,8 +636,8 @@ contract Unit_CastVoteWithReasonAndParams is BaseTest { vm.assume(_support < 2); vm.assume(_params.length > 0); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, block.number + governor.votingDelay(), _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, block.number + governor.votingDelay(), _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); @@ -635,7 +647,7 @@ contract Unit_CastVoteWithReasonAndParams is BaseTest { emit VoteCastWithParams(cat, _proposalId, _support, _voterVotes, _reason, _params); vm.prank(cat); - governor.castVoteWithReasonAndParams(_proposalId, _support, _reason, _params); + governor.castVoteWithReasonAndParams(_proposalId, _support, block.number - 1, _reason, _params); } function test_Call_GetVotes( @@ -656,19 +668,19 @@ contract Unit_CastVoteWithReasonAndParams is BaseTest { vm.assume(_params.length > 0); uint256 _voteStart = block.number + governor.votingDelay(); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, _voteStart, _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); vm.roll(block.number + governor.votingDelay() + 1); vm.expectCall( - address(rabbit), abi.encodeWithSelector(IWonderVotes.getPastVotes.selector, cat, _proposalType, _voteStart), 1 + address(rabbit), abi.encodeWithSelector(IWonderVotes.getSnapshotVotes.selector, cat, _proposalType, _voteStart), 1 ); vm.prank(cat); - governor.castVoteWithReasonAndParams(_proposalId, _support, '', _params); + governor.castVoteWithReasonAndParams(_proposalId, _support, block.number - 1, '', _params); } function test_Count_VoteFor( @@ -687,15 +699,15 @@ contract Unit_CastVoteWithReasonAndParams is BaseTest { vm.assume(_params.length > 0); uint256 _voteStart = block.number + governor.votingDelay(); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, _voteStart, _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); vm.roll(block.number + governor.votingDelay() + 1); vm.prank(cat); - governor.castVoteWithReasonAndParams(_proposalId, 1, _reason, _params); + governor.castVoteWithReasonAndParams(_proposalId, 1, block.number - 1, _reason, _params); (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); @@ -722,15 +734,15 @@ contract Unit_CastVoteWithReasonAndParams is BaseTest { vm.assume(_params.length > 0); uint256 _voteStart = block.number + governor.votingDelay(); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, _voteStart, _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); vm.roll(block.number + governor.votingDelay() + 1); vm.prank(cat); - governor.castVoteWithReasonAndParams(_proposalId, 0, _reason, _params); + governor.castVoteWithReasonAndParams(_proposalId, 0, block.number - 1, _reason, _params); (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); @@ -757,15 +769,15 @@ contract Unit_CastVoteWithReasonAndParams is BaseTest { vm.assume(_params.length > 0); uint256 _voteStart = block.number + governor.votingDelay(); - _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes); - _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes); + _mockgetSnapshotVotes(hatter, _proposalType, block.number - 1, _proposerVotes); + _mockgetSnapshotVotes(cat, _proposalType, _voteStart, _voterVotes); uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes); vm.roll(block.number + governor.votingDelay() + 1); vm.prank(cat); - governor.castVoteWithReasonAndParams(_proposalId, 2, _reason, _params); + governor.castVoteWithReasonAndParams(_proposalId, 2, block.number - 1, _reason, _params); (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) = AliceGovernor(payable(address(governor))).proposalTracks(_proposalId); diff --git a/solidity/test/unit/WonderVotes.t.sol b/solidity/test/unit/WonderVotes.t.sol index e82475a..a6f9a1a 100644 --- a/solidity/test/unit/WonderVotes.t.sol +++ b/solidity/test/unit/WonderVotes.t.sol @@ -8,8 +8,8 @@ import {IWonderGovernor} from 'interfaces/governance/IWonderGovernor.sol'; import {IWonderVotes} from 'interfaces/governance/utils/IWonderVotes.sol'; import {IWonderGovernor} from 'interfaces/governance/IWonderGovernor.sol'; import {WonderVotes} from 'contracts/governance/utils/WonderVotes.sol'; +import {AliceGovernor} from 'examples/AliceGovernor.sol'; 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'; @@ -31,15 +31,15 @@ contract BaseTest is TestExtended { address hatter = makeAddr('hatter'); address cat = makeAddr('cat'); - MockAliceGovernor governor; + AliceGovernor governor; RabbitToken rabbitToken; event DelegateVotesChanged(address indexed delegate, uint8 proposalType, uint256 previousVotes, uint256 newVotes); - function _mockGetPastVotes(address _account, uint8 _proposalType, uint256 _timePoint, uint256 _votes) internal { + function _mockgetSnapshotVotes(address _account, uint8 _proposalType, uint256 _timePoint, uint256 _votes) internal { vm.mockCall( address(rabbitToken), - abi.encodeWithSelector(IWonderVotes.getPastVotes.selector, _account, _proposalType, _timePoint), + abi.encodeWithSelector(IWonderVotes.getSnapshotVotes.selector, _account, _proposalType, _timePoint), abi.encode(_votes) ); } @@ -48,7 +48,7 @@ contract BaseTest is TestExtended { vm.startPrank(deployer); address tokenAddress = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 1); - governor = new MockAliceGovernor(tokenAddress); + governor = new AliceGovernor(tokenAddress); rabbitToken = new WonderVotesForTest(AliceGovernor(payable(address(governor)))); vm.stopPrank(); @@ -688,7 +688,7 @@ contract Unit_TransferVotes is BaseTest { } } -contract Unit_GetPastVotes is BaseTest { +contract Unit_getSnapshotVotes is BaseTest { uint8[] internal _proposalTypes; // 2 delegates 50% each @@ -718,7 +718,7 @@ contract Unit_GetPastVotes is BaseTest { } // Simple delegation - function test_GetPastVotes_After_Mint(uint128 _previousBalance, uint128 _addBalance) public { + function test_getSnapshotVotes_After_Mint(uint128 _previousBalance, uint128 _addBalance) public { WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance); vm.roll(block.number + 1); @@ -726,14 +726,17 @@ contract Unit_GetPastVotes is BaseTest { vm.roll(block.number + 1); for (uint256 i = 0; i < _proposalTypes.length; i++) { - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), _previousBalance); assertEq( - rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), uint256(_previousBalance) + _addBalance + rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 2, block.number - 2), _previousBalance + ); + assertEq( + rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 1, block.number - 1), + uint256(_previousBalance) + _addBalance ); } } - function test_GetPastVotes_After_Burn(uint128 _previousBalance, uint128 _subsBalance) public { + function test_getSnapshotVotes_After_Burn(uint128 _previousBalance, uint128 _subsBalance) public { vm.assume(_previousBalance >= _subsBalance); WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance); @@ -744,12 +747,17 @@ contract Unit_GetPastVotes is BaseTest { vm.roll(block.number + 1); for (uint256 i = 0; i < _proposalTypes.length; i++) { - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), _previousBalance); - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), _previousBalance - _subsBalance); + assertEq( + rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 2, block.number - 2), _previousBalance + ); + assertEq( + rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 1, block.number - 1), + _previousBalance - _subsBalance + ); } } - function test_GetPastVotes_After_Transfer(uint128 _previousBalance, uint128 _addBalance) public { + function test_getSnapshotVotes_After_Transfer(uint128 _previousBalance, uint128 _addBalance) public { WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance); WonderVotesForTest(address(rabbitToken)).mint(cat, _addBalance); @@ -760,13 +768,16 @@ contract Unit_GetPastVotes is BaseTest { vm.roll(block.number + 1); for (uint256 i = 0; i < _proposalTypes.length; i++) { - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), _previousBalance); assertEq( - rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), uint256(_previousBalance) + _addBalance + rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 2, block.number - 2), _previousBalance + ); + assertEq( + rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 1, block.number - 1), + uint256(_previousBalance) + _addBalance ); - assertEq(rabbitToken.getPastVotes(cat, _proposalTypes[i], block.number - 2), _addBalance); - assertEq(rabbitToken.getPastVotes(cat, _proposalTypes[i], block.number - 1), 0); + assertEq(rabbitToken.getSnapshotVotes(cat, _proposalTypes[i], block.number - 2, block.number - 2), _addBalance); + assertEq(rabbitToken.getSnapshotVotes(cat, _proposalTypes[i], block.number - 1, block.number - 1), 0); } } @@ -783,7 +794,7 @@ contract Unit_GetPastVotes is BaseTest { } // Smart Delegation - function test_GetPastVotes_After_Mint_SmartDelegation(uint128 _previousBalance, uint128 _addBalance) public { + function test_getSnapshotVotes_After_Mint_SmartDelegation(uint128 _previousBalance, uint128 _addBalance) public { _smartDelegate(); WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance); @@ -793,18 +804,21 @@ contract Unit_GetPastVotes is BaseTest { vm.roll(block.number + 1); for (uint256 i = 0; i < _proposalTypes.length; i++) { - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0); + assertEq(rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 2, block.number - 2), 0); + assertEq(rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 2, block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 2), _previousBalance); assertEq( - rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 1), + rabbitToken.getSnapshotVotes(_delegates[i], _proposalTypes[i], block.number - 2, block.number - 2), + _previousBalance + ); + assertEq( + rabbitToken.getSnapshotVotes(_delegates[i], _proposalTypes[i], block.number - 1, block.number - 1), uint256(_previousBalance) + _addBalance ); } } - function test_GetPastVotes_After_Burn_SmartDelegation(uint128 _previousBalance, uint128 _subsBalance) public { + function test_getSnapshotVotes_After_Burn_SmartDelegation(uint128 _previousBalance, uint128 _subsBalance) public { vm.assume(_previousBalance >= _subsBalance); _smartDelegate(); @@ -817,26 +831,34 @@ contract Unit_GetPastVotes is BaseTest { vm.roll(block.number + 1); for (uint256 i = 0; i < _proposalTypes.length; i++) { - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), 0); + assertEq(rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 2, block.number - 2), 0); + assertEq(rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 1, block.number - 1), 0); - assertEq(rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 2), _previousBalance); assertEq( - rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 1), _previousBalance - _subsBalance + rabbitToken.getSnapshotVotes(_delegates[i], _proposalTypes[i], block.number - 2, block.number - 2), + _previousBalance + ); + assertEq( + rabbitToken.getSnapshotVotes(_delegates[i], _proposalTypes[i], block.number - 1, block.number - 1), + _previousBalance - _subsBalance ); } for (uint256 i = 0; i < _proposalTypes.length; i++) { for (uint256 j = 0; j < _delegates.length; j++) { if (j != i) { - assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 1), 0); + assertEq( + rabbitToken.getSnapshotVotes(_delegates[j], _proposalTypes[i], block.number - 2, block.number - 2), 0 + ); + assertEq( + rabbitToken.getSnapshotVotes(_delegates[j], _proposalTypes[i], block.number - 1, block.number - 1), 0 + ); } } } } - function test_GetPastVotes_After_Transfer_SmartDelegation(uint128 _previousBalance, uint128 _addBalance) public { + function test_getSnapshotVotes_After_Transfer_SmartDelegation(uint128 _previousBalance, uint128 _addBalance) public { _smartDelegate(); WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance); @@ -849,24 +871,31 @@ contract Unit_GetPastVotes is BaseTest { vm.roll(block.number + 1); for (uint256 i = 0; i < _proposalTypes.length; i++) { - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), 0); + assertEq(rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 2, block.number - 2), 0); + assertEq(rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 1, block.number - 1), 0); - assertEq(rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 2), _previousBalance); assertEq( - rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 1), + rabbitToken.getSnapshotVotes(_delegates[i], _proposalTypes[i], block.number - 2, block.number - 2), + _previousBalance + ); + assertEq( + rabbitToken.getSnapshotVotes(_delegates[i], _proposalTypes[i], block.number - 1, block.number - 1), uint256(_previousBalance) + _addBalance ); - assertEq(rabbitToken.getPastVotes(cat, _proposalTypes[i], block.number - 2), _addBalance); - assertEq(rabbitToken.getPastVotes(cat, _proposalTypes[i], block.number - 1), 0); + assertEq(rabbitToken.getSnapshotVotes(cat, _proposalTypes[i], block.number - 2, block.number - 2), _addBalance); + assertEq(rabbitToken.getSnapshotVotes(cat, _proposalTypes[i], block.number - 1, block.number - 1), 0); } for (uint256 i = 0; i < _proposalTypes.length; i++) { for (uint256 j = 0; j < _delegates.length; j++) { if (j != i) { - assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 1), 0); + assertEq( + rabbitToken.getSnapshotVotes(_delegates[j], _proposalTypes[i], block.number - 2, block.number - 2), 0 + ); + assertEq( + rabbitToken.getSnapshotVotes(_delegates[j], _proposalTypes[i], block.number - 1, block.number - 1), 0 + ); } } } @@ -891,7 +920,10 @@ contract Unit_GetPastVotes is BaseTest { } // Smart and partial delegation - function test_GetPastVotes_After_Mint_SmartAndPartialDelegation(uint128 _previousBalance, uint128 _addBalance) public { + function test_getSnapshotVotes_After_Mint_SmartAndPartialDelegation( + uint128 _previousBalance, + uint128 _addBalance + ) public { _smartAndPartialDelegate(); WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance); @@ -901,24 +933,30 @@ contract Unit_GetPastVotes is BaseTest { vm.roll(block.number + 1); for (uint256 i = 0; i < _proposalTypes.length; i++) { - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), 0); + assertEq(rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 2, block.number - 2), 0); + assertEq(rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 1, block.number - 1), 0); assertApproxEqAbs( - rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 2), _previousBalance / 2, 1, '' + rabbitToken.getSnapshotVotes(_delegates[i], _proposalTypes[i], block.number - 2, block.number - 2), + _previousBalance / 2, + 1, + '' ); assertApproxEqAbs( - rabbitToken.getPastVotes(_delegates2[i], _proposalTypes[i], block.number - 2), _previousBalance / 2, 1, '' + rabbitToken.getSnapshotVotes(_delegates2[i], _proposalTypes[i], block.number - 2, block.number - 2), + _previousBalance / 2, + 1, + '' ); assertApproxEqAbs( - rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 1), + rabbitToken.getSnapshotVotes(_delegates[i], _proposalTypes[i], block.number - 1, block.number - 1), (uint256(_previousBalance) + _addBalance) / 2, 1, '' ); assertApproxEqAbs( - rabbitToken.getPastVotes(_delegates2[i], _proposalTypes[i], block.number - 1), + rabbitToken.getSnapshotVotes(_delegates2[i], _proposalTypes[i], block.number - 1, block.number - 1), (uint256(_previousBalance) + _addBalance) / 2, 1, '' @@ -928,17 +966,25 @@ contract Unit_GetPastVotes is BaseTest { for (uint256 i = 0; i < _proposalTypes.length; i++) { for (uint256 j = 0; j < _delegates.length; j++) { if (j != i) { - assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 1), 0); - - assertEq(rabbitToken.getPastVotes(_delegates2[j], _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(_delegates2[j], _proposalTypes[i], block.number - 1), 0); + assertEq( + rabbitToken.getSnapshotVotes(_delegates[j], _proposalTypes[i], block.number - 2, block.number - 2), 0 + ); + assertEq( + rabbitToken.getSnapshotVotes(_delegates[j], _proposalTypes[i], block.number - 1, block.number - 1), 0 + ); + + assertEq( + rabbitToken.getSnapshotVotes(_delegates2[j], _proposalTypes[i], block.number - 2, block.number - 2), 0 + ); + assertEq( + rabbitToken.getSnapshotVotes(_delegates2[j], _proposalTypes[i], block.number - 1, block.number - 1), 0 + ); } } } } - function test_GetPastVotes_After_Burn_SmartAndPartialDelegation( + function test_getSnapshotVotes_After_Burn_SmartAndPartialDelegation( uint128 _previousBalance, uint128 _subsBalance ) public { @@ -954,24 +1000,30 @@ contract Unit_GetPastVotes is BaseTest { vm.roll(block.number + 1); for (uint256 i = 0; i < _proposalTypes.length; i++) { - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), 0); + assertEq(rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 2, block.number - 2), 0); + assertEq(rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 1, block.number - 1), 0); assertApproxEqAbs( - rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 2), _previousBalance / 2, 1, '' + rabbitToken.getSnapshotVotes(_delegates[i], _proposalTypes[i], block.number - 2, block.number - 2), + _previousBalance / 2, + 1, + '' ); assertApproxEqAbs( - rabbitToken.getPastVotes(_delegates2[i], _proposalTypes[i], block.number - 2), _previousBalance / 2, 1, '' + rabbitToken.getSnapshotVotes(_delegates2[i], _proposalTypes[i], block.number - 2, block.number - 2), + _previousBalance / 2, + 1, + '' ); assertApproxEqAbs( - rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 1), + rabbitToken.getSnapshotVotes(_delegates[i], _proposalTypes[i], block.number - 1, block.number - 1), (uint256(_previousBalance) - _subsBalance) / 2, 1, '' ); assertApproxEqAbs( - rabbitToken.getPastVotes(_delegates2[i], _proposalTypes[i], block.number - 1), + rabbitToken.getSnapshotVotes(_delegates2[i], _proposalTypes[i], block.number - 1, block.number - 1), (uint256(_previousBalance) - _subsBalance) / 2, 1, '' @@ -981,17 +1033,25 @@ contract Unit_GetPastVotes is BaseTest { for (uint256 i = 0; i < _proposalTypes.length; i++) { for (uint256 j = 0; j < _delegates.length; j++) { if (j != i) { - assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 1), 0); - - assertEq(rabbitToken.getPastVotes(_delegates2[j], _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(_delegates2[j], _proposalTypes[i], block.number - 1), 0); + assertEq( + rabbitToken.getSnapshotVotes(_delegates[j], _proposalTypes[i], block.number - 2, block.number - 2), 0 + ); + assertEq( + rabbitToken.getSnapshotVotes(_delegates[j], _proposalTypes[i], block.number - 1, block.number - 1), 0 + ); + + assertEq( + rabbitToken.getSnapshotVotes(_delegates2[j], _proposalTypes[i], block.number - 2, block.number - 2), 0 + ); + assertEq( + rabbitToken.getSnapshotVotes(_delegates2[j], _proposalTypes[i], block.number - 1, block.number - 1), 0 + ); } } } } - function test_GetPastVotes_After_Transfer_SmartAndPartialDelegation( + function test_getSnapshotVotes_After_Transfer_SmartAndPartialDelegation( uint128 _previousBalance, uint128 _addBalance ) public { @@ -1007,41 +1067,55 @@ contract Unit_GetPastVotes is BaseTest { vm.roll(block.number + 1); for (uint256 i = 0; i < _proposalTypes.length; i++) { - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), 0); + assertEq(rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 2, block.number - 2), 0); + assertEq(rabbitToken.getSnapshotVotes(hatter, _proposalTypes[i], block.number - 1, block.number - 1), 0); assertApproxEqAbs( - rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 2), _previousBalance / 2, 1, '' + rabbitToken.getSnapshotVotes(_delegates[i], _proposalTypes[i], block.number - 2, block.number - 2), + _previousBalance / 2, + 1, + '' ); assertApproxEqAbs( - rabbitToken.getPastVotes(_delegates2[i], _proposalTypes[i], block.number - 2), _previousBalance / 2, 1, '' + rabbitToken.getSnapshotVotes(_delegates2[i], _proposalTypes[i], block.number - 2, block.number - 2), + _previousBalance / 2, + 1, + '' ); assertApproxEqAbs( - rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 1), + rabbitToken.getSnapshotVotes(_delegates[i], _proposalTypes[i], block.number - 1, block.number - 1), (uint256(_previousBalance) + _addBalance) / 2, 1, '' ); assertApproxEqAbs( - rabbitToken.getPastVotes(_delegates2[i], _proposalTypes[i], block.number - 1), + rabbitToken.getSnapshotVotes(_delegates2[i], _proposalTypes[i], block.number - 1, block.number - 1), (uint256(_previousBalance) + _addBalance) / 2, 1, '' ); - assertEq(rabbitToken.getPastVotes(cat, _proposalTypes[i], block.number - 2), _addBalance); - assertEq(rabbitToken.getPastVotes(cat, _proposalTypes[i], block.number - 1), 0); + assertEq(rabbitToken.getSnapshotVotes(cat, _proposalTypes[i], block.number - 2, block.number - 2), _addBalance); + assertEq(rabbitToken.getSnapshotVotes(cat, _proposalTypes[i], block.number - 1, block.number - 1), 0); } for (uint256 i = 0; i < _proposalTypes.length; i++) { for (uint256 j = 0; j < _delegates.length; j++) { if (j != i) { - assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 1), 0); - - assertEq(rabbitToken.getPastVotes(_delegates2[j], _proposalTypes[i], block.number - 2), 0); - assertEq(rabbitToken.getPastVotes(_delegates2[j], _proposalTypes[i], block.number - 1), 0); + assertEq( + rabbitToken.getSnapshotVotes(_delegates[j], _proposalTypes[i], block.number - 2, block.number - 2), 0 + ); + assertEq( + rabbitToken.getSnapshotVotes(_delegates[j], _proposalTypes[i], block.number - 1, block.number - 1), 0 + ); + + assertEq( + rabbitToken.getSnapshotVotes(_delegates2[j], _proposalTypes[i], block.number - 2, block.number - 2), 0 + ); + assertEq( + rabbitToken.getSnapshotVotes(_delegates2[j], _proposalTypes[i], block.number - 1, block.number - 1), 0 + ); } } }