How EIP-7702 Works | Account Abstraction Implementation

Published on:
February 27, 2026
Last Updated on:
February 27, 2026
Blockchain
How EIP-7702 Works | Account Abstraction Implementation

Table of contents:

If you're building wallet infrastructure, relayer services, or protocol tooling on Ethereum, and you want account abstraction features without migrating users to smart contract wallets, this guide is for you.

On May 7, 2025, Ethereum activated the Pectra upgrade. One of the core changes included in that fork was EIP-7702, co-authored by Vitalik Buterin.

EIP-7702 allows an Externally Owned Account to temporarily execute smart contract logic while keeping:

  • The same address
  • The same balance
  • The same transaction history

There is no smart wallet deployment.
There is no address migration.
There is no wrapper contract.

Instead, the protocol writes a delegation marker into the EOA’s code field and resolves execution to a delegate contract at runtime.

In this article, we will:

  • Break down the EIP-7702 specification at the protocol level
  • Examine how it is implemented in geth
  • Compare EIP-7702 with ERC-4337 and ERC-2771
  • Walk through a concrete Gas Station architecture using Type-4 transactions

Before diving into the transaction format and client implementation, we need to clarify the problem EIP-7702 is addressing.

The Core Limitation of EOAs

Traditional EOAs have structural constraints:

  • One transaction per signature
  • Gas must be paid in ETH
  • No programmable spending limits
  • No session keys
  • Loss of private key means irreversible loss of funds

Account abstraction efforts aim to remove these constraints.

ERC-4337 introduced Smart Contract Wallets to address most of them. It adds validation logic, batching, sponsored gas via Paymasters, and custom signature schemes.

However, ERC-4337 requires deploying a new contract wallet. That means:

  • New address
  • No inherited transaction history
  • No legacy integrations bound to the old EOA

For many systems, that migration cost is non-trivial.

What ERC-4337 Did — and the Trade-Off

ERC-4337 moved account abstraction into an application-layer architecture:

  • Bundlers package UserOperations
  • EntryPoint validates and executes
  • Paymasters sponsor gas
  • Smart contract wallets define custom validation logic

The advantage is flexibility.

The cost is infrastructure complexity and address migration.

If your goal is multi-user aggregation, permissionless Paymasters, or signature aggregation, ERC-4337 fits well.

If your goal is to extend an existing EOA with delegation, batching, or sponsored execution under a known operator, the migration requirement becomes friction.

What EIP-7702 Changes

EIP-7702 takes a protocol-level approach instead of an application-layer stack.

It introduces:

  • A new transaction type (0x04, SetCodeTransaction)
  • An authorization_list field
  • A delegation marker stored directly in the EOA’s code

Execution works differently from ERC-4337:

  • No EntryPoint
  • No Smart Contract Wallet deployment
  • No change to the user’s address

The EVM checks whether the account’s code matches a delegation prefix.
If it does, it loads bytecode from the delegate contract.
Execution runs in the EOA’s storage and balance context.

From the perspective of target contracts:

  • msg.sender remains the original EOA
  • No forwarder contract is involved
  • No contract modification is required

That design choice is critical for compatibility with existing DeFi protocols.

EIP-7702 Technical Specification

EIP-7702 introduces a new Ethereum transaction type that lets your EOA attach smart contract logic through a signed authorization. It adds an authorization_list to the EIP-1559 transaction format, defines a raw ECDSA signing flow with a 0x05 prefix, and writes a delegation marker directly into the account’s code field.

Now let’s break this down step by step so you can see how it affects your protocol or wallet logic.

SetCodeTransaction (Transaction Type 4 / 0x04)

EIP-7702 introduces Transaction Type 4 (0x04), called SetCodeTransaction.

The proposal extends the EIP-1559 payload by adding a new field: authorization_list.

TransactionPayload = rlp([
  chain_id,
  nonce,
  max_priority_fee_per_gas,
  max_fee_per_gas,
  gas_limit,
  destination,
  value,
  data,
  access_list,
  authorization_list,   // NEW FIELD
  signature_y_parity,
  signature_r,
  signature_s
])

Authorization Tuple

Each entry in the authorization_list contains:

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

Here’s what each field represents:

  • chain_id — network identifier (0 marks cross-chain authorization)
  • address — delegate contract
  • nonce — current EOA nonce to prevent replay
  • y_parity, r, s — ECDSA signature components

This tuple explicitly defines which contract can execute logic on behalf of your EOA and which chain it applies to. Your client validates this tuple independently of the main transaction signature.

So, What EIP-7702 Adds to the Protocol

Only one structural difference: authorization_list.

Everything else—gas, fees, nonce rules—works the same way as EIP-1559.

Conceptually it works this way:

  • The user signs a message authorizing delegation to a contract
  • The relayer includes this in a Type-4 transaction
  • The protocol sets special code on the EOA
  • The EOA call executes delegate code in its own context

No new address. No wrapper. No forwarder.

