From bcedb7ec671bc5981fb2b6e5e48a29eb8643f1b2 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 12 Jan 2023 10:41:14 +0100 Subject: [PATCH] add testing of new governor against legacy voting tokens --- .../mocks/token/ERC20VotesLegacyMock.sol | 270 ++++++++++++++++++ contracts/mocks/token/VotesTimestamp.sol | 2 +- test/governance/Governor.test.js | 20 +- .../GovernorCompatibilityBravo.test.js | 34 ++- .../extensions/GovernorComp.test.js | 14 +- .../extensions/GovernorERC721.test.js | 14 +- .../GovernorPreventLateQuorum.test.js | 28 +- .../GovernorTimelockCompound.test.js | 29 +- .../GovernorTimelockControl.test.js | 31 +- .../GovernorVotesQuorumFraction.test.js | 19 +- .../extensions/GovernorWithParams.test.js | 14 +- test/helpers/governance.js | 8 +- 12 files changed, 400 insertions(+), 83 deletions(-) create mode 100644 contracts/mocks/token/ERC20VotesLegacyMock.sol diff --git a/contracts/mocks/token/ERC20VotesLegacyMock.sol b/contracts/mocks/token/ERC20VotesLegacyMock.sol new file mode 100644 index 00000000000..f547a01960d --- /dev/null +++ b/contracts/mocks/token/ERC20VotesLegacyMock.sol @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../../token/ERC20/extensions/ERC20Permit.sol"; +import "../../utils/math/Math.sol"; +import "../../governance/utils/IVotes.sol"; +import "../../utils/math/SafeCast.sol"; +import "../../utils/cryptography/ECDSA.sol"; + +/** + * @dev Copied from the master branch at commit 86de1e8b6c3fa6b4efa4a5435869d2521be0f5f5 + * A reverting `clock()` function is added to please the compiler + */ +abstract contract ERC20VotesLegacyMock is IVotes, ERC20Permit { + struct Checkpoint { + uint32 fromBlock; + uint224 votes; + } + + bytes32 private constant _DELEGATION_TYPEHASH = + keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + + mapping(address => address) private _delegates; + mapping(address => Checkpoint[]) private _checkpoints; + Checkpoint[] private _totalSupplyCheckpoints; + + /** + * @dev added to please the compiler, reverts (like if it was absent) + */ + function clock() public pure override returns (uint256) { + revert(); + } + + /** + * @dev Get the `pos`-th checkpoint for `account`. + */ + function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoint memory) { + return _checkpoints[account][pos]; + } + + /** + * @dev Get number of checkpoints for `account`. + */ + function numCheckpoints(address account) public view virtual returns (uint32) { + return SafeCast.toUint32(_checkpoints[account].length); + } + + /** + * @dev Get the address `account` is currently delegating to. + */ + function delegates(address account) public view virtual override returns (address) { + return _delegates[account]; + } + + /** + * @dev Gets the current votes balance for `account` + */ + function getVotes(address account) public view virtual override returns (uint256) { + uint256 pos = _checkpoints[account].length; + unchecked { + return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes; + } + } + + /** + * @dev Retrieve the number of votes for `account` at the end of `blockNumber`. + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { + require(blockNumber < block.number, "ERC20Votes: block not yet mined"); + return _checkpointsLookup(_checkpoints[account], blockNumber); + } + + /** + * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances. + * It is NOT the sum of all the delegated votes! + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) { + require(blockNumber < block.number, "ERC20Votes: block not yet mined"); + return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); + } + + /** + * @dev Lookup a value in a list of (sorted) checkpoints. + */ + function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) { + // We run a binary search to look for the earliest checkpoint taken after `blockNumber`. + // + // Initially we check if the block is recent to narrow the search range. + // During the loop, the index of the wanted checkpoint remains in the range [low-1, high). + // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant. + // - If the middle checkpoint is after `blockNumber`, we look in [low, mid) + // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high) + // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not + // out of bounds (in which case we're looking too far in the past and the result is 0). + // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is + // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out + // the same. + uint256 length = ckpts.length; + + uint256 low = 0; + uint256 high = length; + + if (length > 5) { + uint256 mid = length - Math.sqrt(length); + if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + unchecked { + return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes; + } + } + + /** + * @dev Delegate votes from the sender to `delegatee`. + */ + function delegate(address delegatee) public virtual override { + _delegate(_msgSender(), delegatee); + } + + /** + * @dev Delegates votes from signer to `delegatee` + */ + function delegateBySig( + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override { + require(block.timestamp <= expiry, "ERC20Votes: signature expired"); + address signer = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))), + v, + r, + s + ); + require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce"); + _delegate(signer, delegatee); + } + + /** + * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1). + */ + function _maxSupply() internal view virtual returns (uint224) { + return type(uint224).max; + } + + /** + * @dev Snapshots the totalSupply after it has been increased. + */ + function _mint(address account, uint256 amount) internal virtual override { + super._mint(account, amount); + require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); + + _writeCheckpoint(_totalSupplyCheckpoints, _add, amount); + } + + /** + * @dev Snapshots the totalSupply after it has been decreased. + */ + function _burn(address account, uint256 amount) internal virtual override { + super._burn(account, amount); + + _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); + } + + /** + * @dev Move voting power when tokens are transferred. + * + * Emits a {IVotes-DelegateVotesChanged} event. + */ + function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual override { + super._afterTokenTransfer(from, to, amount); + + _moveVotingPower(delegates(from), delegates(to), amount); + } + + /** + * @dev Change delegation for `delegator` to `delegatee`. + * + * Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}. + */ + function _delegate(address delegator, address delegatee) internal virtual { + address currentDelegate = delegates(delegator); + uint256 delegatorBalance = balanceOf(delegator); + _delegates[delegator] = delegatee; + + emit DelegateChanged(delegator, currentDelegate, delegatee); + + _moveVotingPower(currentDelegate, delegatee, delegatorBalance); + } + + function _moveVotingPower(address src, address dst, uint256 amount) private { + if (src != dst && amount > 0) { + if (src != address(0)) { + (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); + emit DelegateVotesChanged(src, oldWeight, newWeight); + } + + if (dst != address(0)) { + (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); + emit DelegateVotesChanged(dst, oldWeight, newWeight); + } + } + } + + function _writeCheckpoint( + Checkpoint[] storage ckpts, + function(uint256, uint256) view returns (uint256) op, + uint256 delta + ) private returns (uint256 oldWeight, uint256 newWeight) { + uint256 pos = ckpts.length; + + unchecked { + Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0) : _unsafeAccess(ckpts, pos - 1); + + oldWeight = oldCkpt.votes; + newWeight = op(oldWeight, delta); + + if (pos > 0 && oldCkpt.fromBlock == block.number) { + _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(newWeight); + } else { + ckpts.push( + Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)}) + ); + } + } + } + + function _add(uint256 a, uint256 b) private pure returns (uint256) { + return a + b; + } + + function _subtract(uint256 a, uint256 b) private pure returns (uint256) { + return a - b; + } + + /** + * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. + */ + function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) { + assembly { + mstore(0, ckpts.slot) + result.slot := add(keccak256(0, 0x20), pos) + } + } +} diff --git a/contracts/mocks/token/VotesTimestamp.sol b/contracts/mocks/token/VotesTimestamp.sol index 1d7fcffd04d..d515944a330 100644 --- a/contracts/mocks/token/VotesTimestamp.sol +++ b/contracts/mocks/token/VotesTimestamp.sol @@ -22,4 +22,4 @@ abstract contract ERC721VotesTimestampMock is ERC721Votes { function clock() public view override returns (uint256) { return block.timestamp; } -} \ No newline at end of file +} diff --git a/test/governance/Governor.test.js b/test/governance/Governor.test.js index 0997f2e0883..ad9aedd5b1b 100644 --- a/test/governance/Governor.test.js +++ b/test/governance/Governor.test.js @@ -15,10 +15,11 @@ const CallReceiver = artifacts.require('CallReceiverMock'); const ERC721 = artifacts.require('$ERC721'); const ERC1155 = artifacts.require('$ERC1155'); -const TOKENS = { - blockNumber: artifacts.require('$ERC20Votes'), - timestamp: artifacts.require('$ERC20VotesTimestampMock'), -}; +const TOKENS = [ + { Token: artifacts.require('$ERC20Votes'), mode: 'blockNumber' }, + { Token: artifacts.require('$ERC20VotesTimestampMock'), mode: 'timestamp' }, + { Token: artifacts.require('$ERC20VotesLegacyMock'), mode: 'blockNumber' }, +]; contract('Governor', function (accounts) { const [owner, proposer, voter1, voter2, voter3, voter4] = accounts; @@ -32,8 +33,8 @@ contract('Governor', function (accounts) { const votingPeriod = web3.utils.toBN(16); const value = web3.utils.toWei('1'); - for (const [mode, Token] of Object.entries(TOKENS)) { - describe(`using ${mode} voting token`, function () { + for (const { mode, Token } of TOKENS) { + describe(`using ${Token._json.contractName}`, function () { beforeEach(async function () { this.chainId = await web3.eth.getChainId(); this.token = await Token.new(tokenName, tokenSymbol, tokenName); @@ -81,7 +82,7 @@ contract('Governor', function (accounts) { }); it('clock is correct', async function () { - expect(await this.token.clock()).to.be.bignumber.equal(await clock[mode]().then(web3.utils.toBN)); + expect(await this.mock.clock()).to.be.bignumber.equal(await clock[mode]().then(web3.utils.toBN)); }); it('nominal workflow', async function () { @@ -103,7 +104,10 @@ contract('Governor', function (accounts) { signatures: this.proposal.signatures, calldatas: this.proposal.data, startBlock: web3.utils.toBN(await clockFromReceipt[mode](txPropose.receipt)).add(votingDelay), - endBlock: web3.utils.toBN(await clockFromReceipt[mode](txPropose.receipt)).add(votingDelay).add(votingPeriod), + endBlock: web3.utils + .toBN(await clockFromReceipt[mode](txPropose.receipt)) + .add(votingDelay) + .add(votingPeriod), description: this.proposal.description, }); diff --git a/test/governance/compatibility/GovernorCompatibilityBravo.test.js b/test/governance/compatibility/GovernorCompatibilityBravo.test.js index 481f262dfee..d916fc2389c 100644 --- a/test/governance/compatibility/GovernorCompatibilityBravo.test.js +++ b/test/governance/compatibility/GovernorCompatibilityBravo.test.js @@ -18,10 +18,10 @@ function makeContractAddress(creator, nonce) { ); } -const TOKENS = { - blockNumber: artifacts.require('$ERC20VotesComp'), - timestamp: artifacts.require('$ERC20VotesCompTimestampMock'), -}; +const TOKENS = [ + { Token: artifacts.require('$ERC20VotesComp'), mode: 'blockNumber' }, + { Token: artifacts.require('$ERC20VotesCompTimestampMock'), mode: 'timestamp' }, +]; contract('GovernorCompatibilityBravo', function (accounts) { const [owner, proposer, voter1, voter2, voter3, voter4, other] = accounts; @@ -36,8 +36,8 @@ contract('GovernorCompatibilityBravo', function (accounts) { const proposalThreshold = web3.utils.toWei('10'); const value = web3.utils.toWei('1'); - for (const [mode, Token] of Object.entries(TOKENS)) { - describe(`using ${mode} voting token`, function () { + for (const { mode, Token } of TOKENS) { + describe(`using ${Token._json.contractName}`, function () { beforeEach(async function () { const [deployer] = await web3.eth.getAccounts(); @@ -164,7 +164,10 @@ contract('GovernorCompatibilityBravo', function (accounts) { signatures: this.proposal.signatures.map(() => ''), // this event doesn't contain the proposal detail calldatas: this.proposal.fulldata, startBlock: web3.utils.toBN(await clockFromReceipt[mode](txPropose.receipt)).add(votingDelay), - endBlock: web3.utils.toBN(await clockFromReceipt[mode](txPropose.receipt)).add(votingDelay).add(votingPeriod), + endBlock: web3.utils + .toBN(await clockFromReceipt[mode](txPropose.receipt)) + .add(votingDelay) + .add(votingPeriod), description: this.proposal.description, }); expectEvent(txExecute, 'ProposalExecuted', { proposalId: this.proposal.id }); @@ -207,14 +210,23 @@ contract('GovernorCompatibilityBravo', function (accounts) { await expectEvent.inTransaction(txExecute.tx, this.receiver, 'MockFunctionCalled'); await expectEvent.inTransaction(txExecute.tx, this.receiver, 'MockFunctionCalled'); - await expectEvent.inTransaction(txExecute.tx, this.receiver, 'MockFunctionCalledWithArgs', { a: '17', b: '42' }); - await expectEvent.inTransaction(txExecute.tx, this.receiver, 'MockFunctionCalledWithArgs', { a: '18', b: '43' }); + await expectEvent.inTransaction(txExecute.tx, this.receiver, 'MockFunctionCalledWithArgs', { + a: '17', + b: '42', + }); + await expectEvent.inTransaction(txExecute.tx, this.receiver, 'MockFunctionCalledWithArgs', { + a: '18', + b: '43', + }); }); describe('should revert', function () { describe('on propose', function () { it('if proposal does not meet proposalThreshold', async function () { - await expectRevert(this.helper.propose({ from: other }), 'Governor: proposer votes below proposal threshold'); + await expectRevert( + this.helper.propose({ from: other }), + 'Governor: proposer votes below proposal threshold', + ); }); }); @@ -249,4 +261,4 @@ contract('GovernorCompatibilityBravo', function (accounts) { }); }); } -}); \ No newline at end of file +}); diff --git a/test/governance/extensions/GovernorComp.test.js b/test/governance/extensions/GovernorComp.test.js index 0e5c647fc62..d0e4ce6af61 100644 --- a/test/governance/extensions/GovernorComp.test.js +++ b/test/governance/extensions/GovernorComp.test.js @@ -5,10 +5,10 @@ const { GovernorHelper } = require('../../helpers/governance'); const Governor = artifacts.require('$GovernorCompMock'); const CallReceiver = artifacts.require('CallReceiverMock'); -const TOKENS = { - blockNumber: artifacts.require('$ERC20VotesComp'), - timestamp: artifacts.require('$ERC20VotesCompTimestampMock'), -}; +const TOKENS = [ + { Token: artifacts.require('$ERC20VotesComp'), mode: 'blockNumber' }, + { Token: artifacts.require('$ERC20VotesCompTimestampMock'), mode: 'timestamp' }, +]; contract('GovernorComp', function (accounts) { const [owner, voter1, voter2, voter3, voter4] = accounts; @@ -22,8 +22,8 @@ contract('GovernorComp', function (accounts) { const votingPeriod = web3.utils.toBN(16); const value = web3.utils.toWei('1'); - for (const [mode, Token] of Object.entries(TOKENS)) { - describe(`using ${mode} voting token`, function () { + for (const { mode, Token } of TOKENS) { + describe(`using ${Token._json.contractName}`, function () { beforeEach(async function () { this.owner = owner; this.token = await Token.new(tokenName, tokenSymbol, tokenName); @@ -85,4 +85,4 @@ contract('GovernorComp', function (accounts) { }); }); } -}); \ No newline at end of file +}); diff --git a/test/governance/extensions/GovernorERC721.test.js b/test/governance/extensions/GovernorERC721.test.js index 076d8901b9f..b15b02d4108 100644 --- a/test/governance/extensions/GovernorERC721.test.js +++ b/test/governance/extensions/GovernorERC721.test.js @@ -6,10 +6,10 @@ const { GovernorHelper } = require('../../helpers/governance'); const Governor = artifacts.require('$GovernorVoteMocks'); const CallReceiver = artifacts.require('CallReceiverMock'); -const TOKENS = { - blockNumber: artifacts.require('$ERC721Votes'), - timestamp: artifacts.require('$ERC721VotesTimestampMock'), -}; +const TOKENS = [ + { Token: artifacts.require('$ERC721Votes'), mode: 'blockNumber' }, + { Token: artifacts.require('$ERC721VotesTimestampMock'), mode: 'timestamp' }, +]; contract('GovernorERC721', function (accounts) { const [owner, voter1, voter2, voter3, voter4] = accounts; @@ -27,8 +27,8 @@ contract('GovernorERC721', function (accounts) { const votingPeriod = web3.utils.toBN(16); const value = web3.utils.toWei('1'); - for (const [mode, Token] of Object.entries(TOKENS)) { - describe(`using ${mode} voting token`, function () { + for (const { mode, Token } of TOKENS) { + describe(`using ${Token._json.contractName}`, function () { beforeEach(async function () { this.owner = owner; this.token = await Token.new(tokenName, tokenSymbol, tokenName, '1'); @@ -112,4 +112,4 @@ contract('GovernorERC721', function (accounts) { }); }); } -}); \ No newline at end of file +}); diff --git a/test/governance/extensions/GovernorPreventLateQuorum.test.js b/test/governance/extensions/GovernorPreventLateQuorum.test.js index 1b0b7d964bf..7a0ddfba7b1 100644 --- a/test/governance/extensions/GovernorPreventLateQuorum.test.js +++ b/test/governance/extensions/GovernorPreventLateQuorum.test.js @@ -1,4 +1,4 @@ -const { BN, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { expect } = require('chai'); const Enums = require('../../helpers/enums'); const { GovernorHelper } = require('../../helpers/governance'); @@ -7,10 +7,10 @@ const { clockFromReceipt } = require('../../helpers/time'); const Governor = artifacts.require('$GovernorPreventLateQuorumMock'); const CallReceiver = artifacts.require('CallReceiverMock'); -const TOKENS = { - blockNumber: artifacts.require('$ERC20Votes'), - timestamp: artifacts.require('$ERC20VotesTimestampMock'), -}; +const TOKENS = [ + { Token: artifacts.require('$ERC20Votes'), mode: 'blockNumber' }, + { Token: artifacts.require('$ERC20VotesTimestampMock'), mode: 'timestamp' }, +]; contract('GovernorPreventLateQuorum', function (accounts) { const [owner, proposer, voter1, voter2, voter3, voter4] = accounts; @@ -26,8 +26,8 @@ contract('GovernorPreventLateQuorum', function (accounts) { const quorum = web3.utils.toWei('1'); const value = web3.utils.toWei('1'); - for (const [mode, Token] of Object.entries(TOKENS)) { - describe(`using ${mode} voting token`, function () { + for (const { mode, Token } of TOKENS) { + describe(`using ${Token._json.contractName}`, function () { beforeEach(async function () { this.owner = owner; this.token = await Token.new(tokenName, tokenSymbol, tokenName); @@ -97,7 +97,10 @@ contract('GovernorPreventLateQuorum', function (accounts) { }); const startBlock = web3.utils.toBN(await clockFromReceipt[mode](txPropose.receipt)).add(votingDelay); - const endBlock = web3.utils.toBN(await clockFromReceipt[mode](txPropose.receipt)).add(votingDelay).add(votingPeriod); + const endBlock = web3.utils + .toBN(await clockFromReceipt[mode](txPropose.receipt)) + .add(votingDelay) + .add(votingPeriod); expect(await this.mock.proposalSnapshot(this.proposal.id)).to.be.bignumber.equal(startBlock); expect(await this.mock.proposalDeadline(this.proposal.id)).to.be.bignumber.equal(endBlock); @@ -119,7 +122,10 @@ contract('GovernorPreventLateQuorum', function (accounts) { // compute original schedule const startBlock = web3.utils.toBN(await clockFromReceipt[mode](txPropose.receipt)).add(votingDelay); - const endBlock = web3.utils.toBN(await clockFromReceipt[mode](txPropose.receipt)).add(votingDelay).add(votingPeriod); + const endBlock = web3.utils + .toBN(await clockFromReceipt[mode](txPropose.receipt)) + .add(votingDelay) + .add(votingPeriod); expect(await this.mock.proposalSnapshot(this.proposal.id)).to.be.bignumber.equal(startBlock); expect(await this.mock.proposalDeadline(this.proposal.id)).to.be.bignumber.equal(endBlock); @@ -131,7 +137,9 @@ contract('GovernorPreventLateQuorum', function (accounts) { expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Active); // compute new extended schedule - const extendedDeadline = web3.utils.toBN(await clockFromReceipt[mode](txVote.receipt)).add(lateQuorumVoteExtension); + const extendedDeadline = web3.utils + .toBN(await clockFromReceipt[mode](txVote.receipt)) + .add(lateQuorumVoteExtension); expect(await this.mock.proposalSnapshot(this.proposal.id)).to.be.bignumber.equal(startBlock); expect(await this.mock.proposalDeadline(this.proposal.id)).to.be.bignumber.equal(extendedDeadline); diff --git a/test/governance/extensions/GovernorTimelockCompound.test.js b/test/governance/extensions/GovernorTimelockCompound.test.js index 2255156d221..af515d3ce6d 100644 --- a/test/governance/extensions/GovernorTimelockCompound.test.js +++ b/test/governance/extensions/GovernorTimelockCompound.test.js @@ -19,10 +19,10 @@ function makeContractAddress(creator, nonce) { ); } -const TOKENS = { - blockNumber: artifacts.require('$ERC20Votes'), - timestamp: artifacts.require('$ERC20VotesTimestampMock'), -}; +const TOKENS = [ + { Token: artifacts.require('$ERC20Votes'), mode: 'blockNumber' }, + { Token: artifacts.require('$ERC20VotesTimestampMock'), mode: 'timestamp' }, +]; contract('GovernorTimelockCompound', function (accounts) { const [owner, voter1, voter2, voter3, voter4, other] = accounts; @@ -36,8 +36,8 @@ contract('GovernorTimelockCompound', function (accounts) { const votingPeriod = web3.utils.toBN(16); const value = web3.utils.toWei('1'); - for (const [mode, Token] of Object.entries(TOKENS)) { - describe(`using ${mode} voting token`, function () { + for (const { mode, Token } of TOKENS) { + describe(`using ${Token._json.contractName}`, function () { beforeEach(async function () { const [deployer] = await web3.eth.getAccounts(); @@ -48,7 +48,15 @@ contract('GovernorTimelockCompound', function (accounts) { const predictGovernor = makeContractAddress(deployer, nonce + 1); this.timelock = await Timelock.new(predictGovernor, 2 * 86400); - this.mock = await Governor.new(name, votingDelay, votingPeriod, 0, this.timelock.address, this.token.address, 0); + this.mock = await Governor.new( + name, + votingDelay, + votingPeriod, + 0, + this.timelock.address, + this.token.address, + 0, + ); this.receiver = await CallReceiver.new(); this.helper = new GovernorHelper(this.mock, mode); @@ -134,7 +142,10 @@ contract('GovernorTimelockCompound', function (accounts) { await this.helper.waitForSnapshot(); await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); await this.helper.waitForDeadline(); - await expectRevert(this.helper.queue(), 'GovernorTimelockCompound: identical proposal action already queued'); + await expectRevert( + this.helper.queue(), + 'GovernorTimelockCompound: identical proposal action already queued', + ); await expectRevert(this.helper.execute(), 'GovernorTimelockCompound: proposal not yet queued'); }); }); @@ -338,4 +349,4 @@ contract('GovernorTimelockCompound', function (accounts) { }); }); } -}); \ No newline at end of file +}); diff --git a/test/governance/extensions/GovernorTimelockControl.test.js b/test/governance/extensions/GovernorTimelockControl.test.js index 0f581fc4887..22c804c106c 100644 --- a/test/governance/extensions/GovernorTimelockControl.test.js +++ b/test/governance/extensions/GovernorTimelockControl.test.js @@ -9,10 +9,10 @@ const Timelock = artifacts.require('TimelockController'); const Governor = artifacts.require('$GovernorTimelockControlMock'); const CallReceiver = artifacts.require('CallReceiverMock'); -const TOKENS = { - blockNumber: artifacts.require('$ERC20Votes'), - timestamp: artifacts.require('$ERC20VotesTimestampMock'), -}; +const TOKENS = [ + { Token: artifacts.require('$ERC20Votes'), mode: 'blockNumber' }, + { Token: artifacts.require('$ERC20VotesTimestampMock'), mode: 'timestamp' }, +]; contract('GovernorTimelockControl', function (accounts) { const [owner, voter1, voter2, voter3, voter4, other] = accounts; @@ -31,14 +31,22 @@ contract('GovernorTimelockControl', function (accounts) { const votingPeriod = web3.utils.toBN(16); const value = web3.utils.toWei('1'); - for (const [mode, Token] of Object.entries(TOKENS)) { - describe(`using ${mode} voting token`, function () { + for (const { mode, Token } of TOKENS) { + describe(`using ${Token._json.contractName}`, function () { beforeEach(async function () { const [deployer] = await web3.eth.getAccounts(); this.token = await Token.new(tokenName, tokenSymbol, tokenName); this.timelock = await Timelock.new(3600, [], [], deployer); - this.mock = await Governor.new(name, votingDelay, votingPeriod, 0, this.timelock.address, this.token.address, 0); + this.mock = await Governor.new( + name, + votingDelay, + votingPeriod, + 0, + this.timelock.address, + this.token.address, + 0, + ); this.receiver = await CallReceiver.new(); this.helper = new GovernorHelper(this.mock, mode); @@ -335,7 +343,12 @@ contract('GovernorTimelockControl', function (accounts) { describe('updateTimelock', function () { beforeEach(async function () { - this.newTimelock = await Timelock.new(3600, [this.mock.address], [this.mock.address], constants.ZERO_ADDRESS); + this.newTimelock = await Timelock.new( + 3600, + [this.mock.address], + [this.mock.address], + constants.ZERO_ADDRESS, + ); }); it('is protected', async function () { @@ -396,4 +409,4 @@ contract('GovernorTimelockControl', function (accounts) { }); }); } -}); \ No newline at end of file +}); diff --git a/test/governance/extensions/GovernorVotesQuorumFraction.test.js b/test/governance/extensions/GovernorVotesQuorumFraction.test.js index 8e850be063b..8ee4b9b4f13 100644 --- a/test/governance/extensions/GovernorVotesQuorumFraction.test.js +++ b/test/governance/extensions/GovernorVotesQuorumFraction.test.js @@ -7,10 +7,10 @@ const { clock } = require('../../helpers/time'); const Governor = artifacts.require('$GovernorMock'); const CallReceiver = artifacts.require('CallReceiverMock'); -const TOKENS = { - blockNumber: artifacts.require('$ERC20Votes'), - timestamp: artifacts.require('$ERC20VotesTimestampMock'), -}; +const TOKENS = [ + { Token: artifacts.require('$ERC20Votes'), mode: 'blockNumber' }, + { Token: artifacts.require('$ERC20VotesTimestampMock'), mode: 'timestamp' }, +]; contract('GovernorVotesQuorumFraction', function (accounts) { const [owner, voter1, voter2, voter3, voter4] = accounts; @@ -26,8 +26,8 @@ contract('GovernorVotesQuorumFraction', function (accounts) { const votingPeriod = web3.utils.toBN(16); const value = web3.utils.toWei('1'); - for (const [mode, Token] of Object.entries(TOKENS)) { - describe(`using ${mode} voting token`, function () { + for (const { mode, Token } of TOKENS) { + describe(`using ${Token._json.contractName}`, function () { beforeEach(async function () { this.owner = owner; this.token = await Token.new(tokenName, tokenSymbol, tokenName); @@ -143,9 +143,12 @@ contract('GovernorVotesQuorumFraction', function (accounts) { await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); await this.helper.waitForDeadline(); - await expectRevert(this.helper.execute(), 'GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator'); + await expectRevert( + this.helper.execute(), + 'GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator', + ); }); }); }); } -}); \ No newline at end of file +}); diff --git a/test/governance/extensions/GovernorWithParams.test.js b/test/governance/extensions/GovernorWithParams.test.js index 91e03de8e2d..f6efaa51012 100644 --- a/test/governance/extensions/GovernorWithParams.test.js +++ b/test/governance/extensions/GovernorWithParams.test.js @@ -17,10 +17,10 @@ const rawParams = { const encodedParams = web3.eth.abi.encodeParameters(['uint256', 'string'], Object.values(rawParams)); -const TOKENS = { - blockNumber: artifacts.require('$ERC20Votes'), - timestamp: artifacts.require('$ERC20VotesTimestampMock'), -}; +const TOKENS = [ + { Token: artifacts.require('$ERC20Votes'), mode: 'blockNumber' }, + { Token: artifacts.require('$ERC20VotesTimestampMock'), mode: 'timestamp' }, +]; contract('GovernorWithParams', function (accounts) { const [owner, proposer, voter1, voter2, voter3, voter4] = accounts; @@ -34,8 +34,8 @@ contract('GovernorWithParams', function (accounts) { const votingPeriod = web3.utils.toBN(16); const value = web3.utils.toWei('1'); - for (const [mode, Token] of Object.entries(TOKENS)) { - describe(`using ${mode} voting token`, function () { + for (const { mode, Token } of TOKENS) { + describe(`using ${Token._json.contractName}`, function () { beforeEach(async function () { this.chainId = await web3.eth.getChainId(); this.token = await Token.new(tokenName, tokenSymbol, tokenName); @@ -173,4 +173,4 @@ contract('GovernorWithParams', function (accounts) { }); }); } -}); \ No newline at end of file +}); diff --git a/test/helpers/governance.js b/test/helpers/governance.js index cb27db622ef..42bd6821028 100644 --- a/test/helpers/governance.js +++ b/test/helpers/governance.js @@ -112,16 +112,12 @@ class GovernorHelper { waitForSnapshot(offset = 0) { const proposal = this.currentProposal; - return this.governor - .proposalSnapshot(proposal.id) - .then(timepoint => forward[this.mode](timepoint.addn(offset))); + return this.governor.proposalSnapshot(proposal.id).then(timepoint => forward[this.mode](timepoint.addn(offset))); } waitForDeadline(offset = 0) { const proposal = this.currentProposal; - return this.governor - .proposalDeadline(proposal.id) - .then(timepoint => forward[this.mode](timepoint.addn(offset))); + return this.governor.proposalDeadline(proposal.id).then(timepoint => forward[this.mode](timepoint.addn(offset))); } waitForEta(offset = 0) {