---
title: Basic Payment
description: Learn how to send a single token transfer between wallets.
---

Solana enables instant, global token transfers with fees under $0.001. Whether
you're building cross-border remittances, payroll disbursements, or treasury
operations, a basic stablecoin payment settles in under a second and costs a
fraction of a cent.

## How It Works

A payment moves stablecoins from the sender's token account to the recipient's
token account. If the recipient is receiving this token for the first time,
their token account can be created as part of the same transaction.

<Callout>
  See [How Payments Work on Solana](/docs/payments/how-payments-work) for core
  payment concepts.
</Callout>

The `token2022Program()` plugin from `@solana-program/token-2022` extends your
[`@solana/kit`](/docs/frontend/client) client with a `client.token2022` helper.
Its `transferToATA` method handles ATA derivation and transaction building
automatically, which is ideal for single payment transfers.

The steps below show the core flow. See the [Demo](#demo) for complete runnable
code.

<ScrollyCoding>

## !!steps Create the Token Helper

Add the `token2022Program()` plugin to your client with
`.use(token2022Program())`. This exposes `client.token2022`, whose
`instructions` provide methods for common token operations.

Use `token2022Program()` for mints owned by the Token-2022 program, or
`tokenProgram()` from `@solana-program/token` for mints owned by the original
Token program.

<CodePlaceholder title="Basic Payment" />

```ts !! title="Basic Payment"
// !focus(1:4)
const client = createClient()
  .use(signer(sender))
  .use(solanaLocalRpc())
  .use(token2022Program());
```

## !!steps Send the Payment

Use `transferToATA()` to transfer tokens between wallets. The method handles:

- **ATA Resolution**: Automatically derives Associated Token Accounts (ATAs) for
  the sender and recipient. If the recipient's ATA doesn't exist, the
  instruction to create the account is automatically added to the same
  transaction.
- **Checked Amounts**: Pass `amount` in base units along with the mint's
  `decimals`. The transfer is checked against the mint's decimals on-chain (e.g.
  250000 base units -> 0.25 tokens, if the mint has 6 decimals)
- **Transaction Building**: `sendTransaction()` builds, signs, and sends the
  transaction, resolving to a result whose `context.signature` holds the
  transaction signature

<CodePlaceholder title="Basic Payment" />

```ts !! title="Basic Payment"
const client = createClient()
  .use(signer(sender))
  .use(solanaLocalRpc())
  .use(token2022Program());

// !focus(1:9)
const result = await client.token2022.instructions
  .transferToATA({
    mint: mint.address,
    authority: sender,
    recipient: recipient.address,
    amount: 250_000n,
    decimals: 6
  })
  .sendTransaction();
```

## !!steps Verify Balances

After the transfer completes, read the token balances from the RPC. Derive each
wallet's ATA with `findAssociatedTokenPda()`, then call
`getTokenAccountBalance()` to fetch the balance for that account.

<CodePlaceholder title="Basic Payment" />

```ts !! title="Basic Payment"
const client = createClient()
  .use(signer(sender))
  .use(solanaLocalRpc())
  .use(token2022Program());

const result = await client.token2022.instructions
  .transferToATA({
    mint: mint.address,
    authority: sender,
    recipient: recipient.address,
    amount: 250_000n,
    decimals: 6
  })
  .sendTransaction();

// !focus(1:14)
const [senderAta] = await findAssociatedTokenPda({
  mint: mint.address,
  owner: sender.address,
  tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipientAta] = await findAssociatedTokenPda({
  mint: mint.address,
  owner: recipient.address,
  tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const senderBalance = await client.rpc.getTokenAccountBalance(senderAta).send();
const recipientBalance = await client.rpc
  .getTokenAccountBalance(recipientAta)
  .send();
```

</ScrollyCoding>

### Demo

<CodeTabs flags="r">

