# EIP-7702: A Deep Dive into Smart EOAs with Implementation Examples
_Special thanks to [Péter Garamvölgyi](https://x.com/thegaram33) for feedback and review._
## Introduction
[EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) introduces a new transaction type that allows an [Externally Owned Account (EOA)](https://ethereum.org/en/developers/docs/accounts/#types-of-account) to specify an address as the pointer to its implementation. For example, this address could be a [generic proxy](https://gist.github.com/lightclient/7742e84fde4962f32928c6177eda7523) or [minimal proxy contract](https://eips.ethereum.org/EIPS/eip-1167) that forwards call messages to an upgradable wallet implementation.
_To make the explanation clearer, it is important to distinguish between the "delegated" code and the "delegator" account. For this reason, this article will sometimes refer to an EOA that has delegated to code as a "smart EOA"._
EIP-7702 effectively upgrades EOAs to function like **Smart Contract (SC) wallets**, unlocking programmability, and composability for EOA users, and enabling features like:
1. **Social recovery**: EIP-7702 supports social recovery for EOA users. This is particularly beneficial for users who are afraid of losing their private keys, they can just overcome this with a simple social recovery setup.[^1]
2. **Transaction batching**: Users can streamline transactions by moving away from the traditional two-step "approve-and-transfer" authorization model for ERC-20 tokens, or further batch-sending transfers.
3. **Transaction sponsorship**: Users can defer transaction execution and the payment of gas fees to a third party, such as a Sequencer or Wallet Server. This feature provides convenience for users who may not hold native tokens for gas payments.
4. **Arbitrary signing keys**: Wallets can utilize various key types (e.g. [WebAuthn](https://webauthn.io/), [P256](https://ldapwiki.com/wiki/Wiki.jsp?page=P-256), [BLS](https://en.wikipedia.org/wiki/BLS_digital_signature), etc.) to verify actions,[^2] providing more flexibility in the design space.
5. **Session keys**: Users can delegate session keys with lifecycle and scoped permissions to key custody services, allowing subscription models. Additionally, users can interact with DApps in a restricted, "sandboxed" environment, significantly reducing the risk and impact of phishing attacks by preventing malicious actors from accessing their full wallet or assets.
6. **Custom gas tokens**, **permission control by multi-signature schemes**, and much more...
EIP-7702's composability with [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) also brings significant advantages. This composability allows smart EOAs to seamlessly integrate with the existing ecosystem of SC wallets and infrastructure built around ERC-4337, simplifying development and adoption of new features. Once a user authorizes an address with a smart contract wallet supporting ERC-4337, the EOA address can serve as the `sender` field of `UserOperation` in ERC-4337.
Although smart EOAs can address the issue of "your private key is now known by no one" through mechanisms like social recovery, they do not resolve the issue of "your private key is now known by others," as private keys still grant full authorization over the EOA. Proposals like [EIP-7377](https://eips.ethereum.org/EIPS/eip-7377), which enable migrating EOAs to account abstraction wallets, or future proposals allowing users to deactivate (and potentially reactivate) private key permissions post-EIP-7702, will be necessary to address this gap.
In the meantime, in the absence of protocol-level security guarantees, wallet clients can delete their locally stored "hot" private keys and prompt users to delete any local or external backups of their private keys after authorizing the EOA as a contract wallet. However, such measures cannot fully eliminate the risks of private key leaks, particularly in scenarios involving [supply chain attacks](https://en.wikipedia.org/wiki/Supply_chain_attack), such as backdoors in the libraries used by the wallet or in the wallet client itself.
So, do smart EOAs offer more interesting or convenient features compared to SC wallets? In my initial thoughts, retaining EOA functionalities offers several distinct advantages:
1. **Preserving existing addresses**: e.g. this allows users to retain items like their [Soulbound Tokens (SBTs)](https://vitalik.eth.limo/general/2022/01/26/soulbound.html). However, not everyone may feel comfortable exposing their SBTs (or on-chain activities) publicly, as this could reveal sensitive personal information. Balancing privacy with the need for transparency and verifiability requires other thoughtful solutions.[^3]
2. **Simplified implementation of stealth addresses**: Implementing [stealth addresses](https://vitalik.eth.limo/general/2023/01/20/stealth.html) based on EOAs is more straightforward compared to doing so with SC wallets.
3. **Compatibility with the existing ecosystem**: Users can benefit from the current tech stack, such as maintaining the same address across multiple chains. While these functionalities can also be achieved with contract wallets, EOAs have a simpler path.
Of course, all of these are just preliminary speculations, and the actual impact on user experience will likely depend on how different creative wallet applications leverage these features to gather meaningful feedback and preferences from diverse user groups. For instance, Ethereum's subcultures, such as Cypherpunks, Regens, and Degens (as categorized in [this article](https://www.coindesk.com/opinion/2024/02/07/absolute-essentials-of-ethereum-by-paul-dylan-ennis-an-excerpt)), may each have unique needs and perspectives on wallet design. Hopefully, we will see more diverse use cases and applications emerge in the future.
Next, let’s dive into the protocol itself.
## Protocol Overview
EIP-7702 introduces a new transaction type, `SET_CODE_TX_TYPE` (`0x04`), with the `TransactionPayload` defined as: <a id="transaction-payload-definition"></a>
<div style="overflow-x: auto; white-space: nowrap;">
$$
\text{rlp} \Big( [ \, \text{chain_id}, \, \text{nonce}, \, \text{max_priority_fee_per_gas}, \, \text{max_fee_per_gas}, \, \text{gas_limit}, \, \text{destination}, \, \text{value}, \, \text{data}, \, \text{access_list}, \, \text{authorization_list}, \, \text{signature_y_parity}, \, \text{signature_r}, \, \text{signature_s} \, ] \Big)
$$
</div>
Where the `authorization_list` is defined as:
$$
\text{authorization_list} = [ \, [ \, \text{chain_id}, \, \text{address}, \, \text{nonce}, \, \text{y_parity}, \, \text{r}, \, \text{s} \, ], \, \dots \, ]
$$
The `address` field in the `authorization_list` represents the delegated code's address, while the EOA's address can be recovered from the payload (`chain_id`, `address`,`nonce`) and the signature (`y_parity`, `r`, `s`). This decoupling of the EOA address and the EIP-7702 transaction's `address` allows the authorization to be submitted on behalf of the original EOA by another EOA account, enabling more flexible delegation and sponsored transactions.
The transaction receipt is defined as: <a id="transaction-receipt-definition"></a>
$$
\text{rlp} \Big( [ \, \text{status}, \, \text{cumulative_transaction_gas_used}, \, \text{logs_bloom}, \, \text{logs} \, ] \Big)
$$
Before diving into the code, the following sections highlight a few notable features as warm-ups.
### Enforcing Non-Empty `authorization_list`
The `authorization_list` must not be empty, ensuring that every EIP-7702 transaction includes an explicit intention to authorize an implementation address. Additionally, the protocol validates the parameters of each `authorization_list` tuple, including the `address` length and reasonable ranges for `chain_id`, `nonce`, `y_parity`, `r`, and `s`.
### Delegation Through `authorization_list`
In each tuple of the `authorization_list`, the `address` field represents the authorized address, while the signer’s address is derived from signature and payload. This design allows EIP-7702 to delegate the responsibility of gas payment away from the EOAs being authorized, enabling what is widely known as **sponsored transactions**. For instance, Layer 2 sequencers could integrate EIP-7702 wallet creation interfaces directly or add APIs to collect user authorizations and batch-submit them with an `authorization_list`. Wallet service providers could also assist users without balance in submitting transactions.
Importantly, even if an authorization in the batch fails validation, other authorizations remain unaffected. This design helps the execution of (sponsored) batch authorization transactions.
### A New Case for Nonce Increment
EIP-7702 introduces a new mechanism that increments the nonce of an EOA after each successful authorization. The handling process of nonce is as follows:
1. First, the transaction's nonce is checked to ensure it matches the current nonce of the account. Then the transaction nonce is incremented.
2. The nonce is checked for each authorization in the list to ensure it uses the correct value. Then the corresponding authorization nonce is incremented when the delegation is successful.
For the code path, please refer to the [Nonce Increment Logic](#Nonce-Increment-Logic) session.
This means that if a transaction includes an authorization list and the authorization signer is the same as the transaction signer, you must set `tx.nonce` to `account.nonce` and `authorization.nonce` to `account.nonce + 1`. If a transaction contains multiple authorizations signed by the same EOA (which will not happen in practical use cases), the nonce of each authorization must be incremented sequentially. Overall, this mechanism may add some complexity for SDKs to initialize delegate transactions, especially when handling all cases.
### Multi-Path Balance State Changes
With EIP-7702, from the perspective of an EOA, ETH balances can now decrease not only through signed transactions but also through transactions triggering contract executions. From the view of smart contracts, EOAs can directly send transactions to change account states. This introduces the following considerations:
1. Previously, EOAs could only send ETH via signed transactions, allowing transaction pools to invalidate pending transactions with insufficient sender balances by checking the balances of senders in new blocks. However, with EIP-7702, delegated code can modify an EOA's balance at any time, making this invariant incompatible and requiring reconsidering related transaction pool logic.
2. EIP-7702 wallet implementations cannot rely on locally maintained invariants and must query real account states, as EOAs can initiate transactions at any time to alter their balances.
### In-Protocol Revocation
Users can utilize EIP-7702 to modify the authorized address. If the `address` field is set to `0x0000000000000000000000000000000000000000`, the previous authorization will be revoked. This clears the account’s code and resets the account’s code hash to the empty hash.
### Re-delegation
Re-delegating an account requires careful storage management to avoid collisions, which can otherwise lead to undefined behavior. To address the challenge in account migration, [ERC-7201](https://eips.ethereum.org/EIPS/eip-7201) roots storage layouts at unique slots, avoiding storage location collision during re-delegation. [ERC-7779 (draft)](https://eips.ethereum.org/EIPS/eip-7779) also provides a standardized re-delegation process for EIP-7702 wallets, by:
1. Using ERC-7201 to prevent storage location conflicts.
2. Validating storage compatibility before re-delegation.
3. Invoking old wallet interfaces to clean up wallet data.
### Classic EIP-1559 Fields
EIP-7702 adopts the gas fee model defined by [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), requiring the sender to specify `max_priority_fee_per_gas` and `max_fee_per_gas` instead of a single `gas_price` field. The transaction can also include fields such as `access_list` and `data`, allowing it to retain most of the capabilities of other Ethereum transactions (though it cannot [carry blobs](https://eips.ethereum.org/EIPS/eip-4844)). Similar to [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844), EIP-7702 enforces a restriction that the `destination` (also known as `to` in other types of transactions) field cannot be empty, aligning with the common practice of restricting contract creation.
Another well-known ban in Ethereum involves banning storage-related instructions (e.g. [EIP-7562](https://eips.ethereum.org/EIPS/eip-7562)). EIP-7702 does not impose such a ban, since storage instructions are important for SC wallets.
Further details will be introduced in the next section.
## Core Protocol Implementations
In this section, we will dig into:
1. The implementation of an EIP-7702 transaction's journey.
2. Interacting with an address that is delegated to code.
The code analysis focuses on the open-sourced repo [Geth](https://github.com/ethereum/go-ethereum), one of the major Ethereum clients listed on the [Client Diversity Dashboard](https://clientdiversity.org/#distribution). The full pull request for the EIP-7702 feature can be found [here](https://github.com/ethereum/go-ethereum/pull/30078).
_Before doing anything, we assume that the node has activated EIP-7702, whether through a hard fork (from a block height, or from a timestamp) or from the genesis block._
### Sending an EIP-7702 Transaction
To send an EIP-7702 transaction, the user first needs to fill in all non-signature fields outside of the `authorization_list` ([definition of `SetCodeTx` here](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/tx_setcode.go#L49-L67)). Then, the user constructs the `authorization_list` [here](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/tx_setcode.go#L61). For each `authorization tuple`, the user fills in the [non-signature fields](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/tx_setcode.go#L73-L75) first, and then signs it using the [SignAuth](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/tx_setcode.go#L88-L96) function.
The user's EOA private key is required for signing. Signing involves hashing the [authorization details](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/tx_setcode.go#L112-L118), which include a magic number `0x05`, a chain ID, the delegated code address, and the current nonce of the EOA. The magic number `0x05` is also a domain separator in EIP-7202 (another is `SET_CODE_TX_TYPE` `0x04`) to ensure that when data of different types happens to encode into the same byte representation, the resulting hash being signed does not collide across domains. More discussions of current signing domain constants can be found [here](https://github.com/ethereum/EIPs/pull/8835).
Notably, setting the chain ID to `0` allows the authorization to be replayed across all EVM-compatible chains supporting EIP-7702, provided the nonce matches. However, since the nonce must be the same, reusing authorization across chains in practice may be challenging. For example, for EOAs with non-zero nonces.
Once all fields except the [signature fields of transaction](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/tx_setcode.go#L63-L66) are prepared, the user initializes the transaction ([instructions here](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/transaction.go#L65-L70)) and a "[prague signer](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/transaction_signing.go#L183-L193)" (which adds support for [EIP-7702 transaction hashing](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/transaction_signing.go#L236-L249)). The user then signs the transaction using [SignTx](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/transaction_signing.go#L108-L116) and serializes it into a byte array using [RLP encoding](https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/) via [MarshalBinary](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/ethclient/ethclient.go#L626). Finally, the RLP byte array of transaction is [hex-encoded and sent via the `eth_sendRawTransaction` RPC method](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/ethclient/ethclient.go#L630).
When a node receives the transaction through JSON-RPC, it validates it and adds it to the transaction pool[^4], broadcasting it to other nodes. Eventually, a block proposer attempts to include it in a block. Before inclusion, the proposer performs [preCheck](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L408), which includes some additional checks for EIP-7702 transactions:
1. Ensuring the transaction is not a contract creation transaction ([details here](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L376-L378)).
2. Checking that the transaction includes at least one authorization operation ([enforced here](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L379-L381)).
Before executing the transaction, the node calculates [IntrinsicGas](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L420), accounting for the gas cost of accessing the `authorization_list` ([details here](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L116-L118)). A `25000` gas cost for each authorization tuple, defined as [CallNewAccountGas](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/params/protocol_params.go#L35) (also the `PER_EMPTY_ACCOUNT_COST` in [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702)) is consumed when the destination address did not previously exist, and [a partial refund](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L568-L572) for each tuple is added to the global refund counter during applying delegation if the account already exists in state (i.e. delegated before this transition).
Finally, during state transition, the node applies each tuple in the `authorization_list` ([implementation here](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L469-L475)). The process includes [validating the tuple](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L563), [refunding gas for existing accounts](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L568-L572), [incrementing nonce](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L575), and handling code modifications for [authorization](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L582-L583) or [revocation](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L576-L580). Importantly, even if [applyAuthorization](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L561-L586) errors out (e.g. due to [validateAuthorization failure](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L563)), other authorizations in the batch remain unaffected. This design minimizes [denial-of-service (DoS) attacks](https://en.wikipedia.org/wiki/Denial-of-service_attack) risk in batch authorization scenarios, which is particularly useful for sponsored transactions.
During [authorization validation](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L530-L559), the node first verifies that [the `auth.ChainID` is `0` or matches the chain ID](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L532-L535). It then checks that [the `auth.Nonce` does not exceed the maximum allowed value](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L536-L539) (`2^64-1` per [EIP-2681](https://eips.ethereum.org/EIPS/eip-2681)) and [validates the signature values and recovers the signer address](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L540-L544). The touched address is then [added to the access list](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L549-L550), and the node ensures the address has [no code or has exisiting delegation](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L551-L554) (i.e. it's not a contract) and that the account nonce [matches the provided `auth.Nonce`](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L555-L557).
Before executing the [EVM call](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L486-L487), the node [marks EIP-7702 addresses as warm addresses](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L477-L484) following [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929).
### Nonce Increment Logic
EIP-7702 introduces a new nonce increment path due to its inclusion of an `authorization_list`. Thus adding this section for the whole landscape of nonce handling. Below is the detailed process for nonce handling:
1. **Transaction Nonce Validation and Increment**: The transaction nonce is first checked to ensure it matches the account's current nonce in the state ([validation here](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L296-L307)). Once validated, the transaction nonce is incremented ([increment here](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L466-L467)).
2. **Authorization Nonce Validation and Increment**: For each authorization tuple in the `authorization_list`, the `auth.Nonce` is validated to ensure it matches the current nonce of the signing account ([validation here](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L536-L539)). After successful validation, the nonce associated with the authorization is incremented ([increment here](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L575)).
### Querying Transaction Status
Users can use `eth_getTransactionByHash` and `eth_getTransactionReceipt` to query a transaction's status. The execution path for EIP-7702 transactions is identical to other transactions. The node adds special handling for [EIP-7702 fields](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/internal/ethapi/api.go#L1038-L1052) in the [transaction payload](#transaction-payload-definition). However, there are no additional fields in the [transaction receipt](#transaction-receipt-definition) compared to other transaction types ([definition in Geth for overall receipt fields](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/receipt.go#L51-L74)), so no special handling is required for receipts.
The above methods only confirm whether a transaction was included in a block. However, in the case of authorizations, if an authorization tuple fails [validity checks](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L530-L559), it will be skipped. An approach to maintain the latest mapping of an EOA address to its delegated code address after the transaction is included would be:
1. Use `eth_getTransactionByHash` to fetch the transaction ([code](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/ethclient/ethclient.go#L242-L257)) and get the [authorization list](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/transaction.go#L477-L484). For each [authorization tuple](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/tx_setcode.go#L71-L79), attempt [recovering the authority](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/tx_setcode.go#L120-L142):
1. If recovery fails, the authorization is invalid. This failure path is consistent with [authorization validation](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L540-L544) during transaction execution.
2. If recovery succeeds, use `eth_getCode` to query the code at the recovered address ([a calling example](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/ethclient/ethclient.go#L399-L405)). This will return the full code (`23` bytes) of the address (different from `EXTCODECOPY`, which only returns the first 2 bytes of the designator, i.e., `0xef01`), which can then be processed using [ParseDelegation](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/tx_setcode.go#L36-L42) to determine whether the account has delegated to code (second return value) and the code address it is delegated to (first return value).
2. For each successful delegation found in step 1.2, update the mapping.
### Instruction Set Modifications
As for instruction set changes, in the implementation, the node [builds on the previous instruction set](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/jump_table.go#L106), [adds the instruction set modifications for EIP-7702](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/jump_table.go#L107), and [initializes a new instruction set that supports EIP-7702](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/jump_table.go#L64). This updated instruction set is [enabled in the EVM interpreter once the hard fork is detected](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/interpreter.go#L112-L113). The [specific changes to the instruction set](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/eips.go#L763-L773) include:
1. [`EXTCODECOPY` only returns `0xef01`](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/eips.go#L724-L726), which marks the address as a delegator EOA address by identifying it with the first two bytes of the [DelegationPrefix](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/tx_setcode.go#L32-L34) `0xef0100`, i.e `0xef01`.
2. [`EXTCODESIZE` only returns `2`](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/eips.go#L737-L739), which is the byte length of `0xef01`.
3. [`EXTCODEHASH` only computes the Keccak256 hash of `0xef01`](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/eips.go#L753-L756).
4. `CALL`, `CALLCODE`, `DELEGATECALL`, and `STATICCALL` will load the code from the delegated code address and execute it in the context of the smart EOA. This is achieved by retrieving the [code hash](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/evm.go#L584-L597) and [code](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/evm.go#L570-L582) during the execution of these opcodes. Invalid delegations (e.g. those without valid code or a precompiled contract address) [will be ignored and treated as having no code](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/interpreter.go#L178-L181).
5. [Gas adjustments for CALL-related opcodes (`CALL`, `CALLCODE`, `DELEGATECALL`, `STATICCALL`)](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/eips.go#L769-L772): These adjustments are to consider EOA address and code address accesses. Geth encapsulates this logic into [a unified function](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/operations_acl.go#L247-L312) and deducts the appropriate gas costs according to the [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929). The gas calculation before EIP-7720 is [here](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/operations_acl.go#L156-L193). The difference comes from [the extra access of the delegated code address](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/operations_acl.go#L275-L288).
### Interacting with Smart EOAs
Interacting with a smart EOA works similarly to calling a smart contract. Users simply need to set the `to` field of the transactions (or the `destination` field for EIP-7702 transactions) as the smart EOA address. This applies whether the call originates from an external transaction or from within another contract.
The following are protocol implementations worth noting:
1. In transaction [preCheck](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L408), the proposer allows smart EOAs to bypass [EIP-3607](https://eips.ethereum.org/EIPS/eip-3607) through [special handling](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L309-L316), enabling the EOA to send transactions as `tx.origin`. For detailed implementations, the proposer [reads the account's code](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L311) and [uses `ParseDelegation`](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L312C25-L312C40) to identify whether the address is a smart EOA or a contract.
2. A smart EOA's code format is `0xef0100 || address` (where `||` denotes concatenation), ensuring no conflict with existing contract codes. This encoding leverages [EIP-3541](https://eips.ethereum.org/EIPS/eip-3541), which rejects contract code starting with the `0xef` byte. The `0xef01` prefix ensures no conflict with `0xef00` (reserved by [EIP-3540](https://eips.ethereum.org/EIPS/eip-3540)), and the `0x00` following `0xef01` is reserved to allow for potential future upgrades of EIP-7702. The delegation encoding and decoding logic is implemented [here](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/types/tx_setcode.go#L32-L47).
3. EIP-7702 prohibits recursive delegation (e.g. creating a potential chain or loop of designators) to ensure protocol simplicity. Nodes only retrieve [one level of delegation](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/vm/evm.go#L570-L582).
## Use Case Implementations
After walking through the code with conceptual examples, let us now analyze the implementation and functionalities of EIP-7702 through concrete examples.
### Creating Wallets and Executing Calls
#### Creating Wallets
We use the [EXP-0001](https://www.ithaca.xyz/writings/exp-0001) example provided by Ithaca for illustration. This example provides a concise implementation for creating wallets. Here's how it works:
1. **Generate EOA:** The process starts by [generating a random private key](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/example/src/modules/Account.ts#L60-L62). In a real-world scenario, this private key could be replaced with the user’s wallet private key.
2. **Create key:** Next, the implementation prompts the end-user to [create a WebAuthn key](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/example/src/modules/Account.ts#L64-L71), which is then used to sign SC wallet calls.
3. **Sign and delegate the wallet:** The EOA is used to [sign and delegate the contract wallet](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/example/src/modules/Account.ts#L121-L127),[^5] and the WebAuthn key is [added as the wallet’s signing key](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/example/src/modules/Account.ts#L133-L146).
##### Notable Implementation Details
1. **Delegate wallet and add signing key in one transaction:** This demo shows how a single transaction can both delegate a wallet and add a signing key. This is possible because EIP-7702 transactions, after [applying delegation](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L469-L475), still retain the ability to [call contracts](https://github.com/ethereum/go-ethereum/blob/f808d7357ed4076b224a8c6fe47893ce022f9409/core/state_transition.go#L486-L487). While combining these actions is not strictly necessary, compared with sending two transactions, it can reduce the number of signing transactions and benefit from amortized intrinsic gas costs.
2. **Submission by 3rd-party:** The transaction can later be submitted by a third-party EOA ([transaction sender is not set](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/example/src/modules/Account.ts#L148)). In this demo, Ithaca’s Odyssey provides the `odyssey_sendTransaction` interface proposed [here](https://www.ithaca.xyz/writings/orc-0001). Its [implementation](https://github.com/ithacaxyz/odyssey/blob/f1d3e8b65be591cc1fe60898a61cea82f5979097/crates/wallet/src/lib.rs#L318-L398) is a sponsored transaction service. In a real use case, requiring users to authenticate or pass social verification (e.g. [Gitcoin Passport Scorer](https://scorer.gitcoin.co/)) with rate limiting would be a first defense against [Sybil attacks](https://en.wikipedia.org/wiki/Sybil_attack) and losing funds.
3. **Preventing signing key tampering:** The SC wallet ensures that the signing key cannot be tampered with. This is achieved by validating [the signer of "the wallet's signing key authorization" matches the EOA address](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/contracts/src/ExperimentDelegation.sol#L95-L104) (the Solidity implementation's `address(this)` of SC wallet is exactly the EOA address). To construct the transaction, the [EOA signs the authorization information](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/example/src/modules/Account.ts#L109-L119), which is then [passed as part of the calldata](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/example/src/modules/Account.ts#L133-L146).
#### Executing Calls
Following the wallet creation, users can transfer funds by signing and executing calls. The user uses the WebAuthn key to [sign the contract call](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/example/src/modules/Account.ts#L221-L230), and [extract the needed fields](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/example/src/modules/Account.ts#L232-L234) in the signature based on the specific wallet implementation. The parameters are prepared and [used to invoke the contract](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/example/src/modules/Account.ts#L236-L243). In this demo, the transaction is sent through the `odyssey_sendTransaction` interface. For an ERC-4337-compatible wallet, it can instead be constructed as a `UserOperation` and sent to a bundler.
##### Notable Implementation Details
1. **Arbitrary keys:** The deployed contract is implemented in [this code](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/contracts/src/ExperimentDelegation.sol). It supports [RIP-7212](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md), enabling [verification of P256 key signatures](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/contracts/src/utils/P256.sol#L13-L29).
2. **Batch call:** The contract also supports a [multiSend interface](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/contracts/src/utils/MultiSend.sol#L12-L65), allowing users to aggregate multiple calls into a single transaction. The calls must be [encoded into calldata, signed, and passed as input](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/example/src/modules/Account.ts#L205-L243) to the contract's `execute` function. To enhance interoperability and simplify SDK and wallet development, a standardized ERC could be designed around the `multiSend` functionality.
#### ERC-4337 Compatibility
The [EXP-0001](https://www.ithaca.xyz/writings/exp-0001) demo opens many functional possibilities. Though it is not implemented as ERC-4337-compatible, this can easily be achieved by replacing the demo’s [deployed contract](https://github.com/ithacaxyz/exp-0001/blob/ff4ed36a51c6fe2eca013fb14be72b20105751c3/contracts/src/ExperimentDelegation.sol) with an ERC-4337-compatible contract, such as Coinbase’s [Smart Wallet](https://github.com/coinbase/smart-wallet). Thus the wallet can inherit all ERC-4337 features seamlessly.
### Recovery from Private Key Loss
Based on the implementation from the previous example, it is already possible to achieve account recovery. When the private key is lost, the user can simply use their registered signing key to transfer assets from the EOA account. This is particularly useful for existing EOA users who want to safeguard against losing their private key. In real usage, a more secure approach would be configuring social recovery permissions properly. For instance, instead of allowing frequently used hot signing keys to transfer all assets, a more secure setup would require signatures from multiple guardians to move the balance.
For new users, wallets can help simplify the process of configuring guardians. For example, they could offer [a 2-of-3 scheme](https://vitalik.eth.limo/general/2024/12/03/wallets.html) that uses:
1. **zk-email** for the user’s email address,
2. **A key stored locally** on the user’s device (e.g. a passkey), and
3. **A backup key held by a service provider.**
### ZK Recovery
EIP-7702 enables wallet recovery by [Zero-Knowledge Proof (ZKP)](https://en.wikipedia.org/wiki/Zero-knowledge_proof), through delegating to a contract that supports these functionalities. One advantage of ZKP is verifying user information without compromising privacy. For instance, the authentication can leverage [OpenID providers](https://en.wikipedia.org/wiki/List_of_OAuth_providers) like Google or Facebook to issue [JSON Web Tokens (JWTs)](https://datatracker.ietf.org/doc/html/rfc7519) as a witness for ZKP, utilizing established identity verification infrastructure.
Here's an example of a [ZK recovery](https://github.com/shield-labs-xyz/zklogin/blob/da7371477144029bb10eed1bcb6ba8338e425d7d/apps/interface/src/lib/services/Eip7702Service.ts) based on EIP-7702 provided by [zklogin](https://github.com/shield-labs-xyz/zklogin). To change the wallet's signing key, the user must use a new WebAuthn key to go through the login flow of an OpenID provider (in the example, [Google](https://github.com/shield-labs-xyz/zklogin/blob/da7371477144029bb10eed1bcb6ba8338e425d7d/apps/interface/src/lib/services/Eip7702Service.ts#L25)) to obtain a JWT. This JWT is then used to [generate the proof](https://github.com/shield-labs-xyz/zklogin/blob/da7371477144029bb10eed1bcb6ba8338e425d7d/apps/interface/src/lib/services/Eip7702Service.ts#L93-L98). Finally, the proof along with additional metadata from the JWT and the new WebAuthn public key [are submitted to the contract](https://github.com/shield-labs-xyz/zklogin/blob/da7371477144029bb10eed1bcb6ba8338e425d7d/apps/interface/src/lib/services/Eip7702Service.ts#L101-L109) to update the signing key.
### Session Keys and Subscriptions
Nowadays, users frequently interact with wallet prompt windows to authorize actions, which can lead to fatigue in high-frequency, low-value payment scenarios. In contrast, in traditional systems like credit cards, "trusted" subscription-based payment services can reduce the cognitive cost of authorization.
One way to simplify user operations is by separating keys for different usages (e.g. signing low permission transactions, encrypting and decrypting messages[^6]) from the wallet's private key (which has the top privilege). Continuing with the example from Ithaca's [EXP-0002](https://www.ithaca.xyz/writings/exp-0002), during wallet creation, the demo program generates a signing key using the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) and then stores it in [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API). This allows websites with the same origin as the demo to access the signing key through specific interfaces, enabling frontend applications to perform signing operations without triggering wallet prompts.
The demo does not specify the subsequent handling of the signing key. In practice, wallet clients can assign **lifespans** and **permissions** to a signing key and delegate it to a private key management service. For instance, Privy has demonstrated how to create and store a "session key" in this [demo video](https://x.com/privy_io/status/1867249205133373905).
## EIP-7702 in Roadmaps of Ethereum and L2s
### Toward Deprecating EOAs
EIP-7702 can help EOA users experience the functionalities of SC wallets, such as social recovery, subscriptions, etc. However, because it retains the existence of private keys, it cannot completely replace ERC-4337. For users who have applied EIP-7702, how can they fully obtain the security guarantees of SC wallets?
The author thinks that this can be achieved through compatibility patches to enable full migration to SC wallets, such as the concept of Migration Transactions proposed in [EIP-7377](https://eips.ethereum.org/EIPS/eip-7377). With slight modifications, it could support the migration of EIP-7702 accounts to SC accounts. e.g. adding a `deactivated` field in the account state for deactivated EOA private keys. If users need to revoke the deactivated status, it would require the highest permissions of SC wallets, such as requiring M-of-N signatures from guardians.
The purpose of retaining the ability to "revoke the deactivated status" is to address migration scenarios that cannot be fully fulfilled through the upgrade of upgradable contracts (or the contract is not upgradable). In such cases, it would be necessary to re-enable the private key's permissions and send another EIP-7702 transaction to delegate the EOA to another code address.
For example, a user can upgrade its delegated code to fully migrate to an [EVM Object Format (EOF)](https://evmobjectformat.org/) contract, taking advantage of the new upgrade schemes supported by EOF and removing the need for a legacy proxy contract. While EOF allows legacy to EOF paths for existing proxy contracts to be able to use EOF upgrades, removing the proxy contract can reduce transaction costs by eliminating the overhead of forwarding calls through the proxy.
### Toward Native AA
Moreover, the Ethereum and L2 communities are actively discussing native AA. Assuming the Ethereum ecosystem will gradually move towards a native AA world, how compatible would EIP-7702 be with these proposals?
On L2, [RIP-7560](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7560.md) has been designed to pioneer the experimentation and implementation of native AA on L2, along with related proposals: [RIP-7711](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7711.md), [RIP-7712](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7712.md), and [RIP-7696](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7696.md). RIP-7560 integrates the tooling provided by ERC-4337 with the design of native AA in [EIP-2938](https://eips.ethereum.org/EIPS/eip-2938), introducing a new `AA_TX_TYPE` transaction. Compared to ERC-4337, it enjoys the benefits of native transactions, such as eliminating the need for additional RPC interfaces, using the contract address as `tx.origin`, having lower gas costs (extra gas overhead of ~42k for a basic `UserOperation` compared to ~21k for a basic transaction), and benefiting from protocol-level censorship resistance mechanisms.
When RIP-7560 becomes widely adopted, users relying on ERC-4337 wallets or other wallets incompatible with RIP-7560 can update their delegated contracts using EIP-7702 to support RIP-7560's new interfaces. This is necessary because RIP-7560 introduces changes to wallet interfaces compared to ERC-4337, such as handling errors via callback functions, which aim to reduce technical debt. Users utilizing proxy contracts can alternatively upgrade the implementation address to adopt RIP-7560 interfaces, ensuring a smooth transition.
On Ethereum, [EIP-7701](https://eips.ethereum.org/EIPS/eip-7701) builds upon the design of RIP-7560 to propose a native AA mechanism. The key difference lies in the use of EOF to define entry point function interfaces, addressing technical debt caused by the current reliance on Solidity's function selectors for entry points (e.g. an idiosyncratic or malicious wallet could use unreadable function names to create the same function hash as a wallet interface). By leveraging EOF, users can upgrade their smart EOAs using EIP-7702 to ensure compatibility with EIP-7701, either by migrating to new EOF-based contracts or by updating their proxy contract's implementation address.
[^1]: As discussed in [this blog](https://vitalik.eth.limo/general/2024/12/03/wallets.html), wallets can implement a simple guardians strategy, such as a 2-of-3 scheme. This includes verifying the user's email address via zk-email, a local key stored on the user's device (e.g. a passkey), and a backup key held by a service provider.
[^2]: Arbitrary signing keys are actively supported in L2 through precompiled contracts like [RIP-7212](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md) and [RIP-7696](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7696.md), enabling AA wallets to verify signatures from various key types.
[^3]: One potential approach to address these privacy concerns is to distribute a user's SBTs and on-chain activities across multiple privacy wallet addresses while maintaining a global perspective on these assets. When verification is required, zk-proofs can be submitted to prove ownership or participation, without revealing sensitive details.
[^4]: Although EIP-7702 introduces [a new scenario where the EOA nonce can be incremented](#A-New-Case-for-Nonce-Increment), the transaction pool can still handle addresses with updated nonces by monitoring newly added blocks on the node. This ensures that transactions with lower nonce will be evicted. However, once an EOA has delegated to code, anyone can call that code at any point in a transaction, breaking a widely used invariant for account balance to invalidate pending transactions with insufficient balance through iterating the senders of new blocks: EOAs have only been able to send value via a transaction. This requires the transaction pool logic to be updated accordingly. See [here](https://eips.ethereum.org/EIPS/eip-7702#transaction-propagation) for more details.
[^5]: Here the demo is using [viem's signAuthorization EIP-7702 experimental function](https://viem.sh/experimental/eip7702/signAuthorization).
[^6]: This is commonly seen in end-to-end encrypted messaging or email services that integrate [ERC-4361: Sign-In with Ethereum](https://eips.ethereum.org/EIPS/eip-4361), such as [MetaMail](https://mirror.xyz/metamailink.eth/g0n_yX4V1EQjaMLg63f53YRwrci-74PsuGRMyG-yQ7Y).