Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

test: add wonder votes unit tests #7

Merged
merged 13 commits into from
Dec 27, 2023
Merged
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"package.json": "sort-package-json"
},
"dependencies": {
"@openzeppelin/contracts": "^5.0.1",
"@defi-wonderland/smock-foundry": "1.0.6",
"@openzeppelin/contracts": "5.0.1",
"isolmate": "github:defi-wonderland/isolmate#59e1804"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ isolmate/=node_modules/isolmate/src

contracts/=solidity/contracts
interfaces/=solidity/interfaces
examples/=solidity/examples
test/=solidity/test
2 changes: 1 addition & 1 deletion solidity/contracts/governance/WonderGovernor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ abstract contract WonderGovernor is
*/
modifier validProposalType(uint8 proposalType) {
if (!_isValidProposalType(proposalType)) {
revert InvalidProposalType(proposalType);
revert GovernorInvalidProposalType(proposalType);
}
_;
}
Expand Down
14 changes: 7 additions & 7 deletions solidity/examples/AliceGovernor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import 'contracts/governance/utils/WonderVotes.sol';
contract AliceGovernor is WonderGovernor {
WonderVotes public votes;
string internal _countingMode = 'support=bravo&quorum=bravo';
uint8[] internal __proposalTypes = [1, 2, 3];
uint8[] internal __proposalTypes = [0, 1, 2, 3];

mapping(uint256 proposalId => mapping(address => Receipt)) public receipts;
mapping(uint256 proposalId => mapping(address => BallotReceipt)) public receipts;
mapping(uint256 proposalId => ProposalTrack) public proposalTracks;

/// @notice Ballot receipt record for a voter
struct Receipt {
struct BallotReceipt {
/// @notice Whether or not a vote has been cast
bool hasVoted;
/// @notice 0 = Against, 1 = For, 2 = Abstain
Expand Down Expand Up @@ -95,11 +95,11 @@ contract AliceGovernor is WonderGovernor {
revert InvalidVoteType(_support);
}

Receipt storage receipt = receipts[_proposalId][_account];
BallotReceipt storage _receipt = receipts[_proposalId][_account];

receipt.hasVoted = true;
receipt.support = _support;
receipt.votes = _weight;
_receipt.hasVoted = true;
_receipt.support = _support;
_receipt.votes = _weight;
}

function hasVoted(uint256 _proposalId, address _account) external view override returns (bool) {
Expand Down
2 changes: 1 addition & 1 deletion solidity/interfaces/governance/IWonderGovernor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ interface IWonderGovernor is IERC165, IERC6372 {
/**
* @dev The proposalType is not supported by the governor.
*/
error InvalidProposalType(uint8 proposalType);
error GovernorInvalidProposalType(uint8 proposalType);

/**
* @dev Emitted when a proposal is created.
Expand Down
20 changes: 20 additions & 0 deletions solidity/test/smock/SmockHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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);
}
}
271 changes: 271 additions & 0 deletions solidity/test/smock/examples/MockAliceGovernor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

Check warning on line 6 in solidity/test/smock/examples/MockAliceGovernor.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

imported name Address is not used
AliceGovernor,
Checkpoints,

Check warning on line 8 in solidity/test/smock/examples/MockAliceGovernor.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

imported name Checkpoints is not used
Context,

Check warning on line 9 in solidity/test/smock/examples/MockAliceGovernor.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

imported name Context is not used
DoubleEndedQueue,

Check warning on line 10 in solidity/test/smock/examples/MockAliceGovernor.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

imported name DoubleEndedQueue is not used
ECDSA,

Check warning on line 11 in solidity/test/smock/examples/MockAliceGovernor.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

imported name ECDSA is not used
EIP712,

Check warning on line 12 in solidity/test/smock/examples/MockAliceGovernor.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

imported name EIP712 is not used
ERC165,

Check warning on line 13 in solidity/test/smock/examples/MockAliceGovernor.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

imported name ERC165 is not used
IERC1155Receiver,

Check warning on line 14 in solidity/test/smock/examples/MockAliceGovernor.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

imported name IERC1155Receiver is not used
IERC165,

Check warning on line 15 in solidity/test/smock/examples/MockAliceGovernor.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

imported name IERC165 is not used
IERC6372,

Check warning on line 16 in solidity/test/smock/examples/MockAliceGovernor.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

imported name IERC6372 is not used
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);
}
}
Loading