From f4ac98ca242b87c8b1c7fe3a58ff69be81a944d4 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 17 Nov 2021 22:42:15 +0100 Subject: [PATCH 1/2] improve GovernorTimelockControl override of state() to detect cancel in timelock --- .../extensions/GovernorTimelockControl.sol | 4 +- .../GovernorTimelockControl.test.js | 42 ++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/contracts/governance/extensions/GovernorTimelockControl.sol b/contracts/governance/extensions/GovernorTimelockControl.sol index f7a01c06da2..1279c1a7246 100644 --- a/contracts/governance/extensions/GovernorTimelockControl.sol +++ b/contracts/governance/extensions/GovernorTimelockControl.sol @@ -57,8 +57,10 @@ abstract contract GovernorTimelockControl is IGovernorTimelock, Governor { return status; } else if (_timelock.isOperationDone(queueid)) { return ProposalState.Executed; - } else { + } else if (_timelock.isOperationPending(queueid)) { return ProposalState.Queued; + } else { + return ProposalState.Canceled; } } diff --git a/test/governance/extensions/GovernorTimelockControl.test.js b/test/governance/extensions/GovernorTimelockControl.test.js index fca7bd53558..fe20fb61ceb 100644 --- a/test/governance/extensions/GovernorTimelockControl.test.js +++ b/test/governance/extensions/GovernorTimelockControl.test.js @@ -16,7 +16,7 @@ const Governor = artifacts.require('GovernorTimelockControlMock'); const CallReceiver = artifacts.require('CallReceiverMock'); contract('GovernorTimelockControl', function (accounts) { - const [ voter ] = accounts; + const [ admin, voter ] = accounts; const name = 'OZ-Governor'; // const version = '1'; @@ -33,6 +33,7 @@ contract('GovernorTimelockControl', function (accounts) { this.receiver = await CallReceiver.new(); // normal setup: governor is proposer, everyone is executor, timelock is its own admin await this.timelock.grantRole(await this.timelock.PROPOSER_ROLE(), this.mock.address); + await this.timelock.grantRole(await this.timelock.PROPOSER_ROLE(), admin); await this.timelock.grantRole(await this.timelock.EXECUTOR_ROLE(), constants.ZERO_ADDRESS); await this.timelock.revokeRole(await this.timelock.TIMELOCK_ADMIN_ROLE(), deployer); await this.token.mint(voter, tokenSupply); @@ -320,6 +321,45 @@ contract('GovernorTimelockControl', function (accounts) { runGovernorWorkflow(); }); + describe('cancel on timelock is forwarded in state', function () { + beforeEach(async function () { + this.settings = { + proposal: [ + [ this.receiver.address ], + [ web3.utils.toWei('0') ], + [ this.receiver.contract.methods.mockFunction().encodeABI() ], + '', + ], + voters: [ + { voter: voter, support: Enums.VoteType.For }, + ], + steps: { + queue: { delay: 3600 }, + execute: { enable: false }, + }, + }; + }); + afterEach(async function () { + const timelockid = await this.timelock.hashOperationBatch( + ...this.settings.proposal.slice(0, 3), + '0x0', + this.descriptionHash, + ); + + expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Queued); + + const receipt = await this.timelock.cancel(timelockid, { from: admin }); + expectEvent( + receipt, + 'Cancelled', + { id: timelockid }, + ); + + expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled); + }); + runGovernorWorkflow(); + }); + describe('updateTimelock', function () { beforeEach(async function () { this.newTimelock = await Timelock.new(3600, [], []); From 2b1b84f1435091995c171fe72c9c2a852160cf04 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 18 Nov 2021 09:58:11 +0100 Subject: [PATCH 2/2] add changelog entry --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60cbc780862..a6a8d4e0f40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +* `GovernorTimelockControl`: improve the `state()` function to have it reflect cases where a proposal has been canceled directly on the timelock. ([#2977](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2977)) + ## Unreleased * `Ownable`: add an internal `_transferOwnership(address)`. ([#2568](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2568))