Skip to main content

What is the difference between 7702 and 4337?

  • 7702 lets you upgrade an EOA into a smart account at the same address, supporting code delegation.
  • 4337 defines the account abstraction infrastructure (userops, paymasters, bundlers) for smart account logic.
  • They work best together—7702 lets you add 4337 support to any EOA.

Key terms

  • Authorization: Signed message specifying chain, address, and signature to allow code delegation; can be single- or multi-chain. Only the most recent authorization is active.
  • Delegate: The contract code that your EOA points to and executes.
  • Relayer: Entity that submits the transaction and pays gas; can be any account with a private key including a bundler.

Will Base Appchains support 7702?

  • Yes, after 7702 is live on Base mainnet, Appchains will follow.

What address should I use for my Smart Account (4337) Implementation?

  • The Coinbase Smart Wallet (CBSW) implementation address for both Base and Base Sepolia is 0x000100abaad02f1cfC8Bbe32bD5a564817339E72

Does the current version of Paymaster support EIP-7702 transactions?

  • Yes, as long as the EOA is upgraded to support ERC-4337 validation logic (i.e., after the 7702 upgrade) by sending an authorization transaction that designates a valid smart contract implementation for the account.

How do I upgrade my wallet to 7702?

EIP‑7702 lets an EOA temporarily “point” to a delegate contract without deploying code at the EOA. A special 7702 transaction carries an signed authorization that sets this delegation designation. After it lands, calls to the EOA execute the delegate’s code against the EOA’s storage. For smart wallet upgrades, do not delegate directly to a wallet implementation—there’s an initialization front‑running risk. Instead, delegate the EOA to the EIP7702Proxy and, in the same transaction, call setImplementation with validator‑checked initialization. This makes the implementation change and initialization atomic, and uses an external NonceTracker so the EOA’s signature cannot be replayed. The snippet below sends one transaction that: (1) applies the 7702 authorization to the proxy, and (2) atomically sets the CoinbaseSmartWallet implementation and runs initialize. Source of truth for deployment addresses. Learn more:
import { createWalletClient, createPublicClient, http, toHex, keccak256, encodeAbiParameters, encodeFunctionData } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { secp256k1 } from '@noble/curves/secp256k1';

// 1) Fill these from the published deployments:
// Base Sepolia deployments (example)
export const EIP7702PROXY_TEMPLATE_ADDRESS  = '0x7702cb554e6bFb442cb743A7dF23154544a7176C' as const;
export const CBSW_IMPLEMENTATION_ADDRESS    = '0x000100abaad02f1cfC8Bbe32bD5a564817339E72' as const;
export const VALIDATOR_ADDRESS              = '0x79A33f950b90C7d07E66950daedf868BD0cDcF96' as const;
export const NONCE_TRACKER_ADDRESS          = '0xD0Ff13c28679FDd75Bc09c0a430a0089bf8b95a8' as const;
// Standard ERC-1967 implementation slot
export const ERC1967_IMPLEMENTATION_SLOT    = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' as const;

// 2) Setup signer and clients
const eoa = privateKeyToAccount('0xPRIVATEKEY');
const walletClient = createWalletClient({ account: eoa, chain: baseSepolia, transport: http(CDP_RPC_URL) });
const publicClient = createPublicClient({ chain: baseSepolia, transport: http(CDP_RPC_URL) });

// 3) Create EIP-7702 authorization to delegate to the EIP7702Proxy (NOT to the implementation)
const authorization = await walletClient.signAuthorization({
  account: eoa,
  contractAddress: EIP7702PROXY_TEMPLATE_ADDRESS,
  // optionally: executor: eoa.address
});

// 4) Prepare initialization calldata for CoinbaseSmartWallet.initialize
// Minimal example: make the EOA the initial owner. You can also encode passkeys if desired.
const ownerArg = encodeAbiParameters([{ type: 'address' }], [eoa.address]);
const initArgs = encodeFunctionData({
  abi: [{ type: 'function', name: 'initialize', inputs: [{ type: 'bytes[]', name: 'owners' }], outputs: [], stateMutability: 'payable' }],
  functionName: 'initialize',
  args: [[ownerArg]],
});

