From ad90dbdaff982ecd557b1a16284d1df6f3f5a8a9 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 16 Apr 2021 18:14:24 +0200 Subject: [PATCH 01/11] 4.1.0-rc.0 --- CHANGELOG.md | 2 +- contracts/package.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e39b92d66..76c408d21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 4.1.0 * `IERC20Metadata`: add a new extended interface that includes the optional `name()`, `symbol()` and `decimals()` functions. ([#2561](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2561)) * `ERC777`: make reception acquirement optional in `_mint`. ([#2552](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2552)) diff --git a/contracts/package.json b/contracts/package.json index b32ba1507..562497a4a 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,7 +1,7 @@ { "name": "@openzeppelin/contracts", "description": "Secure Smart Contract library for Solidity", - "version": "4.0.0", + "version": "4.1.0-rc.0", "files": [ "**/*.sol", "/build/contracts/*.json", diff --git a/package-lock.json b/package-lock.json index 9c6eda7bb..69976ff2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "openzeppelin-solidity", - "version": "4.0.0", + "version": "4.1.0-rc.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "openzeppelin-solidity", - "version": "4.0.0", + "version": "4.1.0-rc.0", "license": "MIT", "bin": { "openzeppelin-contracts-migrate-imports": "scripts/migrate-imports.js" diff --git a/package.json b/package.json index c6644ba38..5000bf0dc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "openzeppelin-solidity", "description": "Secure Smart Contract library for Solidity", - "version": "4.0.0", + "version": "4.1.0-rc.0", "files": [ "/contracts/**/*.sol", "/build/contracts/*.json", From 7360a2532d4dc7a5d8bdb48deef08eef0a39c8f3 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 16 Apr 2021 20:37:54 +0200 Subject: [PATCH 02/11] Add missing changelog entry (cherry picked from commit 72eb905c5803556a44f5a3a920db852f910c6863) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76c408d21..4cd099c4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ * `Multicall`: add abstract contract with `multicall(bytes[] calldata data)` function to bundle multiple calls together ([#2608](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2608)) * `ECDSA`: add support for ERC2098 short-signatures. ([#2582](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2582)) * `AccessControl`: add a `onlyRole` modifier to restrict specific function to callers bearing a specific role. ([#2609](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2609)) + * `StorageSlot`: add a library for reading and writing primitive types to specific storage slots. ([#2542](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2542)) + * UUPS Proxies: add `UUPSUpgradeable` to implement the UUPS proxy pattern together with `EIP1967Proxy`. ([#2542](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2542)) ### Breaking changes From 2fa86736ddc3e0947838ab313c7d3e30c00a18b3 Mon Sep 17 00:00:00 2001 From: Uwe Voelker Date: Sat, 17 Apr 2021 01:07:09 +0200 Subject: [PATCH 03/11] typo: remove duplicate word (#2631) (cherry picked from commit f17624194b55b43a49b7a10e355d7f658f0799a1) --- docs/modules/ROOT/pages/erc20.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc20.adoc b/docs/modules/ROOT/pages/erc20.adoc index 9f2c7bd9e..11b650a1e 100644 --- a/docs/modules/ROOT/pages/erc20.adoc +++ b/docs/modules/ROOT/pages/erc20.adoc @@ -70,7 +70,7 @@ function decimals() public view virtual override returns (uint8) { } ``` -So if you want to send `5` tokens using a token contract with 18 decimals, the the method to call will actually be: +So if you want to send `5` tokens using a token contract with 18 decimals, the method to call will actually be: ```solidity transfer(recipient, 5 * 10^18); From 915109b2c43cf8bc8246b338e60291cb39296475 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 19 Apr 2021 20:44:10 +0200 Subject: [PATCH 04/11] Move UUPSUpgradeable to proxy/utils (cherry picked from commit b9125001f0a1c44d596ca3a47536f1a467e3a29d) --- contracts/mocks/UUPS/TestInProd.sol | 2 +- contracts/proxy/ERC1967/ERC1967Upgrade.sol | 2 +- contracts/proxy/utils/Initializable.sol | 2 -- contracts/proxy/{UUPS => utils}/UUPSUpgradeable.sol | 0 4 files changed, 2 insertions(+), 4 deletions(-) rename contracts/proxy/{UUPS => utils}/UUPSUpgradeable.sol (100%) diff --git a/contracts/mocks/UUPS/TestInProd.sol b/contracts/mocks/UUPS/TestInProd.sol index 66bfbea37..13339ec7a 100644 --- a/contracts/mocks/UUPS/TestInProd.sol +++ b/contracts/mocks/UUPS/TestInProd.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import "../CountersImpl.sol"; -import "../../proxy/UUPS/UUPSUpgradeable.sol"; +import "../../proxy/utils/UUPSUpgradeable.sol"; contract UUPSUpgradeableMock is CountersImpl, UUPSUpgradeable { // Not having any checks in this function is dangerous! Do not do this outside tests! diff --git a/contracts/proxy/ERC1967/ERC1967Upgrade.sol b/contracts/proxy/ERC1967/ERC1967Upgrade.sol index e93166634..828fb5934 100644 --- a/contracts/proxy/ERC1967/ERC1967Upgrade.sol +++ b/contracts/proxy/ERC1967/ERC1967Upgrade.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.2; import "./ERC1967Storage.sol"; diff --git a/contracts/proxy/utils/Initializable.sol b/contracts/proxy/utils/Initializable.sol index 592b514b7..af397bfca 100644 --- a/contracts/proxy/utils/Initializable.sol +++ b/contracts/proxy/utils/Initializable.sol @@ -3,8 +3,6 @@ // solhint-disable-next-line compiler-version pragma solidity ^0.8.0; -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 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an diff --git a/contracts/proxy/UUPS/UUPSUpgradeable.sol b/contracts/proxy/utils/UUPSUpgradeable.sol similarity index 100% rename from contracts/proxy/UUPS/UUPSUpgradeable.sol rename to contracts/proxy/utils/UUPSUpgradeable.sol From df7996b671d309ee949113c64beee9899133dc05 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 19 Apr 2021 20:55:16 +0200 Subject: [PATCH 05/11] add Available since 4.1 comments (cherry picked from commit 9a698e6b7b97a6c6cc78b2df6249ca989b4acd8d) --- contracts/access/AccessControl.sol | 2 ++ contracts/interfaces/IERC1271.sol | 2 ++ contracts/interfaces/IERC3156.sol | 2 ++ contracts/proxy/ERC1967/ERC1967Storage.sol | 2 ++ contracts/proxy/ERC1967/ERC1967Upgrade.sol | 2 ++ contracts/proxy/utils/UUPSUpgradeable.sol | 2 ++ contracts/token/ERC20/extensions/IERC20Metadata.sol | 2 ++ contracts/token/ERC20/extensions/draft-ERC20FlashMint.sol | 2 ++ contracts/token/ERC20/extensions/draft-ERC20Permit.sol | 2 ++ contracts/utils/Multicall.sol | 2 ++ contracts/utils/StorageSlot.sol | 2 +- contracts/utils/cryptography/ECDSA.sol | 2 +- contracts/utils/cryptography/SignatureChecker.sol | 2 ++ 13 files changed, 24 insertions(+), 2 deletions(-) diff --git a/contracts/access/AccessControl.sol b/contracts/access/AccessControl.sol index 5b723def7..cf62500f8 100644 --- a/contracts/access/AccessControl.sol +++ b/contracts/access/AccessControl.sol @@ -99,6 +99,8 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 { * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/ + * + * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role, _msgSender()); diff --git a/contracts/interfaces/IERC1271.sol b/contracts/interfaces/IERC1271.sol index ca5de924c..b15147fc0 100644 --- a/contracts/interfaces/IERC1271.sol +++ b/contracts/interfaces/IERC1271.sol @@ -5,6 +5,8 @@ pragma solidity ^0.8.0; /** * @dev Interface of the ERC1271 standard signature validation method for * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. + * + * _Available since v4.1._ */ interface IERC1271 { /** diff --git a/contracts/interfaces/IERC3156.sol b/contracts/interfaces/IERC3156.sol index 2f3d84608..4b0cb5547 100644 --- a/contracts/interfaces/IERC3156.sol +++ b/contracts/interfaces/IERC3156.sol @@ -5,6 +5,8 @@ pragma solidity ^0.8.0; /** * @dev Interface of the ERC3156 FlashBorrower, as defined in * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. + * + * _Available since v4.1._ */ interface IERC3156FlashBorrower { /** diff --git a/contracts/proxy/ERC1967/ERC1967Storage.sol b/contracts/proxy/ERC1967/ERC1967Storage.sol index ff1753cb3..411402a09 100644 --- a/contracts/proxy/ERC1967/ERC1967Storage.sol +++ b/contracts/proxy/ERC1967/ERC1967Storage.sol @@ -9,6 +9,8 @@ import "../../utils/StorageSlot.sol"; /** * @dev This abstract contract provides setters and getters for the different * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] storage slots. + * + * _Available since v4.1._ */ abstract contract ERC1967Storage { /** diff --git a/contracts/proxy/ERC1967/ERC1967Upgrade.sol b/contracts/proxy/ERC1967/ERC1967Upgrade.sol index 828fb5934..c17fdabb1 100644 --- a/contracts/proxy/ERC1967/ERC1967Upgrade.sol +++ b/contracts/proxy/ERC1967/ERC1967Upgrade.sol @@ -8,6 +8,8 @@ import "./ERC1967Storage.sol"; * @dev This abstract contract provides event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. * + * _Available since v4.1._ + * * @custom:oz-upgrades-unsafe-allow delegatecall */ abstract contract ERC1967Upgrade is ERC1967Storage { diff --git a/contracts/proxy/utils/UUPSUpgradeable.sol b/contracts/proxy/utils/UUPSUpgradeable.sol index 09bf524d5..6f9010233 100644 --- a/contracts/proxy/utils/UUPSUpgradeable.sol +++ b/contracts/proxy/utils/UUPSUpgradeable.sol @@ -10,6 +10,8 @@ import "../ERC1967/ERC1967Upgrade.sol"; * continuation of the upgradability. * * The {_authorizeUpgrade} function MUST be overridden to include access restriction to the upgrade mechanism. + * + * _Available since v4.1._ */ abstract contract UUPSUpgradeable is ERC1967Upgrade { function upgradeTo(address newImplementation) external virtual { diff --git a/contracts/token/ERC20/extensions/IERC20Metadata.sol b/contracts/token/ERC20/extensions/IERC20Metadata.sol index 5bc786069..4fb868ae8 100644 --- a/contracts/token/ERC20/extensions/IERC20Metadata.sol +++ b/contracts/token/ERC20/extensions/IERC20Metadata.sol @@ -6,6 +6,8 @@ import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. + * + * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** diff --git a/contracts/token/ERC20/extensions/draft-ERC20FlashMint.sol b/contracts/token/ERC20/extensions/draft-ERC20FlashMint.sol index a4f2b04c7..36d23218c 100644 --- a/contracts/token/ERC20/extensions/draft-ERC20FlashMint.sol +++ b/contracts/token/ERC20/extensions/draft-ERC20FlashMint.sol @@ -11,6 +11,8 @@ import "../ERC20.sol"; * * Adds the {flashLoan} method, which provides flash loan support at the token * level. By default there is no fee, but this can be changed by overriding {flashFee}. + * + * _Available since v4.1._ */ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { bytes32 constant private RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan"); diff --git a/contracts/token/ERC20/extensions/draft-ERC20Permit.sol b/contracts/token/ERC20/extensions/draft-ERC20Permit.sol index 4dcd9007a..e1341eceb 100644 --- a/contracts/token/ERC20/extensions/draft-ERC20Permit.sol +++ b/contracts/token/ERC20/extensions/draft-ERC20Permit.sol @@ -77,6 +77,8 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { /** * @dev "Consume a nonce": return the current value and increment. + * + * _Available since v4.1._ */ function _useNonce(address owner) internal virtual returns (uint256 current) { Counters.Counter storage nonce = _nonces[owner]; diff --git a/contracts/utils/Multicall.sol b/contracts/utils/Multicall.sol index 2dcb1d7fc..43244bdd0 100644 --- a/contracts/utils/Multicall.sol +++ b/contracts/utils/Multicall.sol @@ -6,6 +6,8 @@ import "./Address.sol"; /** * @dev Provides a function to batch together multiple calls in a single external call. + * + * _Available since v4.1._ */ abstract contract Multicall { /** diff --git a/contracts/utils/StorageSlot.sol b/contracts/utils/StorageSlot.sol index 93a773583..00aa4ec6d 100644 --- a/contracts/utils/StorageSlot.sol +++ b/contracts/utils/StorageSlot.sol @@ -26,7 +26,7 @@ pragma solidity ^0.8.0; * } * ``` * - * Available since v4.1.0 for `address`, `bool`, `bytes32`, and `uint256`. + * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._ */ library StorageSlot { struct AddressSlot { diff --git a/contracts/utils/cryptography/ECDSA.sol b/contracts/utils/cryptography/ECDSA.sol index b58e26e9f..9282977de 100644 --- a/contracts/utils/cryptography/ECDSA.sol +++ b/contracts/utils/cryptography/ECDSA.sol @@ -31,7 +31,7 @@ library ECDSA { // Check the signature length // - case 65: r,s,v signature (standard) - // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) + // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ if (signature.length == 65) { // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. diff --git a/contracts/utils/cryptography/SignatureChecker.sol b/contracts/utils/cryptography/SignatureChecker.sol index 881ae579a..c678a52b6 100644 --- a/contracts/utils/cryptography/SignatureChecker.sol +++ b/contracts/utils/cryptography/SignatureChecker.sol @@ -13,6 +13,8 @@ import "../../interfaces/IERC1271.sol"; * * Note: unlike ECDSA signatures, contract signature's are revocable, and the outcome of this function can thus change * through time. It could return true at block N and false at block N+1 (or the opposite). + * + * _Available since v4.1._ */ library SignatureChecker { function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) { From a664fb1e45f400240d006b2eed0a17e8633de6da Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Mon, 19 Apr 2021 19:35:18 -0300 Subject: [PATCH 06/11] Improve code comments in ERC1967._upgradeToAndCallSecure (cherry picked from commit 2e6ef74593157333a7135405f5b47bbf8ebd2e88) --- contracts/proxy/ERC1967/ERC1967Upgrade.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contracts/proxy/ERC1967/ERC1967Upgrade.sol b/contracts/proxy/ERC1967/ERC1967Upgrade.sol index c17fdabb1..37c5d63e5 100644 --- a/contracts/proxy/ERC1967/ERC1967Upgrade.sol +++ b/contracts/proxy/ERC1967/ERC1967Upgrade.sol @@ -61,16 +61,17 @@ abstract contract ERC1967Upgrade is ERC1967Storage { */ function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal { address oldImplementation = _getImplementation(); - // do inital upgrade + + // Initial upgrade and setup call _setImplementation(newImplementation); - // do setup call if (data.length > 0 || forceCall) { Address.functionDelegateCall(newImplementation, data); } - // check if nested in an upgrade check + + // Perform rollback test if not already in progress StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT); if (!rollbackTesting.value) { - // trigger upgrade check with flag set to true + // Trigger rollback using upgradeTo from the new implementation rollbackTesting.value = true; Address.functionDelegateCall( newImplementation, @@ -80,11 +81,10 @@ abstract contract ERC1967Upgrade is ERC1967Storage { ) ); rollbackTesting.value = false; - // check upgrade was effective + // Check rollback was effective require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades"); - // reset upgrade + // Finally reset to the new implementation and log the upgrade _setImplementation(newImplementation); - // emit event emit Upgraded(newImplementation); } } From 6552adafb2fd4ec7c1284c5a94d30faded004437 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 26 Apr 2021 20:16:19 +0200 Subject: [PATCH 07/11] Merge ERC1967Storage into ERC1967Upgrade (#2644) (cherry picked from commit 5756e31999009f906b6179e42bf062889ba4081e) --- contracts/proxy/ERC1967/ERC1967Proxy.sol | 2 +- contracts/proxy/ERC1967/ERC1967Storage.sol | 87 --------------------- contracts/proxy/ERC1967/ERC1967Upgrade.sol | 88 ++++++++++++++++++++-- contracts/proxy/README.adoc | 2 - 4 files changed, 82 insertions(+), 97 deletions(-) delete mode 100644 contracts/proxy/ERC1967/ERC1967Storage.sol diff --git a/contracts/proxy/ERC1967/ERC1967Proxy.sol b/contracts/proxy/ERC1967/ERC1967Proxy.sol index 38dbf9697..4d893c161 100644 --- a/contracts/proxy/ERC1967/ERC1967Proxy.sol +++ b/contracts/proxy/ERC1967/ERC1967Proxy.sol @@ -27,6 +27,6 @@ contract ERC1967Proxy is Proxy, ERC1967Upgrade { * @dev Returns the current implementation address. */ function _implementation() internal view virtual override returns (address impl) { - return ERC1967Storage._getImplementation(); + return ERC1967Upgrade._getImplementation(); } } diff --git a/contracts/proxy/ERC1967/ERC1967Storage.sol b/contracts/proxy/ERC1967/ERC1967Storage.sol deleted file mode 100644 index 411402a09..000000000 --- a/contracts/proxy/ERC1967/ERC1967Storage.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../beacon/IBeacon.sol"; -import "../../utils/Address.sol"; -import "../../utils/StorageSlot.sol"; - -/** - * @dev This abstract contract provides setters and getters for the different - * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] storage slots. - * - * _Available since v4.1._ - */ -abstract contract ERC1967Storage { - /** - * @dev Storage slot with the address of the current implementation. - * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is - * validated in the constructor. - */ - bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - /** - * @dev Returns the current implementation address. - */ - function _getImplementation() internal view returns (address) { - return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; - } - - /** - * @dev Stores a new address in the EIP1967 implementation slot. - */ - function _setImplementation(address newImplementation) internal { - require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); - StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; - } - - /** - * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. - * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. - */ - bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; - - /** - * @dev Returns the current beacon. - */ - function _getBeacon() internal view returns (address) { - return StorageSlot.getAddressSlot(_BEACON_SLOT).value; - } - - /** - * @dev Stores a new beacon in the EIP1967 beacon slot. - */ - function _setBeacon(address newBeacon) internal { - require( - Address.isContract(newBeacon), - "ERC1967: new beacon is not a contract" - ); - require( - Address.isContract(IBeacon(newBeacon).implementation()), - "ERC1967: beacon implementation is not a contract" - ); - StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; - } - - /** - * @dev Storage slot with the admin of the contract. - * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is - * validated in the constructor. - */ - bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; - - /** - * @dev Returns the current admin. - */ - function _getAdmin() internal view returns (address) { - return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; - } - - /** - * @dev Stores a new address in the EIP1967 admin slot. - */ - function _setAdmin(address newAdmin) internal { - require(newAdmin != address(0), "ERC1967: new admin is the zero address"); - StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; - } -} diff --git a/contracts/proxy/ERC1967/ERC1967Upgrade.sol b/contracts/proxy/ERC1967/ERC1967Upgrade.sol index 37c5d63e5..7d5231a88 100644 --- a/contracts/proxy/ERC1967/ERC1967Upgrade.sol +++ b/contracts/proxy/ERC1967/ERC1967Upgrade.sol @@ -2,34 +2,48 @@ pragma solidity ^0.8.2; -import "./ERC1967Storage.sol"; +import "../beacon/IBeacon.sol"; +import "../../utils/Address.sol"; +import "../../utils/StorageSlot.sol"; /** - * @dev This abstract contract provides event emitting update functions for + * @dev This abstract contract provides getters and event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. * * _Available since v4.1._ * * @custom:oz-upgrades-unsafe-allow delegatecall */ -abstract contract ERC1967Upgrade is ERC1967Storage { +abstract contract ERC1967Upgrade { // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + /** * @dev Emitted when the implementation is upgraded. */ event Upgraded(address indexed implementation); /** - * @dev Emitted when the beacon is upgraded. + * @dev Returns the current implementation address. */ - event BeaconUpgraded(address indexed beacon); + function _getImplementation() internal view returns (address) { + return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; + } /** - * @dev Emitted when the admin account has changed. + * @dev Stores a new address in the EIP1967 implementation slot. */ - event AdminChanged(address previousAdmin, address newAdmin); + function _setImplementation(address newImplementation) private { + require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); + StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; + } /** * @dev Perform implementation upgrade @@ -103,6 +117,33 @@ abstract contract ERC1967Upgrade is ERC1967Storage { } } + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + /** + * @dev Emitted when the admin account has changed. + */ + event AdminChanged(address previousAdmin, address newAdmin); + + /** + * @dev Returns the current admin. + */ + function _getAdmin() internal view returns (address) { + return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; + } + + /** + * @dev Stores a new address in the EIP1967 admin slot. + */ + function _setAdmin(address newAdmin) private { + require(newAdmin != address(0), "ERC1967: new admin is the zero address"); + StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; + } + /** * @dev Changes the admin of the proxy. * @@ -112,4 +153,37 @@ abstract contract ERC1967Upgrade is ERC1967Storage { emit AdminChanged(_getAdmin(), newAdmin); _setAdmin(newAdmin); } + + /** + * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. + * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. + */ + bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; + + /** + * @dev Emitted when the beacon is upgraded. + */ + event BeaconUpgraded(address indexed beacon); + + /** + * @dev Returns the current beacon. + */ + function _getBeacon() internal view returns (address) { + return StorageSlot.getAddressSlot(_BEACON_SLOT).value; + } + + /** + * @dev Stores a new beacon in the EIP1967 beacon slot. + */ + function _setBeacon(address newBeacon) private { + require( + Address.isContract(newBeacon), + "ERC1967: new beacon is not a contract" + ); + require( + Address.isContract(IBeacon(newBeacon).implementation()), + "ERC1967: beacon implementation is not a contract" + ); + StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; + } } diff --git a/contracts/proxy/README.adoc b/contracts/proxy/README.adoc index 66764d787..e317c9389 100644 --- a/contracts/proxy/README.adoc +++ b/contracts/proxy/README.adoc @@ -40,8 +40,6 @@ When doing an upgrade, the second parameter of the {upgradeToAndCall} function a {{ERC1967Proxy}} -{{ERC1967Storage}} - {{ERC1967Upgrade}} == UUPS From 10c763da6ff5c2593b93b4e760ac565d04cbef85 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Mon, 26 Apr 2021 15:37:04 -0300 Subject: [PATCH 08/11] Fix whitespace in doc comments (cherry picked from commit a020a55f9d46536ee3908c9a20dafd212dbffea1) --- .../utils/introspection/IERC1820Registry.sol | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/contracts/utils/introspection/IERC1820Registry.sol b/contracts/utils/introspection/IERC1820Registry.sol index 75da55687..5ecd520a5 100644 --- a/contracts/utils/introspection/IERC1820Registry.sol +++ b/contracts/utils/introspection/IERC1820Registry.sol @@ -80,28 +80,28 @@ interface IERC1820Registry { function interfaceHash(string calldata interfaceName) external pure returns (bytes32); /** - * @notice Updates the cache with whether the contract implements an ERC165 interface or not. - * @param account Address of the contract for which to update the cache. - * @param interfaceId ERC165 interface for which to update the cache. + * @notice Updates the cache with whether the contract implements an ERC165 interface or not. + * @param account Address of the contract for which to update the cache. + * @param interfaceId ERC165 interface for which to update the cache. */ function updateERC165Cache(address account, bytes4 interfaceId) external; /** - * @notice Checks whether a contract implements an ERC165 interface or not. - * If the result is not cached a direct lookup on the contract address is performed. - * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling - * {updateERC165Cache} with the contract address. - * @param account Address of the contract to check. - * @param interfaceId ERC165 interface to check. - * @return True if `account` implements `interfaceId`, false otherwise. + * @notice Checks whether a contract implements an ERC165 interface or not. + * If the result is not cached a direct lookup on the contract address is performed. + * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling + * {updateERC165Cache} with the contract address. + * @param account Address of the contract to check. + * @param interfaceId ERC165 interface to check. + * @return True if `account` implements `interfaceId`, false otherwise. */ function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool); /** - * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache. - * @param account Address of the contract to check. - * @param interfaceId ERC165 interface to check. - * @return True if `account` implements `interfaceId`, false otherwise. + * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache. + * @param account Address of the contract to check. + * @param interfaceId ERC165 interface to check. + * @return True if `account` implements `interfaceId`, false otherwise. */ function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool); From cfd6e7eab994e0b10586b18b654dba28b848dbcf Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Mon, 26 Apr 2021 15:41:17 -0300 Subject: [PATCH 09/11] Fix documentation typos (cherry picked from commit a98814b930910220d5b5ba25ed2d4dcf45e48315) --- contracts/token/ERC1155/ERC1155.sol | 1 - contracts/token/ERC1155/IERC1155Receiver.sol | 2 +- contracts/utils/introspection/ERC1820Implementer.sol | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/contracts/token/ERC1155/ERC1155.sol b/contracts/token/ERC1155/ERC1155.sol index ed2d8056f..7d612e22e 100644 --- a/contracts/token/ERC1155/ERC1155.sol +++ b/contracts/token/ERC1155/ERC1155.sol @@ -10,7 +10,6 @@ import "../../utils/Context.sol"; import "../../utils/introspection/ERC165.sol"; /** - * * @dev Implementation of the basic standard multi-token. * See https://eips.ethereum.org/EIPS/eip-1155 * Originally based on code by Enjin: https://github.com/enjin/erc-1155 diff --git a/contracts/token/ERC1155/IERC1155Receiver.sol b/contracts/token/ERC1155/IERC1155Receiver.sol index ffd61dabc..39a8b979a 100644 --- a/contracts/token/ERC1155/IERC1155Receiver.sol +++ b/contracts/token/ERC1155/IERC1155Receiver.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** - * _Available since v3.1._ + * @dev _Available since v3.1._ */ interface IERC1155Receiver is IERC165 { diff --git a/contracts/utils/introspection/ERC1820Implementer.sol b/contracts/utils/introspection/ERC1820Implementer.sol index 89c87b119..8775d780e 100644 --- a/contracts/utils/introspection/ERC1820Implementer.sol +++ b/contracts/utils/introspection/ERC1820Implementer.sol @@ -18,7 +18,7 @@ contract ERC1820Implementer is IERC1820Implementer { mapping(bytes32 => mapping(address => bool)) private _supportedInterfaces; /** - * See {IERC1820Implementer-canImplementInterfaceForAddress}. + * @dev See {IERC1820Implementer-canImplementInterfaceForAddress}. */ function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) public view virtual override returns (bytes32) { return _supportedInterfaces[interfaceHash][account] ? _ERC1820_ACCEPT_MAGIC : bytes32(0x00); From 903df568371f1d2c5483b49aa84c745e958589cf Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Tue, 27 Apr 2021 09:29:13 -0300 Subject: [PATCH 10/11] Review structure of Proxies API docs (#2645) (cherry picked from commit 4a53ee5a132ba429c82a938a2e5f26745cd676b7) --- contracts/proxy/README.adoc | 42 ++++++++++++++++-------- docs/modules/ROOT/pages/upgradeable.adoc | 4 +-- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/contracts/proxy/README.adoc b/contracts/proxy/README.adoc index e317c9389..8a10bc277 100644 --- a/contracts/proxy/README.adoc +++ b/contracts/proxy/README.adoc @@ -5,32 +5,48 @@ NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/ This is a low-level set of contracts implementing different proxy patterns with and without upgradeability. For an in-depth overview of this pattern check out the xref:upgrades-plugins::proxies.adoc[Proxy Upgrade Pattern] page. -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. +Most of the proxies below are built on an abstract base contract. -{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. +- {Proxy}: Abstract contract implementing the core delegation functionality. -An alternative upgradeability mechanism is provided in <>. 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 {ERC1967Proxy}, 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. +In order to avoid clashes with the storage variables of the implementation contract behind a proxy, we use https://eips.ethereum.org/EIPS/eip-1967[EIP1967] storage slots. -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. +- {ERC1967Upgrade}: Internal functions to get and set the storage slots defined in EIP1967. +- {ERC1967Proxy}: A proxy using EIP1967 storage slots. Not upgradeable by default. + +There are two alternative ways to add upgradeability to an ERC1967 proxy. Their differences are explained below in <>. + +- {TransparentUpgradeableProxy}: A proxy with a built in admin and upgrade interface. +- {UUPSUpgradeable}: An upgradeability mechanism to be included in the implementation for an ERC1967 proxy. 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. -== UUPS Design: Upgradeability as an Implementation Feature +A different family of proxies are beacon proxies. This pattern, popularized by Dharma, allows multiple proxies to be upgraded to a different implementation in a single transaction. -Upgradeable smart contracts rely on proxies to relay the calls in a way that is programmable. As discussed previously, we provide different proxy contracts that each come with a specific set of features. Other designs, not (yet) proposed as part of the OpenZeppelin products, also exist. +- {BeaconProxy}: A proxy that retreives its implementation from a beacon contract. +- {UpgradeableBeacon}: A beacon contract that can be upgraded. -The most simple, and common, design is known as Universal Upgradeable Proxy Standard (UUPS). This design is both lightweight and versatile and is proposed through the {ERC1967Proxy} and the {UUPSUpgradeable} contract. +In this pattern, the proxy contract doesn't hold the implementation address in storage like an ERC1967 proxy, instead the address is stored in a separate beacon contract. The `upgrade` operations that are sent to the beacon instead of to the proxy contract, and all proxies that follow that beacon are automatically upgraded. -While {UUPSUpgradeable} uses the same interface as {TransparentUpgradeableProxy}, in the first case the upgrade is handled by the implementation, and can eventually be removed. {TransparentUpgradeableProxy}, on the other hand, includes the upgrade logic in the proxy. This means {TransparentUpgradeableProxy} is more expensive to deploy. Note that, since both proxies use the same storage slot for the implementation address, using a UUPS compliant implementation with a {TransparentUpgradeableProxy} might allow non-admins to perform upgrade operations. +Outside the realm of upgradeability, proxies can also be useful to make cheap contract clones, such as those created by an on-chain factory contract that creates many instances of the same contract. These instances are designed to be both cheap to deploy, and cheap to call. -According to this design, the {ERC1967Proxy} is only capable of forwarding calls to an implementation contract. Unlike the more complex and expensive to deploy {TransparentUpgradeableProxy}, the {ERC1967Proxy} doesn't by itself provide any upgradeability mechanism. It is the role of the implementation to include, alongside the contract's logic, all the code necessary to update the implementation's address that is stored at a specific slot in the proxy's storage space. This is where the {UUPSUpgradeable} contract comes in. Inheriting from it (and overriding the {_authorizeUpgrade} function with the relevant access control mechanism) will turn your contract into a UUPS complaint implementation. +- {Clones}: A library that can deploy cheap minimal non-upgradeable proxies. -By default, the upgrade mechanism included in {UUPSUpgradeable} contains a security mechanism that will prevent any upgrades to a non UUPS compliant implementation. This prevents upgrades to an implementation contract that wouldn't contain the necessary upgrade mechanism, as it would lock the proxy forever. This security mechanism can however be bypassed, either by: +[[transparent-vs-uups]] +== Transparent vs UUPS Proxies -- Adding a flag mechanism in the implementation that will disable the upgrade function when triggered; -- Upgrading to an implementation that features an upgrade mechanism without the additional security check, and then upgrade again to another implementation without the upgrade mechanism. +The original proxies included in OpenZeppelin followed the https://blog.openzeppelin.com/the-transparent-proxy-pattern/[Transparent Proxy Pattern]. While this pattern is still provided, our recommendation is now shifting towards UUPS proxies, which are both lightweight and versatile. The name UUPS comes from https://eips.ethereum.org/EIPS/eip-1822[EIP1822], which first documented the pattern. -When doing an upgrade, the second parameter of the {upgradeToAndCall} function allows for the atomic execution of an optional initialization/migration call. +While both of these share the same interface for upgrades, in UUPS proxies the upgrade is handled by the implementation, and can eventually be removed. Transparent proxies, on the other hand, include the upgrade and admin logic in the proxy itself. This means {TransparentUpgradeableProxy} is more expensive to deploy than what is possible with UUPS proxies. + +UUPS proxies are implemented using an {ERC1967Proxy}. Note that this proxy is not by itself upgradeable. It is the role of the implementation to include, alongside the contract's logic, all the code necessary to update the implementation's address that is stored at a specific slot in the proxy's storage space. This is where the {UUPSUpgradeable} contract comes in. Inheriting from it (and overriding the {xref-UUPSUpgradeable-_authorizeUpgrade-address-}[`_authorizeUpgrade`] function with the relevant access control mechanism) will turn your contract into a UUPS compliant implementation. + +Note that since both proxies use the same storage slot for the implementation address, using a UUPS compliant implementation with a {TransparentUpgradeableProxy} might allow non-admins to perform upgrade operations. + +By default, the upgrade functionality included in {UUPSUpgradeable} contains a security mechanism that will prevent any upgrades to a non UUPS compliant implementation. This prevents upgrades to an implementation contract that wouldn't contain the necessary upgrade mechanism, as it would lock the upgradeability of the proxy forever. This security mechanism can be bypassed by either of: + +- Adding a flag mechanism in the implementation that will disable the upgrade function when triggered. +- Upgrading to an implementation that features an upgrade mechanism without the additional security check, and then upgrading again to another implementation without the upgrade mechanism. == Core diff --git a/docs/modules/ROOT/pages/upgradeable.adoc b/docs/modules/ROOT/pages/upgradeable.adoc index cc3093bf4..17011cb9d 100644 --- a/docs/modules/ROOT/pages/upgradeable.adoc +++ b/docs/modules/ROOT/pages/upgradeable.adoc @@ -1,6 +1,6 @@ = Using with Upgrades -If your contract is going to be deployed with upgradeability, such as using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins], you will need to use the Upgrade Safe variant of OpenZeppelin Contracts. +If your contract is going to be deployed with upgradeability, such as using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins], you will need to use the Upgradeable variant of OpenZeppelin Contracts. This variant is available as a separate package called `@openzeppelin/contracts-upgradeable`, which is hosted in the repository https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable[OpenZeppelin/openzeppelin-contracts-upgradeable]. @@ -66,6 +66,6 @@ The function `+__{ContractName}_init_unchained+` found in every contract is the === Storage Gaps -You may notice that every contract includes a state variable named `+__gap+`. This is empty reserved space in storage that is put in place in Upgrade Safe contracts. It allows us to freely add new state variables in the future without compromising the storage compatibility with existing deployments. +You may notice that every contract includes a state variable named `+__gap+`. This is empty reserved space in storage that is put in place in Upgradeable contracts. It allows us to freely add new state variables in the future without compromising the storage compatibility with existing deployments. It isn't safe to simply add a state variable because it "shifts down" all of the state variables below in the inheritance chain. This makes the storage layouts incompatible, as explained in xref:upgrades-plugins::writing-upgradeable.adoc#modifying-your-contracts[Writing Upgradeable Contracts]. The size of the `+__gap+` array is calculated so that the amount of storage used by a contract always adds up to the same number (in this case 50 storage slots). From 23869e5b2a7c6b9c3e27dee4289615b8cf50e36b Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Thu, 29 Apr 2021 18:04:21 -0300 Subject: [PATCH 11/11] 4.1.0 --- CHANGELOG.md | 2 +- contracts/package.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cd099c4d..89fa6a1d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 4.1.0 +## 4.1.0 (2021-04-29) * `IERC20Metadata`: add a new extended interface that includes the optional `name()`, `symbol()` and `decimals()` functions. ([#2561](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2561)) * `ERC777`: make reception acquirement optional in `_mint`. ([#2552](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2552)) diff --git a/contracts/package.json b/contracts/package.json index 562497a4a..160a3ca9a 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,7 +1,7 @@ { "name": "@openzeppelin/contracts", "description": "Secure Smart Contract library for Solidity", - "version": "4.1.0-rc.0", + "version": "4.1.0", "files": [ "**/*.sol", "/build/contracts/*.json", diff --git a/package-lock.json b/package-lock.json index 69976ff2c..4eeb724e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "openzeppelin-solidity", - "version": "4.1.0-rc.0", + "version": "4.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "openzeppelin-solidity", - "version": "4.1.0-rc.0", + "version": "4.1.0", "license": "MIT", "bin": { "openzeppelin-contracts-migrate-imports": "scripts/migrate-imports.js" diff --git a/package.json b/package.json index 5000bf0dc..7f845bfdb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "openzeppelin-solidity", "description": "Secure Smart Contract library for Solidity", - "version": "4.1.0-rc.0", + "version": "4.1.0", "files": [ "/contracts/**/*.sol", "/build/contracts/*.json",