4
4
pragma solidity ^ 0.8.0 ;
5
5
6
6
import "./GovernorVotes.sol " ;
7
+ import "../../utils/Checkpoints.sol " ;
8
+ import "../../utils/math/SafeCast.sol " ;
7
9
8
10
/**
9
11
* @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";
12
14
* _Available since v4.3._
13
15
*/
14
16
abstract 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;
16
21
17
22
event QuorumNumeratorUpdated (uint256 oldQuorumNumerator , uint256 newQuorumNumerator );
18
23
@@ -31,7 +36,27 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
31
36
* @dev Returns the current quorum numerator. See {quorumDenominator}.
32
37
*/
33
38
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);
35
60
}
36
61
37
62
/**
@@ -45,7 +70,7 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
45
70
* @dev Returns the quorum for a block number, in terms of number of votes: `supply * numerator / denominator`.
46
71
*/
47
72
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 ();
49
74
}
50
75
51
76
/**
@@ -77,8 +102,17 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
77
102
"GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator "
78
103
);
79
104
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);
82
116
83
117
emit QuorumNumeratorUpdated (oldQuorumNumerator, newQuorumNumerator);
84
118
}
0 commit comments