The authorization_list contains the delegation information that allows your EOA to temporarily assign execution rights to a contract.

Signing Flow with 0x05 Prefix

EIP-7702 uses a magic prefix 0x05 to sign authorizations:

message_hash = keccak256(
  0x05 || rlp([chain_id, address, nonce])
)

This creates a raw ECDSA signature instead of EIP-712 typed data. That means you don’t have to wrestle with typed data domains or complex schema rules when adding EIP-7702 transaction signing to your wallet or protocol.

So, the key points are:

  • Prefix is always 0x05.
  • Payload is RLP-encoded.
  • Signature is standard ECDSA.
  • Integration into wallets or protocols is straightforward, with fewer opportunities for mistakes.

Delegation Marker in EOA Code

When authorization is applied, the EVM writes a 23-byte marker into the EOA’s code field:

0xef0100 || delegate_address   // 3 + 20 = 23 bytes

Where:

  • 0xef0100 — fixed 3-byte prefix
  • delegate_address — 20-byte contract address

Why 0xef?

EIP-3541 prohibits deploying contracts starting with 0xef. That guarantees this marker can never collide with normal contract bytecode.

When the EVM executes a call:

  1. It checks if code matches delegation prefix
  2. If yes, it loads code from delegate address
  3. Execution runs in EOA storage context

It follows protocol-level DELEGATECALL semantics. After writing the marker, the EOA keeps its address, balance, and storage, while execution runs through the delegate’s code. Basically, your account gains temporary logic without changing its identity.

Summing Up EIP-7702 Transaction Signing Changes

EIP-7702 introduces a few key updates you need to be aware of if you’re building wallets, infrastructure, or protocol tooling:

  • Transaction Type 4 (0x04) – a new transaction type called SetCodeTransaction.
  • authorization_list field – a dedicated place to list delegate signatures.
  • Prefixed ECDSA signing (0x05) – all authorization tuples use a fixed prefix for signing.
  • Delegation marker in the EOA code – a 23-byte marker signals when an EOA executes delegate code.

To make sure your code works with EIP-7702 transactions:

  • Sign each authorization tuple with the 0x05 prefix.
  • Validate each delegate tuple in the authorization_list.
  • Process Type-4 transactions alongside standard EIP-1559 transactions.

Everything else follows Ethereum’s execution rules, so your existing logic mostly stays intact.

Now that we covered the EIP-7702 specification and transaction signing changes, let’s see how it’s implemented in Go-Ethereum (geth v1.15.x).

EIP-7702 Authorization Structure in Geth

Geth defines SetCodeTx in core/types/tx_setcode.go. It extends a standard EIP-1559 transaction with an AuthList field for authorizations:

type SetCodeTx struct {
    ChainID    *uint256.Int
    Nonce      uint64
    GasTipCap  *uint256.Int
    GasFeeCap  *uint256.Int
    Gas        uint64
    To         common.Address  // must not be nil
    Value      *uint256.Int
    Data       []byte
    AccessList AccessList
    AuthList   []SetCodeAuthorization  // new field for authorization tuples
    V, R, S    *uint256.Int
}

What SetCodeTx Adds

The key addition is the AuthList. It stores the authorization tuples required for prefixed signing and delegation. All other fields stay consistent with EIP-1559, so Type-4 transactions integrate without extra changes.

Authorization tuple fields:

  • ChainID – ensures the authorization applies to the correct network.
  • Address – the delegate account linked to the EOA.
  • Nonce – prevents replay of old authorizations.
  • V, R, S – ECDSA signature using the 0x05 prefix.

AuthList allows Geth to validate authorizations and apply delegation markers, giving the EOA temporary logic from a delegate contract while keeping the account itself unchanged.

Authorization Tuple Structure

Each authorization in AuthList is represented as SetCodeAuthorization in Go:

type SetCodeAuthorization struct {
    ChainID uint256.Int
    Address common.Address
    Nonce   uint64
    V       uint8
    R, S    uint256.Int
}

Geth can now store and process the authorization_list directly in a transaction, validate it, and apply delegation markers, fully supporting the EIP-7702 flow at the node level.

Geth can now store and process the authorization_list directly in a transaction, validate it, and apply delegation markers, fully supporting the EIP-7702 flow at the node level.

EIP 7702 Delegation Marker in Geth

Geth uses a fixed byte prefix to mark delegated code in an EOA. This ensures the node can recognize when an account’s execution should load code from a delegate contract.

var DelegationPrefix = []byte{0xef, 0x01, 0x00}
}

Let’s look at the key functions for delegation markers creation and reading in EOAs.

  1. Creating a Delegation Marker

The AddressToDelegation function appends the delegate’s address to the fixed prefix, producing a 23-byte marker stored in the EOA code:

func AddressToDelegation(addr common.Address) []byte {
    return append(DelegationPrefix, addr.Bytes()...)
}

The marker lets Geth distinguish delegated code from normal contracts and forward execution to the delegate contract without modifying the EOA’s storage or balance.

  1. Parsing a Delegation Marker

