Borsh Serialization
Borsh (Binary Object Representation Serializer for Hashing) is the canonical encoding for Solana instruction data and most account structs. It is deterministic, little-endian, and length-prefixed for collections.
Search across all documentation pages
Borsh (Binary Object Representation Serializer for Hashing) is the canonical encoding for Solana instruction data and most account structs. It is deterministic, little-endian, and length-prefixed for collections.
use borsh::{BorshDeserialize, BorshSerialize};
#[derive(BorshSerialize, BorshDeserialize)]
pub struct Deposit {
pub amount: u64,
}
let ix_data = Deposit { amount: 1_000 }.try_to_vec()?;When to reach for this:
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
program_error::ProgramError,
pubkey::Pubkey,
};
#[derive(BorshSerialize, BorshDeserialize)]
pub enum VaultInstruction {
Deposit { amount: u64 },
Withdraw { amount: u64 },
}
#[derive(BorshSerialize, BorshDeserialize)]
pub struct VaultState {
pub total: u64,
}
pub const VAULT_LEN: usize = 8;
pub fn process_deposit(
accounts: &[AccountInfo],
amount: u64,
) -> ProgramResult {
let iter = &mut accounts.iter();
let vault = next_account_info(iter)?;
let mut state = VaultState::deserialize(&mut &vault.data.borrow()[..])
.map_err(|_| ProgramError::InvalidAccountData)?;
state.total = state
.total
.checked_add(amount)
.ok_or(ProgramError::InvalidArgument)?;
state.serialize(&mut &mut vault.data.borrow_mut()[..])?;
Ok(())
}What this demonstrates:
u8 discriminant by default.String and Vec<T> prefix with a u32 length.| Field | Bytes |
|---|---|
u64 | 8 |
Pubkey | 32 |
bool | 1 |
String | 4 + utf8 len |
// Use try_to_vec for instruction data; check account.data.len() >= size.
const STATE_LEN: usize = core::mem::size_of::<u64>();data.len() is insufficient. Fix: Allocate exact space at account creation and store LEN constant.borrow_mut.| Alternative | Use When | Don't Use When |
|---|---|---|
| bytemuck / zero-copy | Large fixed structs | Variable strings or vectors |
Anchor Account<T> | Automatic layout checks | Raw control or minimal CU |
| Manual byte packing | Ultra-tight layouts | Maintainability |
Anchor 0.32.1 uses Borsh for account data by default.
Little-endian for primitives.
1-byte tag (0/1) plus inner value when Some.
Deserializer stops at struct end; validate total length for security.
10 MB cap per account - still pay rent and CUs.
Serialize default struct on host or use space constant.
Allowed but grows rent; prefer fixed-capacity arrays for games/order books.
32 raw bytes - same as on wire.
Borsh is canonical for Solana; bincode is not cross-language stable here.
Always check owner, discriminator, and length first.
Supported - 16 bytes little-endian.
Use try_to_vec then deserialize in unit tests on host.
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 16, 2026