EIP-7702 in Practice: Building Smart Account Flows After Pectra
Engineering Team
What EIP-7702 Enables
EIP-7702, activated with Ethereum’s Pectra upgrade in March 2025, introduces a new transaction type that allows any Externally Owned Account (EOA) to set a delegation designator — a pointer to a smart contract whose code the EOA temporarily adopts for the duration of a transaction. This means your existing MetaMask wallet address can execute arbitrary smart contract logic (batch calls, custom validation, gas sponsorship) without deploying a new contract wallet or changing your address.
This is the most significant UX improvement in Ethereum’s history. Users no longer need to choose between the simplicity of an EOA and the power of a smart account.
EIP-7702 vs ERC-4337: When to Use Which
Both EIP-7702 and ERC-4337 enable account abstraction, but they operate at different levels and suit different use cases:
| Feature | EIP-7702 | ERC-4337 |
|---|---|---|
| Level | Protocol (new tx type) | Application (smart contract) |
| Account type | Upgrades existing EOAs | Requires new smart contract wallet |
| Address | Keeps existing EOA address | New address (counterfactual) |
| Persistence | Per-transaction delegation | Permanent smart contract |
| Bundler required | No (standard tx flow) | Yes (separate mempool) |
| Gas overhead | ~20,000 gas for delegation | ~42,000 gas for UserOp validation |
| Supported wallets | MetaMask, Coinbase Wallet, Rainbow | Specialized (Safe, ZeroDev, Biconomy) |
| Best for | Upgrading existing users | New users, complex account logic |
Decision Framework
Use EIP-7702 when:
- Your users already have EOA wallets (MetaMask, Coinbase Wallet)
- You need batch transactions or gas sponsorship
- You want minimal integration complexity
- The smart account logic is per-transaction (not permanent)
Use ERC-4337 when:
- You are building a new wallet or onboarding new users
- You need persistent account logic (permanent multisig, complex recovery)
- You want the account to be a smart contract by default
- You need advanced features like account modules, plugins, or guardians
Use both together when:
- You want ERC-4337 smart accounts that can also process EIP-7702 delegations from EOAs (for backward compatibility)
Code Examples
Batch Transactions
The most common EIP-7702 use case: execute multiple operations in a single transaction. No more “approve then swap” two-step flows.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
/// @title BatchExecutor — EIP-7702 delegation target for batch calls
/// @notice EOAs delegate to this contract to execute multiple calls atomically
contract BatchExecutor {
struct Call {
address target;
uint256 value;
bytes data;
}
/// @notice Execute a batch of calls from the delegating EOA
/// @dev When called via EIP-7702, msg.sender is the EOA itself
/// @param calls Array of calls to execute
function executeBatch(Call[] calldata calls) external payable {
for (uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory result) = calls[i].target.call{
value: calls[i].value
}(calls[i].data);
if (!success) {
// Bubble up the revert reason
assembly {
revert(add(result, 32), mload(result))
}
}
}
}
}
Client-side usage with ethers.js v6:
import { ethers } from "ethers";
// EIP-7702 transaction type (type: 4)
const batchTx = {
type: 4,
to: userAddress, // EOA delegates to itself
authorizationList: [{
chainId: 1,
address: BATCH_EXECUTOR_ADDRESS, // delegation target
nonce: await provider.getTransactionCount(userAddress),
// Signed by the EOA owner:
yParity: 0, r: "0x...", s: "0x..."
}],
data: batchExecutorInterface.encodeFunctionData("executeBatch", [[
{
target: USDC_ADDRESS,
value: 0,
data: usdcInterface.encodeFunctionData("approve", [
UNISWAP_ROUTER, ethers.parseUnits("1000", 6)
])
},
{
target: UNISWAP_ROUTER,
value: 0,
data: routerInterface.encodeFunctionData("exactInputSingle", [{
tokenIn: USDC_ADDRESS,
tokenOut: WETH_ADDRESS,
fee: 3000,
recipient: userAddress,
amountIn: ethers.parseUnits("1000", 6),
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
}])
}
]])
};
const tx = await signer.sendTransaction(batchTx);
Gas Sponsorship
With EIP-7702, a paymaster can sponsor gas for any EOA transaction by submitting the transaction on the user’s behalf while the user’s authorization list delegates execution:
/// @title SponsoredExecutor — gas sponsorship via EIP-7702
contract SponsoredExecutor {
mapping(address => uint256) public nonces;
/// @notice Execute a call on behalf of a user, gas paid by msg.sender
/// @param user The EOA that signed the authorization
/// @param target The contract to call
/// @param data The calldata
/// @param userNonce Replay protection nonce
/// @param deadline Expiration timestamp
/// @param signature User's EIP-712 signature over the above params
function sponsoredExecute(
address user,
address target,
bytes calldata data,
uint256 userNonce,
uint256 deadline,
bytes calldata signature
) external {
require(block.timestamp <= deadline, "Expired");
require(nonces[user] == userNonce, "Invalid nonce");
nonces[user]++;
// Verify user's signature (EIP-712)
bytes32 digest = _hashTypedData(user, target, data, userNonce, deadline);
address signer = ECDSA.recover(digest, signature);
require(signer == user, "Invalid signature");
// Execute the call as the user (via delegatecall in EIP-7702 context)
(bool success, bytes memory result) = target.call(data);
require(success, "Call failed");
}
}
The paymaster submits the type-4 transaction, paying gas. The user signs only an EIP-712 message approving the specific action — no ETH needed in their wallet.
Social Recovery
EIP-7702 enables social recovery patterns without requiring a permanent smart contract wallet:
/// @title RecoveryModule — social recovery via EIP-7702
contract RecoveryModule {
struct RecoveryConfig {
address[] guardians;
uint256 threshold;
uint256 delay;
}
mapping(address => RecoveryConfig) public configs;
mapping(bytes32 => uint256) public recoveryTimestamps;
mapping(bytes32 => uint256) public approvalCounts;
mapping(bytes32 => mapping(address => bool)) public hasApproved;
/// @notice Register recovery guardians (called by the EOA via EIP-7702)
function setupRecovery(
address[] calldata guardians,
uint256 threshold,
uint256 delay
) external {
require(guardians.length >= threshold, "Invalid threshold");
require(threshold >= 2, "Min 2 guardians");
configs[msg.sender] = RecoveryConfig(guardians, threshold, delay);
}
/// @notice Guardian approves a recovery request
function approveRecovery(
address account,
address newOwner
) external {
RecoveryConfig memory config = configs[account];
require(_isGuardian(config, msg.sender), "Not a guardian");
bytes32 recoveryId = keccak256(abi.encode(account, newOwner));
require(!hasApproved[recoveryId][msg.sender], "Already approved");
hasApproved[recoveryId][msg.sender] = true;
approvalCounts[recoveryId]++;
if (approvalCounts[recoveryId] >= config.threshold) {
recoveryTimestamps[recoveryId] = block.timestamp + config.delay;
}
}
/// @notice Execute recovery after delay period
function executeRecovery(
address account,
address newOwner
) external {
bytes32 recoveryId = keccak256(abi.encode(account, newOwner));
uint256 timestamp = recoveryTimestamps[recoveryId];
require(timestamp > 0 && block.timestamp >= timestamp, "Not ready");
// Transfer control by updating the account's delegation target
// to a contract controlled by newOwner
// Implementation depends on the specific wallet architecture
}
}
Wallet Integration Status
As of March 2026, EIP-7702 support has rolled out across major wallets:
MetaMask
MetaMask added EIP-7702 support in version 12.4 (September 2025). Users see a “Smart Transaction” badge when a dApp requests a type-4 transaction. The wallet shows:
- The delegation target contract (with verified source on Etherscan)
- Each call in the batch with decoded parameters
- Estimated gas savings vs separate transactions
- A warning if the delegation target is unverified
Coinbase Wallet
Coinbase Wallet has been the most aggressive adopter, integrating EIP-7702 into its “Smart Wallet” feature (launched November 2025). Key features:
- Auto-batching — the wallet automatically batches approve+swap sequences
- Gas sponsorship — Coinbase sponsors gas for transactions under $1 in fee
- One-click DeFi — pre-built delegation targets for common DeFi operations
Rainbow, Rabby, and Others
Rainbow added support in January 2026. Rabby supports type-4 transactions but lacks the batch visualization UI. Frame (the desktop wallet) was among the first to support EIP-7702, shipping support in April 2025.
Migration Guide: From EOA to Smart Accounts
For dApp developers, here is the recommended migration path to support EIP-7702:
Step 1: Deploy Delegation Targets
Create and audit the smart contracts your users will delegate to. Common patterns:
BatchExecutor — multi-call batching
SessionKeyManager — time-limited permissions
SpendingLimiter — daily/weekly spending caps
RecoveryModule — social recovery setup
Step 2: Update Your Frontend
Detect whether the connected wallet supports EIP-7702:
async function supportsEIP7702(provider: ethers.Provider): Promise<boolean> {
try {
// Check if the node supports type-4 transactions
const capabilities = await provider.send(
"wallet_getCapabilities", []
);
return capabilities?.atomicBatch?.supported === true;
} catch {
return false;
}
}
Step 3: Implement Graceful Degradation
Not all users will have EIP-7702-capable wallets. Your dApp should detect capabilities and fall back:
async function executeSwap(tokenIn, tokenOut, amount) {
if (await supportsEIP7702(provider)) {
// Single transaction: approve + swap
return executeBatchSwap(tokenIn, tokenOut, amount);
} else {
// Legacy: two separate transactions
await approve(tokenIn, ROUTER, amount);
return swap(tokenIn, tokenOut, amount);
}
}
Step 4: Update Transaction Tracking
Batch transactions contain multiple operations in a single tx hash. Update your transaction history UI to parse and display each sub-operation:
interface BatchOperation {
target: string;
method: string;
params: Record<string, unknown>;
status: "success" | "reverted";
}
// Parse batch operations from transaction receipt
function parseBatchOps(receipt: TransactionReceipt): BatchOperation[] {
// Decode logs and internal calls from the receipt
// ...
}
Security Considerations
EIP-7702 introduces new attack surfaces that developers must understand:
Delegation Target Verification
The delegation target contract has full control over the EOA’s assets during the transaction. A malicious delegation target can:
- Transfer all ETH and tokens from the EOA
- Approve unlimited token allowances to attacker addresses
- Interact with DeFi protocols to drain positions
Mitigation: Only delegate to audited, verified contracts. Wallets should maintain an allowlist of trusted delegation targets.
Replay Protection
EIP-7702 authorization lists include a nonce to prevent replay attacks. However, developers must ensure:
- The authorization nonce matches the EOA’s current nonce
- Each authorization is used exactly once
- Cross-chain replay is prevented by including the chain ID
Phishing via Delegation
Attackers may trick users into signing authorization lists that delegate to malicious contracts. This is similar to the existing “approve” phishing problem but potentially more dangerous because delegation grants broader access.
Mitigation: Wallets must clearly display what code the EOA is delegating to, with source verification and risk scoring.
Reentrancy in Batch Execution
Batch executors that use call in a loop are vulnerable to reentrancy if one of the batch targets calls back into the executor. Use reentrancy guards or the checks-effects-interactions pattern:
// Use a reentrancy guard on the batch executor
bool private locked;
modifier noReentrant() {
require(!locked, "Reentrant");
locked = true;
_;
locked = false;
}
function executeBatch(Call[] calldata calls) external payable noReentrant {
// ...
}
Storage Collisions
When an EOA delegates to a contract, the contract’s code executes in the context of the EOA’s storage. If two different delegation targets use the same storage slots for different purposes, data corruption can occur.
Mitigation: Use EIP-7201 (namespaced storage) in all delegation targets:
// EIP-7201 namespaced storage
bytes32 constant STORAGE_SLOT = keccak256(
abi.encode(uint256(keccak256("batch.executor.storage")) - 1)
) & ~bytes32(uint256(0xff));
Frequently Asked Questions
Does EIP-7702 change my wallet address?
No. Your EOA address stays the same. EIP-7702 temporarily gives your address smart contract capabilities for the duration of a single transaction. After the transaction completes, your address returns to being a standard EOA.
Can I use EIP-7702 on L2s?
Yes. All major L2s (Arbitrum, Base, Optimism, zkSync) have adopted EIP-7702 as part of their Pectra-equivalent upgrades. The transaction format and delegation mechanics are identical.
What happens if the delegation target has a bug?
The damage is limited to the single transaction. Unlike a permanent smart contract wallet, EIP-7702 delegation expires after the transaction completes. However, within that transaction, the delegation target has full access to the EOA’s assets — a bug could drain the account.
Is EIP-7702 compatible with hardware wallets?
Yes. Hardware wallets (Ledger, Trezor) can sign type-4 transactions, including the authorization list. Ledger added support in firmware version 2.3.0 (December 2025). The user confirms the delegation target on the hardware device screen.
How much gas does EIP-7702 save?
For a typical approve+swap flow, EIP-7702 saves approximately 40-50% on gas by eliminating the second transaction overhead (21,000 base gas) and enabling more efficient calldata encoding. For complex multi-step DeFi operations (e.g., zap into a vault), savings can reach 60-70%.