Testing token logic on Solana means setting up mints, token accounts, and
authorities before you can test the transfer logic you actually care about. The
tokenProgram() plugin from @solana-program/token collapses that setup to a
few method calls — and handles ATA creation automatically.
Installation
pnpm add @solana/kit @solana/kit-plugin-litesvm @solana/kit-plugin-signer @solana-program/token
Create a Mint
import { createClient, generateKeyPairSigner, lamports } from "@solana/kit";import { litesvm } from "@solana/kit-plugin-litesvm";import { signer } from "@solana/kit-plugin-signer";import { tokenProgram } from "@solana-program/token";const mySigner = await generateKeyPairSigner();const client = createClient().use(signer(mySigner)).use(litesvm()).use(tokenProgram());client.svm.airdrop(client.payer.address, lamports(10_000_000_000n));const mintAuthority = await generateKeyPairSigner();const newMint = await generateKeyPairSigner();await client.token.instructions.createMint({newMint,decimals: 9,mintAuthority: mintAuthority.address}).sendTransaction();// Verifyconst mintAccount = await client.token.accounts.mint.fetch(newMint.address);console.log(mintAccount.data.supply); // 0nconsole.log(mintAccount.data.decimals); // 9
Mint Tokens
mintToATA issues tokens to an owner's Associated Token Account. If the ATA
doesn't exist, it's created in the same transaction.
await client.token.instructions.mintToATA({mint: newMint.address,owner: client.payer.address,mintAuthority,amount: 1_000_000_000_000n, // 1000 tokens (9 decimals)decimals: 9}).sendTransaction();
The decimals parameter is a safety check — it must match the mint's decimals
exactly, or the Token program rejects the transaction with error 0x24. This
prevents accidentally minting 1000x too many tokens.
Transfer Tokens
transferToATA moves tokens between users. The recipient's ATA is created
automatically if needed.
const alice = await generateKeyPairSigner();const bob = await generateKeyPairSigner();// Mint to Aliceawait client.token.instructions.mintToATA({mint: newMint.address,owner: alice.address,mintAuthority,amount: 1_000_000_000_000n,decimals: 9}).sendTransaction();// Transfer 400 tokens from Alice to Bobawait client.token.instructions.transferToATA({mint: newMint.address,authority: alice,recipient: bob.address,amount: 400_000_000_000n,decimals: 9}).sendTransaction();
The authority is the signer who owns the source tokens. The plugin derives
both source and destination ATAs from their addresses + the mint.
Verifying Balances
To read back token balances, derive the ATA address with
findAssociatedTokenPda and fetch the decoded account:
import {findAssociatedTokenPda,TOKEN_PROGRAM_ADDRESS} from "@solana-program/token";const [aliceAta] = await findAssociatedTokenPda({owner: alice.address,mint: newMint.address,tokenProgram: TOKEN_PROGRAM_ADDRESS});const aliceAccount = await client.token.accounts.token.fetch(aliceAta);console.log(aliceAccount.data.amount); // 600_000_000_000nconsole.log(aliceAccount.data.mint); // newMint.addressconsole.log(aliceAccount.data.owner); // alice.address
Decimals
Token amounts are always in raw units. 1000 tokens with 9 decimals is
1_000_000_000_000n, not 1000n.
// USDC has 6 decimalsconst oneUSDC = 10n ** 6n; // 1_000_000n// SOL has 9 decimalsconst oneSOL = 10n ** 9n; // 1_000_000_000n
Always use BigInt for token amounts. JavaScript's Number loses precision
above 2^53.
Plugin Reference
| Method | Description |
|---|---|
client.token.instructions.createMint() | Create a new SPL token mint |
client.token.instructions.mintToATA() | Mint tokens to an owner's ATA (creates ATA if needed) |
client.token.instructions.transferToATA() | Transfer tokens to a recipient's ATA (creates ATA if needed) |
client.token.accounts.mint.fetch() | Fetch and decode a mint account |
client.token.accounts.token.fetch() | Fetch and decode a token account |
Common Errors
| Error | Cause | Fix |
|---|---|---|
custom program error: 0x24 | decimals doesn't match the mint | Use the same decimals value you passed to createMint |
AccountNotFound | Account doesn't exist yet | Ensure mint is created before minting |
InsufficientFunds | Not enough lamports for rent | Airdrop more SOL to the payer |
OwnerMismatch | Wrong program owns the account | Check program ID |
Is this page helpful?