/// @title ERC1967 Upgrade
/// @notice SPDX-License-Identifier: MIT
/// @author asnared <https://github.com/abigger87>
/// @author Maddiaa <https://github.com/cheethas>
/// @dev This abstract contract provides getters and event emitting update functions for https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
/// @notice Adapted from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/ERC1967/ERC1967Upgrade.sol)
#include "../utils/CommonErrors.huff"
// Function Definitions
#define function implementation() view returns (address)
/// @notice Foreign implementation function
#define function proxiableUUID() nonpayable returns (bytes32)
// EIP 1967 Constants
/// @notice This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
#define constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143
/// @notice Storage slot with the address of the current implementation
/// @notice This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1
/// @notice Validated in the constructor
#define constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
/// @notice Storage slot with the admin of the contract
/// @notice This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1
/// @notice Validated in the constructor
#define constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
/// @notice The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy
/// @notice This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1))
/// @notice Validated in the constructor
#define constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50
// Events
/// @notice Emitted when the implementation is upgraded.
#define event Upgraded(address indexed implementation)
/// @notice Emitted when the admin account has changed
#define event AdminChanged(address previousAdmin, address newAdmin)
/// @notice Emitted when the beacon is upgraded
#define event BeaconUpgraded(address indexed beacon)
// Implementation Macros
/// @notice Returns the current implementation
#define macro GET_IMPLEMENTATION() = takes (0) returns (1) {
[_IMPLEMENTATION_SLOT] sload // [address]
}
/// @notice Sets the implementation address of the proxy
#define macro SET_IMPLEMENTATION() = takes (1) returns (0) {
// Input Stack: [address]
// Check that the address is a contract
dup1 extcodesize iszero iszero // [isContract(address), address]
__Is_Contract_JUMP jumpi // [address]
NON_CONTRACT(0x00)
// Set the implementation slot
__Is_Contract_JUMP:
[_IMPLEMENTATION_SLOT] sstore // []
}
/// @notice Perform implementation upgrade
#define macro UPGRADE_TO() = takes (1) returns (0) {
// Input Stack: [address]
// Set the implementation
dup1 SET_IMPLEMENTATION() // [address]
// Emit the Upgraded event
__EVENT_HASH(Upgraded) 0x00 0x00 log2 // []
}
/// @notice Perform implementation upgrade with additional setup call.
#define macro UPGRADE_TO_AND_CALL() = takes (3) returns (0) {
// Input Stack: [address, data, forceCall]
// Set the implementation
dup1 UPGRADE_TO() // [address, data, forceCall]
// Copy the data from calldata to memory
swap2 not // [!forceCall, data, address]
dup2 calldataload dup1 // [dataSize, dataSize, forceCall, data, address]
swap2 swap1 iszero and // [(!forceCall && dataSize == 0), dataSize, data, address]
__IgnoreCall_And_Clean_Stack jumpi
dup1 swap2 0x20 add // [&data[0], dataSize, dataSize, address]
0x00 // [destOffset, calldataOffset, dataSize, dataSize, address]
calldatacopy // [dataSize, address]
// Call the implementation with the given data
0x00 // [retOffset, dataSize, address]
swap2 // [argSize, retOffset, address]
0x00 // [argOffset, argSize, retOffset, address]
0x00 // [retSize, argOffset, argSize, retOffset, address]
swap4 // [address, argOffset, argSize, retOffset, retSize]
gas // [gas, address, argOffset, argSize, retOffset, retSize]
delegatecall // [success]
iszero iszero __DelegateCall_Success jumpi // []
0x00 dup1 revert
__IgnoreCall_And_Clean_Stack:
pop pop pop // []
__DelegateCall_Success:
}
/// @notice Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
#define macro UPGRADE_TO_AND_CALL_UUPS() = takes (3) returns (0) {
// 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
// this special case will break upgrade paths from old UUPS implementation to new ones.
// Input Stack: [newImplementation, data, forceCall]
// Check if there is a value in the rollback slot, if so continue
// if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
[_ROLLBACK_SLOT] sload // [rollbackslot.value]
iszero else jumpi // [!rollbackslot.value]
SET_IMPLEMENTATION()
continue jump
else:
// Branch Input Stack : [newImplementation, data, forceCall]
// call the new implementation
// store function selector in mem
0x00 // [0, newImplementation, data, forceCall]
__FUNC_SIG(proxiableUUID) // [funcSig(proxiableUUID), 0, newImplementation, data, forceCall]
dup2 // [0, funcSig(proxiableUUID), 0, newImplementation, data, forceCall]
mstore // [0, newImplementation, data, forceCall]
// Call
0x20 // [retSize, 0, newImplementation, data, forceCall]
dup2 // [retOffset, retSize, 0, newImplementation, data, forceCall]
0x04 // [argSize, retOffset, retSize, 0, newImplementation, data, forceCall]
0x1c // [argOffset, argSize, retOffset ,retSize, 0, newImplementation, data, forceCall]
dup5 // [value(0), argOffset, argSize, retOffset ,retSize, 0, newImplementation, data, forceCall]
dup7 // [address, value(0), argOffset, argSize, retOffset ,retSize, 0, newImplementation, data, forceCall]
gas // [gas, address, value(0), argOffset, argSize, retOffset ,retSize, 0, newImplementation, data, forceCall]
call // [success, 0, newImplementation, data, forceCall]
// check call success
iszero not_supported_check_revert jumpi
// Try success block
// The return implementation slot is now stored between 00:32 in the current call frame's memory
mload // [slot, newImplementation, data, forceCall]
[_IMPLEMENTATION_SLOT] eq // [(slot == IMPLEMENTATION_SLOT), newImplementation, data, forceCall]
iszero not_uups_revert jumpi // [newImplementation, data, forceCall]
UPGRADE_TO_AND_CALL()
// Continue past revert logic
continue jump
// Branch Input stack: [0, funcSig(proxiableUUID), newImplementation, data, forceCall]
// This branch will terminate execution
not_supported_check_revert:
// Emit error message that the proxy call failed
REVERT_UNSUPPORTED_PROXIABLE_UUID(0x00)
not_uups_revert:
REVERT_UPGRADE_IMPLEMENTATION_NOT_UUPS(0x00)
continue:
// NOTE: Execution will continue after this macro, make sure you terminate execution in the calling block
}
// Admin Macros
/// @notice Returns the current admin
#define macro GET_ADMIN() = takes (0) returns (1) {
[_ADMIN_SLOT] sload // [address]
}
/// @notice Sets the admin address of the proxy
#define macro SET_ADMIN() = takes (1) returns (0) {
// Input Stack: [address]
// Check that the address != address(0)
dup1 iszero iszero // [address != address(0), address]
__Is_Not_Zero_addr_JUMP jumpi // [address]
ZERO_ADDRESS(0x00)
// Set the admin slot
__Is_Not_Zero_addr_JUMP:
[_ADMIN_SLOT] sstore // []
}
/// @notice Change the admin
#define macro CHANGE_ADMIN() = takes (1) returns (0) {
// Input Stack: [address]
// Get the previous admin
GET_ADMIN() // [previousAdmin, address]
// Set the admin
dup2 SET_ADMIN() // [previousAdmin, address]
// Emit the Upgraded event
__EVENT_HASH(AdminChanged) 0x00 0x00 log3 // []
}
// Beacon Macros
/// @notice Returns the current beacon
#define macro GET_BEACON() = takes (0) returns (1) {
[_BEACON_SLOT] sload // [bytes32]
}
/// @notice Sets the proxy beacon
#define macro SET_BEACON() = takes (1) returns (0) {
// Input Stack: [address]
// Check that the address is a contract
dup1 extcodesize iszero iszero // [isContract(address), address]
__Is_Contract_JUMP jumpi // [address]
NON_CONTRACT(0x00)
// Set the beacon slot
__Is_Contract_JUMP:
// Staticcall the implementation on address
__FUNC_SIG(implementation) 0x00 mstore // [address]
0x20 // [retSize, address]
0x00 // [retOffset, retSize, address]
0x04 // [argSize, retOffset, retSize, address]
0x1c // [argOffset, argSize, retOffset, retSize, address]
dup5 // [address, argOffset, argSize, retOffset, retSize, address]
gas // [gas, address, argOffset, argSize, retOffset, retSize, address]
staticcall // [success, address]
iszero iszero __StaticCall_Success jumpi // [address]
0x00 dup1 revert
__StaticCall_Success:
// Load the implementation address from the staticcal
0x00 mload // [implementation, address]
extcodesize iszero iszero // [isContract(address), address]
__Is_Contract_JUMP jumpi // [address]
NON_CONTRACT(0x00)
// The implementation is a contract, so we can now set the beacon slot
__Is_Contract_JUMP:
[_BEACON_SLOT] sstore // []
}
#define macro UPGRADE_TO_BEACON_AND_CALL() = takes (0) returns (0) {
// Input Stack: [newBeacon, data, forceCall]
// Set the new beacon
dup1 SET_BEACON() // [newBeacon, data, forceCall]
// Emit the BeaconUpgraded event
dup1 __EVENT_HASH(BeaconUpgraded) // [sig, newBeacon, newBeacon, data, forceCall]
0x00 0x00 log2 // [newBeacon, data, forceCall]
// Copy the calldata to memory
dup2 calldataload dup1 // [dataSize, dataSize, newBeacon, data, forceCall]
swap4 not swap1 iszero and // [(!forceCall && dataSize == 0), newBeacon, data, dataSize]
__IgnoreCall_And_Clean_Stack jumpi
// Get the implementation address from the newBeacon
__FUNC_SIG(implementation) 0x00 mstore // [newBeacon, data, dataSize]
0x20 // [retSize, newBeacon, data, dataSize]
0x00 // [retOffset, retSize, newBeacon, data, dataSize]
0x04 // [argSize, retOffset, retSize, newBeacon, data, dataSize]
0x1c // [argOffset, argSize, retOffset, retSize, newBeacon, data, dataSize]
dup5 // [newBeacon, argOffset, argSize, retOffset, retSize, newBeacon, data, dataSize]
gas // [gas, newBeacon, argOffset, argSize, retOffset, retSize, newBeacon, data, dataSize]
staticcall // [success, newBeacon, data, dataSize]
iszero iszero __ImplCall_Success jumpi // [newBeacon, data, dataSize]
0x00 dup1 revert
__ImplCall_Success:
// Delegate call to the implementation address
0x00 mload // [implementation, newBeacon, data, dataSize]
swap3 swap1 swap2 // [data, dataSize, newBeacon, implementation]
dup2 dup2 0x20 add // [&data[0], dataSize, data, dataSize, newBeacon, implementation]
0x00 // [destOffset, calldataOffset, dataSize, data, dataSize, newBeacon, implementation]
calldatacopy // [data, dataSize, newBeacon, implementation]
swap1 // [dataSize, data, newBeacon, implementation]
// Call the implementation with the given data
0x00 // [retOffset, dataSize, data, newBeacon, implementation]
0x00 // [retSize, retOffset, dataSize, data, newBeacon, implementation]
swap2 // [argSize, retOffset, retSize, data, newBeacon, implementation]
0x00 // [argOffset, argSize, retOffset, retSize, data, newBeacon, implementation]
dup7 // [newBeacon, argOffset, argSize, retOffset, retSize, data, newBeacon, implementation]
gas // [gas, address, argOffset, argSize, retOffset, retSize, data, newBeacon, implementation]
delegatecall // [success, data, newBeacon, implementation]
iszero iszero __DelegateCall_Success jumpi // [data, newBeacon, implementation]
0x00 dup1 revert
__IgnoreCall_And_Clean_Stack:
pop pop pop // []
__DelegateCall_Success:
}