> ## 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: Create and fund a Solana associated token account (ATA)

> Create a Solana ATA for an owner wallet and pay the creation rent from a payer wallet

On Solana, SPL token transfers require the recipient to have an Associated Token
Account (ATA) for that token. Gas Station does not pay for ATA creation unless
you use [Solana ATA sponsorship](/wallets/gas-station/solana-ata-sponsorship).
Without sponsorship, you must create the ATA and pay its rent before a transfer
can succeed.

This quickstart walks you through a server-side script that creates a USDC ATA.
A payer wallet pays the one-time SOL rent, and an owner wallet owns the ATA and
receives USDC.

<Note>
  All transactions in this guide take place on Solana Devnet. No real funds are
  required beyond testnet SOL for rent and fees. You can adapt the code for
  mainnet by setting `CLUSTER` to `'mainnet-beta'` and using mainnet USDC mint
  addresses.
</Note>

## Prerequisites

Before you begin, ensure that you've:

* Installed [Node.js v18+](https://nodejs.org/).
* Created a payer wallet and obtained its keypair (private key).
* Funded the payer wallet with at least \~0.00204 SOL on Solana Devnet to cover
  ATA rent-exempt minimum and transaction fees:
  * Use the [Solana faucet](https://faucet.solana.com/) to obtain testnet SOL.
* Obtained the owner wallet's public key (base58 address). The owner can be a
  wallet you create or any recipient's address.

## Step 1: Set up the project

This step sets up your project environment and installs the required
dependencies.

### 1.1. Create a new project

Create a new directory and initialize a new Node.js project with default
settings:

```shell Shell theme={null}
mkdir create-usdc-ata-solana
cd create-usdc-ata-solana
npm init -y
npm pkg set type=module
```

### 1.2. Install dependencies

Install the required dependencies for Solana and SPL token interactions, and set
the start script:

```shell Shell theme={null}
npm install @solana/web3.js @solana/spl-token
npm pkg set scripts.start="node --env-file=.env index.ts"
npm install --save-dev typescript @types/node
```

### 1.3. 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.4. Configure environment variables

Create a `.env` file with `PAYER_PRIVATE_KEY` (payer keypair as a JSON array)
and `OWNER_PUBLIC_KEY` (base58 address of the wallet that will own the ATA):

```shell Shell theme={null}
echo "PAYER_PRIVATE_KEY=[1,2,3,...]" > .env
echo "OWNER_PUBLIC_KEY=YourOwnerBase58Address" >> .env
```

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

The `PAYER_PRIVATE_KEY` should be a JSON array of bytes representing your
private key. You can export this from most Solana wallets.

<Accordion title="Converting Base58 private key to JSON array">
  Some wallets export Solana private keys as Base58 encoded strings. If you have a
  Base58 encoded private key, install `bs58`, save the following code as
  `convert-key.ts`, and run it to convert it to a JSON array:

  ```shell Shell theme={null}
  npm install bs58
  node convert-key.ts
  ```

  ```typescript TypeScript theme={null}
  import bs58 from "bs58";

  const privateKeyBase58: string = "YOUR_BASE58_PRIVATE_KEY";

  try {
    // Decode the Base58 string to a Uint8Array
    const privateKeyBytes: Uint8Array = bs58.decode(privateKeyBase58);

    // Convert the Uint8Array to a JSON array string
    const privateKeyJsonString: string = JSON.stringify(
      Array.from(privateKeyBytes),
    );

    console.log("JSON Array:", privateKeyJsonString);
  } catch (error) {
    console.error(
      "Error converting key. Check if the Base58 key is valid.",
      error,
    );
  }
  ```
</Accordion>

## Step 2: Create the script

This step creates the complete script. It adds imports and configuration,
implements ATA creation, and runs the script.

### 2.1. Import dependencies

Create an `index.ts` file:

```shell Shell theme={null}
touch index.ts
```

Then, add the imports and configuration constants:

```typescript index.ts theme={null}
import {
  Connection,
  Keypair,
  PublicKey,
  Transaction,
  sendAndConfirmTransaction,
  clusterApiUrl,
} from "@solana/web3.js";
import {
  createAssociatedTokenAccountIdempotentInstruction,
  getAssociatedTokenAddressSync,
} from "@solana/spl-token";

const CLUSTER: "mainnet-beta" | "devnet" = "devnet";

const USDC_MINT = {
  "mainnet-beta": new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
  devnet: new PublicKey("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"),
} as const;
```

### 2.2. Add the ATA creation logic

Add the following code to `index.ts`. This code defines the `createUSDCata`
function and the main function.

<Accordion icon="circle-info" title="How this script works">
  The `createUSDCata` function:

  * Derives the ATA address.
  * Builds the idempotent ATA instruction.
  * Sends the transaction.

  The `main` function:

  * Loads the payer keypair and owner address from `.env`.
  * Initializes the Solana connection.
  * Calls `createUSDCata`.
  * Prints the final ATA address.

  The payer covers the ATA rent-exempt amount and transaction fees.
</Accordion>

```typescript index.ts theme={null}
async function createUSDCata(
  connection: Connection,
  payer: Keypair,
  owner: PublicKey,
): Promise<string> {
  // Select the correct USDC mint for the configured cluster.
  const mint = USDC_MINT[CLUSTER];

  // Derive the owner's Associated Token Account (ATA) for USDC.
  const ata = getAssociatedTokenAddressSync(mint, owner);

  // Create an idempotent ATA instruction.
  // If the ATA already exists, this instruction is a no-op.
  const ix = createAssociatedTokenAccountIdempotentInstruction(
    payer.publicKey,
    ata,
    owner,
    mint,
  );

  // Build and send the transaction. The payer signs and pays ATA rent plus fees.
  const tx = new Transaction().add(ix);
  await sendAndConfirmTransaction(connection, tx, [payer], {
    commitment: "confirmed",
  });

  // Print the ATA and a Solana Explorer link for verification.
  console.log("ATA created:", ata.toBase58());
  const explorerCluster =
    CLUSTER === "mainnet-beta" ? "" : `?cluster=${CLUSTER}`;
  console.log(
    `Explorer: https://explorer.solana.com/address/${ata.toBase58()}${explorerCluster}`,
  );

  return ata.toBase58();
}

