The Entrypoint & Dispatch
The entrypoint is the single RPC surface of your program. Dispatch parses instruction_data, validates context, and routes to handlers with consistent error semantics.
Search across all documentation pages
The entrypoint is the single RPC surface of your program. Dispatch parses instruction_data, validates context, and routes to handlers with consistent error semantics.
entrypoint!(process_instruction);
let ix = MyInstruction::try_from_slice(data)?;
match ix { /* ... */ }When to reach for this:
use borsh::BorshDeserialize;
use solana_program::{account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey};
entrypoint!(process_instruction);
#[derive(BorshDeserialize)]
pub enum MyInstruction { Init, Increment }
pub fn process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
if data.is_empty() { return Err(ProgramError::InvalidInstructionData); }
let ix = MyInstruction::deserialize(&mut &data[..]).map_err(|_| ProgramError::InvalidInstructionData)?;
match ix {
MyInstruction::Init => init(program_id, accounts),
MyInstruction::Increment => increment(program_id, accounts),
}
}What this demonstrates:
InvalidInstructionData.program_id for PDA derivation.| Style | Pros |
|---|---|
| Borsh enum | Idiomatic, compact |
| Manual opcode byte | C-like, explicit versioning |
Anchor #[program] | IDL + constraints auto |
// Keep handlers thin; push shared checks to validators.data.is_empty() when no default instruction exists.program_id through all paths.deserialize Result mapping.| Alternative | Use When | Don't Use When |
|---|---|---|
| Anchor dispatch | IDL + account validation | Minimal binary |
| Pinocchio entrypoint | Lowest overhead | Macro ecosystem |
| Opcode tables | Firmware-style programs | Simple enums |
No - one per program binary.
Inside each handler after shared header parse.
Common pattern: first byte is schema version.
Publish instruction crate for @solana/kit builders.
Serialize ix bytes in LiteSVM tests.
Borsh returns error - map to InvalidInstructionData.
Document and use sentinel or separate instructions.
Bounded by transaction size (~1232 bytes).
Advanced pattern to avoid allocator on entry.
Never trust callee to parse your data.
OK for dev; avoid in production hot path.
Use fn pointers only if binary size wins matter.
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.
Reviewed by Chris St. John·Last updated Jul 19, 2026