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

# 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 to run the TypeScript file with `tsx`:

```shell Shell theme={null}
npm install @solana/web3.js @solana/spl-token tsx
npm pkg set scripts.start="npx tsx --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 with `tsx` to convert it to a JSON array:

  ```shell Shell theme={null}
  npm install bs58
  npx tsx 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.
