Data Accounts & Layout
Program state is a byte buffer in an account's data field. Anchor 0.32.1 uses Borsh serialization with an 8-byte discriminator prefix. Correct layout sizing prevents init failures and deserialization exploits.
Search across all documentation pages
Program state is a byte buffer in an account's data field. Anchor 0.32.1 uses Borsh serialization with an 8-byte discriminator prefix. Correct layout sizing prevents init failures and deserialization exploits.
#[account]
#[derive(InitSpace)]
pub struct Market {
pub authority: Pubkey,
pub price: u64,
pub name: String, // max 32 in InitSpace via #[max_len(32)]
}
// space = 8 + Market::INIT_SPACEWhen to reach for this:
init space for new account typesuse anchor_lang::prelude::*;
#[account]
#[derive(InitSpace)]
pub struct UserStats {
pub authority: Pubkey,
#[max_len(16)]
pub nickname: String,
pub wins: u32,
pub losses: u32,
}
#[derive(Accounts)]
pub struct InitStats<'info> {
#[account(
init,
payer = authority,
space = 8 + UserStats::INIT_SPACE,
seeds = [b"stats", authority.key().as_ref()],
bump,
)]
pub stats: Account<'info, UserStats>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[program]
pub mod stats {
use super::*;
pub fn init(ctx: Context<InitStats>, nickname: String) -> Result<()> {
require!(nickname.len() <= 16, StatsError::NameTooLong);
ctx.accounts.stats.nickname = nickname;
ctx.accounts.stats.wins = 0;
ctx.accounts.stats.losses = 0;
Ok(())
}
}What this demonstrates:
InitSpace computes Borsh size including max_len strings[ 8 bytes discriminator ][ Borsh field 1 ][ field 2 ]...Pubkey 32, u64 8, bool 1String/Vec: 4-byte length + max content bytesversion: u8 as first field after discriminator#[max_len(N)] in Anchor 0.32.1.InitSpace derive.| Alternative | Use When | Don't Use When |
|---|---|---|
| Borsh + Anchor | Default program state | Multi-MB datasets |
Zero-copy (AccountLoader) | Large structs, CU savings | Small simple structs |
bytemuck POD | Fixed-size packed layouts | Variable-length strings |
| External compression | NFT-scale data | Hot mutable fields |
First 8 bytes of SHA256("account:StructName") in Anchor - identifies account type.
Sum Borsh sizes + 8. For String(max N): 4 + N bytes.
No - breaks deserialization of existing accounts.
Anchor 0.32.1 derive macro calculating INIT_SPACE constant for space attribute.
Use Codama/@solana/kit clients from IDL, or manual buffer reads matching Borsh layout.
10 MB per account in Agave 4.1.1 - practical limits lower due to rent/CU.
Wasteful and unbounded. Use typed binary layouts.
Borsh uses 1-byte variant tag + size of largest variant payload.
#[account(zero_copy)] maps struct directly to account bytes without deserialize alloc.
Only for extreme CU optimization. Borsh default layout is fine for most programs.
Use Option<T> in Borsh (1 byte tag + T) or sentinel values.
No - each Anchor account type has a unique discriminator.
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