Commit 63d337d3 by github-actions

Transpile debaffad

parent 33491331
...@@ -17,10 +17,14 @@ ...@@ -17,10 +17,14 @@
* `ERC20`: do not update allowance on `transferFrom` when allowance is `type(uint256).max`. ([#3085](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#3085)) * `ERC20`: do not update allowance on `transferFrom` when allowance is `type(uint256).max`. ([#3085](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#3085))
* `ERC777`: do not update allowance on `transferFrom` when allowance is `type(uint256).max`. ([#3085](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#3085)) * `ERC777`: do not update allowance on `transferFrom` when allowance is `type(uint256).max`. ([#3085](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#3085))
* `SignedMath`: a new signed version of the Math library with `max`, `min`, and `average`. ([#2686](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2686)) * `SignedMath`: a new signed version of the Math library with `max`, `min`, and `average`. ([#2686](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2686))
* `ERC1967Upgrade`: Refactor the secure upgrade to use `ERC1822` instead of the previous rollback mechanism. This reduces code complexity and attack surface with similar security guarantees. ([#3021](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3021))
* `UUPSUpgradeable`: Add `ERC1822` compliance to support the updated secure upgrade mechanism. ([#3021](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3021))
* Some more functions have been made virtual to customize them via overrides. In many cases this will not imply that other functions in the contract will automatically adapt to the overridden definitions. People who wish to override should consult the source code to understand the impact and if they need to override any additional functions to achieve the desired behavior.
### Breaking change ### Breaking changes
Solidity pragma in `utils/Address.sol` is increased from `^0.8.0` to `^0.8.1`. This is required by the `account.code.length` syntax that replaces inline assembly. This may require users to bump their compiler version from `0.8.0` to `0.8.1` or later. Note that other parts of the code already include stricter requirements. * `ERC1967Upgrade`: The function `_upgradeToAndCallSecure` was renamed to `_upgradeToAndCallUUPS`, along with the change in security mechanism described above.
* `Address`: The Solidity pragma is increased from `^0.8.0` to `^0.8.1`. This is required by the `account.code.length` syntax that replaces inline assembly. This may require users to bump their compiler version from `0.8.0` to `0.8.1` or later. Note that other parts of the code already include stricter requirements.
## 4.4.2 (2022-01-11) ## 4.4.2 (2022-01-11)
......
...@@ -44,7 +44,7 @@ abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessCo ...@@ -44,7 +44,7 @@ abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessCo
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information. * for more information.
*/ */
function getRoleMember(bytes32 role, uint256 index) public view override returns (address) { function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
return _roleMembers[role].at(index); return _roleMembers[role].at(index);
} }
...@@ -52,7 +52,7 @@ abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessCo ...@@ -52,7 +52,7 @@ abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessCo
* @dev Returns the number of accounts that have `role`. Can be used * @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role. * together with {getRoleMember} to enumerate all bearers of a role.
*/ */
function getRoleMemberCount(bytes32 role) public view override returns (uint256) { function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
return _roleMembers[role].length(); return _roleMembers[role].length();
} }
......
...@@ -90,7 +90,7 @@ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, ...@@ -90,7 +90,7 @@ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable,
/** /**
* @dev Returns `true` if `account` has been granted `role`. * @dev Returns `true` if `account` has been granted `role`.
*/ */
function hasRole(bytes32 role, address account) public view override returns (bool) { function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account]; return _roles[role].members[account];
} }
...@@ -101,7 +101,7 @@ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, ...@@ -101,7 +101,7 @@ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable,
* *
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/ */
function _checkRole(bytes32 role, address account) internal view { function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) { if (!hasRole(role, account)) {
revert( revert(
string( string(
...@@ -122,7 +122,7 @@ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, ...@@ -122,7 +122,7 @@ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable,
* *
* To change a role's admin, use {_setRoleAdmin}. * To change a role's admin, use {_setRoleAdmin}.
*/ */
function getRoleAdmin(bytes32 role) public view override returns (bytes32) { function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole; return _roles[role].adminRole;
} }
......
...@@ -379,7 +379,7 @@ abstract contract GovernorUpgradeable is Initializable, ContextUpgradeable, ERC1 ...@@ -379,7 +379,7 @@ abstract contract GovernorUpgradeable is Initializable, ContextUpgradeable, ERC1
address target, address target,
uint256 value, uint256 value,
bytes calldata data bytes calldata data
) external onlyGovernance { ) external virtual onlyGovernance {
AddressUpgradeable.functionCallWithValue(target, data, value); AddressUpgradeable.functionCallWithValue(target, data, value);
} }
......
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.x.0 (proxy/ERC1822/IProxiable.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822ProxiableUpgradeable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./UUPSUpgradeableMockUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";
// This contract implements the pre-4.5 UUPS upgrade function with a rollback test.
// It's used to test that newer UUPS contracts are considered valid upgrades by older UUPS contracts.
contract UUPSUpgradeableLegacyMockUpgradeable is Initializable, UUPSUpgradeableMockUpgradeable {
function __UUPSUpgradeableLegacyMock_init() internal onlyInitializing {
__CountersImpl_init_unchained();
__ERC1967Upgrade_init_unchained();
__UUPSUpgradeable_init_unchained();
__UUPSUpgradeableMock_init_unchained();
__UUPSUpgradeableLegacyMock_init_unchained();
}
function __UUPSUpgradeableLegacyMock_init_unchained() internal onlyInitializing {
}
// Inlined from ERC1967Upgrade
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
// ERC1967Upgrade._setImplementation is private so we reproduce it here.
// An extra underscore prevents a name clash error.
function __setImplementation(address newImplementation) private {
require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
}
function _upgradeToAndCallSecureLegacyV1(
address newImplementation,
bytes memory data,
bool forceCall
) internal {
address oldImplementation = _getImplementation();
// Initial upgrade and setup call
__setImplementation(newImplementation);
if (data.length > 0 || forceCall) {
__functionDelegateCall(newImplementation, data);
}
// Perform rollback test if not already in progress
StorageSlotUpgradeable.BooleanSlot storage rollbackTesting = StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT);
if (!rollbackTesting.value) {
// Trigger rollback using upgradeTo from the new implementation
rollbackTesting.value = true;
__functionDelegateCall(
newImplementation,
abi.encodeWithSignature("upgradeTo(address)", oldImplementation)
);
rollbackTesting.value = false;
// Check rollback was effective
require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
// Finally reset to the new implementation and log the upgrade
_upgradeTo(newImplementation);
}
}
// hooking into the old mechanism
function upgradeTo(address newImplementation) external virtual override {
_upgradeToAndCallSecureLegacyV1(newImplementation, bytes(""), false);
}
function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual override {
_upgradeToAndCallSecureLegacyV1(newImplementation, data, false);
}
// ERC1967Upgrade._functionDelegateCall is private so we reproduce it here.
// An extra underscore prevents a name clash error.
function __functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {
require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
}
uint256[50] private __gap;
}
...@@ -41,24 +41,3 @@ contract UUPSUpgradeableUnsafeMockUpgradeable is Initializable, UUPSUpgradeableM ...@@ -41,24 +41,3 @@ contract UUPSUpgradeableUnsafeMockUpgradeable is Initializable, UUPSUpgradeableM
} }
uint256[50] private __gap; uint256[50] private __gap;
} }
contract UUPSUpgradeableBrokenMockUpgradeable is Initializable, UUPSUpgradeableMockUpgradeable {
function __UUPSUpgradeableBrokenMock_init() internal onlyInitializing {
__CountersImpl_init_unchained();
__ERC1967Upgrade_init_unchained();
__UUPSUpgradeable_init_unchained();
__UUPSUpgradeableMock_init_unchained();
__UUPSUpgradeableBrokenMock_init_unchained();
}
function __UUPSUpgradeableBrokenMock_init_unchained() internal onlyInitializing {
}
function upgradeTo(address) external virtual override {
// pass
}
function upgradeToAndCall(address, bytes memory) external payable virtual override {
// pass
}
uint256[50] private __gap;
}
...@@ -661,25 +661,25 @@ contract CountersImplUpgradeableWithInit is CountersImplUpgradeable { ...@@ -661,25 +661,25 @@ contract CountersImplUpgradeableWithInit is CountersImplUpgradeable {
__CountersImpl_init(); __CountersImpl_init();
} }
} }
import "./UUPS/TestInProdUpgradeable.sol"; import "./UUPS/UUPSUpgradeableMockUpgradeable.sol";
contract UUPSUpgradeableMockUpgradeableWithInit is UUPSUpgradeableMockUpgradeable { contract UUPSUpgradeableMockUpgradeableWithInit is UUPSUpgradeableMockUpgradeable {
constructor() payable initializer { constructor() payable initializer {
__UUPSUpgradeableMock_init(); __UUPSUpgradeableMock_init();
} }
} }
import "./UUPS/TestInProdUpgradeable.sol"; import "./UUPS/UUPSUpgradeableMockUpgradeable.sol";
contract UUPSUpgradeableUnsafeMockUpgradeableWithInit is UUPSUpgradeableUnsafeMockUpgradeable { contract UUPSUpgradeableUnsafeMockUpgradeableWithInit is UUPSUpgradeableUnsafeMockUpgradeable {
constructor() payable initializer { constructor() payable initializer {
__UUPSUpgradeableUnsafeMock_init(); __UUPSUpgradeableUnsafeMock_init();
} }
} }
import "./UUPS/TestInProdUpgradeable.sol"; import "./UUPS/UUPSLegacyUpgradeable.sol";
contract UUPSUpgradeableBrokenMockUpgradeableWithInit is UUPSUpgradeableBrokenMockUpgradeable { contract UUPSUpgradeableLegacyMockUpgradeableWithInit is UUPSUpgradeableLegacyMockUpgradeable {
constructor() payable initializer { constructor() payable initializer {
__UUPSUpgradeableBrokenMock_init(); __UUPSUpgradeableLegacyMock_init();
} }
} }
import "./ReentrancyAttackUpgradeable.sol"; import "./ReentrancyAttackUpgradeable.sol";
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
pragma solidity ^0.8.2; pragma solidity ^0.8.2;
import "../beacon/IBeaconUpgradeable.sol"; import "../beacon/IBeaconUpgradeable.sol";
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../../utils/AddressUpgradeable.sol"; import "../../utils/AddressUpgradeable.sol";
import "../../utils/StorageSlotUpgradeable.sol"; import "../../utils/StorageSlotUpgradeable.sol";
import "../utils/Initializable.sol"; import "../utils/Initializable.sol";
...@@ -84,33 +85,23 @@ abstract contract ERC1967UpgradeUpgradeable is Initializable { ...@@ -84,33 +85,23 @@ abstract contract ERC1967UpgradeUpgradeable is Initializable {
* *
* Emits an {Upgraded} event. * Emits an {Upgraded} event.
*/ */
function _upgradeToAndCallSecure( function _upgradeToAndCallUUPS(
address newImplementation, address newImplementation,
bytes memory data, bytes memory data,
bool forceCall bool forceCall
) internal { ) internal {
address oldImplementation = _getImplementation(); // Upgrades from old implementations will perform a rollback test. This test requires the new
// implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
// Initial upgrade and setup call // this special case will break upgrade paths from old UUPS implementation to new ones.
_setImplementation(newImplementation); if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
if (data.length > 0 || forceCall) { _setImplementation(newImplementation);
_functionDelegateCall(newImplementation, data); } else {
} try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
// Perform rollback test if not already in progress } catch {
StorageSlotUpgradeable.BooleanSlot storage rollbackTesting = StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT); revert("ERC1967Upgrade: new implementation is not UUPS");
if (!rollbackTesting.value) { }
// Trigger rollback using upgradeTo from the new implementation _upgradeToAndCall(newImplementation, data, forceCall);
rollbackTesting.value = true;
_functionDelegateCall(
newImplementation,
abi.encodeWithSignature("upgradeTo(address)", oldImplementation)
);
rollbackTesting.value = false;
// Check rollback was effective
require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
// Finally reset to the new implementation and log the upgrade
_upgradeTo(newImplementation);
} }
} }
......
...@@ -17,14 +17,14 @@ In order to avoid clashes with the storage variables of the implementation contr ...@@ -17,14 +17,14 @@ In order to avoid clashes with the storage variables of the implementation contr
There are two alternative ways to add upgradeability to an ERC1967 proxy. Their differences are explained below in <<transparent-vs-uups>>. There are two alternative ways to add upgradeability to an ERC1967 proxy. Their differences are explained below in <<transparent-vs-uups>>.
- {TransparentUpgradeableProxy}: A proxy with a built in admin and upgrade interface. - {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. - {UUPSUpgradeable}: An upgradeability mechanism to be included in the implementation contract.
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. 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.
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. 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.
- {BeaconProxy}: A proxy that retreives its implementation from a beacon contract. - {BeaconProxy}: A proxy that retreives its implementation from a beacon contract.
- {UpgradeableBeacon}: A beacon contract that can be upgraded. - {UpgradeableBeacon}: A beacon contract with a built in admin that can upgrade the {BeaconProxy} pointing to it.
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. 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.
...@@ -48,6 +48,8 @@ By default, the upgrade functionality included in {UUPSUpgradeable} contains a s ...@@ -48,6 +48,8 @@ By default, the upgrade functionality included in {UUPSUpgradeable} contains a s
- Adding a flag mechanism in the implementation that will disable the upgrade function when triggered. - 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. - 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.
The current implementation of this security mechanism uses https://eips.ethereum.org/EIPS/eip-1822[EIP1822] to detect the storage slot used by the implementation. A previous implementation, now deprecated, relied on a rollback check. It is possible to upgrade from a contract using the old mechanism to a new one. The inverse is however not possible, as old implementations (before version 4.5) did not include the `ERC1822` interface.
== Core == Core
{{Proxy}} {{Proxy}}
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../ERC1967/ERC1967UpgradeUpgradeable.sol"; import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
import "./Initializable.sol"; import "./Initializable.sol";
...@@ -18,7 +19,7 @@ import "./Initializable.sol"; ...@@ -18,7 +19,7 @@ import "./Initializable.sol";
* *
* _Available since v4.1._ * _Available since v4.1._
*/ */
abstract contract UUPSUpgradeable is Initializable, ERC1967UpgradeUpgradeable { abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
function __UUPSUpgradeable_init() internal onlyInitializing { function __UUPSUpgradeable_init() internal onlyInitializing {
__ERC1967Upgrade_init_unchained(); __ERC1967Upgrade_init_unchained();
__UUPSUpgradeable_init_unchained(); __UUPSUpgradeable_init_unchained();
...@@ -43,6 +44,27 @@ abstract contract UUPSUpgradeable is Initializable, ERC1967UpgradeUpgradeable { ...@@ -43,6 +44,27 @@ abstract contract UUPSUpgradeable is Initializable, ERC1967UpgradeUpgradeable {
} }
/** /**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
_;
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate that the this implementation remains valid after an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
return _IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`. * @dev Upgrade the implementation of the proxy to `newImplementation`.
* *
* Calls {_authorizeUpgrade}. * Calls {_authorizeUpgrade}.
...@@ -51,7 +73,7 @@ abstract contract UUPSUpgradeable is Initializable, ERC1967UpgradeUpgradeable { ...@@ -51,7 +73,7 @@ abstract contract UUPSUpgradeable is Initializable, ERC1967UpgradeUpgradeable {
*/ */
function upgradeTo(address newImplementation) external virtual onlyProxy { function upgradeTo(address newImplementation) external virtual onlyProxy {
_authorizeUpgrade(newImplementation); _authorizeUpgrade(newImplementation);
_upgradeToAndCallSecure(newImplementation, new bytes(0), false); _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
} }
/** /**
...@@ -64,7 +86,7 @@ abstract contract UUPSUpgradeable is Initializable, ERC1967UpgradeUpgradeable { ...@@ -64,7 +86,7 @@ abstract contract UUPSUpgradeable is Initializable, ERC1967UpgradeUpgradeable {
*/ */
function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy { function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
_authorizeUpgrade(newImplementation); _authorizeUpgrade(newImplementation);
_upgradeToAndCallSecure(newImplementation, data, true); _upgradeToAndCallUUPS(newImplementation, data, true);
} }
/** /**
......
...@@ -31,7 +31,7 @@ abstract contract ERC20FlashMintUpgradeable is Initializable, ERC20Upgradeable, ...@@ -31,7 +31,7 @@ abstract contract ERC20FlashMintUpgradeable is Initializable, ERC20Upgradeable,
* @param token The address of the token that is requested. * @param token The address of the token that is requested.
* @return The amont of token that can be loaned. * @return The amont of token that can be loaned.
*/ */
function maxFlashLoan(address token) public view override returns (uint256) { function maxFlashLoan(address token) public view virtual override returns (uint256) {
return token == address(this) ? type(uint256).max - ERC20Upgradeable.totalSupply() : 0; return token == address(this) ? type(uint256).max - ERC20Upgradeable.totalSupply() : 0;
} }
...@@ -70,6 +70,7 @@ abstract contract ERC20FlashMintUpgradeable is Initializable, ERC20Upgradeable, ...@@ -70,6 +70,7 @@ abstract contract ERC20FlashMintUpgradeable is Initializable, ERC20Upgradeable,
uint256 amount, uint256 amount,
bytes calldata data bytes calldata data
) public virtual override returns (bool) { ) public virtual override returns (bool) {
require(amount <= maxFlashLoan(token), "ERC20FlashMint: amount exceeds maxFlashLoan");
uint256 fee = flashFee(token, amount); uint256 fee = flashFee(token, amount);
_mint(address(receiver), amount); _mint(address(receiver), amount);
require( require(
......
...@@ -29,14 +29,14 @@ abstract contract ERC20VotesCompUpgradeable is Initializable, ERC20VotesUpgradea ...@@ -29,14 +29,14 @@ abstract contract ERC20VotesCompUpgradeable is Initializable, ERC20VotesUpgradea
/** /**
* @dev Comp version of the {getVotes} accessor, with `uint96` return type. * @dev Comp version of the {getVotes} accessor, with `uint96` return type.
*/ */
function getCurrentVotes(address account) external view returns (uint96) { function getCurrentVotes(address account) external view virtual returns (uint96) {
return SafeCastUpgradeable.toUint96(getVotes(account)); return SafeCastUpgradeable.toUint96(getVotes(account));
} }
/** /**
* @dev Comp version of the {getPastVotes} accessor, with `uint96` return type. * @dev Comp version of the {getPastVotes} accessor, with `uint96` return type.
*/ */
function getPriorVotes(address account, uint256 blockNumber) external view returns (uint96) { function getPriorVotes(address account, uint256 blockNumber) external view virtual returns (uint96) {
return SafeCastUpgradeable.toUint96(getPastVotes(account, blockNumber)); return SafeCastUpgradeable.toUint96(getPastVotes(account, blockNumber));
} }
......
...@@ -64,7 +64,7 @@ abstract contract ERC20VotesUpgradeable is Initializable, IVotesUpgradeable, ERC ...@@ -64,7 +64,7 @@ abstract contract ERC20VotesUpgradeable is Initializable, IVotesUpgradeable, ERC
/** /**
* @dev Gets the current votes balance for `account` * @dev Gets the current votes balance for `account`
*/ */
function getVotes(address account) public view override returns (uint256) { function getVotes(address account) public view virtual override returns (uint256) {
uint256 pos = _checkpoints[account].length; uint256 pos = _checkpoints[account].length;
return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes; return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes;
} }
...@@ -76,7 +76,7 @@ abstract contract ERC20VotesUpgradeable is Initializable, IVotesUpgradeable, ERC ...@@ -76,7 +76,7 @@ abstract contract ERC20VotesUpgradeable is Initializable, IVotesUpgradeable, ERC
* *
* - `blockNumber` must have been already mined * - `blockNumber` must have been already mined
*/ */
function getPastVotes(address account, uint256 blockNumber) public view override returns (uint256) { function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) {
require(blockNumber < block.number, "ERC20Votes: block not yet mined"); require(blockNumber < block.number, "ERC20Votes: block not yet mined");
return _checkpointsLookup(_checkpoints[account], blockNumber); return _checkpointsLookup(_checkpoints[account], blockNumber);
} }
...@@ -89,7 +89,7 @@ abstract contract ERC20VotesUpgradeable is Initializable, IVotesUpgradeable, ERC ...@@ -89,7 +89,7 @@ abstract contract ERC20VotesUpgradeable is Initializable, IVotesUpgradeable, ERC
* *
* - `blockNumber` must have been already mined * - `blockNumber` must have been already mined
*/ */
function getPastTotalSupply(uint256 blockNumber) public view override returns (uint256) { function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) {
require(blockNumber < block.number, "ERC20Votes: block not yet mined"); require(blockNumber < block.number, "ERC20Votes: block not yet mined");
return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber);
} }
......
...@@ -477,7 +477,7 @@ contract ERC777Upgradeable is Initializable, ContextUpgradeable, IERC777Upgradea ...@@ -477,7 +477,7 @@ contract ERC777Upgradeable is Initializable, ContextUpgradeable, IERC777Upgradea
address holder, address holder,
address spender, address spender,
uint256 value uint256 value
) internal { ) internal virtual {
require(holder != address(0), "ERC777: approve from the zero address"); require(holder != address(0), "ERC777: approve from the zero address");
require(spender != address(0), "ERC777: approve to the zero address"); require(spender != address(0), "ERC777: approve to the zero address");
......
...@@ -21,7 +21,7 @@ abstract contract MulticallUpgradeable is Initializable { ...@@ -21,7 +21,7 @@ abstract contract MulticallUpgradeable is Initializable {
/** /**
* @dev Receives and executes a batch of function calls on this contract. * @dev Receives and executes a batch of function calls on this contract.
*/ */
function multicall(bytes[] calldata data) external returns (bytes[] memory results) { function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
results = new bytes[](data.length); results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) { for (uint256 i = 0; i < data.length; i++) {
results[i] = _functionDelegateCall(address(this), data[i]); results[i] = _functionDelegateCall(address(this), data[i]);
......
const ImplementationLabel = 'eip1967.proxy.implementation';
const AdminLabel = 'eip1967.proxy.admin';
const BeaconLabel = 'eip1967.proxy.beacon';
function labelToSlot (label) {
return '0x' + web3.utils.toBN(web3.utils.keccak256(label)).subn(1).toString(16);
}
function getSlot (address, slot) {
return web3.eth.getStorageAt(
web3.utils.isAddress(address) ? address : address.address,
web3.utils.isHex(slot) ? slot : labelToSlot(slot),
);
}
module.exports = {
ImplementationLabel,
AdminLabel,
BeaconLabel,
ImplementationSlot: labelToSlot(ImplementationLabel),
AdminSlot: labelToSlot(AdminLabel),
BeaconSlot: labelToSlot(BeaconLabel),
getSlot,
};
const { BN, expectRevert } = require('@openzeppelin/test-helpers'); const { expectRevert } = require('@openzeppelin/test-helpers');
const ethereumjsUtil = require('ethereumjs-util'); const { getSlot, ImplementationSlot } = require('../helpers/erc1967');
const { expect } = require('chai'); const { expect } = require('chai');
const DummyImplementation = artifacts.require('DummyImplementation'); const DummyImplementation = artifacts.require('DummyImplementation');
const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation';
function toChecksumAddress (address) {
return ethereumjsUtil.toChecksumAddress('0x' + address.replace(/^0x/, '').padStart(40, '0'));
}
module.exports = function shouldBehaveLikeProxy (createProxy, proxyAdminAddress, proxyCreator) { module.exports = function shouldBehaveLikeProxy (createProxy, proxyAdminAddress, proxyCreator) {
it('cannot be initialized with a non-contract address', async function () { it('cannot be initialized with a non-contract address', async function () {
const nonContractAddress = proxyCreator; const nonContractAddress = proxyCreator;
...@@ -28,9 +22,9 @@ module.exports = function shouldBehaveLikeProxy (createProxy, proxyAdminAddress, ...@@ -28,9 +22,9 @@ module.exports = function shouldBehaveLikeProxy (createProxy, proxyAdminAddress,
const assertProxyInitialization = function ({ value, balance }) { const assertProxyInitialization = function ({ value, balance }) {
it('sets the implementation address', async function () { it('sets the implementation address', async function () {
const slot = '0x' + new BN(ethereumjsUtil.keccak256(Buffer.from(IMPLEMENTATION_LABEL))).subn(1).toString(16); const implementationSlot = await getSlot(this.proxy, ImplementationSlot);
const implementation = toChecksumAddress((await web3.eth.getStorageAt(this.proxy, slot)).substr(-40)); const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40));
expect(implementation).to.be.equal(this.implementation); expect(implementationAddress).to.be.equal(this.implementation);
}); });
it('initializes the proxy', async function () { it('initializes the proxy', async function () {
......
const { BN, expectRevert } = require('@openzeppelin/test-helpers'); const { expectRevert } = require('@openzeppelin/test-helpers');
const ethereumjsUtil = require('ethereumjs-util'); const { getSlot, BeaconSlot } = require('../../helpers/erc1967');
const { keccak256 } = ethereumjsUtil;
const { expect } = require('chai'); const { expect } = require('chai');
...@@ -11,13 +10,6 @@ const DummyImplementationV2 = artifacts.require('DummyImplementationV2'); ...@@ -11,13 +10,6 @@ const DummyImplementationV2 = artifacts.require('DummyImplementationV2');
const BadBeaconNoImpl = artifacts.require('BadBeaconNoImpl'); const BadBeaconNoImpl = artifacts.require('BadBeaconNoImpl');
const BadBeaconNotContract = artifacts.require('BadBeaconNotContract'); const BadBeaconNotContract = artifacts.require('BadBeaconNotContract');
function toChecksumAddress (address) {
return ethereumjsUtil.toChecksumAddress('0x' + address.replace(/^0x/, '').padStart(40, '0').substr(-40));
}
const BEACON_LABEL = 'eip1967.proxy.beacon';
const BEACON_SLOT = '0x' + new BN(keccak256(Buffer.from(BEACON_LABEL))).subn(1).toString(16);
contract('BeaconProxy', function (accounts) { contract('BeaconProxy', function (accounts) {
const [anotherAccount] = accounts; const [anotherAccount] = accounts;
...@@ -53,7 +45,8 @@ contract('BeaconProxy', function (accounts) { ...@@ -53,7 +45,8 @@ contract('BeaconProxy', function (accounts) {
describe('initialization', function () { describe('initialization', function () {
before(function () { before(function () {
this.assertInitialized = async ({ value, balance }) => { this.assertInitialized = async ({ value, balance }) => {
const beaconAddress = toChecksumAddress(await web3.eth.getStorageAt(this.proxy.address, BEACON_SLOT)); const beaconSlot = await getSlot(this.proxy, BeaconSlot);
const beaconAddress = web3.utils.toChecksumAddress(beaconSlot.substr(-40));
expect(beaconAddress).to.equal(this.beacon.address); expect(beaconAddress).to.equal(this.beacon.address);
const dummy = new DummyImplementation(this.proxy.address); const dummy = new DummyImplementation(this.proxy.address);
......
const { BN, expectRevert, expectEvent, constants } = require('@openzeppelin/test-helpers'); const { BN, expectRevert, expectEvent, constants } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const ethereumjsUtil = require('ethereumjs-util'); const { getSlot, ImplementationSlot, AdminSlot } = require('../../helpers/erc1967');
const { expect } = require('chai'); const { expect } = require('chai');
...@@ -16,13 +16,6 @@ const InitializableMock = artifacts.require('InitializableMock'); ...@@ -16,13 +16,6 @@ const InitializableMock = artifacts.require('InitializableMock');
const DummyImplementation = artifacts.require('DummyImplementation'); const DummyImplementation = artifacts.require('DummyImplementation');
const ClashingImplementation = artifacts.require('ClashingImplementation'); const ClashingImplementation = artifacts.require('ClashingImplementation');
const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation';
const ADMIN_LABEL = 'eip1967.proxy.admin';
function toChecksumAddress (address) {
return ethereumjsUtil.toChecksumAddress('0x' + address.replace(/^0x/, '').padStart(40, '0').substr(-40));
}
module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createProxy, accounts) { module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createProxy, accounts) {
const [proxyAdminAddress, proxyAdminOwner, anotherAccount] = accounts; const [proxyAdminAddress, proxyAdminOwner, anotherAccount] = accounts;
...@@ -312,15 +305,15 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro ...@@ -312,15 +305,15 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro
describe('storage', function () { describe('storage', function () {
it('should store the implementation address in specified location', async function () { it('should store the implementation address in specified location', async function () {
const slot = '0x' + new BN(ethereumjsUtil.keccak256(Buffer.from(IMPLEMENTATION_LABEL))).subn(1).toString(16); const implementationSlot = await getSlot(this.proxy, ImplementationSlot);
const implementation = toChecksumAddress(await web3.eth.getStorageAt(this.proxyAddress, slot)); const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40));
expect(implementation).to.be.equal(this.implementationV0); expect(implementationAddress).to.be.equal(this.implementationV0);
}); });
it('should store the admin proxy in specified location', async function () { it('should store the admin proxy in specified location', async function () {
const slot = '0x' + new BN(ethereumjsUtil.keccak256(Buffer.from(ADMIN_LABEL))).subn(1).toString(16); const proxyAdminSlot = await getSlot(this.proxy, AdminSlot);
const proxyAdmin = toChecksumAddress(await web3.eth.getStorageAt(this.proxyAddress, slot)); const proxyAdminAddress = web3.utils.toChecksumAddress(proxyAdminSlot.substr(-40));
expect(proxyAdmin).to.be.equal(proxyAdminAddress); expect(proxyAdminAddress).to.be.equal(proxyAdminAddress);
}); });
}); });
......
const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { web3 } = require('@openzeppelin/test-helpers/src/setup');
const { getSlot, ImplementationSlot } = require('../../helpers/erc1967');
const ERC1967Proxy = artifacts.require('ERC1967Proxy'); const ERC1967Proxy = artifacts.require('ERC1967Proxy');
const UUPSUpgradeableMock = artifacts.require('UUPSUpgradeableMock'); const UUPSUpgradeableMock = artifacts.require('UUPSUpgradeableMock');
const UUPSUpgradeableUnsafeMock = artifacts.require('UUPSUpgradeableUnsafeMock'); const UUPSUpgradeableUnsafeMock = artifacts.require('UUPSUpgradeableUnsafeMock');
const UUPSUpgradeableBrokenMock = artifacts.require('UUPSUpgradeableBrokenMock'); const UUPSUpgradeableLegacyMock = artifacts.require('UUPSUpgradeableLegacyMock');
const CountersImpl = artifacts.require('CountersImpl'); const CountersImpl = artifacts.require('CountersImpl');
contract('UUPSUpgradeable', function (accounts) { contract('UUPSUpgradeable', function (accounts) {
...@@ -11,7 +13,6 @@ contract('UUPSUpgradeable', function (accounts) { ...@@ -11,7 +13,6 @@ contract('UUPSUpgradeable', function (accounts) {
this.implInitial = await UUPSUpgradeableMock.new(); this.implInitial = await UUPSUpgradeableMock.new();
this.implUpgradeOk = await UUPSUpgradeableMock.new(); this.implUpgradeOk = await UUPSUpgradeableMock.new();
this.implUpgradeUnsafe = await UUPSUpgradeableUnsafeMock.new(); this.implUpgradeUnsafe = await UUPSUpgradeableUnsafeMock.new();
this.implUpgradeBroken = await UUPSUpgradeableBrokenMock.new();
this.implUpgradeNonUUPS = await CountersImpl.new(); this.implUpgradeNonUUPS = await CountersImpl.new();
}); });
...@@ -44,18 +45,11 @@ contract('UUPSUpgradeable', function (accounts) { ...@@ -44,18 +45,11 @@ contract('UUPSUpgradeable', function (accounts) {
expectEvent(receipt, 'Upgraded', { implementation: this.implUpgradeUnsafe.address }); expectEvent(receipt, 'Upgraded', { implementation: this.implUpgradeUnsafe.address });
}); });
it('reject upgrade to broken upgradeable implementation', async function () {
await expectRevert(
this.instance.upgradeTo(this.implUpgradeBroken.address),
'ERC1967Upgrade: upgrade breaks further upgrades',
);
});
// delegate to a non existing upgradeTo function causes a low level revert // delegate to a non existing upgradeTo function causes a low level revert
it('reject upgrade to non uups implementation', async function () { it('reject upgrade to non uups implementation', async function () {
await expectRevert( await expectRevert(
this.instance.upgradeTo(this.implUpgradeNonUUPS.address), this.instance.upgradeTo(this.implUpgradeNonUUPS.address),
'Address: low-level delegate call failed', 'ERC1967Upgrade: new implementation is not UUPS',
); );
}); });
...@@ -63,10 +57,29 @@ contract('UUPSUpgradeable', function (accounts) { ...@@ -63,10 +57,29 @@ contract('UUPSUpgradeable', function (accounts) {
const { address } = await ERC1967Proxy.new(this.implInitial.address, '0x'); const { address } = await ERC1967Proxy.new(this.implInitial.address, '0x');
const otherInstance = await UUPSUpgradeableMock.at(address); const otherInstance = await UUPSUpgradeableMock.at(address);
// infinite loop reverts when a nested call is out-of-gas
await expectRevert( await expectRevert(
this.instance.upgradeTo(otherInstance.address), this.instance.upgradeTo(otherInstance.address),
'Address: low-level delegate call failed', 'ERC1967Upgrade: new implementation is not UUPS',
); );
}); });
it('can upgrade from legacy implementations', async function () {
const legacyImpl = await UUPSUpgradeableLegacyMock.new();
const legacyInstance = await ERC1967Proxy.new(legacyImpl.address, '0x')
.then(({ address }) => UUPSUpgradeableLegacyMock.at(address));
const receipt = await legacyInstance.upgradeTo(this.implInitial.address);
const UpgradedEvents = receipt.logs.filter(({ address, event }) =>
address === legacyInstance.address &&
event === 'Upgraded',
);
expect(UpgradedEvents.length).to.be.equal(1);
expectEvent(receipt, 'Upgraded', { implementation: this.implInitial.address });
const implementationSlot = await getSlot(legacyInstance, ImplementationSlot);
const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40));
expect(implementationAddress).to.be.equal(this.implInitial.address);
});
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment