GovernorPreventLateQuorum extends the proposalDeadline() as expected, but the proposal is not active past the original voteEnd deadline
See original GitHub issueHello, I believe I have found a bug with the GovernorPreventLateQuorum contract. In my Mocha/Chai tests I first created a proposal and saved the default proposalDeadline() block number. Just before the default voting period was over, I cast a vote that exceeded the quorum set, and when I called proposalDeadline() again after this it showed the expected extension of lateQuorumVoteExtension() being added to the default deadline block number. When I mined blocks past the original proposalDeadline() block number and tried to castVote() in the extension period, I kept getting the error: “Governor: vote not currently active”.
💻 Environment Hardhat & Waffle
📝 Details OpenZeppelin npm contracts version 4.4.1 (I copied GovernorPreventLateQuorum to the node_modules directory manually)
🔢 Code to reproduce bug
“Governor: vote not currently active”. This error was due to GovernorPreventLateQuorum’s overridden _castVotes() function calling that of Governor.sol via this line:
uint256 result = super._castVote(proposalId, account, support, reason);
This then triggers the following line in super._castVote():
require(state(proposalId) == ProposalState.Active, "Governor: vote not currently active");
When looking at what causes the failure, state() makes comparisons with the deadline from proposalDeadline(),
uint256 deadline = proposalDeadline(proposalId);
Where proposalDeadline() gets the deadline value from the ProposalCore struct from the mapping _proposals:
return _proposals[proposalId].voteEnd.getDeadline();
This appears to use the default deadline value to end the proposal on, not the updated value set in GovernorPreventLateQuorum as follows:
Timers.BlockNumber storage extendedDeadline = _extendedDeadlines[proposalId];
I am still learning your contracts so I may be missing something here, in which case sorry! But if not, I have found a potential fix for this that now works as expected in my own testing. I copied state() from Governor into GovernorPreventLateQuorum, adding the following check to see if an extension has been created. If so, then use that and if not use the default deadline:
uint256 deadline;
if (_extendedDeadlines[proposalId].getDeadline() > 0) {
deadline = _extendedDeadlines[proposalId].getDeadline();
} else {
deadline = proposalDeadline(proposalId);
}
I combined the two _castVote() functions into one function in GovernorPreventLateQuorum:
function _castVote(
uint256 proposalId,
address account,
uint8 support,
string memory reason
) internal virtual override returns (uint256) {
ProposalCore storage proposal = _proposals[proposalId];
require(state(proposalId) == ProposalState.Active, "Governor: vote not currently active");
uint256 weight = getVotes(account, proposal.voteStart.getDeadline());
_countVote(proposalId, account, support, weight);
emit VoteCast(account, proposalId, support, weight, reason);
Timers.BlockNumber storage extendedDeadline = _extendedDeadlines[proposalId];
if (extendedDeadline.isUnset() && _quorumReached(proposalId)) {
uint64 extendedDeadlineValue = block.number.toUint64() + lateQuorumVoteExtension();
if (extendedDeadlineValue > proposalDeadline(proposalId)) {
emit ProposalExtended(proposalId, extendedDeadlineValue);
}
extendedDeadline.setDeadline(extendedDeadlineValue);
}
return weight;
}
To access _proposals from Governor I then converted the mapping to public:
mapping(uint256 => ProposalCore) public _proposals;
Lastly, I overrode state() in my own governor file, as _castVote() was already overridden by GovernorPreventLateQuorum. These changes allow my tests to pass as expected. I would like to create a pull request with these changes , if you would like me to do so?
Issue Analytics
- State:
- Created 2 years ago
- Comments:6 (5 by maintainers)
I’m pretty sure this is the source of the issue. You can’t mix different versions of the contract. Please install the latest version from npm and run your tests again.
Just wanted to say that it’s ok, and that you don’t have to apologize.
It’s actually valuable for us to know what the process (good or wrong) or our users are, so we can improve documentation.