|
@@ -4,6 +4,8 @@
|
|
|
pragma solidity ^0.8.0;
|
|
|
|
|
|
import "./GovernorVotes.sol";
|
|
|
+import "../../utils/Checkpoints.sol";
|
|
|
+import "../../utils/math/SafeCast.sol";
|
|
|
|
|
|
/**
|
|
|
* @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";
|
|
|
* _Available since v4.3._
|
|
|
*/
|
|
|
abstract contract GovernorVotesQuorumFraction is GovernorVotes {
|
|
|
- uint256 private _quorumNumerator;
|
|
|
+ using Checkpoints for Checkpoints.History;
|
|
|
+
|
|
|
+ uint256 private _quorumNumerator; // DEPRECATED
|
|
|
+ Checkpoints.History private _quorumNumeratorHistory;
|
|
|
|
|
|
event QuorumNumeratorUpdated(uint256 oldQuorumNumerator, uint256 newQuorumNumerator);
|
|
|
|
|
@@ -31,7 +36,27 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
|
|
|
* @dev Returns the current quorum numerator. See {quorumDenominator}.
|
|
|
*/
|
|
|
function quorumNumerator() public view virtual returns (uint256) {
|
|
|
- return _quorumNumerator;
|
|
|
+ return _quorumNumeratorHistory._checkpoints.length == 0 ? _quorumNumerator : _quorumNumeratorHistory.latest();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @dev Returns the quorum numerator at a specific block number. See {quorumDenominator}.
|
|
|
+ */
|
|
|
+ function quorumNumerator(uint256 blockNumber) public view virtual returns (uint256) {
|
|
|
+ // If history is empty, fallback to old storage
|
|
|
+ uint256 length = _quorumNumeratorHistory._checkpoints.length;
|
|
|
+ if (length == 0) {
|
|
|
+ return _quorumNumerator;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Optimistic search, check the latest checkpoint
|
|
|
+ Checkpoints.Checkpoint memory latest = _quorumNumeratorHistory._checkpoints[length - 1];
|
|
|
+ if (latest._blockNumber <= blockNumber) {
|
|
|
+ return latest._value;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Otherwize, do the binary search
|
|
|
+ return _quorumNumeratorHistory.getAtBlock(blockNumber);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -45,7 +70,7 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
|
|
|
* @dev Returns the quorum for a block number, in terms of number of votes: `supply * numerator / denominator`.
|
|
|
*/
|
|
|
function quorum(uint256 blockNumber) public view virtual override returns (uint256) {
|
|
|
- return (token.getPastTotalSupply(blockNumber) * quorumNumerator()) / quorumDenominator();
|
|
|
+ return (token.getPastTotalSupply(blockNumber) * quorumNumerator(blockNumber)) / quorumDenominator();
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -77,8 +102,17 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
|
|
|
"GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator"
|
|
|
);
|
|
|
|
|
|
- uint256 oldQuorumNumerator = _quorumNumerator;
|
|
|
- _quorumNumerator = newQuorumNumerator;
|
|
|
+ uint256 oldQuorumNumerator = quorumNumerator();
|
|
|
+
|
|
|
+ // Make sure we keep track of the original numerator in contracts upgraded from a version without checkpoints.
|
|
|
+ if (oldQuorumNumerator != 0 && _quorumNumeratorHistory._checkpoints.length == 0) {
|
|
|
+ _quorumNumeratorHistory._checkpoints.push(
|
|
|
+ Checkpoints.Checkpoint({_blockNumber: 0, _value: SafeCast.toUint224(oldQuorumNumerator)})
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set new quorum for future proposals
|
|
|
+ _quorumNumeratorHistory.push(newQuorumNumerator);
|
|
|
|
|
|
emit QuorumNumeratorUpdated(oldQuorumNumerator, newQuorumNumerator);
|
|
|
}
|