Skip to main content
EIP-7702 delegation allows you to temporarily upgrade an Externally Owned Account (EOA) with smart contract capabilities. This enables existing EOA accounts to use features like user operations, batch transactions, and spend permissions without deploying a new smart contract account.

What is EIP-7702?

EIP-7702 is an Ethereum improvement proposal that enables EOAs to delegate their execution to a smart contract implementation. This provides:
  • Smart account features for EOAs: Use account abstraction without migration
  • Temporary delegation: The EOA can revoke delegation at any time
  • Backward compatibility: Works with existing wallets and tools
  • User operations: Send transactions via ERC-4337 bundlers
  • Spend permissions: Enable gasless allowance-based spending

Creating an EIP-7702 Delegation

1

Get or create an EOA account

Start with an existing EOA account:
import { CdpClient } from "@coinbase/cdp-sdk";

const cdp = new CdpClient();
const account = await cdp.evm.getOrCreateAccount({ 
  name: "EIP7702-Demo" 
});

console.log(`Account address: ${account.address}`);
2

Ensure the account has ETH for gas

The delegation transaction requires ETH:
// Request testnet ETH
const faucetResult = await cdp.evm.requestFaucet({
  address: account.address,
  network: "base-sepolia",
  token: "eth"
});

// Wait for faucet transaction
await publicClient.waitForTransactionReceipt({
  hash: faucetResult.transactionHash
});
3

Create the delegation

Create the EIP-7702 delegation:
const txHash = await cdp.evm.createEvmEip7702Delegation({
  address: account.address,
  network: "base-sepolia",
  enableSpendPermissions: true // Optional
});

console.log(`Delegation transaction: ${txHash}`);
4

Wait for delegation to be active

Wait for the delegation status to be CURRENT:
const status = await cdp.evm.waitForEvmEip7702DelegationStatus({
  address: account.address,
  network: "base-sepolia"
});

console.log(`Delegation status: ${status.status}`);
console.log(`Smart contract: ${status.smartContractAddress}`);

Using the Delegated Account

Once delegation is active, convert the EOA to a delegated account:
import { toEvmDelegatedAccount } from "@coinbase/cdp-sdk";

// Convert EOA to delegated account
const delegatedAccount = toEvmDelegatedAccount(account);

// Now use smart account features
const userOp = await delegatedAccount.sendUserOperation({
  calls: [
    {
      to: "0x0000000000000000000000000000000000000000",
      value: 0n,
      data: "0x"
    }
  ],
  network: "base-sepolia"
});

console.log(`User operation: ${userOp.userOpHash}`);

Sending User Operations

Delegated accounts can send user operations like smart accounts:
import { encodeFunctionData, erc20Abi } from "viem";

const delegatedAccount = toEvmDelegatedAccount(account);

// Send a token transfer via user operation
const userOp = await delegatedAccount.sendUserOperation({
  calls: [
    {
      to: "0xUSDCAddress...",
      value: 0n,
      data: encodeFunctionData({
        abi: erc20Abi,
        functionName: "transfer",
        args: ["0xRecipient...", 1000000n]
      })
    }
  ],
  network: "base-sepolia",
  paymasterUrl: "https://paymaster.example.com" // Optional
});

// Wait for confirmation
const confirmed = await delegatedAccount.waitForUserOperation(
  userOp.userOpHash
);
console.log(`Transaction hash: ${confirmed.transactionHash}`);

Batch Operations

Execute multiple operations atomically:
const userOp = await delegatedAccount.sendUserOperation({
  calls: [
    {
      to: "0xContract1...",
      value: 0n,
      data: "0x..."
    },
    {
      to: "0xContract2...",
      value: 0n,
      data: "0x..."
    },
    {
      to: "0xContract3...",
      value: 0n,
      data: "0x..."
    }
  ],
  network: "base-sepolia"
});

Spend Permissions

With enableSpendPermissions: true, delegated accounts support spend permissions:
// Create delegation with spend permissions
await cdp.evm.createEvmEip7702Delegation({
  address: account.address,
  network: "base-sepolia",
  enableSpendPermissions: true
});

// Wait for delegation
await cdp.evm.waitForEvmEip7702DelegationStatus({
  address: account.address,
  network: "base-sepolia"
});

