Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.0.0 Breaking change] Using user defined value type for timers #2962

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions contracts/governance/Governor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import "./IGovernor.sol";
*/
abstract contract Governor is Context, ERC165, EIP712, IGovernor {
using SafeCast for uint256;
using Timers for uint64;
using Timers for Timers.BlockNumber;

bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)");
Expand Down Expand Up @@ -116,9 +117,9 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor {
return ProposalState.Executed;
} else if (proposal.canceled) {
return ProposalState.Canceled;
} else if (proposal.voteStart.getDeadline() >= block.number) {
} else if (proposal.voteStart.toUint64() >= block.number) {
return ProposalState.Pending;
} else if (proposal.voteEnd.getDeadline() >= block.number) {
} else if (proposal.voteEnd.toUint64() >= block.number) {
return ProposalState.Active;
} else if (proposal.voteEnd.isExpired()) {
return
Expand All @@ -134,14 +135,14 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor {
* @dev See {IGovernor-proposalSnapshot}.
*/
function proposalSnapshot(uint256 proposalId) public view virtual override returns (uint256) {
return _proposals[proposalId].voteStart.getDeadline();
return _proposals[proposalId].voteStart.toUint64();
}

/**
* @dev See {IGovernor-proposalDeadline}.
*/
function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) {
return _proposals[proposalId].voteEnd.getDeadline();
return _proposals[proposalId].voteEnd.toUint64();
}

/**
Expand Down Expand Up @@ -199,8 +200,8 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor {
uint64 snapshot = block.number.toUint64() + votingDelay().toUint64();
uint64 deadline = snapshot + votingPeriod().toUint64();

proposal.voteStart.setDeadline(snapshot);
proposal.voteEnd.setDeadline(deadline);
proposal.voteStart = snapshot.toBlockNumber();
proposal.voteEnd = deadline.toBlockNumber();

emit ProposalCreated(
proposalId,
Expand Down Expand Up @@ -339,7 +340,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor {
ProposalCore storage proposal = _proposals[proposalId];
require(state(proposalId) == ProposalState.Active, "Governor: vote not currently active");

uint256 weight = getVotes(account, proposal.voteStart.getDeadline());
uint256 weight = getVotes(account, proposal.voteStart.toUint64());
_countVote(proposalId, account, support, weight);

emit VoteCast(account, proposalId, support, weight, reason);
Expand Down
7 changes: 4 additions & 3 deletions contracts/governance/extensions/GovernorTimelockCompound.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ interface ICompoundTimelock {
*/
abstract contract GovernorTimelockCompound is IGovernorTimelock, Governor {
using SafeCast for uint256;
using Timers for uint64;
using Timers for Timers.Timestamp;

struct ProposalTimelock {
Expand Down Expand Up @@ -135,7 +136,7 @@ abstract contract GovernorTimelockCompound is IGovernorTimelock, Governor {
* @dev Public accessor to check the eta of a queued proposal
*/
function proposalEta(uint256 proposalId) public view virtual override returns (uint256) {
return _proposalTimelocks[proposalId].timer.getDeadline();
return _proposalTimelocks[proposalId].timer.toUint64();
}

/**
Expand All @@ -152,7 +153,7 @@ abstract contract GovernorTimelockCompound is IGovernorTimelock, Governor {
require(state(proposalId) == ProposalState.Succeeded, "Governor: proposal not successful");

uint256 eta = block.timestamp + _timelock.delay();
_proposalTimelocks[proposalId].timer.setDeadline(eta.toUint64());
_proposalTimelocks[proposalId].timer = eta.toUint64().toTimestamp();
for (uint256 i = 0; i < targets.length; ++i) {
require(
!_timelock.queuedTransactions(keccak256(abi.encode(targets[i], values[i], "", calldatas[i], eta))),
Expand Down Expand Up @@ -201,7 +202,7 @@ abstract contract GovernorTimelockCompound is IGovernorTimelock, Governor {
for (uint256 i = 0; i < targets.length; ++i) {
_timelock.cancelTransaction(targets[i], values[i], "", calldatas[i], eta);
}
_proposalTimelocks[proposalId].timer.reset();
_proposalTimelocks[proposalId].timer = uint64(0).toTimestamp();
}

return proposalId;
Expand Down
7 changes: 4 additions & 3 deletions contracts/mocks/TimersBlockNumberImpl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@ pragma solidity ^0.8.0;
import "../utils/Timers.sol";

contract TimersBlockNumberImpl {
using Timers for uint64;
using Timers for Timers.BlockNumber;

Timers.BlockNumber private _timer;

function getDeadline() public view returns (uint64) {
return _timer.getDeadline();
return _timer.toUint64();
}

function setDeadline(uint64 timestamp) public {
_timer.setDeadline(timestamp);
_timer = timestamp.toBlockNumber();
}

function reset() public {
_timer.reset();
_timer = uint64(0).toBlockNumber();
}

function isUnset() public view returns (bool) {
Expand Down
7 changes: 4 additions & 3 deletions contracts/mocks/TimersTimestampImpl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@ pragma solidity ^0.8.0;
import "../utils/Timers.sol";

contract TimersTimestampImpl {
using Timers for uint64;
using Timers for Timers.Timestamp;

Timers.Timestamp private _timer;

function getDeadline() public view returns (uint64) {
return _timer.getDeadline();
return _timer.toUint64();
}

function setDeadline(uint64 timestamp) public {
_timer.setDeadline(timestamp);
_timer = timestamp.toTimestamp();
}

function reset() public {
_timer.reset();
_timer = uint64(0).toTimestamp();
}

function isUnset() public view returns (bool) {
Expand Down
97 changes: 64 additions & 33 deletions contracts/utils/Timers.sol
Original file line number Diff line number Diff line change
@@ -1,73 +1,104 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.3.2 (utils/Timers.sol)

pragma solidity ^0.8.0;
pragma solidity ^0.8.8;

/**
* @dev Tooling for timepoints, timers and delays
*/
library Timers {
struct Timestamp {
uint64 _deadline;
enum Type { TIMESTAMP, BLOCKNUMBER }

type Timer is uint64;

function toTimer(uint64 timer, Type typeSelector) internal pure returns (Timer) {
require(timer >> 63 == 0, 'Timer only support 63 bits values');
return Timer.wrap(uint8(typeSelector) << 63 | timer);
}

function getType(Timer timer) internal pure returns (Type) {
return Type(Timer.unwrap(timer) >> 63);
}

function getValue(Timer timer) internal pure returns (uint64) {
return Timer.unwrap(timer) & (type(uint64).max >> 1);
}

function isUnset(Timer timer) internal pure returns (bool) {
return getValue(timer) == 0;
}

function isStarted(Timer timer) internal pure returns (bool) {
return getValue(timer) > 0;
}

function getDeadline(Timestamp memory timer) internal pure returns (uint64) {
return timer._deadline;
function isPending(Timer timer) internal view returns (bool) {
return getType(timer) == Type.TIMESTAMP
? getValue(timer) > block.timestamp
: getValue(timer) > block.number;
}

function setDeadline(Timestamp storage timer, uint64 timestamp) internal {
timer._deadline = timestamp;
function isExpired(Timer timer) internal view returns (bool) {
return isStarted(timer) && !isPending(timer);
}

function reset(Timestamp storage timer) internal {
timer._deadline = 0;
function unset() internal pure returns (Timer) {
return Timer.wrap(0);
}

function isUnset(Timestamp memory timer) internal pure returns (bool) {
return timer._deadline == 0;
function never() internal pure returns (Timer) {
return Timer.wrap(type(uint64).max);
}

function isStarted(Timestamp memory timer) internal pure returns (bool) {
return timer._deadline > 0;
type Timestamp is uint64;

function toTimestamp(uint64 timer) internal pure returns (Timestamp) {
return Timestamp.wrap(timer);
}

function isPending(Timestamp memory timer) internal view returns (bool) {
return timer._deadline > block.timestamp;
function toUint64(Timestamp timer) internal pure returns (uint64) {
return Timestamp.unwrap(timer);
}

function isExpired(Timestamp memory timer) internal view returns (bool) {
return isStarted(timer) && timer._deadline <= block.timestamp;
function isUnset(Timestamp timer) internal pure returns (bool) {
return Timestamp.unwrap(timer) == 0;
}

struct BlockNumber {
uint64 _deadline;
function isStarted(Timestamp timer) internal pure returns (bool) {
return Timestamp.unwrap(timer) > 0;
}

function getDeadline(BlockNumber memory timer) internal pure returns (uint64) {
return timer._deadline;
function isPending(Timestamp timer) internal view returns (bool) {
return Timestamp.unwrap(timer) > block.timestamp;
}

function setDeadline(BlockNumber storage timer, uint64 timestamp) internal {
timer._deadline = timestamp;
function isExpired(Timestamp timer) internal view returns (bool) {
return isStarted(timer) && Timestamp.unwrap(timer) <= block.timestamp;
}

type BlockNumber is uint64;

function toBlockNumber(uint64 timer) internal pure returns (BlockNumber) {
return BlockNumber.wrap(timer);
}

function reset(BlockNumber storage timer) internal {
timer._deadline = 0;
function toUint64(BlockNumber timer) internal pure returns (uint64) {
return BlockNumber.unwrap(timer);
}

function isUnset(BlockNumber memory timer) internal pure returns (bool) {
return timer._deadline == 0;
function isUnset(BlockNumber timer) internal pure returns (bool) {
return BlockNumber.unwrap(timer) == 0;
}

function isStarted(BlockNumber memory timer) internal pure returns (bool) {
return timer._deadline > 0;
function isStarted(BlockNumber timer) internal pure returns (bool) {
return BlockNumber.unwrap(timer) > 0;
}

function isPending(BlockNumber memory timer) internal view returns (bool) {
return timer._deadline > block.number;
function isPending(BlockNumber timer) internal view returns (bool) {
return BlockNumber.unwrap(timer) > block.number;
}

function isExpired(BlockNumber memory timer) internal view returns (bool) {
return isStarted(timer) && timer._deadline <= block.number;
function isExpired(BlockNumber timer) internal view returns (bool) {
return isStarted(timer) && BlockNumber.unwrap(timer) <= block.number;
}
}
4 changes: 2 additions & 2 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// - COVERAGE: enable coverage report
// - ENABLE_GAS_REPORT: enable gas report
// - COMPILE_MODE: production modes enables optimizations (default: development)
// - COMPILE_VERSION: compiler version (default: 0.8.3)
// - COMPILE_VERSION: compiler version (default: 0.8.9)
// - COINMARKETCAP: coinmarkercat api key for USD value in gas report

const fs = require('fs');
Expand Down Expand Up @@ -33,7 +33,7 @@ const argv = require('yargs/yargs')()
compiler: {
alias: 'compileVersion',
type: 'string',
default: '0.8.3',
default: '0.8.9',
},
coinmarketcap: {
alias: 'coinmarketcapApiKey',
Expand Down