44pragma solidity ^ 0.8.0 ;
55
66import "./GovernorVotesUpgradeable.sol " ;
7+ import "../../utils/CheckpointsUpgradeable.sol " ;
8+ import "../../utils/math/SafeCastUpgradeable.sol " ;
79import "../../proxy/utils/Initializable.sol " ;
810
911/**
@@ -13,7 +15,10 @@ import "../../proxy/utils/Initializable.sol";
1315 * _Available since v4.3._
1416 */
1517abstract contract GovernorVotesQuorumFractionUpgradeable is Initializable , GovernorVotesUpgradeable {
16- uint256 private _quorumNumerator;
18+ using CheckpointsUpgradeable for CheckpointsUpgradeable.History;
19+
20+ uint256 private _quorumNumerator; // DEPRECATED
21+ CheckpointsUpgradeable.History private _quorumNumeratorHistory;
1722
1823 event QuorumNumeratorUpdated (uint256 oldQuorumNumerator , uint256 newQuorumNumerator );
1924
@@ -36,7 +41,27 @@ abstract contract GovernorVotesQuorumFractionUpgradeable is Initializable, Gover
3641 * @dev Returns the current quorum numerator. See {quorumDenominator}.
3742 */
3843 function quorumNumerator () public view virtual returns (uint256 ) {
39- return _quorumNumerator;
44+ return _quorumNumeratorHistory._checkpoints.length == 0 ? _quorumNumerator : _quorumNumeratorHistory.latest ();
45+ }
46+
47+ /**
48+ * @dev Returns the quorum numerator at a specific block number. See {quorumDenominator}.
49+ */
50+ function quorumNumerator (uint256 blockNumber ) public view virtual returns (uint256 ) {
51+ // If history is empty, fallback to old storage
52+ uint256 length = _quorumNumeratorHistory._checkpoints.length ;
53+ if (length == 0 ) {
54+ return _quorumNumerator;
55+ }
56+
57+ // Optimistic search, check the latest checkpoint
58+ CheckpointsUpgradeable.Checkpoint memory latest = _quorumNumeratorHistory._checkpoints[length - 1 ];
59+ if (latest._blockNumber <= blockNumber) {
60+ return latest._value;
61+ }
62+
63+ // Otherwize, do the binary search
64+ return _quorumNumeratorHistory.getAtBlock (blockNumber);
4065 }
4166
4267 /**
@@ -50,7 +75,7 @@ abstract contract GovernorVotesQuorumFractionUpgradeable is Initializable, Gover
5075 * @dev Returns the quorum for a block number, in terms of number of votes: `supply * numerator / denominator`.
5176 */
5277 function quorum (uint256 blockNumber ) public view virtual override returns (uint256 ) {
53- return (token.getPastTotalSupply (blockNumber) * quorumNumerator ()) / quorumDenominator ();
78+ return (token.getPastTotalSupply (blockNumber) * quorumNumerator (blockNumber )) / quorumDenominator ();
5479 }
5580
5681 /**
@@ -82,8 +107,17 @@ abstract contract GovernorVotesQuorumFractionUpgradeable is Initializable, Gover
82107 "GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator "
83108 );
84109
85- uint256 oldQuorumNumerator = _quorumNumerator;
86- _quorumNumerator = newQuorumNumerator;
110+ uint256 oldQuorumNumerator = quorumNumerator ();
111+
112+ // Make sure we keep track of the original numerator in contracts upgraded from a version without checkpoints.
113+ if (oldQuorumNumerator != 0 && _quorumNumeratorHistory._checkpoints.length == 0 ) {
114+ _quorumNumeratorHistory._checkpoints.push (
115+ CheckpointsUpgradeable.Checkpoint ({_blockNumber: 0 , _value: SafeCastUpgradeable.toUint224 (oldQuorumNumerator)})
116+ );
117+ }
118+
119+ // Set new quorum for future proposals
120+ _quorumNumeratorHistory.push (newQuorumNumerator);
87121
88122 emit QuorumNumeratorUpdated (oldQuorumNumerator, newQuorumNumerator);
89123 }
@@ -93,5 +127,5 @@ abstract contract GovernorVotesQuorumFractionUpgradeable is Initializable, Gover
93127 * variables without shifting down storage in the inheritance chain.
94128 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
95129 */
96- uint256 [49 ] private __gap;
130+ uint256 [48 ] private __gap;
97131}
0 commit comments