# 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`.
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. To address this gap, proposals like [EIP-7851](https://eips.ethereum.org/EIPS/eip-7851), which allow users to deactivate (and reactivate) private key permissions post-EIP-7702, will be necessary.
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? Some initial thoughts:
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 (though it will not happen in practical use cases), the nonce of each authorization must be incremented sequentially. Overall, this mechanism requires SDKs to handle nonces correctly.
### Multi-Path Account Balance 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 reconsideration of 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), since [the authors do not expect much demand for atomic delegation and blob submission](https://eips.ethereum.org/EIPS/eip-7702#no-blobs-no-contract-creation)). 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, [to reduce the test vectors and keep the scope of focusing on improving UX](https://eips.ethereum.org/EIPS/eip-7702#no-blobs-no-contract-creation).
### No instruction prohibition
A well-known ban in some EIPs 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 very important for SC wallets.
Further details will be introduced in the next section.
## Core Protocol Implementations
In this section, we will dig into "how an EIP-7702 transaction's journey is implemented". 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).
_We assume that the node has activated EIP-7702 throughout the discussions of this session._
### 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/v1.15.10/core/types/tx_setcode.go#L49-L67)). Then, the user constructs the `authorization_list` [here](https://github.com/ethereum/go-ethereum/blob/v1.15.10/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/v1.15.10/core/types/tx_setcode.go#L73-L75) first, and then signs it using the [SignSetCode](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/types/tx_setcode.go#L90-L106) function.
The user's EOA private key is required for signing. Signing involves [hashing the authorization](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/types/tx_setcode.go#L108-L114), 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/v1.15.10/core/types/tx_setcode.go#L63-L66) are prepared, the user initializes the transaction ([code here](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/types/transaction.go#L65-L70)) and a ["Prague signer"](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/types/transaction_signing.go#L279-L288) (which adds support for [EIP-7702 transactions](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/types/transaction_signing.go#L219-L221)). The user then signs the transaction using [SignTx](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/types/transaction_signing.go#L110-L118) 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/v1.15.10/ethclient/ethclient.go#L686). 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/v1.15.10/ethclient/ethclient.go#L690).
When a node receives the transaction through JSON-RPC, the node validates this transaction 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/v1.15.10/core/state_transition.go#L420), which includes some additional checks for EIP-7702 transactions:
1. Ensuring it is not a contract creation transaction ([code here](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L388-L390)).
2. Checking that it includes at least one authorization operation ([code here](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L391-L393)).
Before executing the transaction, the node calculates [IntrinsicGas](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L432), accounting for the gas cost of accessing the `authorization_list` ([code here](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L113-L115)). A `25000` gas cost for each authorization tuple, defined as [CallNewAccountGas](https://github.com/ethereum/go-ethereum/blob/v1.15.10/params/protocol_params.go#L35) (named `PER_EMPTY_ACCOUNT_COST` in [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702#parameters)) is consumed when the destination address did not previously exist, and [a partial refund](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L597-L601) 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` ([code here](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L491-L497)). The process includes [validating the tuple](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L592), [refunding gas for existing accounts](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L597-L601), [incrementing nonce](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L604), and handling code modifications for [authorization](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L611-L612) or [revocation](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L605-L609). Importantly, even if [applyAuthorization](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L590-L615) errors out (e.g. due to [validateAuthorization failure](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L592)), other authorizations in the batch remain unaffected. This design minimizes [denial-of-service (DoS) attacks](https://en.wikipedia.org/wiki/Denial-of-service_attack) in batch authorization scenarios, which is particularly useful for sponsored transactions.
During [authorization validation](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L559-L588), the node first verifies that [the `auth.ChainID` chain ID is null or equal to current chain ID](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L561-L564). It then checks that [the `auth.Nonce` does not exceed the maximum allowed value](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L565-L568) (`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/v1.15.10/core/state_transition.go#L569-L573). The touched address is then [added to the access list](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L578-L579), and the node ensures the address has [no code or has exisiting delegation](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L580-L583) (i.e. it's not a contract) and that the account nonce [matches the provided `auth.Nonce`](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L584-L586).
Before executing the [EVM call](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L508-L509), the node [marks EIP-7702 addresses as warm addresses](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L499-L506) (a concept from [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/v1.15.10/core/state_transition.go#L308-L319)). Once validated, the transaction nonce is incremented ([increment here](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L488-L489)).
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/v1.15.10/core/state_transition.go#L565-L568)). For successful validation, the nonce associated with the authorization is incremented ([increment here](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L604)).
### 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/v1.15.10/internal/ethapi/api.go#L1044-L1058) 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/v1.15.10/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/v1.15.10/core/state_transition.go#L559-L588), 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 here](https://github.com/ethereum/go-ethereum/blob/v1.15.10/ethclient/ethclient.go#L267-L282)) and get the [authorization list](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/types/transaction.go#L480-L487). For each [authorization tuple](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/types/tx_setcode.go#L71-L79), attempt [recovering the authority](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/types/tx_setcode.go#L116-L138):
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 code snippet to call it](https://github.com/ethereum/go-ethereum/blob/v1.15.10/ethclient/ethclient.go#L424-L430)). The API will return the code of the address, which can then be processed using [ParseDelegation](https://github.com/ethereum/go-ethereum/blob/v1.15.10/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/v1.15.10/core/vm/jump_table.go#L106), [adds the instruction set modifications for EIP-7702](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/vm/jump_table.go#L107), and [initializes a new instruction set that supports EIP-7702](https://github.com/ethereum/go-ethereum/blob/v1.15.10/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/v1.15.10/core/vm/interpreter.go#L112-L113). The [specific changes to the instruction set](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/vm/eips.go#L706-L712) include:
1. `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/v1.15.10/core/vm/evm.go#L576-L589) and [code](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/vm/evm.go#L562-L574) during the execution of these opcodes. Invalid delegations (e.g. addresses without code or precompiled contract addresses) [will be ignored and treated as having no code](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/vm/interpreter.go#L178-L181).
2. [Gas adjustments for CALL-related opcodes (`CALL`, `CALLCODE`, `DELEGATECALL`, `STATICCALL`)](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/vm/eips.go#L708-L711): 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/v1.15.10/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-7702 is [here](https://github.com/ethereum/go-ethereum/blob/v1.15.10/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/v1.15.10/core/vm/operations_acl.go#L275-L288).
### Interacting with Smart EOAs
Interacting with a smart EOA works similarly to calling a smart contract. Users need to set the `to` field of the transactions (or named `destination` field for EIP-7702 transactions) as the smart EOA address. This applies whether the call originates from an external transaction or within another contract.
### Miscellaneous Notes
The following are protocol implementations worth noting:
1. In transaction [preCheck](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L420), 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/v1.15.10/core/state_transition.go#L322-L327), 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/v1.15.10/core/state_transition.go#L323) and [uses `ParseDelegation`](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L324) 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/v1.15.10/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/v1.15.10/core/vm/evm.go#L562-L574).
## Use Case Implementations
After walking through the code with "imaginary" 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/updates/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), 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/v1.15.10/core/state_transition.go#L491-L497), still retain the ability to [call contracts](https://github.com/ethereum/go-ethereum/blob/v1.15.10/core/state_transition.go#L508-L509). 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://ithaca.xyz/updates/exp-0000). Its [implementation](https://github.com/ithacaxyz/odyssey/blob/f1d3e8b65be591cc1fe60898a61cea82f5979097/crates/wallet/src/lib.rs#L318-L398) is a sponsored transaction service. In production, requiring users to authenticate or pass social verification (e.g. [Gitcoin Passport Scorer](https://scorer.gitcoin.co/)) with rate limiting would be a first line of defense against [Sybil attacks](https://en.wikipedia.org/wiki/Sybil_attack).
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 [here](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 contract) development, there is a [draft-ERC-7638](https://eips.ethereum.org/EIPS/eip-7638) to unify batch call encoding.
#### ERC-4337 Compatibility
The [EXP-0001](https://www.ithaca.xyz/updates/exp-0001) demo opens many functional possibilities. Though it is not implemented as ERC-4337-compatible, this can 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 and EIP-7702 compatible contract. There are some work-in-progresses to support ERC-4337 and EIP-7702 in the meantime, such as [EIP-7702 support for Safe Smart Account](https://github.com/5afe/safe-eip7702).
### 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. For a life-saving wallet, a more secure approach would be to configure social recovery permissions properly, instead of a single signing key in [EXP-0001](https://www.ithaca.xyz/updates/exp-0001) demo.
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, small-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[^5]) from the wallet's private key (which has the top privilege). Taking Ithaca's [EXP-0002](https://www.ithaca.xyz/updates/exp-0002) as an example, 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.
[EXP-0002](https://www.ithaca.xyz/updates/exp-0002) is a very simple PoC, and there is another [EXP-0003](https://ithaca.xyz/updates/exp-0003) demo which (i) integrates essential conceptual components (a user client for signing authorizations, a server for key management, a wallet SDK for interaction with each parties) for session keys in smart EOAs to work; (ii) allowing setting granular control over delegated actions via permission rules.
## 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. A [draft-EIP-7851](https://eips.ethereum.org/EIPS/eip-7851) enables users to deactivate their private keys of smart EOAs through a precompiled contract (only after the address is delegated), and they can further deactivate their private keys through invoking the same precompiled contract using the delegated contract, which enables flexible re-delegations.
### 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 L2s, [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, 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), etc. RIP-7560 integrates the tooling provided by ERC-4337, combined with partial 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 might be 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 focus lies in: (i) defining formal entry point function interfaces, e.g. addressing technical debt caused by the current reliance on Solidity's function selectors for entry points (an idiosyncratic or malicious wallet could use unreadable function names to create the same function hash as a wallet interface); (ii) designing "what more are we in" opcodes. Users can optionally upgrade their smart EOAs using EIP-7702 to EIP-7701 compatible wallet implementations after EIP-7701 is rolled out.
[^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]: EIP-7702 breaks two invariances: (1) [a new scenario where the EOA nonce can be incremented](#A-New-Case-for-Nonce-Increment), and (2) anyone can call the delegated code at any point in a transaction, breaking a claim that an EOA's balance can only be decreased by the transaction sender. Since the invariances are widely used in transaction pool's static checks, these changes require the transaction pool logic to be updated accordingly. For example, transaction pools may only accept one pending transaction for delegated EOAs to minimize invalidation risks. See [here](https://eips.ethereum.org/EIPS/eip-7702#transaction-propagation) for more details.
[^5]: 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).