> ## 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: Transfer USDC on Stellar

> Send USDC between accounts on Stellar Testnet using the Stellar SDK.

USDC on Stellar is a native asset issued by Circle. Transferring USDC between
Stellar accounts uses a standard payment operation, submitted through
[Horizon](https://developers.stellar.org/docs/data/apis/horizon), Stellar's HTTP
API for submitting transactions. To transfer USDC crosschain between Stellar and
other blockchains, see
[Transfer USDC to and from Stellar](/cctp/quickstarts/transfer-usdc-stellar-arc).

The [`@stellar/stellar-sdk`](https://github.com/stellar/js-stellar-sdk) script
you build in this guide will:

* Create and fund a recipient Stellar Testnet wallet
* Establish a USDC trustline on the recipient account
* Transfer USDC from your existing sender wallet to the recipient

<Note>
  Before a Stellar account can receive USDC, it must establish a trustline for the
  asset. See
  [Set up a USDC trustline on Stellar](/stablecoins/quickstart-setup-usdc-trustline-stellar)
  for more detail. This quickstart handles trustline setup automatically as part
  of the script.
</Note>

## Prerequisites

Before you begin, ensure that you have:

* Installed [Node.js v22+](https://nodejs.org/)
* Set up a terminal and code editor for running commands and editing files
* Created a Stellar Testnet wallet with the secret key (`S...`) available
  * Funded with testnet XLM (for transaction fees) from
    [Stellar Friendbot](https://lab.stellar.org/account/fund)
  * Funded with testnet USDC from the [Circle Faucet](https://faucet.circle.com)

## Contract addresses

You need the following Stellar Testnet USDC issuer address:

* Testnet:
  [`GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5`](https://stellar.expert/explorer/testnet/asset/USDC-GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5)

## Step 1: Set up the project

### 1.1. Create the project and install dependencies

Create a new directory and install the required dependencies:

```bash Shell theme={null}
# Set up your directory and initialize a Node.js project
mkdir stellar-usdc-transfer
cd stellar-usdc-transfer
npm init -y

# Set up module type and start command
npm pkg set type=module
npm pkg set scripts.start="npx tsx --env-file=.env main.ts"

# Install runtime dependencies
npm install @stellar/stellar-sdk

# Install dev dependencies
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. Configure environment variables

Create a `.env` file in your project directory and add your sender's secret key:

```text .env theme={null}
STELLAR_SECRET_KEY=YOUR_SENDER_SECRET_KEY
```

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

<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

Add `main.ts` at the project root. The script:

1. Loads your existing sender wallet from the environment variable
2. Creates and funds a new recipient wallet using Friendbot
3. Establishes a USDC trustline on the recipient account
4. Submits a USDC payment from the sender to the recipient

```typescript main.ts theme={null}
import {
  Asset,
  BASE_FEE,
  Horizon,
  Keypair,
  Networks,
  Operation,
  TransactionBuilder,
} from "@stellar/stellar-sdk";

const HORIZON_URL = "https://horizon-testnet.stellar.org";
const FRIENDBOT_URL = "https://friendbot.stellar.org";
const USDC_ISSUER = "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5";
const USDC_CODE = "USDC";
const TRANSFER_AMOUNT = "10"; // 10 USDC
const TX_EXPLORER_BASE = "https://stellar.expert/explorer/testnet/tx";

const server = new Horizon.Server(HORIZON_URL);
const usdc = new Asset(USDC_CODE, USDC_ISSUER);

async function fundWithFriendbot(publicKey: string): Promise<void> {
  const response = await fetch(`${FRIENDBOT_URL}?addr=${publicKey}`);
  if (!response.ok) {
    throw new Error(
      `Friendbot funding failed: ${response.status} ${response.statusText}`,
    );
  }
}

async function establishTrustline(keypair: Keypair): Promise<void> {
  const account = await server.loadAccount(keypair.publicKey());
  const tx = new TransactionBuilder(account, {
    fee: BASE_FEE,
    networkPassphrase: Networks.TESTNET,
  })
    .addOperation(Operation.changeTrust({ asset: usdc }))
    .setTimeout(30)
    .build();

  tx.sign(keypair);
  const result = await server.submitTransaction(tx);
  if (!result.successful) {
    throw new Error(`Trustline transaction failed: ${JSON.stringify(result)}`);
  }
  console.log(`Trustline established: ${TX_EXPLORER_BASE}/${result.hash}`);
}

async function transferUsdc(
  sender: Keypair,
  recipientPublicKey: string,
): Promise<void> {
  const account = await server.loadAccount(sender.publicKey());
  const tx = new TransactionBuilder(account, {
    fee: BASE_FEE,
    networkPassphrase: Networks.TESTNET,
  })
    .addOperation(
      Operation.payment({
        destination: recipientPublicKey,
        asset: usdc,
        amount: TRANSFER_AMOUNT,
      }),
    )
    .setTimeout(30)
    .build();

  tx.sign(sender);
  const result = await server.submitTransaction(tx);
  if (!result.successful) {
    throw new Error(`Payment transaction failed: ${JSON.stringify(result)}`);
  }
  console.log(`Transfer successful: ${TX_EXPLORER_BASE}/${result.hash}`);
}

async function main() {
  // Load the sender wallet from the environment variable
  const sender = Keypair.fromSecret(process.env.STELLAR_SECRET_KEY as string);
  console.log("Sender address:", sender.publicKey());

  // Create and fund a new recipient wallet via Friendbot
  console.log("\nCreating recipient wallet...");
  const recipient = Keypair.random();
  console.log("Recipient address:", recipient.publicKey());

  console.log("Funding recipient with testnet XLM...");
  await fundWithFriendbot(recipient.publicKey());
  console.log("Recipient funded with XLM.");

  // Establish a USDC trustline on the recipient account
  console.log("\nEstablishing USDC trustline on recipient account...");
  await establishTrustline(recipient);

  // Transfer USDC from sender to recipient
  console.log(`\nTransferring ${TRANSFER_AMOUNT} USDC to recipient...`);
  await transferUsdc(sender, recipient.publicKey());

  // Confirm recipient balance
  const recipientAccount = await server.loadAccount(recipient.publicKey());
  const usdcBalance = recipientAccount.balances.find(
    (b) =>
      b.asset_type === "credit_alphanum4" &&
      b.asset_code === USDC_CODE &&
      b.asset_issuer === USDC_ISSUER,
  );
  console.log(`\nRecipient USDC balance: ${usdcBalance?.balance ?? "0"} USDC`);
}

main().catch(console.error);
```

## Step 3: Run the script

From the project directory, run:

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

Your output should look similar to the following (addresses and hashes will
differ):

```bash Shell theme={null}
Sender address: GAXYZ...

Creating recipient wallet...
Recipient address: GDEFG...
Funding recipient with testnet XLM...
Recipient funded with XLM.

Establishing USDC trustline on recipient account...
Trustline established: https://stellar.expert/explorer/testnet/tx/abc123...

Transferring 10 USDC to recipient...
Transfer successful: https://stellar.expert/explorer/testnet/tx/def456...

Recipient USDC balance: 10.0000000 USDC
```

<Info>
  Common errors you might encounter:

  * **`Friendbot funding failed: 400`** — The Friendbot rate limit was exceeded.
    Wait a few seconds and try again.
  * **`op_no_trust`** — The recipient account does not have a USDC trustline. The
    script establishes one automatically, but if you're adapting this example for
    your own recipient address, ensure the trustline exists first. See
    [Set up a USDC trustline on Stellar](/stablecoins/quickstart-setup-usdc-trustline-stellar).
  * **`op_underfunded`** — The sender does not have enough USDC. Ensure your
    sender wallet is funded with testnet USDC from the
    [Circle Faucet](https://faucet.circle.com) before running the script.
</Info>

For more detail on Stellar-specific USDC behavior in CCTP, including address
encoding and decimal precision, see [CCTP on Stellar](/cctp/references/stellar).
