> ## Documentation Index
> Fetch the complete documentation index at: https://developers.circle.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Before reading these docs, install the relevant Circle Skill to get pre-loaded context — skills are LLM-optimized and cover architecture decisions, correct flows, and common pitfalls. In Claude Code: run /plugin marketplace add circlefin/skills then /plugin install circle-skills@circle. With Vercel Skills CLI: run npx skills add circlefin/skills. Available skills: use-usdc https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-usdc/SKILL.md (balances, transfers, approvals on EVM and Solana); EURC has no skill — see https://developers.circle.com/stablecoins/what-is-eurc.md; bridge-stablecoin https://github.com/circlefin/skills/blob/master/plugins/circle/skills/bridge-stablecoin/SKILL.md (crosschain transfers via CCTP, USDC, EURC, and third-party assets); use-arc https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-arc/SKILL.md (Arc chain config, contract deployment, USDC bridging — Arc docs: https://docs.arc.io/llms.txt); use-circle-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-circle-wallets/SKILL.md (choose wallet type); use-developer-controlled-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-developer-controlled-wallets/SKILL.md (custodial wallets for payouts and treasury); use-user-controlled-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-user-controlled-wallets/SKILL.md (embedded wallets with Web2 login); use-modular-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-modular-wallets/SKILL.md (smart contract wallets with passkeys and ERC-4337); use-gateway https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-gateway/SKILL.md (unified USDC balance, nanopayments); use-smart-contract-platform https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-smart-contract-platform/SKILL.md (deploy, interact, monitor contracts). Pair any skill with the Circle MCP server for live SDK signatures, contract addresses, and chain IDs: {"mcpServers":{"circle":{"url":"https://api.circle.com/v1/codegen/mcp"}}}.
> Read the product overview first. Each product has an overview explaining when and why to use it. Read it before quickstarts.
> Pick the right wallet type. Developer-controlled (you hold keys), user-controlled (end user holds keys via PIN/social login), or modular (smart contract wallets with passkeys). See https://developers.circle.com/wallets.md.
> Use CCTP V2 for crosschain transfers. CCTP natively transfers USDC and EURC via burn-and-mint, and supports permissionless wrapping of third-party assets via lock-and-mint. V1 is legacy; always use V2 unless V1 is specified or the chain requires it (Noble, Sui, and Aptos still require V1). Migration guide: https://developers.circle.com/cctp/migration-from-v1-to-v2.md.
> Use Bridge Kit for frontend bridging. Wraps CCTP with a simpler SDK for user-facing apps. Use CCTP directly for backend transfers.
> Gas Station vs Paymaster. Gas Station sponsors gas for Circle Wallet transactions. Paymaster lets users pay gas in USDC. Different use cases — read both overviews.
> Gateway Nanopayments for sub-cent payments. Gasless USDC micropayments down to $0.000001 via x402 and batched settlement. For pay-per-request APIs, AI agent payments, streaming.
> Look up USDC addresses per chain. Never hardcode — use https://developers.circle.com/stablecoins/usdc-contract-addresses.md.
> Prefer SDKs over raw API calls. Node.js and Python SDKs handle auth, retries, and errors.
> API key required. Bearer token in Authorization header. Testnet and mainnet use separate keys and may use different base URLs depending on the product.
> Set up webhooks when available. Most operations are async. Webhooks deliver transaction confirmations and state changes.
> When calling list endpoints, paginate using pageSize and pageAfter until no nextPageAfter cursor is returned—stopping at the first page silently misses records.
> Building an AI agent? Start with the Agent Stack—Circle CLI, agent wallets, and nanopayments built for autonomous use cases: https://developers.circle.com/agent-stack.md.

# How-to: Transfer tokens

> Send tokens from a modular wallet, with gas optionally sponsored by Circle's paymaster.

Send tokens from a user's modular wallet. The examples below transfer 1 USDC on
Arc Testnet, with gas sponsored by [Gas Station](/wallets/gas-station).

