> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/solana-foundation/anchor/llms.txt
> Use this file to discover all available pages before exploring further.

# Zero-Copy Deserialization

> Learn how to use zero-copy deserialization with the

Zero-copy deserialization allows programs to read and write account data **directly from memory** without copying or deserializing it. This is essential for handling large accounts efficiently on Solana.

## Why Use Zero-Copy?

Traditional Anchor accounts (`Account&lt;T&gt;`) copy data from the account into a heap-allocated struct during deserialization. This has significant limitations:

**Limitations:**

* **Stack Limit**: 4KB maximum
* **Heap Limit**: 32KB maximum
* **Compute Cost**: Deserialization consumes compute units proportional to data size
* **Memory Overhead**: Data is duplicated in memory

**Zero-Copy Benefits:**

* **Direct Access**: Casts raw account bytes to struct type (no copying)
* **Larger Accounts**: Supports accounts up to 10MB (10,485,760 bytes)
* **Lower Compute**: \~90% reduction in CU usage for large accounts
* **In-Place Updates**: Modifies account data directly

### Performance Comparison

| Account Size | Account\<T> | AccountLoader\<T> | Improvement |
| ------------ | ----------- | ----------------- | ----------- |
| 1 KB         | \~8,000 CU  | \~1,500 CU        | 81% faster  |
| 10 KB        | \~50,000 CU | \~5,000 CU        | 90% faster  |
| 100 KB       | Too large   | \~12,000 CU       | Possible    |
| 1 MB         | Impossible  | \~25,000 CU       | Possible    |

## When to Use Zero-Copy

<CardGroup cols={2}>
  <Card title="Use Zero-Copy For" icon="circle-check">
    * Accounts larger than 1KB
    * Arrays with many elements (orderbooks, event queues)
    * High-frequency read/write operations
    * Compute-sensitive programs
    * Fixed-size data structures
  </Card>

  <Card title="Use Regular Account<T> For" icon="circle-xmark">
    * Small accounts (\< 1KB)
    * Dynamic data structures (Vec, String, HashMap)
    * Frequently changing schemas
    * Simple state that doesn't need optimization
  </Card>
</CardGroup>

## Basic Usage

### 1. Add Bytemuck Dependency

Add `bytemuck` to enable zero-copy features:

```toml title="Cargo.toml" theme={null}
[dependencies]
anchor-lang = "0.32.1"
bytemuck = { version = "1.20.0", features = ["min_const_generics"] }
```

The `min_const_generics` feature allows working with arrays of any size.

### 2. Define a Zero-Copy Account

Use the `#[account(zero_copy)]` attribute:

```rust theme={null}
use anchor_lang::prelude::*;

#[account(zero_copy)]
pub struct Data {
    pub data: [u8; 10232],  // 10240 bytes - 8 byte discriminator
}
```

The attribute automatically implements required traits:

* `Copy` - Allows bitwise copying
* `Clone` - Enables cloning
* `bytemuck::Zeroable` - Allows creation from zeroed bytes
* `bytemuck::Pod` - "Plain Old Data" marker
* `#[repr(C)]` - C-compatible memory layout

### 3. Use AccountLoader

Replace `Account<'info, T>` with `AccountLoader<'info, T>`:

```rust theme={null}
#[derive(Accounts)]
pub struct ProcessData<'info> {
    #[account(mut)]
    pub data_account: AccountLoader<'info, Data>,
}
```

## Account Operations

### Initialize (Small Accounts ≤ 10240 bytes)

For accounts up to 10,240 bytes, use the `init` constraint:

```rust theme={null}
#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(
        init,
        payer = payer,
        space = 8 + 10232,  // 8 bytes discriminator + data
    )]
    pub data_account: AccountLoader<'info, Data>,
    #[account(mut)]
    pub payer: Signer<'info>,
    pub system_program: Program<'info, System>,
}

pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
    let data = &mut ctx.accounts.data_account.load_init()?;
    data.data = [1; 10232];
    Ok(())
}
```

<Callout type="info">
  The `init` constraint is limited to 10,240 bytes due to CPI limitations when calling the System Program.
</Callout>

### Initialize (Large Accounts > 10240 bytes)

For accounts larger than 10,240 bytes, use the `zero` constraint and create the account separately:

**Program:**

```rust theme={null}
#[account(zero_copy)]
pub struct LargeData {
    pub data: [u8; 10_485_752],  // 10MB - 8 bytes discriminator
}

#[derive(Accounts)]
pub struct InitializeLarge<'info> {
    #[account(zero)]  // Verifies discriminator not set
    pub data_account: AccountLoader<'info, LargeData>,
}

pub fn initialize_large(ctx: Context<InitializeLarge>) -> Result<()> {
    let data = &mut ctx.accounts.data_account.load_init()?;
    data.data = [1; 10_485_752];
    Ok(())
}
```

