Try   HackMD

EIP-7702: A Deep Dive into Smart EOAs with Implementation Examples

Special thanks to Péter Garamvölgyi for feedback and review.

Introduction

EIP-7702 introduces a new transaction type that allows an Externally Owned Account (EOA) to specify an address as the pointer to its implementation. For example, this address could be a generic proxy or minimal proxy contract 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, P256, BLS, 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 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, 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, 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). 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 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), 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:

rlp([chain_id,nonce,max_priority_fee_per_gas,max_fee_per_gas,gas_limit,destination,value,data,access_list,authorization_list,signature_y_parity,signature_r,signature_s])

Where the authorization_list is defined as:

authorization_list=[[chain_id,address,nonce,y_parity,r,s],]

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:

rlp([status,cumulative_transaction_gas_used,logs_bloom,logs])

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 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 roots storage layouts at unique slots, avoiding storage location collision during re-delegation. ERC-7779 (draft) 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, 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, since the authors do not expect much demand for atomic delegation and blob submission). Similar to 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.

No instruction prohibition

A well-known ban in some EIPs involves banning storage-related instructions (e.g. 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, one of the major Ethereum clients listed on the Client Diversity Dashboard.

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). Then, the user constructs the authorization_list here. For each authorization tuple, the user fills in the non-signature fields first, and then signs it using the SignSetCode function.

The user's EOA private key is required for signing. Signing involves hashing the authorization, 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.

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 are prepared, the user initializes the transaction (code here) and a "Prague signer" (which adds support for EIP-7702 transactions). The user then signs the transaction using SignTx and serializes it into a byte array using RLP encoding via MarshalBinary. Finally, the RLP byte array of transaction is hex-encoded and sent via the eth_sendRawTransaction RPC method.

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, which includes some additional checks for EIP-7702 transactions:

  1. Ensuring it is not a contract creation transaction (code here).
  2. Checking that it includes at least one authorization operation (code here).

Before executing the transaction, the node calculates IntrinsicGas, accounting for the gas cost of accessing the authorization_list (code here). A 25000 gas cost for each authorization tuple, defined as CallNewAccountGas (named PER_EMPTY_ACCOUNT_COST in EIP-7702) is consumed when the destination address did not previously exist, and a partial refund 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). The process includes validating the tuple, refunding gas for existing accounts, incrementing nonce, and handling code modifications for authorization or revocation. Importantly, even if applyAuthorization errors out (e.g. due to validateAuthorization failure), other authorizations in the batch remain unaffected. This design minimizes denial-of-service (DoS) attacks in batch authorization scenarios, which is particularly useful for sponsored transactions.

During authorization validation, the node first verifies that the auth.ChainID chain ID is null or equal to current chain ID. It then checks that the auth.Nonce does not exceed the maximum allowed value (2^64-1 per EIP-2681) and validates the signature values and recovers the signer address. The touched address is then added to the access list, and the node ensures the address has no code or has exisiting delegation (i.e. it's not a contract) and that the account nonce matches the provided auth.Nonce.

Before executing the EVM call, the node marks EIP-7702 addresses as warm addresses (a concept from 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). Once validated, the transaction nonce is incremented (increment here).
  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). For successful validation, the nonce associated with the authorization is incremented (increment here).

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 in the transaction payload. However, there are no additional fields in the transaction receipt compared to other transaction types (definition in Geth for overall receipt fields), 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, 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) and get the authorization list. For each authorization tuple, attempt recovering the authority:
    1. If recovery fails, the authorization is invalid. This failure path is consistent with authorization validation during transaction execution.
    2. If recovery succeeds, use eth_getCode to query the code at the recovered address (a code snippet to call it). The API will return the code of the address, which can then be processed using ParseDelegation 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, adds the instruction set modifications for EIP-7702, and initializes a new instruction set that supports EIP-7702. This updated instruction set is enabled in the EVM interpreter once the hard fork is detected. The specific changes to the instruction set 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 and code 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.
  2. Gas adjustments for CALL-related opcodes (CALL, CALLCODE, DELEGATECALL, STATICCALL): These adjustments are to consider EOA address and code address accesses. Geth encapsulates this logic into a unified function and deducts the appropriate gas costs according to the EIP-2929. The gas calculation before EIP-7702 is here. The difference comes from the extra access of the delegated code address.

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, the proposer allows smart EOAs to bypass EIP-3607 through special handling, enabling the EOA to send transactions as tx.origin. For detailed implementations, the proposer reads the account's code and uses ParseDelegation 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, which rejects contract code starting with the 0xef byte. The 0xef01 prefix ensures no conflict with 0xef00 (reserved by 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.
  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.

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 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. 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, 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, and the WebAuthn key is added as the wallet’s signing key.
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, still retain the ability to call contracts. 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). In this demo, Ithaca’s Odyssey provides the odyssey_sendTransaction interface proposed here. Its implementation is a sponsored transaction service. In production, requiring users to authenticate or pass social verification (e.g. Gitcoin Passport Scorer) with rate limiting would be a first line of defense against Sybil attacks.
  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 (the Solidity implementation's address(this) of SC wallet is exactly the EOA address). To construct the transaction, the EOA signs the authorization information, which is then passed as part of the calldata.

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, and extract the needed fields in the signature based on the specific wallet implementation. The parameters are prepared and used to invoke the contract. 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. It supports RIP-7212, enabling verification of P256 key signatures.
  2. Batch call: The contract also supports a multiSend interface, allowing users to aggregate multiple calls into a single transaction. The calls must be encoded into calldata, signed, and passed as input to the contract's execute function. To enhance interoperability and simplify SDK (and wallet contract) development, there is a draft-ERC-7638 to unify batch call encoding.

ERC-4337 Compatibility

The 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 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.

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 demo.

For new users, wallets can help simplify the process of configuring guardians. For example, they could offer a 2-of-3 scheme 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), 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 like Google or Facebook to issue JSON Web Tokens (JWTs) as a witness for ZKP, utilizing established identity verification infrastructure.

Here's an example of a ZK recovery based on EIP-7702 provided by 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) to obtain a JWT. This JWT is then used to generate the proof. Finally, the proof along with additional metadata from the JWT and the new WebAuthn public key are submitted to the contract 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 as an example, during wallet creation, the demo program generates a signing key using the Web Crypto API and then stores it in IndexedDB. 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 is a very simple PoC, and there is another 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 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 has been designed to pioneer the experimentation and implementation of native AA, along with related proposals: RIP-7711, RIP-7712, etc. RIP-7560 integrates the tooling provided by ERC-4337, combined with partial design of native AA in 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 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, 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 and RIP-7696, 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, 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 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, such as MetaMail. ↩︎