<Info>
  For a complete working app that transfers tokens, see the [Circle Smart
  Account
  example](https://github.com/circlefin/modularwallets-web-sdk/tree/master/examples/circle-smart-account)
  in the modular wallets web SDK repository.
</Info>

## Before you begin

Before you begin, ensure that you've:

* [Created a modular wallet](/wallets/modular/create-a-modular-wallet) and have
  access to the `smartAccount` and `bundlerClient` from that tutorial.
* Funded the smart account address with USDC on Arc Testnet from
  [faucet.circle.com](https://faucet.circle.com).
* Configured a Gas Station policy in the
  [Circle Console](https://console.circle.com/) if you plan to sponsor gas on
  mainnet. Testnet usage is sponsored automatically.

<Note>
  Before shipping to production, also [set up passkey
  recovery](/wallets/modular/set-up-passkey-recovery) so users can restore
  access if they lose their passkey.
</Note>

## Steps

<Steps>
  <Step title="Send the user operation">
    A user operation packages a contract call, in this case an ERC-20 `transfer`,
    for the bundler to submit through the user's MSCA. Setting `paymaster: true` (or
    `Paymaster.True()` on iOS and Android) sponsors gas through your Gas Station
    policy.

    Testnet usage is sponsored automatically. For mainnet, configure a policy in the
    Circle Console first.

    <CodeGroup>
      ```typescript Web SDK (TypeScript) theme={null}
      import {
        encodeTransfer,
        ContractAddress,
      } from "@circle-fin/modular-wallets-core";
      import { parseUnits } from "viem";

      const recipient = "0x..."; // recipient address

      const { to, data } = encodeTransfer(
        recipient,
        ContractAddress.ArcTestnet_USDC,
        parseUnits("1", 6), // 1 USDC, 6 decimals
      );

      const userOpHash = await bundlerClient.sendUserOperation({
        // calls is an array. Pass multiple entries to batch them atomically.
        calls: [{ to, data }],
        paymaster: true,
      });
      ```

      ```swift iOS SDK (Swift) theme={null}
      import CircleModularWalletsCore

      Task {
          do {
              let recipient = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

              let result = Utils.encodeTransfer(
                  to: recipient,
                  token: ArcTestnetToken.USDC.name,
                  amount: BigInt(1_000_000) // 1 USDC, 6 decimals
              )

              let userOpHash = try await bundlerClient.sendUserOperation(
                  account: smartAccount,
                  calls: [
                      EncodeCallDataArg(
                          to: result.to,
                          value: BigInt(0),
                          data: result.data
                      )
                  ],
                  paymaster: Paymaster.True()
              )
          } catch {
              print(error)
          }
      }
      ```

      ```kotlin Android SDK (Kotlin) theme={null}
      CoroutineScope(Dispatchers.IO).launch {
          try {
              val recipient = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

              val result = encodeTransfer(
                  recipient,
                  Token.ArcTestnet_USDC.name,
                  parseUnits("1", 6), // 1 USDC, 6 decimals
              )

              val userOpHash = bundlerClient.sendUserOperation(
                  context,
                  smartAccount,
                  arrayOf(
                      EncodeCallDataArg(
                          to = result.to,
                          value = BigInteger.ZERO,
                          data = result.data,
                      ),
                  ),
                  paymaster = Paymaster.True(),
              )
          } catch (e: Exception) {
              e.printStackTrace()
          }
      }
      ```

      ```java Android SDK (Java) theme={null}
      try {
          String recipient = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

          EncodeTransferResult result = encodeTransfer(
              recipient,
              Token.ArcTestnet_USDC.name(),
              parseUnits("1", 6) // 1 USDC, 6 decimals
          );

          EncodeCallDataArg transferCall = new EncodeCallDataArg(
              result.getTo(),
              BigInteger.ZERO,
              result.getData()
          );

          CompletableFuture<String> userOpHashFuture = new CompletableFuture<>();
          bundlerClient.sendUserOperation(
              context,
              smartAccount,
              new EncodeCallDataArg[] { transferCall },
              new UserOperationV07(),
              new Paymaster.True(),
              new CustomContinuation<>(userOpHashFuture)
          );
          String userOpHash = userOpHashFuture.join();
      } catch (Exception e) {
          e.printStackTrace();
      }
      ```
    </CodeGroup>

    `sendUserOperation` returns a `userOpHash`, a 66-character hex string that
    identifies the user operation.

    `encodeTransfer` supports the tokens listed in the `ContractAddress` enum,
    including USDC. See the full list in the
    [Web](/sdks/modular/web-sdk#function-encodetransfer),
    [iOS](/sdks/modular/ios-sdk#function-encodetransfer), or
    [Android](/sdks/modular/android-sdk#function-encodetransfer) SDK reference.

    <Note>
      For tokens not listed in `ContractAddress`, or when batching multiple
      contract calls in a single user operation, build `calls` manually. Each
      entry is a `{ to, data }` pair executed by the MSCA. See
      [Batch and parallel user operations](/wallets/modular/batch-and-parallel-user-operations)
      for the pattern.
    </Note>
  </Step>

  <Step title="Wait for the receipt">
    Poll for the receipt to confirm the transfer landed onchain.

    <CodeGroup>
      ```typescript Web SDK (TypeScript) theme={null}
      const { receipt } = await bundlerClient.waitForUserOperationReceipt({
        hash: userOpHash,
      });

      console.log("Transaction hash:", receipt.transactionHash);
      ```

      ```swift iOS SDK (Swift) theme={null}
              let receipt = try await bundlerClient.waitForUserOperationReceipt(
                  userOpHash: userOpHash
              )

              print("Transaction hash: \(receipt.transactionHash)")
      ```

      ```kotlin Android SDK (Kotlin) theme={null}
              val receipt = bundlerClient.waitForUserOperationReceipt(userOpHash)

              println("Transaction hash: ${receipt.transactionHash}")
      ```

      ```java Android SDK (Java) theme={null}
          CompletableFuture<UserOperationReceipt> receiptFuture = new CompletableFuture<>();
          bundlerClient.waitForUserOperationReceipt(
              userOpHash,
              new CustomContinuation<>(receiptFuture)
          );
          UserOperationReceipt receipt = receiptFuture.join();

          System.out.println("Transaction hash: " + receipt.getTransactionHash());
      ```
    </CodeGroup>

    The receipt contains `transactionHash`, the onchain transaction hash. Look it up
    on the block explorer to confirm the transfer landed.

    <Note>
      `waitForUserOperationReceipt` polls locally. To be notified of transfer
      activity on the wallet asynchronously, including inbound transfers from
      external senders, [set up a webhook
      endpoint](/api-reference/webhook-endpoints).
    </Note>
  </Step>
</Steps>

You've now transferred 1 USDC on behalf of your user, with gas paid by your
paymaster policy.
