@@ -4,6 +4,8 @@ pragma solidity ^0.8.30;
44// External Dependencies
55import {IGovernor, Governor} from "@openzeppelin/contracts/governance/Governor.sol " ;
66import {Math} from "@openzeppelin/contracts/utils/math/Math.sol " ;
7+ import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol " ;
8+ import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol " ;
79
810/// @title GovernorExtendVetoPeriod
911/// @author [ScopeLift](https://scopelift.co)
@@ -13,16 +15,20 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
1315/// (contracts/governance/extensions/GovernorPreventLateQuorum.sol) with behavior adapted for
1416/// veto-counting.
1517abstract contract GovernorExtendVetoPeriod is Governor {
18+ using Checkpoints for Checkpoints.Trace208;
19+
1620 /// @notice Emitted when a proposal deadline is pushed back due to reaching its minor veto
1721 /// threshold.
18- event ProposalExtended (uint256 indexed proposalId , uint64 extendedDeadline );
22+ event ProposalExtended (uint256 indexed proposalId , uint256 extendedDeadline );
1923
2024 /// @notice Emitted when the {_votingPeriodExtension} parameter is changed.
21- event VotingPeriodExtensionSet (uint64 oldVotingPeriodExtension , uint64 newVotingPeriodExtension );
25+ event VotingPeriodExtensionSet (
26+ uint256 oldVotingPeriodExtension , uint256 newVotingPeriodExtension
27+ );
2228
2329 /// @notice Emitted when the {_votingPeriodExtensionThresholdPct} parameter is changed.
2430 event MinorVetoExtensionThresholdPctSet (
25- uint16 oldVotingPeriodExtensionThresholdPct , uint16 newVotingPeriodExtensionThresholdPct
31+ uint256 oldVotingPeriodExtensionThresholdPct , uint256 newVotingPeriodExtensionThresholdPct
2632 );
2733
2834 /// @dev Reverts when a minor veto extension threshold exceeds the percent denominator.
@@ -31,11 +37,11 @@ abstract contract GovernorExtendVetoPeriod is Governor {
3137
3238 /// @dev The extra time (seconds or blocks, depending on the governor clock mode) that may be
3339 /// added when the minor veto threshold is met.
34- uint48 private _votingPeriodExtension;
40+ Checkpoints.Trace208 private _votingPeriodExtension;
3541
3642 /// @dev The minor threshold in percentage points of veto threshold required to trigger an
3743 /// extension.
38- uint16 private _minorVetoExtensionThresholdPct;
44+ Checkpoints.Trace208 private _minorVetoExtensionThresholdPct;
3945
4046 /// @dev Mapping of proposal ID to extended deadline.
4147 mapping (uint256 proposalId = > uint48 ) private _extendedDeadlines;
@@ -56,16 +62,31 @@ abstract contract GovernorExtendVetoPeriod is Governor {
5662 return Math.max (super .proposalDeadline (_proposalId), _extendedDeadlines[_proposalId]);
5763 }
5864
59- /// @notice Returns the current voting period extension duration applied when the minor veto
60- /// threshold is triggered.
61- function votingPeriodExtension () public view virtual returns (uint48 ) {
62- return _votingPeriodExtension;
65+ /// @notice Returns the latest voting period extension duration.
66+ function votingPeriodExtension () public view virtual returns (uint256 ) {
67+ return _votingPeriodExtension.latest ();
6368 }
6469
65- /// @notice Returns the minor veto threshold expressed in percentage points of the real veto
66- /// threshold that must be reached to extend the voting period.
67- function minorVetoExtensionThresholdPct () public view virtual returns (uint16 ) {
68- return _minorVetoExtensionThresholdPct;
70+ /// @notice Returns the voting period extension duration at a specific timepoint.
71+ /// @dev Use {proposalSnapshot} for snapshot-based semantics.
72+ function votingPeriodExtension (uint256 _timepoint ) public view virtual returns (uint256 ) {
73+ return _optimisticUpperLookupRecent (_votingPeriodExtension, _timepoint);
74+ }
75+
76+ /// @notice Returns the latest minor veto threshold percentage.
77+ function minorVetoExtensionThresholdPct () public view virtual returns (uint256 ) {
78+ return _minorVetoExtensionThresholdPct.latest ();
79+ }
80+
81+ /// @notice Returns the minor veto threshold percentage at a specific timepoint.
82+ /// @dev Use {proposalSnapshot} for snapshot-based semantics.
83+ function minorVetoExtensionThresholdPct (uint256 _timepoint )
84+ public
85+ view
86+ virtual
87+ returns (uint256 )
88+ {
89+ return _optimisticUpperLookupRecent (_minorVetoExtensionThresholdPct, _timepoint);
6990 }
7091
7192 /// @dev Returns the minor veto extension threshold denominator. Defaults to 100, but may be
@@ -113,13 +134,14 @@ abstract contract GovernorExtendVetoPeriod is Governor {
113134 virtual
114135 returns (bool )
115136 {
116- if (_minorVetoExtensionThresholdPct == 0 ) return false ;
137+ uint256 _proposalSnapshot = proposalSnapshot (_proposalId) ;
117138
139+ uint16 _minorThresholdPct = SafeCast.toUint16 (minorVetoExtensionThresholdPct (_proposalSnapshot));
140+ if (_minorThresholdPct == 0 ) return false ;
118141 uint256 _minorThreshold = Math.mulDiv (
119- vetoThreshold (proposalSnapshot (_proposalId)),
120- _minorVetoExtensionThresholdPct,
121- minorVetoExtensionThresholdDenominator ()
142+ vetoThreshold (_proposalSnapshot), _minorThresholdPct, minorVetoExtensionThresholdDenominator ()
122143 );
144+
123145 return proposalVotes (_proposalId) >= _minorThreshold;
124146 }
125147
@@ -135,7 +157,8 @@ abstract contract GovernorExtendVetoPeriod is Governor {
135157 ) {
136158 // Lock in the first extension decision even if it does not lengthen the deadline so later
137159 // tallies cannot attempt to extend the same proposal again.
138- uint48 extendedDeadline = clock () + votingPeriodExtension ();
160+ uint48 extendedDeadline =
161+ clock () + SafeCast.toUint48 (votingPeriodExtension (proposalSnapshot (_proposalId)));
139162
140163 if (extendedDeadline > proposalDeadline (_proposalId)) {
141164 emit ProposalExtended (_proposalId, extendedDeadline);
@@ -149,9 +172,9 @@ abstract contract GovernorExtendVetoPeriod is Governor {
149172 /// event.
150173 /// @param _newVotingPeriodExtension Duration to extend when triggered.
151174 function _setVotingPeriodExtension (uint48 _newVotingPeriodExtension ) internal virtual {
152- emit VotingPeriodExtensionSet (_votingPeriodExtension, _newVotingPeriodExtension);
153-
154- _votingPeriodExtension = _newVotingPeriodExtension ;
175+ ( uint208 oldValue , uint208 newValue ) =
176+ _votingPeriodExtension. push ( clock (), SafeCast. toUint208 (_newVotingPeriodExtension));
177+ emit VotingPeriodExtensionSet (oldValue, newValue) ;
155178 }
156179
157180 /// @dev Internal setter for {_minorVetoExtensionThresholdPct}. Emits a
@@ -166,9 +189,24 @@ abstract contract GovernorExtendVetoPeriod is Governor {
166189 if (_newMinorVetoExtensionThresholdPct > minorVetoExtensionThresholdDenominator ()) {
167190 revert GovernorExtendVetoPeriod_InvalidThreshold (_newMinorVetoExtensionThresholdPct);
168191 }
169- emit MinorVetoExtensionThresholdPctSet (
170- _minorVetoExtensionThresholdPct, _newMinorVetoExtensionThresholdPct
192+ ( uint208 oldValue , uint208 newValue ) = _minorVetoExtensionThresholdPct. push (
193+ clock (), SafeCast. toUint208 ( _newMinorVetoExtensionThresholdPct)
171194 );
172- _minorVetoExtensionThresholdPct = _newMinorVetoExtensionThresholdPct;
195+ emit MinorVetoExtensionThresholdPctSet (oldValue, newValue);
196+ }
197+
198+ /**
199+ * @dev Returns the numerator at a specific timepoint.
200+ */
201+ function _optimisticUpperLookupRecent (Checkpoints.Trace208 storage ckpts , uint256 timepoint )
202+ internal
203+ view
204+ virtual
205+ returns (uint256 )
206+ {
207+ // If trace is empty, key and value are both equal to 0.
208+ // In that case `key <= timepoint` is true, and it is ok to return 0.
209+ (, uint48 key , uint208 value ) = ckpts.latestCheckpoint ();
210+ return key <= timepoint ? value : ckpts.upperLookupRecent (SafeCast.toUint48 (timepoint));
173211 }
174212}
0 commit comments