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

# Quickstart: Transfer USDC on EVM chains

> Transfer USDC between wallets on EVM-compatible blockchains using Viem and Node.js

Send 1 [USDC](/stablecoins/what-is-usdc) to a recipient address on a supported
EVM testnet using [Viem](https://viem.sh/) and Node.js. No Circle account
required.

## Prerequisites

Before you begin, ensure that you've:

* Installed [Node.js v22.6+](https://nodejs.org/)
* Created two EVM testnet wallet addresses: one to send from and one to receive
* Funded the source wallet with testnet USDC from the
  [Circle Faucet](https://faucet.circle.com/)
* Funded the source wallet with testnet native tokens for gas fees (such as ETH
  on Ethereum Sepolia)

## Step 1. Set up the project

### 1.1. Create the project and install dependencies

Create a new directory and install the required dependencies:

```bash theme={null}
mkdir transfer-usdc-evm
cd transfer-usdc-evm
npm init -y

npm pkg set type=module
npm pkg set scripts.start="node --experimental-strip-types --env-file=.env index.ts"

npm install viem
npm install --save-dev typescript @types/node
```

### 1.2. Configure TypeScript (optional)

<Tip>
  This step is optional. It helps prevent missing types in your IDE or editor.
</Tip>

Create a `tsconfig.json` file:

```shell theme={null}
npx tsc --init
```

Then, update the `tsconfig.json` file:

```shell theme={null}
cat <<'EOF' > tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "types": ["node"]
  }
}
EOF
```

### 1.3. Set environment variables

Create a `.env` file in the project directory. Replace `{YOUR_PRIVATE_KEY}` with
the private key from your sending wallet and `{YOUR_RECIPIENT_ADDRESS}` with the
address you want to receive the USDC.

<Info>
  If you use MetaMask, follow their guide for how to [find and export your
  private
  key](https://support.metamask.io/configure/accounts/how-to-export-an-accounts-private-key/).
</Info>

```text theme={null}
PRIVATE_KEY={YOUR_PRIVATE_KEY}
RECIPIENT_ADDRESS={YOUR_RECIPIENT_ADDRESS}
```

* `PRIVATE_KEY`: the private key for the EVM wallet sending the transfer.
* `RECIPIENT_ADDRESS`: the EVM wallet address that will receive the USDC.

<Tip>
  Open `.env` in your editor rather than writing values with shell commands, and
  add `.env` to your `.gitignore`. This prevents credentials from leaking into
  your shell history or version control.
</Tip>

The `npm run start` command loads variables from `.env` using Node.js native
env-file support.

<Warning>
  This example uses one or more private keys for local testing. In production,
  use a secure key management solution and never expose or share private keys.
</Warning>

## Step 2. Create the transfer script

The script derives supported chains from Viem, prompts you to choose one at
runtime, and then runs the same transfer flow for that chain.

### 2.1. Create the script file

```bash theme={null}
touch index.ts
```

### 2.2. Add the script

In `index.ts`, add the following script. It derives supported chains from the
USDC token definition and chain definitions in Viem, so the transfer flow stays
shared across EVM testnets. The script uses the client-attached ERC-20 token
actions and the USDC token definition from `viem/tokens`.

```typescript TypeScript expandable theme={null}
import {
  type Chain,
  createClient,
  http,
  publicActions,
  walletActions,
} from "viem";
import * as chains from "viem/chains";
import { filterChains, isAddress, isHex } from "viem/utils";
import { privateKeyToAccount } from "viem/accounts";
import { usdc } from "viem/tokens";

import * as process from "node:process";
import * as readline from "node:readline/promises";

// 1. Ensure environment variables are set up
if (!isHex(process.env.PRIVATE_KEY) || process.env.PRIVATE_KEY.length !== 66)
  throw new Error("Set PRIVATE_KEY to a 0x-prefixed 32-byte hex string");
if (!process.env.RECIPIENT_ADDRESS || !isAddress(process.env.RECIPIENT_ADDRESS))
  throw new Error("Set RECIPIENT_ADDRESS to valid EVM address");

// 2. Setup Viem Client with `usdc` token and public/wallet actions
const client = createClient({
  account: privateKeyToAccount(process.env.PRIVATE_KEY),
  chain: await selectChain(),
  tokens: [usdc],
  transport: http(),
})
  .extend(publicActions)
  .extend(walletActions);

try {
  // 3. Read sender token balance before transfer
  const balance = await client.token.getBalance({ token: "usdc" });
  const amount = { formatted: "1" };
  const recipient = process.env.RECIPIENT_ADDRESS;

  console.log("Chain:", client.chain.name);
  console.log("Sender:", client.account.address);
  console.log("Recipient:", recipient);
  console.log("Balance:", balance.formatted, usdc.symbol);

  // 4. Ensure sender has enough balance
  if (Number(amount.formatted) > Number(balance.formatted))
    throw new Error("Insufficient balance");

  // 5. Estimate gas and apply a safety floor
  const estimatedGas = await client.token.transfer.estimateGas({
    amount,
    to: recipient,
    token: "usdc",
  });
  const buffered = estimatedGas + (estimatedGas * 50n) / 100n;
  const floor = 85_000n;

  // 6. Submit USDC transfer
  const hash = await client.token.transfer({
    amount,
    gas: buffered > floor ? buffered : floor,
    to: recipient,
    token: "usdc",
  });

  // 7. Print transaction details
  console.log("Transaction submitted.");
  console.log("Tx hash:", hash);
  if (client.chain.blockExplorers?.default)
    console.log(
      "Explorer:",
      `${client.chain.blockExplorers.default.url}/tx/${hash}`,
    );

  // 8. Wait for transaction confirmation
  const receipt = await client.waitForTransactionReceipt({ hash });
  if (receipt.status !== "success") throw new Error("Transaction reverted");

  console.log("Transfer confirmed!");
} catch (error) {
  // 9. Handle transfer errors
  console.error(
    "Transfer failed:",
    error instanceof Error ? error.message : error,
  );
  process.exit(1);
}

async function selectChain(): Promise<Chain> {
  // Find supported testnet chains for `usdc`
  const supportedChains = filterChains({
    chains,
    sort: "name",
    testnet: true,
    token: usdc,
  });
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
  try {
    // Prompt user to choose a chain
    const answer = await rl.question(
      `Select a chain for your ${usdc.symbol} transfer:\n${supportedChains
        .map((chain, index) => `${index + 1}. ${chain.name}`)
        .join("\n")}\n\nEnter a number: `,
    );
    // Validate selected chain
    const selected = supportedChains[Number.parseInt(answer, 10) - 1];
    if (!selected) throw new Error("Select a valid chain");
    return selected;
  } finally {
    rl.close();
  }
}
```

## Step 3. Run the script

```bash theme={null}
npm start
```

If you see an `ExperimentalWarning` about type stripping, you can safely ignore
it.

You'll see output similar to the following:

```text theme={null}
Select a chain for your USDC transfer:
1. Arc Testnet
2. Arbitrum Sepolia
...

Enter a number: 1
Chain: Arc Testnet
Sender: 0x1A2b...7890
Recipient: 0x9F8f...1234
Balance: 250.0 USDC
Transaction submitted.
Tx hash: 0xabc123...def456
Explorer: https://explorer.testnet.arc.io/tx/0xabc123...def456
Transfer confirmed!
```

To verify the transfer, open the URL from the `Explorer:` line in your browser.
This takes you to the testnet's block explorer, where you can view full
transaction details.

## Next steps

* **[Set up developer-controlled wallets](/wallets/dev-controlled/create-your-first-wallet)**:
  Create and manage wallets programmatically for multi-user applications.
* **[Sponsor gas fees](/wallets/gas-station)**: Use Gas Station so your users
  don't need to hold tokens for gas.
* **[Bridge USDC across blockchains](/cctp/quickstarts/transfer-usdc-ethereum-to-arc)**:
  Move USDC between blockchains using CCTP.
* **[Transfer EURC on EVM](/stablecoins/quickstarts/transfer-eurc-evm)**:
  Transfer EURC on EVM blockchains using the same approach.
