From 4b152bd8ce0ce8c39c969da5b429e1b79de3526c Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Wed, 4 Aug 2021 18:29:13 -0300 Subject: [PATCH] Improve Governor (#2794) Co-authored-by: Hadrien Croubois --- contracts/governance/Governor.sol | 20 ----- contracts/governance/IGovernor.sol | 36 ++++---- .../GovernorCompatibilityBravo.sol | 84 +++++++++++-------- .../IGovernorCompatibilityBravo.sol | 25 ++++-- .../extensions/GovernorCountingSimple.sol | 4 +- .../extensions/GovernorProposalThreshold.sol | 34 ++++++++ .../extensions/IGovernorTimelock.sol | 14 ++-- contracts/mocks/GovernorCompMock.sol | 2 +- .../mocks/GovernorCompatibilityBravoMock.sol | 10 +-- contracts/mocks/GovernorMock.sol | 2 +- .../mocks/GovernorTimelockCompoundMock.sol | 8 +- .../mocks/GovernorTimelockControlMock.sol | 8 +- test/governance/Governor.test.js | 46 +++++----- .../GovernorCompatibilityBravo.test.js | 45 ++++++++++ .../SupportsInterface.behavior.js | 2 +- 15 files changed, 211 insertions(+), 129 deletions(-) create mode 100644 contracts/governance/extensions/GovernorProposalThreshold.sol diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index 40db01e76..d7a0888c2 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -136,26 +136,6 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { return _proposals[proposalId].voteEnd.getDeadline(); } - /** - * @dev See {IGovernor-votingDelay} - */ - function votingDelay() public view virtual override returns (uint256); - - /** - * @dev See {IGovernor-votingPeriod} - */ - function votingPeriod() public view virtual override returns (uint256); - - /** - * @dev See {IGovernor-quorum} - */ - function quorum(uint256 blockNumber) public view virtual override returns (uint256); - - /** - * @dev See {IGovernor-getVotes} - */ - function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256); - /** * @dev Amount of votes already casted passes the threshold limit. */ diff --git a/contracts/governance/IGovernor.sol b/contracts/governance/IGovernor.sol index 222487771..3cf2b9f6b 100644 --- a/contracts/governance/IGovernor.sol +++ b/contracts/governance/IGovernor.sol @@ -9,7 +9,7 @@ import "../utils/introspection/ERC165.sol"; * * _Available since v4.3._ */ -interface IGovernor is IERC165 { +abstract contract IGovernor is IERC165 { enum ProposalState { Pending, Active, @@ -57,13 +57,13 @@ interface IGovernor is IERC165 { * @notice module:core * @dev Name of the governor instance (used in building the ERC712 domain separator). */ - function name() external view returns (string memory); + function name() public view virtual returns (string memory); /** * @notice module:core * @dev Version of the governor instance (used in building the ERC712 domain separator). Default: "1" */ - function version() external view returns (string memory); + function version() public view virtual returns (string memory); /** * @notice module:voting @@ -82,7 +82,7 @@ interface IGovernor is IERC165 { * JavaScript class. */ // solhint-disable-next-line func-name-mixedcase - function COUNTING_MODE() external pure returns (string memory); + function COUNTING_MODE() public pure virtual returns (string memory); /** * @notice module:core @@ -93,32 +93,32 @@ interface IGovernor is IERC165 { uint256[] calldata values, bytes[] calldata calldatas, bytes32 descriptionHash - ) external pure returns (uint256); + ) public pure virtual returns (uint256); /** * @notice module:core * @dev Current state of a proposal, following Compound's convention */ - function state(uint256 proposalId) external view returns (ProposalState); + function state(uint256 proposalId) public view virtual returns (ProposalState); /** * @notice module:core * @dev block number used to retrieve user's votes and quorum. */ - function proposalSnapshot(uint256 proposalId) external view returns (uint256); + function proposalSnapshot(uint256 proposalId) public view virtual returns (uint256); /** * @notice module:core * @dev timestamp at which votes close. */ - function proposalDeadline(uint256 proposalId) external view returns (uint256); + function proposalDeadline(uint256 proposalId) public view virtual returns (uint256); /** * @notice module:user-config * @dev delay, in number of block, between the proposal is created and the vote starts. This can be increassed to * leave time for users to buy voting power, of delegate it, before the voting of a proposal starts. */ - function votingDelay() external view returns (uint256); + function votingDelay() public view virtual returns (uint256); /** * @notice module:user-config @@ -127,7 +127,7 @@ interface IGovernor is IERC165 { * Note: the {votingDelay} can delay the start of the vote. This must be considered when setting the voting * duration compared to the voting delay. */ - function votingPeriod() external view returns (uint256); + function votingPeriod() public view virtual returns (uint256); /** * @notice module:user-config @@ -136,7 +136,7 @@ interface IGovernor is IERC165 { * Note: The `blockNumber` parameter corresponds to the snaphot used for counting vote. This allows to scale the * quroum depending on values such as the totalSupply of a token at this block (see {ERC20Votes}). */ - function quorum(uint256 blockNumber) external view returns (uint256); + function quorum(uint256 blockNumber) public view virtual returns (uint256); /** * @notice module:reputation @@ -145,13 +145,13 @@ interface IGovernor is IERC165 { * 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, uint256 blockNumber) external view returns (uint256); + function getVotes(address account, uint256 blockNumber) public view virtual returns (uint256); /** * @notice module:voting * @dev Returns weither `account` has casted a vote on `proposalId`. */ - function hasVoted(uint256 proposalId, address account) external view returns (bool); + function hasVoted(uint256 proposalId, address account) public view virtual returns (bool); /** * @dev Create a new proposal. Vote start {IGovernor-votingDelay} blocks after the proposal is created and ends @@ -164,7 +164,7 @@ interface IGovernor is IERC165 { uint256[] memory values, bytes[] memory calldatas, string memory description - ) external returns (uint256 proposalId); + ) public virtual returns (uint256 proposalId); /** * @dev Execute a successful proposal. This requiers the quorum to be reached, the vote to be successful, and the @@ -179,14 +179,14 @@ interface IGovernor is IERC165 { uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash - ) external payable returns (uint256 proposalId); + ) public payable virtual returns (uint256 proposalId); /** * @dev Cast a vote * * Emits a {VoteCast} event. */ - function castVote(uint256 proposalId, uint8 support) external returns (uint256 balance); + function castVote(uint256 proposalId, uint8 support) public virtual returns (uint256 balance); /** * @dev Cast a with a reason @@ -197,7 +197,7 @@ interface IGovernor is IERC165 { uint256 proposalId, uint8 support, string calldata reason - ) external returns (uint256 balance); + ) public virtual returns (uint256 balance); /** * @dev Cast a vote using the user cryptographic signature. @@ -210,5 +210,5 @@ interface IGovernor is IERC165 { uint8 v, bytes32 r, bytes32 s - ) external returns (uint256 balance); + ) public virtual returns (uint256 balance); } diff --git a/contracts/governance/compatibility/GovernorCompatibilityBravo.sol b/contracts/governance/compatibility/GovernorCompatibilityBravo.sol index 5b22bf1aa..78f4e018d 100644 --- a/contracts/governance/compatibility/GovernorCompatibilityBravo.sol +++ b/contracts/governance/compatibility/GovernorCompatibilityBravo.sol @@ -5,6 +5,7 @@ pragma solidity ^0.8.0; import "../../utils/Counters.sol"; import "../../utils/math/SafeCast.sol"; import "../extensions/IGovernorTimelock.sol"; +import "../extensions/GovernorProposalThreshold.sol"; import "../Governor.sol"; import "./IGovernorCompatibilityBravo.sol"; @@ -16,7 +17,12 @@ import "./IGovernorCompatibilityBravo.sol"; * * _Available since v4.3._ */ -abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorCompatibilityBravo, Governor { +abstract contract GovernorCompatibilityBravo is + IGovernorTimelock, + IGovernorCompatibilityBravo, + Governor, + GovernorProposalThreshold +{ using Counters for Counters.Counter; using Timers for Timers.BlockNumber; @@ -41,20 +47,6 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp mapping(uint256 => ProposalDetails) private _proposalDetails; - // public for hooking - function proposalThreshold() public view virtual override returns (uint256); - - // public for hooking - function proposalEta(uint256 proposalId) public view virtual override returns (uint256); - - // public for hooking - function queue( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) public virtual override returns (uint256); - // solhint-disable-next-line func-name-mixedcase function COUNTING_MODE() public pure virtual override returns (string memory) { return "support=bravo&quorum=bravo"; @@ -69,8 +61,9 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp uint256[] memory values, bytes[] memory calldatas, string memory description - ) public virtual override(IGovernor, Governor) returns (uint256) { - return propose(targets, values, new string[](calldatas.length), calldatas, description); + ) public virtual override(IGovernor, Governor, GovernorProposalThreshold) returns (uint256) { + _storeProposal(_msgSender(), targets, values, new string[](calldatas.length), calldatas, description); + return super.propose(targets, values, calldatas, description); } /** @@ -83,14 +76,8 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp bytes[] memory calldatas, string memory description ) public virtual override returns (uint256) { - require( - getVotes(msg.sender, block.number - 1) >= proposalThreshold(), - "GovernorCompatibilityBravo: proposer votes below proposal threshold" - ); - - uint256 proposalId = super.propose(targets, values, _encodeCalldata(signatures, calldatas), description); - _storeProposal(proposalId, _msgSender(), targets, values, signatures, calldatas, description); - return proposalId; + _storeProposal(_msgSender(), targets, values, signatures, calldatas, description); + return propose(targets, values, _encodeCalldata(signatures, calldatas), description); } /** @@ -119,6 +106,22 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp ); } + function cancel(uint256 proposalId) public virtual override { + ProposalDetails storage details = _proposalDetails[proposalId]; + + require( + _msgSender() == details.proposer || getVotes(details.proposer, block.number - 1) < proposalThreshold(), + "GovernorBravo: proposer above threshold" + ); + + _cancel( + details.targets, + details.values, + _encodeCalldata(details.signatures, details.calldatas), + details.descriptionHash + ); + } + /** * @dev Encodes calldatas with optional function signature. */ @@ -132,7 +135,7 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp for (uint256 i = 0; i < signatures.length; ++i) { fullcalldatas[i] = bytes(signatures[i]).length == 0 ? calldatas[i] - : abi.encodePacked(bytes4(keccak256(bytes(signatures[i]))), calldatas[i]); + : abi.encodeWithSignature(signatures[i], calldatas[i]); } return fullcalldatas; @@ -142,7 +145,6 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp * @dev Store proposal metadata for later lookup */ function _storeProposal( - uint256 proposalId, address proposer, address[] memory targets, uint256[] memory values, @@ -150,17 +152,31 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp bytes[] memory calldatas, string memory description ) private { - ProposalDetails storage details = _proposalDetails[proposalId]; + bytes32 descriptionHash = keccak256(bytes(description)); + uint256 proposalId = hashProposal(targets, values, _encodeCalldata(signatures, calldatas), descriptionHash); - details.proposer = proposer; - details.targets = targets; - details.values = values; - details.signatures = signatures; - details.calldatas = calldatas; - details.descriptionHash = keccak256(bytes(description)); + ProposalDetails storage details = _proposalDetails[proposalId]; + if (details.descriptionHash == bytes32(0)) { + details.proposer = proposer; + details.targets = targets; + details.values = values; + details.signatures = signatures; + details.calldatas = calldatas; + details.descriptionHash = descriptionHash; + } } // ==================================================== Views ===================================================== + /** + * @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_. + */ + function proposalThreshold() + public + view + virtual + override(IGovernorCompatibilityBravo, GovernorProposalThreshold) + returns (uint256); + /** * @dev See {IGovernorCompatibilityBravo-proposals}. */ diff --git a/contracts/governance/compatibility/IGovernorCompatibilityBravo.sol b/contracts/governance/compatibility/IGovernorCompatibilityBravo.sol index 92725d4ad..4f4229d3d 100644 --- a/contracts/governance/compatibility/IGovernorCompatibilityBravo.sol +++ b/contracts/governance/compatibility/IGovernorCompatibilityBravo.sol @@ -9,7 +9,7 @@ import "../IGovernor.sol"; * * _Available since v4.3._ */ -interface IGovernorCompatibilityBravo is IGovernor { +abstract contract IGovernorCompatibilityBravo is IGovernor { /** * @dev Proposal structure from Compound Governor Bravo. Not actually used by the compatibility layer, as * {{proposal}} returns a very different structure. @@ -44,14 +44,15 @@ interface IGovernorCompatibilityBravo is IGovernor { /** * @dev Part of the Governor Bravo's interface. */ - function quorumVotes() external view returns (uint256); + function quorumVotes() public view virtual returns (uint256); /** * @dev Part of the Governor Bravo's interface: _"The official record of all proposals ever proposed"_. */ function proposals(uint256) - external + public view + virtual returns ( uint256 id, address proposer, @@ -74,24 +75,30 @@ interface IGovernorCompatibilityBravo is IGovernor { string[] memory signatures, bytes[] memory calldatas, string memory description - ) external returns (uint256); + ) public virtual returns (uint256); /** * @dev Part of the Governor Bravo's interface: _"Queues a proposal of state succeeded"_. */ - function queue(uint256 proposalId) external; + function queue(uint256 proposalId) public virtual; /** * @dev Part of the Governor Bravo's interface: _"Executes a queued proposal if eta has passed"_. */ - function execute(uint256 proposalId) external payable; + function execute(uint256 proposalId) public payable virtual; + + /** + * @dev Cancels a proposal only if sender is the proposer, or proposer delegates dropped below proposal threshold. + */ + function cancel(uint256 proposalId) public virtual; /** * @dev Part of the Governor Bravo's interface: _"Gets actions of a proposal"_. */ function getActions(uint256 proposalId) - external + public view + virtual returns ( address[] memory targets, uint256[] memory values, @@ -102,10 +109,10 @@ interface IGovernorCompatibilityBravo is IGovernor { /** * @dev Part of the Governor Bravo's interface: _"Gets the receipt for a voter on a given proposal"_. */ - function getReceipt(uint256 proposalId, address voter) external view returns (Receipt memory); + function getReceipt(uint256 proposalId, address voter) public view virtual returns (Receipt memory); /** * @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_. */ - function proposalThreshold() external view returns (uint256); + function proposalThreshold() public view virtual returns (uint256); } diff --git a/contracts/governance/extensions/GovernorCountingSimple.sol b/contracts/governance/extensions/GovernorCountingSimple.sol index abd0faee8..0707324cc 100644 --- a/contracts/governance/extensions/GovernorCountingSimple.sol +++ b/contracts/governance/extensions/GovernorCountingSimple.sol @@ -66,9 +66,7 @@ abstract contract GovernorCountingSimple is Governor { function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { ProposalVote storage proposalvote = _proposalVotes[proposalId]; - return - quorum(proposalSnapshot(proposalId)) <= - proposalvote.againstVotes + proposalvote.forVotes + proposalvote.abstainVotes; + return quorum(proposalSnapshot(proposalId)) <= proposalvote.forVotes + proposalvote.abstainVotes; } /** diff --git a/contracts/governance/extensions/GovernorProposalThreshold.sol b/contracts/governance/extensions/GovernorProposalThreshold.sol new file mode 100644 index 000000000..6bf4adfd1 --- /dev/null +++ b/contracts/governance/extensions/GovernorProposalThreshold.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../Governor.sol"; + +/** + * @dev Extension of {Governor} for proposal restriction to token holders with a minimum balance. + * + * _Available since v4.3._ + */ +abstract contract GovernorProposalThreshold is Governor { + /** + * @dev See {IGovernor-propose}. + */ + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override returns (uint256) { + require( + getVotes(msg.sender, block.number - 1) >= proposalThreshold(), + "GovernorCompatibilityBravo: proposer votes below proposal threshold" + ); + + return super.propose(targets, values, calldatas, description); + } + + /** + * @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_. + */ + function proposalThreshold() public view virtual returns (uint256); +} diff --git a/contracts/governance/extensions/IGovernorTimelock.sol b/contracts/governance/extensions/IGovernorTimelock.sol index 5e8f384de..2387ae4e0 100644 --- a/contracts/governance/extensions/IGovernorTimelock.sol +++ b/contracts/governance/extensions/IGovernorTimelock.sol @@ -9,17 +9,17 @@ import "../IGovernor.sol"; * * _Available since v4.3._ */ -interface IGovernorTimelock is IGovernor { +abstract contract IGovernorTimelock is IGovernor { event ProposalQueued(uint256 proposalId, uint256 eta); - function timelock() external view returns (address); + function timelock() public view virtual returns (address); - function proposalEta(uint256 proposalId) external view returns (uint256); + function proposalEta(uint256 proposalId) public view virtual returns (uint256); function queue( - address[] calldata targets, - uint256[] calldata values, - bytes[] calldata calldatas, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, bytes32 descriptionHash - ) external returns (uint256 proposalId); + ) public virtual returns (uint256 proposalId); } diff --git a/contracts/mocks/GovernorCompMock.sol b/contracts/mocks/GovernorCompMock.sol index 400932bf7..a0381f2da 100644 --- a/contracts/mocks/GovernorCompMock.sol +++ b/contracts/mocks/GovernorCompMock.sol @@ -47,7 +47,7 @@ contract GovernorCompMock is Governor, GovernorVotesComp, GovernorCountingSimple public view virtual - override(Governor, GovernorVotesComp) + override(IGovernor, GovernorVotesComp) returns (uint256) { return super.getVotes(account, blockNumber); diff --git a/contracts/mocks/GovernorCompatibilityBravoMock.sol b/contracts/mocks/GovernorCompatibilityBravoMock.sol index c46e0508d..061e51e91 100644 --- a/contracts/mocks/GovernorCompatibilityBravoMock.sol +++ b/contracts/mocks/GovernorCompatibilityBravoMock.sol @@ -34,11 +34,11 @@ contract GovernorCompatibilityBravoMock is GovernorCompatibilityBravo, GovernorT return super.supportsInterface(interfaceId); } - function votingDelay() public view override(IGovernor, Governor) returns (uint256) { + function votingDelay() public view override returns (uint256) { return _votingDelay; } - function votingPeriod() public view override(IGovernor, Governor) returns (uint256) { + function votingPeriod() public view override returns (uint256) { return _votingPeriod; } @@ -46,7 +46,7 @@ contract GovernorCompatibilityBravoMock is GovernorCompatibilityBravo, GovernorT return _proposalThreshold; } - function quorum(uint256) public pure override(IGovernor, Governor) returns (uint256) { + function quorum(uint256) public pure override returns (uint256) { return 0; } @@ -64,7 +64,7 @@ contract GovernorCompatibilityBravoMock is GovernorCompatibilityBravo, GovernorT public view virtual - override(GovernorCompatibilityBravo, GovernorTimelockCompound) + override(IGovernorTimelock, GovernorTimelockCompound) returns (uint256) { return super.proposalEta(proposalId); @@ -84,7 +84,7 @@ contract GovernorCompatibilityBravoMock is GovernorCompatibilityBravo, GovernorT uint256[] memory values, bytes[] memory calldatas, bytes32 salt - ) public virtual override(GovernorCompatibilityBravo, GovernorTimelockCompound) returns (uint256) { + ) public virtual override(IGovernorTimelock, GovernorTimelockCompound) returns (uint256) { return super.queue(targets, values, calldatas, salt); } diff --git a/contracts/mocks/GovernorMock.sol b/contracts/mocks/GovernorMock.sol index 31f9dc242..f31269055 100644 --- a/contracts/mocks/GovernorMock.sol +++ b/contracts/mocks/GovernorMock.sol @@ -44,7 +44,7 @@ contract GovernorMock is Governor, GovernorVotesQuorumFraction, GovernorCounting public view virtual - override(Governor, GovernorVotes) + override(IGovernor, GovernorVotes) returns (uint256) { return super.getVotes(account, blockNumber); diff --git a/contracts/mocks/GovernorTimelockCompoundMock.sol b/contracts/mocks/GovernorTimelockCompoundMock.sol index 211641a4a..b3af0782f 100644 --- a/contracts/mocks/GovernorTimelockCompoundMock.sol +++ b/contracts/mocks/GovernorTimelockCompoundMock.sol @@ -37,18 +37,18 @@ contract GovernorTimelockCompoundMock is GovernorTimelockCompound, GovernorVotes return super.supportsInterface(interfaceId); } - function votingDelay() public view override(IGovernor, Governor) returns (uint256) { + function votingDelay() public view override returns (uint256) { return _votingDelay; } - function votingPeriod() public view override(IGovernor, Governor) returns (uint256) { + function votingPeriod() public view override returns (uint256) { return _votingPeriod; } function quorum(uint256 blockNumber) public view - override(IGovernor, Governor, GovernorVotesQuorumFraction) + override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { return super.quorum(blockNumber); @@ -99,7 +99,7 @@ contract GovernorTimelockCompoundMock is GovernorTimelockCompound, GovernorVotes public view virtual - override(IGovernor, Governor, GovernorVotes) + override(IGovernor, GovernorVotes) returns (uint256) { return super.getVotes(account, blockNumber); diff --git a/contracts/mocks/GovernorTimelockControlMock.sol b/contracts/mocks/GovernorTimelockControlMock.sol index b3ceb8631..d9a19ee31 100644 --- a/contracts/mocks/GovernorTimelockControlMock.sol +++ b/contracts/mocks/GovernorTimelockControlMock.sol @@ -37,18 +37,18 @@ contract GovernorTimelockControlMock is GovernorTimelockControl, GovernorVotesQu return super.supportsInterface(interfaceId); } - function votingDelay() public view override(IGovernor, Governor) returns (uint256) { + function votingDelay() public view override returns (uint256) { return _votingDelay; } - function votingPeriod() public view override(IGovernor, Governor) returns (uint256) { + function votingPeriod() public view override returns (uint256) { return _votingPeriod; } function quorum(uint256 blockNumber) public view - override(IGovernor, Governor, GovernorVotesQuorumFraction) + override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { return super.quorum(blockNumber); @@ -99,7 +99,7 @@ contract GovernorTimelockControlMock is GovernorTimelockControl, GovernorVotesQu public view virtual - override(IGovernor, Governor, GovernorVotes) + override(IGovernor, GovernorVotes) returns (uint256) { return super.getVotes(account, blockNumber); diff --git a/test/governance/Governor.test.js b/test/governance/Governor.test.js index e9c2e75db..863277bcc 100644 --- a/test/governance/Governor.test.js +++ b/test/governance/Governor.test.js @@ -29,7 +29,7 @@ contract('Governor', function (accounts) { beforeEach(async function () { this.owner = owner; this.token = await Token.new(tokenName, tokenSymbol); - this.mock = await Governor.new(name, this.token.address, 4, 16, 0); + this.mock = await Governor.new(name, this.token.address, 4, 16, 10); this.receiver = await CallReceiver.new(); await this.token.mint(owner, tokenSupply); await this.token.delegate(voter1, { from: voter1 }); @@ -72,7 +72,7 @@ contract('Governor', function (accounts) { tokenHolder: owner, voters: [ { voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For, reason: 'This is nice' }, - { voter: voter2, weight: web3.utils.toWei('10'), support: Enums.VoteType.For }, + { voter: voter2, weight: web3.utils.toWei('7'), support: Enums.VoteType.For }, { voter: voter3, weight: web3.utils.toWei('5'), support: Enums.VoteType.Against }, { voter: voter4, weight: web3.utils.toWei('2'), support: Enums.VoteType.Abstain }, ], @@ -194,7 +194,7 @@ contract('Governor', function (accounts) { ], tokenHolder: owner, voters: [ - { voter: this.voter, signature, weight: web3.utils.toWei('1'), support: Enums.VoteType.For }, + { voter: this.voter, signature, weight: web3.utils.toWei('10'), support: Enums.VoteType.For }, ], }; }); @@ -252,8 +252,8 @@ contract('Governor', function (accounts) { ], tokenHolder: owner, voters: [ - { voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For }, - { voter: voter2, weight: web3.utils.toWei('1'), support: Enums.VoteType.Abstain }, + { voter: voter1, weight: web3.utils.toWei('5'), support: Enums.VoteType.For }, + { voter: voter2, weight: web3.utils.toWei('5'), support: Enums.VoteType.Abstain }, ], }; }); @@ -285,7 +285,7 @@ contract('Governor', function (accounts) { ], tokenHolder: owner, voters: [ - { voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For }, + { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For }, ], steps: { execute: { error: 'Governor: call reverted without message' }, @@ -306,7 +306,7 @@ contract('Governor', function (accounts) { ], tokenHolder: owner, voters: [ - { voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For }, + { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For }, ], steps: { execute: { error: 'CallReceiverMock: reverting' }, @@ -329,13 +329,13 @@ contract('Governor', function (accounts) { voters: [ { voter: voter1, - weight: web3.utils.toWei('1'), + weight: web3.utils.toWei('5'), support: Enums.VoteType.For, error: 'Governor: unknown proposal id', }, { voter: voter2, - weight: web3.utils.toWei('1'), + weight: web3.utils.toWei('5'), support: Enums.VoteType.Abstain, error: 'Governor: unknown proposal id', }, @@ -382,8 +382,8 @@ contract('Governor', function (accounts) { ], tokenHolder: owner, voters: [ - { voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For }, - { voter: voter2, weight: web3.utils.toWei('1'), support: Enums.VoteType.Abstain }, + { voter: voter1, weight: web3.utils.toWei('5'), support: Enums.VoteType.For }, + { voter: voter2, weight: web3.utils.toWei('5'), support: Enums.VoteType.Abstain }, ], }; }); @@ -406,7 +406,7 @@ contract('Governor', function (accounts) { voters: [ { voter: voter1, - weight: web3.utils.toWei('1'), + weight: web3.utils.toWei('10'), support: new BN('255'), error: 'GovernorVotingSimple: invalid value for enum VoteType', }, @@ -433,12 +433,12 @@ contract('Governor', function (accounts) { voters: [ { voter: voter1, - weight: web3.utils.toWei('1'), + weight: web3.utils.toWei('5'), support: Enums.VoteType.For, }, { voter: voter1, - weight: web3.utils.toWei('1'), + weight: web3.utils.toWei('5'), support: Enums.VoteType.For, error: 'GovernorVotingSimple: vote already casted', }, @@ -459,7 +459,9 @@ contract('Governor', function (accounts) { ], tokenHolder: owner, voters: [ - { voter: voter1, weight: web3.utils.toWei('0'), support: Enums.VoteType.For }, + { voter: voter1, weight: web3.utils.toWei('5'), support: Enums.VoteType.For }, + { voter: voter2, weight: web3.utils.toWei('4'), support: Enums.VoteType.Abstain }, + { voter: voter3, weight: web3.utils.toWei('10'), support: Enums.VoteType.Against }, ], steps: { execute: { error: 'Governor: proposal not successful' }, @@ -480,7 +482,7 @@ contract('Governor', function (accounts) { ], tokenHolder: owner, voters: [ - { voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.Against }, + { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.Against }, ], steps: { execute: { error: 'Governor: proposal not successful' }, @@ -501,7 +503,7 @@ contract('Governor', function (accounts) { ], tokenHolder: owner, voters: [ - { voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For }, + { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For }, ], steps: { wait: { enable: false }, @@ -593,7 +595,7 @@ contract('Governor', function (accounts) { ], tokenHolder: owner, voters: [ - { voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For }, + { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For }, ], steps: { execute: { enable: false }, @@ -617,7 +619,7 @@ contract('Governor', function (accounts) { ], tokenHolder: owner, voters: [ - { voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For }, + { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For }, ], }; }); @@ -692,7 +694,7 @@ contract('Governor', function (accounts) { ], tokenHolder: owner, voters: [ - { voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For }, + { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For }, ], steps: { wait: { enable: false }, @@ -723,7 +725,7 @@ contract('Governor', function (accounts) { ], tokenHolder: owner, voters: [ - { voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For }, + { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For }, ], steps: { execute: { enable: false }, @@ -753,7 +755,7 @@ contract('Governor', function (accounts) { ], tokenHolder: owner, voters: [ - { voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For }, + { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For }, ], }; }); diff --git a/test/governance/compatibility/GovernorCompatibilityBravo.test.js b/test/governance/compatibility/GovernorCompatibilityBravo.test.js index 5b6b40eb7..2f93908dd 100644 --- a/test/governance/compatibility/GovernorCompatibilityBravo.test.js +++ b/test/governance/compatibility/GovernorCompatibilityBravo.test.js @@ -215,6 +215,51 @@ contract('GovernorCompatibilityBravo', function (accounts) { runGovernorWorkflow(); }); + describe('cancel', function () { + beforeEach(async function () { + this.settings = { + proposal: [ + [ this.receiver.address ], // targets + [ web3.utils.toWei('0') ], // values + [ this.receiver.contract.methods.mockFunction().encodeABI() ], // calldatas + '', // description + ], + proposer, + tokenHolder: owner, + steps: { + wait: { enable: false }, + queue: { enable: false }, + execute: { enable: false }, + }, + }; + }); + + describe('by proposer', function () { + afterEach(async function () { + await this.mock.cancel(this.id, { from: proposer }); + }); + runGovernorWorkflow(); + }); + + describe('if proposer below threshold', function () { + afterEach(async function () { + await this.token.transfer(voter1, web3.utils.toWei('1'), { from: proposer }); + await this.mock.cancel(this.id); + }); + runGovernorWorkflow(); + }); + + describe('not if proposer above threshold', function () { + afterEach(async function () { + await expectRevert( + this.mock.cancel(this.id), + 'GovernorBravo: proposer above threshold', + ); + }); + runGovernorWorkflow(); + }); + }); + describe('with compatibility interface', function () { beforeEach(async function () { this.settings = { diff --git a/test/utils/introspection/SupportsInterface.behavior.js b/test/utils/introspection/SupportsInterface.behavior.js index 8ffe78f24..2027b4057 100644 --- a/test/utils/introspection/SupportsInterface.behavior.js +++ b/test/utils/introspection/SupportsInterface.behavior.js @@ -100,7 +100,7 @@ function shouldSupportInterfaces (interfaces = []) { expect(await this.contractUnderTest.supportsInterface.estimateGas(interfaceId)).to.be.lte(30000); }); - it('claims support', async function () { + it('claims support [skip-on-coverage]', async function () { expect(await this.contractUnderTest.supportsInterface(interfaceId)).to.equal(true); }); });