Simplify UUPSUpgradeable along the lines of ERC1822 (#3021)
Co-authored-by: Francisco Giordano <frangio.1@gmail.com>pull/3078/head^2
parent
3458c1e854
commit
e192fac276
@ -0,0 +1,20 @@ |
||||
// 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 IERC1822Proxiable { |
||||
/** |
||||
* @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); |
||||
} |
@ -0,0 +1,58 @@ |
||||
// SPDX-License-Identifier: MIT |
||||
|
||||
pragma solidity ^0.8.0; |
||||
|
||||
import "./UUPSUpgradeableMock.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 UUPSUpgradeableLegacyMock is UUPSUpgradeableMock { |
||||
// 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(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); |
||||
StorageSlot.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) { |
||||
Address.functionDelegateCall(newImplementation, data); |
||||
} |
||||
|
||||
// Perform rollback test if not already in progress |
||||
StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT); |
||||
if (!rollbackTesting.value) { |
||||
// Trigger rollback using upgradeTo from the new implementation |
||||
rollbackTesting.value = true; |
||||
Address.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); |
||||
} |
||||
} |
@ -0,0 +1,24 @@ |
||||
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, |
||||
}; |
Loading…
Reference in new issue