// 5) Read NonceTracker nonce and current ERC-1967 implementation at your EOA address
const nonce: bigint = await publicClient.readContract({
  address: NONCE_TRACKER_ADDRESS,
  abi: [{ type: 'function', name: 'nonces', inputs: [{ name: 'account', type: 'address' }], outputs: [{ type: 'uint256' }], stateMutability: 'view' }],
  functionName: 'nonces',
  args: [eoa.address],
});
const implSlot = await publicClient.getStorageAt({ address: eoa.address, slot: ERC1967_IMPLEMENTATION_SLOT });
const currentImplementation = (`0x${implSlot.slice(-40)}`) as `0x${string}`;

// 6) Create the EIP7702Proxy.setImplementation hash and sign it (raw secp256k1, no Eth prefix)
const TYPE_STRING =
  'EIP7702ProxyImplementationSet(uint256 chainId,address proxy,uint256 nonce,address currentImplementation,address newImplementation,bytes callData,address validator,uint256 expiry)';
const typeHash = keccak256(toHex(TYPE_STRING));
const callDataHash = keccak256(initArgs);
const expiry = 2n ** 256n - 1n; // or your own expiry
const encodedForHash = encodeAbiParameters(
  [
    { type: 'bytes32' }, // typehash
    { type: 'uint256' }, // chainId
    { type: 'address' }, // proxy
    { type: 'uint256' }, // nonce
    { type: 'address' }, // currentImplementation
    { type: 'address' }, // newImplementation
    { type: 'bytes32' }, // keccak256(callData)
    { type: 'address' }, // validator
    { type: 'uint256' }, // expiry
  ],
  [
    typeHash,
    BigInt(baseSepolia.id),
    EIP7702PROXY_TEMPLATE_ADDRESS,
    nonce,
    currentImplementation,
    CBSW_IMPLEMENTATION_ADDRESS,
    callDataHash,
    VALIDATOR_ADDRESS,
    expiry,
  ],
);
const toSign = keccak256(encodedForHash);

// sign raw 32-byte hash with the EOA private key (no prefix)
const priv = eoa.source instanceof Uint8Array ? eoa.source : Buffer.from(eoa.source.slice(2), 'hex');
const sig = secp256k1.sign(Buffer.from(toSign.slice(2), 'hex'), priv);
const r = `0x${sig.r.toString(16).padStart(64, '0')}`;
const s = `0x${sig.s.toString(16).padStart(64, '0')}`;
const v = 27 + sig.recovery; // 27 or 28
const signature = (`0x${r.slice(2)}${s.slice(2)}${v.toString(16).padStart(2, '0')}`) as `0x${string}`;

// 7) Encode EIP7702Proxy.setImplementation(newImpl, initArgs, validator, expiry, signature, allowCrossChainReplay=false)
const setImplCalldata = encodeFunctionData({
  abi: [{
    type: 'function',
    name: 'setImplementation',
    inputs: [
      { name: 'newImplementation', type: 'address' },
      { name: 'callData', type: 'bytes' },
      { name: 'validator', type: 'address' },
      { name: 'expiry', type: 'uint256' },
      { name: 'signature', type: 'bytes' },
      { name: 'allowCrossChainReplay', type: 'bool' },
    ],
    outputs: [],
    stateMutability: 'payable',
  }],
  functionName: 'setImplementation',
  args: [CBSW_IMPLEMENTATION_ADDRESS, initArgs, VALIDATOR_ADDRESS, expiry, signature, false],
});

// 8) Send one transaction that includes the 7702 authorization and calls setImplementation
const hash = await walletClient.sendTransaction({
  account: eoa,
  to: eoa.address,
  data: setImplCalldata,
  authorizationList: [authorization],
});

How can I tell if a wallet is a smart account or EOA?

Who can be a relayer?

  • Any account with a private key can relay the upgrade transaction.
  • For sponsored (gasless) transactions after upgrade, a relayer may interact with a bundler or paymaster for reimbursement.
  • Bundlers are not required for the initial 7702 tx, but are needed for subsequent ERC-4337 (userop) flows.

How can developers protect their users from 7702 attacks?

  • Use only trusted delegate contracts: Verify that the smart contract implementation you’re asking users to delegate to is legitimate and audited
  • Verify contract addresses on block explorers: Double-check contract addresses on a block explorer (Etherscan/Basescan) before implementing them in your application to ensure they match expected implementations
  • Implement proper validation: Add checks in your application to verify that the delegate contract address matches known safe implementations (e.g., Coinbase Smart Wallet implementation)
  • Educate users: Provide clear information about what the authorization does and which contract they’re delegating to
  • Use established implementations: Prefer well-known, audited smart account implementations rather than custom or unverified contracts