Commit 222e8750 by github-actions

Transpile cc39fb66

parent 5c629305
...@@ -10,5 +10,5 @@ about: Suggest an idea for OpenZeppelin Contracts ...@@ -10,5 +10,5 @@ about: Suggest an idea for OpenZeppelin Contracts
**📝 Details** **📝 Details**
<!-- Please describe your feature request in detail. --> <!-- Please describe your feature request in detail. -->
<!-- Make sure that you have reviewed the OpenZeppelin Contributor Guidelines. --> <!-- Make sure that you have reviewed the OpenZeppelin Contracts Contributor Guidelines. -->
<!-- https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CONTRIBUTING.md --> <!-- https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CONTRIBUTING.md -->
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
## Unreleased ## Unreleased
* `ERC2771Context`: use private variable from storage to store the forwarder address. Fixes issues where `_msgSender()` was not callable from constructors. ([#2754](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2754)) * `ERC2771Context`: use private variable from storage to store the forwarder address. Fixes issues where `_msgSender()` was not callable from constructors. ([#2754](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2754))
* `EnumerableSet`: add `values()` functions that returns an array containing all values in a single call. ([#2768](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2768))
* `Governor`: added a modular system of `Governor` contracts based on `GovernorAlpha` and `GovernorBravo`. ([#2672](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2672))
## 4.2.0 (2021-06-30) ## 4.2.0 (2021-06-30)
...@@ -17,7 +19,7 @@ ...@@ -17,7 +19,7 @@
* `ERC1155Supply`: add a new `ERC1155` extension that keeps track of the totalSupply of each tokenId. ([#2593](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2593)) * `ERC1155Supply`: add a new `ERC1155` extension that keeps track of the totalSupply of each tokenId. ([#2593](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2593))
* `BitMaps`: add a new `BitMaps` library that provides a storage efficient datastructure for `uint256` to `bool` mapping with contiguous keys. ([#2710](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2710)) * `BitMaps`: add a new `BitMaps` library that provides a storage efficient datastructure for `uint256` to `bool` mapping with contiguous keys. ([#2710](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2710))
### Breaking Changes ### Breaking Changes
* `ERC20FlashMint` is no longer a Draft ERC. ([#2673](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2673))) * `ERC20FlashMint` is no longer a Draft ERC. ([#2673](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2673)))
......
...@@ -49,9 +49,9 @@ npm run lint ...@@ -49,9 +49,9 @@ npm run lint
*IMPORTANT* Read the PR template very carefully and make sure to follow all the instructions. These instructions *IMPORTANT* Read the PR template very carefully and make sure to follow all the instructions. These instructions
refer to some very important conditions that your PR must meet in order to be accepted, such as making sure that all tests pass, JS linting tests pass, Solidity linting tests pass, etc. refer to some very important conditions that your PR must meet in order to be accepted, such as making sure that all tests pass, JS linting tests pass, Solidity linting tests pass, etc.
6) Maintainers will review your code and possibly ask for changes before your code is pulled in to the main repository. We'll check that all tests pass, review the coding style, and check for general code correctness. If everything is OK, we'll merge your pull request and your code will be part of OpenZeppelin. 6) Maintainers will review your code and possibly ask for changes before your code is pulled in to the main repository. We'll check that all tests pass, review the coding style, and check for general code correctness. If everything is OK, we'll merge your pull request and your code will be part of OpenZeppelin Contracts.
*IMPORTANT* Please pay attention to the maintainer's feedback, since its a necessary step to keep up with the standards OpenZeppelin attains to. *IMPORTANT* Please pay attention to the maintainer's feedback, since its a necessary step to keep up with the standards OpenZeppelin Contracts attains to.
## All set! ## All set!
......
Design Guidelines Design Guidelines
======= =======
These are some global design goals in OpenZeppelin. These are some global design goals in OpenZeppelin Contracts.
#### D0 - Security in Depth #### D0 - Security in Depth
We strive to provide secure, tested, audited code. To achieve this, we need to match intention with function. Thus, documentation, code clarity, community review and security discussions are fundamental. We strive to provide secure, tested, audited code. To achieve this, we need to match intention with function. Thus, documentation, code clarity, community review and security discussions are fundamental.
......
...@@ -77,9 +77,9 @@ Finally, you may want to take a look at the [guides on our blog](https://blog.op ...@@ -77,9 +77,9 @@ Finally, you may want to take a look at the [guides on our blog](https://blog.op
## Security ## Security
This project is maintained by [OpenZeppelin](https://openzeppelin.com), and developed following our high standards for code quality and security. OpenZeppelin is meant to provide tested and community-audited code, but please use common sense when doing anything that deals with real money! We take no responsibility for your implementation decisions and any security problems you might experience. This project is maintained by [OpenZeppelin](https://openzeppelin.com), and developed following our high standards for code quality and security. OpenZeppelin Contracts is meant to provide tested and community-audited code, but please use common sense when doing anything that deals with real money! We take no responsibility for your implementation decisions and any security problems you might experience.
The core development principles and strategies that OpenZeppelin is based on include: security in depth, simple and modular code, clarity-driven naming conventions, comprehensive unit testing, pre-and-post-condition sanity checks, code consistency, and regular audits. The core development principles and strategies that OpenZeppelin Contracts is based on include: security in depth, simple and modular code, clarity-driven naming conventions, comprehensive unit testing, pre-and-post-condition sanity checks, code consistency, and regular audits.
The latest audit was done on October 2018 on version 2.0.0. The latest audit was done on October 2018 on version 2.0.0.
...@@ -87,8 +87,8 @@ Please report any security issues you find to security@openzeppelin.org. ...@@ -87,8 +87,8 @@ Please report any security issues you find to security@openzeppelin.org.
## Contribute ## Contribute
OpenZeppelin exists thanks to its contributors. There are many ways you can participate and help build high quality software. Check out the [contribution guide](CONTRIBUTING.md)! OpenZeppelin Contracts exists thanks to its contributors. There are many ways you can participate and help build high quality software. Check out the [contribution guide](CONTRIBUTING.md)!
## License ## License
OpenZeppelin is released under the [MIT License](LICENSE). OpenZeppelin Contracts is released under the [MIT License](LICENSE).
...@@ -10,10 +10,10 @@ Publish a release candidate with `npm run release rc`. ...@@ -10,10 +10,10 @@ Publish a release candidate with `npm run release rc`.
Publish the final release with `npm run release final`. Publish the final release with `npm run release final`.
Follow the general [OpenZeppelin release checklist]. Follow the general [OpenZeppelin Contracts release checklist].
[details about release schedule]: https://docs.openzeppelin.com/contracts/releases-stability [details about release schedule]: https://docs.openzeppelin.com/contracts/releases-stability
[OpenZeppelin release checklist]: https://github.com/OpenZeppelin/code-style/blob/master/RELEASE_CHECKLIST.md [OpenZeppelin Contracts release checklist]: https://github.com/OpenZeppelin/code-style/blob/master/RELEASE_CHECKLIST.md
## Merging the release branch ## Merging the release branch
......
# OpenZeppelin Audit # OpenZeppelin Audit
NOTE ON 2021-07-19: This report makes reference to Zeppelin, OpenZeppelin, OpenZeppelin [C]ontracts, the OpenZeppelin team, and OpenZeppelin library. Many of these things have since been renamed and know that this audit applies to what is currently called the OpenZeppelin Contracts which are maintained by the OpenZeppelin Conracts Community.
March, 2017 March, 2017
Authored by Dennis Peterson and Peter Vessenes Authored by Dennis Peterson and Peter Vessenes
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/introspection/ERC165Upgradeable.sol";
/**
* @dev Interface of the {Governor} core.
*
* _Available since v4.3._
*/
interface IGovernorUpgradeable is IERC165Upgradeable {
enum ProposalState {
Pending,
Active,
Canceled,
Defeated,
Succeeded,
Queued,
Expired,
Executed
}
/**
* @dev Emitted when a proposal is created.
*/
event ProposalCreated(
uint256 proposalId,
address proposer,
address[] targets,
uint256[] values,
string[] signatures,
bytes[] calldatas,
uint256 startBlock,
uint256 endBlock,
string description
);
/**
* @dev Emitted when a proposal is canceled.
*/
event ProposalCanceled(uint256 proposalId);
/**
* @dev Emitted when a proposal is executed.
*/
event ProposalExecuted(uint256 proposalId);
/**
* @dev Emitted when a vote is casted.
*
* Note: `support` values should be seen as buckets. There interpretation depends on the voting module used.
*/
event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);
/**
* @notice module:core
* @dev Name of the governor instance (used in building the ERC712 domain separator).
*/
function name() external view returns (string memory);
/**
* @notice module:core
* @dev Version of the governor instance (used in building the ERC712 domain separator). Default: "1"
*/
function version() external view returns (string memory);
/**
* @notice module:voting
* @dev A description of the possible `support` values for {castVote} and the way these votes are counted, meant to
* be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of
* key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`.
*
* There are 2 standard keys: `support` and `quorum`.
*
* - `support=bravo` refers to the vote options 0 = For, 1 = Against, 2 = Abstain, as in `GovernorBravo`.
* - `quorum=bravo` means that only For votes are counted towards quorum.
* - `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum.
*
* NOTE: The string can be decoded by the standard
* https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams[`URLSearchParams`]
* JavaScript class.
*/
// solhint-disable-next-line func-name-mixedcase
function COUNTING_MODE() external pure returns (string memory);
/**
* @notice module:core
* @dev Hashing function used to (re)build the proposal id from the proposal details..
*/
function hashProposal(
address[] calldata targets,
uint256[] calldata values,
bytes[] calldata calldatas,
bytes32 descriptionHash
) external pure returns (uint256);
/**
* @notice module:core
* @dev Current state of a proposal, following Compound's convention
*/
function state(uint256 proposalId) external view returns (ProposalState);
/**
* @notice module:core
* @dev block number used to retrieve user's votes and quorum.
*/
function proposalSnapshot(uint256 proposalId) external view returns (uint256);
/**
* @notice module:core
* @dev timestamp at which votes close.
*/
function proposalDeadline(uint256 proposalId) external view returns (uint256);
/**
* @notice module:user-config
* @dev delay, in number of block, between the proposal is created and the vote starts. This can be increassed to
* leave time for users to buy voting power, of delegate it, before the voting of a proposal starts.
*/
function votingDelay() external view returns (uint256);
/**
* @notice module:user-config
* @dev delay, in number of blocks, between the vote start and vote ends.
*
* Note: the {votingDelay} can delay the start of the vote. This must be considered when setting the voting
* duration compared to the voting delay.
*/
function votingPeriod() external view returns (uint256);
/**
* @notice module:user-config
* @dev Minimum number of casted voted requiered for a proposal to be successful.
*
* Note: The `blockNumber` parameter corresponds to the snaphot used for counting vote. This allows to scale the
* quroum depending on values such as the totalSupply of a token at this block (see {ERC20Votes}).
*/
function quorum(uint256 blockNumber) external view returns (uint256);
/**
* @notice module:reputation
* @dev Voting power of an `account` at a specific `blockNumber`.
*
* Note: this can be implemented in a number of ways, for example by reading the delegated balance from one (or
* multiple), {ERC20Votes} tokens.
*/
function getVotes(address account, uint256 blockNumber) external view returns (uint256);
/**
* @notice module:voting
* @dev Returns weither `account` has casted a vote on `proposalId`.
*/
function hasVoted(uint256 proposalId, address account) external view returns (bool);
/**
* @dev Create a new proposal. Vote start {IGovernor-votingDelay} blocks after the proposal is created and ends
* {IGovernor-votingPeriod} blocks after the voting starts.
*
* Emits a {ProposalCreated} event.
*/
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) external returns (uint256 proposalId);
/**
* @dev Execute a successful proposal. This requiers the quorum to be reached, the vote to be successful, and the
* deadline to be reached.
*
* Emits a {ProposalExecuted} event.
*
* Note: some module can modify the requierements for execution, for example by adding an additional timelock.
*/
function execute(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external payable returns (uint256 proposalId);
/**
* @dev Cast a vote
*
* Emits a {VoteCast} event.
*/
function castVote(uint256 proposalId, uint8 support) external returns (uint256 balance);
/**
* @dev Cast a with a reason
*
* Emits a {VoteCast} event.
*/
function castVoteWithReason(
uint256 proposalId,
uint8 support,
string calldata reason
) external returns (uint256 balance);
/**
* @dev Cast a vote using the user cryptographic signature.
*
* Emits a {VoteCast} event.
*/
function castVoteBySig(
uint256 proposalId,
uint8 support,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint256 balance);
}
...@@ -3,10 +3,64 @@ ...@@ -3,10 +3,64 @@
[.readme-notice] [.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/governance NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/governance
This directory includes primitives for on-chain governance. We currently only offer the {TimelockController} contract, that can be used as a component in a governance system to introduce a delay between a proposal and its execution. This directory includes primitives for on-chain governance.
== Governor
The {Governor} contract provides primitive to set an on-chain voting system similar to https://compound.finance/docs/governance[Compound's Governor Alpha & Bravo].
Similarly to our other contracts, it is customizable through inheritance and comes with extensions:
* {GovernorTimelockControl}: A {Governor} extension that performs executions through a {TimelockController}. This requires a successful proposal to be queued before then can be executed. The {TimelockController} will enforce a delay between the queueing and the execution. With this module, proposals are executed by the external {TimelockController} contract, which would have to hold the assets that are being governed.
* {GovernorTimelockCompound}: A {Governor} extension that performs executions through a compound https://github.com/compound-finance/compound-protocol/blob/master/contracts/Timelock.sol[`Timelock`]. This requires a successful proposal to be queued before then can be executed. The `Timelock` will enforce a delay between the queueing and the execution. With this module, proposals are executed by the external `Timelock` contract, which would have to hold the assets that are being governed.
* {GovernorCountingSimple}: A simple voting mechanism for {Governor} with support 3 vote options: Against, For and Abstain.
* {GovernorVotes}: Binding to extract voting weight from an {ERC20Votes} token.
* {GovernorVotesQuorumFraction}: Binding to extract voting weight from an {ERC20Votes} token and set the quorum as a fraction of the (snapshoted) total token supply.
* {GovernorVotesComp}: Binding to extract voting weight from a Comp or {ERC20VotesComp} token.
In addition to modules, the {Governor} requires a few virtual functions to be implemented to your particular specifications:
* <<Governor-votingOffset-,`votingOffset()`>>: Delay (number of blocks), between the proposal, is submitted and the snapshot block used for voting. This can be used to enforce a delay after a proposal is published for users to buy tokens, or delegate their votes (default: 0).
* <<Governor-votingDuration-,`votingDuration()`>>: Delay (in seconds), between the proposal, is submitted and the vote ends.
* <<Governor-quorum-uint256-,`quorum(uint256 blockNumber)`>>: Quorum required for a proposal to be successful. This function includes a `blockNumber` argument so the quorum can adapt through time, for example, to follow a token's `totalSupply`.
Note: Function of the {Governor} contract does NOT include access control. If you want to restrict access (for example to require a minimum balance to submit a proposal), you should add these checks by overloading the particular functions. For security reasons, the {Governor-_cancel} method is internal, and you will have to expose it (which the right access control mechanism) yourself if this is a mechanism you need.
Events emitted by the {Governor} contract are compatible with Compound's `GovernorBravo`. Additionnaly, function compatibility can be added using the {GovernorCompatibilityBravo} compatibility layer. This layer includes a voting system but does not include token bindings. This layer also requiers a timelock module (either {GovernorTimelockControl} or {GovernorTimelockCompound}).
=== Core
{{IGovernor}}
{{Governor}}
=== Extensions
{{GovernorTimelockControl}}
{{GovernorTimelockCompound}}
{{GovernorCountingSimple}}
{{GovernorVotes}}
{{GovernorVotesQuorumFraction}}
{{GovernorVotesComp}}
=== Compatibility
{{GovernorCompatibilityBravo}}
== Timelock == Timelock
In a governance systems, the {TimelockController} contract is in carge of introducing a delay between a proposal and its execution. It can be used with or without a {Governor}.
{{TimelockController}} {{TimelockController}}
[[timelock-terminology]] [[timelock-terminology]]
...@@ -27,7 +81,7 @@ This directory includes primitives for on-chain governance. We currently only of ...@@ -27,7 +81,7 @@ This directory includes primitives for on-chain governance. We currently only of
[[timelock-operation]] [[timelock-operation]]
==== Operation structure ==== Operation structure
Operation executed by the xref:api:governance.adoc#TimelockController[`TimelockControler`] can contain one or multiple subsequent calls. Depending on whether you need to multiple calls to be executed atomically, you can either use simple or batched operations. Operation executed by the xref:api:governance.adoc#TimelockController[`TimelockController`] can contain one or multiple subsequent calls. Depending on whether you need to multiple calls to be executed atomically, you can either use simple or batched operations.
Both operations contain: Both operations contain:
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IGovernorUpgradeable.sol";
/**
* @dev Interface extension that adds missing functions to the {Governor} core to provide `GovernorBravo` compatibility.
*
* _Available since v4.3._
*/
interface IGovernorCompatibilityBravoUpgradeable is IGovernorUpgradeable {
/**
* @dev Proposal structure from Compound Governor Bravo. Not actually used by the compatibility layer, as
* {{proposal}} returns a very different structure.
*/
struct Proposal {
uint256 id;
address proposer;
uint256 eta;
address[] targets;
uint256[] values;
string[] signatures;
bytes[] calldatas;
uint256 startBlock;
uint256 endBlock;
uint256 forVotes;
uint256 againstVotes;
uint256 abstainVotes;
bool canceled;
bool executed;
mapping(address => Receipt) receipts;
}
/**
* @dev Receipt structure from Compound Governor Bravo
*/
struct Receipt {
bool hasVoted;
uint8 support;
uint96 votes;
}
/**
* @dev Part of the Governor Bravo's interface.
*/
function quorumVotes() external view returns (uint256);
/**
* @dev Part of the Governor Bravo's interface: _"The official record of all proposals ever proposed"_.
*/
function proposals(uint256)
external
view
returns (
uint256 id,
address proposer,
uint256 eta,
uint256 startBlock,
uint256 endBlock,
uint256 forVotes,
uint256 againstVotes,
uint256 abstainVotes,
bool canceled,
bool executed
);
/**
* @dev Part of the Governor Bravo's interface: _"Function used to propose a new proposal"_.
*/
function propose(
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas,
string memory description
) external returns (uint256);
/**
* @dev Part of the Governor Bravo's interface: _"Queues a proposal of state succeeded"_.
*/
function queue(uint256 proposalId) external;
/**
* @dev Part of the Governor Bravo's interface: _"Executes a queued proposal if eta has passed"_.
*/
function execute(uint256 proposalId) external payable;
/**
* @dev Part of the Governor Bravo's interface: _"Gets actions of a proposal"_.
*/
function getActions(uint256 proposalId)
external
view
returns (
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas
);
/**
* @dev Part of the Governor Bravo's interface: _"Gets the receipt for a voter on a given proposal"_.
*/
function getReceipt(uint256 proposalId, address voter) external view returns (Receipt memory);
/**
* @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_.
*/
function proposalThreshold() external view returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../GovernorUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";
/**
* @dev Extension of {Governor} for simple, 3 options, vote counting.
*
* _Available since v4.3._
*/
abstract contract GovernorCountingSimpleUpgradeable is Initializable, GovernorUpgradeable {
function __GovernorCountingSimple_init() internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__EIP712_init_unchained(name_, version());
__GovernorCountingSimple_init_unchained();
}
function __GovernorCountingSimple_init_unchained() internal initializer {
}
/**
* @dev Supported vote types. Matches Governor Bravo ordering.
*/
enum VoteType {
Against,
For,
Abstain
}
struct ProposalVote {
uint256 againstVotes;
uint256 forVotes;
uint256 abstainVotes;
mapping(address => bool) hasVoted;
}
mapping(uint256 => ProposalVote) private _proposalVotes;
/**
* @dev See {IGovernor-COUNTING_MODE}.
*/
// solhint-disable-next-line func-name-mixedcase
function COUNTING_MODE() public pure virtual override returns (string memory) {
return "support=bravo&quorum=for,abstain";
}
/**
* @dev See {IGovernor-hasVoted}.
*/
function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) {
return _proposalVotes[proposalId].hasVoted[account];
}
/**
* @dev Accessor to the internal vote counts.
*/
function proposalVotes(uint256 proposalId)
public
view
virtual
returns (
uint256 againstVotes,
uint256 forVotes,
uint256 abstainVotes
)
{
ProposalVote storage proposalvote = _proposalVotes[proposalId];
return (proposalvote.againstVotes, proposalvote.forVotes, proposalvote.abstainVotes);
}
/**
* @dev See {Governor-_quorumReached}.
*/
function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) {
ProposalVote storage proposalvote = _proposalVotes[proposalId];
return
quorum(proposalSnapshot(proposalId)) <=
proposalvote.againstVotes + proposalvote.forVotes + proposalvote.abstainVotes;
}
/**
* @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be scritly over the againstVotes.
*/
function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) {
ProposalVote storage proposalvote = _proposalVotes[proposalId];
return proposalvote.forVotes > proposalvote.againstVotes;
}
/**
* @dev See {Governor-_countVote}. In this module, the support follows the `VoteType` enum (from Governor Bravo).
*/
function _countVote(
uint256 proposalId,
address account,
uint8 support,
uint256 weight
) internal virtual override {
ProposalVote storage proposalvote = _proposalVotes[proposalId];
require(!proposalvote.hasVoted[account], "GovernorVotingSimple: vote already casted");
proposalvote.hasVoted[account] = true;
if (support == uint8(VoteType.Against)) {
proposalvote.againstVotes += weight;
} else if (support == uint8(VoteType.For)) {
proposalvote.forVotes += weight;
} else if (support == uint8(VoteType.Abstain)) {
proposalvote.abstainVotes += weight;
} else {
revert("GovernorVotingSimple: invalid value for enum VoteType");
}
}
uint256[49] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IGovernorTimelockUpgradeable.sol";
import "../GovernorUpgradeable.sol";
import "../../utils/math/SafeCastUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";
/**
* https://github.com/compound-finance/compound-protocol/blob/master/contracts/Timelock.sol[Compound's timelock] interface
*/
interface ICompoundTimelockUpgradeable {
receive() external payable;
// solhint-disable-next-line func-name-mixedcase
function GRACE_PERIOD() external view returns (uint256);
// solhint-disable-next-line func-name-mixedcase
function MINIMUM_DELAY() external view returns (uint256);
// solhint-disable-next-line func-name-mixedcase
function MAXIMUM_DELAY() external view returns (uint256);
function admin() external view returns (address);
function pendingAdmin() external view returns (address);
function delay() external view returns (uint256);
function queuedTransactions(bytes32) external view returns (bool);
function setDelay(uint256) external;
function acceptAdmin() external;
function setPendingAdmin(address) external;
function queueTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) external returns (bytes32);
function cancelTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) external;
function executeTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) external payable returns (bytes memory);
}
/**
* @dev Extension of {Governor} that binds the execution process to a Compound Timelock. This adds a delay, enforced by
* the external timelock to all successful proposal (in addition to the voting duration). The {Governor} needs to be
* the admin of the timelock for any operation to be performed. A public, unrestricted,
* {GovernorTimelockCompound-__acceptAdmin} is available to accept ownership of the timelock.
*
* Using this model means the proposal will be operated by the {TimelockController} and not by the {Governor}. Thus,
* the assets and permissions must be attached to the {TimelockController}. Any asset sent to the {Governor} will be
* inaccessible.
*
* _Available since v4.3._
*/
abstract contract GovernorTimelockCompoundUpgradeable is Initializable, IGovernorTimelockUpgradeable, GovernorUpgradeable {
using SafeCastUpgradeable for uint256;
using TimersUpgradeable for TimersUpgradeable.Timestamp;
struct ProposalTimelock {
TimersUpgradeable.Timestamp timer;
}
ICompoundTimelockUpgradeable private _timelock;
mapping(uint256 => ProposalTimelock) private _proposalTimelocks;
/**
* @dev Emitted when the timelock controller used for proposal execution is modified.
*/
event TimelockChange(address oldTimelock, address newTimelock);
/**
* @dev Set the timelock.
*/
function __GovernorTimelockCompound_init(ICompoundTimelockUpgradeable timelockAddress) internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__EIP712_init_unchained(name_, version());
__GovernorTimelockCompound_init_unchained(timelockAddress);
}
function __GovernorTimelockCompound_init_unchained(ICompoundTimelockUpgradeable timelockAddress) internal initializer {
_updateTimelock(timelockAddress);
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, GovernorUpgradeable) returns (bool) {
return interfaceId == type(IGovernorTimelockUpgradeable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Overriden version of the {Governor-state} function with added support for the `Queued` and `Expired` status.
*/
function state(uint256 proposalId) public view virtual override(IGovernorUpgradeable, GovernorUpgradeable) returns (ProposalState) {
ProposalState status = super.state(proposalId);
if (status != ProposalState.Succeeded) {
return status;
}
uint256 eta = proposalEta(proposalId);
if (eta == 0) {
return status;
} else if (block.timestamp >= eta + _timelock.GRACE_PERIOD()) {
return ProposalState.Expired;
} else {
return ProposalState.Queued;
}
}
/**
* @dev Public accessor to check the address of the timelock
*/
function timelock() public view virtual override returns (address) {
return address(_timelock);
}
/**
* @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();
}
/**
* @dev Function to queue a proposal to the timelock.
*/
function queue(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public virtual override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
require(state(proposalId) == ProposalState.Succeeded, "Governor: proposal not successful");
uint256 eta = block.timestamp + _timelock.delay();
_proposalTimelocks[proposalId].timer.setDeadline(eta.toUint64());
for (uint256 i = 0; i < targets.length; ++i) {
require(
!_timelock.queuedTransactions(keccak256(abi.encode(targets[i], values[i], "", calldatas[i], eta))),
"GovernorTimelockCompound: identical proposal action already queued"
);
_timelock.queueTransaction(targets[i], values[i], "", calldatas[i], eta);
}
emit ProposalQueued(proposalId, eta);
return proposalId;
}
/**
* @dev Overriden execute function that run the already queued proposal through the timelock.
*/
function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 /*descriptionHash*/
) internal virtual override {
uint256 eta = proposalEta(proposalId);
require(eta > 0, "GovernorTimelockCompound: proposal not yet queued");
for (uint256 i = 0; i < targets.length; ++i) {
_timelock.executeTransaction{value: values[i]}(targets[i], values[i], "", calldatas[i], eta);
}
}
/**
* @dev Overriden version of the {Governor-_cancel} function to cancel the timelocked proposal if it as already
* been queued.
*/
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual override returns (uint256) {
uint256 proposalId = super._cancel(targets, values, calldatas, descriptionHash);
uint256 eta = proposalEta(proposalId);
if (eta > 0) {
for (uint256 i = 0; i < targets.length; ++i) {
_timelock.cancelTransaction(targets[i], values[i], "", calldatas[i], eta);
}
_proposalTimelocks[proposalId].timer.reset();
}
return proposalId;
}
/**
* @dev Address through which the governor executes action. In this case, the timelock.
*/
function _executor() internal view virtual override returns (address) {
return address(_timelock);
}
/**
* @dev Accept admin right over the timelock.
*/
// solhint-disable-next-line private-vars-leading-underscore
function __acceptAdmin() public {
_timelock.acceptAdmin();
}
/**
* @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates
* must be proposed, scheduled and executed using the {Governor} workflow.
*
* For security reason, the timelock must be handed over to another admin before setting up a new one. The two
* operations (hand over the timelock) and do the update can be batched in a single proposal.
*
* Note that if the timelock admin has been handed over in a previous operation, we refuse updates made through the
* timelock if admin of the timelock has already been accepted and the operation is executed outside the scope of
* governance.
*/
function updateTimelock(ICompoundTimelockUpgradeable newTimelock) external virtual onlyGovernance {
_updateTimelock(newTimelock);
}
function _updateTimelock(ICompoundTimelockUpgradeable newTimelock) private {
emit TimelockChange(address(_timelock), address(newTimelock));
_timelock = newTimelock;
}
uint256[48] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IGovernorTimelockUpgradeable.sol";
import "../GovernorUpgradeable.sol";
import "../TimelockControllerUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";
/**
* @dev Extension of {Governor} that binds the execution process to an instance of {TimelockController}. This adds a
* delay, enforced by the {TimelockController} to all successful proposal (in addition to the voting duration). The
* {Governor} needs the proposer (an ideally the executor) roles for the {Governor} to work properly.
*
* Using this model means the proposal will be operated by the {TimelockController} and not by the {Governor}. Thus,
* the assets and permissions must be attached to the {TimelockController}. Any asset sent to the {Governor} will be
* inaccessible.
*
* _Available since v4.3._
*/
abstract contract GovernorTimelockControlUpgradeable is Initializable, IGovernorTimelockUpgradeable, GovernorUpgradeable {
TimelockControllerUpgradeable private _timelock;
mapping(uint256 => bytes32) private _timelockIds;
/**
* @dev Emitted when the timelock controller used for proposal execution is modified.
*/
event TimelockChange(address oldTimelock, address newTimelock);
/**
* @dev Set the timelock.
*/
function __GovernorTimelockControl_init(TimelockControllerUpgradeable timelockAddress) internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__EIP712_init_unchained(name_, version());
__GovernorTimelockControl_init_unchained(timelockAddress);
}
function __GovernorTimelockControl_init_unchained(TimelockControllerUpgradeable timelockAddress) internal initializer {
_updateTimelock(timelockAddress);
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, GovernorUpgradeable) returns (bool) {
return interfaceId == type(IGovernorTimelockUpgradeable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Overriden version of the {Governor-state} function with added support for the `Queued` status.
*/
function state(uint256 proposalId) public view virtual override(IGovernorUpgradeable, GovernorUpgradeable) returns (ProposalState) {
ProposalState status = super.state(proposalId);
if (status != ProposalState.Succeeded) {
return status;
}
// core tracks execution, so we just have to check if successful proposal have been queued.
bytes32 queueid = _timelockIds[proposalId];
if (queueid == bytes32(0)) {
return status;
} else if (_timelock.isOperationDone(queueid)) {
return ProposalState.Executed;
} else {
return ProposalState.Queued;
}
}
/**
* @dev Public accessor to check the address of the timelock
*/
function timelock() public view virtual override returns (address) {
return address(_timelock);
}
/**
* @dev Public accessor to check the eta of a queued proposal
*/
function proposalEta(uint256 proposalId) public view virtual override returns (uint256) {
uint256 eta = _timelock.getTimestamp(_timelockIds[proposalId]);
return eta == 1 ? 0 : eta; // _DONE_TIMESTAMP (1) should be replaced with a 0 value
}
/**
* @dev Function to queue a proposal to the timelock.
*/
function queue(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public virtual override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
require(state(proposalId) == ProposalState.Succeeded, "Governor: proposal not successful");
uint256 delay = _timelock.getMinDelay();
_timelockIds[proposalId] = _timelock.hashOperationBatch(targets, values, calldatas, 0, descriptionHash);
_timelock.scheduleBatch(targets, values, calldatas, 0, descriptionHash, delay);
emit ProposalQueued(proposalId, block.timestamp + delay);
return proposalId;
}
/**
* @dev Overriden execute function that run the already queued proposal through the timelock.
*/
function _execute(
uint256, /* proposalId */
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual override {
_timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash);
}
/**
* @dev Overriden version of the {Governor-_cancel} function to cancel the timelocked proposal if it as already
* been queued.
*/
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual override returns (uint256) {
uint256 proposalId = super._cancel(targets, values, calldatas, descriptionHash);
if (_timelockIds[proposalId] != 0) {
_timelock.cancel(_timelockIds[proposalId]);
delete _timelockIds[proposalId];
}
return proposalId;
}
/**
* @dev Address through which the governor executes action. In this case, the timelock.
*/
function _executor() internal view virtual override returns (address) {
return address(_timelock);
}
/**
* @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates
* must be proposed, scheduled and executed using the {Governor} workflow.
*/
function updateTimelock(TimelockControllerUpgradeable newTimelock) external virtual onlyGovernance {
_updateTimelock(newTimelock);
}
function _updateTimelock(TimelockControllerUpgradeable newTimelock) private {
emit TimelockChange(address(_timelock), address(newTimelock));
_timelock = newTimelock;
}
uint256[48] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../GovernorUpgradeable.sol";
import "../../token/ERC20/extensions/ERC20VotesCompUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";
/**
* @dev Extension of {Governor} for voting weight extraction from a Comp token.
*
* _Available since v4.3._
*/
abstract contract GovernorVotesCompUpgradeable is Initializable, GovernorUpgradeable {
ERC20VotesCompUpgradeable public token;
function __GovernorVotesComp_init(ERC20VotesCompUpgradeable token_) internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__EIP712_init_unchained(name_, version());
__GovernorVotesComp_init_unchained(token_);
}
function __GovernorVotesComp_init_unchained(ERC20VotesCompUpgradeable token_) internal initializer {
token = token_;
}
/**
* Read the voting weight from the token's built in snapshot mechanism (see {IGovernor-getVotes}).
*/
function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) {
return token.getPriorVotes(account, blockNumber);
}
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./GovernorVotesUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";
/**
* @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token and a quorum expressed as a
* fraction of the total supply.
*
* _Available since v4.3._
*/
abstract contract GovernorVotesQuorumFractionUpgradeable is Initializable, GovernorVotesUpgradeable {
uint256 private _quorumNumerator;
event QuorumNumeratorUpdated(uint256 oldQuorumNumerator, uint256 newQuorumNumerator);
function __GovernorVotesQuorumFraction_init(uint256 quorumNumeratorValue) internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__EIP712_init_unchained(name_, version());
__GovernorVotesQuorumFraction_init_unchained(quorumNumeratorValue);
}
function __GovernorVotesQuorumFraction_init_unchained(uint256 quorumNumeratorValue) internal initializer {
_updateQuorumNumerator(quorumNumeratorValue);
}
function quorumNumerator() public view virtual returns (uint256) {
return _quorumNumerator;
}
function quorumDenominator() public view virtual returns (uint256) {
return 100;
}
function quorum(uint256 blockNumber) public view virtual override returns (uint256) {
return (token.getPastTotalSupply(blockNumber) * quorumNumerator()) / quorumDenominator();
}
function updateQuorumNumerator(uint256 newQuorumNumerator) external virtual onlyGovernance {
_updateQuorumNumerator(newQuorumNumerator);
}
function _updateQuorumNumerator(uint256 newQuorumNumerator) internal virtual {
require(
newQuorumNumerator <= quorumDenominator(),
"GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator"
);
uint256 oldQuorumNumerator = _quorumNumerator;
_quorumNumerator = newQuorumNumerator;
emit QuorumNumeratorUpdated(oldQuorumNumerator, newQuorumNumerator);
}
uint256[49] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../GovernorUpgradeable.sol";
import "../../token/ERC20/extensions/ERC20VotesUpgradeable.sol";
import "../../utils/math/MathUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";
/**
* @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token.
*
* _Available since v4.3._
*/
abstract contract GovernorVotesUpgradeable is Initializable, GovernorUpgradeable {
ERC20VotesUpgradeable public token;
function __GovernorVotes_init(ERC20VotesUpgradeable tokenAddress) internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__EIP712_init_unchained(name_, version());
__GovernorVotes_init_unchained(tokenAddress);
}
function __GovernorVotes_init_unchained(ERC20VotesUpgradeable tokenAddress) internal initializer {
token = tokenAddress;
}
/**
* Read the voting weight from the token's built in snapshot mechanism (see {IGovernor-getVotes}).
*/
function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) {
return token.getPastVotes(account, blockNumber);
}
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IGovernorUpgradeable.sol";
/**
* @dev Extension of the {IGovernor} for timelock supporting modules.
*
* _Available since v4.3._
*/
interface IGovernorTimelockUpgradeable is IGovernorUpgradeable {
event ProposalQueued(uint256 proposalId, uint256 eta);
function timelock() external view returns (address);
function proposalEta(uint256 proposalId) external view returns (uint256);
function queue(
address[] calldata targets,
uint256[] calldata values,
bytes[] calldata calldatas,
bytes32 descriptionHash
) external returns (uint256 proposalId);
}
...@@ -40,6 +40,10 @@ contract EnumerableBytes32SetMockUpgradeable is Initializable { ...@@ -40,6 +40,10 @@ contract EnumerableBytes32SetMockUpgradeable is Initializable {
function at(uint256 index) public view returns (bytes32) { function at(uint256 index) public view returns (bytes32) {
return _set.at(index); return _set.at(index);
} }
function values() public view returns (bytes32[] memory) {
return _set.values();
}
uint256[48] private __gap; uint256[48] private __gap;
} }
...@@ -78,6 +82,10 @@ contract EnumerableAddressSetMockUpgradeable is Initializable { ...@@ -78,6 +82,10 @@ contract EnumerableAddressSetMockUpgradeable is Initializable {
function at(uint256 index) public view returns (address) { function at(uint256 index) public view returns (address) {
return _set.at(index); return _set.at(index);
} }
function values() public view returns (address[] memory) {
return _set.values();
}
uint256[48] private __gap; uint256[48] private __gap;
} }
...@@ -116,5 +124,9 @@ contract EnumerableUintSetMockUpgradeable is Initializable { ...@@ -116,5 +124,9 @@ contract EnumerableUintSetMockUpgradeable is Initializable {
function at(uint256 index) public view returns (uint256) { function at(uint256 index) public view returns (uint256) {
return _set.at(index); return _set.at(index);
} }
function values() public view returns (uint256[] memory) {
return _set.values();
}
uint256[48] private __gap; uint256[48] private __gap;
} }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../governance/GovernorUpgradeable.sol";
import "../governance/extensions/GovernorCountingSimpleUpgradeable.sol";
import "../governance/extensions/GovernorVotesCompUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
contract GovernorCompMockUpgradeable is Initializable, GovernorUpgradeable, GovernorVotesCompUpgradeable, GovernorCountingSimpleUpgradeable {
uint256 _votingDelay;
uint256 _votingPeriod;
function __GovernorCompMock_init(
string memory name_,
ERC20VotesCompUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_
) internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__EIP712_init_unchained(name_, version());
__Governor_init_unchained(name_);
__GovernorVotesComp_init_unchained(token_);
__GovernorCountingSimple_init_unchained();
__GovernorCompMock_init_unchained(name_, token_, votingDelay_, votingPeriod_);
}
function __GovernorCompMock_init_unchained(
string memory name_,
ERC20VotesCompUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_
) internal initializer {
_votingDelay = votingDelay_;
_votingPeriod = votingPeriod_;
}
receive() external payable {}
function votingDelay() public view override returns (uint256) {
return _votingDelay;
}
function votingPeriod() public view override returns (uint256) {
return _votingPeriod;
}
function quorum(uint256) public pure override returns (uint256) {
return 0;
}
function cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 salt
) public returns (uint256 proposalId) {
return _cancel(targets, values, calldatas, salt);
}
function getVotes(address account, uint256 blockNumber)
public
view
virtual
override(GovernorUpgradeable, GovernorVotesCompUpgradeable)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../governance/compatibility/GovernorCompatibilityBravoUpgradeable.sol";
import "../governance/extensions/GovernorVotesCompUpgradeable.sol";
import "../governance/extensions/GovernorTimelockCompoundUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
contract GovernorCompatibilityBravoMockUpgradeable is Initializable, GovernorCompatibilityBravoUpgradeable, GovernorTimelockCompoundUpgradeable, GovernorVotesCompUpgradeable {
uint256 _votingDelay;
uint256 _votingPeriod;
uint256 _proposalThreshold;
function __GovernorCompatibilityBravoMock_init(
string memory name_,
ERC20VotesCompUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_,
uint256 proposalThreshold_,
ICompoundTimelockUpgradeable timelock_
) internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__EIP712_init_unchained(name_, version());
__Governor_init_unchained(name_);
__GovernorCompatibilityBravo_init_unchained();
__GovernorTimelockCompound_init_unchained(timelock_);
__GovernorVotesComp_init_unchained(token_);
__GovernorCompatibilityBravoMock_init_unchained(name_, token_, votingDelay_, votingPeriod_, proposalThreshold_, timelock_);
}
function __GovernorCompatibilityBravoMock_init_unchained(
string memory name_,
ERC20VotesCompUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_,
uint256 proposalThreshold_,
ICompoundTimelockUpgradeable timelock_
) internal initializer {
_votingDelay = votingDelay_;
_votingPeriod = votingPeriod_;
_proposalThreshold = proposalThreshold_;
}
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(IERC165Upgradeable, GovernorUpgradeable, GovernorTimelockCompoundUpgradeable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
function votingDelay() public view override(IGovernorUpgradeable, GovernorUpgradeable) returns (uint256) {
return _votingDelay;
}
function votingPeriod() public view override(IGovernorUpgradeable, GovernorUpgradeable) returns (uint256) {
return _votingPeriod;
}
function proposalThreshold() public view virtual override returns (uint256) {
return _proposalThreshold;
}
function quorum(uint256) public pure override(IGovernorUpgradeable, GovernorUpgradeable) returns (uint256) {
return 0;
}
function state(uint256 proposalId)
public
view
virtual
override(IGovernorUpgradeable, GovernorUpgradeable, GovernorTimelockCompoundUpgradeable)
returns (ProposalState)
{
return super.state(proposalId);
}
function proposalEta(uint256 proposalId)
public
view
virtual
override(GovernorCompatibilityBravoUpgradeable, GovernorTimelockCompoundUpgradeable)
returns (uint256)
{
return super.proposalEta(proposalId);
}
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual override(IGovernorUpgradeable, GovernorUpgradeable, GovernorCompatibilityBravoUpgradeable) returns (uint256) {
return super.propose(targets, values, calldatas, description);
}
function queue(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 salt
) public virtual override(GovernorCompatibilityBravoUpgradeable, GovernorTimelockCompoundUpgradeable) returns (uint256) {
return super.queue(targets, values, calldatas, salt);
}
function execute(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 salt
) public payable virtual override(IGovernorUpgradeable, GovernorUpgradeable) returns (uint256) {
return super.execute(targets, values, calldatas, salt);
}
function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual override(GovernorUpgradeable, GovernorTimelockCompoundUpgradeable) {
super._execute(proposalId, targets, values, calldatas, descriptionHash);
}
/**
* @notice WARNING: this is for mock purposes only. Ability to the _cancel function should be restricted for live
* deployments.
*/
function cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 salt
) public returns (uint256 proposalId) {
return _cancel(targets, values, calldatas, salt);
}
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 salt
) internal virtual override(GovernorUpgradeable, GovernorTimelockCompoundUpgradeable) returns (uint256 proposalId) {
return super._cancel(targets, values, calldatas, salt);
}
function getVotes(address account, uint256 blockNumber)
public
view
virtual
override(IGovernorUpgradeable, GovernorVotesCompUpgradeable)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
function _executor() internal view virtual override(GovernorUpgradeable, GovernorTimelockCompoundUpgradeable) returns (address) {
return super._executor();
}
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../governance/GovernorUpgradeable.sol";
import "../governance/extensions/GovernorCountingSimpleUpgradeable.sol";
import "../governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
contract GovernorMockUpgradeable is Initializable, GovernorUpgradeable, GovernorVotesQuorumFractionUpgradeable, GovernorCountingSimpleUpgradeable {
uint256 _votingDelay;
uint256 _votingPeriod;
function __GovernorMock_init(
string memory name_,
ERC20VotesUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_,
uint256 quorumNumerator_
) internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__EIP712_init_unchained(name_, version());
__Governor_init_unchained(name_);
__GovernorVotes_init_unchained(token_);
__GovernorVotesQuorumFraction_init_unchained(quorumNumerator_);
__GovernorCountingSimple_init_unchained();
__GovernorMock_init_unchained(name_, token_, votingDelay_, votingPeriod_, quorumNumerator_);
}
function __GovernorMock_init_unchained(
string memory name_,
ERC20VotesUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_,
uint256 quorumNumerator_
) internal initializer {
_votingDelay = votingDelay_;
_votingPeriod = votingPeriod_;
}
receive() external payable {}
function votingDelay() public view override returns (uint256) {
return _votingDelay;
}
function votingPeriod() public view override returns (uint256) {
return _votingPeriod;
}
function cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 salt
) public returns (uint256 proposalId) {
return _cancel(targets, values, calldatas, salt);
}
function getVotes(address account, uint256 blockNumber)
public
view
virtual
override(GovernorUpgradeable, GovernorVotesUpgradeable)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../governance/extensions/GovernorTimelockCompoundUpgradeable.sol";
import "../governance/extensions/GovernorCountingSimpleUpgradeable.sol";
import "../governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
contract GovernorTimelockCompoundMockUpgradeable is Initializable, GovernorTimelockCompoundUpgradeable, GovernorVotesQuorumFractionUpgradeable, GovernorCountingSimpleUpgradeable {
uint256 _votingDelay;
uint256 _votingPeriod;
function __GovernorTimelockCompoundMock_init(
string memory name_,
ERC20VotesUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_,
ICompoundTimelockUpgradeable timelock_,
uint256 quorumNumerator_
) internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__EIP712_init_unchained(name_, version());
__Governor_init_unchained(name_);
__GovernorTimelockCompound_init_unchained(timelock_);
__GovernorVotes_init_unchained(token_);
__GovernorVotesQuorumFraction_init_unchained(quorumNumerator_);
__GovernorCountingSimple_init_unchained();
__GovernorTimelockCompoundMock_init_unchained(name_, token_, votingDelay_, votingPeriod_, timelock_, quorumNumerator_);
}
function __GovernorTimelockCompoundMock_init_unchained(
string memory name_,
ERC20VotesUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_,
ICompoundTimelockUpgradeable timelock_,
uint256 quorumNumerator_
) internal initializer {
_votingDelay = votingDelay_;
_votingPeriod = votingPeriod_;
}
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(GovernorUpgradeable, GovernorTimelockCompoundUpgradeable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
function votingDelay() public view override(IGovernorUpgradeable, GovernorUpgradeable) returns (uint256) {
return _votingDelay;
}
function votingPeriod() public view override(IGovernorUpgradeable, GovernorUpgradeable) returns (uint256) {
return _votingPeriod;
}
function quorum(uint256 blockNumber)
public
view
override(IGovernorUpgradeable, GovernorUpgradeable, GovernorVotesQuorumFractionUpgradeable)
returns (uint256)
{
return super.quorum(blockNumber);
}
function cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 salt
) public returns (uint256 proposalId) {
return _cancel(targets, values, calldatas, salt);
}
/**
* Overriding nightmare
*/
function state(uint256 proposalId)
public
view
virtual
override(GovernorUpgradeable, GovernorTimelockCompoundUpgradeable)
returns (ProposalState)
{
return super.state(proposalId);
}
function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual override(GovernorUpgradeable, GovernorTimelockCompoundUpgradeable) {
super._execute(proposalId, targets, values, calldatas, descriptionHash);
}
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 salt
) internal virtual override(GovernorUpgradeable, GovernorTimelockCompoundUpgradeable) returns (uint256 proposalId) {
return super._cancel(targets, values, calldatas, salt);
}
function getVotes(address account, uint256 blockNumber)
public
view
virtual
override(IGovernorUpgradeable, GovernorUpgradeable, GovernorVotesUpgradeable)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
function _executor() internal view virtual override(GovernorUpgradeable, GovernorTimelockCompoundUpgradeable) returns (address) {
return super._executor();
}
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../governance/extensions/GovernorTimelockControlUpgradeable.sol";
import "../governance/extensions/GovernorCountingSimpleUpgradeable.sol";
import "../governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
contract GovernorTimelockControlMockUpgradeable is Initializable, GovernorTimelockControlUpgradeable, GovernorVotesQuorumFractionUpgradeable, GovernorCountingSimpleUpgradeable {
uint256 _votingDelay;
uint256 _votingPeriod;
function __GovernorTimelockControlMock_init(
string memory name_,
ERC20VotesUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_,
TimelockControllerUpgradeable timelock_,
uint256 quorumNumerator_
) internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__EIP712_init_unchained(name_, version());
__Governor_init_unchained(name_);
__GovernorTimelockControl_init_unchained(timelock_);
__GovernorVotes_init_unchained(token_);
__GovernorVotesQuorumFraction_init_unchained(quorumNumerator_);
__GovernorCountingSimple_init_unchained();
__GovernorTimelockControlMock_init_unchained(name_, token_, votingDelay_, votingPeriod_, timelock_, quorumNumerator_);
}
function __GovernorTimelockControlMock_init_unchained(
string memory name_,
ERC20VotesUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_,
TimelockControllerUpgradeable timelock_,
uint256 quorumNumerator_
) internal initializer {
_votingDelay = votingDelay_;
_votingPeriod = votingPeriod_;
}
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(GovernorUpgradeable, GovernorTimelockControlUpgradeable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
function votingDelay() public view override(IGovernorUpgradeable, GovernorUpgradeable) returns (uint256) {
return _votingDelay;
}
function votingPeriod() public view override(IGovernorUpgradeable, GovernorUpgradeable) returns (uint256) {
return _votingPeriod;
}
function quorum(uint256 blockNumber)
public
view
override(IGovernorUpgradeable, GovernorUpgradeable, GovernorVotesQuorumFractionUpgradeable)
returns (uint256)
{
return super.quorum(blockNumber);
}
function cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public returns (uint256 proposalId) {
return _cancel(targets, values, calldatas, descriptionHash);
}
/**
* Overriding nightmare
*/
function state(uint256 proposalId)
public
view
virtual
override(GovernorUpgradeable, GovernorTimelockControlUpgradeable)
returns (ProposalState)
{
return super.state(proposalId);
}
function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual override(GovernorUpgradeable, GovernorTimelockControlUpgradeable) {
super._execute(proposalId, targets, values, calldatas, descriptionHash);
}
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual override(GovernorUpgradeable, GovernorTimelockControlUpgradeable) returns (uint256 proposalId) {
return super._cancel(targets, values, calldatas, descriptionHash);
}
function getVotes(address account, uint256 blockNumber)
public
view
virtual
override(IGovernorUpgradeable, GovernorUpgradeable, GovernorVotesUpgradeable)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
function _executor() internal view virtual override(GovernorUpgradeable, GovernorTimelockControlUpgradeable) returns (address) {
return super._executor();
}
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/TimersUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
contract TimersBlockNumberImplUpgradeable is Initializable {
function __TimersBlockNumberImpl_init() internal initializer {
__TimersBlockNumberImpl_init_unchained();
}
function __TimersBlockNumberImpl_init_unchained() internal initializer {
}
using TimersUpgradeable for TimersUpgradeable.BlockNumber;
TimersUpgradeable.BlockNumber private _timer;
function getDeadline() public view returns (uint64) {
return _timer.getDeadline();
}
function setDeadline(uint64 timestamp) public {
_timer.setDeadline(timestamp);
}
function reset() public {
_timer.reset();
}
function isUnset() public view returns (bool) {
return _timer.isUnset();
}
function isStarted() public view returns (bool) {
return _timer.isStarted();
}
function isPending() public view returns (bool) {
return _timer.isPending();
}
function isExpired() public view returns (bool) {
return _timer.isExpired();
}
uint256[49] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/TimersUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
contract TimersTimestampImplUpgradeable is Initializable {
function __TimersTimestampImpl_init() internal initializer {
__TimersTimestampImpl_init_unchained();
}
function __TimersTimestampImpl_init_unchained() internal initializer {
}
using TimersUpgradeable for TimersUpgradeable.Timestamp;
TimersUpgradeable.Timestamp private _timer;
function getDeadline() public view returns (uint64) {
return _timer.getDeadline();
}
function setDeadline(uint64 timestamp) public {
_timer.setDeadline(timestamp);
}
function reset() public {
_timer.reset();
}
function isUnset() public view returns (bool) {
return _timer.isUnset();
}
function isStarted() public view returns (bool) {
return _timer.isStarted();
}
function isPending() public view returns (bool) {
return _timer.isPending();
}
function isExpired() public view returns (bool) {
return _timer.isExpired();
}
uint256[49] private __gap;
}
// SPDX-License-Identifier: BSD-3-Clause
// solhint-disable private-vars-leading-underscore
/**
* Copyright 2020 Compound Labs, Inc.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
pragma solidity ^0.8.0;
import "../../proxy/utils/Initializable.sol";
contract CompTimelockUpgradeable is Initializable {
event NewAdmin(address indexed newAdmin);
event NewPendingAdmin(address indexed newPendingAdmin);
event NewDelay(uint256 indexed newDelay);
event CancelTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 eta
);
event ExecuteTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 eta
);
event QueueTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 eta
);
uint256 public constant GRACE_PERIOD = 14 days;
uint256 public constant MINIMUM_DELAY = 2 days;
uint256 public constant MAXIMUM_DELAY = 30 days;
address public admin;
address public pendingAdmin;
uint256 public delay;
mapping(bytes32 => bool) public queuedTransactions;
function __CompTimelock_init(address admin_, uint256 delay_) internal initializer {
__CompTimelock_init_unchained(admin_, delay_);
}
function __CompTimelock_init_unchained(address admin_, uint256 delay_) internal initializer {
require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay.");
require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
admin = admin_;
delay = delay_;
}
receive() external payable {}
function setDelay(uint256 delay_) public {
require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock.");
require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay.");
require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
delay = delay_;
emit NewDelay(delay);
}
function acceptAdmin() public {
require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin.");
admin = msg.sender;
pendingAdmin = address(0);
emit NewAdmin(admin);
}
function setPendingAdmin(address pendingAdmin_) public {
require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock.");
pendingAdmin = pendingAdmin_;
emit NewPendingAdmin(pendingAdmin);
}
function queueTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) public returns (bytes32) {
require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin.");
require(
eta >= getBlockTimestamp() + delay,
"Timelock::queueTransaction: Estimated execution block must satisfy delay."
);
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
queuedTransactions[txHash] = true;
emit QueueTransaction(txHash, target, value, signature, data, eta);
return txHash;
}
function cancelTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) public {
require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin.");
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
queuedTransactions[txHash] = false;
emit CancelTransaction(txHash, target, value, signature, data, eta);
}
function executeTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) public payable returns (bytes memory) {
require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
require(getBlockTimestamp() <= eta + GRACE_PERIOD, "Timelock::executeTransaction: Transaction is stale.");
queuedTransactions[txHash] = false;
bytes memory callData;
if (bytes(signature).length == 0) {
callData = data;
} else {
callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
}
// solium-disable-next-line security/no-call-value
(bool success, bytes memory returnData) = target.call{value: value}(callData);
require(success, "Timelock::executeTransaction: Transaction execution reverted.");
emit ExecuteTransaction(txHash, target, value, signature, data, eta);
return returnData;
}
function getBlockTimestamp() internal view returns (uint256) {
// solium-disable-next-line security/no-block-members
return block.timestamp;
}
uint256[46] private __gap;
}
...@@ -209,26 +209,7 @@ abstract contract ERC1967UpgradeUpgradeable is Initializable { ...@@ -209,26 +209,7 @@ abstract contract ERC1967UpgradeUpgradeable is Initializable {
// solhint-disable-next-line avoid-low-level-calls // solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data); (bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, "Address: low-level delegate call failed"); return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
} }
uint256[50] private __gap; uint256[50] private __gap;
} }
...@@ -18,9 +18,10 @@ import "../../proxy/utils/Initializable.sol"; ...@@ -18,9 +18,10 @@ import "../../proxy/utils/Initializable.sol";
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms]. * to implement supply mechanisms].
* *
* We have followed general OpenZeppelin guidelines: functions revert instead * We have followed general OpenZeppelin Contracts guidelines: functions revert
* of returning `false` on failure. This behavior is nonetheless conventional * instead returning `false` on failure. This behavior is nonetheless
* and does not conflict with the expectations of ERC20 applications. * conventional and does not conflict with the expectations of ERC20
* applications.
* *
* Additionally, an {Approval} event is emitted on calls to {transferFrom}. * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just * This allows applications to reconstruct the allowance for all accounts just
......
...@@ -33,7 +33,6 @@ Finally, there are some utilities to interact with ERC20 contracts in various wa ...@@ -33,7 +33,6 @@ Finally, there are some utilities to interact with ERC20 contracts in various wa
The following related EIPs are in draft status. The following related EIPs are in draft status.
- {ERC20Permit} - {ERC20Permit}
- {ERC20FlashMint}
NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC20 (such as <<ERC20-_mint-address-uint256-,`_mint`>>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc20.adoc#Presets[ERC20 Presets] (such as {ERC20PresetMinterPauser}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts. NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC20 (such as <<ERC20-_mint-address-uint256-,`_mint`>>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc20.adoc#Presets[ERC20 Presets] (such as {ERC20PresetMinterPauser}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts.
...@@ -61,14 +60,14 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel ...@@ -61,14 +60,14 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
{{ERC20Wrapper}} {{ERC20Wrapper}}
{{ERC20FlashMint}}
== Draft EIPs == Draft EIPs
The following EIPs are still in Draft status. Due to their nature as drafts, the details of these contracts may change and we cannot guarantee their xref:ROOT:releases-stability.adoc[stability]. Minor releases of OpenZeppelin Contracts may contain breaking changes for the contracts in this directory, which will be duly announced in the https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CHANGELOG.md[changelog]. The EIPs included here are used by projects in production and this may make them less likely to change significantly. The following EIPs are still in Draft status. Due to their nature as drafts, the details of these contracts may change and we cannot guarantee their xref:ROOT:releases-stability.adoc[stability]. Minor releases of OpenZeppelin Contracts may contain breaking changes for the contracts in this directory, which will be duly announced in the https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CHANGELOG.md[changelog]. The EIPs included here are used by projects in production and this may make them less likely to change significantly.
{{ERC20Permit}} {{ERC20Permit}}
{{ERC20FlashMint}}
== Presets == Presets
These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code.
......
...@@ -61,7 +61,7 @@ abstract contract ERC20FlashMintUpgradeable is Initializable, ERC20Upgradeable, ...@@ -61,7 +61,7 @@ abstract contract ERC20FlashMintUpgradeable is Initializable, ERC20Upgradeable,
* supported. * supported.
* @param amount The amount of tokens to be loaned. * @param amount The amount of tokens to be loaned.
* @param data An arbitrary datafield that is passed to the receiver. * @param data An arbitrary datafield that is passed to the receiver.
* @return `true` is the flash loan was successfull. * @return `true` is the flash loan was successful.
*/ */
function flashLoan( function flashLoan(
IERC3156FlashBorrowerUpgradeable receiver, IERC3156FlashBorrowerUpgradeable receiver,
......
...@@ -129,7 +129,7 @@ library AddressUpgradeable { ...@@ -129,7 +129,7 @@ library AddressUpgradeable {
require(isContract(target), "Address: call to non-contract"); require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data); (bool success, bytes memory returndata) = target.call{value: value}(data);
return _verifyCallResult(success, returndata, errorMessage); return verifyCallResult(success, returndata, errorMessage);
} }
/** /**
...@@ -156,14 +156,20 @@ library AddressUpgradeable { ...@@ -156,14 +156,20 @@ library AddressUpgradeable {
require(isContract(target), "Address: static call to non-contract"); require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data); (bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage); return verifyCallResult(success, returndata, errorMessage);
} }
function _verifyCallResult( /**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success, bool success,
bytes memory returndata, bytes memory returndata,
string memory errorMessage string memory errorMessage
) private pure returns (bytes memory) { ) internal pure returns (bytes memory) {
if (success) { if (success) {
return returndata; return returndata;
} else { } else {
......
...@@ -39,26 +39,7 @@ abstract contract MulticallUpgradeable is Initializable { ...@@ -39,26 +39,7 @@ abstract contract MulticallUpgradeable is Initializable {
// solhint-disable-next-line avoid-low-level-calls // solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data); (bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, "Address: low-level delegate call failed"); return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
} }
uint256[50] private __gap; uint256[50] private __gap;
} }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Tooling for timepoints, timers and delays
*/
library TimersUpgradeable {
struct Timestamp {
uint64 _deadline;
}
function getDeadline(Timestamp memory timer) internal pure returns (uint64) {
return timer._deadline;
}
function setDeadline(Timestamp storage timer, uint64 timestamp) internal {
timer._deadline = timestamp;
}
function reset(Timestamp storage timer) internal {
timer._deadline = 0;
}
function isUnset(Timestamp memory timer) internal pure returns (bool) {
return timer._deadline == 0;
}
function isStarted(Timestamp memory timer) internal pure returns (bool) {
return timer._deadline > 0;
}
function isPending(Timestamp memory timer) internal view returns (bool) {
return timer._deadline > block.timestamp;
}
function isExpired(Timestamp memory timer) internal view returns (bool) {
return isStarted(timer) && timer._deadline <= block.timestamp;
}
struct BlockNumber {
uint64 _deadline;
}
function getDeadline(BlockNumber memory timer) internal pure returns (uint64) {
return timer._deadline;
}
function setDeadline(BlockNumber storage timer, uint64 timestamp) internal {
timer._deadline = timestamp;
}
function reset(BlockNumber storage timer) internal {
timer._deadline = 0;
}
function isUnset(BlockNumber memory timer) internal pure returns (bool) {
return timer._deadline == 0;
}
function isStarted(BlockNumber memory timer) internal pure returns (bool) {
return timer._deadline > 0;
}
function isPending(BlockNumber memory timer) internal view returns (bool) {
return timer._deadline > block.number;
}
function isExpired(BlockNumber memory timer) internal view returns (bool) {
return isStarted(timer) && timer._deadline <= block.number;
}
}
...@@ -90,7 +90,7 @@ library ECDSAUpgradeable { ...@@ -90,7 +90,7 @@ library ECDSAUpgradeable {
) internal pure returns (address) { ) internal pure returns (address) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order. // signatures from current libraries generate a unique signature with an s-value in the lower half order.
// //
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
......
...@@ -130,6 +130,18 @@ library EnumerableSetUpgradeable { ...@@ -130,6 +130,18 @@ library EnumerableSetUpgradeable {
return set._values[index]; return set._values[index];
} }
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set // Bytes32Set
struct Bytes32Set { struct Bytes32Set {
...@@ -184,6 +196,18 @@ library EnumerableSetUpgradeable { ...@@ -184,6 +196,18 @@ library EnumerableSetUpgradeable {
return _at(set._inner, index); return _at(set._inner, index);
} }
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
return _values(set._inner);
}
// AddressSet // AddressSet
struct AddressSet { struct AddressSet {
...@@ -238,6 +262,25 @@ library EnumerableSetUpgradeable { ...@@ -238,6 +262,25 @@ library EnumerableSetUpgradeable {
return address(uint160(uint256(_at(set._inner, index)))); return address(uint160(uint256(_at(set._inner, index))));
} }
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly {
result := store
}
return result;
}
// UintSet // UintSet
struct UintSet { struct UintSet {
...@@ -291,4 +334,23 @@ library EnumerableSetUpgradeable { ...@@ -291,4 +334,23 @@ library EnumerableSetUpgradeable {
function at(UintSet storage set, uint256 index) internal view returns (uint256) { function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index)); return uint256(_at(set._inner, index));
} }
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly {
result := store
}
return result;
}
} }
...@@ -7,7 +7,7 @@ Access control—that is, "who is allowed to do this thing"—is incredibly impo ...@@ -7,7 +7,7 @@ Access control—that is, "who is allowed to do this thing"—is incredibly impo
The most common and basic form of access control is the concept of _ownership_: there's an account that is the `owner` of a contract and can do administrative tasks on it. This approach is perfectly reasonable for contracts that have a single administrative user. The most common and basic form of access control is the concept of _ownership_: there's an account that is the `owner` of a contract and can do administrative tasks on it. This approach is perfectly reasonable for contracts that have a single administrative user.
OpenZeppelin provides xref:api:access.adoc#Ownable[`Ownable`] for implementing ownership in your contracts. OpenZeppelin Contracts provides xref:api:access.adoc#Ownable[`Ownable`] for implementing ownership in your contracts.
[source,solidity] [source,solidity]
---- ----
......
...@@ -108,4 +108,4 @@ contract ERC20WithAutoMinerReward is ERC20 { ...@@ -108,4 +108,4 @@ contract ERC20WithAutoMinerReward is ERC20 {
[[wrapping-up]] [[wrapping-up]]
== Wrapping Up == Wrapping Up
We've seen two ways to implement ERC20 supply mechanisms: internally through `_mint`, and externally through `ERC20PresetMinterPauser`. Hopefully this has helped you understand how to use OpenZeppelin and some of the design principles behind it, and you can apply them to your own smart contracts. We've seen two ways to implement ERC20 supply mechanisms: internally through `_mint`, and externally through `ERC20PresetMinterPauser`. Hopefully this has helped you understand how to use OpenZeppelin Contracts and some of the design principles behind it, and you can apply them to your own smart contracts.
...@@ -24,7 +24,7 @@ After several months or a year a new major release may come out. These are not s ...@@ -24,7 +24,7 @@ After several months or a year a new major release may come out. These are not s
[[api-stability]] [[api-stability]]
== API Stability == API Stability
On the https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v2.0.0[OpenZeppelin 2.0 release], we committed ourselves to keeping a stable API. We aim to more precisely define what we understand by _stable_ and _API_ here, so users of the library can understand these guarantees and be confident their project won't break unexpectedly. On the https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v2.0.0[OpenZeppelin Contracts 2.0 release], we committed ourselves to keeping a stable API. We aim to more precisely define what we understand by _stable_ and _API_ here, so users of the library can understand these guarantees and be confident their project won't break unexpectedly.
In a nutshell, the API being stable means _if your project is working today, it will continue to do so after a minor upgrade_. New contracts and features will be added in minor releases, but only in a backwards compatible way. In a nutshell, the API being stable means _if your project is working today, it will continue to do so after a minor upgrade_. New contracts and features will be added in minor releases, but only in a backwards compatible way.
......
...@@ -23,6 +23,8 @@ for (const f of fs.readdirSync(path.join(__dirname, 'hardhat'))) { ...@@ -23,6 +23,8 @@ for (const f of fs.readdirSync(path.join(__dirname, 'hardhat'))) {
require(path.join(__dirname, 'hardhat', f)); require(path.join(__dirname, 'hardhat', f));
} }
const withOptimizations = argv.enableGasReport || argv.compileMode === 'production';
/** /**
* @type import('hardhat/config').HardhatUserConfig * @type import('hardhat/config').HardhatUserConfig
*/ */
...@@ -31,7 +33,7 @@ module.exports = { ...@@ -31,7 +33,7 @@ module.exports = {
version: '0.8.3', version: '0.8.3',
settings: { settings: {
optimizer: { optimizer: {
enabled: argv.enableGasReport || argv.compileMode === 'production', enabled: withOptimizations,
runs: 200, runs: 200,
}, },
}, },
...@@ -39,6 +41,7 @@ module.exports = { ...@@ -39,6 +41,7 @@ module.exports = {
networks: { networks: {
hardhat: { hardhat: {
blockGasLimit: 10000000, blockGasLimit: 10000000,
allowUnlimitedContractSize: !withOptimizations,
}, },
}, },
gasReporter: { gasReporter: {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -75,13 +75,13 @@ ...@@ -75,13 +75,13 @@
"merkletreejs": "^0.2.13", "merkletreejs": "^0.2.13",
"micromatch": "^4.0.2", "micromatch": "^4.0.2",
"prettier": "^2.3.0", "prettier": "^2.3.0",
"prettier-plugin-solidity": "^1.0.0-beta.13", "prettier-plugin-solidity": "^1.0.0-beta.16",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"solhint": "^3.3.6", "solhint": "^3.3.6",
"solidity-ast": "^0.4.25", "solidity-ast": "^0.4.25",
"solidity-coverage": "^0.7.11", "solidity-coverage": "^0.7.11",
"solidity-docgen": "^0.5.3", "solidity-docgen": "^0.5.3",
"web3": "^1.3.0", "web3": "^1.3.0",
"yargs": "^17.0.0" "yargs": "^16.2.0"
} }
} }
...@@ -21,16 +21,22 @@ for (const artifact of artifacts) { ...@@ -21,16 +21,22 @@ for (const artifact of artifacts) {
} }
} }
graphlib.alg.findCycles(graph).forEach(([ c1, c2 ]) => { /// graphlib.alg.findCycles will not find minimal cycles.
console.log(`Conflict between ${names[c1]} and ${names[c2]} detected in the following dependency chains:`); /// We are only interested int cycles of lengths 2 (needs proof)
linearized graph.nodes().forEach((x, i, nodes) => nodes
.filter(chain => chain.includes(parseInt(c1)) && chain.includes(parseInt(c2))) .slice(i + 1)
.forEach(chain => { .filter(y => graph.hasEdge(x, y) && graph.hasEdge(y, x))
const comp = chain.indexOf(c1) < chain.indexOf(c2) ? '>' : '<'; .map(y => {
console.log(`- ${names[c1]} ${comp} ${names[c2]}: ${chain.reverse().map(id => names[id]).join(', ')}`); console.log(`Conflict between ${names[x]} and ${names[y]} detected in the following dependency chains:`);
}); linearized
process.exitCode = 1; .filter(chain => chain.includes(parseInt(x)) && chain.includes(parseInt(y)))
}); .forEach(chain => {
const comp = chain.indexOf(parseInt(x)) < chain.indexOf(parseInt(y)) ? '>' : '<';
console.log(`- ${names[x]} ${comp} ${names[y]} in ${names[chain.find(Boolean)]}`);
// console.log(`- ${names[x]} ${comp} ${names[y]}: ${chain.reverse().map(id => names[id]).join(', ')}`);
});
process.exitCode = 1;
}));
} }
if (!process.exitCode) { if (!process.exitCode) {
......
...@@ -143,6 +143,8 @@ elif [[ "$*" == "final" ]]; then ...@@ -143,6 +143,8 @@ elif [[ "$*" == "final" ]]; then
push_and_publish latest push_and_publish latest
npm deprecate 'openzeppelin-solidity@>=4.0.0' "This package is now published as @openzeppelin/contracts. Please change your dependency."
log "Remember to merge the release branch into master and push upstream" log "Remember to merge the release branch into master and push upstream"
else else
......
## Testing ## Testing
Unit test are critical to the OpenZeppelin framework. They help ensure code quality and mitigate against security vulnerabilities. The directory structure within the `/test` directory corresponds to the `/contracts` directory. Unit test are critical to OpenZeppelin Contracts. They help ensure code quality and mitigate against security vulnerabilities. The directory structure within the `/test` directory corresponds to the `/contracts` directory.
const { expectRevert, time } = require('@openzeppelin/test-helpers');
async function getReceiptOrRevert (promise, error = undefined) {
if (error) {
await expectRevert(promise, error);
return undefined;
} else {
const { receipt } = await promise;
return receipt;
}
}
function tryGet (obj, path = '') {
try {
return path.split('.').reduce((o, k) => o[k], obj);
} catch (_) {
return undefined;
}
}
function runGovernorWorkflow () {
beforeEach(async function () {
this.receipts = {};
this.descriptionHash = web3.utils.keccak256(this.settings.proposal.slice(-1).find(Boolean));
this.id = await this.mock.hashProposal(...this.settings.proposal.slice(0, -1), this.descriptionHash);
});
it('run', async function () {
// transfer tokens
if (tryGet(this.settings, 'voters')) {
for (const voter of this.settings.voters) {
if (voter.weight) {
await this.token.transfer(voter.voter, voter.weight, { from: this.settings.tokenHolder });
}
}
}
// propose
if (this.mock.propose && tryGet(this.settings, 'steps.propose.enable') !== false) {
this.receipts.propose = await getReceiptOrRevert(
this.mock.methods['propose(address[],uint256[],bytes[],string)'](
...this.settings.proposal,
{ from: this.settings.proposer },
),
tryGet(this.settings, 'steps.propose.error'),
);
if (tryGet(this.settings, 'steps.propose.error') === undefined) {
this.deadline = await this.mock.proposalDeadline(this.id);
this.snapshot = await this.mock.proposalSnapshot(this.id);
}
if (tryGet(this.settings, 'steps.propose.delay')) {
await time.increase(tryGet(this.settings, 'steps.propose.delay'));
}
if (
tryGet(this.settings, 'steps.propose.error') === undefined &&
tryGet(this.settings, 'steps.propose.noadvance') !== true
) {
await time.advanceBlockTo(this.snapshot);
}
}
// vote
if (tryGet(this.settings, 'voters')) {
this.receipts.castVote = [];
for (const voter of this.settings.voters) {
if (!voter.signature) {
this.receipts.castVote.push(
await getReceiptOrRevert(
voter.reason
? this.mock.castVoteWithReason(this.id, voter.support, voter.reason, { from: voter.voter })
: this.mock.castVote(this.id, voter.support, { from: voter.voter }),
voter.error,
),
);
} else {
const { v, r, s } = await voter.signature({ proposalId: this.id, support: voter.support });
this.receipts.castVote.push(
await getReceiptOrRevert(
this.mock.castVoteBySig(this.id, voter.support, v, r, s),
voter.error,
),
);
}
if (tryGet(voter, 'delay')) {
await time.increase(tryGet(voter, 'delay'));
}
}
}
// fast forward
if (tryGet(this.settings, 'steps.wait.enable') !== false) {
await time.advanceBlockTo(this.deadline);
}
// queue
if (this.mock.queue && tryGet(this.settings, 'steps.queue.enable') !== false) {
this.receipts.queue = await getReceiptOrRevert(
this.mock.methods['queue(address[],uint256[],bytes[],bytes32)'](
...this.settings.proposal.slice(0, -1),
this.descriptionHash,
{ from: this.settings.queuer },
),
tryGet(this.settings, 'steps.queue.error'),
);
this.eta = await this.mock.proposalEta(this.id);
if (tryGet(this.settings, 'steps.queue.delay')) {
await time.increase(tryGet(this.settings, 'steps.queue.delay'));
}
}
// execute
if (this.mock.execute && tryGet(this.settings, 'steps.execute.enable') !== false) {
this.receipts.execute = await getReceiptOrRevert(
this.mock.methods['execute(address[],uint256[],bytes[],bytes32)'](
...this.settings.proposal.slice(0, -1),
this.descriptionHash,
{ from: this.settings.executer },
),
tryGet(this.settings, 'steps.execute.error'),
);
if (tryGet(this.settings, 'steps.execute.delay')) {
await time.increase(tryGet(this.settings, 'steps.execute.delay'));
}
}
});
}
module.exports = {
runGovernorWorkflow,
};
const { BN, expectEvent } = require('@openzeppelin/test-helpers');
const Enums = require('../../helpers/enums');
const {
runGovernorWorkflow,
} = require('./../GovernorWorkflow.behavior');
const Token = artifacts.require('ERC20VotesCompMock');
const Governor = artifacts.require('GovernorCompMock');
const CallReceiver = artifacts.require('CallReceiverMock');
contract('GovernorComp', function (accounts) {
const [ owner, voter1, voter2, voter3, voter4 ] = accounts;
const name = 'OZ-Governor';
// const version = '1';
const tokenName = 'MockToken';
const tokenSymbol = 'MTKN';
const tokenSupply = web3.utils.toWei('100');
beforeEach(async function () {
this.owner = owner;
this.token = await Token.new(tokenName, tokenSymbol);
this.mock = await Governor.new(name, this.token.address, 4, 16);
this.receiver = await CallReceiver.new();
await this.token.mint(owner, tokenSupply);
await this.token.delegate(voter1, { from: voter1 });
await this.token.delegate(voter2, { from: voter2 });
await this.token.delegate(voter3, { from: voter3 });
await this.token.delegate(voter4, { from: voter4 });
});
it('deployment check', async function () {
expect(await this.mock.name()).to.be.equal(name);
expect(await this.mock.token()).to.be.equal(this.token.address);
expect(await this.mock.votingDelay()).to.be.bignumber.equal('4');
expect(await this.mock.votingPeriod()).to.be.bignumber.equal('16');
expect(await this.mock.quorum(0)).to.be.bignumber.equal('0');
});
describe('voting with comp token', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
tokenHolder: owner,
voters: [
{ voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For },
{ voter: voter2, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
{ voter: voter3, weight: web3.utils.toWei('5'), support: Enums.VoteType.Against },
{ voter: voter4, weight: web3.utils.toWei('2'), support: Enums.VoteType.Abstain },
],
};
});
afterEach(async function () {
expect(await this.mock.hasVoted(this.id, owner)).to.be.equal(false);
expect(await this.mock.hasVoted(this.id, voter1)).to.be.equal(true);
expect(await this.mock.hasVoted(this.id, voter2)).to.be.equal(true);
expect(await this.mock.hasVoted(this.id, voter3)).to.be.equal(true);
expect(await this.mock.hasVoted(this.id, voter4)).to.be.equal(true);
this.receipts.castVote.filter(Boolean).forEach(vote => {
const { voter } = vote.logs.find(Boolean).args;
expectEvent(
vote,
'VoteCast',
this.settings.voters.find(({ address }) => address === voter),
);
});
await this.mock.proposalVotes(this.id).then(result => {
for (const [key, value] of Object.entries(Enums.VoteType)) {
expect(result[`${key.toLowerCase()}Votes`]).to.be.bignumber.equal(
Object.values(this.settings.voters).filter(({ support }) => support === value).reduce(
(acc, { weight }) => acc.add(new BN(weight)),
new BN('0'),
),
);
}
});
});
runGovernorWorkflow();
});
});
const { BN, expectEvent, time } = require('@openzeppelin/test-helpers');
const Enums = require('../../helpers/enums');
const {
runGovernorWorkflow,
} = require('./../GovernorWorkflow.behavior');
const Token = artifacts.require('ERC20VotesMock');
const Governor = artifacts.require('GovernorMock');
const CallReceiver = artifacts.require('CallReceiverMock');
contract('GovernorVotesQuorumFraction', function (accounts) {
const [ owner, voter1, voter2, voter3, voter4 ] = accounts;
const name = 'OZ-Governor';
// const version = '1';
const tokenName = 'MockToken';
const tokenSymbol = 'MTKN';
const tokenSupply = new BN(web3.utils.toWei('100'));
const ratio = new BN(8); // percents
const newRatio = new BN(6); // percents
beforeEach(async function () {
this.owner = owner;
this.token = await Token.new(tokenName, tokenSymbol);
this.mock = await Governor.new(name, this.token.address, 4, 16, ratio);
this.receiver = await CallReceiver.new();
await this.token.mint(owner, tokenSupply);
await this.token.delegate(voter1, { from: voter1 });
await this.token.delegate(voter2, { from: voter2 });
await this.token.delegate(voter3, { from: voter3 });
await this.token.delegate(voter4, { from: voter4 });
});
it('deployment check', async function () {
expect(await this.mock.name()).to.be.equal(name);
expect(await this.mock.token()).to.be.equal(this.token.address);
expect(await this.mock.votingDelay()).to.be.bignumber.equal('4');
expect(await this.mock.votingPeriod()).to.be.bignumber.equal('16');
expect(await this.mock.quorum(0)).to.be.bignumber.equal('0');
expect(await this.mock.quorumNumerator()).to.be.bignumber.equal(ratio);
expect(await this.mock.quorumDenominator()).to.be.bignumber.equal('100');
expect(await time.latestBlock().then(blockNumber => this.mock.quorum(blockNumber.subn(1))))
.to.be.bignumber.equal(tokenSupply.mul(ratio).divn(100));
});
describe('quroum not reached', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
tokenHolder: owner,
voters: [
{ voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For },
],
steps: {
execute: { error: 'Governor: proposal not successful' },
},
};
});
runGovernorWorkflow();
});
describe('update quorum ratio through proposal', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.mock.address ],
[ web3.utils.toWei('0') ],
[ this.mock.contract.methods.updateQuorumNumerator(newRatio).encodeABI() ],
'<proposal description>',
],
tokenHolder: owner,
voters: [
{ voter: voter1, weight: tokenSupply, support: Enums.VoteType.For },
],
};
});
afterEach(async function () {
await expectEvent.inTransaction(
this.receipts.execute.transactionHash,
this.mock,
'QuorumNumeratorUpdated',
{
oldQuorumNumerator: ratio,
newQuorumNumerator: newRatio,
},
);
expect(await this.mock.quorumNumerator()).to.be.bignumber.equal(newRatio);
expect(await this.mock.quorumDenominator()).to.be.bignumber.equal('100');
expect(await time.latestBlock().then(blockNumber => this.mock.quorum(blockNumber.subn(1))))
.to.be.bignumber.equal(tokenSupply.mul(newRatio).divn(100));
});
runGovernorWorkflow();
});
describe('update quorum over the maximum', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.mock.address ],
[ web3.utils.toWei('0') ],
[ this.mock.contract.methods.updateQuorumNumerator(new BN(101)).encodeABI() ],
'<proposal description>',
],
tokenHolder: owner,
voters: [
{ voter: voter1, weight: tokenSupply, support: Enums.VoteType.For },
],
steps: {
execute: { error: 'GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator' },
},
};
});
runGovernorWorkflow();
});
});
const { BN } = require('@openzeppelin/test-helpers');
function Enum (...options) {
return Object.fromEntries(options.map((key, i) => [ key, new BN(i) ]));
}
module.exports = {
Enum,
ProposalState: Enum(
'Pending',
'Active',
'Canceled',
'Defeated',
'Succeeded',
'Queued',
'Expired',
'Executed',
),
VoteType: Enum(
'Against',
'For',
'Abstain',
),
};
const { BN, time } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const TimersBlockNumberImpl = artifacts.require('TimersBlockNumberImpl');
contract('TimersBlockNumber', function (accounts) {
beforeEach(async function () {
this.instance = await TimersBlockNumberImpl.new();
this.now = await web3.eth.getBlock('latest').then(({ number }) => number);
});
it('unset', async function () {
expect(await this.instance.getDeadline()).to.be.bignumber.equal('0');
expect(await this.instance.isUnset()).to.be.equal(true);
expect(await this.instance.isStarted()).to.be.equal(false);
expect(await this.instance.isPending()).to.be.equal(false);
expect(await this.instance.isExpired()).to.be.equal(false);
});
it('pending', async function () {
await this.instance.setDeadline(this.now + 3);
expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(this.now + 3));
expect(await this.instance.isUnset()).to.be.equal(false);
expect(await this.instance.isStarted()).to.be.equal(true);
expect(await this.instance.isPending()).to.be.equal(true);
expect(await this.instance.isExpired()).to.be.equal(false);
});
it('expired', async function () {
await this.instance.setDeadline(this.now - 3);
expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(this.now - 3));
expect(await this.instance.isUnset()).to.be.equal(false);
expect(await this.instance.isStarted()).to.be.equal(true);
expect(await this.instance.isPending()).to.be.equal(false);
expect(await this.instance.isExpired()).to.be.equal(true);
});
it('reset', async function () {
await this.instance.reset();
expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(0));
expect(await this.instance.isUnset()).to.be.equal(true);
expect(await this.instance.isStarted()).to.be.equal(false);
expect(await this.instance.isPending()).to.be.equal(false);
expect(await this.instance.isExpired()).to.be.equal(false);
});
it('fast forward', async function () {
await this.instance.setDeadline(this.now + 3);
expect(await this.instance.isPending()).to.be.equal(true);
expect(await this.instance.isExpired()).to.be.equal(false);
await time.advanceBlockTo(this.now + 3);
expect(await this.instance.isPending()).to.be.equal(false);
expect(await this.instance.isExpired()).to.be.equal(true);
});
});
const { BN, time } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const TimersTimestampImpl = artifacts.require('TimersTimestampImpl');
contract('TimersTimestamp', function (accounts) {
beforeEach(async function () {
this.instance = await TimersTimestampImpl.new();
this.now = await web3.eth.getBlock('latest').then(({ timestamp }) => timestamp);
});
it('unset', async function () {
expect(await this.instance.getDeadline()).to.be.bignumber.equal('0');
expect(await this.instance.isUnset()).to.be.equal(true);
expect(await this.instance.isStarted()).to.be.equal(false);
expect(await this.instance.isPending()).to.be.equal(false);
expect(await this.instance.isExpired()).to.be.equal(false);
});
it('pending', async function () {
await this.instance.setDeadline(this.now + 100);
expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(this.now + 100));
expect(await this.instance.isUnset()).to.be.equal(false);
expect(await this.instance.isStarted()).to.be.equal(true);
expect(await this.instance.isPending()).to.be.equal(true);
expect(await this.instance.isExpired()).to.be.equal(false);
});
it('expired', async function () {
await this.instance.setDeadline(this.now - 100);
expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(this.now - 100));
expect(await this.instance.isUnset()).to.be.equal(false);
expect(await this.instance.isStarted()).to.be.equal(true);
expect(await this.instance.isPending()).to.be.equal(false);
expect(await this.instance.isExpired()).to.be.equal(true);
});
it('reset', async function () {
await this.instance.reset();
expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(0));
expect(await this.instance.isUnset()).to.be.equal(true);
expect(await this.instance.isStarted()).to.be.equal(false);
expect(await this.instance.isPending()).to.be.equal(false);
expect(await this.instance.isExpired()).to.be.equal(false);
});
it('fast forward', async function () {
await this.instance.setDeadline(this.now + 100);
expect(await this.instance.isPending()).to.be.equal(true);
expect(await this.instance.isExpired()).to.be.equal(false);
await time.increaseTo(this.now + 100);
expect(await this.instance.isPending()).to.be.equal(false);
expect(await this.instance.isExpired()).to.be.equal(true);
});
});
...@@ -50,6 +50,30 @@ const INTERFACES = { ...@@ -50,6 +50,30 @@ const INTERFACES = {
'getRoleMember(bytes32,uint256)', 'getRoleMember(bytes32,uint256)',
'getRoleMemberCount(bytes32)', 'getRoleMemberCount(bytes32)',
], ],
Governor: [
'name()',
'version()',
'COUNTING_MODE()',
'hashProposal(address[],uint256[],bytes[],bytes32)',
'state(uint256)',
'proposalSnapshot(uint256)',
'proposalDeadline(uint256)',
'votingDelay()',
'votingPeriod()',
'quorum(uint256)',
'getVotes(address,uint256)',
'hasVoted(uint256,address)',
'propose(address[],uint256[],bytes[],string)',
'execute(address[],uint256[],bytes[],bytes32)',
'castVote(uint256,uint8)',
'castVoteWithReason(uint256,uint8,string)',
'castVoteBySig(uint256,uint8,uint8,bytes32,bytes32)',
],
GovernorTimelock: [
'timelock()',
'proposalEta(uint256)',
'queue(address[],uint256[],bytes[],bytes32)',
],
}; };
const INTERFACE_IDS = {}; const INTERFACE_IDS = {};
......
...@@ -3,18 +3,27 @@ const { expect } = require('chai'); ...@@ -3,18 +3,27 @@ const { expect } = require('chai');
function shouldBehaveLikeSet (valueA, valueB, valueC) { function shouldBehaveLikeSet (valueA, valueB, valueC) {
async function expectMembersMatch (set, values) { async function expectMembersMatch (set, values) {
await Promise.all(values.map(async value => const contains = await Promise.all(values.map(value => set.contains(value)));
expect(await set.contains(value)).to.equal(true), expect(contains.every(Boolean)).to.be.equal(true);
));
expect(await set.length()).to.bignumber.equal(values.length.toString()); const length = await set.length();
expect(length).to.bignumber.equal(values.length.toString());
// To compare values we convert to strings to workaround Chai // To compare values we convert to strings to workaround Chai
// limitations when dealing with nested arrays (required for BNs) // limitations when dealing with nested arrays (required for BNs)
expect(await Promise.all([...Array(values.length).keys()].map(async (index) => { const indexedValues = await Promise.all(Array(values.length).fill().map((_, index) => set.at(index)));
const entry = await set.at(index); expect(
return entry.toString(); indexedValues.map(v => v.toString()),
}))).to.have.same.members(values.map(v => v.toString())); ).to.have.same.members(
values.map(v => v.toString()),
);
const returnedValues = await set.values();
expect(
returnedValues.map(v => v.toString()),
).to.have.same.members(
values.map(v => v.toString()),
);
} }
it('starts empty', async function () { it('starts empty', async function () {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment