-
Notifications
You must be signed in to change notification settings - Fork 11.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Simplify UUPSUpgradeable along the lines of ERC1822 (#3021)
Co-authored-by: Francisco Giordano <[email protected]>
- Loading branch information
Showing
12 changed files
with
191 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.