Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cloud.coinbase.com/llms.txt

Use this file to discover all available pages before exploring further.

Smart accounts (ERC-4337) are programmable wallets that enable advanced features like batching multiple transactions together and sponsoring gas fees for your users. Key benefits of CDP Smart Accounts include:

Batch transactions

Execute multiple calls in a single user operation

Gas sponsorship

Optional paymasters for gasless UX

Multi-chain support

Deploy on 8 mainnets and 2 testnets across EVM chains
For keeping the same address, EIP-7702 upgrades an existing EOA with the same smart account capabilities without creating a new contract address.

Create a smart account

Configure createOnLogin: "smart" in your provider so new users get a smart account automatically on sign-in.
import { CDPHooksProvider, useCurrentUser } from "@coinbase/cdp-hooks";

function App() {
  return (
    <CDPHooksProvider
      config={{
        projectId: "your-project-id",
        ethereum: { createOnLogin: "smart" },
      }}
    >
      <YourApp />
    </CDPHooksProvider>
  );
}

function SmartAccountInfo() {
  const { currentUser } = useCurrentUser();
  const smartAccount = currentUser?.evmSmartAccounts?.[0];
  return <p>Smart Account: {smartAccount}</p>;
}

Send a user operation

On Base Sepolia, user operations are subsidized and the smart account does not need to be funded with ETH. On Base mainnet, fund the smart account with ETH before submitting.
useSendUserOperation tracks status, data, and error through on-chain confirmation.
import { useSendUserOperation, useCurrentUser } from "@coinbase/cdp-hooks";

function SendUserOperation() {
  const { sendUserOperation, status, data, error } = useSendUserOperation();
  const { currentUser } = useCurrentUser();

  const handleSend = async () => {
    const smartAccount = currentUser?.evmSmartAccounts?.[0];
    if (!smartAccount) return;

    await sendUserOperation({
      evmSmartAccount: smartAccount,
      network: "base-sepolia",
      calls: [{ to: "0x000...000", value: 0n, data: "0x" }],
    });
  };

  return (
    <div>
      <button onClick={handleSend} disabled={status === "pending"}>
        {status === "pending" ? "Sending..." : "Send"}
      </button>
      {status === "success" && data && <p>Tx: {data.transactionHash}</p>}
      {error && <p>Error: {error.message}</p>}
    </div>
  );
}

Batch calls

Pass multiple entries in calls[] to execute them atomically in a single user operation. Calls run in order and revert together on failure.
await sendUserOperation({
  evmSmartAccount: smartAccount,
  network: "base-sepolia",
  calls: [
    { to: addr1, value: parseEther("0.001"), data: "0x" },
    { to: addr2, value: parseEther("0.001"), data: "0x" },
    { to: addr3, value: parseEther("0.001"), data: "0x" },
  ],
});

Encode contract calls

To interact with contracts, pass data using an ABI-encoded payload. This example encodes an ERC-20 transfer using viem:
Node (TypeScript)
import { encodeFunctionData } from "viem";

const erc20Abi = [
  {
    name: "transfer",
    type: "function",
    stateMutability: "nonpayable",
    inputs: [
      { name: "to", type: "address" },
      { name: "value", type: "uint256" },
    ],
    outputs: [{ name: "", type: "bool" }],
  },
];

const data = encodeFunctionData({
  abi: erc20Abi as const,
  functionName: "transfer",
  args: [recipient, 1_000_000n], // 1 USDC assuming 6 decimals
});

await sendUserOperation({
  evmSmartAccount: smartAccount,
  network: "base-sepolia",
  calls: [
    {
      to: usdcAddress,
      data,
      value: 0n,
    },
  ],
});

Gas sponsorship

Pass a paymasterUrl to cover gas fees for your users with any ERC-7677-compatible paymaster.
React also supports useCdpPaymaster: true to use the CDP Paymaster on Base without providing a URL.
useCdpPaymaster is only supported on Base. You cannot specify both useCdpPaymaster and paymasterUrl.
await sendUserOperation({
  evmSmartAccount: smartAccount,
  network: "base-sepolia",
  calls: [{ to: recipient, value: 10n ** 15n, data: "0x" }],
  useCdpPaymaster: true,
  // or: paymasterUrl: "https://your-paymaster.example.com"
});

Builder Codes

Base Builder Codes attribute onchain activity back to your app for rewards and analytics. Pass dataSuffix on any user operation — no contract changes needed. First, register at base.dev and generate your suffix:
import { Attribution } from "ox/erc8021";

const DATA_SUFFIX = Attribution.toDataSuffix({
  codes: ["YOUR-BUILDER-CODE"],
});
Then pass it to sendUserOperation:
import { useSendUserOperation, useCurrentUser } from "@coinbase/cdp-hooks";

await sendUserOperation({
  evmSmartAccount: smartAccount,
  network: "base",
  calls: [{ to: "0xYourContract", value: 0n, data: "0x" }],
  dataSuffix: DATA_SUFFIX,
});

Supported networks

NetworkMainnetTestnet
Base✓ (Base Sepolia)
Arbitrum
Optimism
Zora
Polygon
BNB Chain
Avalanche
Ethereum✓ (Sepolia)

Debugging

When a user operation reverts, the revert reason is included in its receipts if it can be decoded:
const userOp = await cdp.evm.getUserOperation({
  userOpHash: result.userOpHash,
  smartAccount,
});
console.log(userOp.receipts);