ByAUJay
Session-Based Authentication on Ethereum: Delegation Patterns for EIP-7702
EIP-7702, shipped in Ethereum’s Pectra mainnet upgrade on May 7, 2025, lets any EOA opt into smart-account behaviors by delegating its execution to contract code. This post maps concrete delegation patterns to build safe, wallet-managed “sessions” that reduce prompts, enable sponsorship, and constrain risk—all without migrating user addresses. (blog.ethereum.org)
Summary (1–2 sentences)
Pectra’s EIP-7702 turns EOAs into programmable, policy‑enforced wallets by writing a delegation pointer to the account code. With the right patterns, you can implement time‑boxed, budget‑scoped session keys, sponsor gas, and roll out passkey or multi‑sig auth—while keeping today’s addresses and infrastructure. (eips.ethereum.org)
Who should read this
- Product and engineering leaders planning wallet UX upgrades without user migration
- Platform teams evaluating account abstraction (AA) roadmaps and compliance controls
- Dapps seeking fewer prompts, better conversion, and safer automation
What EIP‑7702 actually changes (in practice)
EIP‑7702 introduces a new type 0x04 “set code” transaction that processes an authorization list of tuples and writes a delegation indicator—bytes 0xef0100 followed by a 20‑byte address—into the EOA’s code. From then on, external and internal calls to that EOA execute the code at the delegate address but in the EOA’s storage/balance context. Key spec points: (eips.ethereum.org)
- Transaction type and constants:
- SET_CODE_TX_TYPE = 0x04
- PER_AUTH_BASE_COST = 12,500 gas
- PER_EMPTY_ACCOUNT_COST = 25,000 gas (eips.ethereum.org)
- Auth tuple format: [chain_id, address, nonce, y_parity, r, s], signed over keccak(MAGIC || rlp([chain_id, address, nonce])). The chain_id may be 0 (cross‑chain authorization), otherwise it must match the current chain. (eips.ethereum.org)
- Nonce semantics: the tuple’s nonce must equal the authority’s current nonce; after processing, the authority nonce increments by 1. Multiple tuples for the same authority: the last valid one wins. (eips.ethereum.org)
- Gas: intrinsic cost follows EIP‑2930 plus PER_EMPTY_ACCOUNT_COST per tuple, with a refund of PER_EMPTY_ACCOUNT_COST − PER_AUTH_BASE_COST when the authority isn’t empty—so a “normal” existing EOA effectively pays ~12.5k gas to (re)point delegation. (eips.ethereum.org)
- Execution: CALL, CALLCODE, DELEGATECALL, STATICCALL follow the delegation; EXTCODESIZE/EXTCODECOPY read the 23‑byte indicator while CODESIZE/CODECOPY see the delegate’s runtime code during delegated execution. This affects code‑hash checks and some anti‑MEV patterns. (eips.ethereum.org)
- Self‑sponsoring: the tx.origin may set and use its own delegated code, enabling users to benefit from 7702 without external relayers. (eips.ethereum.org)
Pectra activation details: mainnet epoch 364032 (May 7, 2025), with 7702 delivered alongside other UX and scaling EIPs. (blog.ethereum.org)
Why sessions now make sense for EOAs
Before Pectra, “session keys” lived mostly in ERC‑4337 smart wallets via plugins/modules and required deploying a separate account. With 7702, an EOA can point to mature 4337 wallet code paths and use the same module ecosystems (e.g., ERC‑7579) for permissions and passkeys—without migrating assets or addresses. Wallets and bundlers already expose 7702‑aware flows via EntryPoint v0.8 and a new eip7702Auth field. (eips.ethereum.org)
Today’s infra snapshot you can rely on:
- EntryPoint v0.8 deployments at a stable address: 0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108. (github.com)
- Bundlers (e.g., Etherspot Skandha, Candide Voltaire) support 7702, treating authorizations as part of UserOperations via eip7702Auth. (etherspot.io)
- Modular session stacks (ERC‑7579) power Smart Sessions from Rhinestone/Biconomy, including reusable policies and validators. (eips.ethereum.org)
Delegation patterns for session-based authentication
Below are patterns we’re implementing with clients. Each pattern uses 7702 to “activate” smart logic for an EOA, then applies modules/policies for time‑boxed, scope‑limited permissions.
Pattern A — Wallet‑managed permanent delegation to a modular smart account
- What you do: Point the EOA to a battle‑tested 4337 account implementation that supports ERC‑7579 modules (validators, executors, hooks). Install a “session manager” module that issues and checks scoped session keys. (eips.ethereum.org)
- When to use: High‑trust wallets; broad feature set (batched calls, sponsorship, passkeys) across many dapps.
- Pros:
- One‑time 7702 pointer (~12.5k gas on existing EOAs). (eips.ethereum.org)
- Reuses EntryPoint v0.8 mempool and tools; no opcode changes. (eips.ethereum.org)
- Gotchas:
- Don’t rely on EXTCODEHASH to detect wallet behavior—it returns the indicator hash, not the delegate code’s hash. Design feature detection with interfaces or off‑chain allowlists. (eips.ethereum.org)
Pattern B — Time‑boxed session delegation to a “policy proxy” delegate
- What you do: Delegate to a minimal PolicyDelegate contract that reads session state from the EOA’s storage (the delegated execution context) and enforces:
- expiry (validUntil),
- per‑function/contract allowlists,
- value and token spend caps,
- optional rate limits.
The delegate validates a session key signature (EIP‑1271 style) and forwards permitted calls.
- When to use: App‑specific sessions (e.g., a trading UI for a day, or a game session).
- Pros: Tight blast radius; simple audits; easy revocation by resetting 7702 to zero address. (eips.ethereum.org)
- Gotchas:
- Remember chain_id: 0 authorizations are cross‑chain replayable; for app‑specific sessions, bind to the current chain. (eips.ethereum.org)
Pattern C — Sponsored sessions via 4337 paymasters
- What you do: Keep 7702 pointing to a 4337‑compatible account; issue session keys; route transactions through a bundler and a paymaster to cover gas in ERC‑20 or by the app. Ensure bundlers enforce ERC‑7562 validation rules to avoid DoS vectors. (eips.ethereum.org)
- When to use: Onboarding, promotions, high‑frequency UX where users lack ETH.
- Pros: Mature infra; fine‑grained sponsor controls.
- Gotchas:
- Bundler policy now restricts how 7702 authorizations appear in UserOps (single 7702 auth per UserOp; sender must match; consistent delegate). Align with bundler expectations. (ercs.ethereum.org)
Pattern D — Passkey‑first sessions (WebAuthn)
- What you do: Use a passkey (P‑256) to sign UserOps or session tokens; the 7702‑delegated account verifies P‑256 in validateUserOp/1271. Combine with short‑lived sessions for a “sign once, act many” feel. Example repos demonstrate P‑256 and session key rotation. (github.com)
- When to use: Consumer apps where biometric UX and credential rotation matter.
- Gotchas:
- Passkeys are non‑exportable; plan multi‑device enrollment and recovery. (developers.flow.com)
Pattern E — Delegation Manager workflows (emerging)
- What you do: Wrap session permissions into standardized “delegations” redeemable via an ERC‑7710 DelegationManager. This aligns wallet‑managed permission requests (ERC‑7715) with on‑chain redemption flows. (eips.ethereum.org)
- When to use: Multi‑app ecosystems needing a single permission vocabulary across wallets and services.
How to implement: from signature to session
1) Point an EOA to a 4337‑compatible account
- One‑time step: have the user sign a 7702 authorization tuple with address = your chosen smart account code. On processing, the client writes 0xef0100||address into the EOA’s code and increments the EOA’s nonce. The delegation persists until cleared. (eips.ethereum.org)
- If you’re using EntryPoint v0.8, your bundler can carry the 7702 tuple in UserOps.
Example: UserOperation with eip7702Auth (EntryPoint v0.8 bundlers)
{ "sender": "0xEoaAddress", "callData": "0x...", "maxFeePerGas": "0x...", "maxPriorityFeePerGas": "0x...", "signature": "0x...", "eip7702Auth": { "chainId": "0x01", "address": "0xDelegateContract", "nonce": "0x000000000000000A", "yParity": "0x01", "r": "0x...", "s": "0x..." } }
Bundlers such as Voltaire (Candide) and Skandha (Etherspot) accept the eip7702Auth field with EntryPoint v0.8. (docs.candide.dev)
EntryPoint v0.8 address reference: 0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108 (mainnet and other supported networks). (github.com)
2) Install a session module (ERC‑7579)
- If your delegate is a modular smart account, add a session manager (e.g., Rhinestone/Biconomy “SmartSession”) and configure policies:
- target contract/function selectors,
- value and token budgets,
- expiries and rate limits. (github.com)
3) Register a session key
- Issue a temporary key (e.g., P‑256) and register its pubkey + constraints in the module; the module enforces these constraints inside validateUserOp or equivalent dispatcher. (docs-devx.biconomy.io)
4) Execute batched flows with sponsorship (optional)
- Use paymasters and 4337 bundlers; ensure ERC‑7562 validation compliance and staking for entities that keep state (factories, paymasters, aggregators) to avoid mempool throttling/bans. (eips.ethereum.org)
Minimal PolicyDelegate example for “wallet‑local” sessions
Below is a minimal delegate contract meant to be the 7702 target. It enforces:
- a single session key,
- an expiry timestamp,
- per‑call target and function selector allowlists,
- ETH and ERC‑20 limits.
Notes:
- Storage is the EOA’s storage during delegated execution—so reads/writes here persist “in” the EOA. (eips.ethereum.org)
- In production, prefer an ERC‑7579 module over a bespoke policy; add EIP‑712 typed session tokens and comprehensive events.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; interface IERC20 { function transferFrom(address a, address b, uint256 v) external returns (bool); } interface IERC1271 { function isValidSignature(bytes32, bytes calldata) external view returns (bytes4); } // Deployed once. 7702 points EOAs here. Storage is the EOA's storage under delegation. contract SessionPolicyDelegate { // Session state in the EOA's storage address public sessionKey; // EOA-scoped uint64 public validUntil; // unix time uint96 public ethBudget; // remaining wei mapping(address => uint256) public erc20Budget; // token -> remaining mapping(address => mapping(bytes4 => bool)) public allowed; // target -> selector -> ok address public admin; // EOA owner sets during init event SessionStarted(address key, uint64 until); event SessionCleared(); event CallExecuted(address to, uint256 value, bytes4 sel); event BudgetUsed(address asset, uint256 amount); modifier onlyAdmin() { require(msg.sender == admin, "not admin"); _; } function init(address _admin) external { require(admin == address(0), "inited"); admin = _admin; } // Register/refresh a session function startSession( address key, uint64 until, uint96 maxEth, address[] calldata tokens, uint256[] calldata maxToken, address[] calldata targets, bytes4[] calldata selectors ) external onlyAdmin { require(until > block.timestamp, "expiry"); sessionKey = key; validUntil = until; ethBudget = maxEth; for (uint256 i; i < tokens.length; i++) erc20Budget[tokens[i]] = maxToken[i]; for (uint256 j; j < targets.length; j++) allowed[targets[j]][selectors[j]] = true; emit SessionStarted(key, until); } function clearSession() external onlyAdmin { sessionKey = address(0); validUntil = 0; ethBudget = 0; emit SessionCleared(); } // Forward a permitted call using a session signature (off-chain signed intent) function forward( address to, uint256 value, bytes calldata data, bytes calldata sessionSig, bytes32 intentHash // EIP-712 domain recommended in production ) external payable returns (bytes memory) { require(block.timestamp <= validUntil, "expired"); require(sessionKey != address(0), "no session"); // Verify the session key via 1271-style contract or EOA ecrecover if (isContract(sessionKey)) { bytes4 magic = IERC1271(sessionKey).isValidSignature(intentHash, sessionSig); require(magic == 0x1626ba7e, "bad 1271"); } else { // For brevity: assume data encodes r,s,v and recover equals sessionKey (bytes32 r, bytes32 s, uint8 v) = abi.decode(sessionSig, (bytes32,bytes32,uint8)); address rec = ecrecover(intentHash, v, r, s); require(rec == sessionKey, "bad EOA sig"); } // Scope checks bytes4 sel = data.length >= 4 ? bytes4(data[0:4]) : bytes4(0); require(allowed[to][sel], "not allowed"); // Budget checks (ETH) if (value > 0) { require(value <= ethBudget, "eth budget"); ethBudget -= uint96(value); emit BudgetUsed(address(0), value); } // Optional ERC-20 budget check if call is transferFrom or transfer if (sel == 0x23b872dd /* transferFrom */ || sel == 0xa9059cbb /* transfer */) { address token = to; (address a, , uint256 amt) = sel == 0x23b872dd ? abi.decode(data[4:], (address,address,uint256)) : (address(0), address(0), abi.decode(data[4:], (address,uint256))._2); require(erc20Budget[token] >= amt, "token budget"); erc20Budget[token] -= amt; emit BudgetUsed(token, amt); } // Execute (bool ok, bytes memory ret) = to.call{value: value}(data); require(ok, "call fail"); emit CallExecuted(to, value, sel); return ret; } function isContract(address a) internal view returns (bool) { uint256 size; assembly { size := extcodesize(a) } return size > 0; } }
How it fits 7702:
- The EOA first sets code to the 0xef0100||address for this PolicyDelegate.
- The admin is the EOA owner; sessions live in the EOA’s storage; clearing the 7702 pointer to zero instantly disables delegation. (eips.ethereum.org)
Production guidance:
- Prefer audited 4337+7579 modules (Rhinestone/Biconomy SmartSession) instead of bespoke delegates for rich policy composition. (github.com)
Security and compliance checklist (what we enforce in reviews)
- Wallet‑controlled authorizations only. The EIP itself warns that apps should not be able to prompt arbitrary 7702 authorizations; wallet UIs must audit/whitelist delegate code. Treat 7702 pointers like upgrades. (eips.ethereum.org)
- Panic revoke: expose a one‑click “revert to EOA” by setting the delegate address to zero (clears code hash to empty per spec). (eips.ethereum.org)
- Cross‑chain risk: avoid chain_id = 0 unless you explicitly intend cross‑chain reuse. Pin sessions to specific chain IDs. (eips.ethereum.org)
- tx.origin invariants: 7702 allows delegated accounts to originate transactions, breaking assumptions like require(msg.sender == tx.origin). Review legacy controls that rely on tx.origin. (eips.ethereum.org)
- Code‑hash checks: EXTCODE* returns the 23‑byte indicator; don’t rely on codehash equality to gate behavior. Use interface detection or explicit registry checks. (eips.ethereum.org)
- Front‑running and initialization: follow spec guidance on initializing delegates and avoiding races (e.g., deploy code templates; never inline per‑user initcode). (eips.ethereum.org)
- Bundler policy: if using 4337, ensure ERC‑7562 validation rules and reputations are followed; stake entities that keep state, avoid mass invalidation. (eips.ethereum.org)
- Passkeys: provision multiple credentials; prompt users to enroll an extra device or hardware key to avoid lockouts. (developers.flow.com)
Gas and performance notes
- The one‑time cost to set/refresh delegation on an existing EOA nets to ~12,500 gas per authorization (protocol charges 25k then refunds the delta for non‑empty accounts). Empty accounts pay 25k. This is orders of magnitude cheaper than deploying a new smart account (~400k–600k in many implementations). (eips.ethereum.org)
- 7702’s design nudges the ecosystem to reuse common code templates (pointer to code) instead of per‑user deployments—keeping state size and auditing surface small. (eips.ethereum.org)
Deployment and rollout plan (6–8 weeks)
- Discovery and threat model
- Map target flows to a session matrix: allowed contracts/selectors, budgets, expiries, rate limits.
- Determine whether to reuse a modular smart account (ERC‑7579) or a minimal PolicyDelegate for app‑specific sessions. (eips.ethereum.org)
- Prototype on Sepolia/Holesky with EntryPoint v0.8
- Use a 7702‑aware bundler and confirm eip7702Auth integration. Validate sponsor flows end‑to‑end with your paymaster. (docs.candide.dev)
- Wallet UI and policy registry
- Whitelist delegate code templates in the wallet, align with “wallet‑controlled authorization” guidance from the EIP. (eips.ethereum.org)
- Observability
- Capture: session issuance/revocation events, validation failures (ERC‑7562 rules), budget burn‑down, sponsor spend. (eips.ethereum.org)
- Security review
- Audit session policies and upgrade paths; simulate revocations under load; test tx.origin‑sensitive contracts in your estate. (eips.ethereum.org)
- Gradual exposure
- Start with self‑sponsored (tx.origin) transactions for power users; then enable sponsored 4337 routes for first‑time users. (eips.ethereum.org)
KPIs we recommend
- Prompt reduction: avg. confirmations per task before vs. after sessions
- Conversion: success rate for multi‑step flows (approve+swap, list+sell)
- Safety: % of actions blocked by policy; mean loss averted via budgets
- Cost: gas/user vs. 4337 migration baselines (set‑code vs. new deploys)
Reference snippets and spec hooks (for implementers)
- Detecting a 7702‑delegated account off‑chain: read code length 23 bytes, prefix 0xef0100, then decode the 20‑byte delegate address. Remember: EXTCODE* reads the indicator; CODESIZE differs during delegated execution. (eips.ethereum.org)
- Clearing delegation: set delegate address to 0x0 in the next 7702 authorization; clients MUST reset the code hash to the empty code. (eips.ethereum.org)
- 4337 bundlers and v0.8 EntryPoint address: 0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108; use eip7702Auth in UserOps. (github.com)
- Session key modules: adopt ERC‑7579 “SmartSession” for interoperable policies across Safe, Kernel, and others. (github.com)
The bottom line
7702 gives you a safe bridge from EOAs to smart‑account UX—no address migrations, no opcode churn. Pair it with modular session policies and 4337 infra, and you’ll ship fewer prompts, better conversion, and guard‑railed automation. The spec bakes in the boundaries (auth tuple, nonce, chain_id, 0xef0100 pointer); your job is to keep authorization flows wallet‑controlled and policies auditable. If you need an experienced partner to design, audit, and roll this out across wallets and dapps, 7Block Labs can help.
Sources and further reading
- EIP‑7702 Set Code for EOAs (spec, gas constants, behavior, security notes). (eips.ethereum.org)
- Pectra mainnet announcement and activation timing (Ethereum Foundation; The Block coverage). (blog.ethereum.org)
- EntryPoint v0.8 address (eth‑infinitism repo; Pimlico chain table). (github.com)
- Bundlers with 7702 support (Candide Voltaire; Etherspot Skandha). (docs.candide.dev)
- ERC‑7579 modular smart accounts; Smart Sessions (Rhinestone/Biconomy). (eips.ethereum.org)
- ERC‑7562 validation rules (mempool safety for AA). (eips.ethereum.org)
- ERC‑7710 Smart Contract Delegation; ERC‑7715 Wallet‑granted permissions; ERC‑7739 readable typed signatures. (eips.ethereum.org)
Like what you're reading? Let's build together.
Get a free 30‑minute consultation with our engineering team.