async function main(): Promise<void> {
  const payerRaw = process.env.PAYER_PRIVATE_KEY;
  const ownerRaw = process.env.OWNER_PUBLIC_KEY;

  // Validate required environment variables.
  if (!payerRaw || !ownerRaw) {
    throw new Error(
      "Set PAYER_PRIVATE_KEY and OWNER_PUBLIC_KEY in .env (see Step 1.4)",
    );
  }

  // Parse keys and create an RPC connection.
  const payer = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(payerRaw)));
  const owner = new PublicKey(ownerRaw);
  const connection = new Connection(clusterApiUrl(CLUSTER), "confirmed");

  // Create the ATA and print the final address.
  const ataAddress = await createUSDCata(connection, payer, owner);
  console.log("Ready to receive USDC at:", ataAddress);
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
```

### 2.3. Run the script

Run the script with the following command:

```shell Shell theme={null}
npm start
```

You should see output similar to:

```text theme={null}
ATA created: DC85yuMEnGDTLpubqUC53BgmMeMjVvoqQyqopekUXffz
Explorer: https://explorer.solana.com/address/DC85yuMEnGDTLpubqUC53BgmMeMjVvoqQyqopekUXffz?cluster=devnet
Ready to receive USDC at: DC85yuMEnGDTLpubqUC53BgmMeMjVvoqQyqopekUXffz
```

Open the Solana Explorer link to verify the ATA onchain. Run the script again to
confirm idempotent behavior: the transaction still succeeds and the ATA address
is unchanged.