The ParseDelegation function reads an EOA’s code and checks for the delegation prefix. If found, it extracts the delegate address:

func ParseDelegation(code []byte) (common.Address, bool) {
    if len(code) != 23 ||
       !bytes.HasPrefix(code, DelegationPrefix) {
        return common.Address{}, false
    }
    return common.BytesToAddress(code[3:]), true
}

By parsing the marker, Geth can determine whether an EOA is using delegated logic and route execution accordingly, while preserving the account’s original storage and balance.

Let’s walk through how go-ethereum processes this.

Before diving into low-level details, it helps to see how Geth processes EIP-7702 authorizations at a high level. Execution in state_transition.go follows three main steps:

  1. Increment transaction sender nonce
  2. Apply authorizations
  3. Perform EVM call

Authorization validation checks:

  • Chain ID matches (or zero)
  • Nonce matches current EOA nonce
  • Signature recovers authority
  • Account has no conflicting contract code
  • Nonce overflow protection

If valid:

  • Code field is replaced with delegation marker
  • Nonce is incremented

Then execution proceeds.

Resolution happens in evm.go:

  • If code is delegation marker
  • Load code from delegate address
  • Execute in original EOA context

From a protocol standpoint, this is minimal surface area.

How to Execute EIP-7702 Authorizations

Geth’s core/state_transition.go defines the process for applying EIP-7702 authorizations. Execution involves three main steps: incrementing the sender nonce, applying authorization tuples, and running the EVM call. Each step ensures proper transaction sequencing and enforces delegation markers according to the EIP-7702 Ethereum account abstraction specification.

func (st *stateTransition) execute() (*ExecutionResult, error) {

    // 1. Increment the transaction sender's nonce
    st.state.SetNonce(msg.From,
        st.state.GetNonce(msg.From)+1,
        tracing.NonceChangeEoACall)

    // 2. Apply EIP-7702 authorizations
    if msg.SetCodeAuthorizations != nil {
        for _, auth := range msg.SetCodeAuthorizations {
            // Errors are ignored — invalid authorizations are simply skipped
            st.applyAuthorization(&auth)
        }
    }

    // 3. Execute the EVM call
    ret, st.gasRemaining, vmerr = st.evm.Call(
        msg.From, *msg.To, msg.Data,
        st.gasRemaining, value)

    // Additional execution handling happens here...
}

Let’s walk through how go-ethereum processes this:

Step 1: Increment Sender Nonce

Incrementing the sender’s nonce prevents replay attacks and guarantees that transactions execute in order. Every transaction in an EIP-7702 wallet increases the nonce before any delegation logic is applied.

Step 2: Apply EIP-7702 Authorization Tuples

Geth processes each authorization tuple in the SetCodeAuthorizations list. Invalid or expired authorizations are skipped. Valid tuples attach delegation markers to the externally owned account (EOA), linking it to the delegated contract while preserving the account’s storage and balance.

Delegation markers allow the EOA to execute code from another contract while maintaining its own account context, demonstrating how EIP-7702 transaction signing changes work at the node level.

Step 3: Execute EVM Call

How to Apply EIP 7702 Authorization for EOA

The applyAuthorization function sets the execution code for an externally owned account  according to EIP-7702 authorizations. It validates the authorization, assigns a delegation marker if needed, and updates the authorizer’s nonce.

func (st *stateTransition) applyAuthorization(
    auth *types.SetCodeAuthorization) error {

    // Validate the authorization and recover the authorizer's address
    authority, err := st.validateAuthorization(auth)
    if err != nil {
        return err
    }

    // Revocation: if the address is zero, clear the EOA code
    if auth.Address == (common.Address{}) {
        st.state.SetCode(authority, nil,
            tracing.CodeChangeAuthorization)
    } else {
        // Set the delegation designator pointing to the delegate contract
        st.state.SetCode(authority,
            types.AddressToDelegation(auth.Address),
            tracing.CodeChangeAuthorization)
    }

    // Increment the authorizer's nonce to prevent replay
    st.state.SetNonce(authority, auth.Nonce+1,
        tracing.NonceChangeAuthorization)

    return nil
}

Step 1: Validate the Authorization

Validation recovers the authorizer address and ensures the authorization follows the rules defined in EIP-7702. Invalid authorizations cause the function to return an error without modifying the state.

Step 2: Apply or Revoke Delegation Code

Assigning a zero address clears the code, effectively revoking any previous delegation. We’re going to cover how to revoke delegation in more detail later in this article. Non-zero addresses store a delegation designator, pointing the EOA to the target delegate contract while preserving the EOA’s balance and storage.

Step 3: Increment the Authorizer Nonce

The authorizer’s nonce is incremented to prevent replay attacks and ensure proper ordering of subsequent EIP-7702 transactions.

applyAuthorization executes EIP-7702 logic for EOAs by combining validation, delegation assignment, and nonce updates, allowing Geth to enforce delegation markers securely and correctly.