**Client (create account first):**

```typescript theme={null}
import * as anchor from "@anchor-lang/core";

const dataAccount = anchor.web3.Keypair.generate();
const space = 10_485_760;  // 10MB max account size
const lamports = await program.provider.connection
  .getMinimumBalanceForRentExemption(space);

// Create account via System Program
const createAccountIx = anchor.web3.SystemProgram.createAccount({
  fromPubkey: program.provider.publicKey,
  newAccountPubkey: dataAccount.publicKey,
  space,
  lamports,
  programId: program.programId,
});

// Initialize account in your program
const initializeIx = await program.methods
  .initializeLarge()
  .accounts({ dataAccount: dataAccount.publicKey })
  .instruction();

// Send both instructions
const tx = new anchor.web3.Transaction()
  .add(createAccountIx)
  .add(initializeIx);

await program.provider.sendAndConfirm(tx, [dataAccount]);
```

### Read Data

Use `load()` for read-only access:

```rust theme={null}
#[derive(Accounts)]
pub struct ReadData<'info> {
    pub data_account: AccountLoader<'info, Data>,
}

pub fn read_data(ctx: Context<ReadData>) -> Result<()> {
    let data = ctx.accounts.data_account.load()?;
    msg!("First 10 bytes: {:?}", &data.data[..10]);
    Ok(())
}
```

### Update Data

Use `load_mut()` for mutable access:

```rust theme={null}
#[derive(Accounts)]
pub struct UpdateData<'info> {
    #[account(mut)]
    pub data_account: AccountLoader<'info, Data>,
}

pub fn update_data(ctx: Context<UpdateData>) -> Result<()> {
    let data = &mut ctx.accounts.data_account.load_mut()?;
    data.data = [2; 10232];
    Ok(())
}
```

## Advanced Patterns

### Nested Zero-Copy Types

Define reusable zero-copy types with `#[zero_copy]` (without `account`):

```rust theme={null}
#[account(zero_copy)]
pub struct OrderBook {
    pub market: Pubkey,
    pub bids: [Order; 1000],
    pub asks: [Order; 1000],
}

#[zero_copy]
pub struct Order {
    pub trader: Pubkey,
    pub price: u64,
    pub quantity: u64,
}
```

### Accessor Methods for Byte Arrays

Since zero-copy uses `#[repr(packed)]`, field references are unsafe. Use `#[accessor]` for safe getters/setters:

```rust theme={null}
#[account(zero_copy)]
pub struct Config {
    pub authority: Pubkey,
    #[accessor(Pubkey)]  // Generate safe accessor methods
    pub secondary_authority: [u8; 32],
}

// Usage:
pub fn use_accessors(ctx: Context<UseConfig>) -> Result<()> {
    let config = &mut ctx.accounts.config.load_mut()?;
    
    // Safe getter
    let secondary = config.get_secondary_authority();
    msg!("Secondary authority: {}", secondary);
    
    // Safe setter
    let new_authority = Pubkey::new_unique();
    config.set_secondary_authority(&new_authority);
    
    Ok(())
}
```

### Zero-Copy with PDAs

Zero-copy accounts work seamlessly with PDAs:

```rust theme={null}
#[derive(Accounts)]
pub struct InitializePDA<'info> {
    #[account(
        init,
        payer = authority,
        space = 8 + std::mem::size_of::<Data>(),
        seeds = [b"data", authority.key().as_ref()],
        bump,
    )]
    pub data_account: AccountLoader<'info, Data>,
    #[account(mut)]
    pub authority: Signer<'info>,
    pub system_program: Program<'info, System>,
}
```

### Separate Types for RPC Parameters

Zero-copy types cannot derive `AnchorSerialize`/`AnchorDeserialize`. Use separate types for instruction parameters:

```rust theme={null}
// Zero-copy account
#[zero_copy]
pub struct Event {
    pub from: Pubkey,
    pub data: u64,
}

// RPC parameter type
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct EventParams {
    pub from: Pubkey,
    pub data: u64,
}

impl From<EventParams> for Event {
    fn from(params: EventParams) -> Self {
        Event {
            from: params.from,
            data: params.data,
        }
    }
}

pub fn create_event(
    ctx: Context<CreateEvent>,
    params: EventParams,
) -> Result<()> {
    let event = &mut ctx.accounts.event.load_init()?;
    *event = params.into();
    Ok(())
}
```

## Common Pitfalls

### 1. Forgetting the Account Discriminator

Always add 8 bytes for the account discriminator:

```rust theme={null}
//    Wrong - missing discriminator
space = std::mem::size_of::<Data>()

//    Correct - includes discriminator
space = 8 + std::mem::size_of::<Data>()
```

### 2. Using Dynamic Types

Zero-copy requires all fields to be `Copy` types:

