Zero-Copy Accounts
Zero-copy accounts use bytemuck layout and AccountLoader to avoid Borsh serialize/deserialize on large structs, saving CU for big state.
Recipe
#[account(zero_copy)]
pub struct Market {
pub bids: [u64; 1024],
}
#[derive(Accounts)]
pub struct Update<'info> {
#[account(mut)]
pub market: AccountLoader<'info, Market>,
}When to reach for this: Account data is large (order books, tick arrays) and Borsh costs too much CU.
Working Example
use anchor_lang::prelude::*;
#[account(zero_copy)]
#[repr(C)]
pub struct LargeState {
pub head: u64,
pub data: [u8; 10_000],
}
pub fn touch(ctx: Context<Touch>) -> Result<()> {
let mut market = ctx.accounts.state.load_mut()?;
market.head += 1;
Ok(())
}
#[derive(Accounts)]
pub struct Touch<'info> {
#[account(mut)]
pub state: AccountLoader<'info, LargeState>,
}What this demonstrates:
- zero_copy requires repr(C) layout
- AccountLoader::load_mut for mutation
- No INIT_SPACE Borsh sizing same way
- Discriminator still applies
Deep Dive
Layout Discipline
- Fixed-size arrays only unless using careful manual management.
- Alignment matters; follow bytemuck rules.
- Migrations harder than Borsh accounts.
CU Win
Avoid full account memcpy on each instruction.
Gotchas
- Unaligned fields - Undefined behavior.. Fix: Use repr(C) and bytemuck Pod.
- Mixing Borsh and zero_copy - Wrong loader type.. Fix: Pick one per account.
- Dynamic Vec in zero_copy - Not supported naively.. Fix: Fixed buffers or Borsh account.
- Forgot mut on loader account - Cannot persist.. Fix: Mark account mut.
- Upgrade layout without migration - Corrupts account.. Fix: Version and migrate.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Borsh Account<T> | Small configs | Large fixed buffers |
| Account compression off-chain | No on-chain book | On-chain order book |
FAQs
Anchor version?
0.32.1 across advanced topics.
When to use zero_copy?
Large fixed layouts when Borsh CU too high.
Is realloc reversible?
Not easily; plan forward growth.
Are remaining accounts in IDL?
No; document separately.
declare_program! needs what?
Callee IDL JSON at compile time.
Can Anchor CPI to Pinocchio?
Yes with correct account metas.
Feature flags and IDL?
Avoid different IDL per feature on mainnet.
How to measure CU?
LiteSVM, Surfpool, devnet simulation.
Custom account types common?
No; last resort.
Next steps?
See Related links for zero copy.
Related
- Zero-Copy Accounts - serialization view
- CU & Size Optimization - CU
- Custom Account Types - traits
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.