Authorization Validation Step in the Geth Execution Flow

During transaction execution, Geth validates each SetCodeAuthorization before any state mutation occurs. The validateAuthorization function enforces all invariants required by EIP-7702 inside the state transition pipeline.

func (st *stateTransition) validateAuthorization(
    auth *types.SetCodeAuthorization,
) (common.Address, error) {

    // 1. Chain ID: must be 0 (cross-chain) or match the current network
    if !auth.ChainID.IsZero() &&
       auth.ChainID.CmpBig(st.evm.ChainConfig().ChainID) != 0 {
        return common.Address{},
            ErrAuthorizationWrongChainID
    }

    // 2. Nonce overflow check: ensure incrementing the nonce won't wrap around
    if auth.Nonce+1 < auth.Nonce {
        return common.Address{},
            ErrAuthorizationNonceOverflow
    }

    // 3. Recover the authorizer address from the signature (ECDSA / ecrecover)
    authority, err := auth.Authority()
    if err != nil {
        return common.Address{}, err
    }

    // 4. Add the recovered address to the access list (EIP-2929)
    st.state.AddAddressToAccessList(authority)

    // 5. Code must be empty or already a valid delegation marker
    code := st.state.GetCode(authority)
    if len(code) > 0 {
        if _, ok := types.ParseDelegation(code); !ok {
            return common.Address{},
                ErrAuthorizationDestinationIsContract
        }
    }

    // 6. Authorization nonce must exactly match the account's current nonce
    if auth.Nonce != st.state.GetNonce(authority) {
        return common.Address{},
            ErrAuthorizationNonceMismatch
    }

    // Return the recovered and validated authority address
    return authority, nil
}

Validation happens before applyAuthorization modifies account code.

1. Chain ID Check

The authorization is accepted only if:

  • ChainID == 0, or
  • ChainID matches the current network’s chain ID.

Mismatch causes immediate rejection, preventing cross-chain replay.

2. Nonce Overflow Guard

Overflow detection ensures that incrementing the nonce will not wrap around. Any overflow attempt results in failure.

3. Signature Recovery (ecrecover)

Signature recovery derives the authorizer address from the signed authorization payload. Failure stops execution.

Successful recovery proves that the signer controls the account whose code may be modified.

4. Access List Update (EIP-2929)

The recovered address is added to the access list. Under EIP-2929 rules, access list updates affect gas accounting for cold versus warm accesses during execution.

5. Code Constraint: Empty or Delegated Only

Accounts are allowed to:

  • Have no code, or
  • Already contain a valid delegation designator.

If the account contains regular contract bytecode, validation fails. Geth does not permit overwriting deployed smart contracts using EIP-7702.

6. Nonce Equality Requirement

Authorization nonce must exactly match the account’s current nonce. Any mismatch causes rejection.

Strict equality guarantees ordering and replay protection for delegation updates.

Role of validateAuthorization in the Execution Pipeline

Within the state transition:

  1. Sender nonce is incremented.
  2. Each authorization is validated using validateAuthorization.
  3. Only after successful validation does applyAuthorization modify account code and nonce.
  4. EVM execution begins.

Validation therefore acts as a gatekeeper step in the EIP-7702 execution flow inside Geth.

How to Revoke EIP-7702 Authorization in Geth

Revocation in EIP-7702 is performed by submitting a new authorization where the Address field is set to address(0).

From the original function:

if auth.Address == (common.Address{}) {
    st.state.SetCode(authority, nil,
        tracing.CodeChangeAuthorization)
}

When auth.Address equals the zero address:

  • SetCode(authority, nil) clears the code stored on the EOA.
  • Any previously stored delegation designator is removed.
  • The account returns to behaving like a normal EOA with no delegated execution logic.

No separate “revoke” opcode exists. Revocation is achieved by writing nil code through a new valid authorization.

Delegation Resolution Inside the EVM Execution Path

After validateAuthorization and applyAuthorization complete, execution reaches the EVM. If the caller is an EOA that previously stored a delegation designator under EIP-7702, the EVM resolves which bytecode should be executed.

The logic lives in core/vm/evm.go:

func (evm *EVM) resolveCode(addr common.Address) []byte {
    code := evm.StateDB.GetCode(addr)


    // Check for delegation designator
    if target, ok := types.ParseDelegation(code); ok {
        // Load code from delegate contract
        return evm.StateDB.GetCode(target)
    }


    return code
}

Step 1: Load Account Code

The EVM retrieves the code stored at the account address.

  • For a regular EOA, code is empty.
  • For a smart contract, code contains deployed bytecode.
  • For an EIP-7702 delegated EOA, code contains a delegation designator.

Step 2: Detect Delegation Marker

ParseDelegation checks whether the stored bytes represent a delegation designator instead of normal contract bytecode.

If parsing succeeds:

  • target becomes the delegate contract address.
  • Execution bytecode is resolved from that target.

Step 3: Load Delegate Contract Code

The EVM loads the bytecode from the delegate contract address.

