Commit 222e8750 by github-actions

Transpile cc39fb66

parent 5c629305
......@@ -10,5 +10,5 @@ about: Suggest an idea for OpenZeppelin Contracts
**📝 Details**
<!-- 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 -->
......@@ -3,6 +3,8 @@
## 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))
* `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)
......@@ -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))
* `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)))
......
......@@ -49,9 +49,9 @@ npm run lint
*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.
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!
......
Design Guidelines
=======
These are some global design goals in OpenZeppelin.
These are some global design goals in OpenZeppelin Contracts.
#### 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.
......
......@@ -77,9 +77,9 @@ Finally, you may want to take a look at the [guides on our blog](https://blog.op
## 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.
......@@ -87,8 +87,8 @@ Please report any security issues you find to security@openzeppelin.org.
## 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
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`.
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
[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
......
# 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
Authored by Dennis Peterson and Peter Vessenes
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/cryptography/ECDSAUpgradeable.sol";
import "../utils/cryptography/draft-EIP712Upgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../utils/math/SafeCastUpgradeable.sol";
import "../utils/AddressUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/TimersUpgradeable.sol";
import "./IGovernorUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Core of the governance system, designed to be extended though various modules.
*
* This contract is abstract and requiers several function to be implemented in various modules:
*
* - A counting module must implement {quorum}, {_quorumReached}, {_voteSucceeded} and {_countVote}
* - A voting module must implement {getVotes}
* - Additionanly, the {votingPeriod} must also be implemented
*
* _Available since v4.3._
*/
abstract contract GovernorUpgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, EIP712Upgradeable, IGovernorUpgradeable {
using SafeCastUpgradeable for uint256;
using TimersUpgradeable for TimersUpgradeable.BlockNumber;
bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)");
struct ProposalCore {
TimersUpgradeable.BlockNumber voteStart;
TimersUpgradeable.BlockNumber voteEnd;
bool executed;
bool canceled;
}
string private _name;
mapping(uint256 => ProposalCore) private _proposals;
/**
* @dev Restrict access to governor executing address. Some module might override the _executor function to make
* sure this modifier is consistant with the execution model.
*/
modifier onlyGovernance() {
require(_msgSender() == _executor(), "Governor: onlyGovernance");
_;
}
/**
* @dev Sets the value for {name} and {version}
*/
function __Governor_init(string memory name_) internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__EIP712_init_unchained(name_, version());
__Governor_init_unchained(name_);
}
function __Governor_init_unchained(string memory name_) internal initializer {
_name = name_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC165Upgradeable) returns (bool) {
return interfaceId == type(IGovernorUpgradeable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev See {IGovernor-name}.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev See {IGovernor-version}.
*/
function version() public view virtual override returns (string memory) {
return "1";
}
/**
* @dev See {IGovernor-hashProposal}.
*
* The proposal id is produced by hashing the RLC encoded `targets` array, the `values` array, the `calldatas` array
* and the descriptionHash (bytes32 which itself is the keccak256 hash of the description string). This proposal id
* can be produced from the proposal data which is part of the {ProposalCreated} event. It can even be computed in
* advance, before the proposal is submitted.
*
* Note that the chainId and the governor address are not part of the proposal id computation. Consequently, the
* same proposal (with same operation and same description) will have the same id if submitted on multiple governors
* accross multiple networks. This also means that in order to execute the same operation twice (on the same
* governor) the proposer will have to change the description in order to avoid proposal id conflicts.
*/
function hashProposal(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public pure virtual override returns (uint256) {
return uint256(keccak256(abi.encode(targets, values, calldatas, descriptionHash)));
}
/**
* @dev See {IGovernor-state}.
*/
function state(uint256 proposalId) public view virtual override returns (ProposalState) {
ProposalCore memory proposal = _proposals[proposalId];
if (proposal.executed) {
return ProposalState.Executed;
} else if (proposal.canceled) {
return ProposalState.Canceled;
} else if (proposal.voteStart.isPending()) {
return ProposalState.Pending;
} else if (proposal.voteEnd.isPending()) {
return ProposalState.Active;
} else if (proposal.voteEnd.isExpired()) {
return
_quorumReached(proposalId) && _voteSucceeded(proposalId)
? ProposalState.Succeeded
: ProposalState.Defeated;
} else {
revert("Governor: unknown proposal id");
}
}
/**
* @dev See {IGovernor-proposalSnapshot}.
*/
function proposalSnapshot(uint256 proposalId) public view virtual override returns (uint256) {
return _proposals[proposalId].voteStart.getDeadline();
}
/**
* @dev See {IGovernor-proposalDeadline}.
*/
function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) {
return _proposals[proposalId].voteEnd.getDeadline();
}
/**
* @dev See {IGovernor-votingDelay}
*/
function votingDelay() public view virtual override returns (uint256);
/**
* @dev See {IGovernor-votingPeriod}
*/
function votingPeriod() public view virtual override returns (uint256);
/**
* @dev See {IGovernor-quorum}
*/
function quorum(uint256 blockNumber) public view virtual override returns (uint256);
/**
* @dev See {IGovernor-getVotes}
*/
function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256);
/**
* @dev Amount of votes already casted passes the threshold limit.
*/
function _quorumReached(uint256 proposalId) internal view virtual returns (bool);
/**
* @dev Is the proposal successful or not.
*/
function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool);
/**
* @dev Register a vote with a given support and voting weight.
*
* Note: Support is generic and can represent various things depending on the voting system used.
*/
function _countVote(
uint256 proposalId,
address account,
uint8 support,
uint256 weight
) internal virtual;
/**
* @dev See {IGovernor-propose}.
*/
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, keccak256(bytes(description)));
require(targets.length == values.length, "Governor: invalid proposal length");
require(targets.length == calldatas.length, "Governor: invalid proposal length");
require(targets.length > 0, "Governor: empty proposal");
ProposalCore storage proposal = _proposals[proposalId];
require(proposal.voteStart.isUnset(), "Governor: proposal already exists");
uint64 snapshot = block.number.toUint64() + votingDelay().toUint64();
uint64 deadline = snapshot + votingPeriod().toUint64();
proposal.voteStart.setDeadline(snapshot);
proposal.voteEnd.setDeadline(deadline);
emit ProposalCreated(
proposalId,
_msgSender(),
targets,
values,
new string[](targets.length),
calldatas,
snapshot,
deadline,
description
);
return proposalId;
}
/**
* @dev See {IGovernor-execute}.
*/
function execute(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public payable virtual override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
ProposalState status = state(proposalId);
require(
status == ProposalState.Succeeded || status == ProposalState.Queued,
"Governor: proposal not successful"
);
_proposals[proposalId].executed = true;
emit ProposalExecuted(proposalId);
_execute(proposalId, targets, values, calldatas, descriptionHash);
return proposalId;
}
/**
* @dev Internal execution mechanism. Can be overriden to implement different execution mechanism
*/
function _execute(
uint256, /* proposalId */
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 /*descriptionHash*/
) internal virtual {
string memory errorMessage = "Governor: call reverted without message";
for (uint256 i = 0; i < targets.length; ++i) {
(bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]);
AddressUpgradeable.verifyCallResult(success, returndata, errorMessage);
}
}
/**
* @dev Internal cancel mechanism: locks up the proposal timer, preventing it from being re-submitted. Marks it as
* canceled to allow distinguishing it from executed proposals.
*
* Emits a {IGovernor-ProposalCanceled} event.
*/
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
ProposalState status = state(proposalId);
require(
status != ProposalState.Canceled && status != ProposalState.Expired && status != ProposalState.Executed,
"Governor: proposal not active"
);
_proposals[proposalId].canceled = true;
emit ProposalCanceled(proposalId);
return proposalId;
}
/**
* @dev See {IGovernor-castVote}.
*/
function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) {
address voter = _msgSender();
return _castVote(proposalId, voter, support, "");
}
/**
* @dev See {IGovernor-castVoteWithReason}.
*/
function castVoteWithReason(
uint256 proposalId,
uint8 support,
string calldata reason
) public virtual override returns (uint256) {
address voter = _msgSender();
return _castVote(proposalId, voter, support, reason);
}
/**
* @dev See {IGovernor-castVoteBySig}.
*/
function castVoteBySig(
uint256 proposalId,
uint8 support,
uint8 v,
bytes32 r,
bytes32 s
) public virtual override returns (uint256) {
address voter = ECDSAUpgradeable.recover(
_hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support))),
v,
r,
s
);
return _castVote(proposalId, voter, support, "");
}
/**
* @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been casted yet, retrieve
* voting weight using {IGovernor-getVotes} and call the {_countVote} internal function.
*
* Emits a {IGovernor-VoteCast} event.
*/
function _castVote(
uint256 proposalId,
address account,
uint8 support,
string memory reason
) internal virtual returns (uint256) {
ProposalCore storage proposal = _proposals[proposalId];
require(state(proposalId) == ProposalState.Active, "Governor: vote not currently active");
uint256 weight = getVotes(account, proposal.voteStart.getDeadline());
_countVote(proposalId, account, support, weight);
emit VoteCast(account, proposalId, support, weight, reason);
return weight;
}
/**
* @dev Address through which the governor executes action. Will be overloaded by module that execute actions
* through another contract such as a timelock.
*/
function _executor() internal view virtual returns (address) {
return address(this);
}
uint256[48] private __gap;
}
// 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 @@
[.readme-notice]
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
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}}
[[timelock-terminology]]
......@@ -27,7 +81,7 @@ This directory includes primitives for on-chain governance. We currently only of
[[timelock-operation]]
==== 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:
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../utils/CountersUpgradeable.sol";
import "../../utils/math/SafeCastUpgradeable.sol";
import "../extensions/IGovernorTimelockUpgradeable.sol";
import "../GovernorUpgradeable.sol";
import "./IGovernorCompatibilityBravoUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";
/**
* @dev Compatibility layer that implements GovernorBravo compatibility on to of {Governor}.
*
* This compatibility layer includes a voting system and requires a {IGovernorTimelock} compatible module to be added
* through inheritance. It does not include token bindings, not does it include any variable upgrade patterns.
*
* _Available since v4.3._
*/
abstract contract GovernorCompatibilityBravoUpgradeable is Initializable, IGovernorTimelockUpgradeable, IGovernorCompatibilityBravoUpgradeable, GovernorUpgradeable {
function __GovernorCompatibilityBravo_init() internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__EIP712_init_unchained(name_, version());
__GovernorCompatibilityBravo_init_unchained();
}
function __GovernorCompatibilityBravo_init_unchained() internal initializer {
}
using CountersUpgradeable for CountersUpgradeable.Counter;
using TimersUpgradeable for TimersUpgradeable.BlockNumber;
enum VoteType {
Against,
For,
Abstain
}
struct ProposalDetails {
address proposer;
address[] targets;
uint256[] values;
string[] signatures;
bytes[] calldatas;
uint256 forVotes;
uint256 againstVotes;
uint256 abstainVotes;
mapping(address => Receipt) receipts;
bytes32 descriptionHash;
}
mapping(uint256 => ProposalDetails) private _proposalDetails;
// public for hooking
function proposalThreshold() public view virtual override returns (uint256);
// public for hooking
function proposalEta(uint256 proposalId) public view virtual override returns (uint256);
// public for hooking
function queue(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public virtual override returns (uint256);
// solhint-disable-next-line func-name-mixedcase
function COUNTING_MODE() public pure virtual override returns (string memory) {
return "support=bravo&quorum=bravo";
}
// ============================================== Proposal lifecycle ==============================================
/**
* @dev See {IGovernor-propose}.
*/
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual override(IGovernorUpgradeable, GovernorUpgradeable) returns (uint256) {
return propose(targets, values, new string[](calldatas.length), calldatas, description);
}
/**
* @dev See {IGovernorCompatibilityBravo-propose}.
*/
function propose(
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas,
string memory description
) public virtual override returns (uint256) {
require(
getVotes(msg.sender, block.number - 1) >= proposalThreshold(),
"GovernorCompatibilityBravo: proposer votes below proposal threshold"
);
uint256 proposalId = super.propose(targets, values, _encodeCalldata(signatures, calldatas), description);
_storeProposal(proposalId, _msgSender(), targets, values, signatures, calldatas, description);
return proposalId;
}
/**
* @dev See {IGovernorCompatibilityBravo-queue}.
*/
function queue(uint256 proposalId) public virtual override {
ProposalDetails storage details = _proposalDetails[proposalId];
queue(
details.targets,
details.values,
_encodeCalldata(details.signatures, details.calldatas),
details.descriptionHash
);
}
/**
* @dev See {IGovernorCompatibilityBravo-execute}.
*/
function execute(uint256 proposalId) public payable virtual override {
ProposalDetails storage details = _proposalDetails[proposalId];
execute(
details.targets,
details.values,
_encodeCalldata(details.signatures, details.calldatas),
details.descriptionHash
);
}
/**
* @dev Encodes calldatas with optional function signature.
*/
function _encodeCalldata(string[] memory signatures, bytes[] memory calldatas)
private
pure
returns (bytes[] memory)
{
bytes[] memory fullcalldatas = new bytes[](calldatas.length);
for (uint256 i = 0; i < signatures.length; ++i) {
fullcalldatas[i] = bytes(signatures[i]).length == 0
? calldatas[i]
: abi.encodePacked(bytes4(keccak256(bytes(signatures[i]))), calldatas[i]);
}
return fullcalldatas;
}
/**
* @dev Store proposal metadata for later lookup
*/
function _storeProposal(
uint256 proposalId,
address proposer,
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas,
string memory description
) private {
ProposalDetails storage details = _proposalDetails[proposalId];
details.proposer = proposer;
details.targets = targets;
details.values = values;
details.signatures = signatures;
details.calldatas = calldatas;
details.descriptionHash = keccak256(bytes(description));
}
// ==================================================== Views =====================================================
/**
* @dev See {IGovernorCompatibilityBravo-proposals}.
*/
function proposals(uint256 proposalId)
public
view
virtual
override
returns (
uint256 id,
address proposer,
uint256 eta,
uint256 startBlock,
uint256 endBlock,
uint256 forVotes,
uint256 againstVotes,
uint256 abstainVotes,
bool canceled,
bool executed
)
{
id = proposalId;
eta = proposalEta(proposalId);
startBlock = proposalSnapshot(proposalId);
endBlock = proposalDeadline(proposalId);
ProposalDetails storage details = _proposalDetails[proposalId];
proposer = details.proposer;
forVotes = details.forVotes;
againstVotes = details.againstVotes;
abstainVotes = details.abstainVotes;
ProposalState status = state(proposalId);
canceled = status == ProposalState.Canceled;
executed = status == ProposalState.Executed;
}
/**
* @dev See {IGovernorCompatibilityBravo-getActions}.
*/
function getActions(uint256 proposalId)
public
view
virtual
override
returns (
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas
)
{
ProposalDetails storage details = _proposalDetails[proposalId];
return (details.targets, details.values, details.signatures, details.calldatas);
}
/**
* @dev See {IGovernorCompatibilityBravo-getReceipt}.
*/
function getReceipt(uint256 proposalId, address voter) public view virtual override returns (Receipt memory) {
return _proposalDetails[proposalId].receipts[voter];
}
/**
* @dev See {IGovernorCompatibilityBravo-quorumVotes}.
*/
function quorumVotes() public view virtual override returns (uint256) {
return quorum(block.number - 1);
}
// ==================================================== Voting ====================================================
/**
* @dev See {IGovernor-hasVoted}.
*/
function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) {
return _proposalDetails[proposalId].receipts[account].hasVoted;
}
/**
* @dev See {Governor-_quorumReached}. In this module, only forVotes count toward the quorum.
*/
function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) {
ProposalDetails storage details = _proposalDetails[proposalId];
return quorum(proposalSnapshot(proposalId)) < details.forVotes;
}
/**
* @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) {
ProposalDetails storage details = _proposalDetails[proposalId];
return details.forVotes > details.againstVotes;
}
/**
* @dev See {Governor-_countVote}. In this module, the support follows Governor Bravo.
*/
function _countVote(
uint256 proposalId,
address account,
uint8 support,
uint256 weight
) internal virtual override {
ProposalDetails storage details = _proposalDetails[proposalId];
Receipt storage receipt = details.receipts[account];
require(!receipt.hasVoted, "GovernorCompatibilityBravo: vote already casted");
receipt.hasVoted = true;
receipt.support = support;
receipt.votes = SafeCastUpgradeable.toUint96(weight);
if (support == uint8(VoteType.Against)) {
details.againstVotes += weight;
} else if (support == uint8(VoteType.For)) {
details.forVotes += weight;
} else if (support == uint8(VoteType.Abstain)) {
details.abstainVotes += weight;
} else {
revert("GovernorCompatibilityBravo: invalid vote type");
}
}
uint256[49] private __gap;
}
// 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 {
function at(uint256 index) public view returns (bytes32) {
return _set.at(index);
}
function values() public view returns (bytes32[] memory) {
return _set.values();
}
uint256[48] private __gap;
}
......@@ -78,6 +82,10 @@ contract EnumerableAddressSetMockUpgradeable is Initializable {
function at(uint256 index) public view returns (address) {
return _set.at(index);
}
function values() public view returns (address[] memory) {
return _set.values();
}
uint256[48] private __gap;
}
......@@ -116,5 +124,9 @@ contract EnumerableUintSetMockUpgradeable is Initializable {
function at(uint256 index) public view returns (uint256) {
return _set.at(index);
}
function values() public view returns (uint256[] memory) {
return _set.values();
}
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;
}
......@@ -19,22 +19,18 @@ contract TimelockControllerUpgradeableWithInit is TimelockControllerUpgradeable
__TimelockController_init(minDelay, proposers, executors);
}
}
import "../token/ERC721/presets/ERC721PresetMinterPauserAutoIdUpgradeable.sol";
import "./GovernorTimelockControlMockUpgradeable.sol";
contract ERC721PresetMinterPauserAutoIdUpgradeableWithInit is ERC721PresetMinterPauserAutoIdUpgradeable {
contract GovernorTimelockControlMockUpgradeableWithInit is GovernorTimelockControlMockUpgradeable {
constructor(
string memory name,
string memory symbol,
string memory baseTokenURI
string memory name_,
ERC20VotesUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_,
TimelockControllerUpgradeable timelock_,
uint256 quorumNumerator_
) public payable {
__ERC721PresetMinterPauserAutoId_init(name, symbol, baseTokenURI);
}
}
import "../token/ERC721/ERC721Upgradeable.sol";
contract ERC721UpgradeableWithInit is ERC721Upgradeable {
constructor(string memory name_, string memory symbol_) public payable {
__ERC721_init(name_, symbol_);
__GovernorTimelockControlMock_init(name_, token_, votingDelay_, votingPeriod_, timelock_, quorumNumerator_);
}
}
import "../token/ERC20/ERC20Upgradeable.sol";
......@@ -44,11 +40,64 @@ contract ERC20UpgradeableWithInit is ERC20Upgradeable {
__ERC20_init(name_, symbol_);
}
}
import "./MathMockUpgradeable.sol";
import "./GovernorTimelockCompoundMockUpgradeable.sol";
contract MathMockUpgradeableWithInit is MathMockUpgradeable {
contract GovernorTimelockCompoundMockUpgradeableWithInit is GovernorTimelockCompoundMockUpgradeable {
constructor(
string memory name_,
ERC20VotesUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_,
ICompoundTimelockUpgradeable timelock_,
uint256 quorumNumerator_
) public payable {
__GovernorTimelockCompoundMock_init(name_, token_, votingDelay_, votingPeriod_, timelock_, quorumNumerator_);
}
}
import "./GovernorMockUpgradeable.sol";
contract GovernorMockUpgradeableWithInit is GovernorMockUpgradeable {
constructor(
string memory name_,
ERC20VotesUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_,
uint256 quorumNumerator_
) public payable {
__GovernorMock_init(name_, token_, votingDelay_, votingPeriod_, quorumNumerator_);
}
}
import "./GovernorCompMockUpgradeable.sol";
contract GovernorCompMockUpgradeableWithInit is GovernorCompMockUpgradeable {
constructor(
string memory name_,
ERC20VotesCompUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_
) public payable {
__GovernorCompMock_init(name_, token_, votingDelay_, votingPeriod_);
}
}
import "./GovernorCompatibilityBravoMockUpgradeable.sol";
contract GovernorCompatibilityBravoMockUpgradeableWithInit is GovernorCompatibilityBravoMockUpgradeable {
constructor(
string memory name_,
ERC20VotesCompUpgradeable token_,
uint256 votingDelay_,
uint256 votingPeriod_,
uint256 proposalThreshold_,
ICompoundTimelockUpgradeable timelock_
) public payable {
__GovernorCompatibilityBravoMock_init(name_, token_, votingDelay_, votingPeriod_, proposalThreshold_, timelock_);
}
}
import "./SafeCastMockUpgradeable.sol";
contract SafeCastMockUpgradeableWithInit is SafeCastMockUpgradeable {
constructor() public payable {
__MathMock_init();
__SafeCastMock_init();
}
}
import "./ERC20VotesMockUpgradeable.sol";
......@@ -58,6 +107,13 @@ contract ERC20VotesMockUpgradeableWithInit is ERC20VotesMockUpgradeable {
__ERC20VotesMock_init(name, symbol);
}
}
import "./ERC20VotesCompMockUpgradeable.sol";
contract ERC20VotesCompMockUpgradeableWithInit is ERC20VotesCompMockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC20VotesCompMock_init(name, symbol);
}
}
import "./ERC1271WalletMockUpgradeable.sol";
contract ERC1271WalletMockUpgradeableWithInit is ERC1271WalletMockUpgradeable {
......@@ -91,74 +147,102 @@ contract ERC20PresetMinterPauserUpgradeableWithInit is ERC20PresetMinterPauserUp
__ERC20PresetMinterPauser_init(name, symbol);
}
}
import "../token/ERC1155/presets/ERC1155PresetMinterPauserUpgradeable.sol";
import "../token/ERC721/presets/ERC721PresetMinterPauserAutoIdUpgradeable.sol";
contract ERC1155PresetMinterPauserUpgradeableWithInit is ERC1155PresetMinterPauserUpgradeable {
constructor(string memory uri) public payable {
__ERC1155PresetMinterPauser_init(uri);
contract ERC721PresetMinterPauserAutoIdUpgradeableWithInit is ERC721PresetMinterPauserAutoIdUpgradeable {
constructor(
string memory name,
string memory symbol,
string memory baseTokenURI
) public payable {
__ERC721PresetMinterPauserAutoId_init(name, symbol, baseTokenURI);
}
}
import "../token/ERC1155/ERC1155Upgradeable.sol";
import "../token/ERC721/ERC721Upgradeable.sol";
contract ERC1155UpgradeableWithInit is ERC1155Upgradeable {
constructor(string memory uri_) public payable {
__ERC1155_init(uri_);
contract ERC721UpgradeableWithInit is ERC721Upgradeable {
constructor(string memory name_, string memory symbol_) public payable {
__ERC721_init(name_, symbol_);
}
}
import "./PausableMockUpgradeable.sol";
import "./MathMockUpgradeable.sol";
contract PausableMockUpgradeableWithInit is PausableMockUpgradeable {
contract MathMockUpgradeableWithInit is MathMockUpgradeable {
constructor() public payable {
__PausableMock_init();
__MathMock_init();
}
}
import "./ERC721URIStorageMockUpgradeable.sol";
import "./ArraysImplUpgradeable.sol";
contract ERC721URIStorageMockUpgradeableWithInit is ERC721URIStorageMockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC721URIStorageMock_init(name, symbol);
contract ArraysImplUpgradeableWithInit is ArraysImplUpgradeable {
constructor(uint256[] memory array) public payable {
__ArraysImpl_init(array);
}
}
import "./ERC721MockUpgradeable.sol";
import "./ERC20SnapshotMockUpgradeable.sol";
contract ERC721MockUpgradeableWithInit is ERC721MockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC721Mock_init(name, symbol);
contract ERC20SnapshotMockUpgradeableWithInit is ERC20SnapshotMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20SnapshotMock_init(name, symbol, initialAccount, initialBalance);
}
}
import "./ERC165CheckerMockUpgradeable.sol";
import "./CountersImplUpgradeable.sol";
contract ERC165CheckerMockUpgradeableWithInit is ERC165CheckerMockUpgradeable {
contract CountersImplUpgradeableWithInit is CountersImplUpgradeable {
constructor() public payable {
__ERC165CheckerMock_init();
__CountersImpl_init();
}
}
import "./ERC165/ERC165InterfacesSupportedUpgradeable.sol";
import "./UUPS/TestInProdUpgradeable.sol";
contract SupportsInterfaceWithLookupMockUpgradeableWithInit is SupportsInterfaceWithLookupMockUpgradeable {
contract UUPSUpgradeableMockUpgradeableWithInit is UUPSUpgradeableMockUpgradeable {
constructor() public payable {
__SupportsInterfaceWithLookupMock_init();
__UUPSUpgradeableMock_init();
}
}
import "./ERC165/ERC165InterfacesSupportedUpgradeable.sol";
import "./UUPS/TestInProdUpgradeable.sol";
contract ERC165InterfacesSupportedUpgradeableWithInit is ERC165InterfacesSupportedUpgradeable {
constructor(bytes4[] memory interfaceIds) public payable {
__ERC165InterfacesSupported_init(interfaceIds);
contract UUPSUpgradeableUnsafeMockUpgradeableWithInit is UUPSUpgradeableUnsafeMockUpgradeable {
constructor() public payable {
__UUPSUpgradeableUnsafeMock_init();
}
}
import "./ERC165StorageMockUpgradeable.sol";
import "./UUPS/TestInProdUpgradeable.sol";
contract ERC165StorageMockUpgradeableWithInit is ERC165StorageMockUpgradeable {
contract UUPSUpgradeableBrokenMockUpgradeableWithInit is UUPSUpgradeableBrokenMockUpgradeable {
constructor() public payable {
__ERC165StorageMock_init();
__UUPSUpgradeableBrokenMock_init();
}
}
import "./ERC165MockUpgradeable.sol";
import "./ERC20PermitMockUpgradeable.sol";
contract ERC165MockUpgradeableWithInit is ERC165MockUpgradeable {
contract ERC20PermitMockUpgradeableWithInit is ERC20PermitMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20PermitMock_init(name, symbol, initialAccount, initialBalance);
}
}
import "./EIP712ExternalUpgradeable.sol";
contract EIP712ExternalUpgradeableWithInit is EIP712ExternalUpgradeable {
constructor(string memory name, string memory version) public payable {
__EIP712External_init(name, version);
}
}
import "../metatx/MinimalForwarderUpgradeable.sol";
contract MinimalForwarderUpgradeableWithInit is MinimalForwarderUpgradeable {
constructor() public payable {
__ERC165Mock_init();
__MinimalForwarder_init();
}
}
import "./ERC1155ReceiverMockUpgradeable.sol";
......@@ -173,6 +257,81 @@ contract ERC1155ReceiverMockUpgradeableWithInit is ERC1155ReceiverMockUpgradeabl
__ERC1155ReceiverMock_init(recRetval, recReverts, batRetval, batReverts);
}
}
import "./ERC721URIStorageMockUpgradeable.sol";
contract ERC721URIStorageMockUpgradeableWithInit is ERC721URIStorageMockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC721URIStorageMock_init(name, symbol);
}
}
import "./PausableMockUpgradeable.sol";
contract PausableMockUpgradeableWithInit is PausableMockUpgradeable {
constructor() public payable {
__PausableMock_init();
}
}
import "./ERC20PausableMockUpgradeable.sol";
contract ERC20PausableMockUpgradeableWithInit is ERC20PausableMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20PausableMock_init(name, symbol, initialAccount, initialBalance);
}
}
import "../token/ERC1155/ERC1155Upgradeable.sol";
contract ERC1155UpgradeableWithInit is ERC1155Upgradeable {
constructor(string memory uri_) public payable {
__ERC1155_init(uri_);
}
}
import "../token/ERC1155/presets/ERC1155PresetMinterPauserUpgradeable.sol";
contract ERC1155PresetMinterPauserUpgradeableWithInit is ERC1155PresetMinterPauserUpgradeable {
constructor(string memory uri) public payable {
__ERC1155PresetMinterPauser_init(uri);
}
}
import "./AccessControlEnumerableMockUpgradeable.sol";
contract AccessControlEnumerableMockUpgradeableWithInit is AccessControlEnumerableMockUpgradeable {
constructor() public payable {
__AccessControlEnumerableMock_init();
}
}
import "./EnumerableSetMockUpgradeable.sol";
contract EnumerableBytes32SetMockUpgradeableWithInit is EnumerableBytes32SetMockUpgradeable {
constructor() public payable {
__EnumerableBytes32SetMock_init();
}
}
import "./EnumerableSetMockUpgradeable.sol";
contract EnumerableAddressSetMockUpgradeableWithInit is EnumerableAddressSetMockUpgradeable {
constructor() public payable {
__EnumerableAddressSetMock_init();
}
}
import "./EnumerableSetMockUpgradeable.sol";
contract EnumerableUintSetMockUpgradeableWithInit is EnumerableUintSetMockUpgradeable {
constructor() public payable {
__EnumerableUintSetMock_init();
}
}
import "./EnumerableMapMockUpgradeable.sol";
contract EnumerableMapMockUpgradeableWithInit is EnumerableMapMockUpgradeable {
constructor() public payable {
__EnumerableMapMock_init();
}
}
import "./ERC1155SupplyMockUpgradeable.sol";
contract ERC1155SupplyMockUpgradeableWithInit is ERC1155SupplyMockUpgradeable {
......@@ -201,18 +360,18 @@ contract ERC1155BurnableMockUpgradeableWithInit is ERC1155BurnableMockUpgradeabl
__ERC1155BurnableMock_init(uri);
}
}
import "../token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
import "./ERC721PausableMockUpgradeable.sol";
contract ERC1155HolderUpgradeableWithInit is ERC1155HolderUpgradeable {
constructor() public payable {
__ERC1155Holder_init();
contract ERC721PausableMockUpgradeableWithInit is ERC721PausableMockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC721PausableMock_init(name, symbol);
}
}
import "./ERC721EnumerableMockUpgradeable.sol";
import "./ERC721MockUpgradeable.sol";
contract ERC721EnumerableMockUpgradeableWithInit is ERC721EnumerableMockUpgradeable {
contract ERC721MockUpgradeableWithInit is ERC721MockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC721EnumerableMock_init(name, symbol);
__ERC721Mock_init(name, symbol);
}
}
import "./StringsMockUpgradeable.sol";
......@@ -243,23 +402,53 @@ contract ERC721BurnableMockUpgradeableWithInit is ERC721BurnableMockUpgradeable
__ERC721BurnableMock_init(name, symbol);
}
}
import "./ERC721PausableMockUpgradeable.sol";
import "./ERC721EnumerableMockUpgradeable.sol";
contract ERC721PausableMockUpgradeableWithInit is ERC721PausableMockUpgradeable {
contract ERC721EnumerableMockUpgradeableWithInit is ERC721EnumerableMockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC721PausableMock_init(name, symbol);
__ERC721EnumerableMock_init(name, symbol);
}
}
import "./ERC20PausableMockUpgradeable.sol";
import "./ERC165CheckerMockUpgradeable.sol";
contract ERC20PausableMockUpgradeableWithInit is ERC20PausableMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20PausableMock_init(name, symbol, initialAccount, initialBalance);
contract ERC165CheckerMockUpgradeableWithInit is ERC165CheckerMockUpgradeable {
constructor() public payable {
__ERC165CheckerMock_init();
}
}
import "./ERC165/ERC165InterfacesSupportedUpgradeable.sol";
contract SupportsInterfaceWithLookupMockUpgradeableWithInit is SupportsInterfaceWithLookupMockUpgradeable {
constructor() public payable {
__SupportsInterfaceWithLookupMock_init();
}
}
import "./ERC165/ERC165InterfacesSupportedUpgradeable.sol";
contract ERC165InterfacesSupportedUpgradeableWithInit is ERC165InterfacesSupportedUpgradeable {
constructor(bytes4[] memory interfaceIds) public payable {
__ERC165InterfacesSupported_init(interfaceIds);
}
}
import "../token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
contract ERC1155HolderUpgradeableWithInit is ERC1155HolderUpgradeable {
constructor() public payable {
__ERC1155Holder_init();
}
}
import "./ERC165StorageMockUpgradeable.sol";
contract ERC165StorageMockUpgradeableWithInit is ERC165StorageMockUpgradeable {
constructor() public payable {
__ERC165StorageMock_init();
}
}
import "./ERC165MockUpgradeable.sol";
contract ERC165MockUpgradeableWithInit is ERC165MockUpgradeable {
constructor() public payable {
__ERC165Mock_init();
}
}
import "./SafeERC20HelperUpgradeable.sol";
......@@ -469,32 +658,6 @@ contract SafeMathMockUpgradeableWithInit is SafeMathMockUpgradeable {
__SafeMathMock_init();
}
}
import "./EIP712ExternalUpgradeable.sol";
contract EIP712ExternalUpgradeableWithInit is EIP712ExternalUpgradeable {
constructor(string memory name, string memory version) public payable {
__EIP712External_init(name, version);
}
}
import "../metatx/MinimalForwarderUpgradeable.sol";
contract MinimalForwarderUpgradeableWithInit is MinimalForwarderUpgradeable {
constructor() public payable {
__MinimalForwarder_init();
}
}
import "./ERC20PermitMockUpgradeable.sol";
contract ERC20PermitMockUpgradeableWithInit is ERC20PermitMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20PermitMock_init(name, symbol, initialAccount, initialBalance);
}
}
import "./ERC20DecimalsMockUpgradeable.sol";
contract ERC20DecimalsMockUpgradeableWithInit is ERC20DecimalsMockUpgradeable {
......@@ -573,34 +736,6 @@ contract StorageSlotMockUpgradeableWithInit is StorageSlotMockUpgradeable {
__StorageSlotMock_init();
}
}
import "./UUPS/TestInProdUpgradeable.sol";
contract UUPSUpgradeableMockUpgradeableWithInit is UUPSUpgradeableMockUpgradeable {
constructor() public payable {
__UUPSUpgradeableMock_init();
}
}
import "./UUPS/TestInProdUpgradeable.sol";
contract UUPSUpgradeableUnsafeMockUpgradeableWithInit is UUPSUpgradeableUnsafeMockUpgradeable {
constructor() public payable {
__UUPSUpgradeableUnsafeMock_init();
}
}
import "./UUPS/TestInProdUpgradeable.sol";
contract UUPSUpgradeableBrokenMockUpgradeableWithInit is UUPSUpgradeableBrokenMockUpgradeable {
constructor() public payable {
__UUPSUpgradeableBrokenMock_init();
}
}
import "./CountersImplUpgradeable.sol";
contract CountersImplUpgradeableWithInit is CountersImplUpgradeable {
constructor() public payable {
__CountersImpl_init();
}
}
import "./OwnableMockUpgradeable.sol";
contract OwnableMockUpgradeableWithInit is OwnableMockUpgradeable {
......@@ -622,72 +757,18 @@ contract ECDSAMockUpgradeableWithInit is ECDSAMockUpgradeable {
__ECDSAMock_init();
}
}
import "./SafeCastMockUpgradeable.sol";
contract SafeCastMockUpgradeableWithInit is SafeCastMockUpgradeable {
constructor() public payable {
__SafeCastMock_init();
}
}
import "./ERC20VotesCompMockUpgradeable.sol";
contract ERC20VotesCompMockUpgradeableWithInit is ERC20VotesCompMockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC20VotesCompMock_init(name, symbol);
}
}
import "./ArraysImplUpgradeable.sol";
contract ArraysImplUpgradeableWithInit is ArraysImplUpgradeable {
constructor(uint256[] memory array) public payable {
__ArraysImpl_init(array);
}
}
import "./ERC20SnapshotMockUpgradeable.sol";
contract ERC20SnapshotMockUpgradeableWithInit is ERC20SnapshotMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20SnapshotMock_init(name, symbol, initialAccount, initialBalance);
}
}
import "./AccessControlEnumerableMockUpgradeable.sol";
import "./TimersTimestampImplUpgradeable.sol";
contract AccessControlEnumerableMockUpgradeableWithInit is AccessControlEnumerableMockUpgradeable {
contract TimersTimestampImplUpgradeableWithInit is TimersTimestampImplUpgradeable {
constructor() public payable {
__AccessControlEnumerableMock_init();
__TimersTimestampImpl_init();
}
}
import "./EnumerableSetMockUpgradeable.sol";
import "./TimersBlockNumberImplUpgradeable.sol";
contract EnumerableBytes32SetMockUpgradeableWithInit is EnumerableBytes32SetMockUpgradeable {
contract TimersBlockNumberImplUpgradeableWithInit is TimersBlockNumberImplUpgradeable {
constructor() public payable {
__EnumerableBytes32SetMock_init();
}
}
import "./EnumerableSetMockUpgradeable.sol";
contract EnumerableAddressSetMockUpgradeableWithInit is EnumerableAddressSetMockUpgradeable {
constructor() public payable {
__EnumerableAddressSetMock_init();
}
}
import "./EnumerableSetMockUpgradeable.sol";
contract EnumerableUintSetMockUpgradeableWithInit is EnumerableUintSetMockUpgradeable {
constructor() public payable {
__EnumerableUintSetMock_init();
}
}
import "./EnumerableMapMockUpgradeable.sol";
contract EnumerableMapMockUpgradeableWithInit is EnumerableMapMockUpgradeable {
constructor() public payable {
__EnumerableMapMock_init();
__TimersBlockNumberImpl_init();
}
}
import "./BadBeaconUpgradeable.sol";
......@@ -725,6 +806,13 @@ contract ClashingImplementationUpgradeableWithInit is ClashingImplementationUpgr
__ClashingImplementation_init();
}
}
import "./compound/CompTimelockUpgradeable.sol";
contract CompTimelockUpgradeableWithInit is CompTimelockUpgradeable {
constructor(address admin_, uint256 delay_) public payable {
__CompTimelock_init(admin_, delay_);
}
}
import "./DummyImplementationUpgradeable.sol";
contract DummyImplementationUpgradeableWithInit is DummyImplementationUpgradeable {
......
// 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 {
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return _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);
}
}
return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
}
uint256[50] private __gap;
}
......@@ -18,9 +18,10 @@ import "../../proxy/utils/Initializable.sol";
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* 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
The following related EIPs are in draft status.
- {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.
......@@ -61,14 +60,14 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
{{ERC20Wrapper}}
{{ERC20FlashMint}}
== 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.
{{ERC20Permit}}
{{ERC20FlashMint}}
== 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.
......
......@@ -61,7 +61,7 @@ abstract contract ERC20FlashMintUpgradeable is Initializable, ERC20Upgradeable,
* supported.
* @param amount The amount of tokens to be loaned.
* @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(
IERC3156FlashBorrowerUpgradeable receiver,
......
......@@ -129,7 +129,7 @@ library AddressUpgradeable {
require(isContract(target), "Address: call to non-contract");
(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 {
require(isContract(target), "Address: static call to non-contract");
(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,
bytes memory returndata,
string memory errorMessage
) private pure returns (bytes memory) {
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
......
......@@ -39,26 +39,7 @@ abstract contract MulticallUpgradeable is Initializable {
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return _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);
}
}
return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
}
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 {
) internal pure returns (address) {
// 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
// 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.
//
// 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 {
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
struct Bytes32Set {
......@@ -184,6 +196,18 @@ library EnumerableSetUpgradeable {
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
struct AddressSet {
......@@ -238,6 +262,25 @@ library EnumerableSetUpgradeable {
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
struct UintSet {
......@@ -291,4 +334,23 @@ library EnumerableSetUpgradeable {
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
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
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]
----
......
......@@ -108,4 +108,4 @@ contract ERC20WithAutoMinerReward is ERC20 {
[[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
[[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.
......
......@@ -23,6 +23,8 @@ for (const f of fs.readdirSync(path.join(__dirname, 'hardhat'))) {
require(path.join(__dirname, 'hardhat', f));
}
const withOptimizations = argv.enableGasReport || argv.compileMode === 'production';
/**
* @type import('hardhat/config').HardhatUserConfig
*/
......@@ -31,7 +33,7 @@ module.exports = {
version: '0.8.3',
settings: {
optimizer: {
enabled: argv.enableGasReport || argv.compileMode === 'production',
enabled: withOptimizations,
runs: 200,
},
},
......@@ -39,6 +41,7 @@ module.exports = {
networks: {
hardhat: {
blockGasLimit: 10000000,
allowUnlimitedContractSize: !withOptimizations,
},
},
gasReporter: {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -75,13 +75,13 @@
"merkletreejs": "^0.2.13",
"micromatch": "^4.0.2",
"prettier": "^2.3.0",
"prettier-plugin-solidity": "^1.0.0-beta.13",
"prettier-plugin-solidity": "^1.0.0-beta.16",
"rimraf": "^3.0.2",
"solhint": "^3.3.6",
"solidity-ast": "^0.4.25",
"solidity-coverage": "^0.7.11",
"solidity-docgen": "^0.5.3",
"web3": "^1.3.0",
"yargs": "^17.0.0"
"yargs": "^16.2.0"
}
}
......@@ -21,16 +21,22 @@ for (const artifact of artifacts) {
}
}
graphlib.alg.findCycles(graph).forEach(([ c1, c2 ]) => {
console.log(`Conflict between ${names[c1]} and ${names[c2]} detected in the following dependency chains:`);
linearized
.filter(chain => chain.includes(parseInt(c1)) && chain.includes(parseInt(c2)))
.forEach(chain => {
const comp = chain.indexOf(c1) < chain.indexOf(c2) ? '>' : '<';
console.log(`- ${names[c1]} ${comp} ${names[c2]}: ${chain.reverse().map(id => names[id]).join(', ')}`);
});
process.exitCode = 1;
});
/// graphlib.alg.findCycles will not find minimal cycles.
/// We are only interested int cycles of lengths 2 (needs proof)
graph.nodes().forEach((x, i, nodes) => nodes
.slice(i + 1)
.filter(y => graph.hasEdge(x, y) && graph.hasEdge(y, x))
.map(y => {
console.log(`Conflict between ${names[x]} and ${names[y]} detected in the following dependency chains:`);
linearized
.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) {
......
......@@ -143,6 +143,8 @@ elif [[ "$*" == "final" ]]; then
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"
else
......
## 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 { BN, constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers');
const ethSigUtil = require('eth-sig-util');
const Wallet = require('ethereumjs-wallet').default;
const Enums = require('../helpers/enums');
const { EIP712Domain } = require('../helpers/eip712');
const { fromRpcSig } = require('ethereumjs-util');
const {
runGovernorWorkflow,
} = require('./GovernorWorkflow.behavior');
const {
shouldSupportInterfaces,
} = require('../utils/introspection/SupportsInterface.behavior');
const Token = artifacts.require('ERC20VotesMock');
const Governor = artifacts.require('GovernorMock');
const CallReceiver = artifacts.require('CallReceiverMock');
contract('Governor', function (accounts) {
const [ owner, proposer, 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, 0);
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 });
});
shouldSupportInterfaces([
'ERC165',
'Governor',
]);
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.COUNTING_MODE()).to.be.equal('support=bravo&quorum=for,abstain');
});
describe('scenario', function () {
describe('nominal', function () {
beforeEach(async function () {
this.value = web3.utils.toWei('1');
await web3.eth.sendTransaction({ from: owner, to: this.mock.address, value: this.value });
expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(this.value);
expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal('0');
this.settings = {
proposal: [
[ this.receiver.address ],
[ this.value ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
proposer,
tokenHolder: owner,
voters: [
{ voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For, reason: 'This is nice' },
{ 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 },
],
};
this.votingDelay = await this.mock.votingDelay();
this.votingPeriod = await this.mock.votingPeriod();
});
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);
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'),
),
);
}
});
expectEvent(
this.receipts.propose,
'ProposalCreated',
{
proposalId: this.id,
proposer,
targets: this.settings.proposal[0],
// values: this.settings.proposal[1].map(value => new BN(value)),
signatures: this.settings.proposal[2].map(() => ''),
calldatas: this.settings.proposal[2],
startBlock: new BN(this.receipts.propose.blockNumber).add(this.votingDelay),
endBlock: new BN(this.receipts.propose.blockNumber).add(this.votingDelay).add(this.votingPeriod),
description: this.settings.proposal[3],
},
);
this.receipts.castVote.filter(Boolean).forEach(vote => {
const { voter } = vote.logs.find(Boolean).args;
expectEvent(
vote,
'VoteCast',
this.settings.voters.find(({ address }) => address === voter),
);
});
expectEvent(
this.receipts.execute,
'ProposalExecuted',
{ proposalId: this.id },
);
await expectEvent.inTransaction(
this.receipts.execute.transactionHash,
this.receiver,
'MockFunctionCalled',
);
expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal('0');
expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal(this.value);
});
runGovernorWorkflow();
});
describe('vote with signature', function () {
beforeEach(async function () {
const chainId = await web3.eth.getChainId();
// generate voter by signature wallet
const voterBySig = Wallet.generate();
this.voter = web3.utils.toChecksumAddress(voterBySig.getAddressString());
// use delegateBySig to enable vote delegation for this wallet
const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
voterBySig.getPrivateKey(),
{
data: {
types: {
EIP712Domain,
Delegation: [
{ name: 'delegatee', type: 'address' },
{ name: 'nonce', type: 'uint256' },
{ name: 'expiry', type: 'uint256' },
],
},
domain: { name: tokenName, version: '1', chainId, verifyingContract: this.token.address },
primaryType: 'Delegation',
message: { delegatee: this.voter, nonce: 0, expiry: constants.MAX_UINT256 },
},
},
));
await this.token.delegateBySig(this.voter, 0, constants.MAX_UINT256, v, r, s);
// prepare signature for vote by signature
const signature = async (message) => {
return fromRpcSig(ethSigUtil.signTypedMessage(
voterBySig.getPrivateKey(),
{
data: {
types: {
EIP712Domain,
Ballot: [
{ name: 'proposalId', type: 'uint256' },
{ name: 'support', type: 'uint8' },
],
},
domain: { name, version, chainId, verifyingContract: this.mock.address },
primaryType: 'Ballot',
message,
},
},
));
};
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
tokenHolder: owner,
voters: [
{ voter: this.voter, signature, weight: web3.utils.toWei('1'), support: Enums.VoteType.For },
],
};
});
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(false);
expect(await this.mock.hasVoted(this.id, voter2)).to.be.equal(false);
expect(await this.mock.hasVoted(this.id, this.voter)).to.be.equal(true);
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'),
),
);
}
});
expectEvent(
this.receipts.propose,
'ProposalCreated',
{ proposalId: this.id },
);
expectEvent(
this.receipts.execute,
'ProposalExecuted',
{ proposalId: this.id },
);
await expectEvent.inTransaction(
this.receipts.execute.transactionHash,
this.receiver,
'MockFunctionCalled',
);
});
runGovernorWorkflow();
});
describe('send ethers', function () {
beforeEach(async function () {
this.receiver = { address: web3.utils.toChecksumAddress(web3.utils.randomHex(20)) };
this.value = web3.utils.toWei('1');
await web3.eth.sendTransaction({ from: owner, to: this.mock.address, value: this.value });
expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(this.value);
expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal('0');
this.settings = {
proposal: [
[ this.receiver.address ],
[ this.value ],
[ '0x' ],
'<proposal description>',
],
tokenHolder: owner,
voters: [
{ voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For },
{ voter: voter2, weight: web3.utils.toWei('1'), support: Enums.VoteType.Abstain },
],
};
});
afterEach(async function () {
expectEvent(
this.receipts.propose,
'ProposalCreated',
{ proposalId: this.id },
);
expectEvent(
this.receipts.execute,
'ProposalExecuted',
{ proposalId: this.id },
);
expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal('0');
expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal(this.value);
});
runGovernorWorkflow();
});
describe('receiver revert without reason', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ 0 ],
[ this.receiver.contract.methods.mockFunctionRevertsNoReason().encodeABI() ],
'<proposal description>',
],
tokenHolder: owner,
voters: [
{ voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For },
],
steps: {
execute: { error: 'Governor: call reverted without message' },
},
};
});
runGovernorWorkflow();
});
describe('receiver revert with reason', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ 0 ],
[ this.receiver.contract.methods.mockFunctionRevertsReason().encodeABI() ],
'<proposal description>',
],
tokenHolder: owner,
voters: [
{ voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For },
],
steps: {
execute: { error: 'CallReceiverMock: reverting' },
},
};
});
runGovernorWorkflow();
});
describe('missing proposal', 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,
error: 'Governor: unknown proposal id',
},
{
voter: voter2,
weight: web3.utils.toWei('1'),
support: Enums.VoteType.Abstain,
error: 'Governor: unknown proposal id',
},
],
steps: {
propose: { enable: false },
wait: { enable: false },
execute: { error: 'Governor: unknown proposal id' },
},
};
});
runGovernorWorkflow();
});
describe('duplicate pending proposal', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
steps: {
wait: { enable: false },
execute: { enable: false },
},
};
});
afterEach(async function () {
await expectRevert(this.mock.propose(...this.settings.proposal), 'Governor: proposal already exists');
});
runGovernorWorkflow();
});
describe('duplicate executed proposal', 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('1'), support: Enums.VoteType.Abstain },
],
};
});
afterEach(async function () {
await expectRevert(this.mock.propose(...this.settings.proposal), 'Governor: proposal already exists');
});
runGovernorWorkflow();
});
describe('Invalid vote type', 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: new BN('255'),
error: 'GovernorVotingSimple: invalid value for enum VoteType',
},
],
steps: {
wait: { enable: false },
execute: { enable: false },
},
};
});
runGovernorWorkflow();
});
describe('double cast', 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: voter1,
weight: web3.utils.toWei('1'),
support: Enums.VoteType.For,
error: 'GovernorVotingSimple: vote already casted',
},
],
};
});
runGovernorWorkflow();
});
describe('quorum 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('0'), support: Enums.VoteType.For },
],
steps: {
execute: { error: 'Governor: proposal not successful' },
},
};
});
runGovernorWorkflow();
});
describe('score 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.Against },
],
steps: {
execute: { error: 'Governor: proposal not successful' },
},
};
});
runGovernorWorkflow();
});
describe('vote not over', 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: {
wait: { enable: false },
execute: { error: 'Governor: proposal not successful' },
},
};
});
runGovernorWorkflow();
});
});
describe('state', function () {
describe('Unset', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
steps: {
propose: { enable: false },
wait: { enable: false },
execute: { enable: false },
},
};
});
afterEach(async function () {
await expectRevert(this.mock.state(this.id), 'Governor: unknown proposal id');
});
runGovernorWorkflow();
});
describe('Pending & Active', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
steps: {
propose: { noadvance: true },
wait: { enable: false },
execute: { enable: false },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Pending);
await time.advanceBlockTo(this.snapshot);
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Active);
});
runGovernorWorkflow();
});
describe('Defeated', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
steps: {
execute: { enable: false },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Defeated);
});
runGovernorWorkflow();
});
describe('Succeeded', 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: { enable: false },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded);
});
runGovernorWorkflow();
});
describe('Executed', 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 },
],
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Executed);
});
runGovernorWorkflow();
});
});
describe('Cancel', function () {
describe('Before proposal', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
steps: {
propose: { enable: false },
wait: { enable: false },
execute: { enable: false },
},
};
});
afterEach(async function () {
await expectRevert(
this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'Governor: unknown proposal id',
);
});
runGovernorWorkflow();
});
describe('After proposal', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
steps: {
wait: { enable: false },
execute: { enable: false },
},
};
});
afterEach(async function () {
await this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash);
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
await expectRevert(
this.mock.castVote(this.id, new BN('100'), { from: voter1 }),
'Governor: vote not currently active',
);
});
runGovernorWorkflow();
});
describe('After vote', 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: {
wait: { enable: false },
execute: { enable: false },
},
};
});
afterEach(async function () {
await this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash);
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
await expectRevert(
this.mock.execute(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'Governor: proposal not successful',
);
});
runGovernorWorkflow();
});
describe('After deadline', 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: { enable: false },
},
};
});
afterEach(async function () {
await this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash);
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
await expectRevert(
this.mock.execute(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'Governor: proposal not successful',
);
});
runGovernorWorkflow();
});
describe('After execution', 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 },
],
};
});
afterEach(async function () {
await expectRevert(
this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'Governor: proposal not active',
);
});
runGovernorWorkflow();
});
});
describe('Proposal length', function () {
it('empty', async function () {
await expectRevert(
this.mock.propose(
[],
[],
[],
'<proposal description>',
),
'Governor: empty proposal',
);
});
it('missmatch #1', async function () {
await expectRevert(
this.mock.propose(
[ ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
),
'Governor: invalid proposal length',
);
});
it('missmatch #2', async function () {
await expectRevert(
this.mock.propose(
[ this.receiver.address ],
[ ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
),
'Governor: invalid proposal length',
);
});
it('missmatch #3', async function () {
await expectRevert(
this.mock.propose(
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ ],
'<proposal description>',
),
'Governor: invalid proposal length',
);
});
});
});
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, expectRevert, time } = require('@openzeppelin/test-helpers');
const Enums = require('../../helpers/enums');
const RLP = require('rlp');
const {
runGovernorWorkflow,
} = require('../GovernorWorkflow.behavior');
const Token = artifacts.require('ERC20VotesCompMock');
const Timelock = artifacts.require('CompTimelock');
const Governor = artifacts.require('GovernorCompatibilityBravoMock');
const CallReceiver = artifacts.require('CallReceiverMock');
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 makeContractAddress (creator, nonce) {
return web3.utils.toChecksumAddress(web3.utils.sha3(RLP.encode([creator, nonce])).slice(12).substring(14));
}
contract('GovernorCompatibilityBravo', function (accounts) {
const [ owner, proposer, voter1, voter2, voter3, voter4, other ] = accounts;
const name = 'OZ-Governor';
// const version = '1';
const tokenName = 'MockToken';
const tokenSymbol = 'MTKN';
const tokenSupply = web3.utils.toWei('100');
const proposalThreshold = web3.utils.toWei('10');
beforeEach(async function () {
const [ deployer ] = await web3.eth.getAccounts();
this.token = await Token.new(tokenName, tokenSymbol);
// Need to predict governance address to set it as timelock admin with a delayed transfer
const nonce = await web3.eth.getTransactionCount(deployer);
const predictGovernor = makeContractAddress(deployer, nonce + 1);
this.timelock = await Timelock.new(predictGovernor, 2 * 86400);
this.mock = await Governor.new(name, this.token.address, 4, 16, proposalThreshold, this.timelock.address);
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 });
await this.token.transfer(proposer, proposalThreshold, { from: owner });
await this.token.delegate(proposer, { from: proposer });
});
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.quorumVotes()).to.be.bignumber.equal('0');
expect(await this.mock.COUNTING_MODE()).to.be.equal('support=bravo&quorum=bravo');
});
describe('nominal', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ], // targets
[ web3.utils.toWei('0') ], // values
[ this.receiver.contract.methods.mockFunction().encodeABI() ], // calldatas
'<proposal description>', // description
],
proposer,
tokenHolder: owner,
voters: [
{
voter: voter1,
weight: web3.utils.toWei('1'),
support: Enums.VoteType.Abstain,
},
{
voter: voter2,
weight: web3.utils.toWei('10'),
support: Enums.VoteType.For,
},
{
voter: voter3,
weight: web3.utils.toWei('5'),
support: Enums.VoteType.Against,
},
{
voter: voter4,
support: '100',
error: 'GovernorCompatibilityBravo: invalid vote type',
},
{
voter: voter1,
support: Enums.VoteType.For,
error: 'GovernorCompatibilityBravo: vote already casted',
skip: true,
},
],
steps: {
queue: { delay: 7 * 86400 },
},
};
this.votingDelay = await this.mock.votingDelay();
this.votingPeriod = await this.mock.votingPeriod();
this.receipts = {};
});
afterEach(async function () {
const proposal = await this.mock.proposals(this.id);
expect(proposal.id).to.be.bignumber.equal(this.id);
expect(proposal.proposer).to.be.equal(proposer);
expect(proposal.eta).to.be.bignumber.equal(this.eta);
expect(proposal.startBlock).to.be.bignumber.equal(this.snapshot);
expect(proposal.endBlock).to.be.bignumber.equal(this.deadline);
expect(proposal.canceled).to.be.equal(false);
expect(proposal.executed).to.be.equal(true);
for (const [key, value] of Object.entries(Enums.VoteType)) {
expect(proposal[`${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'),
),
);
}
const action = await this.mock.getActions(this.id);
expect(action.targets).to.be.deep.equal(this.settings.proposal[0]);
// expect(action.values).to.be.deep.equal(this.settings.proposal[1]);
expect(action.signatures).to.be.deep.equal(Array(this.settings.proposal[2].length).fill(''));
expect(action.calldatas).to.be.deep.equal(this.settings.proposal[2]);
for (const voter of this.settings.voters.filter(({ skip }) => !skip)) {
expect(await this.mock.hasVoted(this.id, voter.voter)).to.be.equal(voter.error === undefined);
const receipt = await this.mock.getReceipt(this.id, voter.voter);
expect(receipt.hasVoted).to.be.equal(voter.error === undefined);
expect(receipt.support).to.be.bignumber.equal(voter.error === undefined ? voter.support : '0');
expect(receipt.votes).to.be.bignumber.equal(voter.error === undefined ? voter.weight : '0');
}
expectEvent(
this.receipts.propose,
'ProposalCreated',
{
proposalId: this.id,
proposer,
targets: this.settings.proposal[0],
// values: this.settings.proposal[1].map(value => new BN(value)),
signatures: this.settings.proposal[2].map(() => ''),
calldatas: this.settings.proposal[2],
startBlock: new BN(this.receipts.propose.blockNumber).add(this.votingDelay),
endBlock: new BN(this.receipts.propose.blockNumber).add(this.votingDelay).add(this.votingPeriod),
description: this.settings.proposal[3],
},
);
this.receipts.castVote.filter(Boolean).forEach(vote => {
const { voter } = vote.logs.find(Boolean).args;
expectEvent(
vote,
'VoteCast',
this.settings.voters.find(({ address }) => address === voter),
);
});
expectEvent(
this.receipts.execute,
'ProposalExecuted',
{ proposalId: this.id },
);
await expectEvent.inTransaction(
this.receipts.execute.transactionHash,
this.receiver,
'MockFunctionCalled',
);
});
runGovernorWorkflow();
});
describe('proposalThreshold not reached', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ], // targets
[ web3.utils.toWei('0') ], // values
[ this.receiver.contract.methods.mockFunction().encodeABI() ], // calldatas
'<proposal description>', // description
],
proposer: other,
steps: {
propose: { error: 'GovernorCompatibilityBravo: proposer votes below proposal threshold' },
wait: { enable: false },
queue: { enable: false },
execute: { enable: false },
},
};
});
runGovernorWorkflow();
});
describe('with compatibility interface', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ], // targets
[ web3.utils.toWei('0') ], // values
[ '' ], // signatures
[ this.receiver.contract.methods.mockFunction().encodeABI() ], // calldatas
'<proposal description>', // description
],
proposer,
tokenHolder: owner,
voters: [
{
voter: voter1,
weight: web3.utils.toWei('1'),
support: Enums.VoteType.Abstain,
},
{
voter: voter2,
weight: web3.utils.toWei('10'),
support: Enums.VoteType.For,
},
{
voter: voter3,
weight: web3.utils.toWei('5'),
support: Enums.VoteType.Against,
},
{
voter: voter4,
support: '100',
error: 'GovernorCompatibilityBravo: invalid vote type',
},
{
voter: voter1,
support: Enums.VoteType.For,
error: 'GovernorCompatibilityBravo: vote already casted',
skip: true,
},
],
steps: {
queue: { delay: 7 * 86400 },
},
};
this.votingDelay = await this.mock.votingDelay();
this.votingPeriod = await this.mock.votingPeriod();
this.receipts = {};
});
afterEach(async function () {
const proposal = await this.mock.proposals(this.id);
expect(proposal.id).to.be.bignumber.equal(this.id);
expect(proposal.proposer).to.be.equal(proposer);
expect(proposal.eta).to.be.bignumber.equal(this.eta);
expect(proposal.startBlock).to.be.bignumber.equal(this.snapshot);
expect(proposal.endBlock).to.be.bignumber.equal(this.deadline);
expect(proposal.canceled).to.be.equal(false);
expect(proposal.executed).to.be.equal(true);
for (const [key, value] of Object.entries(Enums.VoteType)) {
expect(proposal[`${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'),
),
);
}
const action = await this.mock.getActions(this.id);
expect(action.targets).to.be.deep.equal(this.settings.proposal[0]);
// expect(action.values).to.be.deep.equal(this.settings.proposal[1]);
expect(action.signatures).to.be.deep.equal(this.settings.proposal[2]);
expect(action.calldatas).to.be.deep.equal(this.settings.proposal[3]);
for (const voter of this.settings.voters.filter(({ skip }) => !skip)) {
expect(await this.mock.hasVoted(this.id, voter.voter)).to.be.equal(voter.error === undefined);
const receipt = await this.mock.getReceipt(this.id, voter.voter);
expect(receipt.hasVoted).to.be.equal(voter.error === undefined);
expect(receipt.support).to.be.bignumber.equal(voter.error === undefined ? voter.support : '0');
expect(receipt.votes).to.be.bignumber.equal(voter.error === undefined ? voter.weight : '0');
}
expectEvent(
this.receipts.propose,
'ProposalCreated',
{
proposalId: this.id,
proposer,
targets: this.settings.proposal[0],
// values: this.settings.proposal[1].map(value => new BN(value)),
signatures: this.settings.proposal[2],
calldatas: this.settings.proposal[3],
startBlock: new BN(this.receipts.propose.blockNumber).add(this.votingDelay),
endBlock: new BN(this.receipts.propose.blockNumber).add(this.votingDelay).add(this.votingPeriod),
description: this.settings.proposal[4],
},
);
this.receipts.castVote.filter(Boolean).forEach(vote => {
const { voter } = vote.logs.find(Boolean).args;
expectEvent(
vote,
'VoteCast',
this.settings.voters.find(({ address }) => address === voter),
);
});
expectEvent(
this.receipts.execute,
'ProposalExecuted',
{ proposalId: this.id },
);
await expectEvent.inTransaction(
this.receipts.execute.transactionHash,
this.receiver,
'MockFunctionCalled',
);
});
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[],string[],bytes[],string)'](
...this.settings.proposal,
{ from: this.settings.proposer },
),
tryGet(this.settings, 'steps.propose.error'),
);
if (tryGet(this.settings, 'steps.propose.error') === undefined) {
this.id = this.receipts.propose.logs.find(({ event }) => event === 'ProposalCreated').args.proposalId;
this.snapshot = await this.mock.proposalSnapshot(this.id);
this.deadline = await this.mock.proposalDeadline(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(
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(uint256)'](this.id, { 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(uint256)'](this.id, { 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'));
}
}
});
});
});
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 { constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const Enums = require('../../helpers/enums');
const RLP = require('rlp');
const {
runGovernorWorkflow,
} = require('../GovernorWorkflow.behavior');
const {
shouldSupportInterfaces,
} = require('../../utils/introspection/SupportsInterface.behavior');
const Token = artifacts.require('ERC20VotesMock');
const Timelock = artifacts.require('CompTimelock');
const Governor = artifacts.require('GovernorTimelockCompoundMock');
const CallReceiver = artifacts.require('CallReceiverMock');
function makeContractAddress (creator, nonce) {
return web3.utils.toChecksumAddress(web3.utils.sha3(RLP.encode([creator, nonce])).slice(12).substring(14));
}
contract('GovernorTimelockCompound', function (accounts) {
const [ admin, voter ] = accounts;
const name = 'OZ-Governor';
// const version = '1';
const tokenName = 'MockToken';
const tokenSymbol = 'MTKN';
const tokenSupply = web3.utils.toWei('100');
beforeEach(async function () {
const [ deployer ] = await web3.eth.getAccounts();
this.token = await Token.new(tokenName, tokenSymbol);
// Need to predict governance address to set it as timelock admin with a delayed transfer
const nonce = await web3.eth.getTransactionCount(deployer);
const predictGovernor = makeContractAddress(deployer, nonce + 1);
this.timelock = await Timelock.new(predictGovernor, 2 * 86400);
this.mock = await Governor.new(name, this.token.address, 4, 16, this.timelock.address, 0);
this.receiver = await CallReceiver.new();
await this.token.mint(voter, tokenSupply);
await this.token.delegate(voter, { from: voter });
});
shouldSupportInterfaces([
'ERC165',
'Governor',
'GovernorTimelock',
]);
it('post 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.timelock()).to.be.equal(this.timelock.address);
expect(await this.timelock.admin()).to.be.equal(this.mock.address);
});
describe('nominal', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 7 * 86400 },
},
};
});
afterEach(async function () {
expectEvent(
this.receipts.propose,
'ProposalCreated',
{ proposalId: this.id },
);
expectEvent(
this.receipts.queue,
'ProposalQueued',
{ proposalId: this.id },
);
await expectEvent.inTransaction(
this.receipts.queue.transactionHash,
this.timelock,
'QueueTransaction',
{ eta: this.eta },
);
expectEvent(
this.receipts.execute,
'ProposalExecuted',
{ proposalId: this.id },
);
await expectEvent.inTransaction(
this.receipts.execute.transactionHash,
this.timelock,
'ExecuteTransaction',
{ eta: this.eta },
);
await expectEvent.inTransaction(
this.receipts.execute.transactionHash,
this.receiver,
'MockFunctionCalled',
);
});
runGovernorWorkflow();
});
describe('not queued', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { enable: false },
execute: { error: 'GovernorTimelockCompound: proposal not yet queued' },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded);
});
runGovernorWorkflow();
});
describe('to early', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
execute: { error: 'Timelock::executeTransaction: Transaction hasn\'t surpassed time lock' },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Queued);
});
runGovernorWorkflow();
});
describe('to late', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 30 * 86400 },
execute: { error: 'Governor: proposal not successful' },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Expired);
});
runGovernorWorkflow();
});
describe('deplicated underlying call', function () {
beforeEach(async function () {
this.settings = {
proposal: [
Array(2).fill(this.token.address),
Array(2).fill(web3.utils.toWei('0')),
Array(2).fill(this.token.contract.methods.approve(this.receiver.address, constants.MAX_UINT256).encodeABI()),
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: {
error: 'GovernorTimelockCompound: identical proposal action already queued',
},
execute: {
error: 'GovernorTimelockCompound: proposal not yet queued',
},
},
};
});
runGovernorWorkflow();
});
describe('re-queue / re-execute', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 7 * 86400 },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Executed);
await expectRevert(
this.mock.queue(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'Governor: proposal not successful',
);
await expectRevert(
this.mock.execute(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'Governor: proposal not successful',
);
});
runGovernorWorkflow();
});
describe('cancel before queue prevents scheduling', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { enable: false },
execute: { enable: false },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded);
expectEvent(
await this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'ProposalCanceled',
{ proposalId: this.id },
);
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
await expectRevert(
this.mock.queue(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'Governor: proposal not successful',
);
});
runGovernorWorkflow();
});
describe('cancel after queue prevents executing', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 7 * 86400 },
execute: { enable: false },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Queued);
const receipt = await this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash);
expectEvent(
receipt,
'ProposalCanceled',
{ proposalId: this.id },
);
await expectEvent.inTransaction(
receipt.receipt.transactionHash,
this.timelock,
'CancelTransaction',
);
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
await expectRevert(
this.mock.execute(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'Governor: proposal not successful',
);
});
runGovernorWorkflow();
});
describe('updateTimelock', function () {
beforeEach(async function () {
this.newTimelock = await Timelock.new(this.mock.address, 7 * 86400);
});
it('protected', async function () {
await expectRevert(
this.mock.updateTimelock(this.newTimelock.address),
'Governor: onlyGovernance',
);
});
describe('using workflow', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[
this.timelock.address,
this.mock.address,
],
[
web3.utils.toWei('0'),
web3.utils.toWei('0'),
],
[
this.timelock.contract.methods.setPendingAdmin(admin).encodeABI(),
this.mock.contract.methods.updateTimelock(this.newTimelock.address).encodeABI(),
],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 7 * 86400 },
},
};
});
afterEach(async function () {
expectEvent(
this.receipts.propose,
'ProposalCreated',
{ proposalId: this.id },
);
expectEvent(
this.receipts.execute,
'ProposalExecuted',
{ proposalId: this.id },
);
expectEvent(
this.receipts.execute,
'TimelockChange',
{ oldTimelock: this.timelock.address, newTimelock: this.newTimelock.address },
);
expect(await this.mock.timelock()).to.be.bignumber.equal(this.newTimelock.address);
});
runGovernorWorkflow();
});
});
describe('transfer timelock to new governor', function () {
beforeEach(async function () {
this.newGovernor = await Governor.new(name, this.token.address, 8, 32, this.timelock.address, 0);
});
describe('using workflow', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.timelock.address ],
[ web3.utils.toWei('0') ],
[ this.timelock.contract.methods.setPendingAdmin(this.newGovernor.address).encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 7 * 86400 },
},
};
});
afterEach(async function () {
expectEvent(
this.receipts.propose,
'ProposalCreated',
{ proposalId: this.id },
);
expectEvent(
this.receipts.execute,
'ProposalExecuted',
{ proposalId: this.id },
);
await expectEvent.inTransaction(
this.receipts.execute.transactionHash,
this.timelock,
'NewPendingAdmin',
{ newPendingAdmin: this.newGovernor.address },
);
await this.newGovernor.__acceptAdmin();
expect(await this.timelock.admin()).to.be.bignumber.equal(this.newGovernor.address);
});
runGovernorWorkflow();
});
});
});
const { constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const Enums = require('../../helpers/enums');
const {
runGovernorWorkflow,
} = require('../GovernorWorkflow.behavior');
const {
shouldSupportInterfaces,
} = require('../../utils/introspection/SupportsInterface.behavior');
const Token = artifacts.require('ERC20VotesMock');
const Timelock = artifacts.require('TimelockController');
const Governor = artifacts.require('GovernorTimelockControlMock');
const CallReceiver = artifacts.require('CallReceiverMock');
contract('GovernorTimelockControl', function (accounts) {
const [ voter ] = accounts;
const name = 'OZ-Governor';
// const version = '1';
const tokenName = 'MockToken';
const tokenSymbol = 'MTKN';
const tokenSupply = web3.utils.toWei('100');
beforeEach(async function () {
const [ deployer ] = await web3.eth.getAccounts();
this.token = await Token.new(tokenName, tokenSymbol);
this.timelock = await Timelock.new(3600, [], []);
this.mock = await Governor.new(name, this.token.address, 4, 16, this.timelock.address, 0);
this.receiver = await CallReceiver.new();
// normal setup: governor is proposer, everyone is executor, timelock is its own admin
await this.timelock.grantRole(await this.timelock.PROPOSER_ROLE(), this.mock.address);
await this.timelock.grantRole(await this.timelock.EXECUTOR_ROLE(), constants.ZERO_ADDRESS);
await this.timelock.revokeRole(await this.timelock.TIMELOCK_ADMIN_ROLE(), deployer);
await this.token.mint(voter, tokenSupply);
await this.token.delegate(voter, { from: voter });
});
shouldSupportInterfaces([
'ERC165',
'Governor',
'GovernorTimelock',
]);
it('post 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.timelock()).to.be.equal(this.timelock.address);
});
describe('nominal', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 3600 },
},
};
});
afterEach(async function () {
const timelockid = await this.timelock.hashOperationBatch(
...this.settings.proposal.slice(0, 3),
'0x0',
this.descriptionHash,
);
expectEvent(
this.receipts.propose,
'ProposalCreated',
{ proposalId: this.id },
);
expectEvent(
this.receipts.queue,
'ProposalQueued',
{ proposalId: this.id },
);
await expectEvent.inTransaction(
this.receipts.queue.transactionHash,
this.timelock,
'CallScheduled',
{ id: timelockid },
);
expectEvent(
this.receipts.execute,
'ProposalExecuted',
{ proposalId: this.id },
);
await expectEvent.inTransaction(
this.receipts.execute.transactionHash,
this.timelock,
'CallExecuted',
{ id: timelockid },
);
await expectEvent.inTransaction(
this.receipts.execute.transactionHash,
this.receiver,
'MockFunctionCalled',
);
});
runGovernorWorkflow();
});
describe('executed by other proposer', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 3600 },
execute: { enable: false },
},
};
});
afterEach(async function () {
await this.timelock.executeBatch(
...this.settings.proposal.slice(0, 3),
'0x0',
this.descriptionHash,
);
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Executed);
await expectRevert(
this.mock.execute(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'Governor: proposal not successful',
);
});
runGovernorWorkflow();
});
describe('not queued', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { enable: false },
execute: { error: 'TimelockController: operation is not ready' },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded);
});
runGovernorWorkflow();
});
describe('to early', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
execute: { error: 'TimelockController: operation is not ready' },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Queued);
});
runGovernorWorkflow();
});
describe('re-queue / re-execute', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 3600 },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Executed);
await expectRevert(
this.mock.queue(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'Governor: proposal not successful',
);
await expectRevert(
this.mock.execute(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'Governor: proposal not successful',
);
});
runGovernorWorkflow();
});
describe('cancel before queue prevents scheduling', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { enable: false },
execute: { enable: false },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded);
expectEvent(
await this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'ProposalCanceled',
{ proposalId: this.id },
);
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
await expectRevert(
this.mock.queue(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'Governor: proposal not successful',
);
});
runGovernorWorkflow();
});
describe('cancel after queue prevents execution', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ web3.utils.toWei('0') ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 3600 },
execute: { enable: false },
},
};
});
afterEach(async function () {
const timelockid = await this.timelock.hashOperationBatch(
...this.settings.proposal.slice(0, 3),
'0x0',
this.descriptionHash,
);
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Queued);
const receipt = await this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash);
expectEvent(
receipt,
'ProposalCanceled',
{ proposalId: this.id },
);
await expectEvent.inTransaction(
receipt.receipt.transactionHash,
this.timelock,
'Cancelled',
{ id: timelockid },
);
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
await expectRevert(
this.mock.execute(...this.settings.proposal.slice(0, -1), this.descriptionHash),
'Governor: proposal not successful',
);
});
runGovernorWorkflow();
});
describe('updateTimelock', function () {
beforeEach(async function () {
this.newTimelock = await Timelock.new(3600, [], []);
});
it('protected', async function () {
await expectRevert(
this.mock.updateTimelock(this.newTimelock.address),
'Governor: onlyGovernance',
);
});
describe('using workflow', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.mock.address ],
[ web3.utils.toWei('0') ],
[ this.mock.contract.methods.updateTimelock(this.newTimelock.address).encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 3600 },
},
};
});
afterEach(async function () {
expectEvent(
this.receipts.propose,
'ProposalCreated',
{ proposalId: this.id },
);
expectEvent(
this.receipts.execute,
'ProposalExecuted',
{ proposalId: this.id },
);
expectEvent(
this.receipts.execute,
'TimelockChange',
{ oldTimelock: this.timelock.address, newTimelock: this.newTimelock.address },
);
expect(await this.mock.timelock()).to.be.bignumber.equal(this.newTimelock.address);
});
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 = {
'getRoleMember(bytes32,uint256)',
'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 = {};
......
......@@ -3,18 +3,27 @@ const { expect } = require('chai');
function shouldBehaveLikeSet (valueA, valueB, valueC) {
async function expectMembersMatch (set, values) {
await Promise.all(values.map(async value =>
expect(await set.contains(value)).to.equal(true),
));
const contains = await Promise.all(values.map(value => set.contains(value)));
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
// limitations when dealing with nested arrays (required for BNs)
expect(await Promise.all([...Array(values.length).keys()].map(async (index) => {
const entry = await set.at(index);
return entry.toString();
}))).to.have.same.members(values.map(v => v.toString()));
const indexedValues = await Promise.all(Array(values.length).fill().map((_, index) => set.at(index)));
expect(
indexedValues.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 () {
......
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