LogoAnchor Docs

Account Types

Anchor Account Type Examples

Minimal reference examples for Anchor account types.

See the account types source code for implementation details.

Account Types

Account<'info, T>

Description: Account container that checks ownership on deserialization
Examples: Github | Solpg

snippet
#[derive(Accounts)]
pub struct InstructionAccounts<'info> {


    pub account: Account<'info, CustomAccountType>,
}
 
#[account]
pub struct CustomAccountType {
    data: u64,
}

AccountInfo<'info>

Description: AccountInfo can be used as a type but Unchecked Account should be used instead
Examples: Github | Solpg

snippet
#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    /// CHECK: AccountInfo is an unchecked account


    pub unchecked_account: AccountInfo<'info>,
}

AccountLoader<'info, T>

Description: Type facilitating on demand zero copy deserialization
Examples: Github | Solpg

snippet
#[derive(Accounts)]
pub struct InstructionAccounts<'info> {


    pub account: AccountLoader<'info, ZeroCopyAccountType>,
}
 
#[account(zero_copy)]
pub struct ZeroCopyAccountType {
    data: u64,
}

Box<Account<'info, T>>

Description: Box type to save stack space
Examples: Github | Solpg

snippet
#[derive(Accounts)]
pub struct InstructionAccounts<'info> {


    pub account: Box<Account<'info, AccountType>>,
}

Interface<'info, T>

Description: Type validating that the account is one of a set of given Programs
Examples: Github | Solpg

snippet
// Token program or Token2022 program
use anchor_spl::token_interface::TokenInterface;
 
#[derive(Accounts)]
pub struct InstructionAccounts<'info> {


    pub program: Interface<'info, TokenInterface>,
}

InterfaceAccount<'info, T>

Description: Account container that checks ownership on deserialization
Examples: Github | Solpg

snippet
// Token program or Token2022 program Mint/TokenAccount
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
 
#[derive(Accounts)]
pub struct InstructionAccounts<'info> {


    pub mint: InterfaceAccount<'info, Mint>,
    pub token: InterfaceAccount<'info, TokenAccount>,
    pub program: Interface<'info, TokenInterface>,
}

Option<Account<'info, T>>

Description: Option type for optional accounts
Examples: Github | Solpg

snippet
#[derive(Accounts)]
pub struct InstructionAccounts<'info> {


    pub account: Option<Account<'info, AccountType>>,
}

Program<'info, T>

Description: Type validating that the account is the given Program
Examples: Github | Solpg

snippet
use anchor_spl::token::Token;
 
#[derive(Accounts)]
pub struct InstructionAccounts<'info> {


    pub system_program: Program<'info, System>,
    pub token_program: Program<'info, Token>,
}

Signer<'info>

Description: Type validating that the account signed the transaction
Examples: Github | Solpg

snippet
#[derive(Accounts)]
pub struct InstructionAccounts<'info> {


    pub signer: Signer<'info>,
}

SystemAccount<'info>

Description: Type validating that the account is owned by the system program
Examples: Github | Solpg

snippet
#[derive(Accounts)]
pub struct InstructionAccounts<'info> {


    pub account: SystemAccount<'info>,
}

Sysvar<'info, T>

Description: Type validating that the account is a sysvar and deserializing it
Examples: Github | Solpg

snippet
#[derive(Accounts)]
pub struct InstructionAccounts<'info> {


    pub rent: Sysvar<'info, Rent>,
    pub clock: Sysvar<'info, Clock>,
}

UncheckedAccount<'info>

Description: Explicit wrapper for AccountInfo types to emphasize that no checks are performed
Examples: Github | Solpg

snippet
#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    // CHECK: No checks are performed


    pub account: UncheckedAccount<'info>,
}

Migration<'info, From, To>

Description: Account container that handles schema migrations from one account type (From) to another (To). During deserialization, the account must be in the From format. On instruction exit, the account must be migrated to the To format, which is then serialized. Typically used with the realloc constraint to resize accounts during migration.

Checks:

  • Account.info.owner == From::owner()
  • Account is initialized (not owned by system program with 0 lamports)
  • Account deserializes as From type
snippet
use anchor_lang::prelude::*;
 
#[account]
pub struct AccountV1 {
    pub data: u64,
}
 
#[account]
pub struct AccountV2 {
    pub data: u64,
    pub new_field: u64,
}
 
#[derive(Accounts)]
pub struct MigrateAccount<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,


    #[account(
        mut,
        realloc = 8 + AccountV2::INIT_SPACE,
        realloc::payer = payer,
        realloc::zero = false
    )]
    pub my_account: Migration<'info, AccountV1, AccountV2>,
    pub system_program: Program<'info, System>,
}

Usage patterns:

migrate with explicit call
// Access old fields via Deref before migration
let old_data = ctx.accounts.my_account.data;
 
// Migrate to new schema
ctx.accounts.my_account.migrate(AccountV2 {
    data: old_data,
    new_field: 42,
})?;
idempotent migration with into_inner
// Migrates if needed, returns reference to new data
let migrated = ctx.accounts.my_account.into_inner(AccountV2 {
    data: ctx.accounts.my_account.data,
    new_field: ctx.accounts.my_account.data * 2,
});
msg!("New field: {}", migrated.new_field);
idempotent migration with mutation
// Migrates if needed, returns mutable reference
let migrated = ctx.accounts.my_account.into_inner_mut(AccountV2 {
    data: ctx.accounts.my_account.data,
    new_field: 0,
});
migrated.new_field = 42;