If no delegation marker exists, execution proceeds with the originally retrieved code:

return code

Protocol-Level Delegatecall Semantics

It is important to know that code executes in the context of the EOA — using its storage and balance — while being loaded from the delegate contract. This follows DELEGATECALL semantics at the protocol level.

  • EOA context preserved: Storage and balance changes affect the EOA, not the delegate.
  • Delegate contract code loaded: Execution logic comes from the delegate contract’s bytecode.

This distinction ensures that delegated execution does not modify the EOA’s state outside the intended logic.

How to Revoke EIP-7702 Delegation

Delegation under EIP-7702 works by storing a delegation designator as the EOA’s account code:

st.state.SetCode(
    authority,
    types.AddressToDelegation(auth.Address),
    tracing.CodeChangeAuthorization)

The EOA does not deploy contract bytecode in the traditional sense. Instead, it stores a special delegation marker that points to another contract’s address. During execution, the EVM resolves that marker and runs the delegate’s code while keeping the EOA’s balance and storage context.

Revocation happens by submitting a new valid SetCodeAuthorization where:

auth.Address == (common.Address{})

The corresponding branch in applyAuthorization clears the account code:

st.state.SetCode(authority, nil,
    tracing.CodeChangeAuthorization)

Clearing the code removes the delegation designator entirely. After that state transition:

  • No delegation marker remains on the EOA.
  • The account no longer resolves execution to a delegate contract.
  • The EOA behaves like a standard externally owned account again.

Nonce handling is still enforced:

st.state.SetNonce(authority, auth.Nonce+1,
    tracing.NonceChangeAuthorization)

Summary

Revoke EIP-7702 authorization by submitting a new signed authorization with Address = address(0).

Revoke EIP-7702 delegation by clearing the delegation designator, which is implemented internally as SetCode(authority, nil).

No separate revocation opcode exists. State transition logic replaces the stored delegation marker with empty code.

ERC-7702 vs ERC-4337

Both ERC-7702 and ERC-4337 are designed for account abstraction, but they take fundamentally different approaches.  Let’s break down the core specs and see where each one makes sense.

Key Specifications Differences

Aspect EIP-7702 ERC-4337
User address Original EOA stays the same New smart contract wallet
Protocol changes Adds a new transaction type (0x04) No changes needed
Infrastructure Delegate contract + Relayer Bundler + EntryPoint + Paymaster
Gas per auth ~12,500 ~42,000+ (validation)
msg.sender for dApps Original EOA Smart contract wallet
Compatibility Works with any existing contracts Requires ERC-4337 support
Batch transactions Through delegate contract Native support
Gas sponsorship Relayer (tx.origin) Paymaster contract

When ERC-4337 Fits

ERC-4337 makes sense in the following use cases:

  • Permissionless Paymasters: You need a public gas sponsorship service, for example in a DeFi protocol, without relying on a trusted operator.
  • Transaction Aggregation: You want to bundle multiple users’ operations into a single on-chain transaction, e.g., BLS signature aggregation.

When to use EIP-7702

EIP-7702 is enough when your flows are limited to:

  • Batching transactions for a single user
  • Session keys and delegated permissions
  • Gas sponsorship from a known operator
  • Spending limits and time locks
  • Social recovery

Use EIP-7702 when the goal is single-user delegation, session keys, or trusted gas sponsorship — it preserves the EOA and keeps implementation lightweight. ERC-4337 fits scenarios requiring public paymasters or batching multiple users’ operations into a single transaction. Choosing between them depends on whether your flow needs minimal EOA delegation or a full smart contract wallet stack.

Comparison EIP 7702 Gas Sponsorship with ERC-2771

Before EIP-7702, gas sponsorship was implemented using ERC-2771, the standard for meta-transactions.

How ERC-2771 Works

ERC-2771 uses a Trusted Forwarder pattern:

(bool success,) = target.call(
    abi.encodePacked(data, from)  // 20 bytes appended
);


// Recipient contract extracts the sender
function _msgSender() internal view returns (address) {
    if (isTrustedForwarder(msg.sender)) {
        // Last 20 bytes of calldata = actual sender
        return address(bytes20(msg.data[msg.data.length - 20:]));
    }
    return msg.sender;
}

Key Difference: msg.sender

The main distinction between ERC-2771 and EIP-7702 is what msg.sender resolves to in the target contract:

Aspect ERC-2771 EIP-7702
msg.sender in target contract Forwarder contract User’s EOA
Requires contract modification Yes (ERC2771Context) No
Works with standard DeFi contracts (Uniswap, Aave, OpenSea) No Yes
Address forwarding mechanism Calldata append (20 bytes) Protocol-level delegation
Signature verification On-chain in Forwarder At EVM level (ecrecover)

ERC-2771 requires the target contract to use _msgSender() instead of msg.sender. Without this, existing contracts like Uniswap or Aave cannot interact with meta-transactions.

