44pragma solidity ^ 0.8.0 ;
55
66import "./GovernorVotes.sol " ;
7+ import "../../utils/Checkpoints.sol " ;
8+ import "../../utils/math/SafeCast.sol " ;
79
810/**
911 * @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token and a quorum expressed as a
@@ -12,7 +14,10 @@ import "./GovernorVotes.sol";
1214 * _Available since v4.3._
1315 */
1416abstract contract GovernorVotesQuorumFraction is GovernorVotes {
15- uint256 private _quorumNumerator;
17+ using Checkpoints for Checkpoints.History;
18+
19+ uint256 private _quorumNumerator; // DEPRECATED
20+ Checkpoints.History private _quorumNumeratorHistory;
1621
1722 event QuorumNumeratorUpdated (uint256 oldQuorumNumerator , uint256 newQuorumNumerator );
1823
@@ -31,7 +36,27 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
3136 * @dev Returns the current quorum numerator. See {quorumDenominator}.
3237 */
3338 function quorumNumerator () public view virtual returns (uint256 ) {
34- return _quorumNumerator;
39+ return _quorumNumeratorHistory._checkpoints.length == 0 ? _quorumNumerator : _quorumNumeratorHistory.latest ();
40+ }
41+
42+ /**
43+ * @dev Returns the quorum numerator at a specific block number. See {quorumDenominator}.
44+ */
45+ function quorumNumerator (uint256 blockNumber ) public view virtual returns (uint256 ) {
46+ // If history is empty, fallback to old storage
47+ uint256 length = _quorumNumeratorHistory._checkpoints.length ;
48+ if (length == 0 ) {
49+ return _quorumNumerator;
50+ }
51+
52+ // Optimistic search, check the latest checkpoint
53+ Checkpoints.Checkpoint memory latest = _quorumNumeratorHistory._checkpoints[length - 1 ];
54+ if (latest._blockNumber <= blockNumber) {
55+ return latest._value;
56+ }
57+
58+ // Otherwize, do the binary search
59+ return _quorumNumeratorHistory.getAtBlock (blockNumber);
3560 }
3661
3762 /**
@@ -45,7 +70,7 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
4570 * @dev Returns the quorum for a block number, in terms of number of votes: `supply * numerator / denominator`.
4671 */
4772 function quorum (uint256 blockNumber ) public view virtual override returns (uint256 ) {
48- return (token.getPastTotalSupply (blockNumber) * quorumNumerator ()) / quorumDenominator ();
73+ return (token.getPastTotalSupply (blockNumber) * quorumNumerator (blockNumber )) / quorumDenominator ();
4974 }
5075
5176 /**
@@ -77,8 +102,17 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
77102 "GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator "
78103 );
79104
80- uint256 oldQuorumNumerator = _quorumNumerator;
81- _quorumNumerator = newQuorumNumerator;
105+ uint256 oldQuorumNumerator = quorumNumerator ();
106+
107+ // Make sure we keep track of the original numerator in contracts upgraded from a version without checkpoints.
108+ if (oldQuorumNumerator != 0 && _quorumNumeratorHistory._checkpoints.length == 0 ) {
109+ _quorumNumeratorHistory._checkpoints.push (
110+ Checkpoints.Checkpoint ({_blockNumber: 0 , _value: SafeCast.toUint224 (oldQuorumNumerator)})
111+ );
112+ }
113+
114+ // Set new quorum for future proposals
115+ _quorumNumeratorHistory.push (newQuorumNumerator);
82116
83117 emit QuorumNumeratorUpdated (oldQuorumNumerator, newQuorumNumerator);
84118 }
0 commit comments