Commit 2667acb2 by Francisco Giordano

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

parents ec097571 9bded169
{
"extends" : [
"standard",
"plugin:promise/recommended",
"standard"
],
"plugins": [
"mocha-no-only",
"promise",
"mocha"
],
"env": {
"browser" : true,
......@@ -53,7 +51,7 @@
"semi": ["error", "always"],
"space-before-function-paren": ["error", "always"],
"mocha-no-only/mocha-no-only": ["error"],
"mocha/no-exclusive-tests": ["error"],
"promise/always-return": "off",
"promise/avoid-new": "off",
......
contact_links:
- name: Support request
- name: Questions & Support Requests
url: https://forum.openzeppelin.com/c/support/contracts/18
about: Ask the community in the Community Forum
about: Ask in the OpenZeppelin Forum
name: Build Docs
on:
push:
branches: [release-v*]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 12.x
- uses: actions/cache@v2
id: cache
with:
path: '**/node_modules'
key: npm-v2-${{ hashFiles('**/package-lock.json') }}
restore-keys: npm-v2-
- run: npm ci
if: steps.cache.outputs.cache-hit != 'true'
- run: bash scripts/git-user-config.sh
- run: node scripts/update-docs-branch.js
- run: git push --all origin
......@@ -12,8 +12,8 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 12.x
- uses: actions/cache@v2
......
{
"singleQuote": true,
"trailingComma": "all",
"overrides": [
{
"files": "*.sol",
"options": {
"singleQuote": false,
"printWidth": 120,
"explicitTypes": "always"
}
......
......@@ -2,9 +2,23 @@
## Unreleased
* `ERC2891`: add implementation of the royalty standard, and the respective extensions for `ERC721` and `ERC1155`. ([#3012](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3012))
* `AccessControl`: add a virtual `_checkRole(bytes32)` function that can be overridden to alter the `onlyRole` modifier behavior. ([#3137](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3137))
* `EnumerableMap`: add new `AddressToUintMap` map type. ([#3150](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3150))
* `ERC1155`: Add a `_afterTokenTransfer` hook for improved extensibility. ([#3166](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3166))
* `DoubleEndedQueue`: a new data structure that supports efficient push and pop to both front and back, useful for FIFO and LIFO queues. ([#3153](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3153))
* `Governor`: improved security of `onlyGovernance` modifier when using an external executor contract (e.g. a timelock) that can operate without necessarily going through the governance protocol. ([#3147](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3147))
* `Governor`: Add a way to parameterize votes. This can be used to implement voting systems such as fractionalized voting, ERC721 based voting, or any number of other systems. The `params` argument added to `_countVote` method, and included in the newly added `_getVotes` method, can be used by counting and voting modules respectively for such purposes.
### Breaking changes
* `Governor`: Adds internal virtual `_getVotes` method that must be implemented; this is a breaking change for existing concrete extensions to `Governor`. To fix this on an existing voting module extension, rename `getVotes` to `_getVotes` and add a `bytes memory` argument. ([#3043](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3043))
* `Governor`: Adds `params` parameter to internal virtual `_countVote ` method; this is a breaking change for existing concrete extensions to `Governor`. To fix this on an existing counting module extension, add a `bytes memory` argument to `_countVote`. ([#3043](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3043))
* `Governor`: Does not emit `VoteCast` event when params data is non-empty; instead emits `VoteCastWithParams` event. To fix this on an integration that consumes the `VoteCast` event, also fetch/monitor `VoteCastWithParams` events. ([#3043](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3043))
## 4.5.0 (2022-02-09)
* `ERC2981`: add implementation of the royalty standard, and the respective extensions for `ERC721` and `ERC1155`. ([#3012](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3012))
* `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))
* `GovernorPreventLateQuorum`: 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))
......@@ -12,11 +26,15 @@
* `Votes`: Added a base contract for vote tracking with delegation. ([#2944](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2944))
* `ERC721Votes`: Added an extension of ERC721 enabled with vote tracking and delegation. ([#2944](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2944))
* `ERC2771Context`: use immutable storage to store the forwarder address, no longer an issue since Solidity >=0.8.8 allows reading immutable variables in the constructor. ([#2917](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2917))
* `Base64`: add a library to parse bytes into base64 strings using `encode(bytes memory)` function, and provide examples to show how to use to build URL-safe `tokenURIs`. ([#2884](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#2884))
* `ERC20`: reduce allowance before triggering transfer. ([#3056](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#3056))
* `ERC20`: do not update allowance on `transferFrom` when allowance is `type(uint256).max`. ([#3085](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#3085))
* `ERC777`: do not update allowance on `transferFrom` when allowance is `type(uint256).max`. ([#3085](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#3085))
* `Base64`: add a library to parse bytes into base64 strings using `encode(bytes memory)` function, and provide examples to show how to use to build URL-safe `tokenURIs`. ([#2884](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2884))
* `ERC20`: reduce allowance before triggering transfer. ([#3056](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3056))
* `ERC20`: do not update allowance on `transferFrom` when allowance is `type(uint256).max`. ([#3085](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3085))
* `ERC20`: add a `_spendAllowance` internal function. ([#3170](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3170))
* `ERC20Burnable`: do not update allowance on `burnFrom` when allowance is `type(uint256).max`. ([#3170](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3170))
* `ERC777`: do not update allowance on `transferFrom` when allowance is `type(uint256).max`. ([#3085](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3085))
* `ERC777`: add a `_spendAllowance` internal function. ([#3170](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3170))
* `SignedMath`: a new signed version of the Math library with `max`, `min`, and `average`. ([#2686](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2686))
* `SignedMath`: add a `abs(int256)` method that returns the unsigned absolute value of a signed value. ([#2984](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2984))
* `ERC1967Upgrade`: Refactor the secure upgrade to use `ERC1822` instead of the previous rollback mechanism. This reduces code complexity and attack surface with similar security guarantees. ([#3021](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3021))
* `UUPSUpgradeable`: Add `ERC1822` compliance to support the updated secure upgrade mechanism. ([#3021](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3021))
* Some more functions have been made virtual to customize them via overrides. In many cases this will not imply that other functions in the contract will automatically adapt to the overridden definitions. People who wish to override should consult the source code to understand the impact and if they need to override any additional functions to achieve the desired behavior.
......@@ -29,7 +47,7 @@
## 4.4.2 (2022-01-11)
### Bugfixes
* `GovernorCompatibilityBravo`: Fix error in the encoding of calldata for proposals submitted through the compatibility interface with explicit signatures. ([#3100](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#3100))
* `GovernorCompatibilityBravo`: Fix error in the encoding of calldata for proposals submitted through the compatibility interface with explicit signatures. ([#3100](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3100))
## 4.4.1 (2021-12-14)
......
The MIT License (MIT)
Copyright (c) 2016-2020 zOS Global Limited
Copyright (c) 2016-2022 zOS Global Limited and contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
......
......@@ -27,6 +27,10 @@ It follows all of the rules for [Writing Upgradeable Contracts]: constructors ar
$ npm install @openzeppelin/contracts-upgradeable
```
OpenZeppelin Contracts features a [stable API](https://docs.openzeppelin.com/contracts/releases-stability#api-stability), which means your contracts won't break unexpectedly when upgrading to a newer minor version.
An alternative to npm is to use the GitHub repository `openzeppelin/openzeppelin-contracts` to retrieve the contracts. When doing this, make sure to specify the tag for a release such as `v4.5.0`, instead of using the `master` branch.
### Usage
The package replicates the structure of the main OpenZeppelin Contracts package, but every file and contract has the suffix `Upgradeable`.
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
......@@ -67,7 +67,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role, _msgSender());
_checkRole(role);
_;
}
......@@ -86,6 +86,18 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (governance/IGovernor.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (governance/IGovernor.sol)
pragma solidity ^0.8.0;
......@@ -48,13 +48,28 @@ abstract contract IGovernor is IERC165 {
event ProposalExecuted(uint256 proposalId);
/**
* @dev Emitted when a vote is cast.
* @dev Emitted when a vote is cast without params.
*
* Note: `support` values should be seen as buckets. There interpretation depends on the voting module used.
* Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
*/
event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);
/**
* @dev Emitted when a vote is cast with params.
*
* Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
* `params` are additional encoded parameters. Their intepepretation also depends on the voting module used.
*/
event VoteCastWithParams(
address indexed voter,
uint256 proposalId,
uint8 support,
uint256 weight,
string reason,
bytes params
);
/**
* @notice module:core
* @dev Name of the governor instance (used in building the ERC712 domain separator).
*/
......@@ -78,6 +93,12 @@ abstract contract IGovernor is IERC165 {
* - `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.
*
* If a counting module makes use of encoded `params`, it should include this under a `params` key with a unique
* name that describes the behavior. For example:
*
* - `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain.
* - `params=erc721` might refer to a scheme where specific NFTs are delegated to vote.
*
* NOTE: The string can be decoded by the standard
* https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams[`URLSearchParams`]
* JavaScript class.
......@@ -152,6 +173,16 @@ abstract contract IGovernor is IERC165 {
function getVotes(address account, uint256 blockNumber) public view virtual returns (uint256);
/**
* @notice module:reputation
* @dev Voting power of an `account` at a specific `blockNumber` given additional encoded parameters.
*/
function getVotesWithParams(
address account,
uint256 blockNumber,
bytes memory params
) public view virtual returns (uint256);
/**
* @notice module:voting
* @dev Returns weither `account` has cast a vote on `proposalId`.
*/
......@@ -204,7 +235,19 @@ abstract contract IGovernor is IERC165 {
) public virtual returns (uint256 balance);
/**
* @dev Cast a vote using the user cryptographic signature.
* @dev Cast a vote with a reason and additional encoded parameters
*
* Emits a {VoteCast} event.
*/
function castVoteWithReasonAndParams(
uint256 proposalId,
uint8 support,
string calldata reason,
bytes memory params
) public virtual returns (uint256 balance);
/**
* @dev Cast a vote using the user's cryptographic signature.
*
* Emits a {VoteCast} event.
*/
......@@ -215,4 +258,19 @@ abstract contract IGovernor is IERC165 {
bytes32 r,
bytes32 s
) public virtual returns (uint256 balance);
/**
* @dev Cast a vote with a reason and additional encoded parameters using the user's cryptographic signature.
*
* Emits a {VoteCast} event.
*/
function castVoteWithReasonAndParamsBySig(
uint256 proposalId,
uint8 support,
string calldata reason,
bytes memory params,
uint8 v,
bytes32 r,
bytes32 s
) public virtual returns (uint256 balance);
}
......@@ -261,6 +261,9 @@ contract TimelockController is AccessControl {
*
* - the caller must have the 'executor' role.
*/
// This function can reenter, but it doesn't pose a risk because _afterCall checks that the proposal is pending,
// thus any modifications to the operation during reentrancy should be caught.
// slither-disable-next-line reentrancy-eth
function execute(
address target,
uint256 value,
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (governance/compatibility/GovernorCompatibilityBravo.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (governance/compatibility/GovernorCompatibilityBravo.sol)
pragma solidity ^0.8.0;
......@@ -265,7 +265,8 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp
uint256 proposalId,
address account,
uint8 support,
uint256 weight
uint256 weight,
bytes memory // params
) internal virtual override {
ProposalDetails storage details = _proposalDetails[proposalId];
Receipt storage receipt = details.receipts[account];
......
......@@ -86,7 +86,8 @@ abstract contract GovernorCountingSimple is Governor {
uint256 proposalId,
address account,
uint8 support,
uint256 weight
uint256 weight,
bytes memory // params
) internal virtual override {
ProposalVote storage proposalvote = _proposalVotes[proposalId];
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (governance/extensions/GovernorPreventLateQuorum.sol)
pragma solidity ^0.8.0;
......@@ -56,9 +57,10 @@ abstract contract GovernorPreventLateQuorum is Governor {
uint256 proposalId,
address account,
uint8 support,
string memory reason
string memory reason,
bytes memory params
) internal virtual override returns (uint256) {
uint256 result = super._castVote(proposalId, account, support, reason);
uint256 result = super._castVote(proposalId, account, support, reason, params);
Timers.BlockNumber storage extendedDeadline = _extendedDeadlines[proposalId];
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (governance/extensions/GovernorTimelockCompound.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (governance/extensions/GovernorTimelockCompound.sol)
pragma solidity ^0.8.0;
......@@ -224,14 +224,16 @@ abstract contract GovernorTimelockCompound is IGovernorTimelock, Governor {
/**
* @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates
* must be proposed, scheduled and executed using the {Governor} workflow.
* must be proposed, scheduled, and executed through governance proposals.
*
* For security reason, the timelock must be handed over to another admin before setting up a new one. The two
* For security reasons, the timelock must be handed over to another admin before setting up a new one. The two
* operations (hand over the timelock) and do the update can be batched in a single proposal.
*
* Note that if the timelock admin has been handed over in a previous operation, we refuse updates made through the
* timelock if admin of the timelock has already been accepted and the operation is executed outside the scope of
* governance.
* CAUTION: It is not recommended to change the timelock while there are other queued governance proposals.
*/
function updateTimelock(ICompoundTimelock newTimelock) external virtual onlyGovernance {
_updateTimelock(newTimelock);
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (governance/extensions/GovernorTimelockControl.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (governance/extensions/GovernorTimelockControl.sol)
pragma solidity ^0.8.0;
......@@ -16,9 +16,10 @@ import "../TimelockController.sol";
* the assets and permissions must be attached to the {TimelockController}. Any asset sent to the {Governor} will be
* inaccessible.
*
* WARNING: Setting up the TimelockController to have additional proposers besides the governor introduces the risk that
* approved governance proposals could be blocked by the other proposers, effectively executing a Denial of Service attack,
* and therefore blocking access to governance-controlled assets.
* WARNING: Setting up the TimelockController to have additional proposers besides the governor is very risky, as it
* grants them powers that they must be trusted or known not to use: 1) {onlyGovernance} functions like {relay} are
* available to them through the timelock, and 2) approved governance proposals can be blocked by them, effectively
* executing a Denial of Service attack. This risk will be mitigated in a future release.
*
* _Available since v4.3._
*/
......@@ -122,6 +123,9 @@ abstract contract GovernorTimelockControl is IGovernorTimelock, Governor {
* @dev Overriden version of the {Governor-_cancel} function to cancel the timelocked proposal if it as already
* been queued.
*/
// This function can reenter through the external call to the timelock, but we assume the timelock is trusted and
// well behaved (according to TimelockController) and this will not happen.
// slither-disable-next-line reentrancy-no-eth
function _cancel(
address[] memory targets,
uint256[] memory values,
......@@ -147,7 +151,9 @@ abstract contract GovernorTimelockControl is IGovernorTimelock, Governor {
/**
* @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates
* must be proposed, scheduled and executed using the {Governor} workflow.
* must be proposed, scheduled, and executed through governance proposals.
*
* CAUTION: It is not recommended to change the timelock while there are other queued governance proposals.
*/
function updateTimelock(TimelockController newTimelock) external virtual onlyGovernance {
_updateTimelock(newTimelock);
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (governance/extensions/GovernorVotes.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (governance/extensions/GovernorVotes.sol)
pragma solidity ^0.8.0;
......@@ -19,9 +19,13 @@ abstract contract GovernorVotes is Governor {
}
/**
* Read the voting weight from the token's built in snapshot mechanism (see {IGovernor-getVotes}).
* Read the voting weight from the token's built in snapshot mechanism (see {Governor-_getVotes}).
*/
function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) {
function _getVotes(
address account,
uint256 blockNumber,
bytes memory /*params*/
) internal view virtual override returns (uint256) {
return token.getPastVotes(account, blockNumber);
}
}
......@@ -19,9 +19,13 @@ abstract contract GovernorVotesComp is Governor {
}
/**
* Read the voting weight from the token's built in snapshot mechanism (see {IGovernor-getVotes}).
* Read the voting weight from the token's built in snapshot mechanism (see {Governor-_getVotes}).
*/
function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) {
function _getVotes(
address account,
uint256 blockNumber,
bytes memory /*params*/
) internal view virtual override returns (uint256) {
return token.getPriorVotes(account, blockNumber);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (governance/extensions/GovernorVotesQuorumFraction.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (governance/extensions/GovernorVotesQuorumFraction.sol)
pragma solidity ^0.8.0;
......@@ -16,26 +16,61 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
event QuorumNumeratorUpdated(uint256 oldQuorumNumerator, uint256 newQuorumNumerator);
/**
* @dev Initialize quorum as a fraction of the token's total supply.
*
* The fraction is specified as `numerator / denominator`. By default the denominator is 100, so quorum is
* specified as a percent: a numerator of 10 corresponds to quorum being 10% of total supply. The denominator can be
* customized by overriding {quorumDenominator}.
*/
constructor(uint256 quorumNumeratorValue) {
_updateQuorumNumerator(quorumNumeratorValue);
}
/**
* @dev Returns the current quorum numerator. See {quorumDenominator}.
*/
function quorumNumerator() public view virtual returns (uint256) {
return _quorumNumerator;
}
/**
* @dev Returns the quorum denominator. Defaults to 100, but may be overridden.
*/
function quorumDenominator() public view virtual returns (uint256) {
return 100;
}
/**
* @dev Returns the quorum for a block number, in terms of number of votes: `supply * numerator / denominator`.
*/
function quorum(uint256 blockNumber) public view virtual override returns (uint256) {
return (token.getPastTotalSupply(blockNumber) * quorumNumerator()) / quorumDenominator();
}
/**
* @dev Changes the quorum numerator.
*
* Emits a {QuorumNumeratorUpdated} event.
*
* Requirements:
*
* - Must be called through a governance proposal.
* - New numerator must be smaller or equal to the denominator.
*/
function updateQuorumNumerator(uint256 newQuorumNumerator) external virtual onlyGovernance {
_updateQuorumNumerator(newQuorumNumerator);
}
/**
* @dev Changes the quorum numerator.
*
* Emits a {QuorumNumeratorUpdated} event.
*
* Requirements:
*
* - New numerator must be smaller or equal to the denominator.
*/
function _updateQuorumNumerator(uint256 newQuorumNumerator) internal virtual {
require(
newQuorumNumerator <= quorumDenominator(),
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (interfaces/IVotes.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.0;
/**
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/Votes.sol)
pragma solidity ^0.8.0;
import "../../utils/Context.sol";
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC2981.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/IERC2981.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
import "../utils/introspection/IERC165.sol";
/**
* @dev Interface for the NFT Royalty Standard.
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.x.0 (proxy/ERC1822/IProxiable.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (metatx/ERC2771Context.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (metatx/ERC2771Context.sol)
pragma solidity ^0.8.9;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (metatx/MinimalForwarder.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (metatx/MinimalForwarder.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/structs/DoubleEndedQueue.sol";
// Bytes32Deque
contract Bytes32DequeMock {
using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque;
event OperationResult(bytes32 value);
DoubleEndedQueue.Bytes32Deque private _vector;
function pushBack(bytes32 value) public {
_vector.pushBack(value);
}
function pushFront(bytes32 value) public {
_vector.pushFront(value);
}
function popFront() public returns (bytes32) {
bytes32 value = _vector.popFront();
emit OperationResult(value);
return value;
}
function popBack() public returns (bytes32) {
bytes32 value = _vector.popBack();
emit OperationResult(value);
return value;
}
function front() public view returns (bytes32) {
return _vector.front();
}
function back() public view returns (bytes32) {
return _vector.back();
}
function at(uint256 i) public view returns (bytes32) {
return _vector.at(i);
}
function clear() public {
_vector.clear();
}
function length() public view returns (uint256) {
return _vector.length();
}
function empty() public view returns (bool) {
return _vector.empty();
}
}
......@@ -4,7 +4,8 @@ pragma solidity ^0.8.0;
import "../utils/structs/EnumerableMap.sol";
contract EnumerableMapMock {
// UintToAddressMap
contract UintToAddressMapMock {
using EnumerableMap for EnumerableMap.UintToAddressMap;
event OperationResult(bool result);
......@@ -45,3 +46,42 @@ contract EnumerableMapMock {
return _map.get(key, errorMessage);
}
}
// AddressToUintMap
contract AddressToUintMapMock {
using EnumerableMap for EnumerableMap.AddressToUintMap;
event OperationResult(bool result);
EnumerableMap.AddressToUintMap private _map;
function contains(address key) public view returns (bool) {
return _map.contains(key);
}
function set(address key, uint256 value) public {
bool result = _map.set(key, value);
emit OperationResult(result);
}
function remove(address key) public {
bool result = _map.remove(key);
emit OperationResult(result);
}
function length() public view returns (uint256) {
return _map.length();
}
function at(uint256 index) public view returns (address key, uint256 value) {
return _map.at(index);
}
function tryGet(address key) public view returns (bool, uint256) {
return _map.tryGet(key);
}
function get(address key) public view returns (uint256) {
return _map.get(key);
}
}
......@@ -28,14 +28,4 @@ contract GovernorCompMock is GovernorVotesComp, GovernorCountingSimple {
) public returns (uint256 proposalId) {
return _cancel(targets, values, calldatas, salt);
}
function getVotes(address account, uint256 blockNumber)
public
view
virtual
override(IGovernor, GovernorVotesComp)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
}
......@@ -124,16 +124,6 @@ contract GovernorCompatibilityBravoMock is
return super._cancel(targets, values, calldatas, salt);
}
function getVotes(address account, uint256 blockNumber)
public
view
virtual
override(IGovernor, GovernorVotesComp)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
function _executor() internal view virtual override(Governor, GovernorTimelockCompound) returns (address) {
return super._executor();
}
......
......@@ -35,16 +35,6 @@ contract GovernorMock is
return _cancel(targets, values, calldatas, salt);
}
function getVotes(address account, uint256 blockNumber)
public
view
virtual
override(IGovernor, GovernorVotes)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) {
return super.proposalThreshold();
}
......
......@@ -53,8 +53,9 @@ contract GovernorPreventLateQuorumMock is
uint256 proposalId,
address account,
uint8 support,
string memory reason
string memory reason,
bytes memory params
) internal virtual override(Governor, GovernorPreventLateQuorum) returns (uint256) {
return super._castVote(proposalId, account, support, reason);
return super._castVote(proposalId, account, support, reason, params);
}
}
......@@ -92,16 +92,6 @@ contract GovernorTimelockCompoundMock is
return super._cancel(targets, values, calldatas, salt);
}
function getVotes(address account, uint256 blockNumber)
public
view
virtual
override(IGovernor, GovernorVotes)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
function _executor() internal view virtual override(Governor, GovernorTimelockCompound) returns (address) {
return super._executor();
}
......
......@@ -92,17 +92,9 @@ contract GovernorTimelockControlMock is
return super._cancel(targets, values, calldatas, descriptionHash);
}
function getVotes(address account, uint256 blockNumber)
public
view
virtual
override(IGovernor, GovernorVotes)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
function _executor() internal view virtual override(Governor, GovernorTimelockControl) returns (address) {
return super._executor();
}
function nonGovernanceFunction() external {}
}
......@@ -28,14 +28,4 @@ contract GovernorVoteMocks is GovernorVotes, GovernorCountingSimple {
) public returns (uint256 proposalId) {
return _cancel(targets, values, calldatas, salt);
}
function getVotes(address account, uint256 blockNumber)
public
view
virtual
override(IGovernor, GovernorVotes)
returns (uint256)
{
return super.getVotes(account, blockNumber);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../governance/extensions/GovernorCountingSimple.sol";
import "../governance/extensions/GovernorVotes.sol";
contract GovernorWithParamsMock is GovernorVotes, GovernorCountingSimple {
event CountParams(uint256 uintParam, string strParam);
constructor(string memory name_, IVotes token_) Governor(name_) GovernorVotes(token_) {}
function quorum(uint256) public pure override returns (uint256) {
return 0;
}
function votingDelay() public pure override returns (uint256) {
return 4;
}
function votingPeriod() public pure override returns (uint256) {
return 16;
}
function _getVotes(
address account,
uint256 blockNumber,
bytes memory params
) internal view virtual override(Governor, GovernorVotes) returns (uint256) {
uint256 reduction = 0;
// If the user provides parameters, we reduce the voting weight by the amount of the integer param
if (params.length > 0) {
(reduction, ) = abi.decode(params, (uint256, string));
}
// reverts on overflow
return super._getVotes(account, blockNumber, params) - reduction;
}
function _countVote(
uint256 proposalId,
address account,
uint8 support,
uint256 weight,
bytes memory params
) internal virtual override(Governor, GovernorCountingSimple) {
if (params.length > 0) {
(uint256 _uintParam, string memory _strParam) = abi.decode(params, (uint256, string));
emit CountParams(_uintParam, _strParam);
}
return super._countVote(proposalId, account, support, weight, params);
}
function cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 salt
) public returns (uint256 proposalId) {
return _cancel(targets, values, calldatas, salt);
}
}
......@@ -20,8 +20,4 @@ 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);
}
}
......@@ -16,4 +16,8 @@ contract SignedMathMock {
function average(int256 a, int256 b) public pure returns (int256) {
return SignedMath.average(a, b);
}
function abs(int256 n) public pure returns (uint256) {
return SignedMath.abs(n);
}
}
......@@ -40,15 +40,6 @@ contract MyGovernor1 is
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);
}
......
......@@ -46,15 +46,6 @@ contract MyGovernor2 is
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);
}
......
......@@ -44,15 +44,6 @@ contract MyGovernor is
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
......
{
"name": "@openzeppelin/contracts-upgradeable",
"description": "Secure Smart Contract library for Solidity",
"version": "4.4.1",
"version": "4.5.0",
"files": [
"**/*.sol",
"/build/contracts/*.json",
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Upgrade.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
pragma solidity ^0.8.2;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/Proxy.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/Proxy.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/utils/Initializable.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/utils/UUPSUpgradeable.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.0;
......
......@@ -167,8 +167,10 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
require(to != address(0), "ERC1155: transfer to the zero address");
address operator = _msgSender();
uint256[] memory ids = _asSingletonArray(id);
uint256[] memory amounts = _asSingletonArray(amount);
_beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data);
_beforeTokenTransfer(operator, from, to, ids, amounts, data);
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
......@@ -179,6 +181,8 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
emit TransferSingle(operator, from, to, id, amount);
_afterTokenTransfer(operator, from, to, ids, amounts, data);
_doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
}
......@@ -220,6 +224,8 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
emit TransferBatch(operator, from, to, ids, amounts);
_afterTokenTransfer(operator, from, to, ids, amounts, data);
_doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
}
......@@ -266,12 +272,16 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
require(to != address(0), "ERC1155: mint to the zero address");
address operator = _msgSender();
uint256[] memory ids = _asSingletonArray(id);
uint256[] memory amounts = _asSingletonArray(amount);
_beforeTokenTransfer(operator, address(0), to, _asSingletonArray(id), _asSingletonArray(amount), data);
_beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
_balances[id][to] += amount;
emit TransferSingle(operator, address(0), to, id, amount);
_afterTokenTransfer(operator, address(0), to, ids, amounts, data);
_doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
}
......@@ -303,6 +313,8 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
emit TransferBatch(operator, address(0), to, ids, amounts);
_afterTokenTransfer(operator, address(0), to, ids, amounts, data);
_doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
}
......@@ -322,8 +334,10 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
require(from != address(0), "ERC1155: burn from the zero address");
address operator = _msgSender();
uint256[] memory ids = _asSingletonArray(id);
uint256[] memory amounts = _asSingletonArray(amount);
_beforeTokenTransfer(operator, from, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");
_beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
......@@ -332,6 +346,8 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
}
emit TransferSingle(operator, from, address(0), id, amount);
_afterTokenTransfer(operator, from, address(0), ids, amounts, "");
}
/**
......@@ -365,6 +381,8 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
}
emit TransferBatch(operator, from, address(0), ids, amounts);
_afterTokenTransfer(operator, from, address(0), ids, amounts, "");
}
/**
......@@ -411,6 +429,35 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
bytes memory data
) internal virtual {}
/**
* @dev Hook that is called after any token transfer. This includes minting
* and burning, as well as batched variants.
*
* The same hook is called on both single and batched variants. For single
* transfers, the length of the `id` and `amount` arrays will be 1.
*
* Calling conditions (for each `id` and `amount` pair):
*
* - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* of token type `id` will be transferred to `to`.
* - When `from` is zero, `amount` tokens of token type `id` will be minted
* for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
* will be burned.
* - `from` and `to` are never both zero.
* - `ids` and `amounts` have the same, non-zero length.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {}
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155Receiver.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.0;
......
......@@ -51,7 +51,13 @@ abstract contract ERC1155Supply is ERC1155 {
if (to == address(0)) {
for (uint256 i = 0; i < ids.length; ++i) {
_totalSupply[ids[i]] -= amounts[i];
uint256 id = ids[i];
uint256 amount = amounts[i];
uint256 supply = _totalSupply[id];
require(supply >= amount, "ERC1155: burn amount exceeds totalSupply");
unchecked {
_totalSupply[id] = supply - amount;
}
}
}
}
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/presets/ERC1155PresetMinterPauser.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/presets/ERC1155PresetMinterPauser.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Holder.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
......@@ -107,11 +107,12 @@ contract ERC20 is Context, IERC20, IERC20Metadata {
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
......@@ -133,7 +134,8 @@ contract ERC20 is Context, IERC20, IERC20Metadata {
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
......@@ -148,26 +150,19 @@ contract ERC20 is Context, IERC20, IERC20Metadata {
*
* Requirements:
*
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address sender,
address recipient,
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
uint256 currentAllowance = _allowances[sender][_msgSender()];
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
}
_transfer(sender, recipient, amount);
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
......@@ -184,7 +179,8 @@ contract ERC20 is Context, IERC20, IERC20Metadata {
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
......@@ -203,10 +199,11 @@ contract ERC20 is Context, IERC20, IERC20Metadata {
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
uint256 currentAllowance = _allowances[_msgSender()][spender];
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(_msgSender(), spender, currentAllowance - subtractedValue);
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
......@@ -222,30 +219,30 @@ contract ERC20 is Context, IERC20, IERC20Metadata {
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address sender,
address recipient,
address from,
address to,
uint256 amount
) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_beforeTokenTransfer(from, to, amount);
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
_balances[from] = fromBalance - amount;
}
_balances[recipient] += amount;
_balances[to] += amount;
emit Transfer(sender, recipient, amount);
emit Transfer(from, to, amount);
_afterTokenTransfer(sender, recipient, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
......@@ -323,6 +320,28 @@ contract ERC20 is Context, IERC20, IERC20Metadata {
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
......@@ -18,13 +18,13 @@ interface IERC20 {
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
......@@ -52,7 +52,7 @@ interface IERC20 {
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
......@@ -61,8 +61,8 @@ interface IERC20 {
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
address from,
address to,
uint256 amount
) external returns (bool);
......
......@@ -7,7 +7,7 @@ This set of interfaces, contracts, and utilities are all related to the https://
TIP: For an overview of ERC20 tokens and a walk through on how to create a token contract read our xref:ROOT:erc20.adoc[ERC20 guide].
There a few core contracts that implement the behavior specified in the EIP:
There are a few core contracts that implement the behavior specified in the EIP:
* {IERC20}: the interface all ERC20 implementations should conform to.
* {IERC20Metadata}: the extended ERC20 interface including the <<ERC20-name,`name`>>, <<ERC20-symbol,`symbol`>> and <<ERC20-decimals,`decimals`>> functions.
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Burnable.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
pragma solidity ^0.8.0;
......@@ -33,11 +33,7 @@ abstract contract ERC20Burnable is Context, ERC20 {
* `amount`.
*/
function burnFrom(address account, uint256 amount) public virtual {
uint256 currentAllowance = allowance(account, _msgSender());
require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance");
unchecked {
_approve(account, _msgSender(), currentAllowance - amount);
}
_spendAllowance(account, _msgSender(), amount);
_burn(account, amount);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20FlashMint.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20FlashMint.sol)
pragma solidity ^0.8.0;
import "../../../interfaces/IERC3156.sol";
import "../../../interfaces/IERC3156FlashBorrower.sol";
import "../../../interfaces/IERC3156FlashLender.sol";
import "../ERC20.sol";
/**
......@@ -21,7 +22,7 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
/**
* @dev Returns the maximum amount of tokens available for loan.
* @param token The address of the token that is requested.
* @return The amont of token that can be loaned.
* @return The amount of token that can be loaned.
*/
function maxFlashLoan(address token) public view virtual override returns (uint256) {
return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0;
......@@ -54,8 +55,11 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
* supported.
* @param amount The amount of tokens to be loaned.
* @param data An arbitrary datafield that is passed to the receiver.
* @return `true` is the flash loan was successful.
* @return `true` if the flash loan was successful.
*/
// This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount
// minted at the beginning is always recovered and burned at the end, or else the entire function will revert.
// slither-disable-next-line reentrancy-no-eth
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
......
......@@ -22,7 +22,7 @@ import "../../../utils/Counters.sol";
* and the account address.
*
* NOTE: Snapshot policy can be customized by overriding the {_getCurrentSnapshotId} method. For example, having it
* return `block.number` will trigger the creation of snapshot at the begining of each new block. When overridding this
* return `block.number` will trigger the creation of snapshot at the beginning of each new block. When overriding this
* function, be careful about the monotonicity of its result. Non-monotonic snapshot ids will break the contract.
*
* Implementing snapshots for every block using this method will incur significant gas costs. For a gas-efficient
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Votes.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Votes.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20VotesComp.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20VotesComp.sol)
pragma solidity ^0.8.0;
......
......@@ -41,7 +41,7 @@ abstract contract ERC20Wrapper is ERC20 {
}
/**
* @dev Mint wrapped token to cover any underlyingTokens that would have been transfered by mistake. Internal
* @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal
* function that can be exposed with access control if desired.
*/
function _recover(address account) internal virtual returns (uint256) {
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/presets/ERC20PresetFixedSupply.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/presets/ERC20PresetFixedSupply.sol)
pragma solidity ^0.8.0;
import "../extensions/ERC20Burnable.sol";
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/presets/ERC20PresetMinterPauser.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/presets/ERC20PresetMinterPauser.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/TokenTimelock.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/utils/TokenTimelock.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/ERC721.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/ERC721.sol)
pragma solidity ^0.8.0;
......@@ -100,7 +100,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overriden in child contracts.
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
......
......@@ -16,7 +16,7 @@ interface IERC721Receiver {
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/extensions/ERC721Royalty.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/ERC721Royalty.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Enumerable.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/extensions/draft-ERC721Votes.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/draft-ERC721Votes.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC777/ERC777.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC777/ERC777.sol)
pragma solidity ^0.8.0;
......@@ -144,16 +144,7 @@ contract ERC777 is Context, IERC777, IERC20 {
* Also emits a {Sent} event.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
require(recipient != address(0), "ERC777: transfer to the zero address");
address from = _msgSender();
_callTokensToSend(from, from, recipient, amount, "", "");
_move(from, from, recipient, amount, "", "");
_callTokensReceived(from, from, recipient, amount, "", "", false);
_send(_msgSender(), recipient, amount, "", "", false);
return true;
}
......@@ -286,25 +277,9 @@ contract ERC777 is Context, IERC777, IERC20 {
address recipient,
uint256 amount
) public virtual override returns (bool) {
require(recipient != address(0), "ERC777: transfer to the zero address");
require(holder != address(0), "ERC777: transfer from the zero address");
address spender = _msgSender();
_callTokensToSend(spender, holder, recipient, amount, "", "");
uint256 currentAllowance = _allowances[holder][spender];
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC777: transfer amount exceeds allowance");
unchecked {
_approve(holder, spender, currentAllowance - amount);
}
}
_move(spender, holder, recipient, amount, "", "");
_callTokensReceived(spender, holder, recipient, amount, "", "", false);
_spendAllowance(holder, spender, amount);
_send(holder, recipient, amount, "", "", false);
return true;
}
......@@ -392,8 +367,8 @@ contract ERC777 is Context, IERC777, IERC20 {
bytes memory operatorData,
bool requireReceptionAck
) internal virtual {
require(from != address(0), "ERC777: send from the zero address");
require(to != address(0), "ERC777: send to the zero address");
require(from != address(0), "ERC777: transfer from the zero address");
require(to != address(0), "ERC777: transfer to the zero address");
address operator = _msgSender();
......@@ -527,6 +502,28 @@ contract ERC777 is Context, IERC777, IERC20 {
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC777: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any token transfer. This includes
* calls to {send}, {transfer}, {operatorSend}, minting and burning.
*
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/common/ERC2981.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (token/common/ERC2981.sol)
pragma solidity ^0.8.0;
......@@ -40,7 +40,13 @@ abstract contract ERC2981 is IERC2981, ERC165 {
/**
* @inheritdoc IERC2981
*/
function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view override returns (address, uint256) {
function royaltyInfo(uint256 _tokenId, uint256 _salePrice)
external
view
virtual
override
returns (address, uint256)
{
RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId];
if (royalty.receiver == address(0)) {
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
pragma solidity ^0.8.1;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Base64.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides a set of functions to operate with Base64 strings.
*
* _Available since v4.5._
*/
library Base64 {
/**
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Checkpoints.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Multicall.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)
pragma solidity ^0.8.0;
......
......@@ -88,6 +88,8 @@ Note that, in all cases, accounts simply _declare_ their interfaces, but they ar
{{EnumerableSet}}
{{DoubleEndedQueue}}
{{Checkpoints}}
== Libraries
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/ECDSA.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/MerkleProof.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.0;
......@@ -11,6 +11,11 @@ pragma solidity ^0.8.0;
* Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
*
* See `test/utils/cryptography/MerkleProof.test.js` for some examples.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the merkle tree could be reinterpreted as a leaf value.
*/
library MerkleProof {
/**
......@@ -28,7 +33,7 @@ library MerkleProof {
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merklee tree up
* @dev Returns the rebuilt hash obtained by traversing a Merkle 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.
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/SignatureChecker.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/SignatureChecker.sol)
pragma solidity ^0.8.0;
......@@ -8,16 +8,20 @@ import "../Address.sol";
import "../../interfaces/IERC1271.sol";
/**
* @dev Signature verification helper: Provide a single mechanism to verify both private-key (EOA) ECDSA signature and
* ERC1271 contract signatures. Using this instead of ECDSA.recover in your contract will make them compatible with
* smart contract wallets such as Argent and Gnosis.
*
* Note: unlike ECDSA signatures, contract signature's are revocable, and the outcome of this function can thus change
* through time. It could return true at block N and false at block N+1 (or the opposite).
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
* Argent and Gnosis Safe.
*
* _Available since v4.1._
*/
library SignatureChecker {
/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
* signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidSignatureNow(
address signer,
bytes32 hash,
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)
// OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
......@@ -40,14 +40,4 @@ 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 (last updated v4.5.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
......@@ -29,4 +30,14 @@ library SignedMath {
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @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
pragma solidity ^0.8.4;
import "../math/SafeCast.sol";
/**
* @dev A sequence of items with the ability to efficiently push and pop items (i.e. insert and remove) on both ends of
* the sequence (called front and back). Among other access patterns, it can be used to implement efficient LIFO and
* FIFO queues. Storage use is optimized, and all operations are O(1) constant time. This includes {clear}, given that
* the existing queue contents are left in storage.
*
* The struct is called `Bytes32Deque`. Other types can be cast to and from `bytes32`. This data structure can only be
* used in storage, and not in memory.
* ```
* DoubleEndedQueue.Bytes32Deque queue;
* ```
*
* _Available since v4.6._
*/
library DoubleEndedQueue {
/**
* @dev An operation (e.g. {front}) couldn't be completed due to the queue being empty.
*/
error Empty();
/**
* @dev An operation (e.g. {at}) could't be completed due to an index being out of bounds.
*/
error OutOfBounds();
/**
* @dev Indices are signed integers because the queue can grow in any direction. They are 128 bits so begin and end
* are packed in a single storage slot for efficient access. Since the items are added one at a time we can safely
* assume that these 128-bit indices will not overflow, and use unchecked arithmetic.
*
* Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
* directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
* lead to unexpected behavior.
*
* Indices are in the range [begin, end) which means the first item is at data[begin] and the last item is at
* data[end - 1].
*/
struct Bytes32Deque {
int128 _begin;
int128 _end;
mapping(int128 => bytes32) _data;
}
/**
* @dev Inserts an item at the end of the queue.
*/
function pushBack(Bytes32Deque storage deque, bytes32 value) internal {
int128 backIndex = deque._end;
deque._data[backIndex] = value;
unchecked {
deque._end = backIndex + 1;
}
}
/**
* @dev Removes the item at the end of the queue and returns it.
*
* Reverts with `Empty` if the queue is empty.
*/
function popBack(Bytes32Deque storage deque) internal returns (bytes32 value) {
if (empty(deque)) revert Empty();
int128 backIndex;
unchecked {
backIndex = deque._end - 1;
}
value = deque._data[backIndex];
delete deque._data[backIndex];
deque._end = backIndex;
}
/**
* @dev Inserts an item at the beginning of the queue.
*/
function pushFront(Bytes32Deque storage deque, bytes32 value) internal {
int128 frontIndex;
unchecked {
frontIndex = deque._begin - 1;
}
deque._data[frontIndex] = value;
deque._begin = frontIndex;
}
/**
* @dev Removes the item at the beginning of the queue and returns it.
*
* Reverts with `Empty` if the queue is empty.
*/
function popFront(Bytes32Deque storage deque) internal returns (bytes32 value) {
if (empty(deque)) revert Empty();
int128 frontIndex = deque._begin;
value = deque._data[frontIndex];
delete deque._data[frontIndex];
unchecked {
deque._begin = frontIndex + 1;
}
}
/**
* @dev Returns the item at the beginning of the queue.
*
* Reverts with `Empty` if the queue is empty.
*/
function front(Bytes32Deque storage deque) internal view returns (bytes32 value) {
if (empty(deque)) revert Empty();
int128 frontIndex = deque._begin;
return deque._data[frontIndex];
}
/**
* @dev Returns the item at the end of the queue.
*
* Reverts with `Empty` if the queue is empty.
*/
function back(Bytes32Deque storage deque) internal view returns (bytes32 value) {
if (empty(deque)) revert Empty();
int128 backIndex;
unchecked {
backIndex = deque._end - 1;
}
return deque._data[backIndex];
}
/**
* @dev Return the item at a position in the queue given by `index`, with the first item at 0 and last item at
* `length(deque) - 1`.
*
* Reverts with `OutOfBounds` if the index is out of bounds.
*/
function at(Bytes32Deque storage deque, uint256 index) internal view returns (bytes32 value) {
// int256(deque._begin) is a safe upcast
int128 idx = SafeCast.toInt128(int256(deque._begin) + SafeCast.toInt256(index));
if (idx >= deque._end) revert OutOfBounds();
return deque._data[idx];
}
/**
* @dev Resets the queue back to being empty.
*
* NOTE: The current items are left behind in storage. This does not affect the functioning of the queue, but misses
* out on potential gas refunds.
*/
function clear(Bytes32Deque storage deque) internal {
deque._begin = 0;
deque._end = 0;
}
/**
* @dev Returns the number of items in the queue.
*/
function length(Bytes32Deque storage deque) internal view returns (uint256) {
// The interface preserves the invariant that begin <= end so we assume this will not overflow.
// We also assume there are at most int256.max items in the queue.
unchecked {
return uint256(int256(deque._end) - int256(deque._begin));
}
}
/**
* @dev Returns true if the queue is empty.
*/
function empty(Bytes32Deque storage deque) internal view returns (bool) {
return deque._end <= deque._begin;
}
}
......@@ -26,8 +26,10 @@ import "./EnumerableSet.sol";
* }
* ```
*
* As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are
* supported.
* The following map types are supported:
*
* - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
* - `address -> uint256` (`AddressToUintMap`) since v4.6.0
*/
library EnumerableMap {
using EnumerableSet for EnumerableSet.Bytes32Set;
......@@ -237,4 +239,84 @@ library EnumerableMap {
) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage))));
}
// AddressToUintMap
struct AddressToUintMap {
Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(
AddressToUintMap storage map,
address key,
uint256 value
) internal returns (bool) {
return _set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(AddressToUintMap storage map, address key) internal returns (bool) {
return _remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
return _contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(AddressToUintMap storage map) internal view returns (uint256) {
return _length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the set. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) {
(bytes32 key, bytes32 value) = _at(map._inner, index);
return (address(uint160(uint256(key))), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*
* _Available since v3.4._
*/
function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = _tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
return uint256(_get(map._inner, bytes32(uint256(uint160(key)))));
}
}
......@@ -58,9 +58,9 @@ To work around this, xref:api:token/ERC20.adoc#ERC20[`ERC20`] provides a xref:ap
How can this be achieved? It's actually very simple: a token contract can use larger integer values, so that a balance of `50` will represent `5 GLD`, a transfer of `15` will correspond to `1.5 GLD` being sent, and so on.
It is important to understand that `decimals` is _only used for display purposes_. All arithmetic inside the contract is still performed on integers, and it is the different user interfaces (wallets, exchanges, etc.) that must adjust the displayed values according to `decimals`. The total token supply and balance of each account are not specified in `GLD`: you need to divide by `10^decimals` to get the actual `GLD` amount.
It is important to understand that `decimals` is _only used for display purposes_. All arithmetic inside the contract is still performed on integers, and it is the different user interfaces (wallets, exchanges, etc.) that must adjust the displayed values according to `decimals`. The total token supply and balance of each account are not specified in `GLD`: you need to divide by `10 ** decimals` to get the actual `GLD` amount.
You'll probably want to use a `decimals` value of `18`, just like Ether and most ERC20 token contracts in use, unless you have a very special reason not to. When minting tokens or transferring them around, you will be actually sending the number `num GLD * 10^decimals`.
You'll probably want to use a `decimals` value of `18`, just like Ether and most ERC20 token contracts in use, unless you have a very special reason not to. When minting tokens or transferring them around, you will be actually sending the number `num GLD * (10 ** decimals)`.
NOTE: By default, `ERC20` uses a value of `18` for `decimals`. To use a different value, you will need to override the `decimals()` function in your contract.
......@@ -73,7 +73,7 @@ function decimals() public view virtual override returns (uint8) {
So if you want to send `5` tokens using a token contract with 18 decimals, the method to call will actually be:
```solidity
transfer(recipient, 5 * 10^18);
transfer(recipient, 5 * (10 ** 18));
```
[[Presets]]
......
......@@ -16,11 +16,10 @@ Here's what a contract for tokenized items might look like:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract GameItem is ERC721, ERC721URIStorage {
contract GameItem is ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
......@@ -30,12 +29,11 @@ contract GameItem is ERC721, ERC721URIStorage {
public
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(player, newItemId);
_setTokenURI(newItemId, tokenURI);
_tokenIds.increment();
return newItemId;
}
}
......
......@@ -248,7 +248,7 @@ contract MyGovernor is Governor, GovernorCompatibilityBravo, GovernorVotes, Gove
It is good practice to add a timelock to governance decisions. This allows users to exit the system if they disagree with a decision before it is executed. We will use OpenZeppelin’s TimelockController in combination with the GovernorTimelockControl module.
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.)
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. Before version 4.5 there was no way to recover funds in the Governor contract when using a timelock! Before version 4.3, when using the Compound Timelock, ETH in the timelock was not easily accessible.
TimelockController uses an AccessControl setup that we need to understand in order to set up roles.
......@@ -294,7 +294,9 @@ This will create a new proposal, with a proposal id that is obtained by hashing
=== Cast a Vote
Once a proposal is active, stakeholders can cast their vote. This is done through a function in the Governor contract that users can invoke directly from a governance UI such as Tally.
Once a proposal is active, delegates can cast their vote. Note that it is delegates who carry voting power: if a token holder wants to participate, they can set a trusted representative as their delegate, or they can become a delegate themselves by self-delegating their voting power.
Votes are cast by interacting with the Governor contract through the `castVote` family of functions. Voters would generally invoke this from a governance UI such as Tally.
image::tally-vote.png[Voting in Tally]
......
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.4.1",
"version": "4.5.0",
"files": [
"/contracts/**/*.sol",
"/build/contracts/*.json",
......@@ -30,7 +30,8 @@
"version": "scripts/release/version.sh",
"test": "hardhat test",
"test:inheritance": "node scripts/inheritanceOrdering artifacts/build-info/*",
"gas-report": "env ENABLE_GAS_REPORT=true npm run test"
"gas-report": "env ENABLE_GAS_REPORT=true npm run test",
"slither": "npm run clean && slither . --detect reentrancy-eth,reentrancy-no-eth,reentrancy-unlimited-gas"
},
"repository": {
"type": "git",
......@@ -55,19 +56,16 @@
"@nomiclabs/hardhat-web3": "^2.0.0",
"@openzeppelin/docs-utils": "^0.1.0",
"@openzeppelin/test-helpers": "^0.5.13",
"@truffle/abi-utils": "^0.2.3",
"chai": "^4.2.0",
"eslint": "^6.5.1",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "^2.20.0",
"eslint-plugin-mocha-no-only": "^1.1.0",
"eslint-plugin-node": "^10.0.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"eslint": "^7.32.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-mocha": "^10.0.3",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.2.0",
"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",
......@@ -82,9 +80,9 @@
"semver": "^7.3.5",
"solhint": "^3.3.6",
"solidity-ast": "^0.4.25",
"solidity-coverage": "^0.7.11",
"solidity-coverage": "^0.7.18",
"solidity-docgen": "^0.5.3",
"web3": "^1.3.0",
"yargs": "^16.2.0"
"yargs": "^17.0.0"
}
}
#!/usr/bin/env bash
set -euo pipefail -x
git config user.name 'github-actions'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
......@@ -26,7 +26,7 @@ for (const artifact of artifacts) {
graph.nodes().forEach((x, i, nodes) => nodes
.slice(i + 1)
.filter(y => graph.hasEdge(x, y) && graph.hasEdge(y, x))
.map(y => {
.forEach(y => {
console.log(`Conflict between ${names[x]} and ${names[y]} detected in the following dependency chains:`);
linearized
.filter(chain => chain.includes(parseInt(x)) && chain.includes(parseInt(y)))
......
......@@ -18,4 +18,6 @@ solidity-docgen \
--helpers ./docs/helpers.js \
--solc-module ./scripts/prepare-docs-solc.js
rm -f "$OUTDIR"/token/*/presets.md
node scripts/gen-nav.js "$OUTDIR" > "$OUTDIR/../nav.adoc"
const proc = require('child_process');
const read = cmd => proc.execSync(cmd, { encoding: 'utf8' }).trim();
const run = cmd => { proc.execSync(cmd, { stdio: 'inherit' }); };
const tryRead = cmd => { try { return read(cmd); } catch (e) { return undefined; } };
const releaseBranchRegex = /^release-v(?<version>(?<major>\d+)\.(?<minor>\d+)(?:\.(?<patch>\d+))?)$/;
const currentBranch = read('git rev-parse --abbrev-ref HEAD');
const match = currentBranch.match(releaseBranchRegex);
if (!match) {
console.error('Not currently on a release branch');
process.exit(1);
}
if (/-.*$/.test(require('../package.json').version)) {
console.error('Refusing to update docs: prerelease detected');
process.exit(0);
}
const current = match.groups;
const docsBranch = `docs-v${current.major}.x`;
// Fetch remotes and find the docs branch if it exists
run('git fetch --all --no-tags');
const matchingDocsBranches = tryRead(`git rev-parse --glob='*/${docsBranch}'`);
if (!matchingDocsBranches) {
// Create the branch
run(`git checkout --orphan ${docsBranch}`);
} else {
const [publishedRef, ...others] = new Set(matchingDocsBranches.split('\n'));
if (others.length > 0) {
console.error(
`Found conflicting ${docsBranch} branches.\n` +
'Either local branch is outdated or there are multiple matching remote branches.',
);
process.exit(1);
}
const publishedVersion = JSON.parse(read(`git show ${publishedRef}:package.json`)).version;
const publishedMinor = publishedVersion.match(/\d+\.(?<minor>\d+)\.\d+/).groups.minor;
if (current.minor < publishedMinor) {
console.error('Refusing to update docs: newer version is published');
process.exit(0);
}
run('git checkout --quiet --detach');
run(`git reset --soft ${publishedRef}`);
run(`git checkout ${docsBranch}`);
}
run('npm run prepare-docs');
run('git add -f docs'); // --force needed because generated docs files are gitignored
run('git commit -m "Update docs"');
run(`git checkout ${currentBranch}`);
......@@ -41,6 +41,7 @@ contract('Governor', function (accounts) {
shouldSupportInterfaces([
'ERC165',
'Governor',
'GovernorWithParams',
]);
it('deployment check', async function () {
......
......@@ -48,6 +48,7 @@ contract('GovernorTimelockCompound', function (accounts) {
shouldSupportInterfaces([
'ERC165',
'Governor',
'GovernorWithParams',
'GovernorTimelock',
]);
......
const { constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const Enums = require('../../helpers/enums');
......@@ -31,7 +31,7 @@ contract('GovernorTimelockControl', function (accounts) {
this.timelock = await Timelock.new(3600, [], []);
this.mock = await Governor.new(name, this.token.address, 4, 16, this.timelock.address, 0);
this.receiver = await CallReceiver.new();
// normal setup: governor is proposer, everyone is executor, timelock is its own admin
// normal setup: governor and admin are proposers, 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);
......@@ -43,6 +43,7 @@ contract('GovernorTimelockControl', function (accounts) {
shouldSupportInterfaces([
'ERC165',
'Governor',
'GovernorWithParams',
'GovernorTimelock',
]);
......@@ -338,6 +339,32 @@ contract('GovernorTimelockControl', function (accounts) {
);
});
it('protected against other proposers', async function () {
await this.timelock.schedule(
this.mock.address,
web3.utils.toWei('0'),
this.mock.contract.methods.relay(...this.call).encodeABI(),
constants.ZERO_BYTES32,
constants.ZERO_BYTES32,
3600,
{ from: admin },
);
await time.increase(3600);
await expectRevert(
this.timelock.execute(
this.mock.address,
web3.utils.toWei('0'),
this.mock.contract.methods.relay(...this.call).encodeABI(),
constants.ZERO_BYTES32,
constants.ZERO_BYTES32,
{ from: admin },
),
'TimelockController: underlying transaction reverted',
);
});
describe('using workflow', function () {
beforeEach(async function () {
this.settings = {
......@@ -461,4 +488,33 @@ contract('GovernorTimelockControl', function (accounts) {
runGovernorWorkflow();
});
});
describe('clear queue of pending governor calls', function () {
beforeEach(async function () {
this.settings = {
proposal: [
[ this.mock.address ],
[ web3.utils.toWei('0') ],
[ this.mock.contract.methods.nonGovernanceFunction().encodeABI() ],
'<proposal description>',
],
voters: [
{ voter: voter, support: Enums.VoteType.For },
],
steps: {
queue: { delay: 3600 },
},
};
});
afterEach(async function () {
expectEvent(
this.receipts.execute,
'ProposalExecuted',
{ proposalId: this.id },
);
});
runGovernorWorkflow();
});
});
const { BN, constants, expectEvent } = require('@openzeppelin/test-helpers');
const { web3 } = require('@openzeppelin/test-helpers/src/setup');
const Enums = require('../../helpers/enums');
const ethSigUtil = require('eth-sig-util');
const Wallet = require('ethereumjs-wallet').default;
const { EIP712Domain } = require('../../helpers/eip712');
const { fromRpcSig } = require('ethereumjs-util');
const { runGovernorWorkflow } = require('../GovernorWorkflow.behavior');
const { expect } = require('chai');
const Token = artifacts.require('ERC20VotesCompMock');
const Governor = artifacts.require('GovernorWithParamsMock');
const CallReceiver = artifacts.require('CallReceiverMock');
contract('GovernorWithParams', 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);
beforeEach(async function () {
this.owner = owner;
this.token = await Token.new(tokenName, tokenSymbol);
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 });
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);
});
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.filter(({ event }) => event === 'VoteCast').find(Boolean).args;
expectEvent(
vote,
'VoteCast',
this.settings.voters.find(({ address }) => address === voter),
);
});
expectEvent(this.receipts.execute, 'ProposalExecuted', { proposalId: this.id });
await expectEvent.inTransaction(this.receipts.execute.transactionHash, this.receiver, 'MockFunctionCalled');
});
runGovernorWorkflow();
});
describe('Voting with params is properly supported', function () {
const voter2Weight = web3.utils.toWei('1.0');
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: voter2Weight }, // do not actually vote, only getting tokenss
],
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 uintParam = new BN(1);
const strParam = 'These are my params';
const reducedWeight = new BN(voter2Weight).sub(uintParam);
const params = web3.eth.abi.encodeParameters(['uint256', 'string'], [uintParam, strParam]);
const tx = await this.mock.castVoteWithReasonAndParams(this.id, Enums.VoteType.For, '', params, { from: voter2 });
expectEvent(tx, 'CountParams', { uintParam, strParam });
expectEvent(tx, 'VoteCastWithParams', { voter: voter2, weight: reducedWeight, params });
const votes = await this.mock.proposalVotes(this.id);
expect(votes.forVotes).to.be.bignumber.equal(reducedWeight);
});
runGovernorWorkflow();
});
describe('Voting with params by signature is properly supported', function () {
const voterBySig = Wallet.generate(); // generate voter by signature wallet
const sigVoterWeight = web3.utils.toWei('1.0');
beforeEach(async function () {
this.chainId = await web3.eth.getChainId();
this.voter = web3.utils.toChecksumAddress(voterBySig.getAddressString());
// use delegateBySig to enable vote delegation sig voting wallet
const { v, r, s } = fromRpcSig(
ethSigUtil.signTypedMessage(voterBySig.getPrivateKey(), {
data: {
types: {
EIP712Domain,
Delegation: [
{ name: 'delegatee', type: 'address' },
{ name: 'nonce', type: 'uint256' },
{ name: 'expiry', type: 'uint256' },
],
},
domain: { name: tokenName, version: '1', chainId: this.chainId, verifyingContract: this.token.address },
primaryType: 'Delegation',
message: { delegatee: this.voter, nonce: 0, expiry: constants.MAX_UINT256 },
},
}),
);
await this.token.delegateBySig(this.voter, 0, constants.MAX_UINT256, v, r, s);
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: this.voter, weight: sigVoterWeight }, // 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 reason = 'This is my reason';
const uintParam = new BN(1);
const strParam = 'These are my params';
const reducedWeight = new BN(sigVoterWeight).sub(uintParam);
const params = web3.eth.abi.encodeParameters(['uint256', 'string'], [uintParam, strParam]);
// prepare signature for vote by signature
const { v, r, s } = fromRpcSig(
ethSigUtil.signTypedMessage(voterBySig.getPrivateKey(), {
data: {
types: {
EIP712Domain,
ExtendedBallot: [
{ name: 'proposalId', type: 'uint256' },
{ name: 'support', type: 'uint8' },
{ name: 'reason', type: 'string' },
{ name: 'params', type: 'bytes' },
],
},
domain: { name, version, chainId: this.chainId, verifyingContract: this.mock.address },
primaryType: 'ExtendedBallot',
message: { proposalId: this.id, support: Enums.VoteType.For, reason, params },
},
}),
);
const tx = await this.mock.castVoteWithReasonAndParamsBySig(this.id, Enums.VoteType.For, reason, params, v, r, s);
expectEvent(tx, 'CountParams', { uintParam, strParam });
expectEvent(tx, 'VoteCastWithParams', { voter: this.voter, weight: reducedWeight, params });
const votes = await this.mock.proposalVotes(this.id);
expect(votes.forVotes).to.be.bignumber.equal(reducedWeight);
});
runGovernorWorkflow();
});
});
......@@ -108,7 +108,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
it('reverts', async function () {
await expectRevert(
this.token.transferFrom(tokenOwner, to, amount, { from: spender }),
`${errorPrefix}: transfer amount exceeds allowance`,
`${errorPrefix}: insufficient allowance`,
);
});
});
......
......@@ -98,7 +98,7 @@ function shouldBehaveLikeERC20Burnable (owner, initialBalance, [burner]) {
it('reverts', async function () {
await this.token.approve(burner, allowance, { from: owner });
await expectRevert(this.token.burnFrom(owner, allowance.addn(1), { from: burner }),
'ERC20: burn amount exceeds allowance',
'ERC20: insufficient allowance',
);
});
});
......
......@@ -59,7 +59,7 @@ contract('ERC20', function (accounts) {
it('missing approval', async function () {
await expectRevert(
this.token.depositFor(initialHolder, initialSupply, { from: initialHolder }),
'ERC20: transfer amount exceeds allowance',
'ERC20: insufficient allowance',
);
});
......
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