EIP-7702 handles delegation at the protocol level: the target contract sees the real EOA as msg.sender, not an intermediate contract, which allows interaction with standard DeFi contracts without modification.

Gas Station Transaction Flow

Gas Station allows users to execute transactions without holding ETH. The user signs their intent and a relayer submits the transaction, while execution still occurs in the user’s account context.

Let’s take a look at flowchart to see how the transaction moves from the user through the relayer and executes on-chain.

EIP 7702 Gas Station Transaction Work Flow

How the Flow Works

  1. User Signs the Intent – The user’s EOA signs both a gas authorization for the Gas Station and the batch of calls they want executed.
  2. Relayer Submits the Transaction – The Gas Station relayer collects the signed data and submits a Type-4 EIP-7702 transaction on behalf of the user.
  3. EVM Validation and Preparation – On-chain, the EVM checks that the target address is valid and that the authorization list is not empty. It calculates intrinsic gas, increments the sender nonce, and applies authorization markers linking the user EOA to the delegated batch contract.
  4. Batch Execution – The EVM calls the user’s EOA with the encoded batch data. Delegation ensures the delegate contract executes the requested operations.
  5. Execution in User Context – All calls run in the user’s context. The msg.sender in the target contract reflects the Gas Station relayer, while address(this) points to the user’s EOA.

Delegate Contract for Batch Execution

The GasStationDelegate contract executes multiple calls in a single transaction. Each call specifies a target contract, an ETH value, and calldata. The contract processes these calls sequentially while preserving the user’s EOA as the context for balances, storage, and msg.sender.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;


contract GasStationDelegate {
    struct Call {
        address target;
        uint256 value;
        bytes data;
    }


    function executeBatch(Call[] calldata calls) external {
        for (uint i = 0; i < calls.length; i++) {
            (bool success, bytes memory result) =
                calls[i].target.call{value: calls[i].value}(calls[i].data);


            if (!success) {
                assembly {
                    revert(add(result, 32), mload(result))
                }
            }
        }
    }
}

How it works

  • Loops through each Call and executes it on the target contract.
  • Reverts the batch if any call fails, returning the original error.
  • Ensures the user’s EOA context is preserved, so msg.sender, storage, and balances behave as if the user called directly.

Gas Station Backend Flow (Go)

The backend constructs and signs a SetCode transaction that instructs the user’s EOA to execute the batch via the delegate contract:

func buildSetCodeTx(
    userAuth *types.SetCodeAuthorization,
    userAddress common.Address,
    callData []byte,
    relayerKey *ecdsa.PrivateKey,
) (*types.Transaction, error) {


    tx := types.NewTx(&types.SetCodeTx{
        ChainID:   uint256.NewInt(1), // Mainnet
        Nonce:     relayerNonce,
        GasTipCap: uint256.NewInt(1e9),
        GasFeeCap: uint256.NewInt(50e9),
        Gas:       500000,
        To:        userAddress,       // call user EOA
        Value:     uint256.NewInt(0),
        Data:      callData,          // executeBatch encoded
        AuthList:  []types.SetCodeAuthorization{
            *userAuth,
        },
    })


    signer := types.NewPragueSigner(big.NewInt(1)) // chainId
    return types.SignTx(tx, signer, relayerKey)
}

What’s happening here

  • The relayer builds a SetCode transaction for the user’s EOA.
  • callData contains the batch of operations the delegate contract will run.
  • The transaction is signed by the relayer but executes with the user’s EOA as context — msg.sender inside the targets resolves to the user, not the relayer.

EIP-7702 Step-by-Step Gasless Swap Example

Let’s say Alice wants to swap USDC for DAI but has no ETH to pay for gas. Using the Gas Station with EIP-7702, she can execute this swap while keeping her EOA as the context for the transaction.

// 1. User signs the authorization
auth := &types.SetCodeAuthorization{
    ChainID: *uint256.NewInt(1),
    Address: GAS_STATION_DELEGATE,
    Nonce:   userNonce,
}
auth.Sign(userPrivateKey)


// 2. Forming a batch of operations
calls := []Call{
    {
        Target: USDC_ADDRESS,
        Value:  0,
        Data:   encodeApprove(DEX_ROUTER, amount),
    },
    {
        Target: DEX_ROUTER,
        Value:  0,
        Data:   encodeSwap(USDC, DAI, amount, minOut),
    },
}


// 3. Gas Station sends a Type-4 transaction
// DEX Router sees msg.sender = userAddress ✓

Step 1: Alice signs authorization

Alice signs a SetCodeAuthorization for her EOA to delegate execution to the GasStationDelegate. This proves she approved the batch operation without sending ETH herself.

Step 2: Construct the batch of operations

Alice wants to first approve the DEX router to spend her USDC, then swap USDC for DAI. These calls are packed into a batch that the delegate contract will execute sequentially.

Step 3: Gas Station relayer submits the transaction

