Documentation Index
Fetch the complete documentation index at: https://mintlify.com/solana-foundation/anchor/llms.txt
Use this file to discover all available pages before exploring further.
Instruction handlers are the core functions that execute when your program receives an instruction. In Anchor, these handlers are defined as public functions within a module annotated with the #[program] attribute.
Basic Structure
Every instruction handler follows this pattern:
pub fn handler_name(ctx: Context<AccountsStruct>, ...args) -> Result<()> {
// Instruction logic
Ok(())
}
The first parameter is always a Context<T> type where T is a struct that defines the accounts required by the instruction.
The Context Type
The Context type provides access to non-argument inputs needed by the instruction:
pub struct Context<'a, 'b, 'c, 'info, T: Bumps> {
/// Currently executing program id
pub program_id: &'a Pubkey,
/// Deserialized accounts
pub accounts: &'b mut T,
/// Remaining accounts given but not deserialized or validated
pub remaining_accounts: &'c [AccountInfo<'info>],
/// Bump seeds found during constraint validation
pub bumps: T::Bumps,
}
Accessing Context Fields
program_id: The currently executing program’s address
pub fn check_program(ctx: Context<CheckProgram>) -> Result<()> {
msg!("Program ID: {}", ctx.program_id);
Ok(())
}
accounts: Access to the deserialized and validated accounts
pub fn update_data(ctx: Context<UpdateData>, new_value: u64) -> Result<()> {
ctx.accounts.my_account.data = new_value;
Ok(())
}
remaining_accounts: Accounts passed to the instruction but not specified in the Accounts struct
pub fn process_remaining(ctx: Context<ProcessData>) -> Result<()> {
for account in ctx.remaining_accounts.iter() {
msg!("Remaining account: {}", account.key);
}
Ok(())
}
bumps: PDA bump seeds automatically discovered during validation
pub fn use_bump(ctx: Context<UsePda>) -> Result<()> {
let bump = ctx.bumps.my_pda;
msg!("PDA bump: {}", bump);
Ok(())
}
Complete Example
Here’s a complete example demonstrating instruction handlers with various parameter types:
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod example {
use super::*;
pub fn initialize(ctx: Context<Initialize>, initial_value: u64) -> Result<()> {
let account = &mut ctx.accounts.my_account;
account.data = initial_value;
account.authority = ctx.accounts.authority.key();
msg!("Initialized with value: {}", initial_value);
Ok(())
}
pub fn update(ctx: Context<Update>, new_value: u64) -> Result<()> {
let account = &mut ctx.accounts.my_account;
require!(new_value > account.data, ErrorCode::InvalidValue);
account.data = new_value;
msg!("Updated to: {}", new_value);
Ok(())
}
pub fn close_account(ctx: Context<CloseAccount>) -> Result<()> {
msg!("Closing account: {}", ctx.accounts.my_account.key());
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(
init,
payer = authority,
space = 8 + MyAccount::INIT_SPACE
)]
pub my_account: Account<'info, MyAccount>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Update<'info> {
#[account(
mut,
has_one = authority
)]
pub my_account: Account<'info, MyAccount>,
pub authority: Signer<'info>,
}
#[derive(Accounts)]
pub struct CloseAccount<'info> {
#[account(
mut,
has_one = authority,
close = authority
)]
pub my_account: Account<'info, MyAccount>,
#[account(mut)]
pub authority: Signer<'info>,
}
#[account]
#[derive(InitSpace)]
pub struct MyAccount {
pub data: u64,
pub authority: Pubkey,
}
#[error_code]
pub enum ErrorCode {
#[msg("New value must be greater than current value")]
InvalidValue,
}
Instruction Arguments
You can pass additional arguments to instruction handlers after the Context parameter:
pub fn transfer(
ctx: Context<Transfer>,
amount: u64,
memo: String,
) -> Result<()> {
// Use amount and memo
msg!("Transferring {} with memo: {}", amount, memo);
Ok(())
}
Supported Argument Types
- Primitive types:
u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, bool
String and Vec<T>
Pubkey
- Custom structs that implement
AnchorSerialize and AnchorDeserialize
Return Values
All instruction handlers must return Result<()>. To return an error, use the err! macro or require! macro:
pub fn validate(ctx: Context<Validate>, value: u64) -> Result<()> {
require!(value <= 100, ErrorCode::ValueTooLarge);
Ok(())
}
Best Practices
- Keep handlers focused: Each instruction should have a single, clear purpose
- Validate early: Perform validation checks at the start of the handler
- Use descriptive names: Choose clear, action-oriented names for your handlers
- Log important events: Use
msg! to log important state changes
- Handle errors gracefully: Use custom errors to provide clear feedback
CPI Context
For cross-program invocations, use CpiContext instead of Context:
pub fn do_cpi(ctx: Context<DoCpi>, amount: u64) -> Result<()> {
let cpi_accounts = Transfer {
from: ctx.accounts.from.to_account_info(),
to: ctx.accounts.to.to_account_info(),
authority: ctx.accounts.authority.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
token::transfer(cpi_ctx, amount)?;
Ok(())
}
For CPIs with PDA signers:
pub fn do_cpi_with_signer(ctx: Context<DoCpiWithSigner>) -> Result<()> {
let bump = ctx.bumps.pda_authority;
let seeds = &[
b"authority",
&[bump],
];
let signer_seeds = &[&seeds[..]];
let cpi_ctx = CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
cpi_accounts,
signer_seeds,
);
token::transfer(cpi_ctx, amount)?;
Ok(())
}