```ts !! title="Demo"
// !collapse(1:14) collapsed
// Click ">" icon on left to expand demo imports
import {
  createClient,
  generateKeyPairSigner,
  lamports,
  type TransactionSigner
} from "@solana/kit";
import { solanaLocalRpc } from "@solana/kit-plugin-rpc";
import { signer } from "@solana/kit-plugin-signer";
import {
  token2022Program,
  findAssociatedTokenPda,
  TOKEN_2022_PROGRAM_ADDRESS
} from "@solana-program/token-2022";

// Generate signers for sender and recipient
const sender = await generateKeyPairSigner();
const recipient = await generateKeyPairSigner();

console.log("Sender Address:", sender.address);
console.log("Recipient Address:", recipient.address);

// Demo Setup: Create client, mint account, and fund the sender with initial tokens
const { client, mint } = await demoSetup(sender);

console.log("\nMint Address:", mint.address);

// =============================================================================
// Basic Token Payment Demo
// =============================================================================

// Transfer tokens from sender to recipient. The source and destination ATAs are
// derived automatically, and the recipient's ATA is created if it does not exist.
// !mark(1:9)
const result = await client.token2022.instructions
  .transferToATA({
    mint: mint.address,
    authority: sender,
    recipient: recipient.address,
    amount: 250_000n, // 0.25 tokens (base units for a mint with 6 decimals)
    decimals: 6
  })
  .sendTransaction();

console.log("\n=== Token Payment Complete ===");
console.log("Transaction Signature:", result.context.signature);

// Derive the sender and recipient ATAs to read their balances
const [senderAta] = await findAssociatedTokenPda({
  mint: mint.address,
  owner: sender.address,
  tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipientAta] = await findAssociatedTokenPda({
  mint: mint.address,
  owner: recipient.address,
  tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});

// Fetch final token account balances via RPC
const senderBalance = await client.rpc.getTokenAccountBalance(senderAta).send();
const recipientBalance = await client.rpc
  .getTokenAccountBalance(recipientAta)
  .send();
console.log(
  "\nSender Token Account Balance:",
  senderBalance.value.uiAmountString
);
console.log(
  "Recipient Token Account Balance:",
  recipientBalance.value.uiAmountString
);

// =============================================================================
// Demo Setup Helper Function
// =============================================================================
// !collapse(1:1000) collapsed

/**
 * Sets up for a token transfer demo:
 * - Creates a @solana/kit client with the Token-2022 program plugin
 * - Airdrops SOL to the sender for transaction fees
 * - Creates and initializes the mint (6 decimals)
 * - Mints initial tokens to the sender's ATA (created automatically)
 *
 * @param sender - The sender's signer (funded and used as the mint authority)
 * @returns The client instance and the mint signer
 */
async function demoSetup(sender: TransactionSigner) {
  // Create a @solana/kit client pointing to the local validator.
  // signer(sender) sets the fee payer; solanaLocalRpc() targets 127.0.0.1:8899;
  // token2022Program() adds the client.token2022 helper.
  const client = createClient()
    .use(signer(sender))
    .use(solanaLocalRpc())
    .use(token2022Program());

  // Fund the sender with SOL for transaction fees
  await client.airdrop(sender.address, lamports(1_000_000_000n));

  // Generate the signer to use as the mint's address
  const mint = await generateKeyPairSigner();

  // Create and initialize the mint account.
  // Invokes the System program (create account) and the Token-2022 program.
  await client.token2022.instructions
    .createMint({
      newMint: mint,
      decimals: 6,
      mintAuthority: sender
    })
    .sendTransaction();

  // Mint initial tokens to the sender's ATA (created if it does not exist)
  await client.token2022.instructions
    .mintToATA({
      mint: mint.address,
      owner: sender.address,
      mintAuthority: sender,
      amount: 1_000_000n, // 1.00 tokens (1,000,000 base units with 6 decimals)
      decimals: 6
    })
    .sendTransaction();

  return {
    client,
    mint
  };
}
```

</CodeTabs>