The Gas Station picks up Alice’s signed authorization and submits a Type-4 transaction. The GasStationDelegateexecutes the batch on behalf of Alice.

  • The DEX router sees msg.sender as Alice’s EOA, not the Gas Station.
  • Storage and balances are updated in Alice’s context.
  • Alice completes the swap without holding ETH.

Observations from the execution flow

  • Batch execution allows multiple operations in one transaction.
  • EIP-7702 preserves msg.sender as the user EOA, ensuring compatibility with standard DeFi contracts.
  • Gas sponsorship is handled externally by the relayer, so users can interact with contracts without ETH.

EIP-7702 Security Considerations

Delegating execution through EIP-7702 introduces specific risks that need careful handling.

Risks in Delegation

  • Malicious delegate contract — the delegate can fully access the user’s EOA storage and balances. Only audited delegates should be used.
  • Front-running initialization — EIP-7702 does not support init code; an attacker could intercept a user’s authorization before execution.
  • Storage collision — replacing a delegate may conflict with existing storage layouts (see ERC-7201).
  • Cross-chain replay — authorizations with chainId = 0 are valid on all chains, enabling replay attacks.

Protections at the Contract Level

Contracts can block delegated calls with a modifier like:

modifier noEIP7702Delegation() {
    bytes memory code = msg.sender.code;
    if (code.length >= 3 &&
        code[0] == 0xef &&
        code[1] == 0x01 &&
        code[2] == 0x00) {
        revert("Delegation not allowed");
    }
    _;
}

This ensures only direct EOA calls reach the contract.

Protections at the Gas Station Level

  • Delegate whitelist — only allow authorizations for vetted delegate contracts.
  • Rate limiting — restrict the number of sponsored transactions per user.
  • Gas price caps — prevent manipulation of transaction fees.
  • Simulation — test transactions before execution to catch failures or unexpected behavior.
  • Monitoring — track abnormal relayer activity to identify potential misuse.

Gas Costs of EIP-7702 Transactions

Delegated transactions under EIP-7702 introduce extra gas components compared to a standard transfer.

Gas Breakdown

Component Gas
Base transaction 21,000
PER_AUTH_BASE_COST (per authorization) ~12,500
PER_EMPTY_ACCOUNT_COST (new delegation) 25,000
Cold account access (delegate resolution) 2,600
Warm account access 100

A simple transfer using EIP-7702 can increase gas by roughly 50%. However, batching multiple operations in a single transaction offsets these costs, often resulting in net savings compared to executing N individual transactions.

Where EIP-7702 Fits Best in System Design

Use it when you need:

  • Gas sponsorship with known operator
  • Session keys
  • Spending limits
  • Batch execution
  • Social recovery

Avoid it when:

  • You need permissionless Paymasters
  • You aggregate multiple users per transaction
  • You require advanced signature aggregation

Architecturally:

7702 minimizes additional surface area while preserving universal dApp compatibility.

Wrapping Up EIP-7702

EIP-7702 bridges the simplicity and compatibility of EOAs with capabilities typically reserved for Smart Contract Wallets.

How it Stands Out

  • Compared to ERC-4337: preserves the user’s address, maintains native msg.sender, and has lower gas overhead.
  • Compared to ERC-2771: works with any existing contract without requiring modifications.
  • Compared to EIP-3074: forward-compatible with the broader Account Abstraction roadmap.

Deployment Recommendations

  • Accept authorizations only from audited delegate contracts.
  • Use chain-specific authorizations (chainId != 0) to prevent cross-chain replay.
  • Plan for nonce management when handling sponsored transactions.
  • Test thoroughly on Sepolia before deploying to mainnet.

EIP-7702 opens access to Account Abstraction features for every existing Ethereum wallet while keeping interactions straightforward and compatible.

Key Takeaways

What is EIP-7702?

EIP-7702 is an Ethereum proposal that allows an Externally Owned Account (EOA) to temporarily execute smart contract code via a signed authorization, without changing its address, balance, or transaction history.

It introduces a new transaction type (Type 4 / 0x04) and an authorization_list field that enables protocol-level delegation. This design brings account abstraction capabilities directly to standard EOAs.

How Does EIP-7702 Work?

At a high level:

  1. The user signs an authorization using a 0x05-prefixed ECDSA signature.
  2. A relayer submits a SetCodeTransaction (0x04).
  3. The EVM writes a 23-byte delegation marker (0xef0100 || delegate_address) into the EOA’s code field.
  4. During execution, the EVM loads bytecode from the delegate contract.
  5. Execution runs in the EOA’s storage and balance context.

No new wallet address. No wrapper contract. No forwarder logic in target contracts.

This is protocol-level delegation with DELEGATECALL semantics.

What Type of Transaction Is an EIP-7702 Tx?

An EIP-7702 transaction is:

  • Transaction Type 4 (0x04)
  • An extension of the EIP-1559 transaction format
  • Includes a new authorization_list field
  • Uses raw ECDSA signing with a 0x05 prefix

Everything else — gas rules, nonce behavior, fee mechanics — follows standard Ethereum execution rules.

