Commit dda49727 by github-actions

Transpile 32e498bb

parent 1e533d92
......@@ -28,6 +28,20 @@
* `GSN`: Deprecate GSNv1 support in favor of upcomming support for GSNv2. ([#2521](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2521))
* `ERC165`: Remove uses of storage in the base ERC165 implementation. ERC165 based contracts now use storage-less virtual functions. Old behaviour remains available in the `ERC165Storage` extension. ([#2505](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2505))
* `Initializable`: Make initializer check stricter during construction. ([#2531](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2531))
* `ERC721`: remove enumerability of tokens from the base implementation. This feature is now provided separately through the `ERC721Enumerable` extension. ([#2511](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2511))
* `AccessControl`: removed enumerability by default for a more lightweight contract. It is now opt-in through `AccessControlEnumerable`. ([#2512](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2512))
* Meta Transactions: add `ERC2771Context` and a `MinimalForwarder` for meta-transactions. ([#2508](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2508))
* Overall reorganisation of the contract folder to improve clarity and discoverability. ([#2503](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2503))
### How to upgrade from 3.x
Since this version has moved a few contracts to different directories, users upgrading from a previous version will need to adjust their import statements. To make this easier, the package includes a script that will migrate import statements automatically. After upgrading to the latest version of the package, run:
```
npx openzeppelin-contracts-migrate-imports
```
Make sure you're using git or another version control system to be able to recover from any potential error in our script.
## 3.4.0 (2021-02-02)
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./AccessControlUpgradeable.sol";
import "../utils/structs/EnumerableSetUpgradeable.sol";
import "../utils/Initializable.sol";
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerableUpgradeable is Initializable, AccessControlUpgradeable {
function __AccessControlEnumerable_init() internal initializer {
__Context_init_unchained();
__AccessControl_init_unchained();
__AccessControlEnumerable_init_unchained();
}
function __AccessControlEnumerable_init_unchained() internal initializer {
}
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
mapping (bytes32 => EnumerableSetUpgradeable.AddressSet) private _roleMembers;
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
return _roleMembers[role].at(index);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view returns (uint256) {
return _roleMembers[role].length();
}
/**
* @dev Overload {grantRole} to track enumerable memberships
*/
function grantRole(bytes32 role, address account) public virtual override {
super.grantRole(role, account);
_roleMembers[role].add(account);
}
/**
* @dev Overload {revokeRole} to track enumerable memberships
*/
function revokeRole(bytes32 role, address account) public virtual override {
super.revokeRole(role, account);
_roleMembers[role].remove(account);
}
/**
* @dev Overload {_setupRole} to track enumerable memberships
*/
function _setupRole(bytes32 role, address account) internal virtual override {
super._setupRole(role, account);
_roleMembers[role].add(account);
}
uint256[49] private __gap;
}
......@@ -2,14 +2,15 @@
pragma solidity ^0.8.0;
import "../utils/EnumerableSetUpgradeable.sol";
import "../utils/AddressUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms.
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
......@@ -50,11 +51,8 @@ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable
function __AccessControl_init_unchained() internal initializer {
}
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
using AddressUpgradeable for address;
struct RoleData {
EnumerableSetUpgradeable.AddressSet members;
mapping (address => bool) members;
bytes32 adminRole;
}
......@@ -93,31 +91,7 @@ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view returns (bool) {
return _roles[role].members.contains(account);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view returns (uint256) {
return _roles[role].members.length();
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
return _roles[role].members.at(index);
return _roles[role].members[account];
}
/**
......@@ -141,7 +115,7 @@ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to grant");
_grantRole(role, account);
}
......@@ -156,7 +130,7 @@ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to revoke");
_revokeRole(role, account);
}
......@@ -207,18 +181,20 @@ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
emit RoleAdminChanged(role, getRoleAdmin(role), adminRole);
_roles[role].adminRole = adminRole;
}
function _grantRole(bytes32 role, address account) private {
if (_roles[role].members.add(account)) {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) private {
if (_roles[role].members.remove(account)) {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
......
......@@ -7,7 +7,6 @@ This directory provides ways to restrict who can access the functions of a contr
- {AccessControl} provides a general role based access control mechanism. Multiple hierarchical roles can be created and assigned each to multiple accounts.
- {Ownable} is a simpler mechanism with a single owner "role" that can be assigned to a single account. This simpler mechanism can be useful for quick tests but projects with production concerns are likely to outgrow it.
- {TimelockController} is used in combination with one of the above two mechanisms. By assigning a role to an instance of the `TimelockController` contract, the access to the functions controlled by that role will be delayed by some amount of time.
== Authorization
......@@ -15,87 +14,4 @@ This directory provides ways to restrict who can access the functions of a contr
{{AccessControl}}
== Timelock
{{TimelockController}}
[[timelock-terminology]]
==== Terminology
* *Operation:* A transaction (or a set of transactions) that is the subject of the timelock. It has to be scheduled by a proposer and executed by an executor. The timelock enforces a minimum delay between the proposition and the execution (see xref:access-control.adoc#operation_lifecycle[operation lifecycle]). If the operation contains multiple transactions (batch mode), they are executed atomically. Operations are identified by the hash of their content.
* *Operation status:*
** *Unset:* An operation that is not part of the timelock mechanism.
** *Pending:* An operation that has been scheduled, before the timer expires.
** *Ready:* An operation that has been scheduled, after the timer expires.
** *Done:* An operation that has been executed.
* *Predecessor*: An (optional) dependency between operations. An operation can depend on another operation (its predecessor), forcing the execution order of these two operations.
* *Role*:
** *Proposer:* An address (smart contract or EOA) that is in charge of scheduling (and cancelling) operations.
** *Executor:* An address (smart contract or EOA) that is in charge of executing operations.
[[timelock-operation]]
==== Operation structure
Operation executed by the xref:api:access.adoc#TimelockController[`TimelockControler`] can contain one or multiple subsequent calls. Depending on whether you need to multiple calls to be executed atomically, you can either use simple or batched operations.
Both operations contain:
* *Target*, the address of the smart contract that the timelock should operate on.
* *Value*, in wei, that should be sent with the transaction. Most of the time this will be 0. Ether can be deposited before-end or passed along when executing the transaction.
* *Data*, containing the encoded function selector and parameters of the call. This can be produced using a number of tools. For example, a maintenance operation granting role `ROLE` to `ACCOUNT` can be encode using web3js as follows:
```javascript
const data = timelock.contract.methods.grantRole(ROLE, ACCOUNT).encodeABI()
```
* *Predecessor*, that specifies a dependency between operations. This dependency is optional. Use `bytes32(0)` if the operation does not have any dependency.
* *Salt*, used to disambiguate two otherwise identical operations. This can be any random value.
In the case of batched operations, `target`, `value` and `data` are specified as arrays, which must be of the same length.
[[timelock-operation-lifecycle]]
==== Operation lifecycle
Timelocked operations are identified by a unique id (their hash) and follow a specific lifecycle:
`Unset` -> `Pending` -> `Pending` + `Ready` -> `Done`
* By calling xref:api:access.adoc#TimelockController-schedule-address-uint256-bytes-bytes32-bytes32-uint256-[`schedule`] (or xref:api:access.adoc#TimelockController-scheduleBatch-address---uint256---bytes---bytes32-bytes32-uint256-[`scheduleBatch`]), a proposer moves the operation from the `Unset` to the `Pending` state. This starts a timer that must be longer than the minimum delay. The timer expires at a timestamp accessible through the xref:api:access.adoc#TimelockController-getTimestamp-bytes32-[`getTimestamp`] method.
* Once the timer expires, the operation automatically gets the `Ready` state. At this point, it can be executed.
* By calling xref:api:access.adoc#TimelockController-TimelockController-execute-address-uint256-bytes-bytes32-bytes32-[`execute`] (or xref:api:access.adoc#TimelockController-executeBatch-address---uint256---bytes---bytes32-bytes32-[`executeBatch`]), an executor triggers the operation's underlying transactions and moves it to the `Done` state. If the operation has a predecessor, it has to be in the `Done` state for this transition to succeed.
* xref:api:access.adoc#TimelockController-TimelockController-cancel-bytes32-[`cancel`] allows proposers to cancel any `Pending` operation. This resets the operation to the `Unset` state. It is thus possible for a proposer to re-schedule an operation that has been cancelled. In this case, the timer restarts when the operation is re-scheduled.
Operations status can be queried using the functions:
* xref:api:access.adoc#TimelockController-isOperationPending-bytes32-[`isOperationPending(bytes32)`]
* xref:api:access.adoc#TimelockController-isOperationReady-bytes32-[`isOperationReady(bytes32)`]
* xref:api:access.adoc#TimelockController-isOperationDone-bytes32-[`isOperationDone(bytes32)`]
[[timelock-roles]]
==== Roles
[[timelock-admin]]
===== Admin
The admins are in charge of managing proposers and executors. For the timelock to be self-governed, this role should only be given to the timelock itself. Upon deployment, both the timelock and the deployer have this role. After further configuration and testing, the deployer can renounce this role such that all further maintenance operations have to go through the timelock process.
This role is identified by the *TIMELOCK_ADMIN_ROLE* value: `0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5`
[[timelock-proposer]]
===== Proposer
The proposers are in charge of scheduling (and cancelling) operations. This is a critical role, that should be given to governing entities. This could be an EOA, a multisig, or a DAO.
WARNING: *Proposer fight:* Having multiple proposers, while providing redundancy in case one becomes unavailable, can be dangerous. As proposer have their say on all operations, they could cancel operations they disagree with, including operations to remove them for the proposers.
This role is identified by the *PROPOSER_ROLE* value: `0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1`
[[timelock-executor]]
===== Executor
The executors are in charge of executing the operations scheduled by the proposers once the timelock expires. Logic dictates that multisig or DAO that are proposers should also be executors in order to guarantee operations that have been scheduled will eventually be executed. However, having additional executor can reduce the cost (the executing transaction does not require validation by the multisig or DAO that proposed it), while ensuring whoever is in charge of execution cannot trigger actions that have not been scheduled by the proposers.
This role is identified by the *EXECUTOR_ROLE* value: `0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63`
WARNING: A live contract without at least one proposer and one executor is locked. Make sure these roles are filled by reliable entities before the deployer renounces its administrative rights in favour of the timelock contract itself. See the {AccessControl} documentation to learn more about role management.
{{AccessControlEnumerable}}
= Cryptography
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/cryptography
This collection of libraries provides simple and safe ways to use different cryptographic primitives.
The following related EIPs are in draft status and can be found in the drafts directory.
- {EIP712}
== Libraries
{{ECDSA}}
{{MerkleProof}}
= Draft EIPs
This directory contains implementations of EIPs that are still in Draft status.
Due to their nature as drafts, the details of these contracts may change and we cannot guarantee their xref:ROOT:releases-stability.adoc[stability]. Minor releases of OpenZeppelin Contracts may contain breaking changes for the contracts in this directory, which will be duly announced in the https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CHANGELOG.md[changelog]. The EIPs included here are used by projects in production and this may make them less likely to change significantly.
== Cryptography
{{EIP712}}
== ERC 20
{{IERC20Permit}}
{{ERC20Permit}}
= Governance
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/access
This directory includes primitives for on-chain governance. We currently only offer the {TimelockController} contract, that can be used as a component in a governance systems to introduce a delay between a proposal and its execution.
== Timelock
{{TimelockController}}
[[timelock-terminology]]
==== Terminology
* *Operation:* A transaction (or a set of transactions) that is the subject of the timelock. It has to be scheduled by a proposer and executed by an executor. The timelock enforces a minimum delay between the proposition and the execution (see xref:access-control.adoc#operation_lifecycle[operation lifecycle]). If the operation contains multiple transactions (batch mode), they are executed atomically. Operations are identified by the hash of their content.
* *Operation status:*
** *Unset:* An operation that is not part of the timelock mechanism.
** *Pending:* An operation that has been scheduled, before the timer expires.
** *Ready:* An operation that has been scheduled, after the timer expires.
** *Done:* An operation that has been executed.
* *Predecessor*: An (optional) dependency between operations. An operation can depend on another operation (its predecessor), forcing the execution order of these two operations.
* *Role*:
** *Proposer:* An address (smart contract or EOA) that is in charge of scheduling (and cancelling) operations.
** *Executor:* An address (smart contract or EOA) that is in charge of executing operations.
[[timelock-operation]]
==== Operation structure
Operation executed by the xref:api:access.adoc#TimelockController[`TimelockControler`] can contain one or multiple subsequent calls. Depending on whether you need to multiple calls to be executed atomically, you can either use simple or batched operations.
Both operations contain:
* *Target*, the address of the smart contract that the timelock should operate on.
* *Value*, in wei, that should be sent with the transaction. Most of the time this will be 0. Ether can be deposited before-end or passed along when executing the transaction.
* *Data*, containing the encoded function selector and parameters of the call. This can be produced using a number of tools. For example, a maintenance operation granting role `ROLE` to `ACCOUNT` can be encode using web3js as follows:
```javascript
const data = timelock.contract.methods.grantRole(ROLE, ACCOUNT).encodeABI()
```
* *Predecessor*, that specifies a dependency between operations. This dependency is optional. Use `bytes32(0)` if the operation does not have any dependency.
* *Salt*, used to disambiguate two otherwise identical operations. This can be any random value.
In the case of batched operations, `target`, `value` and `data` are specified as arrays, which must be of the same length.
[[timelock-operation-lifecycle]]
==== Operation lifecycle
Timelocked operations are identified by a unique id (their hash) and follow a specific lifecycle:
`Unset` -> `Pending` -> `Pending` + `Ready` -> `Done`
* By calling xref:api:access.adoc#TimelockController-schedule-address-uint256-bytes-bytes32-bytes32-uint256-[`schedule`] (or xref:api:access.adoc#TimelockController-scheduleBatch-address---uint256---bytes---bytes32-bytes32-uint256-[`scheduleBatch`]), a proposer moves the operation from the `Unset` to the `Pending` state. This starts a timer that must be longer than the minimum delay. The timer expires at a timestamp accessible through the xref:api:access.adoc#TimelockController-getTimestamp-bytes32-[`getTimestamp`] method.
* Once the timer expires, the operation automatically gets the `Ready` state. At this point, it can be executed.
* By calling xref:api:access.adoc#TimelockController-TimelockController-execute-address-uint256-bytes-bytes32-bytes32-[`execute`] (or xref:api:access.adoc#TimelockController-executeBatch-address---uint256---bytes---bytes32-bytes32-[`executeBatch`]), an executor triggers the operation's underlying transactions and moves it to the `Done` state. If the operation has a predecessor, it has to be in the `Done` state for this transition to succeed.
* xref:api:access.adoc#TimelockController-TimelockController-cancel-bytes32-[`cancel`] allows proposers to cancel any `Pending` operation. This resets the operation to the `Unset` state. It is thus possible for a proposer to re-schedule an operation that has been cancelled. In this case, the timer restarts when the operation is re-scheduled.
Operations status can be queried using the functions:
* xref:api:access.adoc#TimelockController-isOperationPending-bytes32-[`isOperationPending(bytes32)`]
* xref:api:access.adoc#TimelockController-isOperationReady-bytes32-[`isOperationReady(bytes32)`]
* xref:api:access.adoc#TimelockController-isOperationDone-bytes32-[`isOperationDone(bytes32)`]
[[timelock-roles]]
==== Roles
[[timelock-admin]]
===== Admin
The admins are in charge of managing proposers and executors. For the timelock to be self-governed, this role should only be given to the timelock itself. Upon deployment, both the timelock and the deployer have this role. After further configuration and testing, the deployer can renounce this role such that all further maintenance operations have to go through the timelock process.
This role is identified by the *TIMELOCK_ADMIN_ROLE* value: `0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5`
[[timelock-proposer]]
===== Proposer
The proposers are in charge of scheduling (and cancelling) operations. This is a critical role, that should be given to governing entities. This could be an EOA, a multisig, or a DAO.
WARNING: *Proposer fight:* Having multiple proposers, while providing redundancy in case one becomes unavailable, can be dangerous. As proposer have their say on all operations, they could cancel operations they disagree with, including operations to remove them for the proposers.
This role is identified by the *PROPOSER_ROLE* value: `0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1`
[[timelock-executor]]
===== Executor
The executors are in charge of executing the operations scheduled by the proposers once the timelock expires. Logic dictates that multisig or DAO that are proposers should also be executors in order to guarantee operations that have been scheduled will eventually be executed. However, having additional executor can reduce the cost (the executing transaction does not require validation by the multisig or DAO that proposed it), while ensuring whoever is in charge of execution cannot trigger actions that have not been scheduled by the proposers.
This role is identified by the *EXECUTOR_ROLE* value: `0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63`
WARNING: A live contract without at least one proposer and one executor is locked. Make sure these roles are filled by reliable entities before the deployer renounces its administrative rights in favour of the timelock contract itself. See the {AccessControl} documentation to learn more about role management.
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "./AccessControlUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../access/AccessControlUpgradeable.sol";
import "../utils/Initializable.sol";
/**
* @dev Contract module which acts as a timelocked controller. When set as the
......
= Introspection
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/introspection
This set of interfaces and contracts deal with https://en.wikipedia.org/wiki/Type_introspection[type introspection] of contracts, that is, examining which functions can be called on them. This is usually referred to as a contract's _interface_.
Ethereum contracts have no native concept of an interface, so applications must usually simply trust they are not making an incorrect call. For trusted setups this is a non-issue, but often unknown and untrusted third-party addresses need to be interacted with. There may even not be any direct calls to them! (e.g. `ERC20` tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract _declaring_ its interface can be very helpful in preventing errors.
There are two main ways to approach this.
* Locally, where a contract implements `IERC165` and declares an interface, and a second one queries it directly via `ERC165Checker`.
* Globally, where a global and unique registry (`IERC1820Registry`) is used to register implementers of a certain interface (`IERC1820Implementer`). It is then the registry that is queried, which allows for more complex setups, like contracts implementing interfaces for externally-owned accounts.
Note that, in all cases, accounts simply _declare_ their interfaces, but they are not required to actually implement them. This mechanism can therefore be used to both prevent errors and allow for complex interactions (see `ERC777`), but it must not be relied on for security.
== Local
{{IERC165}}
{{ERC165}}
{{ERC165Storage}}
{{ERC165Checker}}
== Global
{{IERC1820Registry}}
{{IERC1820Implementer}}
{{ERC1820Implementer}}
= Math
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/math
These are math-related utilities.
== Libraries
{{SafeMath}}
{{SignedSafeMath}}
{{Math}}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../utils/Initializable.sol";
/*
* @dev Context variant with ERC2771 support.
*/
abstract contract ERC2771ContextUpgradeable is Initializable, ContextUpgradeable {
address _trustedForwarder;
function __ERC2771Context_init(address trustedForwarder) internal initializer {
__Context_init_unchained();
__ERC2771Context_init_unchained(trustedForwarder);
}
function __ERC2771Context_init_unchained(address trustedForwarder) internal initializer {
_trustedForwarder = trustedForwarder;
}
function isTrustedForwarder(address forwarder) public view virtual returns(bool) {
return forwarder == _trustedForwarder;
}
function _msgSender() internal view virtual override returns (address sender) {
if (isTrustedForwarder(msg.sender)) {
// The assembly code is more direct than the Solidity version using `abi.decode`.
assembly { sender := shr(96, calldataload(sub(calldatasize(), 20))) }
} else {
return super._msgSender();
}
}
function _msgData() internal view virtual override returns (bytes calldata) {
if (isTrustedForwarder(msg.sender)) {
return msg.data[:msg.data.length-20];
} else {
return super._msgData();
}
}
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/cryptography/ECDSAUpgradeable.sol";
import "../utils/cryptography/draft-EIP712Upgradeable.sol";
import "../utils/Initializable.sol";
/*
* @dev Simple minimal forwarder to be used together with an ERC2771 compatible contract. See {ERC2771Context}.
*/
contract MinimalForwarderUpgradeable is Initializable, EIP712Upgradeable {
using ECDSAUpgradeable for bytes32;
struct ForwardRequest {
address from;
address to;
uint256 value;
uint256 gas;
uint256 nonce;
bytes data;
}
bytes32 private constant TYPEHASH = keccak256("ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data)");
mapping(address => uint256) private _nonces;
function __MinimalForwarder_init() internal initializer {
__EIP712_init_unchained("MinimalForwarder", "0.0.1");
__MinimalForwarder_init_unchained();
}
function __MinimalForwarder_init_unchained() internal initializer {}
function getNonce(address from) public view returns (uint256) {
return _nonces[from];
}
function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool) {
address signer = _hashTypedDataV4(keccak256(abi.encode(
TYPEHASH,
req.from,
req.to,
req.value,
req.gas,
req.nonce,
keccak256(req.data)
))).recover(signature);
return _nonces[req.from] == req.nonce && signer == req.from;
}
function execute(ForwardRequest calldata req, bytes calldata signature) public payable returns (bool, bytes memory) {
require(verify(req, signature), "MinimalForwarder: signature does not match request");
_nonces[req.from] = req.nonce + 1;
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = req.to.call{gas: req.gas, value: req.value}(abi.encodePacked(req.data, req.from));
// Validate that the relayer has sent enough gas for the call.
// See https://ronan.eth.link/blog/ethereum-gas-dangers/
assert(gasleft() > req.gas / 63);
return (success, returndata);
}
uint256[49] private __gap;
}
= Meta Transactions
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/metatx
== Core
{{ERC2771Context}}
== Utils
{{MinimalForwarder}}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../access/AccessControlEnumerableUpgradeable.sol";
import "../utils/Initializable.sol";
contract AccessControlEnumerableMockUpgradeable is Initializable, AccessControlEnumerableUpgradeable {
function __AccessControlEnumerableMock_init() internal initializer {
__Context_init_unchained();
__AccessControl_init_unchained();
__AccessControlEnumerable_init_unchained();
__AccessControlEnumerableMock_init_unchained();
}
function __AccessControlEnumerableMock_init_unchained() internal initializer {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
function setRoleAdmin(bytes32 roleId, bytes32 adminRoleId) public {
_setRoleAdmin(roleId, adminRoleId);
}
uint256[50] private __gap;
}
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../access/AccessControlUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract AccessControlMockUpgradeable is Initializable, AccessControlUpgradeable {
function __AccessControlMock_init() internal initializer {
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../utils/AddressUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract AddressImplUpgradeable is Initializable {
function __AddressImpl_init() internal initializer {
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../utils/ArraysUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract ArraysImplUpgradeable is Initializable {
using ArraysUpgradeable for uint256[];
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract BadBeaconNoImplUpgradeable is Initializable {
function __BadBeaconNoImpl_init() internal initializer {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract CallReceiverMockUpgradeable is Initializable {
function __CallReceiverMock_init() internal initializer {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
/**
......
......@@ -4,7 +4,7 @@ pragma solidity ^0.8.0;
import "../proxy/ClonesUpgradeable.sol";
import "../utils/AddressUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract ClonesMockUpgradeable is Initializable {
function __ClonesMock_init() internal initializer {
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../payment/escrow/ConditionalEscrowUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/escrow/ConditionalEscrowUpgradeable.sol";
import "../utils/Initializable.sol";
// mock class using ConditionalEscrow
contract ConditionalEscrowMockUpgradeable is Initializable, ConditionalEscrowUpgradeable {
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract ContextMockUpgradeable is Initializable, ContextUpgradeable {
function __ContextMock_init() internal initializer {
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../utils/CountersUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract CountersImplUpgradeable is Initializable {
function __CountersImpl_init() internal initializer {
......
......@@ -3,8 +3,8 @@
pragma solidity ^0.8.0;
import "../utils/Create2Upgradeable.sol";
import "../introspection/ERC1820ImplementerUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/introspection/ERC1820ImplementerUpgradeable.sol";
import "../utils/Initializable.sol";
contract Create2ImplUpgradeable is Initializable {
function __Create2Impl_init() internal initializer {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
abstract contract ImplUpgradeable is Initializable {
function __Impl_init() internal initializer {
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../cryptography/ECDSAUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/cryptography/ECDSAUpgradeable.sol";
import "../utils/Initializable.sol";
contract ECDSAMockUpgradeable is Initializable {
function __ECDSAMock_init() internal initializer {
......
......@@ -2,9 +2,9 @@
pragma solidity ^0.8.0;
import "../drafts/EIP712Upgradeable.sol";
import "../cryptography/ECDSAUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/cryptography/draft-EIP712Upgradeable.sol";
import "../utils/cryptography/ECDSAUpgradeable.sol";
import "../utils/Initializable.sol";
contract EIP712ExternalUpgradeable is Initializable, EIP712Upgradeable {
function __EIP712External_init(string memory name, string memory version) internal initializer {
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../token/ERC1155/ERC1155BurnableUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../token/ERC1155/extensions/ERC1155BurnableUpgradeable.sol";
import "../utils/Initializable.sol";
contract ERC1155BurnableMockUpgradeable is Initializable, ERC1155BurnableUpgradeable {
function __ERC1155BurnableMock_init(string memory uri) internal initializer {
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../token/ERC1155/ERC1155Upgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
/**
* @title ERC1155Mock
......
......@@ -3,8 +3,8 @@
pragma solidity ^0.8.0;
import "./ERC1155MockUpgradeable.sol";
import "../token/ERC1155/ERC1155PausableUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../token/ERC1155/extensions/ERC1155PausableUpgradeable.sol";
import "../utils/Initializable.sol";
contract ERC1155PausableMockUpgradeable is Initializable, ERC1155MockUpgradeable, ERC1155PausableUpgradeable {
function __ERC1155PausableMock_init(string memory uri) internal initializer {
......
......@@ -2,9 +2,9 @@
pragma solidity ^0.8.0;
import "../introspection/ERC165Upgradeable.sol";
import "../token/ERC1155/IERC1155ReceiverUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../utils/Initializable.sol";
contract ERC1155ReceiverMockUpgradeable is Initializable, IERC1155ReceiverUpgradeable, ERC165Upgradeable {
bytes4 private _recRetval;
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../../introspection/IERC165Upgradeable.sol";
import "../../proxy/Initializable.sol";
import "../../utils/introspection/IERC165Upgradeable.sol";
import "../../utils/Initializable.sol";
/**
* https://eips.ethereum.org/EIPS/eip-214#specification
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../proxy/Initializable.sol";
import "../../utils/Initializable.sol";
contract ERC165MissingDataUpgradeable is Initializable {
function __ERC165MissingData_init() internal initializer {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../proxy/Initializable.sol";
import "../../utils/Initializable.sol";
contract ERC165NotSupportedUpgradeable is Initializable { function __ERC165NotSupported_init() internal initializer {
__ERC165NotSupported_init_unchained();
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../introspection/ERC165CheckerUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/introspection/ERC165CheckerUpgradeable.sol";
import "../utils/Initializable.sol";
contract ERC165CheckerMockUpgradeable is Initializable {
function __ERC165CheckerMock_init() internal initializer {
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../introspection/ERC165Upgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../utils/Initializable.sol";
contract ERC165MockUpgradeable is Initializable, ERC165Upgradeable {
function __ERC165Mock_init() internal initializer {
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../introspection/ERC165StorageUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/introspection/ERC165StorageUpgradeable.sol";
import "../utils/Initializable.sol";
contract ERC165StorageMockUpgradeable is Initializable, ERC165StorageUpgradeable {
function __ERC165StorageMock_init() internal initializer {
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../introspection/ERC1820ImplementerUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/introspection/ERC1820ImplementerUpgradeable.sol";
import "../utils/Initializable.sol";
contract ERC1820ImplementerMockUpgradeable is Initializable, ERC1820ImplementerUpgradeable {
function __ERC1820ImplementerMock_init() internal initializer {
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20BurnableUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import "../utils/Initializable.sol";
contract ERC20BurnableMockUpgradeable is Initializable, ERC20BurnableUpgradeable {
function __ERC20BurnableMock_init(
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20CappedUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../token/ERC20/extensions/ERC20CappedUpgradeable.sol";
import "../utils/Initializable.sol";
contract ERC20CappedMockUpgradeable is Initializable, ERC20CappedUpgradeable {
function __ERC20CappedMock_init(string memory name, string memory symbol, uint256 cap) internal initializer {
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20Upgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract ERC20DecimalsMockUpgradeable is Initializable, ERC20Upgradeable {
uint8 private _decimals;
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20Upgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
// mock class using ERC20
contract ERC20MockUpgradeable is Initializable, ERC20Upgradeable {
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20PausableUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../token/ERC20/extensions/ERC20PausableUpgradeable.sol";
import "../utils/Initializable.sol";
// mock class using ERC20Pausable
contract ERC20PausableMockUpgradeable is Initializable, ERC20PausableUpgradeable {
......
......@@ -2,8 +2,9 @@
pragma solidity ^0.8.0;
import "../drafts/ERC20PermitUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol";
import "../utils/Initializable.sol";
contract ERC20PermitMockUpgradeable is Initializable, ERC20PermitUpgradeable {
function __ERC20PermitMock_init(
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20SnapshotUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../token/ERC20/extensions/ERC20SnapshotUpgradeable.sol";
import "../utils/Initializable.sol";
contract ERC20SnapshotMockUpgradeable is Initializable, ERC20SnapshotUpgradeable {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./ContextMockUpgradeable.sol";
import "../metatx/ERC2771ContextUpgradeable.sol";
import "../utils/Initializable.sol";
// By inheriting from ERC2771Context, Context's internal functions are overridden automatically
contract ERC2771ContextMockUpgradeable is Initializable, ContextMockUpgradeable, ERC2771ContextUpgradeable {
function __ERC2771ContextMock_init(address trustedForwarder) internal initializer {
__Context_init_unchained();
__ContextMock_init_unchained();
__ERC2771Context_init_unchained(trustedForwarder);
__ERC2771ContextMock_init_unchained(trustedForwarder);
}
function __ERC2771ContextMock_init_unchained(address trustedForwarder) internal initializer {}
function _msgSender() internal override(ContextUpgradeable, ERC2771ContextUpgradeable) view virtual returns (address) {
return ERC2771ContextUpgradeable._msgSender();
}
function _msgData() internal override(ContextUpgradeable, ERC2771ContextUpgradeable) view virtual returns (bytes calldata) {
return ERC2771ContextUpgradeable._msgData();
}
uint256[50] private __gap;
}
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../token/ERC721/ERC721BurnableUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import "../utils/Initializable.sol";
contract ERC721BurnableMockUpgradeable is Initializable, ERC721BurnableUpgradeable {
function __ERC721BurnableMock_init(string memory name, string memory symbol) internal initializer {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "../utils/Initializable.sol";
/**
* @title ERC721Mock
* This mock just provides a public safeMint, mint, and burn functions for testing purposes
*/
contract ERC721EnumerableMockUpgradeable is Initializable, ERC721EnumerableUpgradeable {
string private _baseTokenURI;
function __ERC721EnumerableMock_init(string memory name, string memory symbol) internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__ERC721_init_unchained(name, symbol);
__ERC721Enumerable_init_unchained();
__ERC721EnumerableMock_init_unchained(name, symbol);
}
function __ERC721EnumerableMock_init_unchained(string memory name, string memory symbol) internal initializer { }
function _baseURI() internal view virtual override returns (string memory) {
return _baseTokenURI;
}
function setBaseURI(string calldata newBaseTokenURI) public {
_baseTokenURI = newBaseTokenURI;
}
function baseURI() public view returns (string memory) {
return _baseURI();
}
function mint(address to, uint256 tokenId) public {
_mint(to, tokenId);
}
function safeMint(address to, uint256 tokenId) public {
_safeMint(to, tokenId);
}
function safeMint(address to, uint256 tokenId, bytes memory _data) public {
_safeMint(to, tokenId, _data);
}
function burn(uint256 tokenId) public {
_burn(tokenId);
}
uint256[49] private __gap;
}
......@@ -3,13 +3,15 @@
pragma solidity ^0.8.0;
import "../token/ERC721/ERC721Upgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
/**
* @title ERC721Mock
* This mock just provides a public safeMint, mint, and burn functions for testing purposes
*/
contract ERC721MockUpgradeable is Initializable, ERC721Upgradeable {
string private _baseTokenURI;
function __ERC721Mock_init(string memory name, string memory symbol) internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
......@@ -19,16 +21,20 @@ contract ERC721MockUpgradeable is Initializable, ERC721Upgradeable {
function __ERC721Mock_init_unchained(string memory name, string memory symbol) internal initializer { }
function exists(uint256 tokenId) public view returns (bool) {
return _exists(tokenId);
function _baseURI() internal view virtual override returns (string memory) {
return _baseTokenURI;
}
function setTokenURI(uint256 tokenId, string memory uri) public {
_setTokenURI(tokenId, uri);
function setBaseURI(string calldata newBaseTokenURI) public {
_baseTokenURI = newBaseTokenURI;
}
function setBaseURI(string memory baseURI) public {
_setBaseURI(baseURI);
function baseURI() public view returns (string memory) {
return _baseURI();
}
function exists(uint256 tokenId) public view returns (bool) {
return _exists(tokenId);
}
function mint(address to, uint256 tokenId) public {
......@@ -46,5 +52,5 @@ contract ERC721MockUpgradeable is Initializable, ERC721Upgradeable {
function burn(uint256 tokenId) public {
_burn(tokenId);
}
uint256[50] private __gap;
uint256[49] private __gap;
}
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../token/ERC721/ERC721PausableUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../token/ERC721/extensions/ERC721PausableUpgradeable.sol";
import "../utils/Initializable.sol";
/**
* @title ERC721PausableMock
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../token/ERC721/IERC721ReceiverUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract ERC721ReceiverMockUpgradeable is Initializable, IERC721ReceiverUpgradeable {
enum Error {
......
......@@ -4,7 +4,7 @@ pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../token/ERC777/ERC777Upgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract ERC777MockUpgradeable is Initializable, ContextUpgradeable, ERC777Upgradeable {
event BeforeTokenTransfer();
......
......@@ -2,13 +2,13 @@
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../token/ERC777/IERC777Upgradeable.sol";
import "../token/ERC777/IERC777SenderUpgradeable.sol";
import "../token/ERC777/IERC777RecipientUpgradeable.sol";
import "../introspection/IERC1820RegistryUpgradeable.sol";
import "../introspection/ERC1820ImplementerUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/introspection/IERC1820RegistryUpgradeable.sol";
import "../utils/introspection/ERC1820ImplementerUpgradeable.sol";
import "../utils/Initializable.sol";
contract ERC777SenderRecipientMockUpgradeable is Initializable, ContextUpgradeable, IERC777SenderUpgradeable, IERC777RecipientUpgradeable, ERC1820ImplementerUpgradeable {
function __ERC777SenderRecipientMock_init() internal initializer {
......@@ -161,4 +161,3 @@ contract ERC777SenderRecipientMockUpgradeable is Initializable, ContextUpgradeab
}
uint256[49] private __gap;
}
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../utils/EnumerableMapUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/structs/EnumerableMapUpgradeable.sol";
import "../utils/Initializable.sol";
contract EnumerableMapMockUpgradeable is Initializable {
function __EnumerableMapMock_init() internal initializer {
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../utils/EnumerableSetUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/structs/EnumerableSetUpgradeable.sol";
import "../utils/Initializable.sol";
// Bytes32Set
contract EnumerableBytes32SetMockUpgradeable is Initializable {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract EtherReceiverMockUpgradeable is Initializable {
function __EtherReceiverMock_init() internal initializer {
......
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
/**
* @title InitializableMock
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../math/MathUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/math/MathUpgradeable.sol";
import "../utils/Initializable.sol";
contract MathMockUpgradeable is Initializable {
function __MathMock_init() internal initializer {
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import { MerkleProofUpgradeable } from "../cryptography/MerkleProofUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/cryptography/MerkleProofUpgradeable.sol";
import "../utils/Initializable.sol";
contract MerkleProofWrapperUpgradeable is Initializable {
function __MerkleProofWrapper_init() internal initializer {
......
......@@ -2,11 +2,11 @@
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
// Sample contracts showing upgradeability with multiple inheritance.
// Child contract inherits from Father and Mother contracts, and Father extends from Gramps.
//
//
// Human
// / \
// | Gramps
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../access/OwnableUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract OwnableMockUpgradeable is Initializable, OwnableUpgradeable { function __OwnableMock_init() internal initializer {
__Context_init_unchained();
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../utils/PausableUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../security/PausableUpgradeable.sol";
import "../utils/Initializable.sol";
contract PausableMockUpgradeable is Initializable, PausableUpgradeable {
bool public drasticMeasureTaken;
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../payment/PullPaymentUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../security/PullPaymentUpgradeable.sol";
import "../utils/Initializable.sol";
// mock class using PullPayment
contract PullPaymentMockUpgradeable is Initializable, PullPaymentUpgradeable {
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract ReentrancyAttackUpgradeable is Initializable, ContextUpgradeable {
function __ReentrancyAttack_init() internal initializer {
__Context_init_unchained();
......
......@@ -2,9 +2,9 @@
pragma solidity ^0.8.0;
import "../utils/ReentrancyGuardUpgradeable.sol";
import "../security/ReentrancyGuardUpgradeable.sol";
import "./ReentrancyAttackUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract ReentrancyMockUpgradeable is Initializable, ReentrancyGuardUpgradeable {
uint256 public counter;
......
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract Implementation1 is Initializable {
uint internal _value;
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../utils/SafeCastUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/math/SafeCastUpgradeable.sol";
import "../utils/Initializable.sol";
contract SafeCastMockUpgradeable is Initializable {
function __SafeCastMock_init() internal initializer {
......
......@@ -4,8 +4,8 @@ pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../token/ERC20/IERC20Upgradeable.sol";
import "../token/ERC20/SafeERC20Upgradeable.sol";
import "../proxy/Initializable.sol";
import "../token/ERC20/utils/SafeERC20Upgradeable.sol";
import "../utils/Initializable.sol";
contract ERC20ReturnFalseMockUpgradeable is Initializable, ContextUpgradeable {
function __ERC20ReturnFalseMock_init() internal initializer {
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../math/SafeMathUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/math/SafeMathUpgradeable.sol";
import "../utils/Initializable.sol";
contract SafeMathMockUpgradeable is Initializable {
function __SafeMathMock_init() internal initializer {
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "../math/SignedSafeMathUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/math/SignedSafeMathUpgradeable.sol";
import "../utils/Initializable.sol";
contract SignedSafeMathMockUpgradeable is Initializable {
function __SignedSafeMathMock_init() internal initializer {
......
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
/**
* @title MigratableMockV1
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../utils/StringsUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
contract StringsMockUpgradeable is Initializable {
function __StringsMock_init() internal initializer {
......
pragma solidity >=0.6 <0.9;
pragma experimental ABIEncoderV2;
import "../presets/ERC721PresetMinterPauserAutoIdUpgradeable.sol";
import "./AccessControlMockUpgradeable.sol";
contract AccessControlMockUpgradeableWithInit is AccessControlMockUpgradeable {
constructor() public payable {
__AccessControlMock_init();
}
}
import "../governance/TimelockControllerUpgradeable.sol";
contract TimelockControllerUpgradeableWithInit is TimelockControllerUpgradeable {
constructor(uint256 minDelay, address[] memory proposers, address[] memory executors) public payable {
__TimelockController_init(minDelay, proposers, executors);
}
}
import "../token/ERC721/presets/ERC721PresetMinterPauserAutoIdUpgradeable.sol";
contract ERC721PresetMinterPauserAutoIdUpgradeableWithInit is ERC721PresetMinterPauserAutoIdUpgradeable {
constructor(string memory name, string memory symbol, string memory baseURI) public payable {
__ERC721PresetMinterPauserAutoId_init(name, symbol, baseURI);
constructor(string memory name, string memory symbol, string memory baseTokenURI) public payable {
__ERC721PresetMinterPauserAutoId_init(name, symbol, baseTokenURI);
}
}
import "../token/ERC721/ERC721Upgradeable.sol";
......@@ -15,13 +29,6 @@ contract ERC721UpgradeableWithInit is ERC721Upgradeable {
__ERC721_init(name_, symbol_);
}
}
import "./PausableMockUpgradeable.sol";
contract PausableMockUpgradeableWithInit is PausableMockUpgradeable {
constructor() public payable {
__PausableMock_init();
}
}
import "../token/ERC20/ERC20Upgradeable.sol";
contract ERC20UpgradeableWithInit is ERC20Upgradeable {
......@@ -29,157 +36,193 @@ contract ERC20UpgradeableWithInit is ERC20Upgradeable {
__ERC20_init(name_, symbol_);
}
}
import "../presets/ERC20PresetMinterPauserUpgradeable.sol";
import "./MathMockUpgradeable.sol";
contract MathMockUpgradeableWithInit is MathMockUpgradeable {
constructor() public payable {
__MathMock_init();
}
}
import "./ArraysImplUpgradeable.sol";
contract ArraysImplUpgradeableWithInit is ArraysImplUpgradeable {
constructor(uint256[] memory array) public payable {
__ArraysImpl_init(array);
}
}
import "../token/ERC20/presets/ERC20PresetMinterPauserUpgradeable.sol";
contract ERC20PresetMinterPauserUpgradeableWithInit is ERC20PresetMinterPauserUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC20PresetMinterPauser_init(name, symbol);
}
}
import "../presets/ERC20PresetFixedSupplyUpgradeable.sol";
import "../utils/PaymentSplitterUpgradeable.sol";
contract ERC20PresetFixedSupplyUpgradeableWithInit is ERC20PresetFixedSupplyUpgradeable {
constructor(
string memory name,
string memory symbol,
uint256 initialSupply,
address owner
) public payable {
__ERC20PresetFixedSupply_init(name, symbol, initialSupply, owner);
contract PaymentSplitterUpgradeableWithInit is PaymentSplitterUpgradeable {
constructor(address[] memory payees, uint256[] memory shares_) public payable {
__PaymentSplitter_init(payees, shares_);
}
}
import "./CountersImplUpgradeable.sol";
import "./SafeMathMockUpgradeable.sol";
contract CountersImplUpgradeableWithInit is CountersImplUpgradeable {
contract SafeMathMockUpgradeableWithInit is SafeMathMockUpgradeable {
constructor() public payable {
__CountersImpl_init();
__SafeMathMock_init();
}
}
import "./ERC20PermitMockUpgradeable.sol";
import "./OwnableMockUpgradeable.sol";
contract ERC20PermitMockUpgradeableWithInit is ERC20PermitMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20PermitMock_init(name, symbol, initialAccount, initialBalance);
contract OwnableMockUpgradeableWithInit is OwnableMockUpgradeable {
constructor() public payable {
__OwnableMock_init();
}
}
import "./EIP712ExternalUpgradeable.sol";
import "../utils/escrow/EscrowUpgradeable.sol";
contract EIP712ExternalUpgradeableWithInit is EIP712ExternalUpgradeable {
constructor(string memory name, string memory version) public payable {
__EIP712External_init(name, version);
contract EscrowUpgradeableWithInit is EscrowUpgradeable {
constructor() public payable {
__Escrow_init();
}
}
import "./ECDSAMockUpgradeable.sol";
import "./PullPaymentMockUpgradeable.sol";
contract ECDSAMockUpgradeableWithInit is ECDSAMockUpgradeable {
contract PullPaymentMockUpgradeableWithInit is PullPaymentMockUpgradeable {
constructor() public payable {
__ECDSAMock_init();
__PullPaymentMock_init();
}
}
import "./MathMockUpgradeable.sol";
import "../utils/escrow/RefundEscrowUpgradeable.sol";
contract MathMockUpgradeableWithInit is MathMockUpgradeable {
contract RefundEscrowUpgradeableWithInit is RefundEscrowUpgradeable {
constructor(address payable beneficiary_) public payable {
__RefundEscrow_init(beneficiary_);
}
}
import "./ConditionalEscrowMockUpgradeable.sol";
contract ConditionalEscrowMockUpgradeableWithInit is ConditionalEscrowMockUpgradeable {
constructor() public payable {
__MathMock_init();
__ConditionalEscrowMock_init();
}
}
import "./ArraysImplUpgradeable.sol";
import "../token/ERC20/utils/TokenTimelockUpgradeable.sol";
contract ArraysImplUpgradeableWithInit is ArraysImplUpgradeable {
constructor(uint256[] memory array) public payable {
__ArraysImpl_init(array);
contract TokenTimelockUpgradeableWithInit is TokenTimelockUpgradeable {
constructor(IERC20Upgradeable token_, address beneficiary_, uint256 releaseTime_) public payable {
__TokenTimelock_init(token_, beneficiary_, releaseTime_);
}
}
import "./ERC20SnapshotMockUpgradeable.sol";
import "./SafeERC20HelperUpgradeable.sol";
contract ERC20SnapshotMockUpgradeableWithInit is ERC20SnapshotMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20SnapshotMock_init(name, symbol, initialAccount, initialBalance);
contract ERC20ReturnFalseMockUpgradeableWithInit is ERC20ReturnFalseMockUpgradeable {
constructor() public payable {
__ERC20ReturnFalseMock_init();
}
}
import "./ERC20MockUpgradeable.sol";
import "./SafeERC20HelperUpgradeable.sol";
contract ERC20MockUpgradeableWithInit is ERC20MockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20Mock_init(name, symbol, initialAccount, initialBalance);
contract ERC20ReturnTrueMockUpgradeableWithInit is ERC20ReturnTrueMockUpgradeable {
constructor() public payable {
__ERC20ReturnTrueMock_init();
}
}
import "./ERC20DecimalsMockUpgradeable.sol";
import "./SafeERC20HelperUpgradeable.sol";
contract ERC20DecimalsMockUpgradeableWithInit is ERC20DecimalsMockUpgradeable {
constructor(string memory name_, string memory symbol_, uint8 decimals_) public payable {
__ERC20DecimalsMock_init(name_, symbol_, decimals_);
contract ERC20NoReturnMockUpgradeableWithInit is ERC20NoReturnMockUpgradeable {
constructor() public payable {
__ERC20NoReturnMock_init();
}
}
import "./ERC20CappedMockUpgradeable.sol";
import "./SafeERC20HelperUpgradeable.sol";
contract ERC20CappedMockUpgradeableWithInit is ERC20CappedMockUpgradeable {
constructor(string memory name, string memory symbol, uint256 cap) public payable {
__ERC20CappedMock_init(name, symbol, cap);
contract SafeERC20WrapperUpgradeableWithInit is SafeERC20WrapperUpgradeable {
constructor(IERC20Upgradeable token) public payable {
__SafeERC20Wrapper_init(token);
}
}
import "../token/ERC20/TokenTimelockUpgradeable.sol";
import "../token/ERC777/ERC777Upgradeable.sol";
contract TokenTimelockUpgradeableWithInit is TokenTimelockUpgradeable {
constructor(IERC20Upgradeable token_, address beneficiary_, uint256 releaseTime_) public payable {
__TokenTimelock_init(token_, beneficiary_, releaseTime_);
contract ERC777UpgradeableWithInit is ERC777Upgradeable {
constructor(
string memory name_,
string memory symbol_,
address[] memory defaultOperators_
) public payable {
__ERC777_init(name_, symbol_, defaultOperators_);
}
}
import "./OwnableMockUpgradeable.sol";
import "../token/ERC777/presets/ERC777PresetFixedSupplyUpgradeable.sol";
contract OwnableMockUpgradeableWithInit is OwnableMockUpgradeable {
contract ERC777PresetFixedSupplyUpgradeableWithInit is ERC777PresetFixedSupplyUpgradeable {
constructor(
string memory name,
string memory symbol,
address[] memory defaultOperators,
uint256 initialSupply,
address owner
) public payable {
__ERC777PresetFixedSupply_init(name, symbol, defaultOperators, initialSupply, owner);
}
}
import "./ERC777SenderRecipientMockUpgradeable.sol";
contract ERC777SenderRecipientMockUpgradeableWithInit is ERC777SenderRecipientMockUpgradeable {
constructor() public payable {
__OwnableMock_init();
__ERC777SenderRecipientMock_init();
}
}
import "../payment/escrow/EscrowUpgradeable.sol";
import "../utils/introspection/ERC1820ImplementerUpgradeable.sol";
contract EscrowUpgradeableWithInit is EscrowUpgradeable {
contract ERC1820ImplementerUpgradeableWithInit is ERC1820ImplementerUpgradeable {
constructor() public payable {
__Escrow_init();
__ERC1820Implementer_init();
}
}
import "./PullPaymentMockUpgradeable.sol";
import "./ERC1820ImplementerMockUpgradeable.sol";
contract PullPaymentMockUpgradeableWithInit is PullPaymentMockUpgradeable {
contract ERC1820ImplementerMockUpgradeableWithInit is ERC1820ImplementerMockUpgradeable {
constructor() public payable {
__PullPaymentMock_init();
__ERC1820ImplementerMock_init();
}
}
import "../payment/escrow/RefundEscrowUpgradeable.sol";
import "./Create2ImplUpgradeable.sol";
contract RefundEscrowUpgradeableWithInit is RefundEscrowUpgradeable {
constructor(address payable beneficiary_) public payable {
__RefundEscrow_init(beneficiary_);
contract Create2ImplUpgradeableWithInit is Create2ImplUpgradeable {
constructor() public payable {
__Create2Impl_init();
}
}
import "./ConditionalEscrowMockUpgradeable.sol";
import "./ERC777MockUpgradeable.sol";
contract ConditionalEscrowMockUpgradeableWithInit is ConditionalEscrowMockUpgradeable {
contract ERC777MockUpgradeableWithInit is ERC777MockUpgradeable {
constructor(
address initialHolder,
uint256 initialBalance,
string memory name,
string memory symbol,
address[] memory defaultOperators
) public payable {
__ERC777Mock_init(initialHolder, initialBalance, name, symbol, defaultOperators);
}
}
import "./PausableMockUpgradeable.sol";
contract PausableMockUpgradeableWithInit is PausableMockUpgradeable {
constructor() public payable {
__ConditionalEscrowMock_init();
__PausableMock_init();
}
}
import "../presets/ERC1155PresetMinterPauserUpgradeable.sol";
import "./ERC20PausableMockUpgradeable.sol";
contract ERC1155PresetMinterPauserUpgradeableWithInit is ERC1155PresetMinterPauserUpgradeable {
constructor(string memory uri) public payable {
__ERC1155PresetMinterPauser_init(uri);
contract ERC20PausableMockUpgradeableWithInit is ERC20PausableMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20PausableMock_init(name, symbol, initialAccount, initialBalance);
}
}
import "../token/ERC1155/ERC1155Upgradeable.sol";
......@@ -189,6 +232,13 @@ contract ERC1155UpgradeableWithInit is ERC1155Upgradeable {
__ERC1155_init(uri_);
}
}
import "../token/ERC1155/presets/ERC1155PresetMinterPauserUpgradeable.sol";
contract ERC1155PresetMinterPauserUpgradeableWithInit is ERC1155PresetMinterPauserUpgradeable {
constructor(string memory uri) public payable {
__ERC1155PresetMinterPauser_init(uri);
}
}
import "./ERC1155MockUpgradeable.sol";
contract ERC1155MockUpgradeableWithInit is ERC1155MockUpgradeable {
......@@ -215,67 +265,18 @@ contract ERC1155ReceiverMockUpgradeableWithInit is ERC1155ReceiverMockUpgradeabl
__ERC1155ReceiverMock_init(recRetval, recReverts, batRetval, batReverts);
}
}
import "./ERC721MockUpgradeable.sol";
import "./ERC721EnumerableMockUpgradeable.sol";
contract ERC721MockUpgradeableWithInit is ERC721MockUpgradeable {
contract ERC721EnumerableMockUpgradeableWithInit is ERC721EnumerableMockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC721Mock_init(name, symbol);
__ERC721EnumerableMock_init(name, symbol);
}
}
import "./StringsMockUpgradeable.sol";
contract StringsMockUpgradeableWithInit is StringsMockUpgradeable {
constructor() public payable {
__StringsMock_init();
}
}
import "./EnumerableSetMockUpgradeable.sol";
contract EnumerableBytes32SetMockUpgradeableWithInit is EnumerableBytes32SetMockUpgradeable {
constructor() public payable {
__EnumerableBytes32SetMock_init();
}
}
import "./EnumerableSetMockUpgradeable.sol";
contract EnumerableAddressSetMockUpgradeableWithInit is EnumerableAddressSetMockUpgradeable {
constructor() public payable {
__EnumerableAddressSetMock_init();
}
}
import "./EnumerableSetMockUpgradeable.sol";
contract EnumerableUintSetMockUpgradeableWithInit is EnumerableUintSetMockUpgradeable {
constructor() public payable {
__EnumerableUintSetMock_init();
}
}
import "./EnumerableMapMockUpgradeable.sol";
contract EnumerableMapMockUpgradeableWithInit is EnumerableMapMockUpgradeable {
constructor() public payable {
__EnumerableMapMock_init();
}
}
import "../token/ERC721/ERC721HolderUpgradeable.sol";
import "./ERC165CheckerMockUpgradeable.sol";
contract ERC721HolderUpgradeableWithInit is ERC721HolderUpgradeable {
contract ERC165CheckerMockUpgradeableWithInit is ERC165CheckerMockUpgradeable {
constructor() public payable {
__ERC721Holder_init();
}
}
import "./ERC721ReceiverMockUpgradeable.sol";
contract ERC721ReceiverMockUpgradeableWithInit is ERC721ReceiverMockUpgradeable {
constructor(bytes4 retval, Error error) public payable {
__ERC721ReceiverMock_init(retval, error);
}
}
import "./ERC721BurnableMockUpgradeable.sol";
contract ERC721BurnableMockUpgradeableWithInit is ERC721BurnableMockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC721BurnableMock_init(name, symbol);
__ERC165CheckerMock_init();
}
}
import "./ERC165/ERC165InterfacesSupportedUpgradeable.sol";
......@@ -292,18 +293,18 @@ contract ERC165InterfacesSupportedUpgradeableWithInit is ERC165InterfacesSupport
__ERC165InterfacesSupported_init(interfaceIds);
}
}
import "./ERC165CheckerMockUpgradeable.sol";
import "../token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
contract ERC165CheckerMockUpgradeableWithInit is ERC165CheckerMockUpgradeable {
contract ERC1155HolderUpgradeableWithInit is ERC1155HolderUpgradeable {
constructor() public payable {
__ERC165CheckerMock_init();
__ERC1155Holder_init();
}
}
import "../token/ERC1155/ERC1155HolderUpgradeable.sol";
import "./ERC165StorageMockUpgradeable.sol";
contract ERC1155HolderUpgradeableWithInit is ERC1155HolderUpgradeable {
contract ERC165StorageMockUpgradeableWithInit is ERC165StorageMockUpgradeable {
constructor() public payable {
__ERC1155Holder_init();
__ERC165StorageMock_init();
}
}
import "./ERC165MockUpgradeable.sol";
......@@ -313,13 +314,6 @@ contract ERC165MockUpgradeableWithInit is ERC165MockUpgradeable {
__ERC165Mock_init();
}
}
import "./ERC165StorageMockUpgradeable.sol";
contract ERC165StorageMockUpgradeableWithInit is ERC165StorageMockUpgradeable {
constructor() public payable {
__ERC165StorageMock_init();
}
}
import "./ERC1155BurnableMockUpgradeable.sol";
contract ERC1155BurnableMockUpgradeableWithInit is ERC1155BurnableMockUpgradeable {
......@@ -327,125 +321,98 @@ contract ERC1155BurnableMockUpgradeableWithInit is ERC1155BurnableMockUpgradeabl
__ERC1155BurnableMock_init(uri);
}
}
import "../payment/PaymentSplitterUpgradeable.sol";
contract PaymentSplitterUpgradeableWithInit is PaymentSplitterUpgradeable {
constructor(address[] memory payees, uint256[] memory shares_) public payable {
__PaymentSplitter_init(payees, shares_);
}
}
import "./SafeMathMockUpgradeable.sol";
import "./ERC721PausableMockUpgradeable.sol";
contract SafeMathMockUpgradeableWithInit is SafeMathMockUpgradeable {
constructor() public payable {
__SafeMathMock_init();
contract ERC721PausableMockUpgradeableWithInit is ERC721PausableMockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC721PausableMock_init(name, symbol);
}
}
import "./SafeERC20HelperUpgradeable.sol";
import "./ERC721MockUpgradeable.sol";
contract ERC20ReturnFalseMockUpgradeableWithInit is ERC20ReturnFalseMockUpgradeable {
constructor() public payable {
__ERC20ReturnFalseMock_init();
contract ERC721MockUpgradeableWithInit is ERC721MockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC721Mock_init(name, symbol);
}
}
import "./SafeERC20HelperUpgradeable.sol";
import "./StringsMockUpgradeable.sol";
contract ERC20ReturnTrueMockUpgradeableWithInit is ERC20ReturnTrueMockUpgradeable {
contract StringsMockUpgradeableWithInit is StringsMockUpgradeable {
constructor() public payable {
__ERC20ReturnTrueMock_init();
__StringsMock_init();
}
}
import "./SafeERC20HelperUpgradeable.sol";
import "../token/ERC721/utils/ERC721HolderUpgradeable.sol";
contract ERC20NoReturnMockUpgradeableWithInit is ERC20NoReturnMockUpgradeable {
contract ERC721HolderUpgradeableWithInit is ERC721HolderUpgradeable {
constructor() public payable {
__ERC20NoReturnMock_init();
}
}
import "./SafeERC20HelperUpgradeable.sol";
contract SafeERC20WrapperUpgradeableWithInit is SafeERC20WrapperUpgradeable {
constructor(IERC20Upgradeable token) public payable {
__SafeERC20Wrapper_init(token);
__ERC721Holder_init();
}
}
import "./ReentrancyAttackUpgradeable.sol";
import "./ERC721ReceiverMockUpgradeable.sol";
contract ReentrancyAttackUpgradeableWithInit is ReentrancyAttackUpgradeable {
constructor() public payable {
__ReentrancyAttack_init();
contract ERC721ReceiverMockUpgradeableWithInit is ERC721ReceiverMockUpgradeable {
constructor(bytes4 retval, Error error) public payable {
__ERC721ReceiverMock_init(retval, error);
}
}
import "./ReentrancyMockUpgradeable.sol";
import "./ERC721BurnableMockUpgradeable.sol";
contract ReentrancyMockUpgradeableWithInit is ReentrancyMockUpgradeable {
constructor() public payable {
__ReentrancyMock_init();
contract ERC721BurnableMockUpgradeableWithInit is ERC721BurnableMockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC721BurnableMock_init(name, symbol);
}
}
import "./ERC777SenderRecipientMockUpgradeable.sol";
import "./ClonesMockUpgradeable.sol";
contract ERC777SenderRecipientMockUpgradeableWithInit is ERC777SenderRecipientMockUpgradeable {
contract ClonesMockUpgradeableWithInit is ClonesMockUpgradeable {
constructor() public payable {
__ERC777SenderRecipientMock_init();
__ClonesMock_init();
}
}
import "../introspection/ERC1820ImplementerUpgradeable.sol";
import "./AddressImplUpgradeable.sol";
contract ERC1820ImplementerUpgradeableWithInit is ERC1820ImplementerUpgradeable {
contract AddressImplUpgradeableWithInit is AddressImplUpgradeable {
constructor() public payable {
__ERC1820Implementer_init();
__AddressImpl_init();
}
}
import "./ERC1820ImplementerMockUpgradeable.sol";
import "./ReentrancyAttackUpgradeable.sol";
contract ERC1820ImplementerMockUpgradeableWithInit is ERC1820ImplementerMockUpgradeable {
contract ReentrancyAttackUpgradeableWithInit is ReentrancyAttackUpgradeable {
constructor() public payable {
__ERC1820ImplementerMock_init();
__ReentrancyAttack_init();
}
}
import "./Create2ImplUpgradeable.sol";
import "./ReentrancyMockUpgradeable.sol";
contract Create2ImplUpgradeableWithInit is Create2ImplUpgradeable {
contract ReentrancyMockUpgradeableWithInit is ReentrancyMockUpgradeable {
constructor() public payable {
__Create2Impl_init();
}
}
import "../token/ERC777/ERC777Upgradeable.sol";
contract ERC777UpgradeableWithInit is ERC777Upgradeable {
constructor(
string memory name_,
string memory symbol_,
address[] memory defaultOperators_
) public payable {
__ERC777_init(name_, symbol_, defaultOperators_);
__ReentrancyMock_init();
}
}
import "../presets/ERC777PresetFixedSupplyUpgradeable.sol";
import "../token/ERC20/presets/ERC20PresetFixedSupplyUpgradeable.sol";
contract ERC777PresetFixedSupplyUpgradeableWithInit is ERC777PresetFixedSupplyUpgradeable {
contract ERC20PresetFixedSupplyUpgradeableWithInit is ERC20PresetFixedSupplyUpgradeable {
constructor(
string memory name,
string memory symbol,
address[] memory defaultOperators,
uint256 initialSupply,
address owner
) public payable {
__ERC777PresetFixedSupply_init(name, symbol, defaultOperators, initialSupply, owner);
__ERC20PresetFixedSupply_init(name, symbol, initialSupply, owner);
}
}
import "./ERC777MockUpgradeable.sol";
import "./ERC20BurnableMockUpgradeable.sol";
contract ERC777MockUpgradeableWithInit is ERC777MockUpgradeable {
contract ERC20BurnableMockUpgradeableWithInit is ERC20BurnableMockUpgradeable {
constructor(
address initialHolder,
uint256 initialBalance,
string memory name,
string memory symbol,
address[] memory defaultOperators
address initialAccount,
uint256 initialBalance
) public payable {
__ERC777Mock_init(initialHolder, initialBalance, name, symbol, defaultOperators);
__ERC20BurnableMock_init(name, symbol, initialAccount, initialBalance);
}
}
import "./ContextMockUpgradeable.sol";
......@@ -462,77 +429,124 @@ contract ContextMockCallerUpgradeableWithInit is ContextMockCallerUpgradeable {
__ContextMockCaller_init();
}
}
import "./ClonesMockUpgradeable.sol";
import "./ERC2771ContextMockUpgradeable.sol";
contract ClonesMockUpgradeableWithInit is ClonesMockUpgradeable {
contract ERC2771ContextMockUpgradeableWithInit is ERC2771ContextMockUpgradeable {
constructor(address trustedForwarder) public payable {
__ERC2771ContextMock_init(trustedForwarder);
}
}
import "./EIP712ExternalUpgradeable.sol";
contract EIP712ExternalUpgradeableWithInit is EIP712ExternalUpgradeable {
constructor(string memory name, string memory version) public payable {
__EIP712External_init(name, version);
}
}
import "../metatx/MinimalForwarderUpgradeable.sol";
contract MinimalForwarderUpgradeableWithInit is MinimalForwarderUpgradeable {
constructor() public payable {
__ClonesMock_init();
__MinimalForwarder_init();
}
}
import "./AddressImplUpgradeable.sol";
import "./ECDSAMockUpgradeable.sol";
contract AddressImplUpgradeableWithInit is AddressImplUpgradeable {
contract ECDSAMockUpgradeableWithInit is ECDSAMockUpgradeable {
constructor() public payable {
__AddressImpl_init();
__ECDSAMock_init();
}
}
import "./ERC20BurnableMockUpgradeable.sol";
import "./ERC20PermitMockUpgradeable.sol";
contract ERC20BurnableMockUpgradeableWithInit is ERC20BurnableMockUpgradeable {
contract ERC20PermitMockUpgradeableWithInit is ERC20PermitMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20BurnableMock_init(name, symbol, initialAccount, initialBalance);
__ERC20PermitMock_init(name, symbol, initialAccount, initialBalance);
}
}
import "./ERC20PausableMockUpgradeable.sol";
import "./ERC20MockUpgradeable.sol";
contract ERC20PausableMockUpgradeableWithInit is ERC20PausableMockUpgradeable {
contract ERC20MockUpgradeableWithInit is ERC20MockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20PausableMock_init(name, symbol, initialAccount, initialBalance);
__ERC20Mock_init(name, symbol, initialAccount, initialBalance);
}
}
import "./ERC721PausableMockUpgradeable.sol";
import "./ERC20DecimalsMockUpgradeable.sol";
contract ERC721PausableMockUpgradeableWithInit is ERC721PausableMockUpgradeable {
constructor(string memory name, string memory symbol) public payable {
__ERC721PausableMock_init(name, symbol);
contract ERC20DecimalsMockUpgradeableWithInit is ERC20DecimalsMockUpgradeable {
constructor(string memory name_, string memory symbol_, uint8 decimals_) public payable {
__ERC20DecimalsMock_init(name_, symbol_, decimals_);
}
}
import "./AccessControlMockUpgradeable.sol";
import "./ERC20CappedMockUpgradeable.sol";
contract AccessControlMockUpgradeableWithInit is AccessControlMockUpgradeable {
contract ERC20CappedMockUpgradeableWithInit is ERC20CappedMockUpgradeable {
constructor(string memory name, string memory symbol, uint256 cap) public payable {
__ERC20CappedMock_init(name, symbol, cap);
}
}
import "./ERC20SnapshotMockUpgradeable.sol";
contract ERC20SnapshotMockUpgradeableWithInit is ERC20SnapshotMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20SnapshotMock_init(name, symbol, initialAccount, initialBalance);
}
}
import "./CountersImplUpgradeable.sol";
contract CountersImplUpgradeableWithInit is CountersImplUpgradeable {
constructor() public payable {
__AccessControlMock_init();
__CountersImpl_init();
}
}
import "../access/TimelockControllerUpgradeable.sol";
import "./AccessControlEnumerableMockUpgradeable.sol";
contract TimelockControllerUpgradeableWithInit is TimelockControllerUpgradeable {
constructor(uint256 minDelay, address[] memory proposers, address[] memory executors) public payable {
__TimelockController_init(minDelay, proposers, executors);
contract AccessControlEnumerableMockUpgradeableWithInit is AccessControlEnumerableMockUpgradeable {
constructor() public payable {
__AccessControlEnumerableMock_init();
}
}
import "./MerkleProofWrapperUpgradeable.sol";
import "./EnumerableSetMockUpgradeable.sol";
contract MerkleProofWrapperUpgradeableWithInit is MerkleProofWrapperUpgradeable {
contract EnumerableBytes32SetMockUpgradeableWithInit is EnumerableBytes32SetMockUpgradeable {
constructor() public payable {
__MerkleProofWrapper_init();
__EnumerableBytes32SetMock_init();
}
}
import "./SignedSafeMathMockUpgradeable.sol";
import "./EnumerableSetMockUpgradeable.sol";
contract SignedSafeMathMockUpgradeableWithInit is SignedSafeMathMockUpgradeable {
contract EnumerableAddressSetMockUpgradeableWithInit is EnumerableAddressSetMockUpgradeable {
constructor() public payable {
__SignedSafeMathMock_init();
__EnumerableAddressSetMock_init();
}
}
import "./EnumerableSetMockUpgradeable.sol";
contract EnumerableUintSetMockUpgradeableWithInit is EnumerableUintSetMockUpgradeable {
constructor() public payable {
__EnumerableUintSetMock_init();
}
}
import "./EnumerableMapMockUpgradeable.sol";
contract EnumerableMapMockUpgradeableWithInit is EnumerableMapMockUpgradeable {
constructor() public payable {
__EnumerableMapMock_init();
}
}
import "./BadBeaconUpgradeable.sol";
......@@ -598,6 +612,13 @@ contract EtherReceiverMockUpgradeableWithInit is EtherReceiverMockUpgradeable {
__EtherReceiverMock_init();
}
}
import "./MerkleProofWrapperUpgradeable.sol";
contract MerkleProofWrapperUpgradeableWithInit is MerkleProofWrapperUpgradeable {
constructor() public payable {
__MerkleProofWrapper_init();
}
}
import "./SafeCastMockUpgradeable.sol";
contract SafeCastMockUpgradeableWithInit is SafeCastMockUpgradeable {
......@@ -605,3 +626,10 @@ contract SafeCastMockUpgradeableWithInit is SafeCastMockUpgradeable {
__SafeCastMock_init();
}
}
import "./SignedSafeMathMockUpgradeable.sol";
contract SignedSafeMathMockUpgradeableWithInit is SignedSafeMathMockUpgradeable {
constructor() public payable {
__SignedSafeMathMock_init();
}
}
= Payment
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/payment
Utilities related to sending and receiving payments. Examples are {PullPayment}, which implements the best security practices when sending funds to third parties, and {PaymentSplitter} to receive incoming payments among a number of beneficiaries.
TIP: When transferring funds to and from untrusted third parties, there is always a security risk of reentrancy. If you would like to learn more about this and ways to protect against it, check out our blog post https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
== Utilities
{{PaymentSplitter}}
{{PullPayment}}
== Escrow
{{Escrow}}
{{ConditionalEscrow}}
{{RefundEscrow}}
= Presets
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/presets
These contracts integrate different Ethereum standards (ERCs) with custom extensions and modules, showcasing common configurations that are ready to deploy **without having to write any Solidity code**.
They can be used as-is for quick prototyping and testing, but are **also suitable for production environments**.
TIP: Intermediate and advanced users can use these as starting points when writing their own contracts, extending them with custom functionality as they see fit.
== Tokens
{{ERC20PresetMinterPauser}}
{{ERC721PresetMinterPauserAutoId}}
{{ERC1155PresetMinterPauser}}
{{ERC20PresetFixedSupply}}
{{ERC777PresetFixedSupply}}
......@@ -21,8 +21,12 @@ CAUTION: Using upgradeable proxies correctly and securely is a difficult task th
{{UpgradeableProxy}}
== Transparent Proxy
{{TransparentUpgradeableProxy}}
{{ProxyAdmin}}
== Beacon
{{BeaconProxy}}
......@@ -34,9 +38,3 @@ CAUTION: Using upgradeable proxies correctly and securely is a difficult task th
== Minimal Clones
{{Clones}}
== Utilities
{{Initializable}}
{{ProxyAdmin}}
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "./ContextUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "./escrow/EscrowUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../utils/escrow/EscrowUpgradeable.sol";
import "../utils/Initializable.sol";
/**
* @dev Simple implementation of a
......
= Security
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/security
These contracts aim to cover common security practices.
* {PullPayment}: A pattern that can be used to avoid reentrancy attacks.
* {ReentrancyGuard}: A modifier that can prevent reentrancy during certain functions.
* {Pausable}: A common emergency response mechanism that can pause functionality while a remediation is pending.
TIP: For an overview on reentrancy and the possible mechanisms to prevent it, read our article https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
== Contracts
{{PullPayment}}
{{ReentrancyGuard}}
{{Pausable}}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
import "../utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
......
......@@ -3,12 +3,12 @@
pragma solidity ^0.8.0;
import "./IERC1155Upgradeable.sol";
import "./IERC1155MetadataURIUpgradeable.sol";
import "./IERC1155ReceiverUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../introspection/ERC165Upgradeable.sol";
import "./extensions/IERC1155MetadataURIUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../proxy/Initializable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../utils/introspection/ERC165Upgradeable.sol";
import "../../utils/Initializable.sol";
/**
*
......
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "../../introspection/IERC165Upgradeable.sol";
import "../../utils/introspection/IERC165Upgradeable.sol";
/**
* _Available since v3.1._
......
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "../../introspection/IERC165Upgradeable.sol";
import "../../utils/introspection/IERC165Upgradeable.sol";
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
......
......@@ -32,6 +32,12 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
{{ERC1155Burnable}}
== Convenience
== Presets
These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code.
{{ERC1155PresetMinterPauser}}
== Utilities
{{ERC1155Holder}}
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "./ERC1155Upgradeable.sol";
import "../../proxy/Initializable.sol";
import "../ERC1155Upgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev Extension of {ERC1155} that allows token holders to destroy both their
......
......@@ -2,9 +2,9 @@
pragma solidity ^0.8.0;
import "./ERC1155Upgradeable.sol";
import "../../utils/PausableUpgradeable.sol";
import "../../proxy/Initializable.sol";
import "../ERC1155Upgradeable.sol";
import "../../../security/PausableUpgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev ERC1155 token with pausable token transfers, minting and burning.
......
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "./IERC1155Upgradeable.sol";
import "../IERC1155Upgradeable.sol";
/**
* @dev Interface of the optional ERC1155MetadataExtension interface, as defined
......
......@@ -2,12 +2,12 @@
pragma solidity ^0.8.0;
import "../access/AccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../token/ERC1155/ERC1155Upgradeable.sol";
import "../token/ERC1155/ERC1155BurnableUpgradeable.sol";
import "../token/ERC1155/ERC1155PausableUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../ERC1155Upgradeable.sol";
import "../extensions/ERC1155BurnableUpgradeable.sol";
import "../extensions/ERC1155PausableUpgradeable.sol";
import "../../../access/AccessControlEnumerableUpgradeable.sol";
import "../../../utils/ContextUpgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev {ERC1155} token, including:
......@@ -23,10 +23,7 @@ import "../proxy/Initializable.sol";
* roles, as well as the default admin role, which will let it grant both minter
* and pauser roles to other accounts.
*/
contract ERC1155PresetMinterPauserUpgradeable is Initializable, ContextUpgradeable, AccessControlUpgradeable, ERC1155BurnableUpgradeable, ERC1155PausableUpgradeable {
function initialize(string memory uri) public virtual initializer {
__ERC1155PresetMinterPauser_init(uri);
}
contract ERC1155PresetMinterPauserUpgradeable is Initializable, ContextUpgradeable, AccessControlEnumerableUpgradeable, ERC1155BurnableUpgradeable, ERC1155PausableUpgradeable {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
......@@ -37,6 +34,7 @@ contract ERC1155PresetMinterPauserUpgradeable is Initializable, ContextUpgradeab
function __ERC1155PresetMinterPauser_init(string memory uri) internal initializer {
__Context_init_unchained();
__AccessControl_init_unchained();
__AccessControlEnumerable_init_unchained();
__ERC165_init_unchained();
__ERC1155_init_unchained(uri);
__ERC1155Burnable_init_unchained();
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "./ERC1155ReceiverUpgradeable.sol";
import "../../proxy/Initializable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev _Available since v3.1._
......
......@@ -2,9 +2,9 @@
pragma solidity ^0.8.0;
import "./IERC1155ReceiverUpgradeable.sol";
import "../../introspection/ERC165Upgradeable.sol";
import "../../proxy/Initializable.sol";
import "../IERC1155ReceiverUpgradeable.sol";
import "../../../utils/introspection/ERC165Upgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev _Available since v3.1._
......
......@@ -2,9 +2,9 @@
pragma solidity ^0.8.0;
import "../../utils/ContextUpgradeable.sol";
import "./IERC20Upgradeable.sol";
import "../../proxy/Initializable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../utils/Initializable.sol";
/**
* @dev Implementation of the {IERC20} interface.
......
......@@ -48,6 +48,22 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
{{ERC20Capped}}
== Draft EIPs
The following EIPs are still in Draft status. Due to their nature as drafts, the details of these contracts may change and we cannot guarantee their xref:ROOT:releases-stability.adoc[stability]. Minor releases of OpenZeppelin Contracts may contain breaking changes for the contracts in this directory, which will be duly announced in the https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CHANGELOG.md[changelog]. The EIPs included here are used by projects in production and this may make them less likely to change significantly.
{{IERC20Permit}}
{{ERC20Permit}}
== Presets
These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code.
{{ERC20PresetMinterPauser}}
{{ERC20PresetFixedSupply}}
== Utilities
{{SafeERC20}}
......
......@@ -2,9 +2,9 @@
pragma solidity ^0.8.0;
import "../../utils/ContextUpgradeable.sol";
import "./ERC20Upgradeable.sol";
import "../../proxy/Initializable.sol";
import "../ERC20Upgradeable.sol";
import "../../../utils/ContextUpgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev Extension of {ERC20} that allows token holders to destroy both their own
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "./ERC20Upgradeable.sol";
import "../../proxy/Initializable.sol";
import "../ERC20Upgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev Extension of {ERC20} that adds a cap to the supply of tokens.
......
......@@ -2,9 +2,9 @@
pragma solidity ^0.8.0;
import "./ERC20Upgradeable.sol";
import "../../utils/PausableUpgradeable.sol";
import "../../proxy/Initializable.sol";
import "../ERC20Upgradeable.sol";
import "../../../security/PausableUpgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev ERC20 token with pausable token transfers, minting and burning.
......
......@@ -2,10 +2,10 @@
pragma solidity ^0.8.0;
import "../../utils/ArraysUpgradeable.sol";
import "../../utils/CountersUpgradeable.sol";
import "./ERC20Upgradeable.sol";
import "../../proxy/Initializable.sol";
import "../ERC20Upgradeable.sol";
import "../../../utils/ArraysUpgradeable.sol";
import "../../../utils/CountersUpgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev This contract extends an ERC20 token with a snapshot mechanism. When a snapshot is created, the balances and
......
......@@ -2,12 +2,12 @@
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20Upgradeable.sol";
import "./IERC20PermitUpgradeable.sol";
import "../cryptography/ECDSAUpgradeable.sol";
import "../utils/CountersUpgradeable.sol";
import "./EIP712Upgradeable.sol";
import "../proxy/Initializable.sol";
import "./draft-IERC20PermitUpgradeable.sol";
import "../ERC20Upgradeable.sol";
import "../../../utils/cryptography/draft-EIP712Upgradeable.sol";
import "../../../utils/cryptography/ECDSAUpgradeable.sol";
import "../../../utils/CountersUpgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20BurnableUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../extensions/ERC20BurnableUpgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev {ERC20} token, including:
......@@ -17,14 +17,6 @@ import "../proxy/Initializable.sol";
* _Available since v3.4._
*/
contract ERC20PresetFixedSupplyUpgradeable is Initializable, ERC20BurnableUpgradeable {
function initialize(
string memory name,
string memory symbol,
uint256 initialSupply,
address owner
) public virtual initializer {
__ERC20PresetFixedSupply_init(name, symbol, initialSupply, owner);
}
/**
* @dev Mints `initialSupply` amount of token and transfers them to `owner`.
*
......
......@@ -2,12 +2,12 @@
pragma solidity ^0.8.0;
import "../access/AccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../token/ERC20/ERC20Upgradeable.sol";
import "../token/ERC20/ERC20BurnableUpgradeable.sol";
import "../token/ERC20/ERC20PausableUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../ERC20Upgradeable.sol";
import "../extensions/ERC20BurnableUpgradeable.sol";
import "../extensions/ERC20PausableUpgradeable.sol";
import "../../../access/AccessControlEnumerableUpgradeable.sol";
import "../../../utils/ContextUpgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev {ERC20} token, including:
......@@ -23,10 +23,7 @@ import "../proxy/Initializable.sol";
* roles, as well as the default admin role, which will let it grant both minter
* and pauser roles to other accounts.
*/
contract ERC20PresetMinterPauserUpgradeable is Initializable, ContextUpgradeable, AccessControlUpgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable {
function initialize(string memory name, string memory symbol) public virtual initializer {
__ERC20PresetMinterPauser_init(name, symbol);
}
contract ERC20PresetMinterPauserUpgradeable is Initializable, ContextUpgradeable, AccessControlEnumerableUpgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
......@@ -39,6 +36,7 @@ contract ERC20PresetMinterPauserUpgradeable is Initializable, ContextUpgradeable
function __ERC20PresetMinterPauser_init(string memory name, string memory symbol) internal initializer {
__Context_init_unchained();
__AccessControl_init_unchained();
__AccessControlEnumerable_init_unchained();
__ERC20_init_unchained(name, symbol);
__ERC20Burnable_init_unchained();
__Pausable_init_unchained();
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import "./IERC20Upgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../IERC20Upgradeable.sol";
import "../../../utils/AddressUpgradeable.sol";
/**
* @title SafeERC20
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "./SafeERC20Upgradeable.sol";
import "../../proxy/Initializable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev A token holder contract that will allow a beneficiary to extract the
......
......@@ -2,51 +2,42 @@
pragma solidity ^0.8.0;
import "../../utils/ContextUpgradeable.sol";
import "./IERC721Upgradeable.sol";
import "./IERC721MetadataUpgradeable.sol";
import "./IERC721EnumerableUpgradeable.sol";
import "./IERC721ReceiverUpgradeable.sol";
import "../../introspection/ERC165Upgradeable.sol";
import "./extensions/IERC721MetadataUpgradeable.sol";
import "./extensions/IERC721EnumerableUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/EnumerableSetUpgradeable.sol";
import "../../utils/EnumerableMapUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../utils/StringsUpgradeable.sol";
import "../../proxy/Initializable.sol";
import "../../utils/introspection/ERC165Upgradeable.sol";
import "../../utils/Initializable.sol";
/**
* @title ERC721 Non-Fungible Token Standard basic implementation
* @dev see https://eips.ethereum.org/EIPS/eip-721
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable, IERC721EnumerableUpgradeable {
contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
using AddressUpgradeable for address;
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;
using EnumerableMapUpgradeable for EnumerableMapUpgradeable.UintToAddressMap;
using StringsUpgradeable for uint256;
// Mapping from holder address to their (enumerable) set of owned tokens
mapping (address => EnumerableSetUpgradeable.UintSet) private _holderTokens;
// Enumerable mapping from token ids to their owners
EnumerableMapUpgradeable.UintToAddressMap private _tokenOwners;
// Mapping from token ID to approved address
mapping (uint256 => address) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping (address => mapping (address => bool)) private _operatorApprovals;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Optional mapping for token URIs
mapping (uint256 => string) private _tokenURIs;
// Mapping from token ID to owner address
mapping (uint256 => address) private _owners;
// Mapping owner address to token count
mapping (address => uint256) private _balances;
// Mapping from token ID to approved address
mapping (uint256 => address) private _tokenApprovals;
// Base URI
string private _baseURI;
// Mapping from owner to operator approvals
mapping (address => mapping (address => bool)) private _operatorApprovals;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
......@@ -68,7 +59,6 @@ contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeab
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
return interfaceId == type(IERC721Upgradeable).interfaceId
|| interfaceId == type(IERC721MetadataUpgradeable).interfaceId
|| interfaceId == type(IERC721EnumerableUpgradeable).interfaceId
|| super.supportsInterface(interfaceId);
}
......@@ -77,14 +67,16 @@ contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeab
*/
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: balance query for the zero address");
return _holderTokens[owner].length();
return _balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");
address owner = _owners[tokenId];
require(owner != address(0), "ERC721: owner query for nonexistent token");
return owner;
}
/**
......@@ -107,51 +99,18 @@ contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeab
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return string(abi.encodePacked(base, tokenId.toString()));
}
/**
* @dev Returns the base URI set via {_setBaseURI}. This will be
* automatically added as a prefix in {tokenURI} to each token's URI, or
* to the token ID if no specific URI is set for that token ID.
*/
function baseURI() public view virtual returns (string memory) {
return _baseURI;
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0
? string(abi.encodePacked(baseURI, tokenId.toString()))
: '';
}
/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
* @dev Base URI for computing {tokenURI}. Empty by default, can be overriden
* in child contracts.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
return _holderTokens[owner].at(index);
}
/**
* @dev See {IERC721Enumerable-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
// _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds
return _tokenOwners.length();
}
/**
* @dev See {IERC721Enumerable-tokenByIndex}.
*/
function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
(uint256 tokenId, ) = _tokenOwners.at(index);
return tokenId;
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
......@@ -251,7 +210,7 @@ contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeab
* and stop existing when they are burned (`_burn`).
*/
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _tokenOwners.contains(tokenId);
return _owners[tokenId] != address(0);
}
/**
......@@ -308,9 +267,8 @@ contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeab
_beforeTokenTransfer(address(0), to, tokenId);
_holderTokens[to].add(tokenId);
_tokenOwners.set(tokenId, to);
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
}
......@@ -326,21 +284,15 @@ contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeab
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal virtual {
address owner = ERC721Upgradeable.ownerOf(tokenId); // internal owner
address owner = ERC721Upgradeable.ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId);
// Clear approvals
_approve(address(0), tokenId);
// Clear metadata (if any)
if (bytes(_tokenURIs[tokenId]).length != 0) {
delete _tokenURIs[tokenId];
}
_holderTokens[owner].remove(tokenId);
_tokenOwners.remove(tokenId);
_balances[owner] -= 1;
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
}
......@@ -357,7 +309,7 @@ contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeab
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) internal virtual {
require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); // internal owner
require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId);
......@@ -365,33 +317,21 @@ contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeab
// Clear approvals from the previous owner
_approve(address(0), tokenId);
_holderTokens[from].remove(tokenId);
_holderTokens[to].add(tokenId);
_tokenOwners.set(tokenId, to);
_balances[from] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
/**
* @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
* @dev Approve `to` to operate on `tokenId`
*
* Requirements:
*
* - `tokenId` must exist.
* Emits a {Approval} event.
*/
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
/**
* @dev Internal function to set the base URI for all token IDs. It is
* automatically added as a prefix to the value returned in {tokenURI},
* or to the token ID if {tokenURI} is empty.
*/
function _setBaseURI(string memory baseURI_) internal virtual {
_baseURI = baseURI_;
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId);
}
/**
......@@ -425,11 +365,6 @@ contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeab
}
}
function _approve(address to, uint256 tokenId) private {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId); // internal owner
}
/**
* @dev Hook that is called before any token transfer. This includes minting
* and burning.
......@@ -446,5 +381,5 @@ contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeab
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
uint256[40] private __gap;
uint256[44] private __gap;
}
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "../../introspection/IERC165Upgradeable.sol";
import "../../utils/introspection/IERC165Upgradeable.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
......
......@@ -7,7 +7,7 @@ This set of interfaces, contracts, and utilities are all related to the https://
TIP: For a walk through on how to create an ERC721 token read our xref:ROOT:erc721.adoc[ERC721 guide].
The EIP consists of three interfaces, found here as {IERC721}, {IERC721Metadata}, and {IERC721Enumerable}. Only the first one is required in a contract to be ERC721 compliant. However, all three are implemented in {ERC721}.
The EIP consists of three interfaces, found here as {IERC721}, {IERC721Metadata}, and {IERC721Enumerable}. Only the first one is required in a contract to be ERC721 compliant. The core interface and the metadata extension are both implemented in {ERC721}. The enumerable extension is provided separately in {ERC721Enumerable}.
Additionally, {IERC721Receiver} can be used to prevent tokens from becoming forever locked in contracts. Imagine sending an in-game item to an exchange address that can't send it back!. When using <<IERC721-safeTransferFrom,`safeTransferFrom`>>, the token contract checks to see that the receiver is an {IERC721Receiver}, which implies that it knows how to handle {ERC721} tokens. If you're writing a contract that needs to receive {ERC721} tokens, you'll want to include this interface.
......@@ -29,6 +29,8 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
{{ERC721}}
{{ERC721Enumerable}}
{{IERC721Receiver}}
== Extensions
......@@ -37,6 +39,12 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
{{ERC721Burnable}}
== Convenience
== Presets
These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code.
{{ERC721PresetMinterPauserAutoId}}
== Utilities
{{ERC721Holder}}
......@@ -2,9 +2,9 @@
pragma solidity ^0.8.0;
import "../../utils/ContextUpgradeable.sol";
import "./ERC721Upgradeable.sol";
import "../../proxy/Initializable.sol";
import "../ERC721Upgradeable.sol";
import "../../../utils/ContextUpgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @title ERC721 Burnable Token
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../ERC721Upgradeable.sol";
import "./IERC721EnumerableUpgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev This implements an optional extension of {ERC721} defined in the EIP that adds
* enumerability of all the token ids in the contract as well as all token ids owned by each
* account.
*/
abstract contract ERC721EnumerableUpgradeable is Initializable, ERC721Upgradeable, IERC721EnumerableUpgradeable {
function __ERC721Enumerable_init() internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__ERC721Enumerable_init_unchained();
}
function __ERC721Enumerable_init_unchained() internal initializer {
}
// Mapping from owner to list of owned token IDs
mapping(address => mapping(uint256 => uint256)) private _ownedTokens;
// Mapping from token ID to index of the owner tokens list
mapping(uint256 => uint256) private _ownedTokensIndex;
// Array with all token ids, used for enumeration
uint256[] private _allTokens;
// Mapping from token id to position in the allTokens array
mapping(uint256 => uint256) private _allTokensIndex;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC721Upgradeable) returns (bool) {
return interfaceId == type(IERC721EnumerableUpgradeable).interfaceId
|| super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
require(index < ERC721Upgradeable.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
return _ownedTokens[owner][index];
}
/**
* @dev See {IERC721Enumerable-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _allTokens.length;
}
/**
* @dev See {IERC721Enumerable-tokenByIndex}.
*/
function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
require(index < ERC721EnumerableUpgradeable.totalSupply(), "ERC721Enumerable: global index out of bounds");
return _allTokens[index];
}
/**
* @dev Hook that is called before any token transfer. This includes minting
* and burning.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, ``from``'s `tokenId` will be burned.
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override {
super._beforeTokenTransfer(from, to, tokenId);
if (from == address(0)) {
_addTokenToAllTokensEnumeration(tokenId);
} else if (from != to) {
_removeTokenFromOwnerEnumeration(from, tokenId);
}
if (to == address(0)) {
_removeTokenFromAllTokensEnumeration(tokenId);
} else if (to != from) {
_addTokenToOwnerEnumeration(to, tokenId);
}
}
/**
* @dev Private function to add a token to this extension's ownership-tracking data structures.
* @param to address representing the new owner of the given token ID
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
*/
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
uint256 length = ERC721Upgradeable.balanceOf(to);
_ownedTokens[to][length] = tokenId;
_ownedTokensIndex[tokenId] = length;
}
/**
* @dev Private function to add a token to this extension's token tracking data structures.
* @param tokenId uint256 ID of the token to be added to the tokens list
*/
function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
/**
* @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
* while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
* gas optimizations e.g. when performing a transfer operation (avoiding double writes).
* This has O(1) time complexity, but alters the order of the _ownedTokens array.
* @param from address representing the previous owner of the given token ID
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
*/
function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
// To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = ERC721Upgradeable.balanceOf(from) - 1;
uint256 tokenIndex = _ownedTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary
if (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
}
// This also deletes the contents at the last position of the array
delete _ownedTokensIndex[tokenId];
delete _ownedTokens[from][lastTokenIndex];
}
/**
* @dev Private function to remove a token from this extension's token tracking data structures.
* This has O(1) time complexity, but alters the order of the _allTokens array.
* @param tokenId uint256 ID of the token to be removed from the tokens list
*/
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
// To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = _allTokens.length - 1;
uint256 tokenIndex = _allTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
// rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
// an 'if' statement (like in _removeTokenFromOwnerEnumeration)
uint256 lastTokenId = _allTokens[lastTokenIndex];
_allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
// This also deletes the contents at the last position of the array
delete _allTokensIndex[tokenId];
_allTokens.pop();
}
uint256[46] private __gap;
}
......@@ -2,9 +2,9 @@
pragma solidity ^0.8.0;
import "./ERC721Upgradeable.sol";
import "../../utils/PausableUpgradeable.sol";
import "../../proxy/Initializable.sol";
import "../ERC721Upgradeable.sol";
import "../../../security/PausableUpgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev ERC721 token with pausable token transfers, minting and burning.
......
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "./IERC721Upgradeable.sol";
import "../IERC721Upgradeable.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
......
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "./IERC721Upgradeable.sol";
import "../IERC721Upgradeable.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
......
......@@ -2,13 +2,14 @@
pragma solidity ^0.8.0;
import "../access/AccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/CountersUpgradeable.sol";
import "../token/ERC721/ERC721Upgradeable.sol";
import "../token/ERC721/ERC721BurnableUpgradeable.sol";
import "../token/ERC721/ERC721PausableUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../ERC721Upgradeable.sol";
import "../extensions/ERC721EnumerableUpgradeable.sol";
import "../extensions/ERC721BurnableUpgradeable.sol";
import "../extensions/ERC721PausableUpgradeable.sol";
import "../../../access/AccessControlEnumerableUpgradeable.sol";
import "../../../utils/ContextUpgradeable.sol";
import "../../../utils/CountersUpgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev {ERC721} token, including:
......@@ -25,10 +26,7 @@ import "../proxy/Initializable.sol";
* roles, as well as the default admin role, which will let it grant both minter
* and pauser roles to other accounts.
*/
contract ERC721PresetMinterPauserAutoIdUpgradeable is Initializable, ContextUpgradeable, AccessControlUpgradeable, ERC721BurnableUpgradeable, ERC721PausableUpgradeable {
function initialize(string memory name, string memory symbol, string memory baseURI) public virtual initializer {
__ERC721PresetMinterPauserAutoId_init(name, symbol, baseURI);
}
contract ERC721PresetMinterPauserAutoIdUpgradeable is Initializable, ContextUpgradeable, AccessControlEnumerableUpgradeable, ERC721EnumerableUpgradeable, ERC721BurnableUpgradeable, ERC721PausableUpgradeable {
using CountersUpgradeable for CountersUpgradeable.Counter;
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
......@@ -36,6 +34,8 @@ contract ERC721PresetMinterPauserAutoIdUpgradeable is Initializable, ContextUpgr
CountersUpgradeable.Counter private _tokenIdTracker;
string private _baseTokenURI;
/**
* @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
* account that deploys the contract.
......@@ -43,24 +43,30 @@ contract ERC721PresetMinterPauserAutoIdUpgradeable is Initializable, ContextUpgr
* Token URIs will be autogenerated based on `baseURI` and their token IDs.
* See {ERC721-tokenURI}.
*/
function __ERC721PresetMinterPauserAutoId_init(string memory name, string memory symbol, string memory baseURI) internal initializer {
function __ERC721PresetMinterPauserAutoId_init(string memory name, string memory symbol, string memory baseTokenURI) internal initializer {
__Context_init_unchained();
__AccessControl_init_unchained();
__AccessControlEnumerable_init_unchained();
__ERC165_init_unchained();
__ERC721_init_unchained(name, symbol);
__ERC721Enumerable_init_unchained();
__ERC721Burnable_init_unchained();
__Pausable_init_unchained();
__ERC721Pausable_init_unchained();
__ERC721PresetMinterPauserAutoId_init_unchained(name, symbol, baseURI);
__ERC721PresetMinterPauserAutoId_init_unchained(name, symbol, baseTokenURI);
}
function __ERC721PresetMinterPauserAutoId_init_unchained(string memory name, string memory symbol, string memory baseURI) internal initializer {
function __ERC721PresetMinterPauserAutoId_init_unchained(string memory name, string memory symbol, string memory baseTokenURI) internal initializer {
_baseTokenURI = baseTokenURI;
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
_setupRole(MINTER_ROLE, _msgSender());
_setupRole(PAUSER_ROLE, _msgSender());
}
_setBaseURI(baseURI);
function _baseURI() internal view virtual override returns (string memory) {
return _baseTokenURI;
}
/**
......@@ -111,8 +117,15 @@ contract ERC721PresetMinterPauserAutoIdUpgradeable is Initializable, ContextUpgr
_unpause();
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override(ERC721Upgradeable, ERC721PausableUpgradeable) {
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override(ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721PausableUpgradeable) {
super._beforeTokenTransfer(from, to, tokenId);
}
uint256[49] private __gap;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721Upgradeable, ERC721EnumerableUpgradeable) returns (bool) {
return super.supportsInterface(interfaceId);
}
uint256[48] private __gap;
}
......@@ -2,13 +2,13 @@
pragma solidity ^0.8.0;
import "./IERC721ReceiverUpgradeable.sol";
import "../../proxy/Initializable.sol";
import "../IERC721ReceiverUpgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev Implementation of the {IERC721Receiver} interface.
*
* Accepts all token transfers.
* Accepts all token transfers.
* Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
*/
contract ERC721HolderUpgradeable is Initializable, IERC721ReceiverUpgradeable {
......
......@@ -2,14 +2,14 @@
pragma solidity ^0.8.0;
import "../../utils/ContextUpgradeable.sol";
import "./IERC777Upgradeable.sol";
import "./IERC777RecipientUpgradeable.sol";
import "./IERC777SenderUpgradeable.sol";
import "../../token/ERC20/IERC20Upgradeable.sol";
import "../ERC20/IERC20Upgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../introspection/IERC1820RegistryUpgradeable.sol";
import "../../proxy/Initializable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../utils/introspection/IERC1820RegistryUpgradeable.sol";
import "../../utils/Initializable.sol";
/**
* @dev Implementation of the {IERC777} interface.
......
......@@ -22,3 +22,9 @@ Additionally there are interfaces used to develop contracts that react to token
{{IERC777Sender}}
{{IERC777Recipient}}
== Presets
These contracts are preconfigured combinations of features. They can be used through inheritance or as models to copy and paste their source code.
{{ERC777PresetFixedSupply}}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../token/ERC777/ERC777Upgradeable.sol";
import "../proxy/Initializable.sol";
import "../ERC777Upgradeable.sol";
import "../../../utils/Initializable.sol";
/**
* @dev {ERC777} token, including:
......@@ -13,15 +13,6 @@ import "../proxy/Initializable.sol";
* _Available since v3.4._
*/
contract ERC777PresetFixedSupplyUpgradeable is Initializable, ERC777Upgradeable {
function initialize(
string memory name,
string memory symbol,
address[] memory defaultOperators,
uint256 initialSupply,
address owner
) public virtual initializer {
__ERC777PresetFixedSupply_init(name, symbol, defaultOperators, initialSupply, owner);
}
/**
* @dev Mints `initialSupply` amount of token and transfers them to `owner`.
*
......
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "../math/MathUpgradeable.sol";
import "./math/MathUpgradeable.sol";
/**
* @dev Collection of functions related to array types.
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
import "./Initializable.sol";
/*
* @dev Provides information about the current execution context, including the
......
......@@ -3,7 +3,7 @@
// solhint-disable-next-line compiler-version
pragma solidity ^0.8.0;
import "../utils/AddressUpgradeable.sol";
import "./AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
......
......@@ -2,10 +2,10 @@
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../math/SafeMathUpgradeable.sol";
import "../utils/AddressUpgradeable.sol";
import "../proxy/Initializable.sol";
import "./AddressUpgradeable.sol";
import "./ContextUpgradeable.sol";
import "./math/SafeMathUpgradeable.sol";
import "./Initializable.sol";
/**
* @title PaymentSplitter
......
......@@ -5,11 +5,6 @@ NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/
Miscellaneous contracts and libraries containing utility functions you can use to improve security, work with new data types, or safely use low-level primitives.
Security tools include:
* {Pausable}: provides a simple way to halt activity in your contracts (often in response to an external threat).
* {ReentrancyGuard}: protects you from https://blog.openzeppelin.com/reentrancy-after-istanbul/[reentrant calls].
The {Address}, {Arrays} and {Strings} libraries provide more operations related to these native data types, while {SafeCast} adds ways to safely convert between the different signed and unsigned numeric types.
For new data types:
......@@ -27,26 +22,69 @@ As of v3.0, {EnumerableMap} supports `uint256 -> address` (`UintToAddressMap`),
Finally, {Create2} contains all necessary utilities to safely use the https://blog.openzeppelin.com/getting-the-most-out-of-create2/[`CREATE2` EVM opcode], without having to deal with low-level assembly.
== Contracts
== Math
{{Pausable}}
{{Math}}
{{ReentrancyGuard}}
{{SafeCast}}
== Libraries
{{SafeMath}}
{{Address}}
{{SignedSafeMath}}
{{Arrays}}
== Cryptography
{{Counters}}
{{ECDSA}}
{{Create2}}
{{MerkleProof}}
{{EIP712}}
== Introspection
This set of interfaces and contracts deal with https://en.wikipedia.org/wiki/Type_introspection[type introspection] of contracts, that is, examining which functions can be called on them. This is usually referred to as a contract's _interface_.
Ethereum contracts have no native concept of an interface, so applications must usually simply trust they are not making an incorrect call. For trusted setups this is a non-issue, but often unknown and untrusted third-party addresses need to be interacted with. There may even not be any direct calls to them! (e.g. `ERC20` tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract _declaring_ its interface can be very helpful in preventing errors.
There are two main ways to approach this.
* Locally, where a contract implements `IERC165` and declares an interface, and a second one queries it directly via `ERC165Checker`.
* Globally, where a global and unique registry (`IERC1820Registry`) is used to register implementers of a certain interface (`IERC1820Implementer`). It is then the registry that is queried, which allows for more complex setups, like contracts implementing interfaces for externally-owned accounts.
Note that, in all cases, accounts simply _declare_ their interfaces, but they are not required to actually implement them. This mechanism can therefore be used to both prevent errors and allow for complex interactions (see `ERC777`), but it must not be relied on for security.
{{IERC165}}
{{ERC165}}
{{ERC165Storage}}
{{ERC165Checker}}
{{IERC1820Registry}}
{{IERC1820Implementer}}
{{ERC1820Implementer}}
== Data Structures
{{EnumerableMap}}
{{EnumerableSet}}
{{SafeCast}}
== Libraries
{{Create2}}
{{Address}}
{{Arrays}}
{{Counters}}
{{Strings}}
== Other
{{Initializable}}
......@@ -47,7 +47,7 @@ library ECDSAUpgradeable {
}
/**
* @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`,
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
......@@ -72,9 +72,9 @@ library ECDSAUpgradeable {
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* replicates the behavior of the
* https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
* JSON-RPC method.
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
......@@ -83,4 +83,17 @@ library ECDSAUpgradeable {
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
import "./ECDSAUpgradeable.sol";
import "../Initializable.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
......@@ -87,7 +89,7 @@ abstract contract EIP712Upgradeable is Initializable {
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash));
return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "./EscrowUpgradeable.sol";
import "../../proxy/Initializable.sol";
import "../Initializable.sol";
/**
* @title ConditionalEscrow
......
......@@ -3,8 +3,8 @@
pragma solidity ^0.8.0;
import "../../access/OwnableUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../proxy/Initializable.sol";
import "../AddressUpgradeable.sol";
import "../Initializable.sol";
/**
* @title Escrow
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "./ConditionalEscrowUpgradeable.sol";
import "../../proxy/Initializable.sol";
import "../Initializable.sol";
/**
* @title RefundEscrow
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "./ERC165Upgradeable.sol";
import "../proxy/Initializable.sol";
import "../Initializable.sol";
/**
* @dev Storage based implementation of the {IERC165} interface.
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "./IERC165Upgradeable.sol";
import "../proxy/Initializable.sol";
import "../Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "./IERC1820ImplementerUpgradeable.sol";
import "../proxy/Initializable.sol";
import "../Initializable.sol";
/**
* @dev Implementation of the {IERC1820Implementer} interface.
......
......@@ -2,7 +2,6 @@
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
......@@ -19,7 +18,6 @@ pragma solidity ^0.8.0;
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCastUpgradeable {
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
......
......@@ -2,8 +2,15 @@
pragma solidity ^0.8.0;
// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.
/**
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
* now has built in overflow checking.
*/
library SafeMathUpgradeable {
/**
......
......@@ -3,8 +3,10 @@
pragma solidity ^0.8.0;
/**
* @title SignedSafeMath
* @dev Signed math operations that revert on error.
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SignedSafeMath` is no longer needed starting with Solidity 0.8. The compiler
* now has built in overflow checking.
*/
library SignedSafeMathUpgradeable {
/**
......
......@@ -8,6 +8,9 @@
"name": "openzeppelin-solidity",
"version": "3.4.0",
"license": "MIT",
"bin": {
"openzeppelin-contracts-migrate-imports": "scripts/migrate-imports.js"
},
"devDependencies": {
"@nomiclabs/hardhat-solhint": "^2.0.0",
"@nomiclabs/hardhat-truffle5": "^2.0.0",
......@@ -10,6 +10,9 @@
"!/contracts/proxy",
"/test/behaviors"
],
"bin": {
"openzeppelin-contracts-migrate-imports": "scripts/migrate-imports.js"
},
"scripts": {
"compile": "hardhat compile",
"coverage": "hardhat coverage",
......
#!/usr/bin/env node
const { promises: fs } = require('fs');
const path = require('path');
const pathUpdates = {
// 'access/AccessControl.sol': undefined,
// 'access/Ownable.sol': undefined,
'access/TimelockController.sol': 'governance/TimelockController.sol',
'cryptography/ECDSA.sol': 'utils/cryptography/ECDSA.sol',
'cryptography/MerkleProof.sol': 'utils/cryptography/MerkleProof.sol',
'drafts/EIP712.sol': 'utils/cryptography/draft-EIP712.sol',
'drafts/ERC20Permit.sol': 'token/ERC20/extensions/draft-ERC20Permit.sol',
'drafts/IERC20Permit.sol': 'token/ERC20/extensions/draft-IERC20Permit.sol',
'GSN/Context.sol': 'utils/Context.sol',
// 'GSN/GSNRecipientERC20Fee.sol': undefined,
// 'GSN/GSNRecipientSignature.sol': undefined,
// 'GSN/GSNRecipient.sol': undefined,
// 'GSN/IRelayHub.sol': undefined,
// 'GSN/IRelayRecipient.sol': undefined,
'introspection/ERC165Checker.sol': 'utils/introspection/ERC165Checker.sol',
'introspection/ERC165.sol': 'utils/introspection/ERC165.sol',
'introspection/ERC1820Implementer.sol': 'utils/introspection/ERC1820Implementer.sol',
'introspection/IERC165.sol': 'utils/introspection/IERC165.sol',
'introspection/IERC1820Implementer.sol': 'utils/introspection/IERC1820Implementer.sol',
'introspection/IERC1820Registry.sol': 'utils/introspection/IERC1820Registry.sol',
'math/Math.sol': 'utils/math/Math.sol',
'math/SafeMath.sol': 'utils/math/SafeMath.sol',
'math/SignedSafeMath.sol': 'utils/math/SignedSafeMath.sol',
'payment/escrow/ConditionalEscrow.sol': 'utils/escrow/ConditionalEscrow.sol',
'payment/escrow/Escrow.sol': 'utils/escrow/Escrow.sol',
'payment/escrow/RefundEscrow.sol': 'utils/escrow/RefundEscrow.sol',
'payment/PaymentSplitter.sol': 'utils/PaymentSplitter.sol',
'payment/PullPayment.sol': 'security/PullPayment.sol',
'presets/ERC1155PresetMinterPauser.sol': 'token/ERC1155/presets/ERC1155PresetMinterPauser.sol',
'presets/ERC20PresetFixedSupply.sol': 'token/ERC20/presets/ERC20PresetFixedSupply.sol',
'presets/ERC20PresetMinterPauser.sol': 'token/ERC20/presets/ERC20PresetMinterPauser.sol',
'presets/ERC721PresetMinterPauserAutoId.sol': 'token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol',
'presets/ERC777PresetFixedSupply.sol': 'token/ERC777/presets/ERC777PresetFixedSupply.sol',
'proxy/BeaconProxy.sol': 'proxy/beacon/BeaconProxy.sol',
// 'proxy/Clones.sol': undefined,
'proxy/IBeacon.sol': 'proxy/beacon/IBeacon.sol',
'proxy/Initializable.sol': 'utils/Initializable.sol',
'proxy/ProxyAdmin.sol': 'proxy/transparent/ProxyAdmin.sol',
// 'proxy/Proxy.sol': undefined,
'proxy/TransparentUpgradeableProxy.sol': 'proxy/transparent/TransparentUpgradeableProxy.sol',
'proxy/UpgradeableBeacon.sol': 'proxy/beacon/UpgradeableBeacon.sol',
// 'proxy/UpgradeableProxy.sol': undefined,
'token/ERC1155/ERC1155Burnable.sol': 'token/ERC1155/extensions/ERC1155Burnable.sol',
'token/ERC1155/ERC1155Holder.sol': 'token/ERC1155/utils/ERC1155Holder.sol',
'token/ERC1155/ERC1155Pausable.sol': 'token/ERC1155/extensions/ERC1155Pausable.sol',
'token/ERC1155/ERC1155Receiver.sol': 'token/ERC1155/utils/ERC1155Receiver.sol',
// 'token/ERC1155/ERC1155.sol': undefined,
'token/ERC1155/IERC1155MetadataURI.sol': 'token/ERC1155/extensions/IERC1155MetadataURI.sol',
// 'token/ERC1155/IERC1155Receiver.sol': undefined,
// 'token/ERC1155/IERC1155.sol': undefined,
'token/ERC20/ERC20Burnable.sol': 'token/ERC20/extensions/ERC20Burnable.sol',
'token/ERC20/ERC20Capped.sol': 'token/ERC20/extensions/ERC20Capped.sol',
'token/ERC20/ERC20Pausable.sol': 'token/ERC20/extensions/ERC20Pausable.sol',
'token/ERC20/ERC20Snapshot.sol': 'token/ERC20/extensions/ERC20Snapshot.sol',
// 'token/ERC20/ERC20.sol': undefined,
// 'token/ERC20/IERC20.sol': undefined,
'token/ERC20/SafeERC20.sol': 'token/ERC20/utils/SafeERC20.sol',
'token/ERC20/TokenTimelock.sol': 'token/ERC20/utils/TokenTimelock.sol',
'token/ERC721/ERC721Burnable.sol': 'token/ERC721/extensions/ERC721Burnable.sol',
'token/ERC721/ERC721Holder.sol': 'token/ERC721/utils/ERC721Holder.sol',
'token/ERC721/ERC721Pausable.sol': 'token/ERC721/extensions/ERC721Pausable.sol',
// 'token/ERC721/ERC721.sol': undefined,
'token/ERC721/IERC721Enumerable.sol': 'token/ERC721/extensions/IERC721Enumerable.sol',
'token/ERC721/IERC721Metadata.sol': 'token/ERC721/extensions/IERC721Metadata.sol',
// 'token/ERC721/IERC721Receiver.sol': undefined,
// 'token/ERC721/IERC721.sol': undefined,
// 'token/ERC777/ERC777.sol': undefined,
// 'token/ERC777/IERC777Recipient.sol': undefined,
// 'token/ERC777/IERC777Sender.sol': undefined,
// 'token/ERC777/IERC777.sol': undefined,
// 'utils/Address.sol': undefined,
// 'utils/Arrays.sol': undefined,
// 'utils/Context.sol': undefined,
// 'utils/Counters.sol': undefined,
// 'utils/Create2.sol': undefined,
'utils/EnumerableMap.sol': 'utils/structs/EnumerableMap.sol',
'utils/EnumerableSet.sol': 'utils/structs/EnumerableSet.sol',
'utils/Pausable.sol': 'security/Pausable.sol',
'utils/ReentrancyGuard.sol': 'security/ReentrancyGuard.sol',
'utils/SafeCast.sol': 'utils/math/SafeCast.sol',
// 'utils/Strings.sol': undefined,
};
async function main (paths = [ 'contracts' ]) {
const files = await listFilesRecursively(paths, /\.sol$/);
const updatedFiles = [];
for (const file of files) {
if (await updateFile(file, updateImportPaths)) {
updatedFiles.push(file);
}
}
if (updatedFiles.length > 0) {
console.log(`${updatedFiles.length} file(s) were updated`);
for (const c of updatedFiles) {
console.log('-', c);
}
} else {
console.log('No files were updated');
}
}
async function listFilesRecursively (paths, filter) {
const queue = paths;
const files = [];
while (queue.length > 0) {
const top = queue.shift();
const stat = await fs.stat(top);
if (stat.isFile()) {
if (top.match(filter)) {
files.push(top);
}
} else if (stat.isDirectory()) {
for (const name of await fs.readdir(top)) {
queue.push(path.join(top, name));
}
}
}
return files;
}
async function updateFile (file, update) {
const content = await fs.readFile(file, 'utf8');
const updatedContent = update(content);
if (updatedContent !== content) {
await fs.writeFile(file, updatedContent);
return true;
} else {
return false;
}
}
function updateImportPaths (source) {
for (const [ oldPath, newPath ] of Object.entries(pathUpdates)) {
source = source.replace(
path.join('@openzeppelin/contracts', oldPath),
path.join('@openzeppelin/contracts', newPath),
);
source = source.replace(
path.join('@openzeppelin/contracts-upgradeable', getUpgradeablePath(oldPath)),
path.join('@openzeppelin/contracts-upgradeable', getUpgradeablePath(newPath)),
);
}
return source;
}
function getUpgradeablePath (file) {
const { dir, name, ext } = path.parse(file);
const upgradeableName = name + 'Upgradeable';
return path.format({ dir, ext, name: upgradeableName });
}
module.exports = {
pathUpdates,
updateImportPaths,
};
if (require.main === module) {
const args = process.argv.length > 2 ? process.argv.slice(2) : undefined;
main(args).catch(e => {
console.error(e);
process.exit(1);
});
}
#!/usr/bin/env bash
set -euo pipefail
shopt -s globstar
# cross platform `mkdir -p`
node -e 'fs.mkdirSync("build/contracts", { recursive: true })'
cp artifacts/*.json build/contracts
cp artifacts/contracts/**/*.json build/contracts
rm build/contracts/*.dbg.json
node scripts/remove-ignored-artifacts.js
......@@ -23,7 +23,12 @@ const ignorePatternsSubtrees = ignorePatterns
.concat(ignorePatterns.map(pat => path.join(pat, '**/*')))
.map(p => p.replace(/^\//, ''));
const solcOutput = readJSON('cache/solc-output.json');
const buildinfo = 'artifacts/build-info';
const filenames = fs.readdirSync(buildinfo);
if (filenames.length !== 1) {
throw new Error(`There should only be one file in ${buildinfo}`);
}
const solcOutput = readJSON(path.join(buildinfo, filenames[0])).output;
const artifactsDir = 'build/contracts';
......
......@@ -9,7 +9,7 @@ npm run compile
# -x: exclude all proxy contracts except Clones library
# -p: emit public initializer
npx @openzeppelin/upgrade-safe-transpiler -D \
-i contracts/proxy/Initializable.sol \
-i contracts/utils/Initializable.sol \
-x 'contracts/proxy/**/*' \
-x '!contracts/proxy/Clones.sol' \
-p 'contracts/presets/**/*'
......
const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const DEFAULT_ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000';
const ROLE = web3.utils.soliditySha3('ROLE');
const OTHER_ROLE = web3.utils.soliditySha3('OTHER_ROLE');
function shouldBehaveLikeAccessControl (errorPrefix, admin, authorized, other, otherAdmin, otherAuthorized) {
describe('default admin', function () {
it('deployer has default admin role', async function () {
expect(await this.accessControl.hasRole(DEFAULT_ADMIN_ROLE, admin)).to.equal(true);
});
it('other roles\'s admin is the default admin role', async function () {
expect(await this.accessControl.getRoleAdmin(ROLE)).to.equal(DEFAULT_ADMIN_ROLE);
});
it('default admin role\'s admin is itself', async function () {
expect(await this.accessControl.getRoleAdmin(DEFAULT_ADMIN_ROLE)).to.equal(DEFAULT_ADMIN_ROLE);
});
});
describe('granting', function () {
it('admin can grant role to other accounts', async function () {
const receipt = await this.accessControl.grantRole(ROLE, authorized, { from: admin });
expectEvent(receipt, 'RoleGranted', { account: authorized, role: ROLE, sender: admin });
expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(true);
});
it('non-admin cannot grant role to other accounts', async function () {
await expectRevert(
this.accessControl.grantRole(ROLE, authorized, { from: other }),
`${errorPrefix}: sender must be an admin to grant`,
);
});
it('accounts can be granted a role multiple times', async function () {
await this.accessControl.grantRole(ROLE, authorized, { from: admin });
const receipt = await this.accessControl.grantRole(ROLE, authorized, { from: admin });
expectEvent.notEmitted(receipt, 'RoleGranted');
});
});
describe('revoking', function () {
it('roles that are not had can be revoked', async function () {
expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(false);
const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: admin });
expectEvent.notEmitted(receipt, 'RoleRevoked');
});
context('with granted role', function () {
beforeEach(async function () {
await this.accessControl.grantRole(ROLE, authorized, { from: admin });
});
it('admin can revoke role', async function () {
const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: admin });
expectEvent(receipt, 'RoleRevoked', { account: authorized, role: ROLE, sender: admin });
expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(false);
});
it('non-admin cannot revoke role', async function () {
await expectRevert(
this.accessControl.revokeRole(ROLE, authorized, { from: other }),
`${errorPrefix}: sender must be an admin to revoke`,
);
});
it('a role can be revoked multiple times', async function () {
await this.accessControl.revokeRole(ROLE, authorized, { from: admin });
const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: admin });
expectEvent.notEmitted(receipt, 'RoleRevoked');
});
});
});
describe('renouncing', function () {
it('roles that are not had can be renounced', async function () {
const receipt = await this.accessControl.renounceRole(ROLE, authorized, { from: authorized });
expectEvent.notEmitted(receipt, 'RoleRevoked');
});
context('with granted role', function () {
beforeEach(async function () {
await this.accessControl.grantRole(ROLE, authorized, { from: admin });
});
it('bearer can renounce role', async function () {
const receipt = await this.accessControl.renounceRole(ROLE, authorized, { from: authorized });
expectEvent(receipt, 'RoleRevoked', { account: authorized, role: ROLE, sender: authorized });
expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(false);
});
it('only the sender can renounce their roles', async function () {
await expectRevert(
this.accessControl.renounceRole(ROLE, authorized, { from: admin }),
`${errorPrefix}: can only renounce roles for self`,
);
});
it('a role can be renounced multiple times', async function () {
await this.accessControl.renounceRole(ROLE, authorized, { from: authorized });
const receipt = await this.accessControl.renounceRole(ROLE, authorized, { from: authorized });
expectEvent.notEmitted(receipt, 'RoleRevoked');
});
});
});
describe('setting role admin', function () {
beforeEach(async function () {
const receipt = await this.accessControl.setRoleAdmin(ROLE, OTHER_ROLE);
expectEvent(receipt, 'RoleAdminChanged', {
role: ROLE,
previousAdminRole: DEFAULT_ADMIN_ROLE,
newAdminRole: OTHER_ROLE,
});
await this.accessControl.grantRole(OTHER_ROLE, otherAdmin, { from: admin });
});
it('a role\'s admin role can be changed', async function () {
expect(await this.accessControl.getRoleAdmin(ROLE)).to.equal(OTHER_ROLE);
});
it('the new admin can grant roles', async function () {
const receipt = await this.accessControl.grantRole(ROLE, authorized, { from: otherAdmin });
expectEvent(receipt, 'RoleGranted', { account: authorized, role: ROLE, sender: otherAdmin });
});
it('the new admin can revoke roles', async function () {
await this.accessControl.grantRole(ROLE, authorized, { from: otherAdmin });
const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: otherAdmin });
expectEvent(receipt, 'RoleRevoked', { account: authorized, role: ROLE, sender: otherAdmin });
});
it('a role\'s previous admins no longer grant roles', async function () {
await expectRevert(
this.accessControl.grantRole(ROLE, authorized, { from: admin }),
'AccessControl: sender must be an admin to grant',
);
});
it('a role\'s previous admins no longer revoke roles', async function () {
await expectRevert(
this.accessControl.revokeRole(ROLE, authorized, { from: admin }),
'AccessControl: sender must be an admin to revoke',
);
});
});
}
function shouldBehaveLikeAccessControlEnumerable (errorPrefix, admin, authorized, other, otherAdmin, otherAuthorized) {
describe('enumerating', function () {
it('role bearers can be enumerated', async function () {
await this.accessControl.grantRole(ROLE, authorized, { from: admin });
await this.accessControl.grantRole(ROLE, other, { from: admin });
await this.accessControl.grantRole(ROLE, otherAuthorized, { from: admin });
await this.accessControl.revokeRole(ROLE, other, { from: admin });
const memberCount = await this.accessControl.getRoleMemberCount(ROLE);
expect(memberCount).to.bignumber.equal('2');
const bearers = [];
for (let i = 0; i < memberCount; ++i) {
bearers.push(await this.accessControl.getRoleMember(ROLE, i));
}
expect(bearers).to.have.members([authorized, otherAuthorized]);
});
});
}
module.exports = {
shouldBehaveLikeAccessControl,
shouldBehaveLikeAccessControlEnumerable,
};
const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const {
shouldBehaveLikeAccessControl,
} = require('./AccessControl.behavior.js');
const AccessControlMock = artifacts.require('AccessControlMock');
contract('AccessControl', function (accounts) {
const [ admin, authorized, otherAuthorized, other, otherAdmin ] = accounts;
const DEFAULT_ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000';
const ROLE = web3.utils.soliditySha3('ROLE');
const OTHER_ROLE = web3.utils.soliditySha3('OTHER_ROLE');
beforeEach(async function () {
this.accessControl = await AccessControlMock.new({ from: admin });
});
describe('default admin', function () {
it('deployer has default admin role', async function () {
expect(await this.accessControl.hasRole(DEFAULT_ADMIN_ROLE, admin)).to.equal(true);
});
it('other roles\'s admin is the default admin role', async function () {
expect(await this.accessControl.getRoleAdmin(ROLE)).to.equal(DEFAULT_ADMIN_ROLE);
});
it('default admin role\'s admin is itself', async function () {
expect(await this.accessControl.getRoleAdmin(DEFAULT_ADMIN_ROLE)).to.equal(DEFAULT_ADMIN_ROLE);
});
});
describe('granting', function () {
it('admin can grant role to other accounts', async function () {
const receipt = await this.accessControl.grantRole(ROLE, authorized, { from: admin });
expectEvent(receipt, 'RoleGranted', { account: authorized, role: ROLE, sender: admin });
expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(true);
});
it('non-admin cannot grant role to other accounts', async function () {
await expectRevert(
this.accessControl.grantRole(ROLE, authorized, { from: other }),
'AccessControl: sender must be an admin to grant',
);
});
it('accounts can be granted a role multiple times', async function () {
await this.accessControl.grantRole(ROLE, authorized, { from: admin });
const receipt = await this.accessControl.grantRole(ROLE, authorized, { from: admin });
expectEvent.notEmitted(receipt, 'RoleGranted');
});
});
describe('revoking', function () {
it('roles that are not had can be revoked', async function () {
expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(false);
const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: admin });
expectEvent.notEmitted(receipt, 'RoleRevoked');
});
context('with granted role', function () {
beforeEach(async function () {
await this.accessControl.grantRole(ROLE, authorized, { from: admin });
});
it('admin can revoke role', async function () {
const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: admin });
expectEvent(receipt, 'RoleRevoked', { account: authorized, role: ROLE, sender: admin });
expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(false);
});
it('non-admin cannot revoke role', async function () {
await expectRevert(
this.accessControl.revokeRole(ROLE, authorized, { from: other }),
'AccessControl: sender must be an admin to revoke',
);
});
it('a role can be revoked multiple times', async function () {
await this.accessControl.revokeRole(ROLE, authorized, { from: admin });
const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: admin });
expectEvent.notEmitted(receipt, 'RoleRevoked');
});
});
this.accessControl = await AccessControlMock.new({ from: accounts[0] });
});
describe('renouncing', function () {
it('roles that are not had can be renounced', async function () {
const receipt = await this.accessControl.renounceRole(ROLE, authorized, { from: authorized });
expectEvent.notEmitted(receipt, 'RoleRevoked');
});
context('with granted role', function () {
beforeEach(async function () {
await this.accessControl.grantRole(ROLE, authorized, { from: admin });
});
it('bearer can renounce role', async function () {
const receipt = await this.accessControl.renounceRole(ROLE, authorized, { from: authorized });
expectEvent(receipt, 'RoleRevoked', { account: authorized, role: ROLE, sender: authorized });
expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(false);
});
it('only the sender can renounce their roles', async function () {
await expectRevert(
this.accessControl.renounceRole(ROLE, authorized, { from: admin }),
'AccessControl: can only renounce roles for self',
);
});
it('a role can be renounced multiple times', async function () {
await this.accessControl.renounceRole(ROLE, authorized, { from: authorized });
const receipt = await this.accessControl.renounceRole(ROLE, authorized, { from: authorized });
expectEvent.notEmitted(receipt, 'RoleRevoked');
});
});
});
describe('enumerating', function () {
it('role bearers can be enumerated', async function () {
await this.accessControl.grantRole(ROLE, authorized, { from: admin });
await this.accessControl.grantRole(ROLE, otherAuthorized, { from: admin });
const memberCount = await this.accessControl.getRoleMemberCount(ROLE);
expect(memberCount).to.bignumber.equal('2');
const bearers = [];
for (let i = 0; i < memberCount; ++i) {
bearers.push(await this.accessControl.getRoleMember(ROLE, i));
}
expect(bearers).to.have.members([authorized, otherAuthorized]);
});
});
describe('setting role admin', function () {
beforeEach(async function () {
const receipt = await this.accessControl.setRoleAdmin(ROLE, OTHER_ROLE);
expectEvent(receipt, 'RoleAdminChanged', {
role: ROLE,
previousAdminRole: DEFAULT_ADMIN_ROLE,
newAdminRole: OTHER_ROLE,
});
await this.accessControl.grantRole(OTHER_ROLE, otherAdmin, { from: admin });
});
it('a role\'s admin role can be changed', async function () {
expect(await this.accessControl.getRoleAdmin(ROLE)).to.equal(OTHER_ROLE);
});
it('the new admin can grant roles', async function () {
const receipt = await this.accessControl.grantRole(ROLE, authorized, { from: otherAdmin });
expectEvent(receipt, 'RoleGranted', { account: authorized, role: ROLE, sender: otherAdmin });
});
it('the new admin can revoke roles', async function () {
await this.accessControl.grantRole(ROLE, authorized, { from: otherAdmin });
const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: otherAdmin });
expectEvent(receipt, 'RoleRevoked', { account: authorized, role: ROLE, sender: otherAdmin });
});
it('a role\'s previous admins no longer grant roles', async function () {
await expectRevert(
this.accessControl.grantRole(ROLE, authorized, { from: admin }),
'AccessControl: sender must be an admin to grant',
);
});
it('a role\'s previous admins no longer revoke roles', async function () {
await expectRevert(
this.accessControl.revokeRole(ROLE, authorized, { from: admin }),
'AccessControl: sender must be an admin to revoke',
);
});
});
shouldBehaveLikeAccessControl('AccessControl', ...accounts);
});
const {
shouldBehaveLikeAccessControl,
shouldBehaveLikeAccessControlEnumerable,
} = require('./AccessControl.behavior.js');
const AccessControlMock = artifacts.require('AccessControlEnumerableMock');
contract('AccessControl', function (accounts) {
beforeEach(async function () {
this.accessControl = await AccessControlMock.new({ from: accounts[0] });
});
shouldBehaveLikeAccessControl('AccessControl', ...accounts);
shouldBehaveLikeAccessControlEnumerable('AccessControl', ...accounts);
});
const ethSigUtil = require('eth-sig-util');
const Wallet = require('ethereumjs-wallet').default;
const { EIP712Domain } = require('../helpers/eip712');
const { expectEvent } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const ERC2771ContextMock = artifacts.require('ERC2771ContextMock');
const MinimalForwarder = artifacts.require('MinimalForwarder');
const ContextMockCaller = artifacts.require('ContextMockCaller');
const { shouldBehaveLikeRegularContext } = require('../utils/Context.behavior');
const name = 'MinimalForwarder';
const version = '0.0.1';
contract('ERC2771Context', function (accounts) {
beforeEach(async function () {
this.forwarder = await MinimalForwarder.new();
this.recipient = await ERC2771ContextMock.new(this.forwarder.address);
this.domain = {
name,
version,
chainId: await web3.eth.getChainId(),
verifyingContract: this.forwarder.address,
};
this.types = {
EIP712Domain,
ForwardRequest: [
{ name: 'from', type: 'address' },
{ name: 'to', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'gas', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'data', type: 'bytes' },
],
};
});
it('recognize trusted forwarder', async function () {
expect(await this.recipient.isTrustedForwarder(this.forwarder.address));
});
context('when called directly', function () {
beforeEach(async function () {
this.context = this.recipient; // The Context behavior expects the contract in this.context
this.caller = await ContextMockCaller.new();
});
shouldBehaveLikeRegularContext(...accounts);
});
context('when receiving a relayed call', function () {
beforeEach(async function () {
this.wallet = Wallet.generate();
this.sender = web3.utils.toChecksumAddress(this.wallet.getAddressString());
this.data = {
types: this.types,
domain: this.domain,
primaryType: 'ForwardRequest',
};
});
describe('msgSender', function () {
it('returns the relayed transaction original sender', async function () {
const data = this.recipient.contract.methods.msgSender().encodeABI();
const req = {
from: this.sender,
to: this.recipient.address,
value: '0',
gas: '100000',
nonce: (await this.forwarder.getNonce(this.sender)).toString(),
data,
};
const sign = ethSigUtil.signTypedMessage(this.wallet.getPrivateKey(), { data: { ...this.data, message: req } });
// rejected by lint :/
// expect(await this.forwarder.verify(req, sign)).to.be.true;
const { tx } = await this.forwarder.execute(req, sign);
await expectEvent.inTransaction(tx, ERC2771ContextMock, 'Sender', { sender: this.sender });
});
});
describe('msgData', function () {
it('returns the relayed transaction original data', async function () {
const integerValue = '42';
const stringValue = 'OpenZeppelin';
const data = this.recipient.contract.methods.msgData(integerValue, stringValue).encodeABI();
const req = {
from: this.sender,
to: this.recipient.address,
value: '0',
gas: '100000',
nonce: (await this.forwarder.getNonce(this.sender)).toString(),
data,
};
const sign = ethSigUtil.signTypedMessage(this.wallet.getPrivateKey(), { data: { ...this.data, message: req } });
// rejected by lint :/
// expect(await this.forwarder.verify(req, sign)).to.be.true;
const { tx } = await this.forwarder.execute(req, sign);
await expectEvent.inTransaction(tx, ERC2771ContextMock, 'Data', { data, integerValue, stringValue });
});
});
});
});
const ethSigUtil = require('eth-sig-util');
const Wallet = require('ethereumjs-wallet').default;
const { EIP712Domain } = require('../helpers/eip712');
const { expectRevert, constants } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const MinimalForwarder = artifacts.require('MinimalForwarder');
const name = 'MinimalForwarder';
const version = '0.0.1';
contract('MinimalForwarder', function (accounts) {
beforeEach(async function () {
this.forwarder = await MinimalForwarder.new();
this.domain = {
name,
version,
chainId: await web3.eth.getChainId(),
verifyingContract: this.forwarder.address,
};
this.types = {
EIP712Domain,
ForwardRequest: [
{ name: 'from', type: 'address' },
{ name: 'to', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'gas', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'data', type: 'bytes' },
],
};
});
context('with message', function () {
beforeEach(async function () {
this.wallet = Wallet.generate();
this.sender = web3.utils.toChecksumAddress(this.wallet.getAddressString());
this.req = {
from: this.sender,
to: constants.ZERO_ADDRESS,
value: '0',
gas: '100000',
nonce: Number(await this.forwarder.getNonce(this.sender)),
data: '0x',
};
this.sign = ethSigUtil.signTypedMessage(
this.wallet.getPrivateKey(),
{
data: {
types: this.types,
domain: this.domain,
primaryType: 'ForwardRequest',
message: this.req,
},
},
);
});
context('verify', function () {
context('valid signature', function () {
beforeEach(async function () {
expect(await this.forwarder.getNonce(this.req.from))
.to.be.bignumber.equal(web3.utils.toBN(this.req.nonce));
});
it('success', async function () {
expect(await this.forwarder.verify(this.req, this.sign)).to.be.equal(true);
});
afterEach(async function () {
expect(await this.forwarder.getNonce(this.req.from))
.to.be.bignumber.equal(web3.utils.toBN(this.req.nonce));
});
});
context('invalid signature', function () {
it('tampered from', async function () {
expect(await this.forwarder.verify({ ...this.req, from: accounts[0] }, this.sign))
.to.be.equal(false);
});
it('tampered to', async function () {
expect(await this.forwarder.verify({ ...this.req, to: accounts[0] }, this.sign))
.to.be.equal(false);
});
it('tampered value', async function () {
expect(await this.forwarder.verify({ ...this.req, value: web3.utils.toWei('1') }, this.sign))
.to.be.equal(false);
});
it('tampered nonce', async function () {
expect(await this.forwarder.verify({ ...this.req, nonce: this.req.nonce + 1 }, this.sign))
.to.be.equal(false);
});
it('tampered data', async function () {
expect(await this.forwarder.verify({ ...this.req, data: '0x1742' }, this.sign))
.to.be.equal(false);
});
it('tampered signature', async function () {
const tamperedsign = web3.utils.hexToBytes(this.sign);
tamperedsign[42] ^= 0xff;
expect(await this.forwarder.verify(this.req, web3.utils.bytesToHex(tamperedsign)))
.to.be.equal(false);
});
});
});
context('execute', function () {
context('valid signature', function () {
beforeEach(async function () {
expect(await this.forwarder.getNonce(this.req.from))
.to.be.bignumber.equal(web3.utils.toBN(this.req.nonce));
});
it('success', async function () {
await this.forwarder.execute(this.req, this.sign); // expect to not revert
});
afterEach(async function () {
expect(await this.forwarder.getNonce(this.req.from))
.to.be.bignumber.equal(web3.utils.toBN(this.req.nonce + 1));
});
});
context('invalid signature', function () {
it('tampered from', async function () {
await expectRevert(
this.forwarder.execute({ ...this.req, from: accounts[0] }, this.sign),
'MinimalForwarder: signature does not match request',
);
});
it('tampered to', async function () {
await expectRevert(
this.forwarder.execute({ ...this.req, to: accounts[0] }, this.sign),
'MinimalForwarder: signature does not match request',
);
});
it('tampered value', async function () {
await expectRevert(
this.forwarder.execute({ ...this.req, value: web3.utils.toWei('1') }, this.sign),
'MinimalForwarder: signature does not match request',
);
});
it('tampered nonce', async function () {
await expectRevert(
this.forwarder.execute({ ...this.req, nonce: this.req.nonce + 1 }, this.sign),
'MinimalForwarder: signature does not match request',
);
});
it('tampered data', async function () {
await expectRevert(
this.forwarder.execute({ ...this.req, data: '0x1742' }, this.sign),
'MinimalForwarder: signature does not match request',
);
});
it('tampered signature', async function () {
const tamperedsign = web3.utils.hexToBytes(this.sign);
tamperedsign[42] ^= 0xff;
await expectRevert(
this.forwarder.execute(this.req, web3.utils.bytesToHex(tamperedsign)),
'MinimalForwarder: signature does not match request',
);
});
});
});
});
});
const path = require('path');
const { promises: fs, constants: { F_OK } } = require('fs');
const { expect } = require('chai');
const { pathUpdates, updateImportPaths } = require('../scripts/migrate-imports.js');
describe('migrate-imports.js', function () {
it('every new path exists', async function () {
for (const p of Object.values(pathUpdates)) {
await fs.access(path.join('contracts', p), F_OK);
}
});
it('replaces import paths in a file', async function () {
const source = `
import '@openzeppelin/contracts/math/Math.sol';
import '@openzeppelin/contracts-upgradeable/math/MathUpgradeable.sol';
`;
const expected = `
import '@openzeppelin/contracts/utils/math/Math.sol';
import '@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol';
`;
expect(updateImportPaths(source)).to.equal(expected);
});
});
const shouldBehaveLikeUpgradeableProxy = require('./UpgradeableProxy.behaviour');
const shouldBehaveLikeUpgradeableProxy = require('../UpgradeableProxy.behaviour');
const shouldBehaveLikeTransparentUpgradeableProxy = require('./TransparentUpgradeableProxy.behaviour');
const TransparentUpgradeableProxy = artifacts.require('TransparentUpgradeableProxy');
......
......@@ -3,7 +3,7 @@ const { ZERO_ADDRESS } = constants;
const { expect } = require('chai');
const { shouldSupportInterfaces } = require('../../introspection/SupportsInterface.behavior');
const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior');
const ERC1155ReceiverMock = artifacts.require('ERC1155ReceiverMock');
......
......@@ -5,7 +5,7 @@ const ERC1155Mock = artifacts.require('ERC1155Mock');
const { expect } = require('chai');
const { shouldSupportInterfaces } = require('../../introspection/SupportsInterface.behavior');
const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior');
contract('ERC1155Holder', function (accounts) {
const [creator] = accounts;
......
const { BN } = require('@openzeppelin/test-helpers');
const { shouldBehaveLikeERC20Burnable } = require('./behaviors/ERC20Burnable.behavior');
const { shouldBehaveLikeERC20Burnable } = require('./ERC20Burnable.behavior');
const ERC20BurnableMock = artifacts.require('ERC20BurnableMock');
contract('ERC20Burnable', function (accounts) {
......
const { BN, ether, expectRevert } = require('@openzeppelin/test-helpers');
const { shouldBehaveLikeERC20Capped } = require('./behaviors/ERC20Capped.behavior');
const { shouldBehaveLikeERC20Capped } = require('./ERC20Capped.behavior');
const ERC20Capped = artifacts.require('ERC20CappedMock');
......
......@@ -10,7 +10,7 @@ const Wallet = require('ethereumjs-wallet').default;
const ERC20PermitMock = artifacts.require('ERC20PermitMock');
const { EIP712Domain, domainSeparator } = require('../helpers/eip712');
const { EIP712Domain, domainSeparator } = require('../../../helpers/eip712');
const Permit = [
{ name: 'owner', type: 'address' },
......
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const { ZERO_ADDRESS } = constants;
const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior');
const ERC721ReceiverMock = artifacts.require('ERC721ReceiverMock');
const Error = [ 'None', 'RevertWithMessage', 'RevertWithoutMessage', 'Panic' ]
.reduce((acc, entry, idx) => Object.assign({ [entry]: idx }, acc), {});
const firstTokenId = new BN('5042');
const secondTokenId = new BN('79217');
const nonExistentTokenId = new BN('13');
const baseURI = 'https://api.com/v1/';
const RECEIVER_MAGIC_VALUE = '0x150b7a02';
function shouldBehaveLikeERC721 (errorPrefix, owner, newOwner, approved, anotherApproved, operator, other) {
shouldSupportInterfaces([
'ERC165',
'ERC721',
]);
context('with minted tokens', function () {
beforeEach(async function () {
await this.token.mint(owner, firstTokenId);
await this.token.mint(owner, secondTokenId);
this.toWhom = other; // default to other for toWhom in context-dependent tests
});
describe('balanceOf', function () {
context('when the given address owns some tokens', function () {
it('returns the amount of tokens owned by the given address', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('2');
});
});
context('when the given address does not own any tokens', function () {
it('returns 0', async function () {
expect(await this.token.balanceOf(other)).to.be.bignumber.equal('0');
});
});
context('when querying the zero address', function () {
it('throws', async function () {
await expectRevert(
this.token.balanceOf(ZERO_ADDRESS), 'ERC721: balance query for the zero address',
);
});
});
});
describe('ownerOf', function () {
context('when the given token ID was tracked by this token', function () {
const tokenId = firstTokenId;
it('returns the owner of the given token ID', async function () {
expect(await this.token.ownerOf(tokenId)).to.be.equal(owner);
});
});
context('when the given token ID was not tracked by this token', function () {
const tokenId = nonExistentTokenId;
it('reverts', async function () {
await expectRevert(
this.token.ownerOf(tokenId), 'ERC721: owner query for nonexistent token',
);
});
});
});
describe('transfers', function () {
const tokenId = firstTokenId;
const data = '0x42';
let logs = null;
beforeEach(async function () {
await this.token.approve(approved, tokenId, { from: owner });
await this.token.setApprovalForAll(operator, true, { from: owner });
});
const transferWasSuccessful = function ({ owner, tokenId, approved }) {
it('transfers the ownership of the given token ID to the given address', async function () {
expect(await this.token.ownerOf(tokenId)).to.be.equal(this.toWhom);
});
it('emits a Transfer event', async function () {
expectEvent.inLogs(logs, 'Transfer', { from: owner, to: this.toWhom, tokenId: tokenId });
});
it('clears the approval for the token ID', async function () {
expect(await this.token.getApproved(tokenId)).to.be.equal(ZERO_ADDRESS);
});
it('emits an Approval event', async function () {
expectEvent.inLogs(logs, 'Approval', { owner, approved: ZERO_ADDRESS, tokenId: tokenId });
});
it('adjusts owners balances', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('1');
});
it('adjusts owners tokens by index', async function () {
if (!this.token.tokenOfOwnerByIndex) return;
expect(await this.token.tokenOfOwnerByIndex(this.toWhom, 0)).to.be.bignumber.equal(tokenId);
expect(await this.token.tokenOfOwnerByIndex(owner, 0)).to.be.bignumber.not.equal(tokenId);
});
};
const shouldTransferTokensByUsers = function (transferFunction) {
context('when called by the owner', function () {
beforeEach(async function () {
({ logs } = await transferFunction.call(this, owner, this.toWhom, tokenId, { from: owner }));
});
transferWasSuccessful({ owner, tokenId, approved });
});
context('when called by the approved individual', function () {
beforeEach(async function () {
({ logs } = await transferFunction.call(this, owner, this.toWhom, tokenId, { from: approved }));
});
transferWasSuccessful({ owner, tokenId, approved });
});
context('when called by the operator', function () {
beforeEach(async function () {
({ logs } = await transferFunction.call(this, owner, this.toWhom, tokenId, { from: operator }));
});
transferWasSuccessful({ owner, tokenId, approved });
});
context('when called by the owner without an approved user', function () {
beforeEach(async function () {
await this.token.approve(ZERO_ADDRESS, tokenId, { from: owner });
({ logs } = await transferFunction.call(this, owner, this.toWhom, tokenId, { from: operator }));
});
transferWasSuccessful({ owner, tokenId, approved: null });
});
context('when sent to the owner', function () {
beforeEach(async function () {
({ logs } = await transferFunction.call(this, owner, owner, tokenId, { from: owner }));
});
it('keeps ownership of the token', async function () {
expect(await this.token.ownerOf(tokenId)).to.be.equal(owner);
});
it('clears the approval for the token ID', async function () {
expect(await this.token.getApproved(tokenId)).to.be.equal(ZERO_ADDRESS);
});
it('emits only a transfer event', async function () {
expectEvent.inLogs(logs, 'Transfer', {
from: owner,
to: owner,
tokenId: tokenId,
});
});
it('keeps the owner balance', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('2');
});
it('keeps same tokens by index', async function () {
if (!this.token.tokenOfOwnerByIndex) return;
const tokensListed = await Promise.all(
[0, 1].map(i => this.token.tokenOfOwnerByIndex(owner, i)),
);
expect(tokensListed.map(t => t.toNumber())).to.have.members(
[firstTokenId.toNumber(), secondTokenId.toNumber()],
);
});
});
context('when the address of the previous owner is incorrect', function () {
it('reverts', async function () {
await expectRevert(
transferFunction.call(this, other, other, tokenId, { from: owner }),
'ERC721: transfer of token that is not own',
);
});
});
context('when the sender is not authorized for the token id', function () {
it('reverts', async function () {
await expectRevert(
transferFunction.call(this, owner, other, tokenId, { from: other }),
'ERC721: transfer caller is not owner nor approved',
);
});
});
context('when the given token ID does not exist', function () {
it('reverts', async function () {
await expectRevert(
transferFunction.call(this, owner, other, nonExistentTokenId, { from: owner }),
'ERC721: operator query for nonexistent token',
);
});
});
context('when the address to transfer the token to is the zero address', function () {
it('reverts', async function () {
await expectRevert(
transferFunction.call(this, owner, ZERO_ADDRESS, tokenId, { from: owner }),
'ERC721: transfer to the zero address',
);
});
});
};
describe('via transferFrom', function () {
shouldTransferTokensByUsers(function (from, to, tokenId, opts) {
return this.token.transferFrom(from, to, tokenId, opts);
});
});
describe('via safeTransferFrom', function () {
const safeTransferFromWithData = function (from, to, tokenId, opts) {
return this.token.methods['safeTransferFrom(address,address,uint256,bytes)'](from, to, tokenId, data, opts);
};
const safeTransferFromWithoutData = function (from, to, tokenId, opts) {
return this.token.methods['safeTransferFrom(address,address,uint256)'](from, to, tokenId, opts);
};
const shouldTransferSafely = function (transferFun, data) {
describe('to a user account', function () {
shouldTransferTokensByUsers(transferFun);
});
describe('to a valid receiver contract', function () {
beforeEach(async function () {
this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.None);
this.toWhom = this.receiver.address;
});
shouldTransferTokensByUsers(transferFun);
it('calls onERC721Received', async function () {
const receipt = await transferFun.call(this, owner, this.receiver.address, tokenId, { from: owner });
await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', {
operator: owner,
from: owner,
tokenId: tokenId,
data: data,
});
});
it('calls onERC721Received from approved', async function () {
const receipt = await transferFun.call(this, owner, this.receiver.address, tokenId, { from: approved });
await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', {
operator: approved,
from: owner,
tokenId: tokenId,
data: data,
});
});
describe('with an invalid token id', function () {
it('reverts', async function () {
await expectRevert(
transferFun.call(
this,
owner,
this.receiver.address,
nonExistentTokenId,
{ from: owner },
),
'ERC721: operator query for nonexistent token',
);
});
});
});
};
describe('with data', function () {
shouldTransferSafely(safeTransferFromWithData, data);
});
describe('without data', function () {
shouldTransferSafely(safeTransferFromWithoutData, null);
});
describe('to a receiver contract returning unexpected value', function () {
it('reverts', async function () {
const invalidReceiver = await ERC721ReceiverMock.new('0x42', Error.None);
await expectRevert(
this.token.safeTransferFrom(owner, invalidReceiver.address, tokenId, { from: owner }),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
describe('to a receiver contract that reverts with message', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithMessage);
await expectRevert(
this.token.safeTransferFrom(owner, revertingReceiver.address, tokenId, { from: owner }),
'ERC721ReceiverMock: reverting',
);
});
});
describe('to a receiver contract that reverts without message', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithoutMessage);
await expectRevert(
this.token.safeTransferFrom(owner, revertingReceiver.address, tokenId, { from: owner }),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
describe('to a receiver contract that panics', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.Panic);
await expectRevert.unspecified(
this.token.safeTransferFrom(owner, revertingReceiver.address, tokenId, { from: owner }),
);
});
});
describe('to a contract that does not implement the required function', function () {
it('reverts', async function () {
const nonReceiver = this.token;
await expectRevert(
this.token.safeTransferFrom(owner, nonReceiver.address, tokenId, { from: owner }),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
});
});
describe('safe mint', function () {
const fourthTokenId = new BN(4);
const tokenId = fourthTokenId;
const data = '0x42';
describe('via safeMint', function () { // regular minting is tested in ERC721Mintable.test.js and others
it('calls onERC721Received — with data', async function () {
this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.None);
const receipt = await this.token.safeMint(this.receiver.address, tokenId, data);
await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', {
from: ZERO_ADDRESS,
tokenId: tokenId,
data: data,
});
});
it('calls onERC721Received — without data', async function () {
this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.None);
const receipt = await this.token.safeMint(this.receiver.address, tokenId);
await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', {
from: ZERO_ADDRESS,
tokenId: tokenId,
});
});
context('to a receiver contract returning unexpected value', function () {
it('reverts', async function () {
const invalidReceiver = await ERC721ReceiverMock.new('0x42', Error.None);
await expectRevert(
this.token.safeMint(invalidReceiver.address, tokenId),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
context('to a receiver contract that reverts with message', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithMessage);
await expectRevert(
this.token.safeMint(revertingReceiver.address, tokenId),
'ERC721ReceiverMock: reverting',
);
});
});
context('to a receiver contract that reverts without message', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithoutMessage);
await expectRevert(
this.token.safeMint(revertingReceiver.address, tokenId),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
context('to a receiver contract that panics', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.Panic);
await expectRevert.unspecified(
this.token.safeMint(revertingReceiver.address, tokenId),
);
});
});
context('to a contract that does not implement the required function', function () {
it('reverts', async function () {
const nonReceiver = this.token;
await expectRevert(
this.token.safeMint(nonReceiver.address, tokenId),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
});
});
describe('approve', function () {
const tokenId = firstTokenId;
let logs = null;
const itClearsApproval = function () {
it('clears approval for the token', async function () {
expect(await this.token.getApproved(tokenId)).to.be.equal(ZERO_ADDRESS);
});
};
const itApproves = function (address) {
it('sets the approval for the target address', async function () {
expect(await this.token.getApproved(tokenId)).to.be.equal(address);
});
};
const itEmitsApprovalEvent = function (address) {
it('emits an approval event', async function () {
expectEvent.inLogs(logs, 'Approval', {
owner: owner,
approved: address,
tokenId: tokenId,
});
});
};
context('when clearing approval', function () {
context('when there was no prior approval', function () {
beforeEach(async function () {
({ logs } = await this.token.approve(ZERO_ADDRESS, tokenId, { from: owner }));
});
itClearsApproval();
itEmitsApprovalEvent(ZERO_ADDRESS);
});
context('when there was a prior approval', function () {
beforeEach(async function () {
await this.token.approve(approved, tokenId, { from: owner });
({ logs } = await this.token.approve(ZERO_ADDRESS, tokenId, { from: owner }));
});
itClearsApproval();
itEmitsApprovalEvent(ZERO_ADDRESS);
});
});
context('when approving a non-zero address', function () {
context('when there was no prior approval', function () {
beforeEach(async function () {
({ logs } = await this.token.approve(approved, tokenId, { from: owner }));
});
itApproves(approved);
itEmitsApprovalEvent(approved);
});
context('when there was a prior approval to the same address', function () {
beforeEach(async function () {
await this.token.approve(approved, tokenId, { from: owner });
({ logs } = await this.token.approve(approved, tokenId, { from: owner }));
});
itApproves(approved);
itEmitsApprovalEvent(approved);
});
context('when there was a prior approval to a different address', function () {
beforeEach(async function () {
await this.token.approve(anotherApproved, tokenId, { from: owner });
({ logs } = await this.token.approve(anotherApproved, tokenId, { from: owner }));
});
itApproves(anotherApproved);
itEmitsApprovalEvent(anotherApproved);
});
});
context('when the address that receives the approval is the owner', function () {
it('reverts', async function () {
await expectRevert(
this.token.approve(owner, tokenId, { from: owner }), 'ERC721: approval to current owner',
);
});
});
context('when the sender does not own the given token ID', function () {
it('reverts', async function () {
await expectRevert(this.token.approve(approved, tokenId, { from: other }),
'ERC721: approve caller is not owner nor approved');
});
});
context('when the sender is approved for the given token ID', function () {
it('reverts', async function () {
await this.token.approve(approved, tokenId, { from: owner });
await expectRevert(this.token.approve(anotherApproved, tokenId, { from: approved }),
'ERC721: approve caller is not owner nor approved for all');
});
});
context('when the sender is an operator', function () {
beforeEach(async function () {
await this.token.setApprovalForAll(operator, true, { from: owner });
({ logs } = await this.token.approve(approved, tokenId, { from: operator }));
});
itApproves(approved);
itEmitsApprovalEvent(approved);
});
context('when the given token ID does not exist', function () {
it('reverts', async function () {
await expectRevert(this.token.approve(approved, nonExistentTokenId, { from: operator }),
'ERC721: owner query for nonexistent token');
});
});
});
describe('setApprovalForAll', function () {
context('when the operator willing to approve is not the owner', function () {
context('when there is no operator approval set by the sender', function () {
it('approves the operator', async function () {
await this.token.setApprovalForAll(operator, true, { from: owner });
expect(await this.token.isApprovedForAll(owner, operator)).to.equal(true);
});
it('emits an approval event', async function () {
const { logs } = await this.token.setApprovalForAll(operator, true, { from: owner });
expectEvent.inLogs(logs, 'ApprovalForAll', {
owner: owner,
operator: operator,
approved: true,
});
});
});
context('when the operator was set as not approved', function () {
beforeEach(async function () {
await this.token.setApprovalForAll(operator, false, { from: owner });
});
it('approves the operator', async function () {
await this.token.setApprovalForAll(operator, true, { from: owner });
expect(await this.token.isApprovedForAll(owner, operator)).to.equal(true);
});
it('emits an approval event', async function () {
const { logs } = await this.token.setApprovalForAll(operator, true, { from: owner });
expectEvent.inLogs(logs, 'ApprovalForAll', {
owner: owner,
operator: operator,
approved: true,
});
});
it('can unset the operator approval', async function () {
await this.token.setApprovalForAll(operator, false, { from: owner });
expect(await this.token.isApprovedForAll(owner, operator)).to.equal(false);
});
});
context('when the operator was already approved', function () {
beforeEach(async function () {
await this.token.setApprovalForAll(operator, true, { from: owner });
});
it('keeps the approval to the given address', async function () {
await this.token.setApprovalForAll(operator, true, { from: owner });
expect(await this.token.isApprovedForAll(owner, operator)).to.equal(true);
});
it('emits an approval event', async function () {
const { logs } = await this.token.setApprovalForAll(operator, true, { from: owner });
expectEvent.inLogs(logs, 'ApprovalForAll', {
owner: owner,
operator: operator,
approved: true,
});
});
});
});
context('when the operator is the owner', function () {
it('reverts', async function () {
await expectRevert(this.token.setApprovalForAll(owner, true, { from: owner }),
'ERC721: approve to caller');
});
});
});
describe('getApproved', async function () {
context('when token is not minted', async function () {
it('reverts', async function () {
await expectRevert(
this.token.getApproved(nonExistentTokenId),
'ERC721: approved query for nonexistent token',
);
});
});
context('when token has been minted ', async function () {
it('should return the zero address', async function () {
expect(await this.token.getApproved(firstTokenId)).to.be.equal(
ZERO_ADDRESS,
);
});
context('when account has been approved', async function () {
beforeEach(async function () {
await this.token.approve(approved, firstTokenId, { from: owner });
});
it('returns approved account', async function () {
expect(await this.token.getApproved(firstTokenId)).to.be.equal(approved);
});
});
});
});
});
describe('_mint(address, uint256)', function () {
it('reverts with a null destination address', async function () {
await expectRevert(
this.token.mint(ZERO_ADDRESS, firstTokenId), 'ERC721: mint to the zero address',
);
});
context('with minted token', async function () {
beforeEach(async function () {
({ logs: this.logs } = await this.token.mint(owner, firstTokenId));
});
it('emits a Transfer event', function () {
expectEvent.inLogs(this.logs, 'Transfer', { from: ZERO_ADDRESS, to: owner, tokenId: firstTokenId });
});
it('creates the token', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('1');
expect(await this.token.ownerOf(firstTokenId)).to.equal(owner);
});
it('reverts when adding a token id that already exists', async function () {
await expectRevert(this.token.mint(owner, firstTokenId), 'ERC721: token already minted');
});
});
});
describe('_burn', function () {
it('reverts when burning a non-existent token id', async function () {
await expectRevert(
this.token.burn(firstTokenId), 'ERC721: owner query for nonexistent token',
);
});
context('with minted tokens', function () {
beforeEach(async function () {
await this.token.mint(owner, firstTokenId);
await this.token.mint(owner, secondTokenId);
});
context('with burnt token', function () {
beforeEach(async function () {
({ logs: this.logs } = await this.token.burn(firstTokenId));
});
it('emits a Transfer event', function () {
expectEvent.inLogs(this.logs, 'Transfer', { from: owner, to: ZERO_ADDRESS, tokenId: firstTokenId });
});
it('emits an Approval event', function () {
expectEvent.inLogs(this.logs, 'Approval', { owner, approved: ZERO_ADDRESS, tokenId: firstTokenId });
});
it('deletes the token', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('1');
await expectRevert(
this.token.ownerOf(firstTokenId), 'ERC721: owner query for nonexistent token',
);
});
it('reverts when burning a token id that has been deleted', async function () {
await expectRevert(
this.token.burn(firstTokenId), 'ERC721: owner query for nonexistent token',
);
});
});
});
});
}
function shouldBehaveLikeERC721Enumerable (errorPrefix, owner, newOwner, approved, anotherApproved, operator, other) {
shouldSupportInterfaces([
'ERC721Enumerable',
]);
context('with minted tokens', function () {
beforeEach(async function () {
await this.token.mint(owner, firstTokenId);
await this.token.mint(owner, secondTokenId);
this.toWhom = other; // default to other for toWhom in context-dependent tests
});
describe('totalSupply', function () {
it('returns total token supply', async function () {
expect(await this.token.totalSupply()).to.be.bignumber.equal('2');
});
});
describe('tokenOfOwnerByIndex', function () {
describe('when the given index is lower than the amount of tokens owned by the given address', function () {
it('returns the token ID placed at the given index', async function () {
expect(await this.token.tokenOfOwnerByIndex(owner, 0)).to.be.bignumber.equal(firstTokenId);
});
});
describe('when the index is greater than or equal to the total tokens owned by the given address', function () {
it('reverts', async function () {
await expectRevert(
this.token.tokenOfOwnerByIndex(owner, 2), 'ERC721Enumerable: owner index out of bounds',
);
});
});
describe('when the given address does not own any token', function () {
it('reverts', async function () {
await expectRevert(
this.token.tokenOfOwnerByIndex(other, 0), 'ERC721Enumerable: owner index out of bounds',
);
});
});
describe('after transferring all tokens to another user', function () {
beforeEach(async function () {
await this.token.transferFrom(owner, other, firstTokenId, { from: owner });
await this.token.transferFrom(owner, other, secondTokenId, { from: owner });
});
it('returns correct token IDs for target', async function () {
expect(await this.token.balanceOf(other)).to.be.bignumber.equal('2');
const tokensListed = await Promise.all(
[0, 1].map(i => this.token.tokenOfOwnerByIndex(other, i)),
);
expect(tokensListed.map(t => t.toNumber())).to.have.members([firstTokenId.toNumber(),
secondTokenId.toNumber()]);
});
it('returns empty collection for original owner', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('0');
await expectRevert(
this.token.tokenOfOwnerByIndex(owner, 0), 'ERC721Enumerable: owner index out of bounds',
);
});
});
});
describe('tokenByIndex', function () {
it('returns all tokens', async function () {
const tokensListed = await Promise.all(
[0, 1].map(i => this.token.tokenByIndex(i)),
);
expect(tokensListed.map(t => t.toNumber())).to.have.members([firstTokenId.toNumber(),
secondTokenId.toNumber()]);
});
it('reverts if index is greater than supply', async function () {
await expectRevert(
this.token.tokenByIndex(2), 'ERC721Enumerable: global index out of bounds',
);
});
[firstTokenId, secondTokenId].forEach(function (tokenId) {
it(`returns all tokens after burning token ${tokenId} and minting new tokens`, async function () {
const newTokenId = new BN(300);
const anotherNewTokenId = new BN(400);
await this.token.burn(tokenId);
await this.token.mint(newOwner, newTokenId);
await this.token.mint(newOwner, anotherNewTokenId);
expect(await this.token.totalSupply()).to.be.bignumber.equal('3');
const tokensListed = await Promise.all(
[0, 1, 2].map(i => this.token.tokenByIndex(i)),
);
const expectedTokens = [firstTokenId, secondTokenId, newTokenId, anotherNewTokenId].filter(
x => (x !== tokenId),
);
expect(tokensListed.map(t => t.toNumber())).to.have.members(expectedTokens.map(t => t.toNumber()));
});
});
});
});
describe('_mint(address, uint256)', function () {
it('reverts with a null destination address', async function () {
await expectRevert(
this.token.mint(ZERO_ADDRESS, firstTokenId), 'ERC721: mint to the zero address',
);
});
context('with minted token', async function () {
beforeEach(async function () {
({ logs: this.logs } = await this.token.mint(owner, firstTokenId));
});
it('adjusts owner tokens by index', async function () {
expect(await this.token.tokenOfOwnerByIndex(owner, 0)).to.be.bignumber.equal(firstTokenId);
});
it('adjusts all tokens list', async function () {
expect(await this.token.tokenByIndex(0)).to.be.bignumber.equal(firstTokenId);
});
});
});
describe('_burn', function () {
it('reverts when burning a non-existent token id', async function () {
await expectRevert(
this.token.burn(firstTokenId), 'ERC721: owner query for nonexistent token',
);
});
context('with minted tokens', function () {
beforeEach(async function () {
await this.token.mint(owner, firstTokenId);
await this.token.mint(owner, secondTokenId);
});
context('with burnt token', function () {
beforeEach(async function () {
({ logs: this.logs } = await this.token.burn(firstTokenId));
});
it('removes that token from the token list of the owner', async function () {
expect(await this.token.tokenOfOwnerByIndex(owner, 0)).to.be.bignumber.equal(secondTokenId);
});
it('adjusts all tokens list', async function () {
expect(await this.token.tokenByIndex(0)).to.be.bignumber.equal(secondTokenId);
});
it('burns all tokens', async function () {
await this.token.burn(secondTokenId, { from: owner });
expect(await this.token.totalSupply()).to.be.bignumber.equal('0');
await expectRevert(
this.token.tokenByIndex(0), 'ERC721Enumerable: global index out of bounds',
);
});
});
});
});
}
function shouldBehaveLikeERC721Metadata (errorPrefix, name, symbol, owner) {
shouldSupportInterfaces([
'ERC721Metadata',
]);
describe('metadata', function () {
it('has a name', async function () {
expect(await this.token.name()).to.be.equal(name);
});
it('has a symbol', async function () {
expect(await this.token.symbol()).to.be.equal(symbol);
});
describe('token URI', function () {
beforeEach(async function () {
await this.token.mint(owner, firstTokenId);
});
it('return empty string by default', async function () {
expect(await this.token.tokenURI(firstTokenId)).to.be.equal('');
});
it('reverts when queried for non existent token id', async function () {
await expectRevert(
this.token.tokenURI(nonExistentTokenId), 'ERC721Metadata: URI query for nonexistent token',
);
});
describe('base URI', function () {
beforeEach(function () {
if (this.token.setBaseURI === undefined) {
this.skip();
}
});
it('base URI can be set', async function () {
await this.token.setBaseURI(baseURI);
expect(await this.token.baseURI()).to.equal(baseURI);
});
it('base URI is added as a prefix to the token URI', async function () {
await this.token.setBaseURI(baseURI);
expect(await this.token.tokenURI(firstTokenId)).to.be.equal(baseURI + firstTokenId.toString());
});
it('token URI can be changed by changing the base URI', async function () {
await this.token.setBaseURI(baseURI);
const newBaseURI = 'https://api.com/v2/';
await this.token.setBaseURI(newBaseURI);
expect(await this.token.tokenURI(firstTokenId)).to.be.equal(newBaseURI + firstTokenId.toString());
});
});
});
});
}
module.exports = {
shouldBehaveLikeERC721,
shouldBehaveLikeERC721Enumerable,
shouldBehaveLikeERC721Metadata,
};
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const { expect } = require('chai');
const { shouldSupportInterfaces } = require('../../introspection/SupportsInterface.behavior');
const {
shouldBehaveLikeERC721,
shouldBehaveLikeERC721Metadata,
} = require('./ERC721.behavior');
const ERC721Mock = artifacts.require('ERC721Mock');
const ERC721ReceiverMock = artifacts.require('ERC721ReceiverMock');
const Error = [ 'None', 'RevertWithMessage', 'RevertWithoutMessage', 'Panic' ]
.reduce((acc, entry, idx) => Object.assign({ [entry]: idx }, acc), {});
contract('ERC721', function (accounts) {
const [owner, newOwner, approved, anotherApproved, operator, other] = accounts;
const name = 'Non Fungible Token';
const symbol = 'NFT';
const firstTokenId = new BN('5042');
const secondTokenId = new BN('79217');
const nonExistentTokenId = new BN('13');
const RECEIVER_MAGIC_VALUE = '0x150b7a02';
beforeEach(async function () {
this.token = await ERC721Mock.new(name, symbol);
});
shouldSupportInterfaces([
'ERC165',
'ERC721',
'ERC721Enumerable',
'ERC721Metadata',
]);
describe('metadata', function () {
it('has a name', async function () {
expect(await this.token.name()).to.be.equal(name);
});
it('has a symbol', async function () {
expect(await this.token.symbol()).to.be.equal(symbol);
});
describe('token URI', function () {
beforeEach(async function () {
await this.token.mint(owner, firstTokenId);
});
const baseURI = 'https://api.com/v1/';
const sampleUri = 'mock://mytoken';
it('it is empty by default', async function () {
expect(await this.token.tokenURI(firstTokenId)).to.be.equal('');
});
it('reverts when queried for non existent token id', async function () {
await expectRevert(
this.token.tokenURI(nonExistentTokenId), 'ERC721Metadata: URI query for nonexistent token',
);
});
it('can be set for a token id', async function () {
await this.token.setTokenURI(firstTokenId, sampleUri);
expect(await this.token.tokenURI(firstTokenId)).to.be.equal(sampleUri);
});
it('reverts when setting for non existent token id', async function () {
await expectRevert(
this.token.setTokenURI(nonExistentTokenId, sampleUri), 'ERC721Metadata: URI set of nonexistent token',
);
});
it('base URI can be set', async function () {
await this.token.setBaseURI(baseURI);
expect(await this.token.baseURI()).to.equal(baseURI);
});
it('base URI is added as a prefix to the token URI', async function () {
await this.token.setBaseURI(baseURI);
await this.token.setTokenURI(firstTokenId, sampleUri);
expect(await this.token.tokenURI(firstTokenId)).to.be.equal(baseURI + sampleUri);
});
it('token URI can be changed by changing the base URI', async function () {
await this.token.setBaseURI(baseURI);
await this.token.setTokenURI(firstTokenId, sampleUri);
const newBaseURI = 'https://api.com/v2/';
await this.token.setBaseURI(newBaseURI);
expect(await this.token.tokenURI(firstTokenId)).to.be.equal(newBaseURI + sampleUri);
});
it('tokenId is appended to base URI for tokens with no URI', async function () {
await this.token.setBaseURI(baseURI);
expect(await this.token.tokenURI(firstTokenId)).to.be.equal(baseURI + firstTokenId);
});
it('tokens with URI can be burnt ', async function () {
await this.token.setTokenURI(firstTokenId, sampleUri);
await this.token.burn(firstTokenId, { from: owner });
expect(await this.token.exists(firstTokenId)).to.equal(false);
await expectRevert(
this.token.tokenURI(firstTokenId), 'ERC721Metadata: URI query for nonexistent token',
);
});
});
});
context('with minted tokens', function () {
beforeEach(async function () {
await this.token.mint(owner, firstTokenId);
await this.token.mint(owner, secondTokenId);
this.toWhom = other; // default to other for toWhom in context-dependent tests
});
describe('balanceOf', function () {
context('when the given address owns some tokens', function () {
it('returns the amount of tokens owned by the given address', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('2');
});
});
context('when the given address does not own any tokens', function () {
it('returns 0', async function () {
expect(await this.token.balanceOf(other)).to.be.bignumber.equal('0');
});
});
context('when querying the zero address', function () {
it('throws', async function () {
await expectRevert(
this.token.balanceOf(ZERO_ADDRESS), 'ERC721: balance query for the zero address',
);
});
});
});
describe('ownerOf', function () {
context('when the given token ID was tracked by this token', function () {
const tokenId = firstTokenId;
it('returns the owner of the given token ID', async function () {
expect(await this.token.ownerOf(tokenId)).to.be.equal(owner);
});
});
context('when the given token ID was not tracked by this token', function () {
const tokenId = nonExistentTokenId;
it('reverts', async function () {
await expectRevert(
this.token.ownerOf(tokenId), 'ERC721: owner query for nonexistent token',
);
});
});
});
describe('transfers', function () {
const tokenId = firstTokenId;
const data = '0x42';
let logs = null;
beforeEach(async function () {
await this.token.approve(approved, tokenId, { from: owner });
await this.token.setApprovalForAll(operator, true, { from: owner });
});
const transferWasSuccessful = function ({ owner, tokenId, approved }) {
it('transfers the ownership of the given token ID to the given address', async function () {
expect(await this.token.ownerOf(tokenId)).to.be.equal(this.toWhom);
});
it('emits a Transfer event', async function () {
expectEvent.inLogs(logs, 'Transfer', { from: owner, to: this.toWhom, tokenId: tokenId });
});
it('clears the approval for the token ID', async function () {
expect(await this.token.getApproved(tokenId)).to.be.equal(ZERO_ADDRESS);
});
it('emits an Approval event', async function () {
expectEvent.inLogs(logs, 'Approval', { owner, approved: ZERO_ADDRESS, tokenId: tokenId });
});
it('adjusts owners balances', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('1');
});
it('adjusts owners tokens by index', async function () {
if (!this.token.tokenOfOwnerByIndex) return;
expect(await this.token.tokenOfOwnerByIndex(this.toWhom, 0)).to.be.bignumber.equal(tokenId);
expect(await this.token.tokenOfOwnerByIndex(owner, 0)).to.be.bignumber.not.equal(tokenId);
});
};
const shouldTransferTokensByUsers = function (transferFunction) {
context('when called by the owner', function () {
beforeEach(async function () {
({ logs } = await transferFunction.call(this, owner, this.toWhom, tokenId, { from: owner }));
});
transferWasSuccessful({ owner, tokenId, approved });
});
context('when called by the approved individual', function () {
beforeEach(async function () {
({ logs } = await transferFunction.call(this, owner, this.toWhom, tokenId, { from: approved }));
});
transferWasSuccessful({ owner, tokenId, approved });
});
context('when called by the operator', function () {
beforeEach(async function () {
({ logs } = await transferFunction.call(this, owner, this.toWhom, tokenId, { from: operator }));
});
transferWasSuccessful({ owner, tokenId, approved });
});
context('when called by the owner without an approved user', function () {
beforeEach(async function () {
await this.token.approve(ZERO_ADDRESS, tokenId, { from: owner });
({ logs } = await transferFunction.call(this, owner, this.toWhom, tokenId, { from: operator }));
});
transferWasSuccessful({ owner, tokenId, approved: null });
});
context('when sent to the owner', function () {
beforeEach(async function () {
({ logs } = await transferFunction.call(this, owner, owner, tokenId, { from: owner }));
});
it('keeps ownership of the token', async function () {
expect(await this.token.ownerOf(tokenId)).to.be.equal(owner);
});
it('clears the approval for the token ID', async function () {
expect(await this.token.getApproved(tokenId)).to.be.equal(ZERO_ADDRESS);
});
it('emits only a transfer event', async function () {
expectEvent.inLogs(logs, 'Transfer', {
from: owner,
to: owner,
tokenId: tokenId,
});
});
it('keeps the owner balance', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('2');
});
it('keeps same tokens by index', async function () {
if (!this.token.tokenOfOwnerByIndex) return;
const tokensListed = await Promise.all(
[0, 1].map(i => this.token.tokenOfOwnerByIndex(owner, i)),
);
expect(tokensListed.map(t => t.toNumber())).to.have.members(
[firstTokenId.toNumber(), secondTokenId.toNumber()],
);
});
});
context('when the address of the previous owner is incorrect', function () {
it('reverts', async function () {
await expectRevert(
transferFunction.call(this, other, other, tokenId, { from: owner }),
'ERC721: transfer of token that is not own',
);
});
});
context('when the sender is not authorized for the token id', function () {
it('reverts', async function () {
await expectRevert(
transferFunction.call(this, owner, other, tokenId, { from: other }),
'ERC721: transfer caller is not owner nor approved',
);
});
});
context('when the given token ID does not exist', function () {
it('reverts', async function () {
await expectRevert(
transferFunction.call(this, owner, other, nonExistentTokenId, { from: owner }),
'ERC721: operator query for nonexistent token',
);
});
});
context('when the address to transfer the token to is the zero address', function () {
it('reverts', async function () {
await expectRevert(
transferFunction.call(this, owner, ZERO_ADDRESS, tokenId, { from: owner }),
'ERC721: transfer to the zero address',
);
});
});
};
describe('via transferFrom', function () {
shouldTransferTokensByUsers(function (from, to, tokenId, opts) {
return this.token.transferFrom(from, to, tokenId, opts);
});
});
describe('via safeTransferFrom', function () {
const safeTransferFromWithData = function (from, to, tokenId, opts) {
return this.token.methods['safeTransferFrom(address,address,uint256,bytes)'](from, to, tokenId, data, opts);
};
const safeTransferFromWithoutData = function (from, to, tokenId, opts) {
return this.token.methods['safeTransferFrom(address,address,uint256)'](from, to, tokenId, opts);
};
const shouldTransferSafely = function (transferFun, data) {
describe('to a user account', function () {
shouldTransferTokensByUsers(transferFun);
});
describe('to a valid receiver contract', function () {
beforeEach(async function () {
this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.None);
this.toWhom = this.receiver.address;
});
shouldTransferTokensByUsers(transferFun);
it('calls onERC721Received', async function () {
const receipt = await transferFun.call(this, owner, this.receiver.address, tokenId, { from: owner });
await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', {
operator: owner,
from: owner,
tokenId: tokenId,
data: data,
});
});
it('calls onERC721Received from approved', async function () {
const receipt = await transferFun.call(this, owner, this.receiver.address, tokenId, { from: approved });
await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', {
operator: approved,
from: owner,
tokenId: tokenId,
data: data,
});
});
describe('with an invalid token id', function () {
it('reverts', async function () {
await expectRevert(
transferFun.call(
this,
owner,
this.receiver.address,
nonExistentTokenId,
{ from: owner },
),
'ERC721: operator query for nonexistent token',
);
});
});
});
};
describe('with data', function () {
shouldTransferSafely(safeTransferFromWithData, data);
});
describe('without data', function () {
shouldTransferSafely(safeTransferFromWithoutData, null);
});
describe('to a receiver contract returning unexpected value', function () {
it('reverts', async function () {
const invalidReceiver = await ERC721ReceiverMock.new('0x42', Error.None);
await expectRevert(
this.token.safeTransferFrom(owner, invalidReceiver.address, tokenId, { from: owner }),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
describe('to a receiver contract that reverts with message', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithMessage);
await expectRevert(
this.token.safeTransferFrom(owner, revertingReceiver.address, tokenId, { from: owner }),
'ERC721ReceiverMock: reverting',
);
});
});
describe('to a receiver contract that reverts without message', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithoutMessage);
await expectRevert(
this.token.safeTransferFrom(owner, revertingReceiver.address, tokenId, { from: owner }),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
describe('to a receiver contract that panics', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.Panic);
await expectRevert.unspecified(
this.token.safeTransferFrom(owner, revertingReceiver.address, tokenId, { from: owner }),
);
});
});
describe('to a contract that does not implement the required function', function () {
it('reverts', async function () {
const nonReceiver = this.token;
await expectRevert(
this.token.safeTransferFrom(owner, nonReceiver.address, tokenId, { from: owner }),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
});
});
describe('safe mint', function () {
const fourthTokenId = new BN(4);
const tokenId = fourthTokenId;
const data = '0x42';
describe('via safeMint', function () { // regular minting is tested in ERC721Mintable.test.js and others
it('calls onERC721Received — with data', async function () {
this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.None);
const receipt = await this.token.safeMint(this.receiver.address, tokenId, data);
await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', {
from: ZERO_ADDRESS,
tokenId: tokenId,
data: data,
});
});
it('calls onERC721Received — without data', async function () {
this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.None);
const receipt = await this.token.safeMint(this.receiver.address, tokenId);
await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', {
from: ZERO_ADDRESS,
tokenId: tokenId,
});
});
context('to a receiver contract returning unexpected value', function () {
it('reverts', async function () {
const invalidReceiver = await ERC721ReceiverMock.new('0x42', Error.None);
await expectRevert(
this.token.safeMint(invalidReceiver.address, tokenId),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
context('to a receiver contract that reverts with message', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithMessage);
await expectRevert(
this.token.safeMint(revertingReceiver.address, tokenId),
'ERC721ReceiverMock: reverting',
);
});
});
context('to a receiver contract that reverts without message', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithoutMessage);
await expectRevert(
this.token.safeMint(revertingReceiver.address, tokenId),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
context('to a receiver contract that panics', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.Panic);
await expectRevert.unspecified(
this.token.safeMint(revertingReceiver.address, tokenId),
);
});
});
context('to a contract that does not implement the required function', function () {
it('reverts', async function () {
const nonReceiver = this.token;
await expectRevert(
this.token.safeMint(nonReceiver.address, tokenId),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
});
});
describe('approve', function () {
const tokenId = firstTokenId;
let logs = null;
const itClearsApproval = function () {
it('clears approval for the token', async function () {
expect(await this.token.getApproved(tokenId)).to.be.equal(ZERO_ADDRESS);
});
};
const itApproves = function (address) {
it('sets the approval for the target address', async function () {
expect(await this.token.getApproved(tokenId)).to.be.equal(address);
});
};
const itEmitsApprovalEvent = function (address) {
it('emits an approval event', async function () {
expectEvent.inLogs(logs, 'Approval', {
owner: owner,
approved: address,
tokenId: tokenId,
});
});
};
context('when clearing approval', function () {
context('when there was no prior approval', function () {
beforeEach(async function () {
({ logs } = await this.token.approve(ZERO_ADDRESS, tokenId, { from: owner }));
});
itClearsApproval();
itEmitsApprovalEvent(ZERO_ADDRESS);
});
context('when there was a prior approval', function () {
beforeEach(async function () {
await this.token.approve(approved, tokenId, { from: owner });
({ logs } = await this.token.approve(ZERO_ADDRESS, tokenId, { from: owner }));
});
itClearsApproval();
itEmitsApprovalEvent(ZERO_ADDRESS);
});
});
context('when approving a non-zero address', function () {
context('when there was no prior approval', function () {
beforeEach(async function () {
({ logs } = await this.token.approve(approved, tokenId, { from: owner }));
});
itApproves(approved);
itEmitsApprovalEvent(approved);
});
context('when there was a prior approval to the same address', function () {
beforeEach(async function () {
await this.token.approve(approved, tokenId, { from: owner });
({ logs } = await this.token.approve(approved, tokenId, { from: owner }));
});
itApproves(approved);
itEmitsApprovalEvent(approved);
});
context('when there was a prior approval to a different address', function () {
beforeEach(async function () {
await this.token.approve(anotherApproved, tokenId, { from: owner });
({ logs } = await this.token.approve(anotherApproved, tokenId, { from: owner }));
});
itApproves(anotherApproved);
itEmitsApprovalEvent(anotherApproved);
});
});
context('when the address that receives the approval is the owner', function () {
it('reverts', async function () {
await expectRevert(
this.token.approve(owner, tokenId, { from: owner }), 'ERC721: approval to current owner',
);
});
});
context('when the sender does not own the given token ID', function () {
it('reverts', async function () {
await expectRevert(this.token.approve(approved, tokenId, { from: other }),
'ERC721: approve caller is not owner nor approved');
});
});
context('when the sender is approved for the given token ID', function () {
it('reverts', async function () {
await this.token.approve(approved, tokenId, { from: owner });
await expectRevert(this.token.approve(anotherApproved, tokenId, { from: approved }),
'ERC721: approve caller is not owner nor approved for all');
});
});
context('when the sender is an operator', function () {
beforeEach(async function () {
await this.token.setApprovalForAll(operator, true, { from: owner });
({ logs } = await this.token.approve(approved, tokenId, { from: operator }));
});
itApproves(approved);
itEmitsApprovalEvent(approved);
});
context('when the given token ID does not exist', function () {
it('reverts', async function () {
await expectRevert(this.token.approve(approved, nonExistentTokenId, { from: operator }),
'ERC721: owner query for nonexistent token');
});
});
});
describe('setApprovalForAll', function () {
context('when the operator willing to approve is not the owner', function () {
context('when there is no operator approval set by the sender', function () {
it('approves the operator', async function () {
await this.token.setApprovalForAll(operator, true, { from: owner });
expect(await this.token.isApprovedForAll(owner, operator)).to.equal(true);
});
it('emits an approval event', async function () {
const { logs } = await this.token.setApprovalForAll(operator, true, { from: owner });
expectEvent.inLogs(logs, 'ApprovalForAll', {
owner: owner,
operator: operator,
approved: true,
});
});
});
context('when the operator was set as not approved', function () {
beforeEach(async function () {
await this.token.setApprovalForAll(operator, false, { from: owner });
});
it('approves the operator', async function () {
await this.token.setApprovalForAll(operator, true, { from: owner });
expect(await this.token.isApprovedForAll(owner, operator)).to.equal(true);
});
it('emits an approval event', async function () {
const { logs } = await this.token.setApprovalForAll(operator, true, { from: owner });
expectEvent.inLogs(logs, 'ApprovalForAll', {
owner: owner,
operator: operator,
approved: true,
});
});
it('can unset the operator approval', async function () {
await this.token.setApprovalForAll(operator, false, { from: owner });
expect(await this.token.isApprovedForAll(owner, operator)).to.equal(false);
});
});
context('when the operator was already approved', function () {
beforeEach(async function () {
await this.token.setApprovalForAll(operator, true, { from: owner });
});
it('keeps the approval to the given address', async function () {
await this.token.setApprovalForAll(operator, true, { from: owner });
expect(await this.token.isApprovedForAll(owner, operator)).to.equal(true);
});
it('emits an approval event', async function () {
const { logs } = await this.token.setApprovalForAll(operator, true, { from: owner });
expectEvent.inLogs(logs, 'ApprovalForAll', {
owner: owner,
operator: operator,
approved: true,
});
});
});
});
context('when the operator is the owner', function () {
it('reverts', async function () {
await expectRevert(this.token.setApprovalForAll(owner, true, { from: owner }),
'ERC721: approve to caller');
});
});
});
describe('getApproved', async function () {
context('when token is not minted', async function () {
it('reverts', async function () {
await expectRevert(
this.token.getApproved(nonExistentTokenId),
'ERC721: approved query for nonexistent token',
);
});
});
context('when token has been minted ', async function () {
it('should return the zero address', async function () {
expect(await this.token.getApproved(firstTokenId)).to.be.equal(
ZERO_ADDRESS,
);
});
context('when account has been approved', async function () {
beforeEach(async function () {
await this.token.approve(approved, firstTokenId, { from: owner });
});
it('returns approved account', async function () {
expect(await this.token.getApproved(firstTokenId)).to.be.equal(approved);
});
});
});
});
describe('totalSupply', function () {
it('returns total token supply', async function () {
expect(await this.token.totalSupply()).to.be.bignumber.equal('2');
});
});
describe('tokenOfOwnerByIndex', function () {
describe('when the given index is lower than the amount of tokens owned by the given address', function () {
it('returns the token ID placed at the given index', async function () {
expect(await this.token.tokenOfOwnerByIndex(owner, 0)).to.be.bignumber.equal(firstTokenId);
});
});
describe('when the index is greater than or equal to the total tokens owned by the given address', function () {
it('reverts', async function () {
await expectRevert(
this.token.tokenOfOwnerByIndex(owner, 2), 'EnumerableSet: index out of bounds',
);
});
});
describe('when the given address does not own any token', function () {
it('reverts', async function () {
await expectRevert(
this.token.tokenOfOwnerByIndex(other, 0), 'EnumerableSet: index out of bounds',
);
});
});
describe('after transferring all tokens to another user', function () {
beforeEach(async function () {
await this.token.transferFrom(owner, other, firstTokenId, { from: owner });
await this.token.transferFrom(owner, other, secondTokenId, { from: owner });
});
it('returns correct token IDs for target', async function () {
expect(await this.token.balanceOf(other)).to.be.bignumber.equal('2');
const tokensListed = await Promise.all(
[0, 1].map(i => this.token.tokenOfOwnerByIndex(other, i)),
);
expect(tokensListed.map(t => t.toNumber())).to.have.members([firstTokenId.toNumber(),
secondTokenId.toNumber()]);
});
it('returns empty collection for original owner', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('0');
await expectRevert(
this.token.tokenOfOwnerByIndex(owner, 0), 'EnumerableSet: index out of bounds',
);
});
});
});
describe('tokenByIndex', function () {
it('returns all tokens', async function () {
const tokensListed = await Promise.all(
[0, 1].map(i => this.token.tokenByIndex(i)),
);
expect(tokensListed.map(t => t.toNumber())).to.have.members([firstTokenId.toNumber(),
secondTokenId.toNumber()]);
});
it('reverts if index is greater than supply', async function () {
await expectRevert(
this.token.tokenByIndex(2), 'EnumerableSet: index out of bounds',
);
});
[firstTokenId, secondTokenId].forEach(function (tokenId) {
it(`returns all tokens after burning token ${tokenId} and minting new tokens`, async function () {
const newTokenId = new BN(300);
const anotherNewTokenId = new BN(400);
await this.token.burn(tokenId);
await this.token.mint(newOwner, newTokenId);
await this.token.mint(newOwner, anotherNewTokenId);
expect(await this.token.totalSupply()).to.be.bignumber.equal('3');
const tokensListed = await Promise.all(
[0, 1, 2].map(i => this.token.tokenByIndex(i)),
);
const expectedTokens = [firstTokenId, secondTokenId, newTokenId, anotherNewTokenId].filter(
x => (x !== tokenId),
);
expect(tokensListed.map(t => t.toNumber())).to.have.members(expectedTokens.map(t => t.toNumber()));
});
});
});
});
describe('_mint(address, uint256)', function () {
it('reverts with a null destination address', async function () {
await expectRevert(
this.token.mint(ZERO_ADDRESS, firstTokenId), 'ERC721: mint to the zero address',
);
});
context('with minted token', async function () {
beforeEach(async function () {
({ logs: this.logs } = await this.token.mint(owner, firstTokenId));
});
it('emits a Transfer event', function () {
expectEvent.inLogs(this.logs, 'Transfer', { from: ZERO_ADDRESS, to: owner, tokenId: firstTokenId });
});
it('creates the token', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('1');
expect(await this.token.ownerOf(firstTokenId)).to.equal(owner);
});
it('adjusts owner tokens by index', async function () {
expect(await this.token.tokenOfOwnerByIndex(owner, 0)).to.be.bignumber.equal(firstTokenId);
});
it('adjusts all tokens list', async function () {
expect(await this.token.tokenByIndex(0)).to.be.bignumber.equal(firstTokenId);
});
it('reverts when adding a token id that already exists', async function () {
await expectRevert(this.token.mint(owner, firstTokenId), 'ERC721: token already minted');
});
});
});
describe('_burn', function () {
it('reverts when burning a non-existent token id', async function () {
await expectRevert(
this.token.burn(firstTokenId), 'ERC721: owner query for nonexistent token',
);
});
context('with minted tokens', function () {
beforeEach(async function () {
await this.token.mint(owner, firstTokenId);
await this.token.mint(owner, secondTokenId);
});
context('with burnt token', function () {
beforeEach(async function () {
({ logs: this.logs } = await this.token.burn(firstTokenId));
});
it('emits a Transfer event', function () {
expectEvent.inLogs(this.logs, 'Transfer', { from: owner, to: ZERO_ADDRESS, tokenId: firstTokenId });
});
it('emits an Approval event', function () {
expectEvent.inLogs(this.logs, 'Approval', { owner, approved: ZERO_ADDRESS, tokenId: firstTokenId });
});
it('deletes the token', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('1');
await expectRevert(
this.token.ownerOf(firstTokenId), 'ERC721: owner query for nonexistent token',
);
});
it('removes that token from the token list of the owner', async function () {
expect(await this.token.tokenOfOwnerByIndex(owner, 0)).to.be.bignumber.equal(secondTokenId);
});
it('adjusts all tokens list', async function () {
expect(await this.token.tokenByIndex(0)).to.be.bignumber.equal(secondTokenId);
});
it('burns all tokens', async function () {
await this.token.burn(secondTokenId, { from: owner });
expect(await this.token.totalSupply()).to.be.bignumber.equal('0');
await expectRevert(
this.token.tokenByIndex(0), 'EnumerableSet: index out of bounds',
);
});
it('reverts when burning a token id that has been deleted', async function () {
await expectRevert(
this.token.burn(firstTokenId), 'ERC721: owner query for nonexistent token',
);
});
});
});
});
shouldBehaveLikeERC721('ERC721', ...accounts);
shouldBehaveLikeERC721Metadata('ERC721', name, symbol, ...accounts);
});
const {
shouldBehaveLikeERC721,
shouldBehaveLikeERC721Metadata,
shouldBehaveLikeERC721Enumerable,
} = require('./ERC721.behavior');
const ERC721Mock = artifacts.require('ERC721EnumerableMock');
contract('ERC721Enumerable', function (accounts) {
const name = 'Non Fungible Token';
const symbol = 'NFT';
beforeEach(async function () {
this.token = await ERC721Mock.new(name, symbol);
});
shouldBehaveLikeERC721('ERC721', ...accounts);
shouldBehaveLikeERC721Metadata('ERC721', name, symbol, ...accounts);
shouldBehaveLikeERC721Enumerable('ERC721', ...accounts);
});
......@@ -27,10 +27,6 @@ contract('ERC721PresetMinterPauserAutoId', function (accounts) {
expect(await this.token.symbol()).to.equal(symbol);
});
it('token has correct base URI', async function () {
expect(await this.token.baseURI()).to.equal(baseURI);
});
it('deployer has the default admin role', async function () {
expect(await this.token.getRoleMemberCount(DEFAULT_ADMIN_ROLE)).to.be.bignumber.equal('1');
expect(await this.token.getRoleMember(DEFAULT_ADMIN_ROLE, 0)).to.equal(deployer);
......
const { expectRevert } = require('@openzeppelin/test-helpers');
const { toEthSignedMessageHash, fixSignature } = require('../helpers/sign');
const { toEthSignedMessageHash, fixSignature } = require('../../helpers/sign');
const { expect } = require('chai');
......
require('@openzeppelin/test-helpers');
const { MerkleTree } = require('../helpers/merkleTree.js');
const { MerkleTree } = require('../../helpers/merkleTree.js');
const { keccakFromString, bufferToHex } = require('ethereumjs-util');
const { expect } = require('chai');
......
const ethSigUtil = require('eth-sig-util');
const Wallet = require('ethereumjs-wallet').default;
const { EIP712Domain, domainSeparator } = require('../helpers/eip712');
const { EIP712Domain, domainSeparator } = require('../../helpers/eip712');
const EIP712 = artifacts.require('EIP712External');
......
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