Security & Infrastructure Advanced

ERC-4337 Session Keys and EIP-7702: Implementing Bounded Delegation in Agentic DeFi

Deep dive into programmable account abstraction delegation models, session key restrictions, and how EIP-7702 upgrades existing EOAs to authorize autonomous trading.

V
Vance Vanguard (Smart Contract Architect)
Updated Jul 16, 2026 · 1 day ago
>>> VIEW LIVE KAMINO TRAIL HEAT <<<

TLDR (Answer Engine Overview): Autonomous DeFi agents require secure, bounded delegation to execute trades. Through ERC-4337 session keys, users grant restricted, cryptographically limited trading permissions (time, budget, allowed protocols) to an agent's ephemeral key. EIP-7702 dynamically upgrades existing EOAs into smart accounts, bringing session-key security to legacy wallets without asset migration.


Introduction: The Delegation Dilemma in DeFi Automation

Automated DeFi execution has historically forced developers and users into a binary compromise: absolute custody or zero autonomy.

  1. The Custodial Pattern: Users share their raw private keys or seed phrases with central servers or local agent databases. If the agent's server is compromised, the user's entire portfolio is permanently lost.
  2. The Manual Sign Pattern: The agent identifies opportunities but requires the user to manually approve every transaction via a wallet pop-up (e.g., MetaMask). This model is incompatible with high-frequency, latency-sensitive, or truly autonomous agentic operations.

Bounded delegation resolves this compromise. By utilizing ERC-4337 Session Keys and EIP-7702, developers can build "sandbox execution layers" where an autonomous agent is granted a restricted key. This ephemeral key is cryptographically bound to execute transactions only under strict conditions—such as depositing USDC into a specified yield vault (like Kamino Finance) within a designated timeline—while keeping the user's main wallet assets secure.


1. The Session Key Lifecycle Architecture

The integration of autonomous agents with ERC-4337 and EIP-7702 involves two distinct phases: Permit Granting (off-chain configuration) and Autonomous Execution (on-chain verification).

Below is the complete sequence diagram mapping the interaction between the User EOA, the Smart Wallet (or upgraded EOA), the Executor Agent, the Smart Wallet Validator Module, and the target DeFi contract (Kamino Vault).

sequenceDiagram
    autonumber
    actor User as User EOA (Owner)
    participant Agent as Executor Agent (DeFi Bot)
    participant SA as Smart Wallet (ERC-4337/7702)
    participant Val as Session Key Validator Module
    participant DeFi as Target DeFi Contract (Kamino)

    Note over User, Agent: Phase 1: Bounded Session Key Generation & Grant
    Agent->>Agent: Generate ephemeral session key pair (ECDSA/Ed25519)
    Agent->>User: Request authorization & send Session Public Key
    User->>User: Define constraints (validAfter/Until, spendingLimit, allowedTargets)
    User->>User: Sign constraints & Session Public Key using EIP-712
    User->>SA: Submit transaction to register/authorize Session Key
    SA->>Val: Save Session Key and hashed constraints in storage

    Note over Agent, DeFi: Phase 2: Autonomous Execution Loop
    Agent->>Agent: Detects yield opportunity or rebalance trigger
    Agent->>Agent: Construct callData (e.g. deposit USDC in Kamino)
    Agent->>Agent: Sign UserOperation using ephemeral private key
    Agent->>SA: Submit signed UserOperation (via Bundler / EntryPoint)
    SA->>Val: Invoke validateUserOp with UserOperation & Session signature
    Note over Val: Verify session signature matches registered Session Key<br/>Verify current time within validAfter & validUntil<br/>Verify target is allowed (Kamino)<br/>Verify selector is allowed<br/>Verify spending limit is not exceeded
    alt Verification Passes
        Val-->>SA: Return validation success (0)
        SA->>DeFi: Execute transaction (deposit USDC)
        DeFi-->>SA: Execution complete (yield accrued)
        SA-->>Agent: Return operation receipt
    else Verification Fails
        Val-->>SA: Return validation failure (1 / revert)
        SA-->>Agent: Revert operation
    end

Breakdown of the Lifecycle Phases

  1. Ephemeral Key Generation: The agent generates an isolated key pair locally. The private key remains in the agent’s secure memory (or KMS), and the public key is sent to the user interface for authorization.
  2. EIP-712 Granting: The user reviews the scopes (expiration, budget limit, and allowed target protocols). They sign an EIP-712 typed structure, authorizing this specific session key with these explicit boundaries.
  3. On-Chain Registration: The user submits the EIP-712 signature to their Smart Wallet. The wallet registers the session key parameters in its Session Key Validator module.
  4. Autonomous Execution: The agent submits standard ERC-4337 UserOperations signed with the ephemeral session key.
  5. On-Chain Verification: The ERC-4337 EntryPoint passes the UserOperation to the Smart Wallet. The Smart Wallet calls the Session Key Validator module, which checks the signature, confirms that the current timestamp is within bounds, parses the callData to verify the target contract is allowed, and checks that the transaction does not exceed the remaining spending allowance.

