> ## 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

Smart accounts ([ERC-4337](https://eips.ethereum.org/EIPS/eip-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:

<CardGroup cols={3}>
  <Card title="Batch transactions" icon="layer-group" href="#batch-calls">
    Execute multiple calls in a single user operation
  </Card>

  <Card title="Gas sponsorship" icon="gas-pump" href="#gas-sponsorship">
    Optional paymasters for gasless UX
  </Card>

  <Card title="Multi-chain support" icon="network-wired" href="#supported-networks">
    Deploy on 8 mainnets and 2 testnets across EVM chains
  </Card>
</CardGroup>

<Note>
  For keeping the same address, [EIP-7702](/wallets/using-wallets/eip-7702) upgrades an existing EOA with the same smart account capabilities without creating a new contract address.
</Note>

## Create a smart account

<Tabs>
  <Tab title="React">
    Configure `createOnLogin: "smart"` in your provider so new users get a smart account automatically on sign-in.

    ```tsx theme={null}
    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>;
    }
    ```
  </Tab>

  <Tab title="Node (TypeScript)">
    A smart account requires an EOA as its owner. The contract is not deployed until the first user operation is submitted.

    ```typescript theme={null}
    const owner = await cdp.evm.createAccount();
    const smartAccount = await cdp.evm.createSmartAccount({ owner });
    console.log("Smart Account:", smartAccount.address);
    ```
  </Tab>

  <Tab title="Python">
    A smart account requires an EOA as its owner. The contract is not deployed until the first user operation is submitted.

    ```python theme={null}
    owner = await cdp.evm.create_account()
    smart_account = await cdp.evm.create_smart_account(owner)
    print(f"Smart Account: {smart_account.address}")
    ```
  </Tab>
</Tabs>

## Send a user operation

<Note>
  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.
</Note>

<Tabs>
  <Tab title="React">
    `useSendUserOperation` tracks `status`, `data`, and `error` through on-chain confirmation.

    ```tsx theme={null}
    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>
      );
    }
    ```
  </Tab>

  <Tab title="Node (TypeScript)">
    ```typescript theme={null}
    import { parseEther } from "viem";

    const result = await cdp.evm.sendUserOperation({
      smartAccount,
      network: "base-sepolia",
      calls: [{ to: "0x000...000", value: parseEther("0"), data: "0x" }],
    });

    const userOperation = await cdp.evm.waitForUserOperation({
      smartAccountAddress: smartAccount.address,
      userOpHash: result.userOpHash,
    });

    if (userOperation.status === "complete") {
      console.log("Tx:", userOperation.transactionHash);
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from cdp.evm_call_types import EncodedCall

    user_operation = await cdp.evm.send_user_operation(
        smart_account=smart_account,
        calls=[EncodedCall(to="0x000...000", data="0x", value=0)],
        network="base-sepolia",
    )

    user_operation = await cdp.evm.wait_for_user_operation(
        smart_account_address=smart_account.address,
        user_op_hash=user_operation.user_op_hash,
    )

    if user_operation.status == "complete":
        print(f"Tx: {user_operation.transaction_hash}")
    ```
  </Tab>
</Tabs>

## 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.

<Tabs>
  <Tab title="React">
    ```tsx theme={null}
    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" },
      ],
    });
    ```
  </Tab>

  <Tab title="Node (TypeScript)">
    ```typescript theme={null}
    import { parseEther } from "viem";

    const destinations = [
      "0xba5f3764f0A714EfaEDC00a5297715Fd75A416B7",
      "0xD84523e4F239190E9553ea59D7e109461752EC3E",
      "0xf1F7Bf05A81dBd5ACBc701c04ce79FbC82fEAD8b",
    ];

    const { userOpHash } = await smartAccount.sendUserOperation({
      network: "base-sepolia",
      calls: destinations.map((to) => ({
        to,
        value: parseEther("0.000001"),
        data: "0x",
      })),
    });

    const result = await smartAccount.waitForUserOperation({ userOpHash });

    if (result.status === "complete") {
      console.log("Tx:", result.transactionHash);
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from cdp.evm_call_types import EncodedCall

    destinations = [
        "0xba5f3764f0A714EfaEDC00a5297715Fd75A416B7",
        "0xD84523e4F239190E9553ea59D7e109461752EC3E",
        "0xf1F7Bf05A81dBd5ACBc701c04ce79FbC82fEAD8b",
    ]

    user_operation = await cdp.evm.send_user_operation(
        smart_account=smart_account,
        calls=[EncodedCall(to=dest, data="0x", value=1000) for dest in destinations],
        network="base-sepolia",
    )

    user_operation = await cdp.evm.wait_for_user_operation(
        smart_account_address=smart_account.address,
        user_op_hash=user_operation.user_op_hash,
    )

    if user_operation.status == "complete":
        print(f"Tx: {user_operation.transaction_hash}")
    ```
  </Tab>
</Tabs>

## Encode contract calls

To interact with contracts, pass `data` using an ABI-encoded payload. This example encodes an ERC-20 `transfer` using `viem`:

```tsx Node (TypeScript) theme={null}
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](https://eips.ethereum.org/EIPS/eip-7677)-compatible paymaster.

<Tabs>
  <Tab title="React">
    React also supports `useCdpPaymaster: true` to use the CDP Paymaster on Base without providing a URL.

    <Warning>
      `useCdpPaymaster` is only supported on Base. You cannot specify both `useCdpPaymaster` and `paymasterUrl`.
    </Warning>

    ```tsx theme={null}
    await sendUserOperation({
      evmSmartAccount: smartAccount,
      network: "base-sepolia",
      calls: [{ to: recipient, value: 10n ** 15n, data: "0x" }],
      useCdpPaymaster: true,
      // or: paymasterUrl: "https://your-paymaster.example.com"
    });
    ```
  </Tab>

  <Tab title="Node (TypeScript)">
    ```typescript theme={null}
    import { parseEther } from "viem";

    const userOperation = await cdp.evm.sendUserOperation({
      smartAccount,
      network: "base-sepolia",
      calls: [
        {
          to: "0x0000000000000000000000000000000000000000",
          value: parseEther("0"),
          data: "0x",
        },
      ],
      paymasterUrl: "https://your-paymaster.example.com",
    });

    const confirmed = await cdp.evm.waitForUserOperation({
      smartAccountAddress: smartAccount.address,
      userOpHash: userOperation.userOpHash,
    });

    if (confirmed.status === "complete") {
      console.log("Tx:", confirmed.transactionHash);
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from cdp.evm_call_types import EncodedCall
    from decimal import Decimal
    from web3 import Web3

    user_operation = await cdp.evm.send_user_operation(
        smart_account=smart_account,
        calls=[
            EncodedCall(
                to="0x0000000000000000000000000000000000000000",
                data="0x",
                value=Web3.to_wei(Decimal("0"), "ether"),
            )
        ],
        network="base-sepolia",
        paymaster_url="https://your-paymaster.example.com",
    )

    confirmed = await cdp.evm.wait_for_user_operation(
        smart_account_address=smart_account.address,
        user_op_hash=user_operation.user_op_hash,
    )

    if confirmed.status == "complete":
        print(f"Tx: {confirmed.transaction_hash}")
    ```
  </Tab>
</Tabs>

## Builder Codes

[Base Builder Codes](https://docs.base.org/base-chain/builder-codes/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](https://base.dev) and generate your suffix:

```typescript theme={null}
import { Attribution } from "ox/erc8021";

const DATA_SUFFIX = Attribution.toDataSuffix({
  codes: ["YOUR-BUILDER-CODE"],
});
```

Then pass it to `sendUserOperation`:

<Tabs>
  <Tab title="React">
    ```tsx theme={null}
    import { useSendUserOperation, useCurrentUser } from "@coinbase/cdp-hooks";

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

  <Tab title="Node (TypeScript)">
    ```typescript theme={null}
    await cdp.evm.sendUserOperation({
      smartAccount,
      network: "base",
      calls: [{ to: "0xYourContract", value: parseEther("0"), data: "0x" }],
      dataSuffix: DATA_SUFFIX,
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    await smart_account.send_user_operation(
        calls=[EncodedCall(to="0xYourContract", value=0, data="0x")],
        network="base",
        data_suffix=DATA_SUFFIX,
    )
    ```
  </Tab>
</Tabs>

## Supported networks

| Network   | Mainnet | Testnet          |
| --------- | ------- | ---------------- |
| 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:

<Tabs>
  <Tab title="Node (TypeScript)">
    ```typescript theme={null}
    const userOp = await cdp.evm.getUserOperation({
      userOpHash: result.userOpHash,
      smartAccount,
    });
    console.log(userOp.receipts);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    user_op = await cdp.evm.get_user_operation(
        smart_account.address,
        user_operation.user_op_hash,
    )
    print(user_op.receipts)
    ```
  </Tab>
</Tabs>
