From 9298d096feb87de9a8873a704ff98f6892064c65 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Wed, 19 Feb 2025 04:09:43 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Tidy=207702=20code,=20rege?= =?UTF-8?q?n=20docs=20(#1358)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/accounts/eip7702proxy.md | 18 +++- docs/accounts/libeip7702.md | 142 +++++++++++++++++++++++++++++++ src/accounts/EIP7702Proxy.sol | 3 + src/accounts/LibEIP7702.sol | 11 ++- src/utils/CallContextChecker.sol | 2 +- 5 files changed, 167 insertions(+), 9 deletions(-) create mode 100644 docs/accounts/libeip7702.md diff --git a/docs/accounts/eip7702proxy.md b/docs/accounts/eip7702proxy.md index 60803c580..fe002539a 100644 --- a/docs/accounts/eip7702proxy.md +++ b/docs/accounts/eip7702proxy.md @@ -13,9 +13,6 @@ EOA -> EIP7702Proxy (relay) -> EIP7702 account implementation. This relay proxy also allows for correctly revealing the "Read as Proxy" and "Write as Proxy" tabs on Etherscan. -This proxy can only be used by a EIP7702 authority. -If any regular contract uses this proxy, it will not work. - @@ -50,4 +47,17 @@ bytes32 internal constant _ERC1967_ADMIN_SLOT = ``` The ERC-1967 storage slot for the admin in the proxy. -`uint256(keccak256("eip1967.proxy.admin")) - 1`. \ No newline at end of file +`uint256(keccak256("eip1967.proxy.admin")) - 1`. + +### _EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT + +```solidity +bytes32 internal constant + _EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT = + 0x94e11c6e41e7fb92cb8bb65e13fdfbd4eba8b831292a1a220f7915c78c7c078f +``` + +The transient storage slot for requesting the proxy to initialize the implementation. +`uint256(keccak256("eip7702.proxy.delegation.initialization.request")) - 1`. +While we would love to use a smaller constant, this slot is used in both the proxy +and the delegation, so we shall just use bytes32 in case we want to standardize this. \ No newline at end of file diff --git a/docs/accounts/libeip7702.md b/docs/accounts/libeip7702.md new file mode 100644 index 000000000..b6a508374 --- /dev/null +++ b/docs/accounts/libeip7702.md @@ -0,0 +1,142 @@ +# LibEIP7702 + +Library for EIP7702 operations. + + + + + + + + +## Custom Errors + +### ProxyQueryFailed() + +```solidity +error ProxyQueryFailed() +``` + +The proxy query has failed. + +### ChangeProxyAdminFailed() + +```solidity +error ChangeProxyAdminFailed() +``` + +Failed to change the proxy admin. + +### UpgradeProxyFailed() + +```solidity +error UpgradeProxyFailed() +``` + +Failed to upgrade the proxy. + +## Constants + +### ERC1967_IMPLEMENTATION_SLOT + +```solidity +bytes32 internal constant ERC1967_IMPLEMENTATION_SLOT = + 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc +``` + +The ERC-1967 storage slot for the implementation in the proxy. +`uint256(keccak256("eip1967.proxy.implementation")) - 1`. + +### EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT + +```solidity +bytes32 internal constant + EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT = + 0x94e11c6e41e7fb92cb8bb65e13fdfbd4eba8b831292a1a220f7915c78c7c078f +``` + +The transient storage slot for requesting the proxy to initialize the implementation. +`uint256(keccak256("eip7702.proxy.delegation.initialization.request")) - 1`. +While we would love to use a smaller constant, this slot is used in both the proxy +and the delegation, so we shall just use bytes32 in case we want to standardize this. + +## Authority Operations + +### delegation(address) + +```solidity +function delegation(address account) + internal + view + returns (address result) +``` + +Returns the delegation of the account. +If the account is not an EIP7702 authority, the `delegation` will be `address(0)`. + +## Proxy Operations + +### proxyImplementation(address) + +```solidity +function proxyImplementation(address proxy) + internal + view + returns (address result) +``` + +Returns the implementation of the proxy. +Assumes that the proxy is a proper EIP7702Proxy, if it exists. + +### proxyAdmin(address) + +```solidity +function proxyAdmin(address proxy) internal view returns (address result) +``` + +Returns the admin of the proxy. +Assumes that the proxy is a proper EIP7702Proxy, if it exists. + +### changeProxyAdmin(address,address) + +```solidity +function changeProxyAdmin(address proxy, address newAdmin) internal +``` + +Changes the admin on the proxy. The caller must be the admin. +Assumes that the proxy is a proper EIP7702Proxy, if it exists. + +### upgradeProxy(address,address) + +```solidity +function upgradeProxy(address proxy, address newImplementation) internal +``` + +Changes the implementation on the proxy. The caller must be the admin. +Assumes that the proxy is a proper EIP7702Proxy, if it exists. + +## Proxy Delegation Operations + +### upgradeProxyDelegation(address) + +```solidity +function upgradeProxyDelegation(address newImplementation) internal +``` + +Upgrades the implementation. +The new implementation will NOT be active until the next UserOp or transaction. +To "auto-upgrade" to the latest implementation on the proxy, pass in `address(0)` to reset +the implementation slot. This causes the proxy to use the latest default implementation, +which may be optionally reinitialized via `requestProxyDelegationInitialization()`. +This function is intended to be used on the authority of an EIP7702Proxy delegation. +The most intended usage pattern is to wrap this in an access-gated admin function. + +### requestProxyDelegationInitialization() + +```solidity +function requestProxyDelegationInitialization() internal +``` + +Requests the implementation to be initialized to the latest implementation on the proxy. +This function is intended to be used on the authority of an EIP7702Proxy delegation. +The most intended usage pattern is to place it at the end of an `execute` function. \ No newline at end of file diff --git a/src/accounts/EIP7702Proxy.sol b/src/accounts/EIP7702Proxy.sol index 8b370d35e..60c5533de 100644 --- a/src/accounts/EIP7702Proxy.sol +++ b/src/accounts/EIP7702Proxy.sol @@ -35,6 +35,8 @@ contract EIP7702Proxy { /// @dev The transient storage slot for requesting the proxy to initialize the implementation. /// `uint256(keccak256("eip7702.proxy.delegation.initialization.request")) - 1`. + /// While we would love to use a smaller constant, this slot is used in both the proxy + /// and the delegation, so we shall just use bytes32 in case we want to standardize this. bytes32 internal constant _EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT = 0x94e11c6e41e7fb92cb8bb65e13fdfbd4eba8b831292a1a220f7915c78c7c078f; @@ -127,6 +129,7 @@ contract EIP7702Proxy { if tload(_EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT) { let implSlot := _ERC1967_IMPLEMENTATION_SLOT // The `implementation` is still at `calldatasize()` in memory. + // Preserve the upper 96 bits when updating in case they are used for some stuff. sstore(implSlot, or(shl(160, shr(160, sload(implSlot))), mload(calldatasize()))) tstore(_EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT, 0) // Clear. } diff --git a/src/accounts/LibEIP7702.sol b/src/accounts/LibEIP7702.sol index 21ff23d58..a8963673f 100644 --- a/src/accounts/LibEIP7702.sol +++ b/src/accounts/LibEIP7702.sol @@ -28,7 +28,9 @@ library LibEIP7702 { /// @dev The transient storage slot for requesting the proxy to initialize the implementation. /// `uint256(keccak256("eip7702.proxy.delegation.initialization.request")) - 1`. - bytes32 internal constant _EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT = + /// While we would love to use a smaller constant, this slot is used in both the proxy + /// and the delegation, so we shall just use bytes32 in case we want to standardize this. + bytes32 internal constant EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT = 0x94e11c6e41e7fb92cb8bb65e13fdfbd4eba8b831292a1a220f7915c78c7c078f; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ @@ -89,7 +91,7 @@ library LibEIP7702 { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x8f283970) // `changeAdmin(address)`. - mstore(0x20, shr(96, shl(96, newAdmin))) + mstore(0x20, newAdmin) // The implementation will clean the upper 96 bits. if iszero(and(eq(mload(0x00), 1), call(gas(), proxy, 0, 0x1c, 0x24, 0x00, 0x20))) { mstore(0x00, 0xc502e37e) // `ChangeProxyAdminFailed()`. revert(0x1c, 0x04) @@ -103,7 +105,7 @@ library LibEIP7702 { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x0900f010) // `upgrade(address)`. - mstore(0x20, shr(96, shl(96, newImplementation))) + mstore(0x20, newImplementation) // The implementation will clean the upper 96 bits. if iszero(and(eq(mload(0x00), 1), call(gas(), proxy, 0, 0x1c, 0x24, 0x00, 0x20))) { mstore(0x00, 0xc6edd882) // `UpgradeProxyFailed()`. revert(0x1c, 0x04) @@ -126,6 +128,7 @@ library LibEIP7702 { /// @solidity memory-safe-assembly assembly { let s := ERC1967_IMPLEMENTATION_SLOT + // Preserve the upper 96 bits when updating in case they are used for some stuff. mstore(0x00, sload(s)) mstore(0x0c, shl(96, newImplementation)) sstore(s, mload(0x00)) @@ -140,7 +143,7 @@ library LibEIP7702 { assembly { if iszero(shl(96, sload(ERC1967_IMPLEMENTATION_SLOT))) { // Use a dedicated transient storage slot for better Swiss-cheese-model safety. - tstore(_EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT, address()) + tstore(EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT, address()) } } } diff --git a/src/utils/CallContextChecker.sol b/src/utils/CallContextChecker.sol index 2408f99e4..83a7d4d7b 100644 --- a/src/utils/CallContextChecker.sol +++ b/src/utils/CallContextChecker.sol @@ -35,7 +35,7 @@ contract CallContextChecker { /// @solidity memory-safe-assembly assembly { extcodecopy(address(), 0x00, 0x00, 0x20) - // Note: checking that it starts with hex"ef01" is the most general and futureproof. + // Note: Checking that it starts with hex"ef01" is the most general and futureproof. // 7702 bytecode is `abi.encodePacked(hex"ef01", uint8(version), address(delegation))`. result := eq(0xef01, shr(240, mload(0x00))) }