```rust theme={null}
//    Invalid - Vec is not Copy
#[account(zero_copy)]
pub struct InvalidData {
    pub items: Vec<u64>,  // Error!
    pub name: String,     // Error!
}

//    Valid - Fixed-size arrays
#[account(zero_copy)]
pub struct ValidData {
    pub items: [u64; 100],
    pub name: [u8; 32],
}
```

### 3. Using load\_init vs load\_mut

Use `load_init()` only for **first-time initialization**:

```rust theme={null}
// First initialization - sets discriminator
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
    let data = &mut ctx.accounts.data_account.load_init()?;
    data.data = [1; 10232];
    Ok(())
}

// Subsequent updates
pub fn update(ctx: Context<Update>) -> Result<()> {
    let data = &mut ctx.accounts.data_account.load_mut()?;
    data.data = [2; 10232];
    Ok(())
}
```

### 4. Not Validating Array Indices

Always validate array indices to prevent panics:

```rust theme={null}
pub fn update_item(
    ctx: Context<Update>,
    index: u32,
    value: u64,
) -> Result<()> {
    let data = &mut ctx.accounts.data_account.load_mut()?;
    
    require!(
        (index as usize) < data.items.len(),
        ErrorCode::IndexOutOfBounds
    );
    
    data.items[index as usize] = value;
    Ok(())
}
```

## Real-World Use Cases

### Event Queue Pattern

Store large sequences of events efficiently:

```rust theme={null}
#[account(zero_copy)]
pub struct EventQueue {
    pub head: u64,
    pub count: u64,
    pub events: [Event; 10000],
}

#[zero_copy]
pub struct Event {
    pub timestamp: i64,
    pub user: Pubkey,
    pub event_type: u8,
    pub data: [u8; 32],
}
```

**Used by**: Trading protocols, audit logs, messaging systems

### Order Book Pattern

Efficient storage for trading pairs:

```rust theme={null}
#[account(zero_copy)]
pub struct OrderBook {
    pub market: Pubkey,
    pub bid_count: u32,
    pub ask_count: u32,
    pub bids: [Order; 1000],
    pub asks: [Order; 1000],
}

#[zero_copy]
pub struct Order {
    pub trader: Pubkey,
    pub price: u64,
    pub size: u64,
    pub timestamp: i64,
}
```

**Used by**: DEXs (Serum, Mango), NFT marketplaces

### Ring Buffer Pattern

Circular buffer for fixed-size history:

```rust theme={null}
#[account(zero_copy)]
pub struct RingBuffer {
    pub head: u64,
    pub tail: u64,
    pub capacity: u64,
    pub items: [Item; 1000],
}

pub fn push_item(
    ctx: Context<PushItem>,
    item: Item,
) -> Result<()> {
    let buffer = &mut ctx.accounts.buffer.load_mut()?;
    
    let index = buffer.head % buffer.capacity;
    buffer.items[index as usize] = item;
    buffer.head = buffer.head.wrapping_add(1);
    
    Ok(())
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Calculate Space Requirements Accurately">
    Use `std::mem::size_of` to calculate exact sizes:

    ```rust theme={null}
    space = 8 + std::mem::size_of::<Data>()
    ```
  </Accordion>

  <Accordion title="Document Size Constraints">
    Comment size calculations for clarity:

    ```rust theme={null}
    #[account(zero_copy)]
    pub struct Data {
        // Total: 10240 bytes
        pub header: [u8; 32],      // 32 bytes
        pub items: [Item; 100],    // 100 * 100 = 10000 bytes
        pub footer: [u8; 208],     // 208 bytes
    }  // Total: 32 + 10000 + 208 = 10240
    ```
  </Accordion>

  <Accordion title="Use Type Aliases for Large Arrays">
    Make code more readable:

    ```rust theme={null}
    type OrderArray = [Order; 1000];

    #[account(zero_copy)]
    pub struct OrderBook {
        pub bids: OrderArray,
        pub asks: OrderArray,
    }
    ```
  </Accordion>

  <Accordion title="Test with Maximum Sizes">
    Test with large accounts to verify compute budget:

    ```rust theme={null}
    #[test]
    fn test_large_account() {
        // Test with maximum data size
        let data = Data {
            data: [255; 10_485_752],
        };
        // Verify operations succeed
    }
    ```
  </Accordion>
</AccordionGroup>

## Resources

* [AccountLoader Source](https://github.com/coral-xyz/anchor/blob/master/lang/src/accounts/account_loader.rs)
* [Zero-Copy Attribute](https://github.com/coral-xyz/anchor/blob/master/lang/attribute/account/src/lib.rs)
* [Bytemuck Documentation](https://docs.rs/bytemuck/latest/bytemuck/)
* [Solana Account Model](https://solana.com/docs/core/accounts)