What Are EIP-7702 Authorizations?

An authorization tuple contains:

  • chain_id
  • delegate address
  • nonce
  • y_parity, r, s (ECDSA signature)

Each authorization explicitly states:

  • Which contract can execute logic on behalf of the EOA
  • On which chain
  • At which nonce

The client validates these independently before applying delegation.

Is EIP-7702 Implemented?

Yes. Go-Ethereum (Geth v1.15.x) includes full support for EIP-7702:

  • Native SetCodeTx structure
  • Validation of AuthList
  • Delegation marker handling
  • Execution resolution inside evm.go

This means EIP-7702 is implemented at the client level and supported by Ethereum nodes running recent Geth versions.

How to Revoke EIP-7702 Authorization?

EIP-7702 authorization is revoked by submitting a new valid signed authorization where the Address field is set to address(0).

When processed, the client executes: SetCode(authority, nil)

This clears the EOA’s code field and removes any previously stored delegation marker. The revocation:

  • Requires a valid 0x05-prefixed ECDSA signature
  • Requires nonce equality
  • Increments the authorizer’s nonce
  • Follows the same validation path as delegation

There is no dedicated revoke opcode. Revocation is performed through a new SetCode authorization transaction (Type 4 / 0x04).

How to Revoke EIP-7702 Delegation?

EIP-7702 delegation is revoked by clearing the delegation designator stored in the EOA’s code field.

Delegation works by writing a 23-byte marker: 0xef0100 || delegate_address

To revoke delegation, a new SetCodeAuthorization must be submitted where: auth.Address == address(0)

Inside the state transition, the client executes: st.state.SetCode(authority, nil)

After this state update:

  • No delegation marker remains
  • The EVM no longer resolves code from a delegate contract
  • The EOA behaves as a standard externally owned account
  • Nonce is incremented to prevent replay

Delegation revocation is not automatic. It must be explicitly authorized and validated like any other EIP-7702 authorization.

EIP-7702 Account Abstraction: What Changes?

EIP-7702 introduces a minimal-surface account abstraction model:

  • Keeps original EOA
  • Preserves msg.sender
  • Enables batching
  • Enables session keys
  • Enables gas sponsorship
  • Enables spending limits and social recovery

Unlike full smart contract wallets, it does not require deploying a new account contract.

ERC-7702 vs ERC-4337

Ethereum builders often compare:

ERC-7702

  • Keeps original EOA
  • Native msg.sender
  • Lower gas overhead (~12.5k per authorization)
  • Works with any existing DeFi contract
  • Requires protocol support (Type 4 tx)

ERC-4337

  • Uses smart contract wallets
  • Requires EntryPoint and Bundlers
  • Higher validation gas (~42k+)
  • Enables permissionless Paymasters
  • No protocol changes

Use EIP-7702 when:

  • You need single-user delegation
  • You control the relayer
  • You want maximum dApp compatibility

Use ERC-4337 when:

  • You need public Paymasters
  • You aggregate multiple users per transaction
  • You require signature aggregation

EIP-7702 vs ERC-2771 (Meta-Transactions)

ERC-2771 uses a Trusted Forwarder and modifies msg.sender logic.

EIP-7702 operates at the protocol level:

  • Target contracts see the real EOA
  • No contract modification required
  • No calldata appending tricks

This makes EIP-7702 compatible with existing DeFi protocols without rewriting contract logic.

Gas Costs of EIP-7702 Transactions

Typical additional gas components:

  • ~12,500 gas per authorization
  • 25,000 gas for new delegation
  • Cold account access: 2,600 gas

A simple transfer may cost ~50% more.

However, batching multiple operations in one transaction often results in net gas savings compared to N separate transactions.

Where EIP-7702 Fits Best

EIP-7702 is ideal for:

  • Gas sponsorship with a known operator
  • Session keys
  • Spending limits
  • Batch execution
  • Social recovery

Avoid it when you need:

  • Permissionless Paymasters
  • Multi-user aggregation
  • Advanced signature aggregation

Architecturally, it provides a lightweight bridge between EOAs and smart contract wallets without forcing migration.

Summary

EIP-7702 brings account abstraction to EOAs without changing their identity.

It introduces:

  • A new transaction type (0x04)
  • An authorization_list field
  • A delegation marker stored in the EOA code
  • 0x05-prefixed ECDSA signing

It is implemented in Geth, compatible with existing DeFi contracts, and suitable for controlled gas sponsorship and delegation flows.

For protocols, wallet builders, and infrastructure teams, EIP-7702 represents a minimal yet powerful extension to Ethereum’s execution model — expanding what EOAs can do without replacing them.

Have an Idea?
Let's chat!

Get free consultation

message on whatsupmessage on telegramcall button
This site is protected by reCAPTCHA and the Privacy Policy and Terms of Service apply.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
WhatsUpmessage on telegram
closeChat with us

A version of this page is available in your language.
Would you like to switch?

Let’s Talk

An error occurred. Please refresh the page and try again.
Awards