Compare commits
29 Commits
v4.0.0-bet
...
v4.0.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ee939e7c4 | |||
| 59f33c1cc1 | |||
| d104ced953 | |||
| 1fd54698ff | |||
| 27fc833550 | |||
| 0b3e0d74b0 | |||
| d75b4cf613 | |||
| b1e0aa487d | |||
| 6505e28c40 | |||
| f07c39be8a | |||
| 69ca2ad676 | |||
| ae1e384a9a | |||
| 954f6110d6 | |||
| 4390b8df12 | |||
| cb88e15b33 | |||
| 136de91049 | |||
| e2bf45f262 | |||
| 93d990c653 | |||
| 3dfd02b4b4 | |||
| 7a7bd8f6d7 | |||
| 16312fcfb9 | |||
| a81a88cca0 | |||
| 5acedf5027 | |||
| 566c601d41 | |||
| 15214a53ce | |||
| d5f4862405 | |||
| 4a1985f870 | |||
| ac8279a0a5 | |||
| 7cab19a2e4 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -54,6 +54,6 @@ allFiredEvents
|
||||
.coverage_cache
|
||||
.coverage_contracts
|
||||
|
||||
# buidler
|
||||
# hardhat
|
||||
cache
|
||||
artifacts
|
||||
|
||||
24
CHANGELOG.md
24
CHANGELOG.md
@ -4,16 +4,26 @@
|
||||
|
||||
* Now targeting the 0.8.x line of Solidity compilers. For 0.6.x (resp 0.7.x) support, use version 3.4.0 (resp 3.4.0-solc-0.7) of OpenZeppelin.
|
||||
* `Context`: making `_msgData` return `bytes calldata` instead of `bytes memory` ([#2492](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2492))
|
||||
* `ERC20`: Removed the `_setDecimals` function and the storage slot associated to decimals. ([#2502](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2502))
|
||||
* `ERC20`: removed the `_setDecimals` function and the storage slot associated to decimals. ([#2502](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2502))
|
||||
* `Strings`: addition of a `toHexString` function. ([#2504](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2504))
|
||||
* `EnumerableMap`: change implementation to optimize for `key → value` lookups instead of enumeration. ([#2518](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2518))
|
||||
* `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))
|
||||
* `GSN`: deprecate GSNv1 support in favor of upcoming 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 behavior 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))
|
||||
* Overall reorganization of the contract folder to improve clarity and discoverability. ([#2503](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2503))
|
||||
* `ERC20Capped`: optimize gas usage by enforcing the check directly in `_mint`. ([#2524](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2524))
|
||||
* Rename `UpgradeableProxy` to `ERC1967Proxy`. ([#2547](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2547))
|
||||
* `ERC777`: optimize the gas costs of the constructor. ([#2551](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2551))
|
||||
* `ERC721URIStorage`: add a new extension that implements the `_setTokenURI` behavior as it was available in 3.4.0. ([#2555](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2555))
|
||||
* `AccessControl`: added ERC165 interface detection. ([#2562](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2562))
|
||||
* `ERC1155`: make `uri` public so overloading function can call it using super. ([#2576](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2576))
|
||||
|
||||
### Bug fixes for beta releases
|
||||
|
||||
* `AccessControlEnumerable`: Fixed `renounceRole` not updating enumerable set of addresses for a role. ([#2572](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2572))
|
||||
|
||||
### How to upgrade from 3.x
|
||||
|
||||
@ -25,6 +35,10 @@ 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.
|
||||
|
||||
### How to upgrade from 4.0-beta.x
|
||||
|
||||
Some further changes have been done between the different beta iterations. Transitions made during this period are configured in the `migrate-imports` script. Consequently, you can upgrade from any previous 4.0-beta.x version using the same script as described in the *How to upgrade from 3.x* section.
|
||||
|
||||
## 3.4.0 (2021-02-02)
|
||||
|
||||
* `BeaconProxy`: added new kind of proxy that allows simultaneous atomic upgrades. ([#2411](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2411))
|
||||
|
||||
@ -3,6 +3,18 @@
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../utils/Context.sol";
|
||||
import "../utils/introspection/ERC165.sol";
|
||||
|
||||
/**
|
||||
* @dev External interface of AccessControl declared to support ERC165 detection.
|
||||
*/
|
||||
interface IAccessControl {
|
||||
function hasRole(bytes32 role, address account) external view returns (bool);
|
||||
function getRoleAdmin(bytes32 role) external view returns (bytes32);
|
||||
function grantRole(bytes32 role, address account) external;
|
||||
function revokeRole(bytes32 role, address account) external;
|
||||
function renounceRole(bytes32 role, address account) external;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Contract module that allows children to implement role-based access
|
||||
@ -42,7 +54,7 @@ import "../utils/Context.sol";
|
||||
* grant and revoke this role. Extra precautions should be taken to secure
|
||||
* accounts that have been granted it.
|
||||
*/
|
||||
abstract contract AccessControl is Context {
|
||||
abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
struct RoleData {
|
||||
mapping (address => bool) members;
|
||||
bytes32 adminRole;
|
||||
@ -79,10 +91,18 @@ abstract contract AccessControl is Context {
|
||||
*/
|
||||
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
|
||||
|
||||
/**
|
||||
* @dev See {IERC165-supportsInterface}.
|
||||
*/
|
||||
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
||||
return interfaceId == type(IAccessControl).interfaceId
|
||||
|| super.supportsInterface(interfaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns `true` if `account` has been granted `role`.
|
||||
*/
|
||||
function hasRole(bytes32 role, address account) public view returns (bool) {
|
||||
function hasRole(bytes32 role, address account) public view override returns (bool) {
|
||||
return _roles[role].members[account];
|
||||
}
|
||||
|
||||
@ -92,7 +112,7 @@ abstract contract AccessControl is Context {
|
||||
*
|
||||
* To change a role's admin, use {_setRoleAdmin}.
|
||||
*/
|
||||
function getRoleAdmin(bytes32 role) public view returns (bytes32) {
|
||||
function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
|
||||
return _roles[role].adminRole;
|
||||
}
|
||||
|
||||
@ -106,7 +126,7 @@ abstract contract AccessControl is Context {
|
||||
*
|
||||
* - the caller must have ``role``'s admin role.
|
||||
*/
|
||||
function grantRole(bytes32 role, address account) public virtual {
|
||||
function grantRole(bytes32 role, address account) public virtual override {
|
||||
require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to grant");
|
||||
|
||||
_grantRole(role, account);
|
||||
@ -121,7 +141,7 @@ abstract contract AccessControl is Context {
|
||||
*
|
||||
* - the caller must have ``role``'s admin role.
|
||||
*/
|
||||
function revokeRole(bytes32 role, address account) public virtual {
|
||||
function revokeRole(bytes32 role, address account) public virtual override {
|
||||
require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to revoke");
|
||||
|
||||
_revokeRole(role, account);
|
||||
@ -141,7 +161,7 @@ abstract contract AccessControl is Context {
|
||||
*
|
||||
* - the caller must be `account`.
|
||||
*/
|
||||
function renounceRole(bytes32 role, address account) public virtual {
|
||||
function renounceRole(bytes32 role, address account) public virtual override {
|
||||
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
|
||||
|
||||
_revokeRole(role, account);
|
||||
|
||||
@ -5,14 +5,30 @@ pragma solidity ^0.8.0;
|
||||
import "./AccessControl.sol";
|
||||
import "../utils/structs/EnumerableSet.sol";
|
||||
|
||||
/**
|
||||
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
|
||||
*/
|
||||
interface IAccessControlEnumerable {
|
||||
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
|
||||
function getRoleMemberCount(bytes32 role) external view returns (uint256);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
|
||||
*/
|
||||
abstract contract AccessControlEnumerable is AccessControl {
|
||||
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
|
||||
using EnumerableSet for EnumerableSet.AddressSet;
|
||||
|
||||
mapping (bytes32 => EnumerableSet.AddressSet) private _roleMembers;
|
||||
|
||||
/**
|
||||
* @dev See {IERC165-supportsInterface}.
|
||||
*/
|
||||
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
||||
return interfaceId == type(IAccessControlEnumerable).interfaceId
|
||||
|| super.supportsInterface(interfaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns one of the accounts that have `role`. `index` must be a
|
||||
* value between 0 and {getRoleMemberCount}, non-inclusive.
|
||||
@ -25,7 +41,7 @@ abstract contract AccessControlEnumerable is AccessControl {
|
||||
* 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) {
|
||||
function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {
|
||||
return _roleMembers[role].at(index);
|
||||
}
|
||||
|
||||
@ -33,7 +49,7 @@ abstract contract AccessControlEnumerable is AccessControl {
|
||||
* @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) {
|
||||
function getRoleMemberCount(bytes32 role) public view override returns (uint256) {
|
||||
return _roleMembers[role].length();
|
||||
}
|
||||
|
||||
@ -53,6 +69,14 @@ abstract contract AccessControlEnumerable is AccessControl {
|
||||
_roleMembers[role].remove(account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Overload {renounceRole} to track enumerable memberships
|
||||
*/
|
||||
function renounceRole(bytes32 role, address account) public virtual override {
|
||||
super.renounceRole(role, account);
|
||||
_roleMembers[role].remove(account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Overload {_setupRole} to track enumerable memberships
|
||||
*/
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "./Address.sol";
|
||||
import "./Context.sol";
|
||||
import "./math/SafeMath.sol";
|
||||
import "../utils/Address.sol";
|
||||
import "../utils/Context.sol";
|
||||
import "../utils/math/SafeMath.sol";
|
||||
|
||||
/**
|
||||
* @title PaymentSplitter
|
||||
10
contracts/finance/README.adoc
Normal file
10
contracts/finance/README.adoc
Normal file
@ -0,0 +1,10 @@
|
||||
= Finance
|
||||
|
||||
[.readme-notice]
|
||||
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/finance
|
||||
|
||||
This directory includes primitives for financial systems. We currently only offer the {PaymentSplitter} contract, but we want to grow this directory so we welcome ideas.
|
||||
|
||||
== PaymentSplitter
|
||||
|
||||
{{PaymentSplitter}}
|
||||
@ -1,7 +1,7 @@
|
||||
= Governance
|
||||
|
||||
[.readme-notice]
|
||||
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/access
|
||||
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/governance
|
||||
|
||||
This directory includes primitives for on-chain governance. We currently only offer the {TimelockController} contract, that can be used as a component in a governance systems to introduce a delay between a proposal and its execution.
|
||||
|
||||
@ -26,7 +26,7 @@ This directory includes primitives for on-chain governance. We currently only of
|
||||
[[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.
|
||||
Operation executed by the xref:api:governance.adoc#TimelockController[`TimelockControler`] can contain one or multiple subsequent calls. Depending on whether you need to multiple calls to be executed atomically, you can either use simple or batched operations.
|
||||
|
||||
Both operations contain:
|
||||
|
||||
@ -50,16 +50,16 @@ Timelocked operations are identified by a unique id (their hash) and follow a sp
|
||||
|
||||
`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.
|
||||
* By calling xref:api:governance.adoc#TimelockController-schedule-address-uint256-bytes-bytes32-bytes32-uint256-[`schedule`] (or xref:api:governance.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:governance.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.
|
||||
* By calling xref:api:governance.adoc#TimelockController-TimelockController-execute-address-uint256-bytes-bytes32-bytes32-[`execute`] (or xref:api:governance.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:governance.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)`]
|
||||
* xref:api:governance.adoc#TimelockController-isOperationPending-bytes32-[`isOperationPending(bytes32)`]
|
||||
* xref:api:governance.adoc#TimelockController-isOperationReady-bytes32-[`isOperationReady(bytes32)`]
|
||||
* xref:api:governance.adoc#TimelockController-isOperationDone-bytes32-[`isOperationDone(bytes32)`]
|
||||
|
||||
[[timelock-roles]]
|
||||
==== Roles
|
||||
|
||||
@ -11,16 +11,16 @@ contract ClonesMock {
|
||||
|
||||
event NewInstance(address instance);
|
||||
|
||||
function clone(address master, bytes calldata initdata) public payable {
|
||||
_initAndEmit(master.clone(), initdata);
|
||||
function clone(address implementation, bytes calldata initdata) public payable {
|
||||
_initAndEmit(implementation.clone(), initdata);
|
||||
}
|
||||
|
||||
function cloneDeterministic(address master, bytes32 salt, bytes calldata initdata) public payable {
|
||||
_initAndEmit(master.cloneDeterministic(salt), initdata);
|
||||
function cloneDeterministic(address implementation, bytes32 salt, bytes calldata initdata) public payable {
|
||||
_initAndEmit(implementation.cloneDeterministic(salt), initdata);
|
||||
}
|
||||
|
||||
function predictDeterministicAddress(address master, bytes32 salt) public view returns (address predicted) {
|
||||
return master.predictDeterministicAddress(salt);
|
||||
function predictDeterministicAddress(address implementation, bytes32 salt) public view returns (address predicted) {
|
||||
return implementation.predictDeterministicAddress(salt);
|
||||
}
|
||||
|
||||
function _initAndEmit(address instance, bytes memory initdata) private {
|
||||
|
||||
@ -7,7 +7,19 @@ import "../token/ERC721/extensions/ERC721Burnable.sol";
|
||||
contract ERC721BurnableMock is ERC721Burnable {
|
||||
constructor(string memory name, string memory symbol) ERC721(name, symbol) { }
|
||||
|
||||
function exists(uint256 tokenId) public view returns (bool) {
|
||||
return _exists(tokenId);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,6 +25,10 @@ contract ERC721EnumerableMock is ERC721Enumerable {
|
||||
return _baseURI();
|
||||
}
|
||||
|
||||
function exists(uint256 tokenId) public view returns (bool) {
|
||||
return _exists(tokenId);
|
||||
}
|
||||
|
||||
function mint(address to, uint256 tokenId) public {
|
||||
_mint(to, tokenId);
|
||||
}
|
||||
|
||||
@ -11,18 +11,6 @@ import "../token/ERC721/extensions/ERC721Pausable.sol";
|
||||
contract ERC721PausableMock is ERC721Pausable {
|
||||
constructor (string memory name, string memory symbol) ERC721(name, symbol) { }
|
||||
|
||||
function mint(address to, uint256 tokenId) public {
|
||||
super._mint(to, tokenId);
|
||||
}
|
||||
|
||||
function burn(uint256 tokenId) public {
|
||||
super._burn(tokenId);
|
||||
}
|
||||
|
||||
function exists(uint256 tokenId) public view returns (bool) {
|
||||
return super._exists(tokenId);
|
||||
}
|
||||
|
||||
function pause() external {
|
||||
_pause();
|
||||
}
|
||||
@ -30,4 +18,24 @@ contract ERC721PausableMock is ERC721Pausable {
|
||||
function unpause() external {
|
||||
_unpause();
|
||||
}
|
||||
|
||||
function exists(uint256 tokenId) public view returns (bool) {
|
||||
return _exists(tokenId);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
51
contracts/mocks/ERC721URIStorageMock.sol
Normal file
51
contracts/mocks/ERC721URIStorageMock.sol
Normal file
@ -0,0 +1,51 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../token/ERC721/extensions/ERC721URIStorage.sol";
|
||||
|
||||
/**
|
||||
* @title ERC721Mock
|
||||
* This mock just provides a public safeMint, mint, and burn functions for testing purposes
|
||||
*/
|
||||
contract ERC721URIStorageMock is ERC721URIStorage {
|
||||
string private _baseTokenURI;
|
||||
|
||||
constructor (string memory name, string memory symbol) ERC721(name, symbol) { }
|
||||
|
||||
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 setTokenURI(uint256 tokenId, string memory _tokenURI) public {
|
||||
_setTokenURI(tokenId, _tokenURI);
|
||||
}
|
||||
|
||||
function exists(uint256 tokenId) public view returns (bool) {
|
||||
return _exists(tokenId);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../utils/Initializable.sol";
|
||||
import "../proxy/utils/Initializable.sol";
|
||||
|
||||
/**
|
||||
* @title InitializableMock
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../utils/Initializable.sol";
|
||||
import "../proxy/utils/Initializable.sol";
|
||||
|
||||
// Sample contracts showing upgradeability with multiple inheritance.
|
||||
// Child contract inherits from Father and Mother contracts, and Father extends from Gramps.
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../utils/Initializable.sol";
|
||||
import "../proxy/utils/Initializable.sol";
|
||||
|
||||
contract Implementation1 is Initializable {
|
||||
uint internal _value;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../utils/Initializable.sol";
|
||||
import "../proxy/utils/Initializable.sol";
|
||||
|
||||
/**
|
||||
* @title MigratableMockV1
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
{
|
||||
"name": "@openzeppelin/contracts",
|
||||
"description": "Secure Smart Contract library for Solidity",
|
||||
"version": "4.0.0-beta.0",
|
||||
"version": "4.0.0-rc.0",
|
||||
"files": [
|
||||
"**/*.sol",
|
||||
"/build/contracts/*.json",
|
||||
"!/mocks",
|
||||
"!/examples"
|
||||
"!/mocks/**/*"
|
||||
],
|
||||
"scripts": {
|
||||
"prepare": "bash ../scripts/prepare-contracts-package.sh",
|
||||
|
||||
@ -17,16 +17,16 @@ pragma solidity ^0.8.0;
|
||||
*/
|
||||
library Clones {
|
||||
/**
|
||||
* @dev Deploys and returns the address of a clone that mimics the behaviour of `master`.
|
||||
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
|
||||
*
|
||||
* This function uses the create opcode, which should never revert.
|
||||
*/
|
||||
function clone(address master) internal returns (address instance) {
|
||||
function clone(address implementation) internal returns (address instance) {
|
||||
// solhint-disable-next-line no-inline-assembly
|
||||
assembly {
|
||||
let ptr := mload(0x40)
|
||||
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
|
||||
mstore(add(ptr, 0x14), shl(0x60, master))
|
||||
mstore(add(ptr, 0x14), shl(0x60, implementation))
|
||||
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
|
||||
instance := create(0, ptr, 0x37)
|
||||
}
|
||||
@ -34,18 +34,18 @@ library Clones {
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Deploys and returns the address of a clone that mimics the behaviour of `master`.
|
||||
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
|
||||
*
|
||||
* This function uses the create2 opcode and a `salt` to deterministically deploy
|
||||
* the clone. Using the same `master` and `salt` multiple time will revert, since
|
||||
* the clone. Using the same `implementation` and `salt` multiple time will revert, since
|
||||
* the clones cannot be deployed twice at the same address.
|
||||
*/
|
||||
function cloneDeterministic(address master, bytes32 salt) internal returns (address instance) {
|
||||
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
|
||||
// solhint-disable-next-line no-inline-assembly
|
||||
assembly {
|
||||
let ptr := mload(0x40)
|
||||
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
|
||||
mstore(add(ptr, 0x14), shl(0x60, master))
|
||||
mstore(add(ptr, 0x14), shl(0x60, implementation))
|
||||
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
|
||||
instance := create2(0, ptr, 0x37, salt)
|
||||
}
|
||||
@ -55,12 +55,12 @@ library Clones {
|
||||
/**
|
||||
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
|
||||
*/
|
||||
function predictDeterministicAddress(address master, bytes32 salt, address deployer) internal pure returns (address predicted) {
|
||||
function predictDeterministicAddress(address implementation, bytes32 salt, address deployer) internal pure returns (address predicted) {
|
||||
// solhint-disable-next-line no-inline-assembly
|
||||
assembly {
|
||||
let ptr := mload(0x40)
|
||||
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
|
||||
mstore(add(ptr, 0x14), shl(0x60, master))
|
||||
mstore(add(ptr, 0x14), shl(0x60, implementation))
|
||||
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
|
||||
mstore(add(ptr, 0x38), shl(0x60, deployer))
|
||||
mstore(add(ptr, 0x4c), salt)
|
||||
@ -72,7 +72,7 @@ library Clones {
|
||||
/**
|
||||
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
|
||||
*/
|
||||
function predictDeterministicAddress(address master, bytes32 salt) internal view returns (address predicted) {
|
||||
return predictDeterministicAddress(master, salt, address(this));
|
||||
function predictDeterministicAddress(address implementation, bytes32 salt) internal view returns (address predicted) {
|
||||
return predictDeterministicAddress(implementation, salt, address(this));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "./Proxy.sol";
|
||||
import "../utils/Address.sol";
|
||||
import "../Proxy.sol";
|
||||
import "../../utils/Address.sol";
|
||||
|
||||
/**
|
||||
* @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
|
||||
@ -14,7 +14,7 @@ import "../utils/Address.sol";
|
||||
* Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see
|
||||
* {TransparentUpgradeableProxy}.
|
||||
*/
|
||||
contract UpgradeableProxy is Proxy {
|
||||
contract ERC1967Proxy is Proxy {
|
||||
/**
|
||||
* @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
|
||||
*
|
||||
@ -66,7 +66,7 @@ contract UpgradeableProxy is Proxy {
|
||||
* @dev Stores a new address in the EIP1967 implementation slot.
|
||||
*/
|
||||
function _setImplementation(address newImplementation) private {
|
||||
require(Address.isContract(newImplementation), "UpgradeableProxy: new implementation is not a contract");
|
||||
require(Address.isContract(newImplementation), "ERC1967Proxy: new implementation is not a contract");
|
||||
|
||||
bytes32 slot = _IMPLEMENTATION_SLOT;
|
||||
|
||||
@ -7,19 +7,21 @@ This is a low-level set of contracts implementing different proxy patterns with
|
||||
|
||||
The abstract {Proxy} contract implements the core delegation functionality. If the concrete proxies that we provide below are not suitable, we encourage building on top of this base contract since it contains an assembly block that may be hard to get right.
|
||||
|
||||
Upgradeability is implemented in the {UpgradeableProxy} contract, although it provides only an internal upgrade interface. For an upgrade interface exposed externally to an admin, we provide {TransparentUpgradeableProxy}. Both of these contracts use the storage slots specified in https://eips.ethereum.org/EIPS/eip-1967[EIP1967] to avoid clashes with the storage of the implementation contract behind the proxy.
|
||||
{ERC1967Proxy} provides a simple, fully functioning, proxy. While this proxy is not by itself upgradeable, it includes an internal upgrade interface. For an upgrade interface exposed externally to an admin, we provide {TransparentUpgradeableProxy}. Both of these contracts use the storage slots specified in https://eips.ethereum.org/EIPS/eip-1967[EIP1967] to avoid clashes with the storage of the implementation contract behind the proxy.
|
||||
|
||||
An alternative upgradeability mechanism is provided in <<Beacon>>. This pattern, popularized by Dharma, allows multiple proxies to be upgraded to a different implementation in a single transaction. In this pattern, the proxy contract doesn't hold the implementation address in storage like {UpgradeableProxy}, but the address of a {UpgradeableBeacon} contract, which is where the implementation address is actually stored and retrieved from. The `upgrade` operations that change the implementation contract address are then sent to the beacon instead of to the proxy contract, and all proxies that follow that beacon are automatically upgraded.
|
||||
|
||||
The {Clones} library provides a way to deploy minimal non-upgradeable proxies for cheap. This can be useful for applications that require deploying many instances of the same contract (for example one per user, or one per task). These instances are designed to be both cheap to deploy, and cheap to call. The drawback being that they are not upgradeable.
|
||||
|
||||
CAUTION: Using upgradeable proxies correctly and securely is a difficult task that requires deep knowledge of the proxy pattern, Solidity, and the EVM. Unless you want a lot of low level control, we recommend using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins] for Truffle and Buidler.
|
||||
CAUTION: Using upgradeable proxies correctly and securely is a difficult task that requires deep knowledge of the proxy pattern, Solidity, and the EVM. Unless you want a lot of low level control, we recommend using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins] for Truffle and Hardhat.
|
||||
|
||||
== Core
|
||||
|
||||
{{Proxy}}
|
||||
|
||||
{{UpgradeableProxy}}
|
||||
== ERC1967
|
||||
|
||||
{{ERC1967Proxy}}
|
||||
|
||||
== Transparent Proxy
|
||||
|
||||
@ -38,3 +40,7 @@ CAUTION: Using upgradeable proxies correctly and securely is a difficult task th
|
||||
== Minimal Clones
|
||||
|
||||
{{Clones}}
|
||||
|
||||
== Utils
|
||||
|
||||
{{Initializable}}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../UpgradeableProxy.sol";
|
||||
import "../ERC1967/ERC1967Proxy.sol";
|
||||
|
||||
/**
|
||||
* @dev This contract implements a proxy that is upgradeable by an admin.
|
||||
@ -25,12 +25,12 @@ import "../UpgradeableProxy.sol";
|
||||
* Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
|
||||
* you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
|
||||
*/
|
||||
contract TransparentUpgradeableProxy is UpgradeableProxy {
|
||||
contract TransparentUpgradeableProxy is ERC1967Proxy {
|
||||
/**
|
||||
* @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
|
||||
* optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}.
|
||||
*/
|
||||
constructor(address _logic, address admin_, bytes memory _data) payable UpgradeableProxy(_logic, _data) {
|
||||
constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
|
||||
assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
|
||||
_setAdmin(admin_);
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// solhint-disable-next-line compiler-version
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "./Address.sol";
|
||||
import "../../utils/Address.sol";
|
||||
|
||||
/**
|
||||
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
|
||||
@ -55,7 +55,7 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
|
||||
* Clients calling this function must replace the `\{id\}` substring with the
|
||||
* actual token type ID.
|
||||
*/
|
||||
function uri(uint256) external view virtual override returns (string memory) {
|
||||
function uri(uint256) public view virtual override returns (string memory) {
|
||||
return _uri;
|
||||
}
|
||||
|
||||
|
||||
@ -89,6 +89,13 @@ contract ERC1155PresetMinterPauser is Context, AccessControlEnumerable, ERC1155B
|
||||
_unpause();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC165-supportsInterface}.
|
||||
*/
|
||||
function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, ERC1155) returns (bool) {
|
||||
return super.supportsInterface(interfaceId);
|
||||
}
|
||||
|
||||
function _beforeTokenTransfer(
|
||||
address operator,
|
||||
address from,
|
||||
|
||||
@ -27,17 +27,10 @@ abstract contract ERC20Capped is ERC20 {
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {ERC20-_beforeTokenTransfer}.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - minted tokens must not cause the total supply to go over the cap.
|
||||
* @dev See {ERC20-_mint}.
|
||||
*/
|
||||
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
|
||||
super._beforeTokenTransfer(from, to, amount);
|
||||
|
||||
if (from == address(0)) { // When minting tokens
|
||||
require(totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
|
||||
}
|
||||
function _mint(address account, uint256 amount) internal virtual override {
|
||||
require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
|
||||
super._mint(account, amount);
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,7 +223,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
|
||||
* @dev Safely mints `tokenId` and transfers it to `to`.
|
||||
*
|
||||
* Requirements:
|
||||
d*
|
||||
*
|
||||
* - `tokenId` must not exist.
|
||||
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
|
||||
*
|
||||
|
||||
@ -39,6 +39,8 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
|
||||
|
||||
{{ERC721Burnable}}
|
||||
|
||||
{{ERC721TokenUri}}
|
||||
|
||||
== 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.
|
||||
|
||||
66
contracts/token/ERC721/extensions/ERC721URIStorage.sol
Normal file
66
contracts/token/ERC721/extensions/ERC721URIStorage.sol
Normal file
@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../ERC721.sol";
|
||||
|
||||
/**
|
||||
* @dev ERC721 token with storage based token uri management.
|
||||
*/
|
||||
abstract contract ERC721URIStorage is ERC721 {
|
||||
using Strings for uint256;
|
||||
|
||||
// Optional mapping for token URIs
|
||||
mapping (uint256 => string) private _tokenURIs;
|
||||
|
||||
/**
|
||||
* @dev See {IERC721Metadata-tokenURI}.
|
||||
*/
|
||||
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
|
||||
require(_exists(tokenId), "ERC721URIStorage: 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));
|
||||
}
|
||||
|
||||
return super.tokenURI(tokenId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `tokenId` must exist.
|
||||
*/
|
||||
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
|
||||
require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
|
||||
_tokenURIs[tokenId] = _tokenURI;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Destroys `tokenId`.
|
||||
* The approval is cleared when the token is burned.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `tokenId` must exist.
|
||||
*
|
||||
* Emits a {Transfer} event.
|
||||
*/
|
||||
function _burn(uint256 tokenId) internal virtual override {
|
||||
super._burn(tokenId);
|
||||
|
||||
if (bytes(_tokenURIs[tokenId]).length != 0) {
|
||||
delete _tokenURIs[tokenId];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,7 +110,7 @@ contract ERC721PresetMinterPauserAutoId is Context, AccessControlEnumerable, ERC
|
||||
/**
|
||||
* @dev See {IERC165-supportsInterface}.
|
||||
*/
|
||||
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC721Enumerable) returns (bool) {
|
||||
function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, ERC721, ERC721Enumerable) returns (bool) {
|
||||
return super.supportsInterface(interfaceId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,8 +65,8 @@ contract ERC777 is Context, IERC777, IERC20 {
|
||||
_symbol = symbol_;
|
||||
|
||||
_defaultOperatorsArray = defaultOperators_;
|
||||
for (uint256 i = 0; i < _defaultOperatorsArray.length; i++) {
|
||||
_defaultOperators[_defaultOperatorsArray[i]] = true;
|
||||
for (uint256 i = 0; i < defaultOperators_.length; i++) {
|
||||
_defaultOperators[defaultOperators_[i]] = true;
|
||||
}
|
||||
|
||||
// register interfaces
|
||||
|
||||
@ -40,6 +40,14 @@ Finally, {Create2} contains all necessary utilities to safely use the https://bl
|
||||
|
||||
{{EIP712}}
|
||||
|
||||
== Escrow
|
||||
|
||||
{{ConditionalEscrow}}
|
||||
|
||||
{{Escrow}}
|
||||
|
||||
{{RefundEscrow}}
|
||||
|
||||
== 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_.
|
||||
@ -84,7 +92,3 @@ Note that, in all cases, accounts simply _declare_ their interfaces, but they ar
|
||||
{{Counters}}
|
||||
|
||||
{{Strings}}
|
||||
|
||||
== Other
|
||||
|
||||
{{Initializable}}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: contracts
|
||||
title: Contracts
|
||||
version: 3.x
|
||||
version: 4.x
|
||||
nav:
|
||||
- modules/ROOT/nav.adoc
|
||||
- modules/api/nav.adoc
|
||||
|
||||
@ -190,15 +190,15 @@ for (let i = 0; i < minterCount; ++i) {
|
||||
|
||||
Access control is essential to prevent unauthorized access to critical functions. These functions may be used to mint tokens, freeze transfers or perform an upgrade that completely changes the smart contract logic. While xref:api:access.adoc#Ownable[`Ownable`] and xref:api:access.adoc#AccessControl[`AccessControl`] can prevent unauthorized access, they do not address the issue of a misbehaving administrator attacking their own system to the prejudice of their users.
|
||||
|
||||
This is the issue the xref:api:access.adoc#TimelockController[`TimelockControler`] is addressing.
|
||||
This is the issue the xref:api:governance.adoc#TimelockController[`TimelockController`] is addressing.
|
||||
|
||||
The xref:api:access.adoc#TimelockController[`TimelockControler`] is a proxy that is governed by proposers and executors. When set as the owner/admin/controller of a smart contract, it ensures that whichever maintenance operation is ordered by the proposers is subject to a delay. This delay protects the users of the smart contract by giving them time to review the maintenance operation and exit the system if they consider it is in their best interest to do so.
|
||||
The xref:api:governance.adoc#TimelockController[`TimelockController`] is a proxy that is governed by proposers and executors. When set as the owner/admin/controller of a smart contract, it ensures that whichever maintenance operation is ordered by the proposers is subject to a delay. This delay protects the users of the smart contract by giving them time to review the maintenance operation and exit the system if they consider it is in their best interest to do so.
|
||||
|
||||
=== Using `TimelockControler`
|
||||
=== Using `TimelockController`
|
||||
|
||||
By default, the address that deployed the xref:api:access.adoc#TimelockController[`TimelockControler`] gets administration privileges over the timelock. This role grants the right to assign proposers, executors, and other administrators.
|
||||
By default, the address that deployed the xref:api:governance.adoc#TimelockController[`TimelockController`] gets administration privileges over the timelock. This role grants the right to assign proposers, executors, and other administrators.
|
||||
|
||||
The first step in configuring the xref:api:access.adoc#TimelockController[`TimelockControler`] is to assign at least one proposer and one executor. These can be assigned during construction or later by anyone with the administrator role. These roles are not exclusive, meaning an account can have both roles.
|
||||
The first step in configuring the xref:api:governance.adoc#TimelockController[`TimelockController`] is to assign at least one proposer and one executor. These can be assigned during construction or later by anyone with the administrator role. These roles are not exclusive, meaning an account can have both roles.
|
||||
|
||||
Roles are managed using the xref:api:access.adoc#AccessControl[`AccessControl`] interface and the `bytes32` values for each role are accessible through the `ADMIN_ROLE`, `PROPOSER_ROLE` and `EXECUTOR_ROLE` constants.
|
||||
|
||||
@ -216,6 +216,6 @@ TIP: A recommended configuration is to grant both roles to a secure governance c
|
||||
|
||||
=== Minimum delay
|
||||
|
||||
Operations executed by the xref:api:access.adoc#TimelockController[`TimelockControler`] are not subject to a fixed delay but rather a minimum delay. Some major updates might call for a longer delay. For example, if a delay of just a few days might be sufficient for users to audit a minting operation, it makes sense to use a delay of a few weeks, or even a few months, when scheduling a smart contract upgrade.
|
||||
Operations executed by the xref:api:governance.adoc#TimelockController[`TimelockController`] are not subject to a fixed delay but rather a minimum delay. Some major updates might call for a longer delay. For example, if a delay of just a few days might be sufficient for users to audit a minting operation, it makes sense to use a delay of a few weeks, or even a few months, when scheduling a smart contract upgrade.
|
||||
|
||||
The minimum delay (accessible through the xref:api:access.adoc#TimelockController-getMinDelay--[`getMinDelay`] method) can be updated by calling the xref:api:access.adoc#TimelockController-updateDelay-uint256-[`updateDelay`] function. Bear in mind that access to this function is only accessible by the timelock itself, meaning this maintenance operation has to go through the timelock itself.
|
||||
The minimum delay (accessible through the xref:api:governance.adoc#TimelockController-getMinDelay--[`getMinDelay`] method) can be updated by calling the xref:api:governance.adoc#TimelockController-updateDelay-uint256-[`updateDelay`] function. Bear in mind that access to this function is only accessible by the timelock itself, meaning this maintenance operation has to go through the timelock itself.
|
||||
|
||||
@ -11,6 +11,7 @@ for (const f of fs.readdirSync(path.join(__dirname, 'hardhat'))) {
|
||||
}
|
||||
|
||||
const enableGasReport = !!process.env.ENABLE_GAS_REPORT;
|
||||
const enableProduction = process.env.COMPILE_MODE === 'production';
|
||||
|
||||
/**
|
||||
* @type import('hardhat/config').HardhatUserConfig
|
||||
@ -20,7 +21,7 @@ module.exports = {
|
||||
version: '0.8.0',
|
||||
settings: {
|
||||
optimizer: {
|
||||
enabled: enableGasReport,
|
||||
enabled: enableGasReport || enableProduction,
|
||||
runs: 200,
|
||||
},
|
||||
},
|
||||
|
||||
17066
package-lock.json
generated
17066
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,11 @@
|
||||
{
|
||||
"name": "openzeppelin-solidity",
|
||||
"description": "Secure Smart Contract library for Solidity",
|
||||
"version": "4.0.0-beta.0",
|
||||
"version": "4.0.0-rc.0",
|
||||
"files": [
|
||||
"/contracts/**/*.sol",
|
||||
"/build/contracts/*.json",
|
||||
"!/contracts/mocks",
|
||||
"/test/behaviors"
|
||||
"!/contracts/mocks/**/*"
|
||||
],
|
||||
"bin": {
|
||||
"openzeppelin-contracts-migrate-imports": "scripts/migrate-imports.js"
|
||||
@ -23,7 +22,7 @@
|
||||
"lint:js:fix": "eslint --ignore-path .gitignore . --fix",
|
||||
"lint:sol": "echo 'solidity linter currently disabled' # solhint --max-warnings 0 \"contracts/**/*.sol\"",
|
||||
"prepublish": "rimraf build contracts/build artifacts cache",
|
||||
"prepare": "npm run compile",
|
||||
"prepare": "env COMPILE_MODE=production npm run compile",
|
||||
"prepack": "scripts/prepack.sh",
|
||||
"release": "scripts/release/release.sh",
|
||||
"version": "scripts/release/version.sh",
|
||||
|
||||
@ -12,12 +12,20 @@ const files = proc.execFileSync(
|
||||
|
||||
console.log('.API');
|
||||
|
||||
function getPageTitle (directory) {
|
||||
if (directory === 'metatx') {
|
||||
return 'Meta Transactions';
|
||||
} else {
|
||||
return startCase(directory);
|
||||
}
|
||||
}
|
||||
|
||||
const links = files.map((file) => {
|
||||
const doc = file.replace(baseDir, '');
|
||||
const title = path.parse(file).name;
|
||||
|
||||
return {
|
||||
xref: `* xref:${doc}[${startCase(title)}]`,
|
||||
xref: `* xref:${doc}[${getPageTitle(title)}]`,
|
||||
title,
|
||||
};
|
||||
});
|
||||
|
||||
@ -30,7 +30,8 @@ const pathUpdates = {
|
||||
'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/PaymentSplitter.sol': 'finance/PaymentSplitter.sol',
|
||||
'utils/PaymentSplitter.sol': 'finance/PaymentSplitter.sol',
|
||||
'payment/PullPayment.sol': 'security/PullPayment.sol',
|
||||
'presets/ERC1155PresetMinterPauser.sol': 'token/ERC1155/presets/ERC1155PresetMinterPauser.sol',
|
||||
'presets/ERC20PresetFixedSupply.sol': 'token/ERC20/presets/ERC20PresetFixedSupply.sol',
|
||||
@ -40,7 +41,8 @@ const pathUpdates = {
|
||||
'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/Initializable.sol': 'proxy/utils/Initializable.sol',
|
||||
'utils/Initializable.sol': 'proxy/utils/Initializable.sol',
|
||||
'proxy/ProxyAdmin.sol': 'proxy/transparent/ProxyAdmin.sol',
|
||||
// 'proxy/Proxy.sol': undefined,
|
||||
'proxy/TransparentUpgradeableProxy.sol': 'proxy/transparent/TransparentUpgradeableProxy.sol',
|
||||
@ -163,6 +165,7 @@ function getUpgradeablePath (file) {
|
||||
module.exports = {
|
||||
pathUpdates,
|
||||
updateImportPaths,
|
||||
getUpgradeablePath,
|
||||
};
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
@ -23,23 +23,21 @@ const ignorePatternsSubtrees = ignorePatterns
|
||||
.concat(ignorePatterns.map(pat => path.join(pat, '**/*')))
|
||||
.map(p => p.replace(/^\//, ''));
|
||||
|
||||
const artifactsDir = 'build/contracts';
|
||||
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';
|
||||
|
||||
let n = 0;
|
||||
|
||||
for (const sourcePath in solcOutput.contracts) {
|
||||
const ignore = match.any(sourcePath, ignorePatternsSubtrees);
|
||||
if (ignore) {
|
||||
for (const contract in solcOutput.contracts[sourcePath]) {
|
||||
fs.unlinkSync(path.join(artifactsDir, contract + '.json'));
|
||||
n += 1;
|
||||
for (const filename of filenames) {
|
||||
const solcOutput = readJSON(path.join(buildinfo, filename)).output;
|
||||
for (const sourcePath in solcOutput.contracts) {
|
||||
const ignore = match.any(sourcePath, ignorePatternsSubtrees);
|
||||
if (ignore) {
|
||||
for (const contract in solcOutput.contracts[sourcePath]) {
|
||||
fs.unlinkSync(path.join(artifactsDir, contract + '.json'));
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
|
||||
const { expect } = require('chai');
|
||||
|
||||
const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior');
|
||||
|
||||
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) {
|
||||
shouldSupportInterfaces(['AccessControl']);
|
||||
|
||||
describe('default admin', function () {
|
||||
it('deployer has default admin role', async function () {
|
||||
expect(await this.accessControl.hasRole(DEFAULT_ADMIN_ROLE, admin)).to.equal(true);
|
||||
@ -156,6 +160,8 @@ function shouldBehaveLikeAccessControl (errorPrefix, admin, authorized, other, o
|
||||
}
|
||||
|
||||
function shouldBehaveLikeAccessControlEnumerable (errorPrefix, admin, authorized, other, otherAdmin, otherAuthorized) {
|
||||
shouldSupportInterfaces(['AccessControlEnumerable']);
|
||||
|
||||
describe('enumerating', function () {
|
||||
it('role bearers can be enumerated', async function () {
|
||||
await this.accessControl.grantRole(ROLE, authorized, { from: admin });
|
||||
@ -173,6 +179,13 @@ function shouldBehaveLikeAccessControlEnumerable (errorPrefix, admin, authorized
|
||||
|
||||
expect(bearers).to.have.members([authorized, otherAuthorized]);
|
||||
});
|
||||
it('role enumeration should be in sync after renounceRole call', async function () {
|
||||
expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('0');
|
||||
await this.accessControl.grantRole(ROLE, admin, { from: admin });
|
||||
expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('1');
|
||||
await this.accessControl.renounceRole(ROLE, admin, { from: admin });
|
||||
expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('0');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -2,12 +2,16 @@ const path = require('path');
|
||||
const { promises: fs, constants: { F_OK } } = require('fs');
|
||||
const { expect } = require('chai');
|
||||
|
||||
const { pathUpdates, updateImportPaths } = require('../scripts/migrate-imports.js');
|
||||
const { pathUpdates, updateImportPaths, getUpgradeablePath } = 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);
|
||||
try {
|
||||
await fs.access(path.join('contracts', p), F_OK);
|
||||
} catch (e) {
|
||||
await fs.access(path.join('contracts', getUpgradeablePath(p)), F_OK);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
13
test/proxy/ERC1967/ERC1967Proxy.test.js
Normal file
13
test/proxy/ERC1967/ERC1967Proxy.test.js
Normal file
@ -0,0 +1,13 @@
|
||||
const shouldBehaveLikeProxy = require('../Proxy.behaviour');
|
||||
|
||||
const ERC1967Proxy = artifacts.require('ERC1967Proxy');
|
||||
|
||||
contract('ERC1967Proxy', function (accounts) {
|
||||
const [proxyAdminOwner] = accounts;
|
||||
|
||||
const createProxy = async function (implementation, _admin, initData, opts) {
|
||||
return ERC1967Proxy.new(implementation, initData, opts);
|
||||
};
|
||||
|
||||
shouldBehaveLikeProxy(createProxy, undefined, proxyAdminOwner);
|
||||
});
|
||||
@ -11,7 +11,7 @@ function toChecksumAddress (address) {
|
||||
return ethereumjsUtil.toChecksumAddress('0x' + address.replace(/^0x/, '').padStart(40, '0'));
|
||||
}
|
||||
|
||||
module.exports = function shouldBehaveLikeUpgradeableProxy (createProxy, proxyAdminAddress, proxyCreator) {
|
||||
module.exports = function shouldBehaveLikeProxy (createProxy, proxyAdminAddress, proxyCreator) {
|
||||
it('cannot be initialized with a non-contract address', async function () {
|
||||
const nonContractAddress = proxyCreator;
|
||||
const initializeData = Buffer.from('');
|
||||
@ -1,13 +0,0 @@
|
||||
const shouldBehaveLikeUpgradeableProxy = require('./UpgradeableProxy.behaviour');
|
||||
|
||||
const UpgradeableProxy = artifacts.require('UpgradeableProxy');
|
||||
|
||||
contract('UpgradeableProxy', function (accounts) {
|
||||
const [proxyAdminOwner] = accounts;
|
||||
|
||||
const createProxy = async function (implementation, _admin, initData, opts) {
|
||||
return UpgradeableProxy.new(implementation, initData, opts);
|
||||
};
|
||||
|
||||
shouldBehaveLikeUpgradeableProxy(createProxy, undefined, proxyAdminOwner);
|
||||
});
|
||||
@ -80,7 +80,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro
|
||||
it('reverts', async function () {
|
||||
await expectRevert(
|
||||
this.proxy.upgradeTo(ZERO_ADDRESS, { from }),
|
||||
'UpgradeableProxy: new implementation is not a contract',
|
||||
'ERC1967Proxy: new implementation is not a contract',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const shouldBehaveLikeUpgradeableProxy = require('../UpgradeableProxy.behaviour');
|
||||
const shouldBehaveLikeProxy = require('../Proxy.behaviour');
|
||||
const shouldBehaveLikeTransparentUpgradeableProxy = require('./TransparentUpgradeableProxy.behaviour');
|
||||
|
||||
const TransparentUpgradeableProxy = artifacts.require('TransparentUpgradeableProxy');
|
||||
@ -10,6 +10,6 @@ contract('TransparentUpgradeableProxy', function (accounts) {
|
||||
return TransparentUpgradeableProxy.new(logic, admin, initData, opts);
|
||||
};
|
||||
|
||||
shouldBehaveLikeUpgradeableProxy(createProxy, proxyAdminAddress, proxyAdminOwner);
|
||||
shouldBehaveLikeProxy(createProxy, proxyAdminAddress, proxyAdminOwner);
|
||||
shouldBehaveLikeTransparentUpgradeableProxy(createProxy, accounts);
|
||||
});
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
|
||||
const { ZERO_ADDRESS } = constants;
|
||||
const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior');
|
||||
|
||||
const { expect } = require('chai');
|
||||
|
||||
@ -24,6 +25,8 @@ contract('ERC1155PresetMinterPauser', function (accounts) {
|
||||
this.token = await ERC1155PresetMinterPauser.new(uri, { from: deployer });
|
||||
});
|
||||
|
||||
shouldSupportInterfaces(['ERC1155', 'AccessControl', 'AccessControlEnumerable']);
|
||||
|
||||
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);
|
||||
|
||||
87
test/token/ERC721/extensions/ERC721URIStorage.test.js
Normal file
87
test/token/ERC721/extensions/ERC721URIStorage.test.js
Normal file
@ -0,0 +1,87 @@
|
||||
const { BN, expectRevert } = require('@openzeppelin/test-helpers');
|
||||
|
||||
const { expect } = require('chai');
|
||||
|
||||
const ERC721URIStorageMock = artifacts.require('ERC721URIStorageMock');
|
||||
|
||||
contract('ERC721URIStorage', function (accounts) {
|
||||
const [ owner ] = accounts;
|
||||
|
||||
const name = 'Non Fungible Token';
|
||||
const symbol = 'NFT';
|
||||
|
||||
const firstTokenId = new BN('5042');
|
||||
const nonExistentTokenId = new BN('13');
|
||||
|
||||
beforeEach(async function () {
|
||||
this.token = await ERC721URIStorageMock.new(name, 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), 'ERC721URIStorage: 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), 'ERC721URIStorage: 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), 'ERC721URIStorage: URI query for nonexistent token',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,5 +1,6 @@
|
||||
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
|
||||
const { ZERO_ADDRESS } = constants;
|
||||
const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior');
|
||||
|
||||
const { expect } = require('chai');
|
||||
|
||||
@ -19,6 +20,8 @@ contract('ERC721PresetMinterPauserAutoId', function (accounts) {
|
||||
this.token = await ERC721PresetMinterPauserAutoId.new(name, symbol, baseURI, { from: deployer });
|
||||
});
|
||||
|
||||
shouldSupportInterfaces(['ERC721', 'ERC721Enumerable', 'AccessControl', 'AccessControlEnumerable']);
|
||||
|
||||
it('token has correct name', async function () {
|
||||
expect(await this.token.name()).to.equal(name);
|
||||
});
|
||||
|
||||
@ -39,6 +39,17 @@ const INTERFACES = {
|
||||
'onERC1155Received(address,address,uint256,uint256,bytes)',
|
||||
'onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)',
|
||||
],
|
||||
AccessControl: [
|
||||
'hasRole(bytes32,address)',
|
||||
'getRoleAdmin(bytes32)',
|
||||
'grantRole(bytes32,address)',
|
||||
'revokeRole(bytes32,address)',
|
||||
'renounceRole(bytes32,address)',
|
||||
],
|
||||
AccessControlEnumerable: [
|
||||
'getRoleMember(bytes32,uint256)',
|
||||
'getRoleMemberCount(bytes32)',
|
||||
],
|
||||
};
|
||||
|
||||
const INTERFACE_IDS = {};
|
||||
@ -54,7 +65,7 @@ for (const k of Object.getOwnPropertyNames(INTERFACES)) {
|
||||
function shouldSupportInterfaces (interfaces = []) {
|
||||
describe('Contract interface', function () {
|
||||
beforeEach(function () {
|
||||
this.contractUnderTest = this.mock || this.token || this.holder;
|
||||
this.contractUnderTest = this.mock || this.token || this.holder || this.accessControl;
|
||||
});
|
||||
|
||||
for (const k of interfaces) {
|
||||
|
||||
Reference in New Issue
Block a user