A photo of Shodipo Ayomide
All Posts

Understanding and Implementing EIP-7702

Sep 27, 2025
·
6 min read
·
views
·
likes
Understanding and Implementing EIP-7702

Let's say you're trying to execute a complex all-in-one atomic transaction that requires swapping tokens, adding liquidity, and staking rewards. But then, your regular Ethereum wallet EOA can't do batch transactions. So, you'll need to sign three separate transactions, pay gas three times, which is a no no, and then pray that MEV bots don't sandwich you between steps.

This is one of the heated conversation in the EVM ecosystem lately, how exactly do we give regular wallets superpowers without breaking everything all at the same time?

Two EIPs came out of this challenge, EIP-3074 and subsequently EIP-7702. Both looking to close the gap between simple EOA and smart contract accounts, but they take different approaches to delegating authority. One offers persistent power in which things can go very wrong and your wallets gets drained. The other can give you both persistent delegation and temporary delegation that could vanish the moment your transaction is executed on-chain.

Let's talk about why this difference matters and how Vitalik's proposal might have just saved Ethereum from a potential security nightmare.

EIP-2718 - Typed Transaction Envelope

Before we get into the delegation topic, there's an important piece of infra that makes all of this possible which is EIP-2718. Think of it as Ethereum's shipping container system for transactions.

// before eip-2718 all transactions looked the same
const legacyTx = rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s]);
 
// after eip-2718 transactions can now be typed
const typedTx = TransactionType || TransactionPayload;

EIP-2718 introduces Typed Transaction Envelopes where TransactionType || TransactionPayload is a valid transaction. TransactionType identifies the format of the transaction while Payload contains the transaction contents. Now, this wrapper system means we can introduce entirely new transaction behaviors without breaking the existing functionality.

By bring in an envelope transaction type, we only need to ensure backward compatibility with existing transactions and from then on we just need to solve the much simpler problem of ensuring there is no numbering conflict between TransactionTypes. So when EIP-7702 needed to introduce its temporary delegation mechanism, it could simply define a new transaction type for e.g, 0x04 without requiring any changes to the EVM itself.

EIP-3074 - Persistent Delegation Approach

EIP-3074 took a bold approach to delegation, giving EOAs the ability to permanently delegate authority to smart contracts using two new EVM opcodes - AUTH and AUTHCALL.

How AUTH and AUTHCALL works

The mechanism is straight forward

// user signs an auth message
bytes32 commit = keccak256(abi.encode(invokerAddress, batchActions));
bytes memory signature = signMessage(userPrivateKey, commit);
 
// invoker contract calls AUTH opcode
AUTH(commit, signature.v, signature.r, signature.s);
// This sets the 'authorized' context variable to user's address
 
// now invoker can make calls as the user
AUTHCALL(gasLimit, targetContract, 0, 0, calldata, calldataLength, 0, 0);

AUTH takes four parameters commit and the signature's yParity, r, and s and if the signature is valid and the signer address equals the authority, the context variable authorized is set to that authority. AUTHCALL then allows the authorized invoker contract to send transactions, with CALLER being set to the authorized address rather than the invoker.

Now, imagine an invoker contract that could handle batched transactions

contract BatchInvoker {
    struct BatchCall {
        address target;
        bytes data;
        uint256 value;
    }
    
    function executeBatch(
        BatchCall[] calldata calls,
        bytes32 commit,
        uint8 v, bytes32 r, bytes32 s
    ) external {
        // authorize the user
        AUTH(commit, v, r, s);
        
        // Execute all calls as the user
        for (uint i = 0; i < calls.length; i++) {
            AUTHCALL(
                gasleft(),
                calls[i].target,
                calls[i].value,
                0, // valueExt (must be 0 for now)
                // ... calldata parameters
            );
        }
    }
}

In as much as this is exciting, these two opcodes brought up a number of security concerns with the EVM community. The context variable authorized persists throughout a single frame of execution of the contract, but is not passed through any calls including DELEGATECALL. This basically means the authorization is active for the entire transaction scope.

The real problem isn't the temporary nature within a transaction, it's what happens when you have widely used invoker contracts. If everyone will be using smart accounts eventually, the AUTH and AUTHCALL opcodes can be a huge technical burden and add complexity to the EVM itself.

What happens when a popular invoker contract like the one above has a bug? Suddenly, every user who's ever authorized that contract would possible be at a rist. One malicious update, one missed edge case, one integer overflow and that's all, a mass drainage starts.

The EVM ecosystem realized that this created a weird dynamic. The security model of EIP-3074 relies heavily on the invoker contract. Since multiple users might rely on the same invoker logic, a vulnerability or exploit in one invoker could compromise assets or permissions of many users all at once, which is why EIP-3074 was withdrawn.

EIP-7702 - The Flexible Delegation Solution

Now, EIP-7702 proposed by Vitalik in May 2024 went mainnet via the pectra upgrade in May 2025 and introduces a new streamlined approach, allowing EOAs to temporarily function like smart contracts for the duration of a single transaction, which is a big deal!

Instead of new opcodes, EIP-7702 leverages EIP-2718's transaction envelope system to introduce a new transaction type