// Now the account can create and use spend permissions
const delegatedAccount = toEvmDelegatedAccount(account);

// Create a spend permission
const permission = await cdp.evm.createSpendPermission({
  account: delegatedAccount.address,
  spender: "0xSpenderAddress...",
  token: "usdc",
  allowance: 1000000n, // 1 USDC
  period: 86400, // 1 day
  start: 0,
  end: 281474976710655,
  network: "base-sepolia"
});

Checking Delegation Status

Check the current delegation status:
const status = await cdp.evm.getEvmEip7702DelegationStatus({
  address: account.address,
  network: "base-sepolia"
});

console.log(`Status: ${status.status}`);
// Possible values: "NOT_STARTED", "PENDING", "CURRENT", "REVOKED"

if (status.status === "CURRENT") {
  console.log(`Delegated to: ${status.smartContractAddress}`);
}

Delegation Lifecycle

  1. NOT_STARTED: No delegation has been created
  2. PENDING: Delegation transaction is submitted but not confirmed
  3. CURRENT: Delegation is active and the account has smart account features
  4. REVOKED: Delegation has been revoked (future feature)

Complete Example

import { CdpClient, toEvmDelegatedAccount } from "@coinbase/cdp-sdk";
import { createPublicClient, http } from "viem";
import { baseSepolia } from "viem/chains";

const cdp = new CdpClient();
const publicClient = createPublicClient({
  chain: baseSepolia,
  transport: http()
});

// Step 1: Get or create account
const account = await cdp.evm.getOrCreateAccount({ 
  name: "EIP7702-Example" 
});
console.log(`Account: ${account.address}`);

// Step 2: Ensure account has ETH
const balance = await publicClient.getBalance({ 
  address: account.address 
});
if (balance === 0n) {
  console.log("Requesting ETH from faucet...");
  const faucetTx = await cdp.evm.requestFaucet({
    address: account.address,
    network: "base-sepolia",
    token: "eth"
  });
  await publicClient.waitForTransactionReceipt({ 
    hash: faucetTx.transactionHash 
  });
  console.log("Faucet confirmed!");
}

// Step 3: Create delegation
console.log("Creating EIP-7702 delegation...");
const txHash = await cdp.evm.createEvmEip7702Delegation({
  address: account.address,
  network: "base-sepolia",
  enableSpendPermissions: false
});
console.log(`Delegation transaction: ${txHash}`);

// Step 4: Wait for delegation to be active
console.log("Waiting for delegation to be active...");
const status = await cdp.evm.waitForEvmEip7702DelegationStatus({
  address: account.address,
  network: "base-sepolia"
});
console.log(`Delegation active! (${status.status})`);
console.log(
  `Explorer: https://sepolia.basescan.org/tx/${txHash}`
);

// Step 5: Use the delegated account
console.log("Sending user operation...");
const delegatedAccount = toEvmDelegatedAccount(account);
const userOp = await delegatedAccount.sendUserOperation({
  calls: [
    {
      to: "0x0000000000000000000000000000000000000000",
      value: 0n,
      data: "0x"
    }
  ],
  network: "base-sepolia"
});

console.log(`User operation: ${userOp.userOpHash}`);
console.log(
  `Check status: https://base-sepolia.blockscout.com/op/${userOp.userOpHash}`
);

Supported Networks

EIP-7702 delegation is currently supported on:
  • Base Sepolia: base-sepolia (testnet)
Check the Networks documentation for the latest list of supported networks.

Troubleshooting

Delegation Transaction Failed

Ensure the account has sufficient ETH for gas:
await cdp.evm.requestFaucet({
  address: account.address,
  network: "base-sepolia",
  token: "eth"
});

Delegation Status Stuck at PENDING

Wait for the transaction to be mined. You can check the transaction on a block explorer using the returned txHash.

User Operation Rejected

Ensure delegation is active before sending user operations:
const status = await cdp.evm.getEvmEip7702DelegationStatus({
  address: account.address,
  network: "base-sepolia"
});

if (status.status !== "CURRENT") {
  throw new Error("Delegation not active");
}

Cannot Convert to Delegated Account

Only accounts with active delegations can be converted:
// This will throw if delegation is not active
const delegatedAccount = toEvmDelegatedAccount(account);

Next Steps

Build docs developers (and LLMs) love