Skip to content

Commit

Permalink
♻️ Tidy 7702 code, regen docs (#1358)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vectorized authored Feb 18, 2025
1 parent fe8ca07 commit 9298d09
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 9 deletions.
18 changes: 14 additions & 4 deletions docs/accounts/eip7702proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.



<!-- customintro:start --><!-- customintro:end -->
Expand Down Expand Up @@ -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`.
`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.
142 changes: 142 additions & 0 deletions docs/accounts/libeip7702.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# LibEIP7702

Library for EIP7702 operations.






<!-- customintro:start --><!-- customintro:end -->

## 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.
3 changes: 3 additions & 0 deletions src/accounts/EIP7702Proxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
}
Expand Down
11 changes: 7 additions & 4 deletions src/accounts/LibEIP7702.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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))
Expand All @@ -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())
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/utils/CallContextChecker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
}
Expand Down

0 comments on commit 9298d09

Please sign in to comment.