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.
See How Payments Work on Solana for core payment concepts.
The token2022Program() plugin from @solana-program/token-2022 extends your
@solana/kit 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 for complete runnable code.
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.
const client = createClient().use(signer(sender)).use(solanaLocalRpc()).use(token2022Program());
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
amountin base units along with the mint'sdecimals. 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 whosecontext.signatureholds the transaction signature
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();
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.
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();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();
Demo
// Generate signers for sender and recipientconst 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 tokensconst { 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.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 balancesconst [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 RPCconst 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// =============================================================================
Is this page helpful?