SPL Token Basics
9 examples to get you started with SPL Token - 7 basic and 2 intermediate.
Search across all documentation pages
9 examples to get you started with SPL Token - 7 basic and 2 intermediate.
solana config set --url devnet
solana airdrop 2
spl-token --versioncargo add anchor-lang@0.32.1 anchor-spl@0.32.1
npm install @solana/kit@7.0.0SPL Token program ID: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA.
A mint account defines decimals, supply, and authorities.
spl-token create-token --decimals 6decimals sets how raw amounts map to human-readable valuesNone means accounts cannot be frozenRelated: Minting & Burning - supply changes
Read supply and authority fields before integrating clients.
spl-token display <MINT>Supply is raw integer units (not UI amount)Mint authority pubkey controls inflationFreeze authority of disabled means no freeze capabilityRelated: Authorities - revoke patterns
Wallets hold balances in token accounts, not the mint itself.
spl-token create-account <MINT>Related: Associated Token Accounts (ATAs)
Increase supply by crediting a token account.
spl-token mint <MINT> 1spl-token amounts are UI amounts: 1 mints 1.0 token (raw 1000000 at 6 decimals). Passing 1000000 here would mint a million tokensRelated: Decimals & Amounts
Move balances without changing total supply.
spl-token transfer <MINT> 0.5 <RECIPIENT_WALLET> --fund-recipient0.5 = half a token), not raw base units--fund-recipient creates recipient ATA if missingRelated: Transfers & Delegates
Clients query token accounts over standard RPC.
import { createSolanaRpc, address } from "@solana/kit";
const rpc = createSolanaRpc("https://api.devnet.solana.com");
const owner = address("YOUR_WALLET");
const { value } = await rpc
.getTokenAccountsByOwner(owner, { mint: address("MINT_PUBKEY") }, { encoding: "jsonParsed" })
.send();
const balance = value[0]?.account.data.parsed.info.tokenAmount.uiAmount;
console.log("Balance:", balance);jsonParsed returns UI amount and raw amount togetherRelated: Wrapped SOL - native SOL as SPL
Permanently remove tokens from circulation.
# burn takes the TOKEN ACCOUNT address, not the mint
spl-token burn "$(spl-token address --token <MINT> --verbose | awk '/Associated token address/{print $NF}')" 0.10.1 = one tenth of a token)Related: Minting & Burning
Programs move tokens via cross-program invocation (CPI).
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Mint, Token, TokenAccount, TransferChecked};
#[derive(Accounts)]
pub struct PayFee<'info> {
pub mint: Account<'info, Mint>,
#[account(mut, token::mint = mint, token::authority = authority)]
pub user_token: Account<'info, TokenAccount>,
#[account(mut, token::mint = mint)]
pub treasury_token: Account<'info, TokenAccount>,
pub authority: Signer<'info>,
pub token_program: Program<'info, Token>,
}
pub fn pay_fee(ctx: Context<PayFee>, amount: u64) -> Result<()> {
let decimals = ctx.accounts.mint.decimals;
let cpi = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
TransferChecked {
mint: ctx.accounts.mint.to_account_info(),
from: ctx.accounts.user_token.to_account_info(),
to: ctx.accounts.treasury_token.to_account_info(),
authority: ctx.accounts.authority.to_account_info(),
},
);
token::transfer_checked(cpi, amount, decimals)?;
Ok(())
}Account<'info, TokenAccount> deserializes SPL layout and validates owner is the classic Token programTransferChecked passes the mint and asserts decimals on-chain; the unchecked Transfer is deprecatedtoken::mint pins both sides to the same asset - an unpinned source is the classic drain buganchor_spl::token types reject Token-2022 accounts - use anchor_spl::token_interface for thoseRelated: SPL Token in Programs
Lock supply after initial distribution.
spl-token authorize <MINT> mint --disableRelated: SPL Token Best Practices - launch checklist
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.
Reviewed by Chris St. John·Last updated Jul 16, 2026