// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorPreventLateQuorum.sol) pragma solidity ^0.8.0; import "../Governor.sol"; import "../../utils/math/Math.sol"; /** * @dev A module that ensures there is a minimum voting period after quorum is reached. This prevents a large voter from * swaying a vote and triggering quorum at the last minute, by ensuring there is always time for other voters to react * and try to oppose the decision. * * If a vote causes quorum to be reached, the proposal's voting period may be extended so that it does not end before at * least a given number of blocks have passed (the "vote extension" parameter). This parameter can be set by the * governance executor (e.g. through a governance proposal). * * _Available since v4.5._ */ abstract contract GovernorPreventLateQuorum is Governor { using SafeCast for uint256; using Timers for Timers.BlockNumber; uint64 private _voteExtension; mapping(uint256 => Timers.BlockNumber) private _extendedDeadlines; /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period. event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); /// @dev Emitted when the {lateQuorumVoteExtension} parameter is changed. event LateQuorumVoteExtensionSet(uint64 oldVoteExtension, uint64 newVoteExtension); /** * @dev Initializes the vote extension parameter: the number of blocks that are required to pass since a proposal * reaches quorum until its voting period ends. If necessary the voting period will be extended beyond the one set * at proposal creation. */ constructor(uint64 initialVoteExtension) { _setLateQuorumVoteExtension(initialVoteExtension); } /** * @dev Returns the proposal deadline, which may have been extended beyond that set at proposal creation, if the * proposal reached quorum late in the voting period. See {Governor-proposalDeadline}. */ function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) { return Math.max(super.proposalDeadline(proposalId), _extendedDeadlines[proposalId].getDeadline()); } /** * @dev Casts a vote and detects if it caused quorum to be reached, potentially extending the voting period. See * {Governor-_castVote}. * * May emit a {ProposalExtended} event. */ function _castVote( uint256 proposalId, address account, uint8 support, string memory reason, bytes memory params ) internal virtual override returns (uint256) { uint256 result = super._castVote(proposalId, account, support, reason, params); 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 result; } /** * @dev Returns the current value of the vote extension parameter: the number of blocks that are required to pass * from the time a proposal reaches quorum until its voting period ends. */ function lateQuorumVoteExtension() public view virtual returns (uint64) { return _voteExtension; } /** * @dev Changes the {lateQuorumVoteExtension}. This operation can only be performed by the governance executor, * generally through a governance proposal. * * Emits a {LateQuorumVoteExtensionSet} event. */ function setLateQuorumVoteExtension(uint64 newVoteExtension) public virtual onlyGovernance { _setLateQuorumVoteExtension(newVoteExtension); } /** * @dev Changes the {lateQuorumVoteExtension}. This is an internal function that can be exposed in a public function * like {setLateQuorumVoteExtension} if another access control mechanism is needed. * * Emits a {LateQuorumVoteExtensionSet} event. */ function _setLateQuorumVoteExtension(uint64 newVoteExtension) internal virtual { emit LateQuorumVoteExtensionSet(_voteExtension, newVoteExtension); _voteExtension = newVoteExtension; } }