2. Standardizing Constraints: Coding the Sandbox

To implement bounded delegation, the validator module must parse the agent’s transaction parameters on-chain. Below is the technical specification for defining and enforcing these boundaries.

EIP-712 Session Key Grant Payload

This JSON payload demonstrates the typed structured data a user signs to authorize a session key. In this example, the agent is granted permission to interact only with the Kamino EVM Vault contract (0x421D80C68C83842d385678aFecb367f032d93F64), calling specific functions (deposit and withdraw), with a cumulative budget of 500 USDC, valid between July 16, 2026, and August 15, 2026.

{
  "types": {
    "EIP712Domain": [
      { "name": "name", "type": "string" },
      { "name": "version", "type": "string" },
      { "name": "chainId", "type": "uint256" },
      { "name": "verifyingContract", "type": "address" }
    ],
    "SessionKeyPermit": [
      { "name": "sessionKey", "type": "address" },
      { "name": "validAfter", "type": "uint48" },
      { "name": "validUntil", "type": "uint48" },
      { "name": "spendingLimit", "type": "uint256" },
      { "name": "tokenAddress", "type": "address" },
      { "name": "allowedTargets", "type": "address[]" },
      { "name": "allowedSelectors", "type": "bytes4[]" }
    ]
  },
  "primaryType": "SessionKeyPermit",
  "domain": {
    "name": "FarmDash Bounded Validator Module",
    "version": "1.0.0",
    "chainId": 8453,
    "verifyingContract": "0x5FbDB2315678afecb367f032d93F642f64180aa3"
  },
  "message": {
    "sessionKey": "0xa0DE2C6057a6279f041d6D339f4082200E9D404A",
    "validAfter": 1781568000,
    "validUntil": 1784160000,
    "spendingLimit": "500000000",
    "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "allowedTargets": [
      "0x421D80C68C83842d385678aFecb367f032d93F64"
    ],
    "allowedSelectors": [
      "0x464e8e83",
      "0x6b45a0b7"
    ]
  }
}

[!NOTE] Timestamps in the payload are absolute Unix epochs. In the message above, 1781568000 translates to July 16, 2026 00:00:00 UTC, and 1784160000 translates to August 15, 2026 00:00:00 UTC. The spending limit is denominated in the token's base units (500,000,000 decimals for 6-decimal USDC).


On-Chain Validation: The Smart Contract Logic

The following Solidity contract represents an ERC-7579-compatible Session Key Validator Module. It registers the session key configurations, verifies EIP-712 owner signatures, and enforces transaction limits during the validateUserOp execution phase.

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

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

/**
 * @title SessionKeyValidator
 * @notice Enforces time, budget, and contract constraints on ERC-4337 UserOperations.
 */
