Commit 5f44f704 by Francisco Giordano

Merge branch 'master' of github.com:OpenZeppelin/openzeppelin-contracts into patches

parents f19051aa 2a09e50d
......@@ -2,10 +2,33 @@
## Unreleased
* `Ownable`: add an internal `_transferOwnership(address)`. ([#2568](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#2568))
* `AccessControl`: add internal `_grantRole(bytes32,address)` and `_revokeRole(bytes32,address)`. ([#2568](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#2568))
* `AccessControl`: mark `_setupRole(bytes32,address)` as deprecated in favor of `_grantRole(bytes32,address)`. ([#2568](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#2568))
* `EIP712`: cache `address(this)` to immutable storage to avoid potential issues if a vanilla contract is used in a delegatecall context. ([#2852](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#2852))
* `GovernorTimelockControl`: improve the `state()` function to have it reflect cases where a proposal has been canceled directly on the timelock. ([#2977](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2977))
* `Math`: add a `abs(int256)` method that returns the unsigned absolute value of a signed value. ([#2984](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2984))
* Preset contracts are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com). ([#2986](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2986))
* `Governor`: add a relay function to help recover assets sent to a governor that is not its own executor (e.g. when using a timelock). ([#2926](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2926))
* `GovernorExtendedVoting`: add new module to ensure a minimum voting duration is available after the quorum is reached. ([#2973](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2973))
## 4.4.0 (2021-11-25)
* `Ownable`: add an internal `_transferOwnership(address)`. ([#2568](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2568))
* `AccessControl`: add internal `_grantRole(bytes32,address)` and `_revokeRole(bytes32,address)`. ([#2568](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2568))
* `AccessControl`: mark `_setupRole(bytes32,address)` as deprecated in favor of `_grantRole(bytes32,address)`. ([#2568](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2568))
* `AccessControlEnumerable`: hook into `_grantRole(bytes32,address)` and `_revokeRole(bytes32,address)`. ([#2946](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2946))
* `EIP712`: cache `address(this)` to immutable storage to avoid potential issues if a vanilla contract is used in a delegatecall context. ([#2852](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2852))
* Add internal `_setApprovalForAll` to `ERC721` and `ERC1155`. ([#2834](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2834))
* `Governor`: shift vote start and end by one block to better match Compound's GovernorBravo and prevent voting at the Governor level if the voting snapshot is not ready. ([#2892](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2892))
* `GovernorCompatibilityBravo`: consider quorum an inclusive rather than exclusive minimum to match Compound's GovernorBravo. ([#2974](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2974))
* `GovernorSettings`: a new governor module that manages voting settings updatable through governance actions. ([#2904](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2904))
* `PaymentSplitter`: now supports ERC20 assets in addition to Ether. ([#2858](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2858))
* `ECDSA`: add a variant of `toEthSignedMessageHash` for arbitrary length message hashing. ([#2865](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2865))
* `MerkleProof`: add a `processProof` function that returns the rebuilt root hash given a leaf and a proof. ([#2841](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2841))
* `VestingWallet`: new contract that handles the vesting of Ether and ERC20 tokens following a customizable vesting schedule. ([#2748](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2748))
* `Governor`: enable receiving Ether when a Timelock contract is not used. ([#2748](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2849))
* `GovernorTimelockCompound`: fix ability to use Ether stored in the Timelock contract. ([#2748](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2849))
## 4.3.3
* `ERC1155Supply`: Handle `totalSupply` changes by hooking into `_beforeTokenTransfer` to ensure consistency of balances and supply during `IERC1155Receiver.onERC1155Received` calls.
## 4.3.2 (2021-09-14)
......
......@@ -83,7 +83,7 @@ The core development principles and strategies that OpenZeppelin Contracts is ba
The latest audit was done on October 2018 on version 2.0.0.
Please report any security issues you find to security@openzeppelin.org.
We have a [**bug bounty program** on Immunefi](https://www.immunefi.com/bounty/openzeppelin). Please report any security issues you find through the Immunefi dashboard, or reach out to security@openzeppelin.com.
Critical bug fixes will be backported to past major releases.
......
# Security Policy
## Bug Bounty
We have a [**bug bounty program** on Immunefi](https://www.immunefi.com/bounty/openzeppelin). Please report any security issues you find through the Immunefi dashboard, or reach out to security@openzeppelin.com.
Critical bug fixes will be backported to past major releases.
## Supported Versions
The recommendation is to use the latest version available.
......@@ -11,8 +17,4 @@ The recommendation is to use the latest version available.
| 2.5 | :white_check_mark: |
| < 2.0 | :x: |
## Reporting a Vulnerability
Please report any security issues you find to security@openzeppelin.org.
Critical bug fixes will be backported to past major releases.
Note that the Solidity language itself only guarantees security updates for the latest release.
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (access/AccessControl.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (access/AccessControlEnumerable.sol)
pragma solidity ^0.8.0;
......@@ -46,34 +47,18 @@ abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessCon
}
/**
* @dev Overload {grantRole} to track enumerable memberships
* @dev Overload {_grantRole} to track enumerable memberships
*/
function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
super.grantRole(role, account);
function _grantRole(bytes32 role, address account) internal virtual override {
super._grantRole(role, account);
_roleMembers[role].add(account);
}
/**
* @dev Overload {revokeRole} to track enumerable memberships
* @dev Overload {_revokeRole} to track enumerable memberships
*/
function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
super.revokeRole(role, account);
function _revokeRole(bytes32 role, address account) internal virtual override {
super._revokeRole(role, account);
_roleMembers[role].remove(account);
}
/**
* @dev Overload {renounceRole} to track enumerable memberships
*/
function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
super.renounceRole(role, account);
_roleMembers[role].remove(account);
}
/**
* @dev Overload {_setupRole} to track enumerable memberships
*/
function _setupRole(bytes32 role, address account) internal virtual override {
super._setupRole(role, account);
_roleMembers[role].add(account);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (access/IAccessControlEnumerable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (access/Ownable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (finance/PaymentSplitter.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/utils/SafeERC20.sol";
import "../utils/Address.sol";
import "../utils/Context.sol";
......@@ -17,10 +19,15 @@ import "../utils/Context.sol";
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
* function.
*
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
* to run tests before sending real value to this contract.
*/
contract PaymentSplitter is Context {
event PayeeAdded(address account, uint256 shares);
event PaymentReleased(address to, uint256 amount);
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
event PaymentReceived(address from, uint256 amount);
uint256 private _totalShares;
......@@ -30,6 +37,9 @@ contract PaymentSplitter is Context {
mapping(address => uint256) private _released;
address[] private _payees;
mapping(IERC20 => uint256) private _erc20TotalReleased;
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
/**
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
* the matching position in the `shares` array.
......@@ -74,6 +84,14 @@ contract PaymentSplitter is Context {
}
/**
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
* contract.
*/
function totalReleased(IERC20 token) public view returns (uint256) {
return _erc20TotalReleased[token];
}
/**
* @dev Getter for the amount of shares held by an account.
*/
function shares(address account) public view returns (uint256) {
......@@ -88,6 +106,14 @@ contract PaymentSplitter is Context {
}
/**
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
* IERC20 contract.
*/
function released(IERC20 token, address account) public view returns (uint256) {
return _erc20Released[token][account];
}
/**
* @dev Getter for the address of the payee number `index`.
*/
function payee(uint256 index) public view returns (address) {
......@@ -101,19 +127,51 @@ contract PaymentSplitter is Context {
function release(address payable account) public virtual {
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
uint256 totalReceived = address(this).balance + _totalReleased;
uint256 payment = (totalReceived * _shares[account]) / _totalShares - _released[account];
uint256 totalReceived = address(this).balance + totalReleased();
uint256 payment = _pendingPayment(account, totalReceived, released(account));
require(payment != 0, "PaymentSplitter: account is not due payment");
_released[account] = _released[account] + payment;
_totalReleased = _totalReleased + payment;
_released[account] += payment;
_totalReleased += payment;
Address.sendValue(account, payment);
emit PaymentReleased(account, payment);
}
/**
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
* contract.
*/
function release(IERC20 token, address account) public virtual {
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
uint256 payment = _pendingPayment(account, totalReceived, released(token, account));
require(payment != 0, "PaymentSplitter: account is not due payment");
_erc20Released[token][account] += payment;
_erc20TotalReleased[token] += payment;
SafeERC20.safeTransfer(token, account, payment);
emit ERC20PaymentReleased(token, account, payment);
}
/**
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
* already released amounts.
*/
function _pendingPayment(
address account,
uint256 totalReceived,
uint256 alreadyReleased
) private view returns (uint256) {
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
}
/**
* @dev Add a new payee to the contract.
* @param account The address of the payee to add.
* @param shares_ The number of shares owned by the payee.
......
......@@ -3,8 +3,18 @@
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/finance
This directory includes primitives for financial systems. We currently only offer the {PaymentSplitter} contract, but we want to grow this directory so we welcome ideas.
This directory includes primitives for financial systems:
== PaymentSplitter
- {PaymentSplitter} allows to split Ether and ERC20 payments among a group of accounts. The sender does not need to be
aware that the assets will be split in this way, since it is handled transparently by the contract. The split can be
in equal parts or in any other arbitrary proportion.
- {VestingWallet} handles the vesting of Ether and ERC20 tokens for a given beneficiary. Custody of multiple tokens can
be given to this contract, which will release the token to the beneficiary following a given, customizable, vesting
schedule.
== Contracts
{{PaymentSplitter}}
{{VestingWallet}}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (finance/VestingWallet.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/utils/SafeERC20.sol";
import "../utils/Address.sol";
import "../utils/Context.sol";
import "../utils/math/Math.sol";
/**
* @title VestingWallet
* @dev This contract handles the vesting of Eth and ERC20 tokens for a given beneficiary. Custody of multiple tokens
* can be given to this contract, which will release the token to the beneficiary following a given vesting schedule.
* The vesting schedule is customizable through the {vestedAmount} function.
*
* Any token transferred to this contract will follow the vesting schedule as if they were locked from the beginning.
* Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly)
* be immediately releasable.
*/
contract VestingWallet is Context {
event EtherReleased(uint256 amount);
event ERC20Released(address indexed token, uint256 amount);
uint256 private _released;
mapping(address => uint256) private _erc20Released;
address private immutable _beneficiary;
uint64 private immutable _start;
uint64 private immutable _duration;
/**
* @dev Set the beneficiary, start timestamp and vesting duration of the vesting wallet.
*/
constructor(
address beneficiaryAddress,
uint64 startTimestamp,
uint64 durationSeconds
) {
require(beneficiaryAddress != address(0), "VestingWallet: beneficiary is zero address");
_beneficiary = beneficiaryAddress;
_start = startTimestamp;
_duration = durationSeconds;
}
/**
* @dev The contract should be able to receive Eth.
*/
receive() external payable virtual {}
/**
* @dev Getter for the beneficiary address.
*/
function beneficiary() public view virtual returns (address) {
return _beneficiary;
}
/**
* @dev Getter for the start timestamp.
*/
function start() public view virtual returns (uint256) {
return _start;
}
/**
* @dev Getter for the vesting duration.
*/
function duration() public view virtual returns (uint256) {
return _duration;
}
/**
* @dev Amount of eth already released
*/
function released() public view virtual returns (uint256) {
return _released;
}
/**
* @dev Amount of token already released
*/
function released(address token) public view virtual returns (uint256) {
return _erc20Released[token];
}
/**
* @dev Release the native token (ether) that have already vested.
*
* Emits a {TokensReleased} event.
*/
function release() public virtual {
uint256 releasable = vestedAmount(uint64(block.timestamp)) - released();
_released += releasable;
emit EtherReleased(releasable);
Address.sendValue(payable(beneficiary()), releasable);
}
/**
* @dev Release the tokens that have already vested.
*
* Emits a {TokensReleased} event.
*/
function release(address token) public virtual {
uint256 releasable = vestedAmount(token, uint64(block.timestamp)) - released(token);
_erc20Released[token] += releasable;
emit ERC20Released(token, releasable);
SafeERC20.safeTransfer(IERC20(token), beneficiary(), releasable);
}
/**
* @dev Calculates the amount of ether that has already vested. Default implementation is a linear vesting curve.
*/
function vestedAmount(uint64 timestamp) public view virtual returns (uint256) {
return _vestingSchedule(address(this).balance + released(), timestamp);
}
/**
* @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve.
*/
function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256) {
return _vestingSchedule(IERC20(token).balanceOf(address(this)) + released(token), timestamp);
}
/**
* @dev Virtual implementation of the vesting formula. This returns the amout vested, as a function of time, for
* an asset given its total historical allocation.
*/
function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) {
if (timestamp < start()) {
return 0;
} else if (timestamp > start() + duration()) {
return totalAllocation;
} else {
return (totalAllocation * (timestamp - start())) / duration();
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/Governor.sol)
pragma solidity ^0.8.0;
......@@ -109,23 +110,36 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor {
* @dev See {IGovernor-state}.
*/
function state(uint256 proposalId) public view virtual override returns (ProposalState) {
ProposalCore memory proposal = _proposals[proposalId];
ProposalCore storage proposal = _proposals[proposalId];
if (proposal.executed) {
return ProposalState.Executed;
} else if (proposal.canceled) {
}
if (proposal.canceled) {
return ProposalState.Canceled;
} else if (proposal.voteStart.isPending()) {
}
uint256 snapshot = proposalSnapshot(proposalId);
if (snapshot == 0) {
revert("Governor: unknown proposal id");
}
if (snapshot >= block.number) {
return ProposalState.Pending;
} else if (proposal.voteEnd.isPending()) {
}
uint256 deadline = proposalDeadline(proposalId);
if (deadline >= block.number) {
return ProposalState.Active;
} else if (proposal.voteEnd.isExpired()) {
return
_quorumReached(proposalId) && _voteSucceeded(proposalId)
? ProposalState.Succeeded
: ProposalState.Defeated;
}
if (_quorumReached(proposalId) && _voteSucceeded(proposalId)) {
return ProposalState.Succeeded;
} else {
revert("Governor: unknown proposal id");
return ProposalState.Defeated;
}
}
......@@ -144,6 +158,13 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor {
}
/**
* @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_.
*/
function proposalThreshold() public view virtual returns (uint256) {
return 0;
}
/**
* @dev Amount of votes already cast passes the threshold limit.
*/
function _quorumReached(uint256 proposalId) internal view virtual returns (bool);
......@@ -174,6 +195,11 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor {
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 = hashProposal(targets, values, calldatas, keccak256(bytes(description)));
require(targets.length == values.length, "Governor: invalid proposal length");
......@@ -335,6 +361,20 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor {
}
/**
* @dev Relays a transaction or function call to an arbitrary target. In cases where the governance executor
* is some contract other than the governor itself, like when using a timelock, this function can be invoked
* in a governance proposal to recover tokens or Ether that was sent to the governor contract by mistake.
* Note that if the executor is simply the governor itself, use of `relay` is redundant.
*/
function relay(
address target,
uint256 value,
bytes calldata data
) external onlyGovernance {
Address.functionCallWithValue(target, data, value);
}
/**
* @dev Address through which the governor executes action. Will be overloaded by module that execute actions
* through another contract such as a timelock.
*/
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/IGovernor.sol)
pragma solidity ^0.8.0;
......@@ -73,7 +74,7 @@ abstract contract IGovernor is IERC165 {
*
* There are 2 standard keys: `support` and `quorum`.
*
* - `support=bravo` refers to the vote options 0 = For, 1 = Against, 2 = Abstain, as in `GovernorBravo`.
* - `support=bravo` refers to the vote options 0 = Against, 1 = For, 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.
*
......@@ -103,28 +104,31 @@ abstract contract IGovernor is IERC165 {
/**
* @notice module:core
* @dev block number used to retrieve user's votes and quorum.
* @dev Block number used to retrieve user's votes and quorum. As per Compound's Comp and OpenZeppelin's
* ERC20Votes, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the
* beginning of the following block.
*/
function proposalSnapshot(uint256 proposalId) public view virtual returns (uint256);
/**
* @notice module:core
* @dev timestamp at which votes close.
* @dev Block number at which votes close. Votes close at the end of this block, so it is possible to cast a vote
* during this block.
*/
function proposalDeadline(uint256 proposalId) public view virtual 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
* @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() public view virtual returns (uint256);
/**
* @notice module:user-config
* @dev delay, in number of blocks, between the vote start and vote ends.
* @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
* 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() public view virtual returns (uint256);
......
......@@ -40,7 +40,9 @@ Other extensions can customize the behavior or interface in multiple ways.
* {GovernorCompatibilityBravo}: Extends the interface to be fully `GovernorBravo`-compatible. Note that events are compatible regardless of whether this extension is included or not.
* {GovernorProposalThreshold}: Restricts proposals to delegates with a minimum voting power.
* {GovernorSettings}: Manages some of the settings (voting delay, voting period duration, and proposal threshold) in a way that can be updated through a governance proposal, without requiering an upgrade.
* {GovernorPreventLateQuorum}: Ensures there is a minimum voting period after quorum is reached as a security protection against large voters.
In addition to modules and extensions, the core contract requires a few virtual functions to be implemented to your particular specifications:
......@@ -48,7 +50,7 @@ In addition to modules and extensions, the core contract requires a few virtual
* <<Governor-votingPeriod-,`votingPeriod()`>>: Delay (in number of blocks) since the proposal starts until voting 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: Functions of the `Governor` contract do not include access control. If you want to restrict access, you should add these checks by overloading the particular functions. Among these, {Governor-_cancel} is internal by default, and you will have to expose it (which the right access control mechanism) yourself if this function is needed.
NOTE: Functions of the `Governor` contract do not include access control. If you want to restrict access, you should add these checks by overloading the particular functions. Among these, {Governor-_cancel} is internal by default, and you will have to expose it (with the right access control mechanism) yourself if this function is needed.
=== Core
......@@ -72,13 +74,19 @@ NOTE: Functions of the `Governor` contract do not include access control. If you
{{GovernorTimelockCompound}}
{{GovernorProposalThreshold}}
{{GovernorSettings}}
{{GovernorPreventLateQuorum}}
{{GovernorCompatibilityBravo}}
=== Deprecated
{{GovernorProposalThreshold}}
== Timelock
In a governance system, 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}.
In a governance system, the {TimelockController} contract is in charge of introducing a delay between a proposal and its execution. It can be used with or without a {Governor}.
{{TimelockController}}
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/TimelockController.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/compatibility/GovernorCompatibilityBravo.sol)
pragma solidity ^0.8.0;
import "../../utils/Counters.sol";
import "../../utils/math/SafeCast.sol";
import "../extensions/IGovernorTimelock.sol";
import "../extensions/GovernorProposalThreshold.sol";
import "../Governor.sol";
import "./IGovernorCompatibilityBravo.sol";
......@@ -19,12 +19,7 @@ import "./IGovernorCompatibilityBravo.sol";
*
* _Available since v4.3._
*/
abstract contract GovernorCompatibilityBravo is
IGovernorTimelock,
IGovernorCompatibilityBravo,
Governor,
GovernorProposalThreshold
{
abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorCompatibilityBravo, Governor {
using Counters for Counters.Counter;
using Timers for Timers.BlockNumber;
......@@ -63,7 +58,7 @@ abstract contract GovernorCompatibilityBravo is
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual override(IGovernor, Governor, GovernorProposalThreshold) returns (uint256) {
) public virtual override(IGovernor, Governor) returns (uint256) {
_storeProposal(_msgSender(), targets, values, new string[](calldatas.length), calldatas, description);
return super.propose(targets, values, calldatas, description);
}
......@@ -170,16 +165,6 @@ abstract contract GovernorCompatibilityBravo is
// ==================================================== Views =====================================================
/**
* @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_.
*/
function proposalThreshold()
public
view
virtual
override(IGovernorCompatibilityBravo, GovernorProposalThreshold)
returns (uint256);
/**
* @dev See {IGovernorCompatibilityBravo-proposals}.
*/
function proposals(uint256 proposalId)
......@@ -262,7 +247,7 @@ abstract contract GovernorCompatibilityBravo is
*/
function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) {
ProposalDetails storage details = _proposalDetails[proposalId];
return quorum(proposalSnapshot(proposalId)) < details.forVotes;
return quorum(proposalSnapshot(proposalId)) <= details.forVotes;
}
/**
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/compatibility/IGovernorCompatibilityBravo.sol)
pragma solidity ^0.8.0;
......@@ -110,9 +111,4 @@ abstract contract IGovernorCompatibilityBravo is IGovernor {
* @dev Part of the Governor Bravo's interface: _"Gets the receipt for a voter on a given proposal"_.
*/
function getReceipt(uint256 proposalId, address voter) public view virtual 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() public view virtual returns (uint256);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/extensions/GovernorCountingSimple.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../Governor.sol";
import "../../utils/math/Math.sol";
/**
* @dev A module that ensures there is a minimum voting period after quorum is reached. This prevents a large voter from
* swaying a vote and triggering quorum at the last minute, by ensuring there is always time for other voters to react
* and try to oppose the decision.
*
* If a vote causes quorum to be reached, the proposal's voting period may be extended so that it does not end before at
* least a given number of blocks have passed (the "vote extension" parameter). This parameter can be set by the
* governance executor (e.g. through a governance proposal).
*
* _Available since v4.5._
*/
abstract contract GovernorPreventLateQuorum is Governor {
using SafeCast for uint256;
using Timers for Timers.BlockNumber;
uint64 private _voteExtension;
mapping(uint256 => Timers.BlockNumber) private _extendedDeadlines;
/// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period.
event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline);
/// @dev Emitted when the {lateQuorumVoteExtension} parameter is changed.
event LateQuorumVoteExtensionSet(uint64 oldVoteExtension, uint64 newVoteExtension);
/**
* @dev Initializes the vote extension parameter: the number of blocks that are required to pass since a proposal
* reaches quorum until its voting period ends. If necessary the voting period will be extended beyond the one set
* at proposal creation.
*/
constructor(uint64 initialVoteExtension) {
_setLateQuorumVoteExtension(initialVoteExtension);
}
/**
* @dev Returns the proposal deadline, which may have been extended beyond that set at proposal creation, if the
* proposal reached quorum late in the voting period. See {Governor-proposalDeadline}.
*/
function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) {
return Math.max(super.proposalDeadline(proposalId), _extendedDeadlines[proposalId].getDeadline());
}
/**
* @dev Casts a vote and detects if it caused quorum to be reached, potentially extending the voting period. See
* {Governor-_castVote}.
*
* May emit a {ProposalExtended} event.
*/
function _castVote(
uint256 proposalId,
address account,
uint8 support,
string memory reason
) internal virtual override returns (uint256) {
uint256 result = super._castVote(proposalId, account, support, reason);
Timers.BlockNumber storage extendedDeadline = _extendedDeadlines[proposalId];
if (extendedDeadline.isUnset() && _quorumReached(proposalId)) {
uint64 extendedDeadlineValue = block.number.toUint64() + lateQuorumVoteExtension();
if (extendedDeadlineValue > proposalDeadline(proposalId)) {
emit ProposalExtended(proposalId, extendedDeadlineValue);
}
extendedDeadline.setDeadline(extendedDeadlineValue);
}
return result;
}
/**
* @dev Returns the current value of the vote extension parameter: the number of blocks that are required to pass
* from the time a proposal reaches quorum until its voting period ends.
*/
function lateQuorumVoteExtension() public view virtual returns (uint64) {
return _voteExtension;
}
/**
* @dev Changes the {lateQuorumVoteExtension}. This operation can only be performed by the governance executor,
* generally through a governance proposal.
*
* Emits a {LateQuorumVoteExtensionSet} event.
*/
function setLateQuorumVoteExtension(uint64 newVoteExtension) public virtual onlyGovernance {
_setLateQuorumVoteExtension(newVoteExtension);
}
/**
* @dev Changes the {lateQuorumVoteExtension}. This is an internal function that can be exposed in a public function
* like {setLateQuorumVoteExtension} if another access control mechanism is needed.
*
* Emits a {LateQuorumVoteExtensionSet} event.
*/
function _setLateQuorumVoteExtension(uint64 newVoteExtension) internal virtual {
emit LateQuorumVoteExtensionSet(_voteExtension, newVoteExtension);
_voteExtension = newVoteExtension;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/extensions/GovernorProposalThreshold.sol)
pragma solidity ^0.8.0;
......@@ -8,27 +9,15 @@ import "../Governor.sol";
* @dev Extension of {Governor} for proposal restriction to token holders with a minimum balance.
*
* _Available since v4.3._
* _Deprecated since v4.4._
*/
abstract contract GovernorProposalThreshold is Governor {
/**
* @dev See {IGovernor-propose}.
*/
function propose(
address[] memory targets,
uint256[] memory values,
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"
);
return super.propose(targets, values, calldatas, description);
}
/**
* @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_.
*/
function proposalThreshold() public view virtual returns (uint256);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/extensions/GovernorSettings.sol)
pragma solidity ^0.8.0;
import "../Governor.sol";
/**
* @dev Extension of {Governor} for settings updatable through governance.
*
* _Available since v4.4._
*/
abstract contract GovernorSettings is Governor {
uint256 private _votingDelay;
uint256 private _votingPeriod;
uint256 private _proposalThreshold;
event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay);
event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod);
event ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold);
/**
* @dev Initialize the governance parameters.
*/
constructor(
uint256 initialVotingDelay,
uint256 initialVotingPeriod,
uint256 initialProposalThreshold
) {
_setVotingDelay(initialVotingDelay);
_setVotingPeriod(initialVotingPeriod);
_setProposalThreshold(initialProposalThreshold);
}
/**
* @dev See {IGovernor-votingDelay}.
*/
function votingDelay() public view virtual override returns (uint256) {
return _votingDelay;
}
/**
* @dev See {IGovernor-votingPeriod}.
*/
function votingPeriod() public view virtual override returns (uint256) {
return _votingPeriod;
}
/**
* @dev See {Governor-proposalThreshold}.
*/
function proposalThreshold() public view virtual override returns (uint256) {
return _proposalThreshold;
}
/**
* @dev Update the voting delay. This operation can only be performed through a governance proposal.
*
* Emits a {VotingDelaySet} event.
*/
function setVotingDelay(uint256 newVotingDelay) public virtual onlyGovernance {
_setVotingDelay(newVotingDelay);
}
/**
* @dev Update the voting period. This operation can only be performed through a governance proposal.
*
* Emits a {VotingPeriodSet} event.
*/
function setVotingPeriod(uint256 newVotingPeriod) public virtual onlyGovernance {
_setVotingPeriod(newVotingPeriod);
}
/**
* @dev Update the proposal threshold. This operation can only be performed through a governance proposal.
*
* Emits a {ProposalThresholdSet} event.
*/
function setProposalThreshold(uint256 newProposalThreshold) public virtual onlyGovernance {
_setProposalThreshold(newProposalThreshold);
}
/**
* @dev Internal setter for the voting delay.
*
* Emits a {VotingDelaySet} event.
*/
function _setVotingDelay(uint256 newVotingDelay) internal virtual {
emit VotingDelaySet(_votingDelay, newVotingDelay);
_votingDelay = newVotingDelay;
}
/**
* @dev Internal setter for the voting period.
*
* Emits a {VotingPeriodSet} event.
*/
function _setVotingPeriod(uint256 newVotingPeriod) internal virtual {
// voting period must be at least one block long
require(newVotingPeriod > 0, "GovernorSettings: voting period too low");
emit VotingPeriodSet(_votingPeriod, newVotingPeriod);
_votingPeriod = newVotingPeriod;
}
/**
* @dev Internal setter for the proposal threshold.
*
* Emits a {ProposalThresholdSet} event.
*/
function _setProposalThreshold(uint256 newProposalThreshold) internal virtual {
emit ProposalThresholdSet(_proposalThreshold, newProposalThreshold);
_proposalThreshold = newProposalThreshold;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/extensions/GovernorTimelockCompound.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/extensions/GovernorTimelockControl.sol)
pragma solidity ^0.8.0;
......@@ -56,8 +57,10 @@ abstract contract GovernorTimelockControl is IGovernorTimelock, Governor {
return status;
} else if (_timelock.isOperationDone(queueid)) {
return ProposalState.Executed;
} else {
} else if (_timelock.isOperationPending(queueid)) {
return ProposalState.Queued;
} else {
return ProposalState.Canceled;
}
}
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/extensions/GovernorVotes.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/extensions/GovernorVotesComp.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/extensions/GovernorVotesQuorumFraction.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (governance/extensions/IGovernorTimelock.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC1155.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC1155MetadataURI.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC1155Receiver.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC1271.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC1363.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC1363Receiver.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC1363Spender.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC165.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC1820Implementer.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC1820Registry.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC20.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC20Metadata.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC2981.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC3156.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC3156FlashBorrower.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC3156FlashLender.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC721.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC721Enumerable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC721Metadata.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC721Receiver.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC777.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC777Recipient.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IERC777Sender.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/draft-IERC2612.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (metatx/ERC2771Context.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (metatx/MinimalForwarder.sol)
pragma solidity ^0.8.0;
......
......@@ -6,6 +6,7 @@ import "../utils/cryptography/ECDSA.sol";
contract ECDSAMock {
using ECDSA for bytes32;
using ECDSA for bytes;
function recover(bytes32 hash, bytes memory signature) public pure returns (address) {
return hash.recover(signature);
......@@ -33,4 +34,8 @@ contract ECDSAMock {
function toEthSignedMessageHash(bytes32 hash) public pure returns (bytes32) {
return hash.toEthSignedMessageHash();
}
function toEthSignedMessageHash(bytes memory s) public pure returns (bytes32) {
return s.toEthSignedMessageHash();
}
}
......@@ -8,37 +8,14 @@ import "../token/ERC1155/extensions/ERC1155Supply.sol";
contract ERC1155SupplyMock is ERC1155Mock, ERC1155Supply {
constructor(string memory uri) ERC1155Mock(uri) {}
function _mint(
address account,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual override(ERC1155, ERC1155Supply) {
super._mint(account, id, amount, data);
}
function _mintBatch(
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual override(ERC1155, ERC1155Supply) {
super._mintBatch(to, ids, amounts, data);
}
function _burn(
address account,
uint256 id,
uint256 amount
) internal virtual override(ERC1155, ERC1155Supply) {
super._burn(account, id, amount);
}
function _burnBatch(
address account,
uint256[] memory ids,
uint256[] memory amounts
) internal virtual override(ERC1155, ERC1155Supply) {
super._burnBatch(account, ids, amounts);
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
}
}
......@@ -2,34 +2,22 @@
pragma solidity ^0.8.0;
import "../governance/Governor.sol";
import "../governance/extensions/GovernorCountingSimple.sol";
import "../governance/extensions/GovernorVotesComp.sol";
contract GovernorCompMock is Governor, GovernorVotesComp, GovernorCountingSimple {
uint256 immutable _votingDelay;
uint256 immutable _votingPeriod;
constructor(
string memory name_,
ERC20VotesComp token_,
uint256 votingDelay_,
uint256 votingPeriod_
) Governor(name_) GovernorVotesComp(token_) {
_votingDelay = votingDelay_;
_votingPeriod = votingPeriod_;
}
contract GovernorCompMock is GovernorVotesComp, GovernorCountingSimple {
constructor(string memory name_, ERC20VotesComp token_) Governor(name_) GovernorVotesComp(token_) {}
function votingDelay() public view override returns (uint256) {
return _votingDelay;
function quorum(uint256) public pure override returns (uint256) {
return 0;
}
function votingPeriod() public view override returns (uint256) {
return _votingPeriod;
function votingDelay() public pure override returns (uint256) {
return 4;
}
function quorum(uint256) public pure override returns (uint256) {
return 0;
function votingPeriod() public pure override returns (uint256) {
return 16;
}
function cancel(
......
......@@ -3,14 +3,16 @@
pragma solidity ^0.8.0;
import "../governance/compatibility/GovernorCompatibilityBravo.sol";
import "../governance/extensions/GovernorVotesComp.sol";
import "../governance/extensions/GovernorTimelockCompound.sol";
import "../governance/extensions/GovernorSettings.sol";
import "../governance/extensions/GovernorVotesComp.sol";
contract GovernorCompatibilityBravoMock is GovernorCompatibilityBravo, GovernorTimelockCompound, GovernorVotesComp {
uint256 immutable _votingDelay;
uint256 immutable _votingPeriod;
uint256 immutable _proposalThreshold;
contract GovernorCompatibilityBravoMock is
GovernorCompatibilityBravo,
GovernorSettings,
GovernorTimelockCompound,
GovernorVotesComp
{
constructor(
string memory name_,
ERC20VotesComp token_,
......@@ -18,11 +20,12 @@ contract GovernorCompatibilityBravoMock is GovernorCompatibilityBravo, GovernorT
uint256 votingPeriod_,
uint256 proposalThreshold_,
ICompoundTimelock timelock_
) Governor(name_) GovernorVotesComp(token_) GovernorTimelockCompound(timelock_) {
_votingDelay = votingDelay_;
_votingPeriod = votingPeriod_;
_proposalThreshold = proposalThreshold_;
}
)
Governor(name_)
GovernorTimelockCompound(timelock_)
GovernorSettings(votingDelay_, votingPeriod_, proposalThreshold_)
GovernorVotesComp(token_)
{}
function supportsInterface(bytes4 interfaceId)
public
......@@ -34,18 +37,6 @@ contract GovernorCompatibilityBravoMock is GovernorCompatibilityBravo, GovernorT
return super.supportsInterface(interfaceId);
}
function votingDelay() public view override returns (uint256) {
return _votingDelay;
}
function votingPeriod() public view override returns (uint256) {
return _votingPeriod;
}
function proposalThreshold() public view virtual override returns (uint256) {
return _proposalThreshold;
}
function quorum(uint256) public pure override returns (uint256) {
return 0;
}
......@@ -70,6 +61,10 @@ contract GovernorCompatibilityBravoMock is GovernorCompatibilityBravo, GovernorT
return super.proposalEta(proposalId);
}
function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) {
return super.proposalThreshold();
}
function propose(
address[] memory targets,
uint256[] memory values,
......
......@@ -2,32 +2,29 @@
pragma solidity ^0.8.0;
import "../governance/Governor.sol";
import "../governance/extensions/GovernorProposalThreshold.sol";
import "../governance/extensions/GovernorSettings.sol";
import "../governance/extensions/GovernorCountingSimple.sol";
import "../governance/extensions/GovernorVotesQuorumFraction.sol";
contract GovernorMock is Governor, GovernorVotesQuorumFraction, GovernorCountingSimple {
uint256 immutable _votingDelay;
uint256 immutable _votingPeriod;
contract GovernorMock is
GovernorProposalThreshold,
GovernorSettings,
GovernorVotesQuorumFraction,
GovernorCountingSimple
{
constructor(
string memory name_,
ERC20Votes token_,
uint256 votingDelay_,
uint256 votingPeriod_,
uint256 quorumNumerator_
) Governor(name_) GovernorVotes(token_) GovernorVotesQuorumFraction(quorumNumerator_) {
_votingDelay = votingDelay_;
_votingPeriod = votingPeriod_;
}
function votingDelay() public view override returns (uint256) {
return _votingDelay;
}
function votingPeriod() public view override returns (uint256) {
return _votingPeriod;
}
)
Governor(name_)
GovernorSettings(votingDelay_, votingPeriod_, 0)
GovernorVotes(token_)
GovernorVotesQuorumFraction(quorumNumerator_)
{}
function cancel(
address[] memory targets,
......@@ -47,4 +44,17 @@ contract GovernorMock is Governor, GovernorVotesQuorumFraction, GovernorCounting
{
return super.getVotes(account, blockNumber);
}
function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) {
return super.proposalThreshold();
}
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual override(Governor, GovernorProposalThreshold) returns (uint256) {
return super.propose(targets, values, calldatas, description);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../governance/extensions/GovernorPreventLateQuorum.sol";
import "../governance/extensions/GovernorSettings.sol";
import "../governance/extensions/GovernorCountingSimple.sol";
import "../governance/extensions/GovernorVotes.sol";
contract GovernorPreventLateQuorumMock is
GovernorSettings,
GovernorVotes,
GovernorCountingSimple,
GovernorPreventLateQuorum
{
uint256 private _quorum;
constructor(
string memory name_,
ERC20Votes token_,
uint256 votingDelay_,
uint256 votingPeriod_,
uint256 quorum_,
uint64 voteExtension_
)
Governor(name_)
GovernorSettings(votingDelay_, votingPeriod_, 0)
GovernorVotes(token_)
GovernorPreventLateQuorum(voteExtension_)
{
_quorum = quorum_;
}
function quorum(uint256) public view virtual override returns (uint256) {
return _quorum;
}
function proposalDeadline(uint256 proposalId)
public
view
virtual
override(Governor, GovernorPreventLateQuorum)
returns (uint256)
{
return super.proposalDeadline(proposalId);
}
function proposalThreshold() public view virtual override(Governor, GovernorSettings) returns (uint256) {
return super.proposalThreshold();
}
function _castVote(
uint256 proposalId,
address account,
uint8 support,
string memory reason
) internal virtual override(Governor, GovernorPreventLateQuorum) returns (uint256) {
return super._castVote(proposalId, account, support, reason);
}
}
......@@ -3,13 +3,16 @@
pragma solidity ^0.8.0;
import "../governance/extensions/GovernorTimelockCompound.sol";
import "../governance/extensions/GovernorSettings.sol";
import "../governance/extensions/GovernorCountingSimple.sol";
import "../governance/extensions/GovernorVotesQuorumFraction.sol";
contract GovernorTimelockCompoundMock is GovernorTimelockCompound, GovernorVotesQuorumFraction, GovernorCountingSimple {
uint256 immutable _votingDelay;
uint256 immutable _votingPeriod;
contract GovernorTimelockCompoundMock is
GovernorSettings,
GovernorTimelockCompound,
GovernorVotesQuorumFraction,
GovernorCountingSimple
{
constructor(
string memory name_,
ERC20Votes token_,
......@@ -20,12 +23,10 @@ contract GovernorTimelockCompoundMock is GovernorTimelockCompound, GovernorVotes
)
Governor(name_)
GovernorTimelockCompound(timelock_)
GovernorSettings(votingDelay_, votingPeriod_, 0)
GovernorVotes(token_)
GovernorVotesQuorumFraction(quorumNumerator_)
{
_votingDelay = votingDelay_;
_votingPeriod = votingPeriod_;
}
{}
function supportsInterface(bytes4 interfaceId)
public
......@@ -37,14 +38,6 @@ contract GovernorTimelockCompoundMock is GovernorTimelockCompound, GovernorVotes
return super.supportsInterface(interfaceId);
}
function votingDelay() public view override returns (uint256) {
return _votingDelay;
}
function votingPeriod() public view override returns (uint256) {
return _votingPeriod;
}
function quorum(uint256 blockNumber)
public
view
......@@ -76,6 +69,10 @@ contract GovernorTimelockCompoundMock is GovernorTimelockCompound, GovernorVotes
return super.state(proposalId);
}
function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) {
return super.proposalThreshold();
}
function _execute(
uint256 proposalId,
address[] memory targets,
......
......@@ -3,13 +3,16 @@
pragma solidity ^0.8.0;
import "../governance/extensions/GovernorTimelockControl.sol";
import "../governance/extensions/GovernorSettings.sol";
import "../governance/extensions/GovernorCountingSimple.sol";
import "../governance/extensions/GovernorVotesQuorumFraction.sol";
contract GovernorTimelockControlMock is GovernorTimelockControl, GovernorVotesQuorumFraction, GovernorCountingSimple {
uint256 immutable _votingDelay;
uint256 immutable _votingPeriod;
contract GovernorTimelockControlMock is
GovernorSettings,
GovernorTimelockControl,
GovernorVotesQuorumFraction,
GovernorCountingSimple
{
constructor(
string memory name_,
ERC20Votes token_,
......@@ -20,12 +23,10 @@ contract GovernorTimelockControlMock is GovernorTimelockControl, GovernorVotesQu
)
Governor(name_)
GovernorTimelockControl(timelock_)
GovernorSettings(votingDelay_, votingPeriod_, 0)
GovernorVotes(token_)
GovernorVotesQuorumFraction(quorumNumerator_)
{
_votingDelay = votingDelay_;
_votingPeriod = votingPeriod_;
}
{}
function supportsInterface(bytes4 interfaceId)
public
......@@ -37,14 +38,6 @@ contract GovernorTimelockControlMock is GovernorTimelockControl, GovernorVotesQu
return super.supportsInterface(interfaceId);
}
function votingDelay() public view override returns (uint256) {
return _votingDelay;
}
function votingPeriod() public view override returns (uint256) {
return _votingPeriod;
}
function quorum(uint256 blockNumber)
public
view
......@@ -76,6 +69,10 @@ contract GovernorTimelockControlMock is GovernorTimelockControl, GovernorVotesQu
return super.state(proposalId);
}
function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) {
return super.proposalThreshold();
}
function _execute(
uint256 proposalId,
address[] memory targets,
......
......@@ -20,4 +20,8 @@ contract MathMock {
function ceilDiv(uint256 a, uint256 b) public pure returns (uint256) {
return Math.ceilDiv(a, b);
}
function abs(int256 n) public pure returns (uint256) {
return Math.abs(n);
}
}
......@@ -12,4 +12,8 @@ contract MerkleProofWrapper {
) public pure returns (bool) {
return MerkleProof.verify(proof, root, leaf);
}
function processProof(bytes32[] memory proof, bytes32 leaf) public pure returns (bytes32) {
return MerkleProof.processProof(proof, leaf);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "../../governance/Governor.sol";
import "../../governance/extensions/GovernorCountingSimple.sol";
import "../../governance/extensions/GovernorVotes.sol";
import "../../governance/extensions/GovernorVotesQuorumFraction.sol";
import "../../governance/extensions/GovernorTimelockControl.sol";
contract MyGovernor1 is
Governor,
GovernorTimelockControl,
GovernorVotes,
GovernorVotesQuorumFraction,
GovernorCountingSimple
{
constructor(ERC20Votes _token, TimelockController _timelock)
Governor("MyGovernor")
GovernorVotes(_token)
GovernorVotesQuorumFraction(4)
GovernorTimelockControl(_timelock)
{}
function votingDelay() public pure override returns (uint256) {
return 1; // 1 block
}
function votingPeriod() public pure override returns (uint256) {
return 45818; // 1 week
}
// The following functions are overrides required by Solidity.
function quorum(uint256 blockNumber)
public
view
override(IGovernor, GovernorVotesQuorumFraction)
returns (uint256)
{
return super.quorum(blockNumber);
}
function getVotes(address account, uint256 blockNumber)
public
view
override(IGovernor, GovernorVotes)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) {
return super.state(proposalId);
}
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public override(Governor, IGovernor) returns (uint256) {
return super.propose(targets, values, calldatas, description);
}
function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) {
super._execute(proposalId, targets, values, calldatas, descriptionHash);
}
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) returns (uint256) {
return super._cancel(targets, values, calldatas, descriptionHash);
}
function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) {
return super._executor();
}
function supportsInterface(bytes4 interfaceId)
public
view
override(Governor, GovernorTimelockControl)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "../../governance/Governor.sol";
import "../../governance/extensions/GovernorProposalThreshold.sol";
import "../../governance/extensions/GovernorCountingSimple.sol";
import "../../governance/extensions/GovernorVotes.sol";
import "../../governance/extensions/GovernorVotesQuorumFraction.sol";
import "../../governance/extensions/GovernorTimelockControl.sol";
contract MyGovernor2 is
Governor,
GovernorTimelockControl,
GovernorProposalThreshold,
GovernorVotes,
GovernorVotesQuorumFraction,
GovernorCountingSimple
{
constructor(ERC20Votes _token, TimelockController _timelock)
Governor("MyGovernor")
GovernorVotes(_token)
GovernorVotesQuorumFraction(4)
GovernorTimelockControl(_timelock)
{}
function votingDelay() public pure override returns (uint256) {
return 1; // 1 block
}
function votingPeriod() public pure override returns (uint256) {
return 45818; // 1 week
}
function proposalThreshold() public pure override returns (uint256) {
return 1000e18;
}
// The following functions are overrides required by Solidity.
function quorum(uint256 blockNumber)
public
view
override(IGovernor, GovernorVotesQuorumFraction)
returns (uint256)
{
return super.quorum(blockNumber);
}
function getVotes(address account, uint256 blockNumber)
public
view
override(IGovernor, GovernorVotes)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) {
return super.state(proposalId);
}
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public override(Governor, GovernorProposalThreshold, IGovernor) returns (uint256) {
return super.propose(targets, values, calldatas, description);
}
function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) {
super._execute(proposalId, targets, values, calldatas, descriptionHash);
}
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) returns (uint256) {
return super._cancel(targets, values, calldatas, descriptionHash);
}
function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) {
return super._executor();
}
function supportsInterface(bytes4 interfaceId)
public
view
override(Governor, GovernorTimelockControl)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "../../governance/Governor.sol";
import "../../governance/compatibility/GovernorCompatibilityBravo.sol";
import "../../governance/extensions/GovernorVotes.sol";
import "../../governance/extensions/GovernorVotesQuorumFraction.sol";
import "../../governance/extensions/GovernorTimelockControl.sol";
contract MyGovernor is
Governor,
GovernorTimelockControl,
GovernorCompatibilityBravo,
GovernorVotes,
GovernorVotesQuorumFraction
{
constructor(ERC20Votes _token, TimelockController _timelock)
Governor("MyGovernor")
GovernorVotes(_token)
GovernorVotesQuorumFraction(4)
GovernorTimelockControl(_timelock)
{}
function votingDelay() public pure override returns (uint256) {
return 1; // 1 block
}
function votingPeriod() public pure override returns (uint256) {
return 45818; // 1 week
}
function proposalThreshold() public pure override returns (uint256) {
return 1000e18;
}
// The following functions are overrides required by Solidity.
function quorum(uint256 blockNumber)
public
view
override(IGovernor, GovernorVotesQuorumFraction)
returns (uint256)
{
return super.quorum(blockNumber);
}
function getVotes(address account, uint256 blockNumber)
public
view
override(IGovernor, GovernorVotes)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
function state(uint256 proposalId)
public
view
override(Governor, IGovernor, GovernorTimelockControl)
returns (ProposalState)
{
return super.state(proposalId);
}
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public override(Governor, GovernorCompatibilityBravo, IGovernor) returns (uint256) {
return super.propose(targets, values, calldatas, description);
}
function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) {
super._execute(proposalId, targets, values, calldatas, descriptionHash);
}
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) returns (uint256) {
return super._cancel(targets, values, calldatas, descriptionHash);
}
function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) {
return super._executor();
}
function supportsInterface(bytes4 interfaceId)
public
view
override(Governor, IERC165, GovernorTimelockControl)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
{
"name": "@openzeppelin/contracts-upgradeable",
"description": "Secure Smart Contract library for Solidity",
"version": "4.3.2",
"version": "4.4.0",
"files": [
"**/*.sol",
"/build/contracts/*.json",
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/Clones.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/ERC1967/ERC1967Proxy.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/ERC1967/ERC1967Upgrade.sol)
pragma solidity ^0.8.2;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/Proxy.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/beacon/BeaconProxy.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/beacon/UpgradeableBeacon.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/transparent/ProxyAdmin.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/transparent/TransparentUpgradeableProxy.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/utils/Initializable.sol)
pragma solidity ^0.8.0;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (security/Pausable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (security/PullPayment.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC1155/ERC1155.sol)
pragma solidity ^0.8.0;
......@@ -100,10 +101,7 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
* @dev See {IERC1155-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
require(_msgSender() != operator, "ERC1155: setting approval status for self");
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_msgSender(), operator, approved);
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
......@@ -249,32 +247,32 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
}
/**
* @dev Creates `amount` tokens of token type `id`, and assigns them to `account`.
* @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - If `account` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* - `to` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _mint(
address account,
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual {
require(account != address(0), "ERC1155: mint to the zero address");
require(to != address(0), "ERC1155: mint to the zero address");
address operator = _msgSender();
_beforeTokenTransfer(operator, address(0), account, _asSingletonArray(id), _asSingletonArray(amount), data);
_beforeTokenTransfer(operator, address(0), to, _asSingletonArray(id), _asSingletonArray(amount), data);
_balances[id][account] += amount;
emit TransferSingle(operator, address(0), account, id, amount);
_balances[id][to] += amount;
emit TransferSingle(operator, address(0), to, id, amount);
_doSafeTransferAcceptanceCheck(operator, address(0), account, id, amount, data);
_doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
}
/**
......@@ -309,31 +307,31 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
}
/**
* @dev Destroys `amount` tokens of token type `id` from `account`
* @dev Destroys `amount` tokens of token type `id` from `from`
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens of token type `id`.
* - `from` cannot be the zero address.
* - `from` must have at least `amount` tokens of token type `id`.
*/
function _burn(
address account,
address from,
uint256 id,
uint256 amount
) internal virtual {
require(account != address(0), "ERC1155: burn from the zero address");
require(from != address(0), "ERC1155: burn from the zero address");
address operator = _msgSender();
_beforeTokenTransfer(operator, account, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");
_beforeTokenTransfer(operator, from, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");
uint256 accountBalance = _balances[id][account];
require(accountBalance >= amount, "ERC1155: burn amount exceeds balance");
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
unchecked {
_balances[id][account] = accountBalance - amount;
_balances[id][from] = fromBalance - amount;
}
emit TransferSingle(operator, account, address(0), id, amount);
emit TransferSingle(operator, from, address(0), id, amount);
}
/**
......@@ -344,29 +342,44 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
* - `ids` and `amounts` must have the same length.
*/
function _burnBatch(
address account,
address from,
uint256[] memory ids,
uint256[] memory amounts
) internal virtual {
require(account != address(0), "ERC1155: burn from the zero address");
require(from != address(0), "ERC1155: burn from the zero address");
require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
address operator = _msgSender();
_beforeTokenTransfer(operator, account, address(0), ids, amounts, "");
_beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
for (uint256 i = 0; i < ids.length; i++) {
uint256 id = ids[i];
uint256 amount = amounts[i];
uint256 accountBalance = _balances[id][account];
require(accountBalance >= amount, "ERC1155: burn amount exceeds balance");
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
unchecked {
_balances[id][account] = accountBalance - amount;
_balances[id][from] = fromBalance - amount;
}
}
emit TransferBatch(operator, account, address(0), ids, amounts);
emit TransferBatch(operator, from, address(0), ids, amounts);
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits a {ApprovalForAll} event.
*/
function _setApprovalForAll(
address owner,
address operator,
bool approved
) internal virtual {
require(owner != operator, "ERC1155: setting approval status for self");
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC1155/IERC1155.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.0;
......@@ -9,18 +10,20 @@ import "../../utils/introspection/IERC165.sol";
*/
interface IERC1155Receiver is IERC165 {
/**
@dev Handles the receipt of a single ERC1155 token type. This function is
called at the end of a `safeTransferFrom` after the balance has been updated.
To accept the transfer, this must return
`bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
(i.e. 0xf23a6e61, or its own function selector).
@param operator The address which initiated the transfer (i.e. msg.sender)
@param from The address which previously owned the token
@param id The ID of the token being transferred
@param value The amount of tokens being transferred
@param data Additional data with no specified format
@return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
* @dev Handles the receipt of a single ERC1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
......@@ -30,18 +33,21 @@ interface IERC1155Receiver is IERC165 {
) external returns (bytes4);
/**
@dev Handles the receipt of a multiple ERC1155 token types. This function
is called at the end of a `safeBatchTransferFrom` after the balances have
been updated. To accept the transfer(s), this must return
`bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
(i.e. 0xbc197c81, or its own function selector).
@param operator The address which initiated the batch transfer (i.e. msg.sender)
@param from The address which previously owned the token
@param ids An array containing ids of each token being transferred (order and length must match values array)
@param values An array containing amounts of each token being transferred (order and length must match ids array)
@param data Additional data with no specified format
@return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
* @dev Handles the receipt of a multiple ERC1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
......
......@@ -26,6 +26,8 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
{{IERC1155Receiver}}
{{ERC1155Receiver}}
== Extensions
{{ERC1155Pausable}}
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC1155/extensions/ERC1155Burnable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC1155/extensions/ERC1155Pausable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC1155/extensions/ERC1155Supply.sol)
pragma solidity ^0.8.0;
......@@ -23,63 +24,35 @@ abstract contract ERC1155Supply is ERC1155 {
}
/**
* @dev Indicates weither any token exist with a given id, or not.
* @dev Indicates whether any token exist with a given id, or not.
*/
function exists(uint256 id) public view virtual returns (bool) {
return ERC1155Supply.totalSupply(id) > 0;
}
/**
* @dev See {ERC1155-_mint}.
* @dev See {ERC1155-_beforeTokenTransfer}.
*/
function _mint(
address account,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual override {
super._mint(account, id, amount, data);
_totalSupply[id] += amount;
}
/**
* @dev See {ERC1155-_mintBatch}.
*/
function _mintBatch(
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual override {
super._mintBatch(to, ids, amounts, data);
for (uint256 i = 0; i < ids.length; ++i) {
_totalSupply[ids[i]] += amounts[i];
}
}
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
/**
* @dev See {ERC1155-_burn}.
*/
function _burn(
address account,
uint256 id,
uint256 amount
) internal virtual override {
super._burn(account, id, amount);
_totalSupply[id] -= amount;
}
if (from == address(0)) {
for (uint256 i = 0; i < ids.length; ++i) {
_totalSupply[ids[i]] += amounts[i];
}
}
/**
* @dev See {ERC1155-_burnBatch}.
*/
function _burnBatch(
address account,
uint256[] memory ids,
uint256[] memory amounts
) internal virtual override {
super._burnBatch(account, ids, amounts);
for (uint256 i = 0; i < ids.length; ++i) {
_totalSupply[ids[i]] -= amounts[i];
if (to == address(0)) {
for (uint256 i = 0; i < ids.length; ++i) {
_totalSupply[ids[i]] -= amounts[i];
}
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC1155/presets/ERC1155PresetMinterPauser.sol)
pragma solidity ^0.8.0;
......@@ -21,6 +22,8 @@ import "../../../utils/Context.sol";
* The account that deploys the contract will be granted the minter and pauser
* roles, as well as the default admin role, which will let it grant both minter
* and pauser roles to other accounts.
*
* _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._
*/
contract ERC1155PresetMinterPauser is Context, AccessControlEnumerable, ERC1155Burnable, ERC1155Pausable {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
......
Contract presets are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com/) as a more powerful alternative.
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC1155/utils/ERC1155Holder.sol)
pragma solidity ^0.8.0;
import "./ERC1155Receiver.sol";
/**
* Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens.
*
* IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
* stuck.
*
* @dev _Available since v3.1._
*/
contract ERC1155Holder is ERC1155Receiver {
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC1155/utils/ERC1155Receiver.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/ERC20Burnable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/ERC20Capped.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/ERC20FlashMint.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/ERC20Pausable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/ERC20Snapshot.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/ERC20Votes.sol)
pragma solidity ^0.8.0;
......@@ -134,7 +135,7 @@ abstract contract ERC20Votes is ERC20Permit {
* @dev Delegate votes from the sender to `delegatee`.
*/
function delegate(address delegatee) public virtual {
return _delegate(_msgSender(), delegatee);
_delegate(_msgSender(), delegatee);
}
/**
......@@ -156,7 +157,7 @@ abstract contract ERC20Votes is ERC20Permit {
s
);
require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce");
return _delegate(signer, delegatee);
_delegate(signer, delegatee);
}
/**
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/ERC20VotesComp.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/ERC20Wrapper.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/draft-ERC20Permit.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/presets/ERC20PresetFixedSupply.sol)
pragma solidity ^0.8.0;
import "../extensions/ERC20Burnable.sol";
......@@ -14,6 +15,8 @@ import "../extensions/ERC20Burnable.sol";
* its documentation for details.
*
* _Available since v3.4._
*
* _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._
*/
contract ERC20PresetFixedSupply is ERC20Burnable {
/**
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/presets/ERC20PresetMinterPauser.sol)
pragma solidity ^0.8.0;
......@@ -21,6 +22,8 @@ import "../../../utils/Context.sol";
* The account that deploys the contract will be granted the minter and pauser
* roles, as well as the default admin role, which will let it grant both minter
* and pauser roles to other accounts.
*
* _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._
*/
contract ERC20PresetMinterPauser is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
......
Contract presets are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com/) as a more powerful alternative.
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/utils/TokenTimelock.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/ERC721.sol)
pragma solidity ^0.8.0;
......@@ -133,10 +134,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
require(operator != _msgSender(), "ERC721: approve to caller");
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_msgSender(), operator, approved);
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
......@@ -357,6 +355,21 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits a {ApprovalForAll} event.
*/
function _setApprovalForAll(
address owner,
address operator,
bool approved
) internal virtual {
require(owner != operator, "ERC721: approve to caller");
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
* The call is not executed if the target address is not a contract.
*
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/extensions/ERC721Burnable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/extensions/ERC721Enumerable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/extensions/ERC721Pausable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/extensions/ERC721URIStorage.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol)
pragma solidity ^0.8.0;
......@@ -24,6 +25,8 @@ import "../../../utils/Counters.sol";
* The account that deploys the contract will be granted the minter and pauser
* roles, as well as the default admin role, which will let it grant both minter
* and pauser roles to other accounts.
*
* _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._
*/
contract ERC721PresetMinterPauserAutoId is
Context,
......
Contract presets are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com/) as a more powerful alternative.
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/utils/ERC721Holder.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC777/ERC777.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC777/IERC777.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC777/IERC777Recipient.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC777/IERC777Sender.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC777/presets/ERC777PresetFixedSupply.sol)
pragma solidity ^0.8.0;
import "../ERC777.sol";
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Address.sol)
pragma solidity ^0.8.0;
......@@ -22,6 +23,15 @@ library Address {
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Arrays.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Context.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Counters.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Create2.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Multicall.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/StorageSlot.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Strings.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Timers.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
......@@ -205,6 +208,18 @@ library ECDSA {
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.0;
......@@ -23,11 +24,21 @@ library MerkleProof {
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
bytes32 computedHash = leaf;
return processProof(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merklee tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*
* _Available since v4.4._
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
// Hash(current computed hash + current element of the proof)
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
......@@ -36,8 +47,6 @@ library MerkleProof {
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
}
// Check if the computed hash (root) is equal to the provided root
return computedHash == root;
return computedHash;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/cryptography/SignatureChecker.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/cryptography/draft-EIP712.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/escrow/ConditionalEscrow.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/escrow/Escrow.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/escrow/RefundEscrow.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/ERC165Checker.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/ERC165Storage.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/ERC1820Implementer.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/IERC1820Implementer.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/IERC1820Registry.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/math/Math.sol)
pragma solidity ^0.8.0;
......@@ -39,4 +40,14 @@ library Math {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a / b + (a % b == 0 ? 0 : 1);
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/math/SafeCast.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/math/SafeMath.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/math/SignedSafeMath.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/structs/BitMaps.sol)
pragma solidity ^0.8.0;
/**
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/structs/EnumerableMap.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/structs/EnumerableSet.sol)
pragma solidity ^0.8.0;
......
......@@ -4,7 +4,7 @@ In this guide you will learn how to create an ERC20 token with a custom supply m
The standard interface implemented by tokens built on Ethereum is called ERC20, and Contracts includes a widely used implementation of it: the aptly named xref:api:token/ERC20.adoc[`ERC20`] contract. This contract, like the standard itself, is quite simple and bare-bones. In fact, if you try to deploy an instance of `ERC20` as-is it will be quite literally useless... it will have no supply! What use is a token with no supply?
The way that supply is created is not defined in the ERC20 document. Every token is free to experiment with their own mechanisms, ranging from the most decentralized to the most centralized, from the most naive to the most researched, and more.
The way that supply is created is not defined in the ERC20 document. Every token is free to experiment with its own mechanisms, ranging from the most decentralized to the most centralized, from the most naive to the most researched, and more.
[[fixed-supply]]
== Fixed Supply
......@@ -99,7 +99,9 @@ contract ERC20WithAutoMinerReward is ERC20 {
}
function _beforeTokenTransfer(address from, address to, uint256 value) internal virtual override {
_mintMinerReward();
if (!(from == address(0) && to == block.coinbase)) {
_mintMinerReward();
}
super._beforeTokenTransfer(from, to, value);
}
}
......
= How to set up on-chain governance
In this guide we will learn how OpenZeppelin’s Govenor contract works, how to set it up, and how to use it to create proposals, vote for them, and execute them, using tools provided by Ethers.js and Tally.
In this guide we will learn how OpenZeppelin’s Governor contract works, how to set it up, and how to use it to create proposals, vote for them, and execute them, using tools provided by Ethers.js and Tally.
NOTE: Find detailed contract documentation at xref:api:governance.adoc[Governance API].
......@@ -28,7 +28,7 @@ When using a timelock with your Governor contract, you can use either OpenZeppel
=== Tally
Tally is a full-fledged application for user owned on-chain governance. It comprises a voting dashboard, proposal creation wizard, real time research and analysis, and educational content.
https://www.withtally.com[Tally] is a full-fledged application for user owned on-chain governance. It comprises a voting dashboard, proposal creation wizard, real time research and analysis, and educational content.
For all of these options, the Governor will be compatible with Tally: users will be able to create proposals, visualize voting power and advocates, navigate proposals, and cast votes. For proposal creation in particular, projects can also use Defender Admin as an alternative interface.
......@@ -134,6 +134,7 @@ For 3) we will use GovernorCountingSimple, a module that offers 3 options to vot
Besides these modules, Governor itself has some parameters we must set.
votingDelay: How long after a proposal is created should voting power be fixed. A large voting delay gives users time to unstake tokens if necessary.
votingPeriod: How long does a proposal remain open to votes.
These parameters are specified in number of blocks. Assuming block time of around 13.14 seconds, we will set votingDelay = 1 day = 6570 blocks, and votingPeriod = 1 week = 45992 blocks.
......@@ -249,7 +250,11 @@ It is good practice to add a timelock to governance decisions. This allows users
IMPORTANT: When using a timelock, it is the timelock that will execute proposals and thus the timelock that should hold any funds, ownership, and access control roles. Funds in the Governor contract are not currently retrievable when using a timelock! (As of version 4.3 there is a caveat when using the Compound Timelock: ETH in the timelock is not easily usable, so it is recommended to manage ERC20 funds only in this combination until a future version resolves the issue.)
TimelockController uses an AccessControl setup that we need to understand in order to set up roles. The Proposer role is in charge of queueing operations: this is the role the Governor instance should be granted, and it should likely be the only proposer in the system. The Executor role is in charge of executing already available operations: we can assign this role to the special zero address to allow anyone to execute (if operations can be particularly time sensitive, the Governor should be made Executor instead). Lastly, there is the Admin role, which can grant and revoke the two previous roles: this is a very sensitive role that will be granted automatically to both deployer and timelock itself, but should be renounced by the deployer after setup.
TimelockController uses an AccessControl setup that we need to understand in order to set up roles.
- The Proposer role is in charge of queueing operations: this is the role the Governor instance should be granted, and it should likely be the only proposer in the system.
- The Executor role is in charge of executing already available operations: we can assign this role to the special zero address to allow anyone to execute (if operations can be particularly time sensitive, the Governor should be made Executor instead).
- Lastly, there is the Admin role, which can grant and revoke the two previous roles: this is a very sensitive role that will be granted automatically to both deployer and timelock itself, but should be renounced by the deployer after setup.
== Proposal Lifecycle
......@@ -301,7 +306,7 @@ image::tally-admin.png[Administration Panel in Tally]
We will see now how to do this manually using Ethers.js.
If a timelock was set up, the first step to execution is queueing. You will notice that both the queue and execute functions require passing in all of the proposal parameters, as opposed to just the proposal id. This is necessary because this data is not stored on chain, as a measure to save gas. Note that these parameters can always be found in the events emitted by the contract. The only parameter that is not sent in its entirety is the description, since this is only needed in its hashed form to compute the proposal id.
If a timelock was set up, the first step to execution is queueing. You will notice that both the queue and execute functions require passing in the entire proposal parameters, as opposed to just the proposal id. This is necessary because this data is not stored on chain, as a measure to save gas. Note that these parameters can always be found in the events emitted by the contract. The only parameter that is not sent in its entirety is the description, since this is only needed in its hashed form to compute the proposal id.
To queue, we call the queue function:
......
......@@ -40,6 +40,11 @@ TIP: If you're new to smart contract development, head to xref:learn::developing
To keep your system secure, you should **always** use the installed code as-is, and neither copy-paste it from online sources, nor modify it yourself. The library is designed so that only the contracts and functions you use are deployed, so you don't need to worry about it needlessly increasing gas costs.
[[security]]
== Security
Please report any security issues you find via our https://www.immunefi.com/bounty/openzeppelin[bug bounty program on Immunefi] or directly to security@openzeppelin.org.
[[next-steps]]
== Learn More
......@@ -51,7 +56,7 @@ The guides in the sidebar will teach about different concepts, and how to use th
The xref:api:token/ERC20.adoc[full API] is also thoroughly documented, and serves as a great reference when developing your smart contract application. You can also ask for help or follow Contracts' development in the https://forum.openzeppelin.com[community forum].
Finally, you may want to take a look at the https://blog.openzeppelin.com/guides/[guides on our blog], which cover several common use cases and good practices.. The following articles provide great background reading, though please note, some of the referenced tools have changed as the tooling in the ecosystem continues to rapidly evolve.
Finally, you may want to take a look at the https://blog.openzeppelin.com/guides/[guides on our blog], which cover several common use cases and good practices. The following articles provide great background reading, though please note, some of the referenced tools have changed as the tooling in the ecosystem continues to rapidly evolve.
* https://blog.openzeppelin.com/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05[The Hitchhiker’s Guide to Smart Contracts in Ethereum] will help you get an overview of the various tools available for smart contract development, and help you set up your environment.
* https://blog.openzeppelin.com/a-gentle-introduction-to-ethereum-programming-part-1-783cc7796094[A Gentle Introduction to Ethereum Programming, Part 1] provides very useful information on an introductory level, including many basic concepts from the Ethereum platform.
......
......@@ -16,7 +16,7 @@ The data signer can be recovered with xref:api:cryptography.adoc#ECDSA-recover-b
using ECDSA for bytes32;
function _verify(bytes32 data, bytes memory signature, address account) internal pure returns (bool) {
return keccak256(data)
return data
.toEthSignedMessageHash()
.recover(signature) == account;
}
......@@ -31,14 +31,14 @@ xref:api:cryptography.adoc#MerkleProof[`MerkleProof`] provides xref:api:cryptogr
[[introspection]]
== Introspection
In Solidity, it's frequently helpful to know whether or not a contract supports an interface you'd like to use. ERC165 is a standard that helps do runtime interface detection. Contracts provides helpers both for implementing ERC165 in your contracts and querying other contracts:
In Solidity, it's frequently helpful to know whether or not a contract supports an interface you'd like to use. ERC165 is a standard that helps do runtime interface detection. Contracts provide helpers both for implementing ERC165 in your contracts and querying other contracts:
* xref:api:introspection.adoc#IERC165[`IERC165`] — this is the ERC165 interface that defines xref:api:introspection.adoc#IERC165-supportsInterface-bytes4-[`supportsInterface`]. When implementing ERC165, you'll conform to this interface.
* xref:api:introspection.adoc#ERC165[`ERC165`] — inherit this contract if you'd like to support interface detection using a lookup table in contract storage. You can register interfaces using xref:api:introspection.adoc#ERC165-_registerInterface-bytes4-[`_registerInterface(bytes4)`]: check out example usage as part of the ERC721 implementation.
* xref:api:introspection.adoc#ERC165Checker[`ERC165Checker`] — ERC165Checker simplifies the process of checking whether or not a contract supports an interface you care about.
* include with `using ERC165Checker for address;`
* xref:api:introspection.adoc#ERC165Checker-_supportsInterface-address-bytes4-[`myAddress._supportsInterface(bytes4)`]
* xref:api:introspection.adoc#ERC165Checker-_supportsAllInterfaces-address-bytes4---[`myAddress._supportsAllInterfaces(bytes4[])`]
* xref:api:introspection.adoc#ERC165Checker-_supportsAllInterfaces-address-bytes4---[`myAddress._supportsAllInterfaces(bytes4[\])`]
[source,solidity]
----
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2,7 +2,7 @@
"private": true,
"name": "openzeppelin-solidity",
"description": "Secure Smart Contract library for Solidity",
"version": "4.3.2",
"version": "4.4.0",
"files": [
"/contracts/**/*.sol",
"/build/contracts/*.json",
......@@ -67,6 +67,7 @@
"eth-sig-util": "^3.0.0",
"ethereumjs-util": "^7.0.7",
"ethereumjs-wallet": "^1.0.1",
"glob": "^7.2.0",
"graphlib": "^2.1.8",
"hardhat": "^2.0.6",
"hardhat-gas-reporter": "^1.0.4",
......
#!/usr/bin/env node
const fs = require('fs');
const glob = require('glob');
const proc = require('child_process');
const gitStatus = proc.execFileSync('git', ['status', '--porcelain', '-uno', 'contracts/**/*.sol']);
if (gitStatus.length > 0) {
console.error('Contracts directory is not clean');
process.exit(1);
}
const { version } = require('../../package.json');
const files = glob.sync('contracts/!(mocks)/**/*.sol');
for (const file of files) {
const current = fs.readFileSync(file, 'utf8');
const updated = current.replace(
/(\/\/ SPDX-License-Identifier:.*)$(\n\/\/ OpenZeppelin Contracts v.*$)?/m,
`$1\n// OpenZeppelin Contracts v${version} (${file.replace('contracts/', '')})`,
);
fs.writeFileSync(file, updated);
}
proc.execFileSync('git', ['add', '--update', 'contracts']);
......@@ -4,5 +4,6 @@ set -o errexit
scripts/release/update-changelog-release-date.js
scripts/release/synchronize-versions.js
scripts/release/update-comment.js
oz-docs update-version
......@@ -4,6 +4,7 @@ const { ZERO_ADDRESS } = constants;
const { expect } = require('chai');
const PaymentSplitter = artifacts.require('PaymentSplitter');
const Token = artifacts.require('ERC20Mock');
contract('PaymentSplitter', function (accounts) {
const [ owner, payee1, payee2, payee3, nonpayee1, payer1 ] = accounts;
......@@ -50,6 +51,7 @@ contract('PaymentSplitter', function (accounts) {
this.shares = [20, 10, 70];
this.contract = await PaymentSplitter.new(this.payees, this.shares);
this.token = await Token.new('MyToken', 'MT', owner, ether('1000'));
});
it('has total shares', async function () {
......@@ -63,10 +65,18 @@ contract('PaymentSplitter', function (accounts) {
}));
});
it('accepts payments', async function () {
await send.ether(owner, this.contract.address, amount);
describe('accepts payments', async function () {
it('Ether', async function () {
await send.ether(owner, this.contract.address, amount);
expect(await balance.current(this.contract.address)).to.be.bignumber.equal(amount);
expect(await balance.current(this.contract.address)).to.be.bignumber.equal(amount);
});
it('Token', async function () {
await this.token.transfer(this.contract.address, amount, { from: owner });
expect(await this.token.balanceOf(this.contract.address)).to.be.bignumber.equal(amount);
});
});
describe('shares', async function () {
......@@ -80,51 +90,107 @@ contract('PaymentSplitter', function (accounts) {
});
describe('release', async function () {
it('reverts if no funds to claim', async function () {
await expectRevert(this.contract.release(payee1),
'PaymentSplitter: account is not due payment',
);
describe('Ether', async function () {
it('reverts if no funds to claim', async function () {
await expectRevert(this.contract.release(payee1),
'PaymentSplitter: account is not due payment',
);
});
it('reverts if non-payee want to claim', async function () {
await send.ether(payer1, this.contract.address, amount);
await expectRevert(this.contract.release(nonpayee1),
'PaymentSplitter: account has no shares',
);
});
});
it('reverts if non-payee want to claim', async function () {
await send.ether(payer1, this.contract.address, amount);
await expectRevert(this.contract.release(nonpayee1),
'PaymentSplitter: account has no shares',
);
describe('Token', async function () {
it('reverts if no funds to claim', async function () {
await expectRevert(this.contract.release(this.token.address, payee1),
'PaymentSplitter: account is not due payment',
);
});
it('reverts if non-payee want to claim', async function () {
await this.token.transfer(this.contract.address, amount, { from: owner });
await expectRevert(this.contract.release(this.token.address, nonpayee1),
'PaymentSplitter: account has no shares',
);
});
});
});
it('distributes funds to payees', async function () {
await send.ether(payer1, this.contract.address, amount);
describe('distributes funds to payees', async function () {
it('Ether', async function () {
await send.ether(payer1, this.contract.address, amount);
// receive funds
const initBalance = await balance.current(this.contract.address);
expect(initBalance).to.be.bignumber.equal(amount);
// distribute to payees
// receive funds
const initBalance = await balance.current(this.contract.address);
expect(initBalance).to.be.bignumber.equal(amount);
const tracker1 = await balance.tracker(payee1);
const { logs: logs1 } = await this.contract.release(payee1);
const profit1 = await tracker1.delta();
expect(profit1).to.be.bignumber.equal(ether('0.20'));
expectEvent.inLogs(logs1, 'PaymentReleased', { to: payee1, amount: profit1 });
// distribute to payees
const tracker2 = await balance.tracker(payee2);
const { logs: logs2 } = await this.contract.release(payee2);
const profit2 = await tracker2.delta();
expect(profit2).to.be.bignumber.equal(ether('0.10'));
expectEvent.inLogs(logs2, 'PaymentReleased', { to: payee2, amount: profit2 });
const tracker1 = await balance.tracker(payee1);
const { logs: logs1 } = await this.contract.release(payee1);
const profit1 = await tracker1.delta();
expect(profit1).to.be.bignumber.equal(ether('0.20'));
expectEvent.inLogs(logs1, 'PaymentReleased', { to: payee1, amount: profit1 });
const tracker3 = await balance.tracker(payee3);
const { logs: logs3 } = await this.contract.release(payee3);
const profit3 = await tracker3.delta();
expect(profit3).to.be.bignumber.equal(ether('0.70'));
expectEvent.inLogs(logs3, 'PaymentReleased', { to: payee3, amount: profit3 });
const tracker2 = await balance.tracker(payee2);
const { logs: logs2 } = await this.contract.release(payee2);
const profit2 = await tracker2.delta();
expect(profit2).to.be.bignumber.equal(ether('0.10'));
expectEvent.inLogs(logs2, 'PaymentReleased', { to: payee2, amount: profit2 });
// end balance should be zero
expect(await balance.current(this.contract.address)).to.be.bignumber.equal('0');
const tracker3 = await balance.tracker(payee3);
const { logs: logs3 } = await this.contract.release(payee3);
const profit3 = await tracker3.delta();
expect(profit3).to.be.bignumber.equal(ether('0.70'));
expectEvent.inLogs(logs3, 'PaymentReleased', { to: payee3, amount: profit3 });
// check correct funds released accounting
expect(await this.contract.totalReleased()).to.be.bignumber.equal(initBalance);
});
// end balance should be zero
expect(await balance.current(this.contract.address)).to.be.bignumber.equal('0');
it('Token', async function () {
expect(await this.token.balanceOf(payee1)).to.be.bignumber.equal('0');
expect(await this.token.balanceOf(payee2)).to.be.bignumber.equal('0');
expect(await this.token.balanceOf(payee3)).to.be.bignumber.equal('0');
// check correct funds released accounting
expect(await this.contract.totalReleased()).to.be.bignumber.equal(initBalance);
await this.token.transfer(this.contract.address, amount, { from: owner });
expectEvent(
await this.contract.release(this.token.address, payee1),
'ERC20PaymentReleased',
{ token: this.token.address, to: payee1, amount: ether('0.20') },
);
await this.token.transfer(this.contract.address, amount, { from: owner });
expectEvent(
await this.contract.release(this.token.address, payee1),
'ERC20PaymentReleased',
{ token: this.token.address, to: payee1, amount: ether('0.20') },
);
expectEvent(
await this.contract.release(this.token.address, payee2),
'ERC20PaymentReleased',
{ token: this.token.address, to: payee2, amount: ether('0.20') },
);
expectEvent(
await this.contract.release(this.token.address, payee3),
'ERC20PaymentReleased',
{ token: this.token.address, to: payee3, amount: ether('1.40') },
);
expect(await this.token.balanceOf(payee1)).to.be.bignumber.equal(ether('0.40'));
expect(await this.token.balanceOf(payee2)).to.be.bignumber.equal(ether('0.20'));
expect(await this.token.balanceOf(payee3)).to.be.bignumber.equal(ether('1.40'));
});
});
});
});
const { expectEvent } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
function releasedEvent (token, amount) {
return token
? [ 'ERC20Released', { token: token.address, amount } ]
: [ 'EtherReleased', { amount } ];
}
function shouldBehaveLikeVesting (beneficiary) {
it('check vesting schedule', async function () {
const [ method, ...args ] = this.token
? [ 'vestedAmount(address,uint64)', this.token.address ]
: [ 'vestedAmount(uint64)' ];
for (const timestamp of this.schedule) {
expect(await this.mock.methods[method](...args, timestamp))
.to.be.bignumber.equal(this.vestingFn(timestamp));
}
});
it('execute vesting schedule', async function () {
const [ method, ...args ] = this.token
? [ 'release(address)', this.token.address ]
: [ 'release()' ];
let released = web3.utils.toBN(0);
const before = await this.getBalance(beneficiary);
{
const receipt = await this.mock.methods[method](...args);
await expectEvent.inTransaction(
receipt.tx,
this.mock,
...releasedEvent(this.token, '0'),
);
await this.checkRelease(receipt, beneficiary, '0');
expect(await this.getBalance(beneficiary)).to.be.bignumber.equal(before);
}
for (const timestamp of this.schedule) {
const vested = this.vestingFn(timestamp);
await new Promise(resolve => web3.currentProvider.send({
method: 'evm_setNextBlockTimestamp',
params: [ timestamp.toNumber() ],
}, resolve));
const receipt = await this.mock.methods[method](...args);
await expectEvent.inTransaction(
receipt.tx,
this.mock,
...releasedEvent(this.token, vested.sub(released)),
);
await this.checkRelease(receipt, beneficiary, vested.sub(released));
expect(await this.getBalance(beneficiary))
.to.be.bignumber.equal(before.add(vested));
released = vested;
}
});
}
module.exports = {
shouldBehaveLikeVesting,
};
const { constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers');
const { web3 } = require('@openzeppelin/test-helpers/src/setup');
const { expect } = require('chai');
const ERC20Mock = artifacts.require('ERC20Mock');
const VestingWallet = artifacts.require('VestingWallet');
const { shouldBehaveLikeVesting } = require('./VestingWallet.behavior');
const min = (...args) => args.slice(1).reduce((x, y) => x.lt(y) ? x : y, args[0]);
contract('VestingWallet', function (accounts) {
const [ sender, beneficiary ] = accounts;
const amount = web3.utils.toBN(web3.utils.toWei('100'));
const duration = web3.utils.toBN(4 * 365 * 86400); // 4 years
beforeEach(async function () {
this.start = (await time.latest()).addn(3600); // in 1 hour
this.mock = await VestingWallet.new(beneficiary, this.start, duration);
});
it('rejects zero address for beneficiary', async function () {
await expectRevert(
VestingWallet.new(constants.ZERO_ADDRESS, this.start, duration),
'VestingWallet: beneficiary is zero address',
);
});
it('check vesting contract', async function () {
expect(await this.mock.beneficiary()).to.be.equal(beneficiary);
expect(await this.mock.start()).to.be.bignumber.equal(this.start);
expect(await this.mock.duration()).to.be.bignumber.equal(duration);
});
describe('vesting schedule', function () {
beforeEach(async function () {
this.schedule = Array(64).fill().map((_, i) => web3.utils.toBN(i).mul(duration).divn(60).add(this.start));
this.vestingFn = timestamp => min(amount, amount.mul(timestamp.sub(this.start)).div(duration));
});
describe('Eth vesting', function () {
beforeEach(async function () {
await web3.eth.sendTransaction({ from: sender, to: this.mock.address, value: amount });
this.getBalance = account => web3.eth.getBalance(account).then(web3.utils.toBN);
this.checkRelease = () => {};
});
shouldBehaveLikeVesting(beneficiary);
});
describe('ERC20 vesting', function () {
beforeEach(async function () {
this.token = await ERC20Mock.new('Name', 'Symbol', this.mock.address, amount);
this.getBalance = (account) => this.token.balanceOf(account);
this.checkRelease = (receipt, to, value) => expectEvent.inTransaction(
receipt.tx,
this.token,
'Transfer',
{ from: this.mock.address, to, value },
);
});
shouldBehaveLikeVesting(beneficiary);
});
});
});
......@@ -559,6 +559,10 @@ contract('Governor', function (accounts) {
await time.advanceBlockTo(this.snapshot);
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Pending);
await time.advanceBlock();
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Active);
});
runGovernorWorkflow();
......@@ -818,4 +822,125 @@ contract('Governor', function (accounts) {
);
});
});
describe('Settings update', function () {
describe('setVotingDelay', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.mock.address ],
[ web3.utils.toWei('0') ],
[ this.mock.contract.methods.setVotingDelay('0').encodeABI() ],
'<proposal description>',
],
tokenHolder: owner,
voters: [
{ voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
],
};
});
afterEach(async function () {
expect(await this.mock.votingDelay()).to.be.bignumber.equal('0');
expectEvent(
this.receipts.execute,
'VotingDelaySet',
{ oldVotingDelay: '4', newVotingDelay: '0' },
);
});
runGovernorWorkflow();
});
describe('setVotingPeriod', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.mock.address ],
[ web3.utils.toWei('0') ],
[ this.mock.contract.methods.setVotingPeriod('32').encodeABI() ],
'<proposal description>',
],
tokenHolder: owner,
voters: [
{ voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
],
};
});
afterEach(async function () {
expect(await this.mock.votingPeriod()).to.be.bignumber.equal('32');
expectEvent(
this.receipts.execute,
'VotingPeriodSet',
{ oldVotingPeriod: '16', newVotingPeriod: '32' },
);
});
runGovernorWorkflow();
});
describe('setVotingPeriod to 0', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.mock.address ],
[ web3.utils.toWei('0') ],
[ this.mock.contract.methods.setVotingPeriod('0').encodeABI() ],
'<proposal description>',
],
tokenHolder: owner,
voters: [
{ voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
],
steps: {
execute: { error: 'GovernorSettings: voting period too low' },
},
};
});
afterEach(async function () {
expect(await this.mock.votingPeriod()).to.be.bignumber.equal('16');
});
runGovernorWorkflow();
});
describe('setProposalThreshold', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.mock.address ],
[ web3.utils.toWei('0') ],
[ this.mock.contract.methods.setProposalThreshold('1000000000000000000').encodeABI() ],
'<proposal description>',
],
tokenHolder: owner,
voters: [
{ voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
],
};
});
afterEach(async function () {
expect(await this.mock.proposalThreshold()).to.be.bignumber.equal('1000000000000000000');
expectEvent(
this.receipts.execute,
'ProposalThresholdSet',
{ oldProposalThreshold: '0', newProposalThreshold: '1000000000000000000' },
);
});
runGovernorWorkflow();
});
describe('update protected', function () {
it('setVotingDelay', async function () {
await expectRevert(this.mock.setVotingDelay('0'), 'Governor: onlyGovernance');
});
it('setVotingPeriod', async function () {
await expectRevert(this.mock.setVotingPeriod('32'), 'Governor: onlyGovernance');
});
it('setProposalThreshold', async function () {
await expectRevert(this.mock.setProposalThreshold('1000000000000000000'), 'Governor: onlyGovernance');
});
});
});
});
......@@ -58,14 +58,14 @@ function runGovernorWorkflow () {
tryGet(this.settings, 'steps.propose.error') === undefined &&
tryGet(this.settings, 'steps.propose.noadvance') !== true
) {
await time.advanceBlockTo(this.snapshot);
await time.advanceBlockTo(this.snapshot.addn(1));
}
}
// vote
if (tryGet(this.settings, 'voters')) {
this.receipts.castVote = [];
for (const voter of this.settings.voters) {
for (const voter of this.settings.voters.filter(({ support }) => !!support)) {
if (!voter.signature) {
this.receipts.castVote.push(
await getReceiptOrRevert(
......@@ -92,7 +92,7 @@ function runGovernorWorkflow () {
// fast forward
if (tryGet(this.settings, 'steps.wait.enable') !== false) {
await time.advanceBlockTo(this.deadline);
await time.advanceBlockTo(this.deadline.addn(1));
}
// queue
......
......@@ -21,7 +21,7 @@ contract('GovernorComp', function (accounts) {
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.mock = await Governor.new(name, this.token.address);
this.receiver = await CallReceiver.new();
await this.token.mint(owner, tokenSupply);
await this.token.delegate(voter1, { from: voter1 });
......
const { BN, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers');
const Enums = require('../../helpers/enums');
const {
runGovernorWorkflow,
} = require('../GovernorWorkflow.behavior');
const Token = artifacts.require('ERC20VotesCompMock');
const Governor = artifacts.require('GovernorPreventLateQuorumMock');
const CallReceiver = artifacts.require('CallReceiverMock');
contract('GovernorPreventLateQuorum', 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');
const votingDelay = new BN(4);
const votingPeriod = new BN(16);
const lateQuorumVoteExtension = new BN(8);
const quorum = web3.utils.toWei('1');
beforeEach(async function () {
this.owner = owner;
this.token = await Token.new(tokenName, tokenSymbol);
this.mock = await Governor.new(
name,
this.token.address,
votingDelay,
votingPeriod,
quorum,
lateQuorumVoteExtension,
);
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(votingDelay);
expect(await this.mock.votingPeriod()).to.be.bignumber.equal(votingPeriod);
expect(await this.mock.quorum(0)).to.be.bignumber.equal(quorum);
expect(await this.mock.lateQuorumVoteExtension()).to.be.bignumber.equal(lateQuorumVoteExtension);
});
describe('nominal is unaffected', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ 0 ],
[ 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('7'), 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);
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'),
),
);
}
});
const startBlock = new BN(this.receipts.propose.blockNumber).add(votingDelay);
const endBlock = new BN(this.receipts.propose.blockNumber).add(votingDelay).add(votingPeriod);
expect(await this.mock.proposalSnapshot(this.id)).to.be.bignumber.equal(startBlock);
expect(await this.mock.proposalDeadline(this.id)).to.be.bignumber.equal(endBlock);
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,
endBlock,
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.notEmitted(
vote,
'ProposalExtended',
);
});
expectEvent(
this.receipts.execute,
'ProposalExecuted',
{ proposalId: this.id },
);
await expectEvent.inTransaction(
this.receipts.execute.transactionHash,
this.receiver,
'MockFunctionCalled',
);
});
runGovernorWorkflow();
});
describe('Delay is extended to prevent last minute take-over', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.receiver.address ],
[ 0 ],
[ this.receiver.contract.methods.mockFunction().encodeABI() ],
'<proposal description>',
],
proposer,
tokenHolder: owner,
voters: [
{ voter: voter1, weight: web3.utils.toWei('0.2'), support: Enums.VoteType.Against },
{ voter: voter2, weight: web3.utils.toWei('1.0') }, // do not actually vote, only getting tokens
{ voter: voter3, weight: web3.utils.toWei('0.9') }, // do not actually vote, only getting tokens
],
steps: {
wait: { enable: false },
execute: { enable: false },
},
};
});
afterEach(async function () {
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Active);
const startBlock = new BN(this.receipts.propose.blockNumber).add(votingDelay);
const endBlock = new BN(this.receipts.propose.blockNumber).add(votingDelay).add(votingPeriod);
expect(await this.mock.proposalSnapshot(this.id)).to.be.bignumber.equal(startBlock);
expect(await this.mock.proposalDeadline(this.id)).to.be.bignumber.equal(endBlock);
// wait until the vote is almost over
await time.advanceBlockTo(endBlock.subn(1));
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Active);
// try to overtake the vote at the last minute
const tx = await this.mock.castVote(this.id, Enums.VoteType.For, { from: voter2 });
// vote duration is extended
const extendedBlock = new BN(tx.receipt.blockNumber).add(lateQuorumVoteExtension);
expect(await this.mock.proposalDeadline(this.id)).to.be.bignumber.equal(extendedBlock);
expectEvent(
tx,
'ProposalExtended',
{ proposalId: this.id, extendedDeadline: extendedBlock },
);
// vote is still active after expected end
await time.advanceBlockTo(endBlock.addn(1));
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Active);
// Still possible to vote
await this.mock.castVote(this.id, Enums.VoteType.Against, { from: voter3 });
// proposal fails
await time.advanceBlockTo(extendedBlock.addn(1));
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Defeated);
});
runGovernorWorkflow();
});
describe('setLateQuorumVoteExtension', function () {
beforeEach(async function () {
this.newVoteExtension = new BN(0); // disable voting delay extension
});
it('protected', async function () {
await expectRevert(
this.mock.setLateQuorumVoteExtension(this.newVoteExtension),
'Governor: onlyGovernance',
);
});
describe('using workflow', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.mock.address ],
[ web3.utils.toWei('0') ],
[ this.mock.contract.methods.setLateQuorumVoteExtension(this.newVoteExtension).encodeABI() ],
'<proposal description>',
],
proposer,
tokenHolder: owner,
voters: [
{ voter: voter1, weight: web3.utils.toWei('1.0'), support: Enums.VoteType.For },
],
};
});
afterEach(async function () {
expectEvent(
this.receipts.propose,
'ProposalCreated',
{ proposalId: this.id },
);
expectEvent(
this.receipts.execute,
'ProposalExecuted',
{ proposalId: this.id },
);
expectEvent(
this.receipts.execute,
'LateQuorumVoteExtensionSet',
{ oldVoteExtension: lateQuorumVoteExtension, newVoteExtension: this.newVoteExtension },
);
expect(await this.mock.lateQuorumVoteExtension()).to.be.bignumber.equal(this.newVoteExtension);
});
runGovernorWorkflow();
});
});
});
......@@ -21,7 +21,7 @@ function makeContractAddress (creator, nonce) {
}
contract('GovernorTimelockCompound', function (accounts) {
const [ admin, voter ] = accounts;
const [ admin, voter, other ] = accounts;
const name = 'OZ-Governor';
// const version = '1';
......@@ -51,6 +51,10 @@ contract('GovernorTimelockCompound', function (accounts) {
'GovernorTimelock',
]);
it('doesn\'t accept ether transfers', async function () {
await expectRevert.unspecified(web3.eth.sendTransaction({ from: voter, to: this.mock.address, value: 1 }));
});
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);
......@@ -324,6 +328,57 @@ contract('GovernorTimelockCompound', function (accounts) {
runGovernorWorkflow();
});
describe('relay', function () {
beforeEach(async function () {
await this.token.mint(this.mock.address, 1);
this.call = [
this.token.address,
0,
this.token.contract.methods.transfer(other, 1).encodeABI(),
];
});
it('protected', async function () {
await expectRevert(
this.mock.relay(...this.call),
'Governor: onlyGovernance',
);
});
describe('using workflow', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[
this.mock.address,
],
[
web3.utils.toWei('0'),
],
[
this.mock.contract.methods.relay(...this.call).encodeABI(),
],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 7 * 86400 },
},
};
expect(await this.token.balanceOf(this.mock.address), 1);
expect(await this.token.balanceOf(other), 0);
});
afterEach(async function () {
expect(await this.token.balanceOf(this.mock.address), 0);
expect(await this.token.balanceOf(other), 1);
});
runGovernorWorkflow();
});
});
describe('updateTimelock', function () {
beforeEach(async function () {
this.newTimelock = await Timelock.new(this.mock.address, 7 * 86400);
......
......@@ -16,7 +16,7 @@ const Governor = artifacts.require('GovernorTimelockControlMock');
const CallReceiver = artifacts.require('CallReceiverMock');
contract('GovernorTimelockControl', function (accounts) {
const [ voter ] = accounts;
const [ admin, voter, other ] = accounts;
const name = 'OZ-Governor';
// const version = '1';
......@@ -33,6 +33,7 @@ contract('GovernorTimelockControl', function (accounts) {
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.PROPOSER_ROLE(), admin);
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);
......@@ -45,6 +46,10 @@ contract('GovernorTimelockControl', function (accounts) {
'GovernorTimelock',
]);
it('doesn\'t accept ether transfers', async function () {
await expectRevert.unspecified(web3.eth.sendTransaction({ from: voter, to: this.mock.address, value: 1 }));
});
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);
......@@ -316,6 +321,96 @@ contract('GovernorTimelockControl', function (accounts) {
runGovernorWorkflow();
});
describe('relay', function () {
beforeEach(async function () {
await this.token.mint(this.mock.address, 1);
this.call = [
this.token.address,
0,
this.token.contract.methods.transfer(other, 1).encodeABI(),
];
});
it('protected', async function () {
await expectRevert(
this.mock.relay(...this.call),
'Governor: onlyGovernance',
);
});
describe('using workflow', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[
this.mock.address,
],
[
web3.utils.toWei('0'),
],
[
this.mock.contract.methods.relay(...this.call).encodeABI(),
],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 7 * 86400 },
},
};
expect(await this.token.balanceOf(this.mock.address), 1);
expect(await this.token.balanceOf(other), 0);
});
afterEach(async function () {
expect(await this.token.balanceOf(this.mock.address), 0);
expect(await this.token.balanceOf(other), 1);
});
runGovernorWorkflow();
});
});
describe('cancel on timelock is forwarded in state', 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.timelock.cancel(timelockid, { from: admin });
expectEvent(
receipt,
'Cancelled',
{ id: timelockid },
);
expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
});
runGovernorWorkflow();
});
describe('updateTimelock', function () {
beforeEach(async function () {
this.newTimelock = await Timelock.new(3600, [], []);
......
......@@ -7,6 +7,7 @@ const ECDSAMock = artifacts.require('ECDSAMock');
const TEST_MESSAGE = web3.utils.sha3('OpenZeppelin');
const WRONG_MESSAGE = web3.utils.sha3('Nope');
const NON_HASH_MESSAGE = '0x' + Buffer.from('abcd').toString('hex');
function to2098Format (signature) {
const long = web3.utils.hexToBytes(signature);
......@@ -84,6 +85,17 @@ contract('ECDSA', function (accounts) {
)).to.equal(other);
});
it('returns signer address with correct signature for arbitrary length message', async function () {
// Create the signature
const signature = await web3.eth.sign(NON_HASH_MESSAGE, other);
// Recover the signer address from the generated message and signature.
expect(await this.ecdsa.recover(
toEthSignedMessageHash(NON_HASH_MESSAGE),
signature,
)).to.equal(other);
});
it('returns a different address', async function () {
const signature = await web3.eth.sign(TEST_MESSAGE, other);
expect(await this.ecdsa.recover(WRONG_MESSAGE, signature)).to.not.equal(other);
......@@ -196,9 +208,15 @@ contract('ECDSA', function (accounts) {
});
});
context('toEthSignedMessage', function () {
it('prefixes hashes correctly', async function () {
expect(await this.ecdsa.toEthSignedMessageHash(TEST_MESSAGE)).to.equal(toEthSignedMessageHash(TEST_MESSAGE));
context('toEthSignedMessageHash', function () {
it('prefixes bytes32 data correctly', async function () {
expect(await this.ecdsa.methods['toEthSignedMessageHash(bytes32)'](TEST_MESSAGE))
.to.equal(toEthSignedMessageHash(TEST_MESSAGE));
});
it('prefixes dynamic length data correctly', async function () {
expect(await this.ecdsa.methods['toEthSignedMessageHash(bytes)'](NON_HASH_MESSAGE))
.to.equal(toEthSignedMessageHash(NON_HASH_MESSAGE));
});
});
});
......@@ -37,7 +37,7 @@ contract('MerkleProof', function (accounts) {
const badElements = ['d', 'e', 'f'];
const badMerkleTree = new MerkleTree(badElements);
const badProof = badMerkleTree.getHexProof(badElements[0], keccak256, { hashLeaves: true, sortPairs: true });
const badProof = badMerkleTree.getHexProof(badElements[0]);
expect(await this.merkleProof.verify(badProof, correctRoot, correctLeaf)).to.equal(false);
});
......
const { BN, constants } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const { MAX_UINT256 } = constants;
const { MAX_UINT256, MAX_INT256, MIN_INT256 } = constants;
const MathMock = artifacts.require('MathMock');
......@@ -85,4 +85,20 @@ contract('Math', function (accounts) {
expect(await this.math.ceilDiv(MAX_UINT256, b)).to.be.bignumber.equal(MAX_UINT256);
});
});
describe('abs', function () {
for (const n of [
MIN_INT256,
MIN_INT256.addn(1),
new BN('-1'),
new BN('0'),
new BN('1'),
MAX_INT256.subn(1),
MAX_INT256,
]) {
it(`correctly computes the absolute value of ${n}`, async function () {
expect(await this.math.abs(n)).to.be.bignumber.equal(n.abs());
});
}
});
});
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