Instruction Data (de)Serialization
Instruction data is the contract between clients and your program. Borsh enums are standard; manual opcodes work for tight control. Both require stable versioning discipline.
Recipe
#[derive(BorshSerialize, BorshDeserialize)]
pub enum MarketInstruction {
PlaceOrder { price: u64, qty: u64 },
}When to reach for this:
- Adding parameters to an instruction.
- Generating @solana/kit 7.0.0 client builders.
- Migrating instruction format across upgrades.
Working Example
#[derive(BorshSerialize, BorshDeserialize)]
pub enum MarketInstruction {
PlaceOrder { price: u64, qty: u64 },
CancelOrder { order_id: u64 },
}
pub fn parse(data: &[u8]) -> Result<MarketInstruction, ProgramError> {
MarketInstruction::deserialize(&mut &data[..]).map_err(|_| ProgramError::InvalidInstructionData)
}What this demonstrates:
- Variants carry typed fields.
- Single parse function centralizes errors.
- Clients mirror enum with same derive.
Deep Dive
Versioning
- Prefix
version: u8before body for breaking changes. - Never reorder enum variants in production programs.
Client Parity
Publish a instructions crate shared by program and TS/Rust clients.
Rust Notes
let data = ix.try_to_vec()?; // off-chain builderGotchas
- String in ix data - Unbounded length from user.. Fix: Cap string bytes or use fixed keys.
- Implicit padding - Manual structs may misalign.. Fix: Prefer Borsh or
#[repr(C)]with care. - Client drift - Old SDK sends wrong layout.. Fix: Version gate and document migration.
- Vec in instruction - Allocates on parse.. Fix: Prefer fixed arrays for CU-sensitive paths.
- Duplicate opcodes - Manual tables with collisions.. Fix: Centralize constants.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Shared crate + Codama | IDL-driven clients | Quick scripts |
Anchor #[program] | Auto serialization | Custom layouts |
| Raw byte slices | Single-field ix | Complex enums |
FAQs
Max instruction data size?
Transaction packet limits - keep ix small.
u8 discriminant enough?
For <256 variants yes.
i64 prices?
Supported - document endianness.
Pubkey in ix?
32 bytes in Borsh.
Optional fields?
Use Option<T> in Borsh.
Skip enum wrapper?
Only if single instruction program.
Hash instruction schema?
Use for upgrade audits.
Fuzz parsing?
Recommended in CI.
Invalid discriminant?
Returns parse error.
Anchor compatibility?
Different encoding if mixing - avoid.
JSON for ix?
Off-chain only; on-chain binary.
Test vectors?
Check in hex fixtures.
Related
- Borsh Serialization - Format
- The Entrypoint & Dispatch - Routing
- Building Instructions - Clients
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.