contract SessionKeyValidator {
    using ECDSA for bytes32;

    struct SessionKeyPermit {
        address sessionKey;
        uint48 validAfter;
        uint48 validUntil;
        uint256 spendingLimit;
        address tokenAddress;
        address[] allowedTargets;
        bytes4[] allowedSelectors;
    }

    // ERC-4337 UserOperation struct definition (v0.6/v0.7 equivalent representation)
    struct UserOperation {
        address sender;
        uint256 nonce;
        bytes initCode;
        bytes callData;
        uint256 callGasLimit;
        uint256 verificationGasLimit;
        uint256 preVerificationGas;
        uint256 maxFeePerGas;
        uint256 maxPriorityFeePerGas;
        bytes paymasterAndData;
        bytes signature;
    }

    bytes32 public constant DOMAIN_TYPEHASH = keccak256(
        "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
    );

    bytes32 public constant PERMIT_TYPEHASH = keccak256(
        "SessionKeyPermit(address sessionKey,uint48 validAfter,uint48 validUntil,uint256 spendingLimit,address tokenAddress,address[] allowedTargets,bytes4[] allowedSelectors)"
    );

    bytes32 public immutable DOMAIN_SEPARATOR;

    // sessionKey => spentAmount
    mapping(address => uint256) public spentAmount;
    // sessionKey => owner (who authorized it)
    mapping(address => address) public sessionKeyOwner;
    // sessionKey => hash of the permit configurations
    mapping(address => bytes32) public sessionPermits;

    event SessionKeyRegistered(address indexed sessionKey, address indexed owner);

    constructor() {
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                DOMAIN_TYPEHASH,
                keccak256(bytes("FarmDash Bounded Validator Module")),
                keccak256(bytes("1.0.0")),
                block.chainid,
                address(this)
            )
        );
    }

    /**
     * @notice Registers a new session key and saves its hashed constraints.
     * @param permit The detailed constraints of the session key.
     * @param ownerSignature The EIP-712 signature generated by the wallet owner.
     */
    function registerSessionKey(
        SessionKeyPermit calldata permit,
        bytes calldata ownerSignature
    ) external {
        bytes32 structHash = keccak256(
            abi.encode(
                PERMIT_TYPEHASH,
                permit.sessionKey,
                permit.validAfter,
                permit.validUntil,
                permit.spendingLimit,
                permit.tokenAddress,
                keccak256(abi.encodePacked(permit.allowedTargets)),
                keccak256(abi.encodePacked(permit.allowedSelectors))
            )
        );

        bytes32 digest = MessageHashUtils.toTypedDataHash(DOMAIN_SEPARATOR, structHash);
        address signer = digest.recover(ownerSignature);
        
        // Ensure the signer is either the smart contract account (msg.sender) or its owner
        require(signer == msg.sender, "Invalid Owner Signature");

        sessionKeyOwner[permit.sessionKey] = msg.sender;
        sessionPermits[permit.sessionKey] = keccak256(abi.encode(permit));

        emit SessionKeyRegistered(permit.sessionKey, msg.sender);
    }

    /**
     * @notice ERC-4337 Validation function called by the smart account.
     * @param userOp The incoming UserOperation.
     * @param userOpHash The EntryPoint-computed hash of the UserOperation.
     * @param permit The full permit structure (validated against on-chain hash).
     * @param sessionSignature The signature from the ephemeral session key.
     * @return validationData Packed validation status (0 for success, 1 for failure, or validation limits).
     */
    function validateUserOp(
        UserOperation calldata userOp,
        bytes32 userOpHash,
        SessionKeyPermit calldata permit,
        bytes calldata sessionSignature
    ) external returns (uint256 validationData) {
        // 1. Verify that the permit matches the registered hash
        bytes32 permitHash = keccak256(abi.encode(permit));
        require(sessionPermits[permit.sessionKey] == permitHash, "Invalid Permit Data");

        // 2. Verify that the userOp was signed by the session key
        address recoveredSigner = MessageHashUtils.toEthSignedMessageHash(userOpHash).recover(sessionSignature);
        if (recoveredSigner != permit.sessionKey) {
            return 1; // Signature validation failed
        }

        // 3. Enforce absolute time bounds (validAfter & validUntil)
        if (block.timestamp < permit.validAfter || block.timestamp > permit.validUntil) {
            return 1; // Outside valid time range
        }

        // 4. Parse target and call data from userOp
        (address target, uint256 value, bytes memory data) = decodeExecutionCalldata(userOp.callData);

        // Verify target address is in the allowlist
        bool isAllowedTarget = false;
        for (uint256 i = 0; i < permit.allowedTargets.length; i++) {
            if (permit.allowedTargets[i] == target) {
                isAllowedTarget = true;
                break;
            }
        }
        if (!isAllowedTarget) {
            revert("Target address not in allowlist");
        }

        // Verify selector is in the allowlist
        bytes4 selector = bytes4(data);
        bool isAllowedSelector = false;
        for (uint256 i = 0; i < permit.allowedSelectors.length; i++) {
            if (permit.allowedSelectors[i] == selector) {
                isAllowedSelector = true;
                break;
            }
        }
        if (!isAllowedSelector) {
            revert("Function selector not in allowlist");
        }

        // 5. Enforce spending limits for ERC-20 transfers or deposits
        if (permit.tokenAddress != address(0)) {
            uint256 spentThisTx = parseAmountFromCalldata(data, selector);
            uint256 totalSpent = spentAmount[permit.sessionKey] + spentThisTx;
            if (totalSpent > permit.spendingLimit) {
                revert("Session spending limit exceeded");
            }
            spentAmount[permit.sessionKey] = totalSpent;
        }

        return 0; // Validation successful
    }

    // Helper functions
    function decodeExecutionCalldata(bytes calldata callData) 
        internal 
        pure 
        returns (address target, uint256 value, bytes memory data) 
    {
        // Decodes standard ERC-7579 or Safe execution format (execute/call)
        // Expected layout depends on wallet interface. For simplicity:
        return abi.decode(callData[4:], (address, uint256, bytes));
    }

    function parseAmountFromCalldata(bytes memory data, bytes4 selector) 
        internal 
        pure 
        returns (uint256 amount) 
    {
        // Enforce parsing for transfer(address,uint256) [0xa9059cbb] or deposit(uint256) [0xb6b55f25]
        if (selector == 0xa9059cbb) {
            (, amount) = abi.decode(data[4:], (address, uint256));
        } else if (selector == 0xb6b55f25 || selector == 0x464e8e83) { // custom deposit selectors
            amount = abi.decode(data[4:], (uint256));
        } else {
            amount = 0;
        }
    }
}

