anchor-lang & anchor-spl
anchor-lang 0.32.1 provides macros, account validation, and IDL generation for on-chain programs. anchor-spl wraps SPL Token, Associated Token, and related programs with typed CPI helpers. This pair is the default Rust framework on Agave 4.1.1 when teams want IDL-driven clients in TypeScript (@solana/kit 7.0.0 via Codama) and Python (AnchorPy).
Recipe
Quick-reference recipe card - copy-paste ready.
[dependencies]
anchor-lang = "0.32.1"
anchor-spl = "0.32.1"use anchor_lang::prelude::*;
use anchor_spl::token::{self, Token, TokenAccount, Transfer};
declare_id!("<PROGRAM_ID>");
#[program]
pub mod my_program {
use super::*;
pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {
token::transfer(ctx.accounts.transfer_ctx(), amount)?;
Ok(())
}
}When to reach for this:
- Greenfield product programs with frequent instruction changes.
- Teams wanting Anchor IDL + TypeScript/Python clients.
- SPL Token CPIs without hand-packing instruction data.
- Standard account constraints (signer, mut, seeds, init).
Working Example
use anchor_lang::prelude::*;
use anchor_spl::{
associated_token::AssociatedToken,
token::{Mint, Token, TokenAccount, Transfer},
};
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod token_vault {
use super::*;
pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
let cpi_accounts = Transfer {
from: ctx.accounts.user_ata.to_account_info(),
to: ctx.accounts.vault_ata.to_account_info(),
authority: ctx.accounts.user.to_account_info(),
};
let cpi_ctx = CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accounts);
anchor_spl::token::transfer(cpi_ctx, amount)?;
Ok(())
}
}
#[derive(Accounts)]
pub struct Deposit<'info> {
#[account(mut)]
pub user: Signer<'info>,
#[account(mut)]
pub user_ata: Account<'info, TokenAccount>,
#[account(mut)]
pub vault_ata: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
}What this demonstrates:
#[program]dispatches instructions with generated discriminator.#[derive(Accounts)]encodes validation and metas.anchor_spl::token::transferperforms CPI to SPL Token program.Signer,Account, andProgramtypes enforce ownership.
Deep Dive
How It Works
- Proc macros expand to
solana-programentrypoints and account iterators. anchor buildemits IDL JSON and TypeScript types (legacy) or feeds Codama.anchor-splmaps SPL layouts toAccount<'info, T>with owner checks.- Feature flags (
init-if-needed,idl-build) tune codegen and IDL contents.
Rust Crate Table
| Crate | Role | Pin |
|---|---|---|
anchor-lang | Macros, accounts, errors, events | 0.32.1 |
anchor-spl | SPL CPI helpers, token metadata types | 0.32.1 |
anchor-derive-accounts | Account validation codegen | Transitive 0.32.1 |
anchor-attribute-* | Instruction/account/program macros | Transitive 0.32.1 |
TypeScript / NPM Table
| Package | Role | Pin |
|---|---|---|
@coral-xyz/anchor | Legacy Anchor TS client | Match CLI 0.32.1 |
@solana/kit | Modern RPC/tx + Codama clients | 7.0.0 |
| Codama | IDL -> kit client codegen | Project-pinned |
Rust Notes
// Match CLI and crate versions exactly
// Anchor.toml: anchor_version = "0.32.1"
// Space for init accounts
#[account(init, payer = user, space = 8 + MyState::INIT_SPACE)]
pub state: Account<'info, MyState>,Gotchas
- CLI vs crate version mismatch - obscure IDL errors. Fix:
avm use 0.32.1andanchor-lang = "0.32.1". - Missing
token_programaccount in CPI - runtime failure. Fix: include all SPL required accounts in struct. - Overusing
UncheckedAccount- security holes. Fix: typed accounts or explicit owner checks. - IDL not checked in - client drift. Fix: commit
target/idl/*.jsonor regenerate in CI. - Init without rent calculation - insufficient lamports. Fix: use
spacemacro +Rentsysvar helpers.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
Native solana-program | Minimum CU/size | Rapid feature iteration |
pinocchio + manual CPI | Extreme optimization | Heavy SPL + IDL needs |
steel framework | Opinionated structure | Team standardized on Anchor |
mpl crates direct | NFT-specific logic | Simple token vaults |
FAQs
anchor-lang without anchor-spl?
Yes for non-token programs; add anchor-spl when CPIing SPL programs.
Token-2022 support?
Check anchor-spl feature flags and program IDs for Token-2022 extensions.
declare_program!?
Consume external IDLs in Rust clients - see IDL section.
Testing?
anchor test with LiteSVM 0.6.x or local validator per Anchor.toml provider.
Upgradeable programs?
Deploy with upgrade authority; align declare_id! with on-chain program id.
Custom errors?
#[error_code] enum maps to IDL errors for clients.
Events?
emit!(MyEvent { ... }) logs structured data parseable by clients.
no_std?
Anchor programs target sBPF; use supported std subset via solana toolchain.
Verifiable builds?
Run anchor build --verifiable aligned with solana-verify workflow.
Client codegen path?
IDL -> Codama -> @solana/kit 7.0.0 recommended for new frontends.
Related
- solana-program & solana-sdk - underlying primitives
- Anchor Basics - accounts model
- spl-token & spl-associated-token-account - token crates
- Codama - client generation
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.