Account Types
Anchor account wrapper types encode common validation rules. Pick the strictest type that matches your threat model, then add attribute constraints for relationships.
Recipe
#[derive(Accounts)]
pub struct Example<'info> {
pub payer: Signer<'info>,
pub data: Account<'info, MyState>,
pub sys: SystemAccount<'info>,
pub token_prog: Program<'info, Token>,
/// CHECK: validated in handler
pub oracle: UncheckedAccount<'info>,
}When to reach for this: You choose types for a new accounts struct field.
Working Example
#[derive(Accounts)]
pub struct Deposit<'info> {
#[account(mut)]
pub user: Signer<'info>,
#[account(
mut,
associated_token::mint = mint,
associated_token::authority = user,
)]
pub user_ata: Account<'info, TokenAccount>,
pub mint: Account<'info, Mint>,
pub token_program: Program<'info, Token>,
pub associated_token_program: Program<'info, AssociatedToken>,
}What this demonstrates:
Signerrequires a transaction signatureAccount<T>checks owner and deserializes with discriminatorProgram<T>pins program IDs for CPI targetsUncheckedAccountskips automatic safety checks
Deep Dive
Type Reference
| Type | Automatic checks |
|---|---|
Signer<'info> | is_signer |
Account<'info, T> | Owner, discriminator, deserialize T |
InterfaceAccount<'info, T> | Token-2022 / interface owners |
Program<'info, T> | Program ID for T |
SystemAccount<'info> | System program owner |
UncheckedAccount<'info> | None |
Rust Notes
Use AccountLoader<'info, T> for zero-copy accounts and Interface<'info, T> for token interfaces in 0.32.
Gotchas
- Account without mut on writable data - Serialize fails at end of instruction.. Fix: Add
mutwhen persisting changes. - Program field omitted - CPI fails with wrong program ID.. Fix: Include explicit
Program<Token>etc. - UncheckedAccount for user funds - Attacker substitutes accounts.. Fix: Use typed accounts or strict constraints.
- Wrong Account<T> for token-2022 - Owner check fails.. Fix: Use
InterfaceAccountwith TokenInterface. - Signer for PDA authority - PDAs cannot sign as Signer.. Fix: Use seeds +
invoke_signedor CPIwith_signer.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| AccountLoader | Large zero-copy state | Small Borsh structs |
| InterfaceAccount | Token-2022 mints/ATAs | Legacy SPL only |
| remaining_accounts | Dynamic sets | Fixed instruction layout |
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 account types topics.
Related
- The #[account] Constraints - attributes
- Token & Associated Token Constraints - SPL helpers
- Custom Account Types - manual traits
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.