In Solana, all state is stored in accounts. Anchor provides strongly-typed account wrappers that automatically handle validation and deserialization.
Account types
Anchor provides several account types that handle common validation patterns:
Account<‘info, T>
The most common account type that wraps a custom account type with automatic validation.
#[derive( Accounts )]
pub struct UpdateData & lt ;' info & gt ; {
#[account( mut )]
pub my_account : Account <' info , MyAccount >,
pub authority : Signer & lt ;' info & gt ;,
}
#[account]
pub struct MyAccount {
pub data : u64 ,
pub authority : Pubkey ,
}
The Account<'info, T> type automatically:
Deserializes the account data into type T
Checks the account owner matches the program ID
Validates the account discriminator matches type T
Signer<‘info>
Verifies that an account signed the transaction.
#[derive( Accounts )]
pub struct Initialize & lt ;' info & gt ; {
#[account( mut )]
pub authority : Signer & lt ;' info & gt ;,
pub system_program : Program <' info , System >,
}
The Signer type automatically checks that authority.is_signer is true.
SystemAccount<‘info>
Represents a native Solana account owned by the System Program.
#[derive( Accounts )]
pub struct TransferSol & lt ;' info & gt ; {
#[account( mut )]
pub from : SystemAccount & lt ;' info & gt ;,
#[account( mut )]
pub to : SystemAccount & lt ;' info & gt ;,
}
Program type
Verifies that an account is an executable program.
#[derive( Accounts )]
pub struct Initialize & lt ;' info & gt ; {
pub system_program : Program <' info , System >,
pub token_program : Program <' info , Token >,
}
The Program type checks that the account is executable.
UncheckedAccount<‘info>
Raw AccountInfo with no automatic checks. Use with caution.
#[derive( Accounts )]
pub struct Dangerous & lt ;' info & gt ; {
/// CHECK: This is not dangerous because we don't read or write from this account
pub unchecked : UncheckedAccount & lt ;' info & gt ;,
}
Always add a /// CHECK: doc comment explaining why the account doesn’t need checks.
Interface types
For Token-2022 compatibility, use interface types:
use anchor_spl :: token_interface :: { TokenAccount , Mint , TokenInterface };
#[derive( Accounts )]
pub struct Transfer & lt ;' info & gt ; {
#[account( mut )]
pub from : InterfaceAccount <' info , TokenAccount >,
#[account( mut )]
pub to : InterfaceAccount <' info , TokenAccount >,
pub token_program : Interface <' info , TokenInterface >,
}
Account validation
Anchor provides constraints for automatic account validation:
Initialization constraints
#[derive( Accounts )]
pub struct Initialize & lt ;' info & gt ; {
#[account(
init,
payer = user,
space = 8 + 32 + 8
)]
pub my_account : Account <' info , MyAccount >,
#[account( mut )]
pub user : Signer & lt ;' info & gt ;,
pub system_program : Program <' info , System >,
}
Mutable accounts
#[account( mut )]
pub my_account : Account <' info , MyAccount >,
Field validation
#[derive( Accounts )]
pub struct Update & lt ;' info & gt ; {
#[account(
mut ,
has_one = authority,
constraint = my_account . count < 100
)]
pub my_account : Account <' info , MyAccount >,
pub authority : Signer & lt ;' info & gt ;,
}
The has_one = authority constraint checks that my_account.authority == authority.key().
PDA validation
#[derive( Accounts )]
pub struct Initialize & lt ;' info & gt ; {
#[account(
init,
payer = user,
space = 8 + 32,
seeds = [ b"vault" , user . key() . as_ref()],
bump
)]
pub vault : Account <' info , Vault >,
#[account( mut )]
pub user : Signer & lt ;' info & gt ;,
pub system_program : Program <' info , System >,
}
Accessing account data
Access account fields directly through the validated account:
pub fn update ( ctx : Context < Update >, new_value : u64 ) -> Result <()> {
let account = & mut ctx . accounts . my_account;
account . data = new_value ;
account . authority = ctx . accounts . authority . key ();
Ok (())
}
Account lifetime
The 'info lifetime indicates that account references are valid for the duration of the instruction:
#[derive( Accounts )]
pub struct MyAccounts & lt ;' info & gt ; {
pub account : Account <' info , MyAccount >,
}
This ensures accounts can’t be used after the instruction completes.
Complete example
use anchor_lang :: prelude ::* ;
declare_id! ( "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" );
#[program]
pub mod accounts_example {
use super ::* ;
pub fn initialize ( ctx : Context < Initialize >, data : u64 ) -> Result <()> {
let account = & mut ctx . accounts . my_account;
account . data = data ;
account . authority = ctx . accounts . authority . key ();
msg! ( "Account initialized with data: {}" , data );
Ok (())
}
pub fn update ( ctx : Context < Update >, new_data : u64 ) -> Result <()> {
let account = & mut ctx . accounts . my_account;
require! (
account . authority == ctx . accounts . authority . key (),
ErrorCode :: Unauthorized
);
account . data = new_data ;
msg! ( "Account updated to: {}" , new_data );
Ok (())
}
}
#[derive( Accounts )]
pub struct Initialize & lt ;' info & gt ; {
#[account(
init,
payer = authority,
space = 8 + 8 + 32
)]
pub my_account : Account <' info , MyAccount >,
#[account( mut )]
pub authority : Signer & lt ;' info & gt ;,
pub system_program : Program <' info , System >,
}
#[derive( Accounts )]
pub struct Update & lt ;' info & gt ; {
#[account(
mut ,
has_one = authority
)]
pub my_account : Account <' info , MyAccount >,
pub authority : Signer & lt ;' info & gt ;,
}
#[account]
pub struct MyAccount {
pub data : u64 ,
pub authority : Pubkey ,
}
#[error_code]
pub enum ErrorCode {
#[msg( "You are not authorized to perform this action" )]
Unauthorized ,
}
Next steps
Account constraints Complete reference of all account constraints
PDAs Learn about Program Derived Addresses