interface EIP7702Transaction {
    chainId: number;
    nonce: number;
    maxPriorityFeePerGas: bigint;
    maxFeePerGas: bigint;
    gasLimit: bigint;
    to: string;
    value: bigint;
    data: Uint8Array;
    accessList: AccessList;
    authorizationList: Authorization[];
}
 
interface Authorization {
    chainId: number;
    address: string;  // the address of the contract code to delegate to
    nonce: number;
    yParity: 0 | 1;
    r: Uint8Array;
    s: Uint8Array;
}

For each authorization object, a delegation indicator (0xef0100 || address) is written to the authorizing account's code. All code executing operations must load and execute the code pointed to by the delegation.

How Delegation Persistence Actually Works

This is where many people get confused. EIP-7702 has evolved since its original draft. Initially, the proposal was designed to make EOAs temporarily smart for just one transaction. But the final version gives you control over how long the delegation lasts.

function processEIP7702Transaction(tx) {
    // verify all auth signatures
    for (const auth of tx.authorizationList) {
        const signer = recoverSigner(auth);
        if (signer !== getAddressFromAuth(auth)) {
            throw new Error("Invalid authorization");
        }
    }
    
    // set delegation pointers (these persist after transaction)
    for (const auth of tx.authorizationList) {
        const eoaAddress = getAddressFromAuth(auth);
        
        // set delegation pointer - 0xef0100 || contract_address
        const delegationPointer = concat(['0xef0100', auth.address]);
        setCode(eoaAddress, delegationPointer);
        
        // nowm this delegation PERSISTS unless explicitly cleared
    }
    
    // execute the transaction normally
    executeTransaction(tx);
    
    // no auto cleanup, delegation stays active
}

NOTE: If a 7702 transaction execution fails, the processed delegation indicators are not rolled back and persists unless explicitly cleared by setting the delegation address to 0x0 in a subsequent 7702 transaction.

This means you have three delegation patterns:

Temporary AND Persistent Options

The good thing about EIP-7702 is that it gives you the option to choose. Want temporary delegation for a one-off complex transaction? You can do that. Want to persistently upgrade your EOA to act like a smart contract wallet? You can do that too.

For temporary delegation, here's an example

// one off delegation
const tempDelegationTx = {
    type: 0x04,
    authorizationList: [{
        address: batchExecutorContract,
        // ... auth details
    }],
    to: userAddress,
    data: encodeBatchCall([
        // execute your complex operation
        swapTokens(),
        addLiquidity(), 
        stakeRewards(),
    ])
};

And for Persistent Delegation, here's an example

// upgrade EOA to smart wallet
const persistentDelegationTx = {
    type: 0x04,
    authorizationList: [{
        address: smartWalletImplementation,
        // ... auth details  
    }],
    to: userAddress,
    data: encodeCall("initializeSmartFeatures", [])
    // this delegation persists for future txns
};
 
// later txns can now use smart wallet features
const futureTransaction = {
    to: userAddress, // your EOA will now execute smart wallet code
    data: encodeCall("batchExecute", [multipleOperations])
    // no new auth needed
};

Why 7702 Is Safer

The key difference between EIP-3074 and EIP-7702 isn't just temporary vs. persistent, it's about risk distribution and control. EIP-3074 creates a shared point of failure where one popular contract's problem could affect thousands of users. EIP-7702 isolates each transaction, so bugs could only affect that specific transaction. EIP-7702 is fully compatible with existing ERC-4337, requiring no new opcodes. This basically means, wallets can adopt it gradually without breaking any existing systems.

The critical insight is that wallets, not dApps, manage delegations. So the general agreement is to only allow wallets to manage delegations which means:

What Dev's Need to Know

If you're building an application that will support EIP-7702, few technical things to note

  1. Transaction Construction Changes Your transaction building logic needs to handle authorization lists
// old: simple EOA transaction
const tx = {
    to: contractAddress,
    data: encodedCall,
    // ... standard fields
};
 
// new way with eip-7702 delegation
const tx = {
    type: 0x04,
    to: userAddress, // calling user's own address
    data: encodeBatchCall([
        contract.interface.encodeFunctionData("swap", [tokenA, tokenB, amount]),
        contract.interface.encodeFunctionData("addLiquidity", [lpTokens]),
        contract.interface.encodeFunctionData("stake", [lpAmount])
    ]),
    authorizationList: [{
        address: batchExecutorImplementation,
        // ... authorization signature
    }],
    // ... standard fields
};
  1. Gas Estimation Complexity If EOA decides to revoke smart contract control, another 7702 transaction must be sent to set delegation back to 0x0. This means your gas estimation needs to account for
  1. Smart Contract Signature Support EIP-7702 enables EIP-1271 signature verification for EOAs. This means your upgraded EOA can sign messages using alternative methods like passkeys, while still maintaining backward compatibility.
// your EOA can now implement smart signature verification
function isValidSignature(bytes32 hash, bytes signature) external view returns (bytes4) {
    // can verify passkey signatures, multisig, etc.
    if (verifyPasskeySignature(hash, signature)) {
        return 0x1626ba7e; // EIP-1271 magic value
    }
    return 0xffffffff;
}
Share this post:

Built with ❤️ © 2025