Add timestamp based governor with EIP-6372 and EIP-5805 (#3934)

Co-authored-by: Francisco Giordano <fg@frang.io>
Co-authored-by: Ernesto García <ernestognw@gmail.com>
Co-authored-by: Francisco <frangio.1@gmail.com>
This commit is contained in:
Hadrien Croubois
2023-02-09 22:33:55 +01:00
committed by GitHub
parent 94cd8ef12e
commit 790cc5b65a
42 changed files with 4081 additions and 3209 deletions

View File

@ -24,18 +24,20 @@ interface IVotes {
function getVotes(address account) external view returns (uint256);
/**
* @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).
* @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value the end of the corresponding block.
*/
function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);
function getPastVotes(address account, uint256 timepoint) external view returns (uint256);
/**
* @dev Returns the total supply of votes available at the end of a past block (`blockNumber`).
* @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value the end of the corresponding block.
*
* NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
* Votes that have not been delegated are still part of total supply, even though they would not participate in a
* vote.
*/
function getPastTotalSupply(uint256 blockNumber) external view returns (uint256);
function getPastTotalSupply(uint256 timepoint) external view returns (uint256);
/**
* @dev Returns the delegate that `account` has chosen.

View File

@ -2,11 +2,11 @@
// OpenZeppelin Contracts (last updated v4.8.0) (governance/utils/Votes.sol)
pragma solidity ^0.8.0;
import "../../interfaces/IERC5805.sol";
import "../../utils/Context.sol";
import "../../utils/Counters.sol";
import "../../utils/Checkpoints.sol";
import "../../utils/cryptography/EIP712.sol";
import "./IVotes.sol";
/**
* @dev This is a base abstract contract that tracks voting units, which are a measure of voting power that can be
@ -28,19 +28,41 @@ import "./IVotes.sol";
*
* _Available since v4.5._
*/
abstract contract Votes is IVotes, Context, EIP712 {
using Checkpoints for Checkpoints.History;
abstract contract Votes is Context, EIP712, IERC5805 {
using Checkpoints for Checkpoints.Trace224;
using Counters for Counters.Counter;
bytes32 private constant _DELEGATION_TYPEHASH =
keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
mapping(address => address) private _delegation;
mapping(address => Checkpoints.History) private _delegateCheckpoints;
Checkpoints.History private _totalCheckpoints;
/// @custom:oz-retyped-from mapping(address => Checkpoints.History)
mapping(address => Checkpoints.Trace224) private _delegateCheckpoints;
/// @custom:oz-retyped-from Checkpoints.History
Checkpoints.Trace224 private _totalCheckpoints;
mapping(address => Counters.Counter) private _nonces;
/**
* @dev Clock used for flagging checkpoints. Can be overridden to implement timestamp based
* checkpoints (and voting), in which case {CLOCK_MODE} should be overridden as well to match.
*/
function clock() public view virtual override returns (uint48) {
return SafeCast.toUint48(block.number);
}
/**
* @dev Machine-readable description of the clock as specified in EIP-6372.
*/
// solhint-disable-next-line func-name-mixedcase
function CLOCK_MODE() public view virtual override returns (string memory) {
// Check that the clock was not modified
require(clock() == block.number);
return "mode=blocknumber&from=default";
}
/**
* @dev Returns the current amount of votes that `account` has.
*/
@ -49,18 +71,21 @@ abstract contract Votes is IVotes, Context, EIP712 {
}
/**
* @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).
* @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value the end of the corresponding block.
*
* Requirements:
*
* - `blockNumber` must have been already mined
* - `timepoint` must be in the past. If operating using block numbers, the block must be already mined.
*/
function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) {
return _delegateCheckpoints[account].getAtProbablyRecentBlock(blockNumber);
function getPastVotes(address account, uint256 timepoint) public view virtual override returns (uint256) {
require(timepoint < clock(), "Votes: future lookup");
return _delegateCheckpoints[account].upperLookupRecent(SafeCast.toUint32(timepoint));
}
/**
* @dev Returns the total supply of votes available at the end of a past block (`blockNumber`).
* @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value the end of the corresponding block.
*
* NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
* Votes that have not been delegated are still part of total supply, even though they would not participate in a
@ -68,11 +93,11 @@ abstract contract Votes is IVotes, Context, EIP712 {
*
* Requirements:
*
* - `blockNumber` must have been already mined
* - `timepoint` must be in the past. If operating using block numbers, the block must be already mined.
*/
function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) {
require(blockNumber < block.number, "Votes: block not yet mined");
return _totalCheckpoints.getAtProbablyRecentBlock(blockNumber);
function getPastTotalSupply(uint256 timepoint) public view virtual override returns (uint256) {
require(timepoint < clock(), "Votes: future lookup");
return _totalCheckpoints.upperLookupRecent(SafeCast.toUint32(timepoint));
}
/**
@ -138,10 +163,10 @@ abstract contract Votes is IVotes, Context, EIP712 {
*/
function _transferVotingUnits(address from, address to, uint256 amount) internal virtual {
if (from == address(0)) {
_totalCheckpoints.push(_add, amount);
_push(_totalCheckpoints, _add, SafeCast.toUint224(amount));
}
if (to == address(0)) {
_totalCheckpoints.push(_subtract, amount);
_push(_totalCheckpoints, _subtract, SafeCast.toUint224(amount));
}
_moveDelegateVotes(delegates(from), delegates(to), amount);
}
@ -152,21 +177,37 @@ abstract contract Votes is IVotes, Context, EIP712 {
function _moveDelegateVotes(address from, address to, uint256 amount) private {
if (from != to && amount > 0) {
if (from != address(0)) {
(uint256 oldValue, uint256 newValue) = _delegateCheckpoints[from].push(_subtract, amount);
(uint256 oldValue, uint256 newValue) = _push(
_delegateCheckpoints[from],
_subtract,
SafeCast.toUint224(amount)
);
emit DelegateVotesChanged(from, oldValue, newValue);
}
if (to != address(0)) {
(uint256 oldValue, uint256 newValue) = _delegateCheckpoints[to].push(_add, amount);
(uint256 oldValue, uint256 newValue) = _push(
_delegateCheckpoints[to],
_add,
SafeCast.toUint224(amount)
);
emit DelegateVotesChanged(to, oldValue, newValue);
}
}
}
function _add(uint256 a, uint256 b) private pure returns (uint256) {
function _push(
Checkpoints.Trace224 storage store,
function(uint224, uint224) view returns (uint224) op,
uint224 delta
) private returns (uint224, uint224) {
return store.push(SafeCast.toUint32(clock()), op(store.latest(), delta));
}
function _add(uint224 a, uint224 b) private pure returns (uint224) {
return a + b;
}
function _subtract(uint256 a, uint256 b) private pure returns (uint256) {
function _subtract(uint224 a, uint224 b) private pure returns (uint224) {
return a - b;
}