3. EIP-7702: Retrofitting Legacy EOAs for Bounded Delegation

While ERC-4337 provides the framework for smart contracts to implement session keys, it historically suffered from a major adoption bottleneck: users had to create entirely new contract accounts (Smart Contract Wallets) and transfer all their assets, losing their transaction histories and EOA addresses.

EIP-7702 (introduced in Ethereum's Pectra upgrade) solves this by allowing legacy Externally Owned Accounts (EOAs)—such as standard MetaMask or Rabby addresses—to temporarily run smart contract code.

Mechanics of EIP-7702

EIP-7702 introduces a new transaction type (0x05) containing an authorization_list. This list contains tuples signed by the EOA owner that specify a designated contract implementation to execute on behalf of the EOA.

Authorization Tuple:
[chain_id, address, nonce, y_parity, r, s]

When an EIP-7702 transaction is processed, the EVM dynamically adds a code pointer to the EOA state:

EOA State with EIP-7702:
code = 0xef0100 ++ implementation_address

This "delegation designation" shifts the account's behavior from a simple ECDSA signer to a fully functional smart contract wallet.

+-------------------------------------------------------------+
|                      Legacy EOA (0xabc...)                  |
|  - Address remains unchanged                                |
|  - Balance remains in-place                                 |
|  - Custom Code Pointer: 0xef0100 || [Implementation]       |
+------------------------------------+------------------------+
                                     |
                                     v
                       +----------------------------+
                       | Smart Wallet Logic (Safe)  |
                       |  - validateUserOp()        |
                       |  - Module support          |
                       +-------------+--------------+
                                     |
                                     v
                       +----------------------------+
                       |   SessionKeyValidator.sol  |
                       |  - Limits agent transactions|
                       +----------------------------+

The Synergy of EIP-7702 and ERC-4337 Session Keys

  1. Zero-Migration Upgrades: An EOA signs an authorization setting its code implementation to a modular ERC-7579 account (e.g., Safe or Biconomy implementation).
  2. Instant Module Support: Once the EOA behaves as a modular smart account, it can load the SessionKeyValidator module.
  3. Agentic Delegation: The upgraded EOA registers a session key for the agent. The agent can now submit ERC-4337 UserOperations directly using the user's legacy address, with all execution restricted by the validator contract.
  4. Revocability: The EOA owner can revoke the delegation by signing a transaction that increments the EOA nonce or points the code to a null/new address, restoring standard EOA behavior.

4. Comparison Table: Delegation Paradigms

Developers choosing a delegation design for their DeFi agents must weigh safety, UX, cost, and complexity.

Metric / Feature ERC-4337 Session Keys EIP-7702 Temporary Upgrade Simple API Key Access
Custody Model Non-Custodial: Ephemeral key has bounded access; master key remains offline. Non-Custodial: Ephemeral key has bounded access; EOA code controls permissions. Custodial / Trusted: Service provider holds raw EOA keys or full RPC control.
UX & Setup Friction High: Requires deploying a new contract wallet and transferring all assets. Low: Single authorization signature upgrades existing EOA; no asset transfer. Low: Traditional Web2 API login, but high risk of absolute asset compromise.
Scope Granularity Extreme: On-chain constraints on token, contract target, value, and timestamp. Extreme: Inherits modular contract rules (time, budget, allowed contracts). Minimal: Only off-chain DB filters. If agent database leaks, full wallet drains occur.
Revocation Velocity Instant (On-chain): Owner calls revokeSessionKey transaction to wipe permit. Instant (On-chain): Owner changes EOA code pointer or increments EOA nonce. Slow (Off-chain): Revoking API key relies on central server. Compromised keys remain valid.
Gas Efficiency Moderate: EntryPoint and bundler routing introduces transaction overhead. Moderate: EntryPoint execution overhead + minor setup signature cost. High: Single EOA transaction; no smart contract parsing overhead.
Legacy EOA Support No: Requires user to interact with a completely separate smart account. Yes: Direct compatibility with MetaMask, Ledger, Trezor, and Rabby. Yes: Directly controls standard legacy EOA private keys.

5. Operational Safeguards & Security Engineering

While cryptographic sandboxes drastically reduce the risk profile of DeFi agents, they also introduce new attack surfaces. High-performance agent integrations must implement the following safeguards.

Key Custody Best Practices

An agent's ephemeral private key is the target of choice for attackers. If compromised, an attacker can drain the user's wallet up to the session's limit.

  • Hardware Security Modules (HSM) / KMS: Never store ephemeral keys in plaintext on-disk, in system environment variables, or in database logs. Utilize AWS KMS, Google Cloud KMS, or HashiCorp Vault.
  • Browser Sandbox Restrictions: For client-side agents, generate keys using the Web Crypto API, storing them in IndexedDB with the extractable parameter set to false. This prevents external scripts (cross-site scripting attacks) from exporting the private key.
  • Strict Expiration Windows: Set narrow expiration ranges (validUntil). Active session windows should not exceed 72 hours for automated trading, and rotation policies should run automatically.

Gas Limits and DoS Mitigation

A malicious or malfunctioning agent can exhaust a user's wallet gas balance through infinite execution loops or by submitting high-fee transactions.

  • Paymaster Sponsorship Caps: Implement an ERC-4337 Paymaster that sponsors gas fees for the agent, but enforce a hard cap on the maximum USD value of gas sponsored per user per day.
  • Max Fee Guardrails: Configure the SessionKeyValidator to check maxFeePerGas and maxPriorityFeePerGas on incoming UserOperations. If the agent attempts to submit a transaction with excessive gas fees (e.g., during market volatility or MEV events), the validator must reject the operation.
  • Sequential Revert Circuit Breaker: Agents must stop execution if three consecutive transactions fail. This prevents infinite loops from draining the user's gas deposits in the EntryPoint contract.

[!WARNING] If a session key is revoked, the transaction should be sent with a high priority fee. If the agent detects the revocation transaction, it might attempt to frontrun the revocation by submitting a high-fee transaction to exhaust the spending limit before the revocation block is finalized.


6. Technical FAQ

Does EIP-7702 require a user to migrate their assets to a new contract wallet?

No. Unlike standard ERC-4337 implementations, EIP-7702 dynamically adds code to the user's existing EOA address. The user's assets, historical balances, and address remain unchanged. EIP-7702 acts as an in-place upgrade, allowing the EOA to inherit smart contract features (such as modular session key validators) directly.

How does EIP-7702 differ from EIP-3074?

EIP-3074 introduced the AUTH and AUTHCALL opcodes, which allowed a transaction to delegate control to an external "Invoker" contract. However, EIP-3074 did not modify the code of the EOA itself. EIP-7702 replaces EIP-3074 by directly setting the EOA's code pointer to point to a smart contract wallet implementation (e.g., a Safe account). This design aligns EOA delegation with the modular ERC-7579 standard, ensuring long-term compatibility with the broader Account Abstraction ecosystem.

What happens if a session key is compromised? Can an attacker drain the entire smart wallet?

No. If the session key is compromised, the attacker can only execute transactions that adhere to the registered constraints. They cannot transfer arbitrary tokens, target unapproved contracts, or exceed the set spending limit. The damage is bounded to the maximum budget allocated to that specific session key. The wallet owner can instantly invalidate the compromised session key on-chain at any time.

How are cross-chain agentic workflows handled using ERC-4337 session keys?

Cross-chain session key verification utilizes Merkle trees and cross-chain message passing. Instead of registering the session key constraints on every network individually, the user signs a single master Merkle tree containing the session key parameters for all target chains. The agent then includes a Merkle proof in the signature field of the ERC-4337 UserOperation when executing on a specific network. The Session Key Validator on that chain verifies the proof against the Merkle root, allowing cross-chain execution with a single initial user signature.


Editorial Transparency Notice

This technical brief is maintained by the FarmDash Smart Contract Architecture group. Code implementations presented are illustrative and optimized for educational clarity. Prior to deploying modular validators or EIP-7702 code pointers on mainnet networks, developers must undergo formal security audits and utilize simulator-first sandboxes (such as the FarmDash Execution Simulator) to verify constraint enforcement under volatile network conditions.

Sources and Technical Specifications:

READY TO HIT THE TRAIL?

Connect your wagon to the FarmDash terminal and track your Pioneer Pace across 81 live protocols.

OPEN TERMINAL