@@ -19,6 +19,10 @@ contract EIP7702Proxy {
1919 /// @dev For allowing the differentiation of the EOA and the proxy itself.
2020 uint256 internal immutable __self = uint256 (uint160 (address (this )));
2121
22+ /// @dev The default implementation. Provided for optimization.
23+ /// Set if the `initialAdmin == address(0) && initialImplementation != address(0)`.
24+ uint256 internal immutable _defaultImplementation;
25+
2226 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
2327 /* STORAGE */
2428 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
@@ -45,11 +49,18 @@ contract EIP7702Proxy {
4549 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
4650
4751 constructor (address initialImplementation , address initialAdmin ) payable {
52+ uint256 defaultImplementation;
4853 /// @solidity memory-safe-assembly
4954 assembly {
50- sstore (_ERC1967_IMPLEMENTATION_SLOT, shr (96 , shl (96 , initialImplementation)))
51- sstore (_ERC1967_ADMIN_SLOT, shr (96 , shl (96 , initialAdmin)))
55+ let implementation := shr (96 , shl (96 , initialImplementation))
56+ let admin := shr (96 , shl (96 , initialAdmin))
57+ // We will store the implementation in the storage regardless,
58+ // to aid proxy detection on block explorers.
59+ sstore (_ERC1967_IMPLEMENTATION_SLOT, implementation)
60+ sstore (_ERC1967_ADMIN_SLOT, admin)
61+ defaultImplementation := mul (lt (admin, iszero (iszero (implementation))), implementation)
5262 }
63+ _defaultImplementation = defaultImplementation;
5364 }
5465
5566 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
@@ -58,6 +69,7 @@ contract EIP7702Proxy {
5869
5970 fallback () external payable virtual {
6071 uint256 s = __self;
72+ uint256 defaultImplementation = _defaultImplementation;
6173 /// @solidity memory-safe-assembly
6274 assembly {
6375 mstore (0x40 , returndatasize ()) // Optimization trick to change `6040608052` into `3d604052`.
@@ -106,35 +118,41 @@ contract EIP7702Proxy {
106118 // Workflow for the EIP7702 authority (i.e. the EOA).
107119 let impl := sload (_ERC1967_IMPLEMENTATION_SLOT) // The preferred implementation on the EOA.
108120 calldatacopy (0x00 , 0x00 , calldatasize ()) // Copy the calldata for the delegatecall.
109- // If the preferred implementation is `address(0)`, perform the initialization workflow .
121+ // If the preferred implementation is `address(0)`.
110122 if iszero (shl (96 , impl)) {
111- if iszero (
112- and ( // The arguments of `and` are evaluated from right to left.
113- delegatecall (
114- gas (), mload (calldatasize ()), 0x00 , calldatasize (), calldatasize (), 0x00
115- ),
116- // Fetch the implementation from the proxy.
117- staticcall (gas (), s, calldatasize (), 0x00 , calldatasize (), 0x20 )
118- )
119- ) {
123+ // If `defaultImplementation` is `address(0)`, perform the initialization workflow.
124+ if iszero (defaultImplementation) {
125+ if iszero (
126+ and ( // The arguments of `and` are evaluated from right to left.
127+ delegatecall (
128+ gas (), mload (calldatasize ()), 0x00 , calldatasize (), 0x00 , 0x00
129+ ),
130+ // Fetch the implementation from the proxy.
131+ staticcall (gas (), s, calldatasize (), 0x00 , calldatasize (), 0x20 )
132+ )
133+ ) {
134+ returndatacopy (0x00 , 0x00 , returndatasize ())
135+ revert (0x00 , returndatasize ())
136+ }
137+ // Because we cannot reliably and efficiently tell if the call is made
138+ // via staticcall or call, we shall ask the delegation to make a proxy delegation
139+ // initialization request to signal that we should initialize the storage slot with
140+ // the actual implementation. This also gives flexibility on whether to let the
141+ // proxy auto-upgrade, or let the authority manually upgrade (via 7702 or passkey).
142+ // A non-zero value in the transient storage denotes a initialization request.
143+ if tload (_EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT) {
144+ let implSlot := _ERC1967_IMPLEMENTATION_SLOT
145+ // The `implementation` is still at `calldatasize()` in memory.
146+ // Preserve the upper 96 bits when updating in case they are used for some stuff.
147+ sstore (
148+ implSlot, or (shl (160 , shr (160 , sload (implSlot))), mload (calldatasize ()))
149+ )
150+ tstore (_EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT, 0 ) // Clear.
151+ }
120152 returndatacopy (0x00 , 0x00 , returndatasize ())
121- revert (0x00 , returndatasize ())
153+ return (0x00 , returndatasize ())
122154 }
123- // Because we cannot reliably and efficiently tell if the call is made
124- // via staticcall or call, we shall ask the delegation to make a proxy delegation
125- // initialization request to signal that we should initialize the storage slot with
126- // the actual implementation. This also gives flexibility on whether to let the
127- // proxy auto-upgrade, or let the authority manually upgrade (via 7702 or passkey).
128- // A non-zero value in the transient storage denotes a initialization request.
129- if tload (_EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT) {
130- let implSlot := _ERC1967_IMPLEMENTATION_SLOT
131- // The `implementation` is still at `calldatasize()` in memory.
132- // Preserve the upper 96 bits when updating in case they are used for some stuff.
133- sstore (implSlot, or (shl (160 , shr (160 , sload (implSlot))), mload (calldatasize ())))
134- tstore (_EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT, 0 ) // Clear.
135- }
136- returndatacopy (0x00 , 0x00 , returndatasize ())
137- return (0x00 , returndatasize ())
155+ impl := defaultImplementation
138156 }
139157 // Otherwise, just delegatecall and bubble up the results without initialization.
140158 if iszero (delegatecall (gas (), impl, 0x00 , calldatasize (), calldatasize (), 0x00 )) {
0 commit comments