Skip to content

Commit

Permalink
Keep track of historical quorum values (#3561)
Browse files Browse the repository at this point in the history
Co-authored-by: Francisco Giordano <frangio.1@gmail.com>
(cherry picked from commit 8ea1fc8)
  • Loading branch information
Amxx authored and frangio committed Jul 27, 2022
1 parent 4337192 commit 67b2572
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 5 deletions.
44 changes: 39 additions & 5 deletions contracts/governance/extensions/GovernorVotesQuorumFraction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);

Expand All @@ -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);
}

/**
Expand All @@ -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();
}

/**
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ contract('GovernorVotesQuorumFraction', function (accounts) {

expect(await this.mock.quorumNumerator()).to.be.bignumber.equal(newRatio);
expect(await this.mock.quorumDenominator()).to.be.bignumber.equal('100');

// it takes one block for the new quorum to take effect
expect(await time.latestBlock().then(blockNumber => this.mock.quorum(blockNumber.subn(1))))
.to.be.bignumber.equal(tokenSupply.mul(ratio).divn(100));

await time.advanceBlock();

expect(await time.latestBlock().then(blockNumber => this.mock.quorum(blockNumber.subn(1))))
.to.be.bignumber.equal(tokenSupply.mul(newRatio).divn(100));
});
Expand Down

0 comments on commit 67b2572

Please # to comment.