Skip to main content

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

  1. Keep handlers focused: Each instruction should have a single, clear purpose
  2. Validate early: Perform validation checks at the start of the handler
  3. Use descriptive names: Choose clear, action-oriented names for your handlers
  4. Log important events: Use msg! to log important state changes
  5. 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(())
}