Token & Associated Token Constraints
anchor-spl provides constraint helpers for SPL Token and Associated Token accounts, reducing manual mint/authority checks.
Recipe
use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::token::{Mint, Token, TokenAccount};
#[account(
init_if_needed,
payer = payer,
associated_token::mint = mint,
associated_token::authority = owner,
)]
pub ata: Account<'info, TokenAccount>,When to reach for this: Instructions move tokens or create ATAs.
Working Example
#[derive(Accounts)]
pub struct TransferTokens<'info> {
#[account(mut)]
pub payer: Signer<'info>,
#[account(mut)]
pub from: Account<'info, TokenAccount>,
#[account(
init_if_needed,
payer = payer,
associated_token::mint = mint,
associated_token::authority = recipient,
)]
pub to: Account<'info, TokenAccount>,
pub mint: Account<'info, Mint>,
pub recipient: SystemAccount<'info>,
pub token_program: Program<'info, Token>,
pub associated_token_program: Program<'info, AssociatedToken>,
pub system_program: Program<'info, System>,
}What this demonstrates:
associated_token::mintties ATA to mintassociated_token::authoritysets owner- Token program must be in accounts list
- Use
token_interfacefor Token-2022
Deep Dive
Token-2022 / Interface
For Token-2022, use InterfaceAccount and TokenInterface:
pub token_program: Interface<'info, TokenInterface>,
pub mint: InterfaceAccount<'info, Mint>,Anchor 0.32.1 unifies patterns under token interface types.
Gotchas
- Wrong token program ID - Constraint fails for Token-2022.. Fix: Use
Interfacewhen supporting both. - ATA init without system program - Creation CPI incomplete.. Fix: Include system + associated token programs.
- Mint mismatch on transfer - Token program rejects.. Fix: Add
constraint = from.mint == mint.key(). - Unchecked recipient ATA - Funds sent to wrong ATA.. Fix: Use associated_token constraints.
- Missing mut on token accounts - Transfer fails.. Fix: Mark source and dest
mut.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Manual token::accessor checks | Custom programs | Standard SPL flows |
| CPI only without ATA constraint | External ATA creation | Same-instruction ATA init |
| token_interface CPI helpers | Token-2022 transfers | Legacy token only |
FAQs
What Anchor version does this site use?
0.32.1 for anchor-lang, Anchor CLI, and examples in this section.
Do I need Solana CLI alongside Anchor?
Yes. Solana CLI 3.0.10 handles keypairs, airdrops, and solana program inspection.
Where does the IDL live after build?
target/idl/<program>.json in your workspace.
Can I mix UncheckedAccount with Signer?
Yes, but every unchecked field needs explicit constraints or handler checks.
How do I test without devnet?
Use anchor test with Surfpool 0.12.0 or LiteSVM 0.6.x in CI.
What is the 8-byte prefix on account data?
Anchor account discriminator; do not strip it when sizing space.
Should I commit generated IDL?
Yes, or publish on-chain IDL so clients have a canonical source.
How do I debug constraint failures?
Run with logs; Anchor prints constraint name and account index.
Does Anchor work on Agave 4.1.1?
Yes. This stack targets Agave validators with Solana CLI 3.0.10.
What should I read next in this section?
See sibling articles linked in Related for deeper token constraints topics.
Related
- SPL Token CPIs - transfer helpers
- Associated Token Accounts - ATA model
- anchor-lang & anchor-spl - crate overview
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.