Skip to content

Latest commit

 

History

History
303 lines (247 loc) · 9.11 KB

File metadata and controls

303 lines (247 loc) · 9.11 KB
title Account Types
description 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

#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    // [!code word: Account]
    // [!code highlight]
    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

#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    /// CHECK: AccountInfo is an unchecked account
    // [!code word:AccountInfo]
    // [!code highlight]
    pub unchecked_account: AccountInfo<'info>,
}

AccountLoader<'info, T>

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

#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    // [!code word:AccountLoader]
    // [!code highlight]
    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

#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    // [!code word:Box]
    // [!code highlight]
    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

// Token program or Token2022 program
use anchor_spl::token_interface::TokenInterface;

#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    // [!code word: Interface]
    // [!code highlight]
    pub program: Interface<'info, TokenInterface>,
}

InterfaceAccount<'info, T>

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

// Token program or Token2022 program Mint/TokenAccount
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};

#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    // [!code word:InterfaceAccount]
    // [!code highlight:2]
    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

#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    // [!code word:Option]
    // [!code highlight]
    pub account: Option<Account<'info, AccountType>>,
}

Program<'info, T>

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

use anchor_spl::token::Token;

#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    // [!code word:Program]
    // [!code highlight:2]
    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

#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    // [!code word:Signer]
    // [!code highlight]
    pub signer: Signer<'info>,
}

SystemAccount<'info>

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

#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    // [!code word:SystemAccount]
    // [!code highlight]
    pub account: SystemAccount<'info>,
}

Sysvar<'info, T>

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

#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    // [!code word:Sysvar]
    // [!code highlight:2]
    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

#[derive(Accounts)]
pub struct InstructionAccounts<'info> {
    // CHECK: No checks are performed
    // [!code word:UncheckedAccount]
    // [!code highlight]
    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
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>,
    // [!code word:Migration]
    // [!code highlight:5]
    #[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:

// 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,
})?;
// 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);
// 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;