Claude Skill

solana-development

Build, test, deploy, and audit Solana programs with Anchor or native Rust, and build with ZK Compression (Light Protocol). Use when developing Solana smart contracts, implementing token operations, optimizing compute, deploying to networks, auditing programs for vulnerabilities,

LLM Mart · 0 points · 23 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download tenequm-skills-skills_solana-development-1ff2284.zip · 213 KB
Part of tenequm/skills — 25 skills

Install

skills CLI npx skills add https://github.com/tenequm/skills/tree/main/skills/solana-development
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install tenequm-skills@llmmart
Git git clone https://github.com/tenequm/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole tenequm/skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Solana

Everything for building on Solana: developing programs (Anchor or native Rust), auditing them for security, and building with ZK Compression. All three share the same core model - accounts, PDAs, CPIs, tokens - and differ only in abstraction level and goal.

What this skill covers

Area Use when Jump to
Development Writing programs, tokens, tests, deployments Development
Security & Auditing Reviewing for vulnerabilities, writing exploits, audit reports Security and Auditing
ZK Compression Rent-free tokens/PDAs at scale via Light Protocol ZK Compression

Development

Build Solana programs with Anchor (recommended) or native Rust. Both share accounts, PDAs, CPIs, and tokens; they differ in syntax and abstraction.

Quick Start

Recommended: Anchor Framework

Macros and tooling that cut boilerplate and generate TypeScript clients:

use anchor_lang::prelude::*;

declare_id!("YourProgramID");

#[program]
pub mod my_program {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
        ctx.accounts.account.data = data;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = user, space = 8 + 8)]
    pub account: Account<'info, MyAccount>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[account]
pub struct MyAccount {
    pub data: u64,
}
cargo install --git https://github.com/coral-xyz/anchor avm --locked --force
avm install latest && avm use latest
anchor init my_project && cd my_project && anchor build && anchor test

→ See references/anchor.md for the complete Anchor guide

Advanced: Native Rust

Maximum control, optimization potential, and deeper runtime understanding:

use solana_program::{
    account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
    pubkey::Pubkey, msg,
};

entrypoint!(process_instruction);

pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    msg!("Processing instruction");
    // Manual account parsing, validation, and instruction routing
    Ok(())
}
cargo new my_program --lib
cd my_program   # configure Cargo.toml (see native-rust.md)
cargo build-sbf

→ See references/native-rust.md for the complete native Rust guide

When to use which

Your need Approach Reason
Standard DeFi/NFT program Anchor Faster, proven patterns
TypeScript client needed Anchor Auto-generates IDL + types
New to Solana Anchor Gentler learning curve
Compute optimization critical Native Rust Direct control, no overhead
Smallest program size Native Rust No abstraction layer
Learning fundamentals Native Rust Understand the platform deeply

You can also start with Anchor for speed, then optimize hot paths with native patterns. Both can coexist in one workspace.

Reference map

Foundations

  • accounts.md - Account model, ownership, rent, validation
  • pda.md - Program Derived Addresses: derivation, canonical bumps, signing
  • cpi.md - Cross-Program Invocations safely

Tokens

Testing

Deployment

Client

  • client-development.md - dApp client: wallet connections, React hooks, SOL/SPL transfers, transaction management (framework-kit and @solana/kit 6.x)

Implementation details

Advanced

Low-level

Common tasks

Task Pointer
New program Anchor: anchor init / Native: cargo new --lib → anchor.md, native-rust.md
Initialize a PDA pda.md
Transfer SPL tokens tokens-operations.md
Fast unit tests Mollusk → testing-frameworks.md
Local mainnet fork surfpool start → surfpool.md
Deploy to devnet deployment.md
Production verified build solana-verify build → production-deployment.md
Optimize compute compute-optimization.md
Handle 40+ accounts Address Lookup Tables → versioned-transactions.md
Offline signing Durable nonces → durable-nonces.md

Security and Auditing

Systematic security review for Solana programs (Anchor or native Rust). The core principle: attackers can pass arbitrary accounts to any instruction, so there are no implicit guarantees - validate everything, trust nothing.

Review process

  1. Initial assessment - Framework (Anchor vs native), Anchor version, dependencies (oracles, external programs), instruction count, account types, program purpose.
  2. Systematic review - For each instruction, check in order: account validation (signer/owner/writable/init), arithmetic safety (checked_*), PDA security (canonical bumps, seed uniqueness), CPI security (validated targets), oracle/external data (staleness, status). → security-checklists.md
  3. Vulnerability pattern detection - Type cosplay, account reloading, improper closing, missing lamports/ownership checks, PDA substitution, arbitrary CPI, overflow/underflow. → vulnerability-patterns.md
  4. Architecture and testing review - PDA design, space/rent, error handling, event emission, compute budget, test coverage (unit/integration/fuzz), upgrade and authority management.
  5. Generate report - Findings by severity, critical first, quick wins, testing recommendations.

Essential checks (every instruction)

Anchor:

#[derive(Accounts)]
pub struct SecureInstruction<'info> {
    #[account(
        mut,
        has_one = authority,                          // relationship check
        seeds = [b"vault", user.key().as_ref()], bump // canonical bump
    )]
    pub vault: Account<'info, Vault>,
    pub authority: Signer<'info>,                     // signer required
    pub token_program: Program<'info, Token>,         // program validation
}

let total = balance.checked_add(amount).ok_or(ErrorCode::Overflow)?;

Native Rust:

if !authority.is_signer { return Err(ProgramError::MissingRequiredSignature); }
if vault.owner != program_id { return Err(ProgramError::IllegalOwner); }
let total = balance.checked_add(amount).ok_or(ProgramError::ArithmeticOverflow)?;

Critical anti-patterns

❌ Never: saturating_* arithmetic (hides errors), unwrap()/expect() in production, init_if_needed without extra checks, skipped signer validation, unchecked arithmetic, arbitrary CPI targets, forgetting to reload accounts after mutations.

✅ Always: checked_* arithmetic, ok_or(error)? on Options, explicit init with validation, Signer/is_signer checks, Program<'info, T> for CPI targets, reload after external mutations, validate ownership + discriminator + relationships.

Severity and finding format

  • 🔴 Critical - Funds stolen/lost, protocol broken
  • 🟠 High - Disruption, partial fund loss possible
  • 🟡 Medium - Edge cases, griefing, suboptimal behavior
  • 🔵 Low - Code quality, gas, best practices
  • 💡 Informational - Recommendations, documentation
## 🔴 [CRITICAL] Title
**Location:** `programs/vault/src/lib.rs:45-52`
**Issue:** Brief description.
**Vulnerable Code:** ```rust ... ```
**Exploit Scenario:** How it's exploited, step by step.
**Recommendation:** ```rust ... ```
**References:** Links to docs or similar exploits.

Reference map

Key questions for every audit

  1. Can an attacker substitute accounts? (PDA validation, program IDs, has_one)
  2. Can arithmetic overflow/underflow? (checked ops, division by zero)
  3. Are all accounts validated? (owner, signer, writable, initialized)
  4. Can the program be drained? (authorization, reentrancy, account confusion)
  5. What happens in edge cases? (zero amounts, max values, closed accounts, expired data)
  6. Are external dependencies safe? (oracle staleness/status, CPI targets, token program)

Modern practices (2025)

Anchor 0.30+ • Token-2022 with proper extension handling • InitSpace derive • emit events for critical state changes • fuzz tests with Trident • document invariants • roadmap: Dev → Audit → Testnet → Audit → Mainnet.


ZK Compression

ZK Compression enables rent-free tokens and PDAs by storing state on the ledger (not in accounts), using zero-knowledge proofs to validate state transitions. Built by Light Protocol, indexed by Helius Photon.

When to use

Use when: creating millions of token accounts (~5000x cheaper), airdrops/loyalty/gaming mints to many recipients, many infrequently-updated user accounts, low-update-frequency PDAs.

Use regular accounts when: updated frequently (>1000 lifetime writes), large data accessed on-chain, or compute budget is critical (compression adds ~100k CU).

Quick Start

# TypeScript client
npm install @lightprotocol/stateless.js @lightprotocol/compressed-token
# Rust SDK for programs
cargo add light-sdk
# CLI + local validator
npm install -g @lightprotocol/zk-compression-cli
light test-validator      # local validator with compression
light init my-program     # new Anchor project with compression

Mint compressed tokens (TypeScript)

import { createRpc } from '@lightprotocol/stateless.js';
import { createMint, mintTo, transfer } from '@lightprotocol/compressed-token';

const rpc = createRpc(); // or createRpc('https://mainnet.helius-rpc.com?api-key=YOUR_KEY')

const { mint } = await createMint(rpc, payer, payer.publicKey, 9);
await mintTo(rpc, payer, mint, recipient, payer, 1_000_000_000);
await transfer(rpc, payer, mint, 500_000_000, owner, recipient);
const accounts = await rpc.getCompressedTokenAccountsByOwner(owner, { mint });

→ See references/compressed-pdas.md for the full Anchor program with compressed PDAs (LightAccount, validity proofs, CPI).

Core concepts

Compressed account model - Like regular accounts but stored on the ledger instead of AccountsDB. No rent. Identified by content hash (changes on every write) or an optional persistent address (PDA-like). State validated by ZK proofs.

State trees - Concurrent Merkle trees (Poseidon hashing); only the root is on-chain. V2 batched trees (mainnet, Jan 2026): ~250x cheaper state-root updates and ~70% less CU vs V1. New deployments use V2 automatically; V1 still supported.

Validity proofs - Groth16 ZK proofs, constant 128 bytes regardless of account count, ~100k CU to verify.

LightAccount operations:

LightAccount::<T>::new_init(&program_id, Some(address), tree_index);     // create
LightAccount::<T>::new_mut(&program_id, &account_meta, current_state)?;  // modify
LightAccount::<T>::new_close(&program_id, &account_meta, current_state)?; // close

RPC and infrastructure

Query compressed state via Helius RPC or self-hosted Photon. Key methods: getCompressedAccount, getCompressedAccountsByOwner, getCompressedTokenAccountsByOwner, getCompressedTokenBalancesByOwner, getValidityProof, getMultipleCompressedAccounts, getCompressionSignaturesForAccount.

Node types: Photon RPC (indexes state, serves queries - Helius or self-host), Prover (generates proofs), Forester (maintains trees, empties nullifier queues).

cargo install photon-indexer
photon --rpc-url=https://api.devnet.solana.com

Trade-offs

Larger transactions (+128 byte proof + account data) • higher CU (~100k proof + ~6k per account) • each write nullifies old state and appends new • requires a Photon indexer for queries. With V2 batched trees, break-even shifts well past V1's ~1000-write threshold.

Reference map

External: ZK Compression Docs • Light Protocol • Helius SDK • Photon Indexer • Program Examples


Resources

Official docs, tools, learning paths, security guides, audit reports, security firms, and community links:

→ See references/resources.md

General best practices

✅ Validate every account (ownership, signers, mutability) • use checked_* arithmetic • test extensively (unit, integration, edge cases) • use PDAs for program-owned accounts • minimize and profile compute • add security.txt so researchers can reach you.

Anchor-specific: InitSpace derive for space • has_one constraints • Program<'info, T> for CPI validation • emit! events • group related constraints.

Native-specific: next_account_info for safe iteration • cache PDA bumps • zero-copy for large structs (50%+ CU savings) • minimize logging (pubkey formatting is expensive) • solana-verify build for production.

Getting help

Files (skills)
  • references
    • accounts.md 24.8 KB
      # Solana Account Model & Validation
      
      This reference provides comprehensive coverage of Solana's account model, validation patterns, and rent mechanics for native Rust program development.
      
      ## Table of Contents
      
      1. [Account Structure](#account-structure)
      2. [Account Types](#account-types)
      3. [Account Ownership](#account-ownership)
      4. [Rent Mechanics](#rent-mechanics)
      5. [Account Validation Patterns](#account-validation-patterns)
      6. [Security Best Practices](#security-best-practices)
      7. [Common Vulnerabilities](#common-vulnerabilities)
      
      ---
      
      ## Account Structure
      
      Every Solana account is a location on the blockchain that stores data. All accounts have a uniform structure defined by the [`Account`](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/account/src/lib.rs#L48-L60) struct:
      
      ```rust
      pub struct Account {
          /// lamports in the account
          pub lamports: u64,
          /// data held in this account
          pub data: Vec<u8>,
          /// the program that owns this account
          pub owner: Pubkey,
          /// this account's data contains a loaded program (and is now read-only)
          pub executable: bool,
          /// the epoch at which this account will next owe rent (DEPRECATED)
          pub rent_epoch: Epoch,
      }
      ```
      
      ### Field Details
      
      #### `lamports` (u64)
      - The account's balance in lamports (1 SOL = 1,000,000,000 lamports)
      - Every account must maintain a minimum balance for rent exemption
      - Rent works as a **refundable deposit** - recoverable when account is closed
      - Only the account owner can deduct lamports
      - Any program can **add** lamports to any account
      
      #### `data` (Vec<u8>)
      - Maximum size: **10 MiB** (10,485,760 bytes)
      - Can contain any arbitrary sequence of bytes
      - Structure defined by the owning program
      - Common patterns:
        - **Program accounts**: Executable code or pointer to program data account
        - **Data accounts**: Serialized state (often using Borsh)
      
      #### `owner` (Pubkey)
      - The program ID that owns this account
      - **Critical security property**: Only the owner can modify `data` or deduct `lamports`
      - Cannot be changed after account creation (except by System Program for newly created accounts)
      - Newly created accounts start owned by System Program
      
      #### `executable` (bool)
      - `true`: Account contains executable program code
      - `false`: Account is a data account
      - Cannot be changed after being set to `true`
      
      #### `rent_epoch` (Epoch)
      - **DEPRECATED** - no longer used
      - Remains in struct for backward compatibility
      - Rent is now a one-time refundable deposit, not periodic payment
      
      ---
      
      ## Account Types
      
      ### 1. Program Accounts (Executable)
      
      Program accounts contain executable code and are owned by a [loader program](https://solana.com/docs/core/programs#loader-programs).
      
      **Simple Program Account Structure:**
      ```
      ┌─────────────────────────────────────┐
      │ Program Account                     │
      ├─────────────────────────────────────┤
      │ lamports: 1000000                   │
      │ data: [executable bytecode]         │
      │ owner: BPFLoaderUpgradeab1e...      │
      │ executable: true                    │
      └─────────────────────────────────────┘
      ```
      
      **Loader-v3 Program Structure (Upgradeable):**
      
      Programs deployed with loader-v3 use a **two-account model**:
      
      ```
      ┌─────────────────────────────────────┐
      │ Program Account                     │
      ├─────────────────────────────────────┤
      │ data: [pointer to program data]    │ ──┐
      │ executable: true                    │   │
      └─────────────────────────────────────┘   │
                                                │
                                                ▼
                                    ┌─────────────────────────────────────┐
                                    │ Program Data Account                │
                                    ├─────────────────────────────────────┤
                                    │ data: [actual executable bytecode]  │
                                    │ executable: false                   │
                                    └─────────────────────────────────────┘
      ```
      
      This separation enables:
      - Program upgrades without changing the program address
      - Buffer accounts for staging uploads
      - Separate upgrade authority management
      
      ### 2. Data Accounts (Non-Executable)
      
      Data accounts store program state and are owned by programs (or System Program).
      
      #### a) Program State Accounts
      
      Accounts created and owned by your program to store application state:
      
      ```rust
      // Example: Note account owned by a note-taking program
      pub struct NoteAccount {
          pub is_initialized: bool,
          pub author: Pubkey,
          pub note_id: u64,
          pub content: String,
      }
      ```
      
      **Creation Process:**
      1. Invoke System Program to create account (allocate space, transfer lamports)
      2. System Program transfers ownership to your program
      3. Your program initializes the account data
      
      ```rust
      // Step 1: Create account via System Program CPI
      invoke_signed(
          &system_instruction::create_account(
              initializer.key,
              pda_account.key,
              rent_lamports,
              account_len.try_into().unwrap(),
              program_id,  // Transfer ownership to our program
          ),
          &[initializer.clone(), pda_account.clone(), system_program.clone()],
          &[&[seeds, &[bump_seed]]],
      )?;
      
      // Step 2: Initialize the account data
      let mut account_data = try_from_slice_unchecked::<NoteAccount>(&pda_account.data.borrow())?;
      account_data.is_initialized = true;
      account_data.author = *initializer.key;
      account_data.note_id = note_id;
      account_data.content = content;
      account_data.serialize(&mut &mut pda_account.data.borrow_mut()[..])?;
      ```
      
      #### b) System Accounts (Wallet Accounts)
      
      Accounts owned by the System Program, typically used as user wallets:
      
      ```
      ┌─────────────────────────────────────┐
      │ Wallet Account                      │
      ├─────────────────────────────────────┤
      │ lamports: 1000000000                │
      │ data: []                            │
      │ owner: 11111111111111111111...      │ ← System Program
      │ executable: false                   │
      └─────────────────────────────────────┘
      ```
      
      **Characteristics:**
      - Can sign transactions (if you have the private key)
      - Can pay transaction fees
      - Can transfer SOL
      - Created automatically when funded with SOL
      
      #### c) Sysvar Accounts
      
      Special accounts at predefined addresses that provide cluster state data:
      
      | Sysvar | Address | Purpose |
      |--------|---------|---------|
      | Clock | `SysvarC1ock11111111111111111111111111111111` | Current slot, epoch, timestamp |
      | Rent | `SysvarRent111111111111111111111111111111111` | Rent rate calculation |
      | EpochSchedule | `SysvarEpochSchedu1e111111111111111111111111` | Epoch duration info |
      | SlotHashes | `SysvarS1otHashes111111111111111111111111111` | Recent slot hashes |
      
      **Access Pattern:**
      ```rust
      use solana_program::sysvar::{clock::Clock, Sysvar};
      
      let clock = Clock::get()?;
      let current_timestamp = clock.unix_timestamp;
      ```
      
      ---
      
      ## Account Ownership
      
      ### Ownership Rules
      
      **The Golden Rule:** Only the account owner can:
      1. Modify the account's `data` field
      2. Deduct lamports from the account
      
      **Critical Security Implication:**
      Programs must verify account ownership to prevent unauthorized state modifications.
      
      ### Ownership in Program Context
      
      When a program receives accounts in an instruction:
      
      ```rust
      pub fn process_instruction(
          program_id: &Pubkey,      // Your program's ID
          accounts: &[AccountInfo],
          instruction_data: &[u8],
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let data_account = next_account_info(account_info_iter)?;
      
          // CRITICAL: Verify ownership before modifying
          if data_account.owner != program_id {
              return Err(ProgramError::IllegalOwner);
          }
      
          // Safe to modify - we own this account
          // ...
      }
      ```
      
      ### AccountInfo Structure
      
      Programs receive accounts as `AccountInfo` structs:
      
      ```rust
      pub struct AccountInfo<'a> {
          pub key: &'a Pubkey,              // Account address
          pub is_signer: bool,              // Did this account sign the transaction?
          pub is_writable: bool,            // Is this account writable in this instruction?
          pub lamports: Rc<RefCell<&'a mut u64>>,  // Mutable lamport balance
          pub data: Rc<RefCell<&'a mut [u8]>>,     // Mutable data
          pub owner: &'a Pubkey,            // Owner program ID
          pub executable: bool,             // Is this executable?
          pub rent_epoch: Epoch,            // Deprecated
      }
      ```
      
      **Key Operations:**
      
      ```rust
      // Read data
      let data = data_account.data.borrow();
      let account_state = MyState::try_from_slice(&data)?;
      
      // Write data
      let mut data = data_account.data.borrow_mut();
      account_state.serialize(&mut *data)?;
      
      // Modify lamports
      **data_account.lamports.borrow_mut() += transfer_amount;
      ```
      
      ---
      
      ## Rent Mechanics
      
      Rent is a **refundable security deposit** required to store data on-chain. Despite the name "rent", it's not a recurring fee—it's a one-time deposit fully recoverable when the account is closed.
      
      ### Rent Calculation
      
      Rent is proportional to account size:
      
      ```rust
      use solana_program::rent::Rent;
      use solana_program::sysvar::Sysvar;
      
      // Get current rent rates
      let rent = Rent::get()?;
      
      // Calculate minimum balance for rent exemption
      let account_size: usize = 1000;  // bytes
      let rent_lamports = rent.minimum_balance(account_size);
      ```
      
      **Formula:**
      Based on [agave source](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/rent/src/lib.rs#L93-L97):
      
      ```rust
      minimum_balance = (LAMPORTS_PER_BYTE_YEAR * account_size) * EXEMPTION_THRESHOLD / slots_per_year
      ```
      
      **Constants:**
      - `LAMPORTS_PER_BYTE_YEAR`: 3,480 lamports
      - `EXEMPTION_THRESHOLD`: 2.0 (200% of annual rent)
      - Typical cost: ~0.00139536 SOL per 100 bytes
      
      ### Rent Exemption
      
      **All accounts must be rent-exempt.** This means:
      - Account lamport balance ≥ `rent.minimum_balance(account.data.len())`
      - The Solana runtime enforces this requirement
      - Non-exempt accounts cannot be created
      
      ### Practical Example
      
      ```rust
      pub fn create_data_account(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          data_size: usize,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let payer = next_account_info(account_info_iter)?;
          let new_account = next_account_info(account_info_iter)?;
          let system_program = next_account_info(account_info_iter)?;
      
          // Calculate rent-exempt balance
          let rent = Rent::get()?;
          let rent_lamports = rent.minimum_balance(data_size);
      
          // Create account with rent-exempt balance
          invoke(
              &system_instruction::create_account(
                  payer.key,
                  new_account.key,
                  rent_lamports,           // Must be rent-exempt
                  data_size as u64,
                  program_id,
              ),
              &[payer.clone(), new_account.clone(), system_program.clone()],
          )?;
      
          Ok(())
      }
      ```
      
      ### Closing Accounts (Recovering Rent)
      
      To recover rent when an account is no longer needed:
      
      ```rust
      pub fn close_account(
          account_to_close: &AccountInfo,
          destination: &AccountInfo,
      ) -> ProgramResult {
          // Transfer all lamports to destination
          let dest_lamports = destination.lamports();
          **destination.lamports.borrow_mut() = dest_lamports
              .checked_add(**account_to_close.lamports.borrow())
              .ok_or(ProgramError::ArithmeticOverflow)?;
      
          // Zero out lamports in closed account
          **account_to_close.lamports.borrow_mut() = 0;
      
          // Zero out data (security best practice)
          let mut data = account_to_close.data.borrow_mut();
          data.fill(0);
      
          Ok(())
      }
      ```
      
      **Important:** The runtime will garbage-collect accounts with 0 lamports.
      
      ---
      
      ## Account Validation Patterns
      
      Proper account validation is **critical for security**. Programs must verify accounts before using them.
      
      ### 1. Ownership Check
      
      **Purpose:** Ensure an account is owned by the expected program.
      
      **When to use:**
      - Before reading/writing account data
      - When validating PDAs
      - When ensuring proper account initialization
      
      ```rust
      // Basic ownership check
      if account.owner != program_id {
          msg!("Account not owned by this program");
          return Err(ProgramError::IllegalOwner);
      }
      
      // PDA ownership check (essential for security)
      if note_pda.owner != program_id {
          msg!("Invalid note account - wrong owner");
          return Err(ProgramError::IllegalOwner);
      }
      ```
      
      **Why it matters:**
      Without ownership checks, malicious actors can pass arbitrary accounts that match the expected data format but are controlled by other programs or themselves.
      
      ### 2. Signer Check
      
      **Purpose:** Verify that an account signed the transaction.
      
      **When to use:**
      - Before transferring funds from an account
      - Before modifying user-specific data
      - Before any privileged operation
      
      ```rust
      if !initializer.is_signer {
          msg!("Missing required signature");
          return Err(ProgramError::MissingRequiredSignature);
      }
      
      // Practical example: Only allow note author to update
      pub fn update_note(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          new_content: String,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let author = next_account_info(account_info_iter)?;
          let note_pda = next_account_info(account_info_iter)?;
      
          // Verify author signed the transaction
          if !author.is_signer {
              msg!("Author must sign to update note");
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          // Deserialize and verify author matches
          let note_data = NoteAccount::try_from_slice(&note_pda.data.borrow())?;
          if note_data.author != *author.key {
              msg!("Author mismatch");
              return Err(ProgramError::IllegalOwner);
          }
      
          // Safe to proceed with update
          // ...
      }
      ```
      
      ### 3. Writable Check
      
      **Purpose:** Verify an account is marked as writable.
      
      **When to use:**
      - Before modifying account data
      - Before changing lamport balances
      - Enforced automatically by runtime, but explicit checks improve clarity
      
      ```rust
      if !account.is_writable {
          msg!("Account must be writable");
          return Err(ProgramError::InvalidAccountData);
      }
      ```
      
      ### 4. Initialization Check
      
      **Purpose:** Prevent re-initialization or use of uninitialized accounts.
      
      **Pattern: Flag-based initialization**
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct DataAccount {
          pub is_initialized: bool,
          // ... other fields
      }
      
      impl DataAccount {
          pub fn is_initialized(&self) -> bool {
              self.is_initialized
          }
      }
      
      // On creation - check NOT initialized
      if account_data.is_initialized() {
          msg!("Account already initialized");
          return Err(ProgramError::AccountAlreadyInitialized);
      }
      
      // On update - check IS initialized
      if !account_data.is_initialized() {
          msg!("Account not initialized");
          return Err(ProgramError::UninitializedAccount);
      }
      ```
      
      ### 5. PDA Validation
      
      **Purpose:** Verify a provided PDA matches expected derivation.
      
      **Critical for security:** Always validate PDAs using canonical bump.
      
      ```rust
      pub fn validate_pda(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          note_id: u64,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let author = next_account_info(account_info_iter)?;
          let note_pda = next_account_info(account_info_iter)?;
      
          // Derive expected PDA
          let (expected_pda, _bump) = Pubkey::find_program_address(
              &[
                  author.key.as_ref(),
                  note_id.to_le_bytes().as_ref(),
              ],
              program_id,
          );
      
          // Validate match
          if expected_pda != *note_pda.key {
              msg!("Invalid PDA - seeds don't match");
              return Err(ProgramError::InvalidSeeds);
          }
      
          Ok(())
      }
      ```
      
      **Why use `find_program_address` instead of accepting a bump?**
      - Prevents bump seed manipulation attacks
      - Ensures canonical bump is used
      - Eliminates category of security vulnerabilities
      
      ### 6. Account Type Validation
      
      **Purpose:** Ensure account contains expected data type.
      
      **Pattern: Discriminator/Type Field**
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub enum AccountType {
          Uninitialized,
          UserProfile,
          GameState,
      }
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct AccountData {
          pub account_type: AccountType,
          // ... other fields
      }
      
      // Validation
      let account_data = AccountData::try_from_slice(&account.data.borrow())?;
      if !matches!(account_data.account_type, AccountType::UserProfile) {
          msg!("Wrong account type");
          return Err(ProgramError::InvalidAccountData);
      }
      ```
      
      ---
      
      ## Security Best Practices
      
      ### 1. Always Validate Before Trusting
      
      **Never assume accounts are correct.** Always validate:
      
      ```rust
      pub fn secure_instruction(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let user = next_account_info(account_info_iter)?;
          let user_data_pda = next_account_info(account_info_iter)?;
      
          // ✅ Signer check
          if !user.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          // ✅ Ownership check
          if user_data_pda.owner != program_id {
              return Err(ProgramError::IllegalOwner);
          }
      
          // ✅ PDA validation
          let (expected_pda, _) = Pubkey::find_program_address(
              &[b"user_data", user.key.as_ref()],
              program_id,
          );
          if expected_pda != *user_data_pda.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // ✅ Initialization check
          let data = UserData::try_from_slice(&user_data_pda.data.borrow())?;
          if !data.is_initialized {
              return Err(ProgramError::UninitializedAccount);
          }
      
          // Now safe to proceed
          // ...
      }
      ```
      
      ### 2. Fail Fast with Meaningful Errors
      
      Return errors immediately when validation fails:
      
      ```rust
      // ✅ Good - fail fast
      if !account.is_signer {
          msg!("User must sign the transaction");
          return Err(ProgramError::MissingRequiredSignature);
      }
      
      // ❌ Bad - continues with invalid state
      if account.is_signer {
          // process...
      }
      ```
      
      ### 3. Use Type Safety
      
      Leverage Rust's type system for compile-time guarantees:
      
      ```rust
      // Define a validated account type
      pub struct ValidatedUserAccount<'a> {
          info: &'a AccountInfo<'a>,
          data: UserAccountData,
      }
      
      impl<'a> ValidatedUserAccount<'a> {
          pub fn validate(
              account: &'a AccountInfo<'a>,
              program_id: &Pubkey,
          ) -> Result<Self, ProgramError> {
              // Ownership check
              if account.owner != program_id {
                  return Err(ProgramError::IllegalOwner);
              }
      
              // Deserialize and validate
              let data = UserAccountData::try_from_slice(&account.data.borrow())?;
              if !data.is_initialized {
                  return Err(ProgramError::UninitializedAccount);
              }
      
              Ok(Self { info: account, data })
          }
      }
      
      // Usage guarantees validated account
      pub fn process_with_validated_account(
          validated: ValidatedUserAccount,
      ) -> ProgramResult {
          // No need to re-validate!
          // ...
      }
      ```
      
      ### 4. Check Arithmetic Operations
      
      Always use checked math to prevent overflow/underflow:
      
      ```rust
      // ❌ Dangerous - can overflow
      let total = amount1 + amount2;
      
      // ✅ Safe - returns error on overflow
      let total = amount1
          .checked_add(amount2)
          .ok_or(ProgramError::ArithmeticOverflow)?;
      ```
      
      ### 5. Validate Data Constraints
      
      Check business logic constraints:
      
      ```rust
      pub fn allocate_points(
          character_account: &AccountInfo,
          new_strength: u8,
      ) -> ProgramResult {
          let mut character = Character::try_from_slice(&character_account.data.borrow())?;
      
          // Validate attribute cap
          if character.strength.checked_add(new_strength).ok_or(ProgramError::ArithmeticOverflow)? > 100 {
              msg!("Attribute cannot exceed 100");
              return Err(ProgramError::InvalidArgument);
          }
      
          // Validate allowance
          if new_strength > character.available_points {
              msg!("Insufficient available points");
              return Err(ProgramError::InsufficientFunds);
          }
      
          character.strength += new_strength;
          character.available_points -= new_strength;
          character.serialize(&mut &mut character_account.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Common Vulnerabilities
      
      ### 1. Missing Ownership Check
      
      **Vulnerability:**
      ```rust
      // ❌ No ownership validation
      pub fn update_data(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          new_value: u64,
      ) -> ProgramResult {
          let data_account = &accounts[0];
      
          // Dangerous - could be any account!
          let mut data = MyData::try_from_slice(&data_account.data.borrow())?;
          data.value = new_value;
          data.serialize(&mut &mut data_account.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      **Exploit:**
      Attacker passes an account they control that happens to deserialize correctly, modifying arbitrary data.
      
      **Fix:**
      ```rust
      // ✅ With ownership check
      if data_account.owner != program_id {
          return Err(ProgramError::IllegalOwner);
      }
      ```
      
      ### 2. Missing Signer Check
      
      **Vulnerability:**
      ```rust
      // ❌ No signer validation
      pub fn withdraw(
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let user_account = &accounts[0];
          let vault = &accounts[1];
      
          // Dangerous - anyone can drain anyone's funds!
          **user_account.lamports.borrow_mut() += amount;
          **vault.lamports.borrow_mut() -= amount;
      
          Ok(())
      }
      ```
      
      **Exploit:**
      Attacker calls instruction with victim's account, draining their funds without signature.
      
      **Fix:**
      ```rust
      // ✅ With signer check
      if !user_account.is_signer {
          return Err(ProgramError::MissingRequiredSignature);
      }
      ```
      
      ### 3. PDA Substitution Attack
      
      **Vulnerability:**
      ```rust
      // ❌ Accepts PDA without validation
      pub fn update_user_data(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          user: &AccountInfo,
          user_pda: &AccountInfo,
      ) -> ProgramResult {
          // No PDA derivation check!
          let mut data = UserData::try_from_slice(&user_pda.data.borrow())?;
          data.balance += 100;
          data.serialize(&mut &mut user_pda.data.borrow_mut()[..])?;
          Ok(())
      }
      ```
      
      **Exploit:**
      Attacker passes a different user's PDA, crediting that user's balance instead.
      
      **Fix:**
      ```rust
      // ✅ Validate PDA derivation
      let (expected_pda, _) = Pubkey::find_program_address(
          &[b"user_data", user.key.as_ref()],
          program_id,
      );
      if expected_pda != *user_pda.key {
          return Err(ProgramError::InvalidSeeds);
      }
      ```
      
      ### 4. Integer Overflow/Underflow
      
      **Vulnerability:**
      ```rust
      // ❌ Unchecked arithmetic
      pub fn add_rewards(
          account: &AccountInfo,
          reward: u64,
      ) -> ProgramResult {
          let mut user = UserData::try_from_slice(&account.data.borrow())?;
          user.total_rewards = user.total_rewards + reward;  // Can overflow!
          user.serialize(&mut &mut account.data.borrow_mut()[..])?;
          Ok(())
      }
      ```
      
      **Exploit:**
      Overflow wraps around: u64::MAX + 1 = 0, causing balance to reset.
      
      **Fix:**
      ```rust
      // ✅ Checked arithmetic
      user.total_rewards = user.total_rewards
          .checked_add(reward)
          .ok_or(ProgramError::ArithmeticOverflow)?;
      ```
      
      ### 5. Unvalidated Account Reuse
      
      **Vulnerability:**
      ```rust
      // ❌ No initialization check
      pub fn update_score(
          accounts: &[AccountInfo],
          score: u64,
      ) -> ProgramResult {
          let score_account = &accounts[0];
          let mut data = ScoreData::try_from_slice(&score_account.data.borrow())?;
      
          // What if account was never initialized?
          data.score = score;
          data.serialize(&mut &mut score_account.data.borrow_mut()[..])?;
          Ok(())
      }
      ```
      
      **Exploit:**
      Reusing uninitialized memory can lead to undefined behavior or data corruption.
      
      **Fix:**
      ```rust
      // ✅ Check initialization
      if !data.is_initialized {
          return Err(ProgramError::UninitializedAccount);
      }
      ```
      
      ---
      
      ## Summary
      
      **Critical Account Validation Checklist:**
      
      - ✅ **Ownership check**: Verify `account.owner == expected_program_id`
      - ✅ **Signer check**: Verify `account.is_signer` for privileged operations
      - ✅ **PDA validation**: Use `find_program_address` with expected seeds
      - ✅ **Initialization check**: Verify account is initialized before use
      - ✅ **Type validation**: Ensure account contains expected data structure
      - ✅ **Rent exemption**: Calculate and enforce rent-exempt balances
      - ✅ **Arithmetic safety**: Use `checked_add`, `checked_sub`, etc.
      - ✅ **Data constraints**: Validate business logic rules
      
      **Think Like an Attacker:**
      For every account your program receives, ask:
      - "What if this is the wrong account?"
      - "What if this account isn't owned by my program?"
      - "What if the user didn't sign for this?"
      - "What if this account is uninitialized?"
      - "What if these seeds derive a different PDA?"
      
      Validate everything. Trust nothing.
      
    • anchor-security.md 26.9 KB
      # Anchor Security Reference
      
      This document covers security patterns, vulnerabilities, and best practices specific to the Anchor framework for Solana program development.
      
      ## 1. Anchor Constraint Security
      
      ### 1.1 Account Constraint Basics
      
      Anchor's `#[account(...)]` constraints provide declarative validation of accounts passed to instructions. Proper use is critical for security.
      
      **Core constraint types:**
      - `init` - Initialize a new account
      - `mut` - Mark account as mutable
      - `has_one` - Verify relationship between accounts
      - `seeds` and `bump` - Validate PDA derivation
      - `constraint` - Custom validation expressions
      - `close` - Close account and return rent
      - `realloc` - Resize account data
      
      ### 1.2 init vs init_if_needed
      
      **VULNERABLE - Using init_if_needed:**
      ```rust
      #[derive(Accounts)]
      pub struct UpdateConfig<'info> {
          #[account(
              init_if_needed,
              payer = authority,
              space = 8 + Config::INIT_SPACE,
              seeds = [b"config"],
              bump
          )]
          pub config: Account<'info, Config>,
          #[account(mut)]
          pub authority: Signer<'info>,
          pub system_program: Program<'info, System>,
      }
      ```
      
      **Issue:** `init_if_needed` allows re-initialization attacks. An attacker can close the account in a previous transaction, then re-initialize it with malicious data.
      
      **SECURE - Separate init and update instructions:**
      ```rust
      #[derive(Accounts)]
      pub struct InitConfig<'info> {
          #[account(
              init,
              payer = authority,
              space = 8 + Config::INIT_SPACE,
              seeds = [b"config"],
              bump
          )]
          pub config: Account<'info, Config>,
          #[account(mut)]
          pub authority: Signer<'info>,
          pub system_program: Program<'info, System>,
      }
      
      #[derive(Accounts)]
      pub struct UpdateConfig<'info> {
          #[account(
              mut,
              seeds = [b"config"],
              bump = config.bump
          )]
          pub config: Account<'info, Config>,
          pub authority: Signer<'info>,
      }
      ```
      
      **When init_if_needed is acceptable:**
      - Idempotent operations where re-initialization is safe
      - Accounts with no state that matters (pure PDAs used only for signing)
      - Always combine with additional constraints to prevent misuse
      
      ### 1.3 has_one Constraints for Relationships
      
      **VULNERABLE - Missing has_one check:**
      ```rust
      #[derive(Accounts)]
      pub struct WithdrawFunds<'info> {
          #[account(mut)]
          pub vault: Account<'info, Vault>,
          #[account(mut)]
          pub owner: Signer<'info>,
          #[account(mut)]
          pub destination: SystemAccount<'info>,
      }
      
      pub fn withdraw_funds(ctx: Context<WithdrawFunds>, amount: u64) -> Result<()> {
          // Missing validation: anyone can withdraw from any vault!
          transfer_lamports(&ctx.accounts.vault, &ctx.accounts.destination, amount)?;
          Ok(())
      }
      ```
      
      **SECURE - Using has_one:**
      ```rust
      #[account]
      pub struct Vault {
          pub owner: Pubkey,
          pub bump: u8,
      }
      
      #[derive(Accounts)]
      pub struct WithdrawFunds<'info> {
          #[account(
              mut,
              has_one = owner, // Validates vault.owner == owner.key()
              seeds = [b"vault", owner.key().as_ref()],
              bump = vault.bump
          )]
          pub vault: Account<'info, Vault>,
          #[account(mut)]
          pub owner: Signer<'info>,
          #[account(mut)]
          pub destination: SystemAccount<'info>,
      }
      ```
      
      ### 1.4 seeds and bump for PDA Validation
      
      **VULNERABLE - Not validating PDA derivation:**
      ```rust
      #[derive(Accounts)]
      pub struct Deposit<'info> {
          #[account(mut)]
          pub vault: Account<'info, Vault>,
          pub depositor: Signer<'info>,
      }
      ```
      
      **Issue:** Attacker can pass any account as vault, including one they control.
      
      **SECURE - Validate PDA with seeds and bump:**
      ```rust
      #[derive(Accounts)]
      pub struct Deposit<'info> {
          #[account(
              mut,
              seeds = [b"vault", depositor.key().as_ref()],
              bump = vault.bump
          )]
          pub vault: Account<'info, Vault>,
          pub depositor: Signer<'info>,
      }
      ```
      
      **CRITICAL: Always use canonical bump:**
      ```rust
      #[account]
      pub struct Vault {
          pub bump: u8, // Store canonical bump at initialization
      }
      
      // At initialization, use:
      #[account(
          init,
          payer = payer,
          space = 8 + Vault::INIT_SPACE,
          seeds = [b"vault", authority.key().as_ref()],
          bump // Anchor automatically finds canonical bump
      )]
      pub vault: Account<'info, Vault>,
      
      // Then store it:
      vault.bump = ctx.bumps.vault; // ctx.bumps available in Anchor 0.29+
      ```
      
      ### 1.5 constraint Expressions and Pitfalls
      
      **VULNERABLE - Using constraint without proper checks:**
      ```rust
      #[derive(Accounts)]
      pub struct Transfer<'info> {
          #[account(
              mut,
              constraint = from.amount >= amount @ ErrorCode::InsufficientFunds
          )]
          pub from: Account<'info, TokenAccount>,
          #[account(mut)]
          pub to: Account<'info, TokenAccount>,
          pub authority: Signer<'info>,
      }
      ```
      
      **Issue:** Missing check that authority actually owns the from account!
      
      **SECURE - Combine constraints appropriately:**
      ```rust
      #[derive(Accounts)]
      pub struct Transfer<'info> {
          #[account(
              mut,
              has_one = authority, // Verify ownership
              constraint = from.amount >= amount @ ErrorCode::InsufficientFunds
          )]
          pub from: Account<'info, TokenAccount>,
          #[account(mut)]
          pub to: Account<'info, TokenAccount>,
          pub authority: Signer<'info>,
      }
      ```
      
      **Constraint expression tips:**
      - Use `@` to specify custom error codes
      - Constraints execute after account deserialization
      - Complex logic should go in instruction handler, not constraints
      - Prefer built-in constraints (`has_one`, `seeds`) over custom `constraint`
      
      ### 1.6 close Constraint Security
      
      **VULNERABLE - close without proper authorization:**
      ```rust
      #[derive(Accounts)]
      pub struct CloseAccount<'info> {
          #[account(
              mut,
              close = destination
          )]
          pub account_to_close: Account<'info, MyAccount>,
          #[account(mut)]
          pub destination: SystemAccount<'info>,
      }
      ```
      
      **Issue:** Anyone can close the account and steal the rent!
      
      **SECURE - Verify authorization before closing:**
      ```rust
      #[derive(Accounts)]
      pub struct CloseAccount<'info> {
          #[account(
              mut,
              has_one = authority,
              close = authority // Return rent to authorized party
          )]
          pub account_to_close: Account<'info, MyAccount>,
          #[account(mut)]
          pub authority: Signer<'info>,
      }
      ```
      
      **CRITICAL: close order matters:**
      ```rust
      // WRONG - closes account before using it
      #[account(
          close = authority,
          has_one = authority
      )]
      pub my_account: Account<'info, MyAccount>,
      
      // CORRECT - validates before closing
      #[account(
          has_one = authority,
          close = authority
      )]
      pub my_account: Account<'info, MyAccount>,
      ```
      
      ### 1.7 realloc Security Considerations
      
      **VULNERABLE - realloc without validation:**
      ```rust
      #[derive(Accounts)]
      pub struct UpdateData<'info> {
          #[account(
              mut,
              realloc = 8 + 4 + new_data.len(),
              realloc::payer = payer,
              realloc::zero = false
          )]
          pub data_account: Account<'info, DataAccount>,
          #[account(mut)]
          pub payer: Signer<'info>,
      }
      ```
      
      **Issues:**
      - No max size check (DoS via huge allocations)
      - No authority check (anyone can realloc)
      - `zero = false` might leak old data
      
      **SECURE - Proper realloc constraints:**
      ```rust
      #[derive(Accounts)]
      pub struct UpdateData<'info> {
          #[account(
              mut,
              has_one = authority,
              realloc = 8 + 4 + new_data.len(),
              realloc::payer = authority,
              realloc::zero = true, // Zero out old data
              constraint = new_data.len() <= MAX_DATA_SIZE @ ErrorCode::DataTooLarge
          )]
          pub data_account: Account<'info, DataAccount>,
          #[account(mut)]
          pub authority: Signer<'info>,
          pub system_program: Program<'info, System>,
      }
      ```
      
      ## 2. Common Anchor Vulnerabilities
      
      ### 2.1 Missing Constraints Leading to Account Substitution
      
      **VULNERABLE - No PDA validation:**
      ```rust
      #[derive(Accounts)]
      pub struct Withdraw<'info> {
          #[account(mut)]
          pub pool: Account<'info, Pool>,
          #[account(mut)]
          pub user_stake: Account<'info, UserStake>,
          pub user: Signer<'info>,
      }
      ```
      
      **Attack:** User passes a fake `user_stake` account they control with inflated balance.
      
      **SECURE - Validate PDAs:**
      ```rust
      #[derive(Accounts)]
      pub struct Withdraw<'info> {
          #[account(
              mut,
              seeds = [b"pool"],
              bump = pool.bump
          )]
          pub pool: Account<'info, Pool>,
          #[account(
              mut,
              seeds = [b"stake", pool.key().as_ref(), user.key().as_ref()],
              bump = user_stake.bump,
              has_one = user,
              has_one = pool
          )]
          pub user_stake: Account<'info, UserStake>,
          pub user: Signer<'info>,
      }
      ```
      
      ### 2.2 Incorrect Constraint Ordering
      
      Anchor evaluates constraints in this order:
      1. `init` / `init_if_needed` / `mut` / `close`
      2. `seeds` and `bump`
      3. `has_one`
      4. `constraint`
      5. Account deserialization
      
      **Implications:**
      - Can't use deserialized data in `seeds`
      - `constraint` expressions can use deserialized data
      - `close` at end ensures account data available for other checks
      
      ### 2.3 Over-Reliance on init_if_needed
      
      Covered in section 1.2. Key takeaway: **Avoid `init_if_needed` unless absolutely necessary.**
      
      ### 2.4 Missing mut on Accounts
      
      **VULNERABLE - Missing mut:**
      ```rust
      #[derive(Accounts)]
      pub struct Deposit<'info> {
          pub vault: Account<'info, Vault>, // Missing mut!
          pub user: Signer<'info>,
      }
      
      pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
          ctx.accounts.vault.balance += amount; // Runtime error!
          Ok(())
      }
      ```
      
      **SECURE:**
      ```rust
      #[derive(Accounts)]
      pub struct Deposit<'info> {
          #[account(mut)]
          pub vault: Account<'info, Vault>,
          pub user: Signer<'info>,
      }
      ```
      
      ### 2.5 PDA Bump Not Using Canonical Bump
      
      **VULNERABLE - Using non-canonical bump:**
      ```rust
      pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
          let (pda, bump) = Pubkey::find_program_address(
              &[b"vault"],
              ctx.program_id
          );
          // Storing bump separately is fine, but must validate it
          ctx.accounts.vault.bump = bump;
          Ok(())
      }
      
      // Later, using wrong bump
      #[account(
          seeds = [b"vault"],
          bump = 254 // WRONG - not canonical!
      )]
      pub vault: Account<'info, Vault>,
      ```
      
      **SECURE - Always use canonical bump:**
      ```rust
      #[derive(Accounts)]
      pub struct Initialize<'info> {
          #[account(
              init,
              payer = payer,
              space = 8 + Vault::INIT_SPACE,
              seeds = [b"vault"],
              bump // Anchor finds canonical bump
          )]
          pub vault: Account<'info, Vault>,
          #[account(mut)]
          pub payer: Signer<'info>,
          pub system_program: Program<'info, System>,
      }
      
      pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
          ctx.accounts.vault.bump = ctx.bumps.vault; // Store canonical bump
          Ok(())
      }
      ```
      
      ### 2.6 Account Reloading After CPI Mutations
      
      **VULNERABLE - Stale account data after CPI:**
      ```rust
      pub fn compound_rewards(ctx: Context<CompoundRewards>) -> Result<()> {
          // CPI to claim rewards (mutates user_rewards account)
          rewards_program::cpi::claim_rewards(
              CpiContext::new(
                  ctx.accounts.rewards_program.to_account_info(),
                  ClaimRewards {
                      user_rewards: ctx.accounts.user_rewards.to_account_info(),
                  }
              )
          )?;
      
          // WRONG - using stale data!
          let rewards = ctx.accounts.user_rewards.amount;
      
          // Reinvest...
          Ok(())
      }
      ```
      
      **SECURE - Reload account after CPI:**
      ```rust
      pub fn compound_rewards(ctx: Context<CompoundRewards>) -> Result<()> {
          rewards_program::cpi::claim_rewards(
              CpiContext::new(
                  ctx.accounts.rewards_program.to_account_info(),
                  ClaimRewards {
                      user_rewards: ctx.accounts.user_rewards.to_account_info(),
                  }
              )
          )?;
      
          // Reload account to get fresh data
          ctx.accounts.user_rewards.reload()?;
          let rewards = ctx.accounts.user_rewards.amount;
      
          // Reinvest...
          Ok(())
      }
      ```
      
      ## 3. Anchor CPI Security
      
      ### 3.1 Using Program<'info, T> for Program Validation
      
      **VULNERABLE - Using AccountInfo for program:**
      ```rust
      #[derive(Accounts)]
      pub struct CallExternal<'info> {
          /// CHECK: This is dangerous!
          pub external_program: AccountInfo<'info>,
      }
      ```
      
      **SECURE - Using Program<'info, T>:**
      ```rust
      #[derive(Accounts)]
      pub struct CallExternal<'info> {
          pub external_program: Program<'info, ExternalProgram>,
      }
      ```
      
      `Program<'info, T>` validates:
      - Account is executable
      - Account owner is BPF Loader
      - Account key matches expected program ID
      
      ### 3.2 CpiContext Usage Patterns
      
      **Basic CPI:**
      ```rust
      use anchor_lang::prelude::*;
      use anchor_spl::token::{self, Transfer};
      
      pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {
          let cpi_accounts = Transfer {
              from: ctx.accounts.from.to_account_info(),
              to: ctx.accounts.to.to_account_info(),
              authority: ctx.accounts.authority.to_account_info(),
          };
      
          let cpi_program = ctx.accounts.token_program.to_account_info();
          let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
      
          token::transfer(cpi_ctx, amount)?;
          Ok(())
      }
      ```
      
      ### 3.3 with_signer for PDA Signing
      
      **SECURE - PDA signing with CPI:**
      ```rust
      pub fn transfer_from_vault(ctx: Context<TransferFromVault>, amount: u64) -> Result<()> {
          let authority_bump = ctx.accounts.vault.authority_bump;
          let authority_seeds = &[
              b"vault-authority",
              &[authority_bump]
          ];
          let signer_seeds = &[&authority_seeds[..]];
      
          let cpi_accounts = Transfer {
              from: ctx.accounts.vault_token_account.to_account_info(),
              to: ctx.accounts.destination.to_account_info(),
              authority: ctx.accounts.vault_authority.to_account_info(),
          };
      
          let cpi_program = ctx.accounts.token_program.to_account_info();
          let cpi_ctx = CpiContext::new_with_signer(
              cpi_program,
              cpi_accounts,
              signer_seeds // PDA can now sign!
          );
      
          token::transfer(cpi_ctx, amount)?;
          Ok(())
      }
      ```
      
      ### 3.4 Validating CPI Return Data
      
      **SECURE - Check CPI return values:**
      ```rust
      pub fn safe_cpi_call(ctx: Context<SafeCpiCall>) -> Result<()> {
          let result = external_program::cpi::risky_operation(
              CpiContext::new(
                  ctx.accounts.external_program.to_account_info(),
                  RiskyOperation { /* ... */ }
              )
          )?;
      
          // Validate return data
          require!(
              result.get().success,
              ErrorCode::CpiOperationFailed
          );
      
          Ok(())
      }
      ```
      
      ### 3.5 Avoiding Arbitrary CPI Targets
      
      **VULNERABLE - Arbitrary CPI target:**
      ```rust
      #[derive(Accounts)]
      pub struct ArbitraryCpi<'info> {
          /// CHECK: DANGEROUS - allows any program!
          pub target_program: AccountInfo<'info>,
      }
      ```
      
      **SECURE - Constrained CPI targets:**
      ```rust
      #[derive(Accounts)]
      pub struct SafeCpi<'info> {
          // Option 1: Type-safe program constraint
          pub token_program: Program<'info, Token>,
      
          // Option 2: Explicit allowlist
          #[account(
              constraint = allowed_programs.contains(&other_program.key())
                  @ ErrorCode::UnauthorizedProgram
          )]
          pub other_program: Program<'info, OtherProgram>,
      }
      ```
      
      ## 4. Account Type Safety
      
      ### 4.1 Account Discriminators
      
      Anchor automatically adds an 8-byte discriminator to each account type (first 8 bytes of SHA256 hash of `"account:<AccountName>"`).
      
      **How it protects you:**
      ```rust
      #[account]
      pub struct Vault {
          pub authority: Pubkey,
          pub balance: u64,
      }
      
      #[account]
      pub struct UserAccount {
          pub authority: Pubkey,
          pub balance: u64,
      }
      
      // Anchor prevents this type confusion:
      #[derive(Accounts)]
      pub struct Withdraw<'info> {
          #[account(mut)]
          pub vault: Account<'info, Vault>, // Won't deserialize UserAccount!
      }
      ```
      
      **Manual discriminator handling:**
      ```rust
      impl Vault {
          pub const DISCRIMINATOR: [u8; 8] = [/* computed at compile time */];
      }
      
      // Checking discriminator manually
      let discriminator = &data[0..8];
      require!(
          discriminator == Vault::DISCRIMINATOR,
          ErrorCode::InvalidAccountType
      );
      ```
      
      ### 4.2 Account<'info, T> vs AccountInfo
      
      **Account<'info, T>:**
      - Type-safe deserialization
      - Automatic discriminator check
      - Automatic owner check
      - Immutable/mutable access control
      
      **AccountInfo:**
      - Raw account data
      - No automatic validation
      - Use only when necessary (non-Anchor programs, dynamic account types)
      
      **VULNERABLE - Using AccountInfo unnecessarily:**
      ```rust
      #[derive(Accounts)]
      pub struct UpdateVault<'info> {
          /// CHECK: Missing type safety!
          pub vault: AccountInfo<'info>,
      }
      ```
      
      **SECURE - Use Account<'info, T>:**
      ```rust
      #[derive(Accounts)]
      pub struct UpdateVault<'info> {
          #[account(mut)]
          pub vault: Account<'info, Vault>,
      }
      ```
      
      ### 4.3 AccountLoader for Zero-Copy Accounts
      
      For large accounts (>10KB), use zero-copy deserialization:
      
      ```rust
      #[account(zero_copy)]
      pub struct LargeAccount {
          pub data: [u8; 100000],
      }
      
      #[derive(Accounts)]
      pub struct UpdateLargeAccount<'info> {
          #[account(mut)]
          pub large_account: AccountLoader<'info, LargeAccount>,
      }
      
      pub fn update(ctx: Context<UpdateLargeAccount>) -> Result<()> {
          let mut account = ctx.accounts.large_account.load_mut()?;
          account.data[0] = 42;
          Ok(())
      }
      ```
      
      **Security note:** Zero-copy accounts use `RefCell` internally. Must call `load()` or `load_mut()` each time you access data to ensure safety.
      
      ### 4.4 Type Cosplay Prevention
      
      **Attack:** Creating fake accounts with correct discriminator but wrong program owner.
      
      **Anchor's defense:**
      ```rust
      #[account]
      #[derive(Default)]
      pub struct MyAccount {
          pub data: u64,
      }
      
      // Anchor checks:
      // 1. Discriminator matches
      // 2. Owner is this program's ID
      // 3. Account is properly sized
      ```
      
      **Additional validation for external accounts:**
      ```rust
      #[derive(Accounts)]
      pub struct UseExternalAccount<'info> {
          #[account(
              constraint = external_account.owner == &external_program::ID
                  @ ErrorCode::InvalidAccountOwner
          )]
          pub external_account: AccountInfo<'info>,
      }
      ```
      
      ## 5. Error Handling Security
      
      ### 5.1 Custom Error Codes
      
      **Define clear error codes:**
      ```rust
      #[error_code]
      pub enum ErrorCode {
          #[msg("Insufficient funds for withdrawal")]
          InsufficientFunds,
          #[msg("Unauthorized access attempt")]
          Unauthorized,
          #[msg("Invalid configuration parameters")]
          InvalidConfig,
          #[msg("Arithmetic overflow occurred")]
          Overflow,
      }
      ```
      
      **Use with require! macro:**
      ```rust
      pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
          require!(
              ctx.accounts.vault.balance >= amount,
              ErrorCode::InsufficientFunds
          );
      
          require!(
              ctx.accounts.vault.authority == ctx.accounts.user.key(),
              ErrorCode::Unauthorized
          );
      
          // Safe to proceed
          Ok(())
      }
      ```
      
      ### 5.2 Error Propagation Patterns
      
      **WRONG - Silencing errors:**
      ```rust
      pub fn risky_operation(ctx: Context<RiskyOp>) -> Result<()> {
          let _ = dangerous_function(); // WRONG - error silenced!
          Ok(())
      }
      ```
      
      **CORRECT - Propagate errors:**
      ```rust
      pub fn risky_operation(ctx: Context<RiskyOp>) -> Result<()> {
          dangerous_function()?; // Propagate error
          Ok(())
      }
      ```
      
      ### 5.3 Avoiding Silent Failures
      
      **VULNERABLE - No error on failure:**
      ```rust
      pub fn transfer(ctx: Context<Transfer>, amount: u64) -> Result<()> {
          if ctx.accounts.from.balance >= amount {
              ctx.accounts.from.balance -= amount;
              ctx.accounts.to.balance += amount;
          }
          // Returns Ok even if transfer didn't happen!
          Ok(())
      }
      ```
      
      **SECURE - Explicit error:**
      ```rust
      pub fn transfer(ctx: Context<Transfer>, amount: u64) -> Result<()> {
          require!(
              ctx.accounts.from.balance >= amount,
              ErrorCode::InsufficientFunds
          );
      
          ctx.accounts.from.balance -= amount;
          ctx.accounts.to.balance += amount;
          Ok(())
      }
      ```
      
      ## 6. Token Program Integration
      
      ### 6.1 anchor_spl Security Patterns
      
      **SECURE - Using anchor_spl helpers:**
      ```rust
      use anchor_spl::token::{self, Token, TokenAccount, Transfer};
      
      #[derive(Accounts)]
      pub struct TransferTokens<'info> {
          #[account(mut)]
          pub from: Account<'info, TokenAccount>,
          #[account(mut)]
          pub to: Account<'info, TokenAccount>,
          pub authority: Signer<'info>,
          pub token_program: Program<'info, Token>,
      }
      
      pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {
          token::transfer(
              CpiContext::new(
                  ctx.accounts.token_program.to_account_info(),
                  Transfer {
                      from: ctx.accounts.from.to_account_info(),
                      to: ctx.accounts.to.to_account_info(),
                      authority: ctx.accounts.authority.to_account_info(),
                  },
              ),
              amount,
          )?;
          Ok(())
      }
      ```
      
      ### 6.2 token_interface Usage
      
      For Token-2022 compatibility:
      
      ```rust
      use anchor_spl::token_interface::{self, TokenInterface, TokenAccount};
      
      #[derive(Accounts)]
      pub struct TransferTokens<'info> {
          #[account(mut)]
          pub from: InterfaceAccount<'info, TokenAccount>,
          #[account(mut)]
          pub to: InterfaceAccount<'info, TokenAccount>,
          pub authority: Signer<'info>,
          pub token_program: Interface<'info, TokenInterface>,
      }
      ```
      
      ### 6.3 Associated Token Account Constraints
      
      **VULNERABLE - Missing ATA validation:**
      ```rust
      #[derive(Accounts)]
      pub struct DepositTokens<'info> {
          #[account(mut)]
          pub user_token_account: Account<'info, TokenAccount>,
          pub user: Signer<'info>,
      }
      ```
      
      **SECURE - Validate ATA:**
      ```rust
      use anchor_spl::associated_token::AssociatedToken;
      
      #[derive(Accounts)]
      pub struct DepositTokens<'info> {
          #[account(
              mut,
              associated_token::mint = mint,
              associated_token::authority = user
          )]
          pub user_token_account: Account<'info, TokenAccount>,
          pub user: Signer<'info>,
          pub mint: Account<'info, Mint>,
          pub token_program: Program<'info, Token>,
          pub associated_token_program: Program<'info, AssociatedToken>,
      }
      ```
      
      ### 6.4 Token-2022 Extension Handling
      
      **Be aware of extensions:**
      ```rust
      pub fn handle_transfer(ctx: Context<HandleTransfer>, amount: u64) -> Result<()> {
          // Token-2022 may have transfer fees, freeze authority, etc.
          // Always check actual amount received after transfer
      
          let before_balance = ctx.accounts.destination.amount;
      
          token_interface::transfer_checked(
              CpiContext::new(/* ... */),
              amount,
              ctx.accounts.mint.decimals,
          )?;
      
          ctx.accounts.destination.reload()?;
          let actual_amount = ctx.accounts.destination.amount - before_balance;
      
          // Use actual_amount for accounting
          Ok(())
      }
      ```
      
      ## 7. Event Security
      
      ### 7.1 When to Emit Events
      
      Events are critical for:
      - Indexing and querying program state
      - Auditing sensitive operations
      - Monitoring for security incidents
      
      **Always emit events for:**
      - State changes (deposits, withdrawals, config updates)
      - Authorization changes (role grants, ownership transfers)
      - Critical operations (program upgrades, emergency actions)
      
      ### 7.2 Event Data Validation
      
      **SECURE - Validate before emitting:**
      ```rust
      #[event]
      pub struct WithdrawalEvent {
          pub user: Pubkey,
          pub amount: u64,
          pub timestamp: i64,
      }
      
      pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
          // Validate first
          require!(
              ctx.accounts.vault.balance >= amount,
              ErrorCode::InsufficientFunds
          );
      
          // Perform operation
          ctx.accounts.vault.balance -= amount;
      
          // Emit event AFTER successful operation
          emit!(WithdrawalEvent {
              user: ctx.accounts.user.key(),
              amount,
              timestamp: Clock::get()?.unix_timestamp,
          });
      
          Ok(())
      }
      ```
      
      ### 7.3 emit! vs emit_cpi!
      
      **emit! - Regular event:**
      ```rust
      emit!(MyEvent {
          data: value,
      });
      ```
      
      **emit_cpi! - Event for CPI callers:**
      ```rust
      // Use when program is called via CPI and event should be
      // visible to the calling program
      emit_cpi!(MyEvent {
          data: value,
      });
      ```
      
      ## 8. Anchor-Specific Best Practices
      
      ### 8.1 Account Space Calculation with InitSpace
      
      **SECURE - Using InitSpace derive macro:**
      ```rust
      use anchor_lang::prelude::*;
      
      #[account]
      #[derive(InitSpace)]
      pub struct UserProfile {
          pub authority: Pubkey,      // 32 bytes
          #[max_len(50)]
          pub name: String,            // 4 + 50 bytes
          pub created_at: i64,         // 8 bytes
          pub bump: u8,                // 1 byte
      }
      
      #[derive(Accounts)]
      pub struct CreateProfile<'info> {
          #[account(
              init,
              payer = payer,
              space = 8 + UserProfile::INIT_SPACE, // 8 for discriminator
              seeds = [b"profile", authority.key().as_ref()],
              bump
          )]
          pub profile: Account<'info, UserProfile>,
          pub authority: Signer<'info>,
          #[account(mut)]
          pub payer: Signer<'info>,
          pub system_program: Program<'info, System>,
      }
      ```
      
      ### 8.2 Remaining Accounts Handling
      
      **SECURE - Validate remaining accounts:**
      ```rust
      pub fn process_multiple_accounts(
          ctx: Context<ProcessAccounts>,
          count: u8,
      ) -> Result<()> {
          let remaining = &ctx.remaining_accounts;
      
          // Validate count
          require!(
              remaining.len() == count as usize,
              ErrorCode::InvalidAccountCount
          );
      
          // Validate each account
          for account_info in remaining.iter() {
              require!(
                  account_info.is_writable,
                  ErrorCode::AccountNotWritable
              );
      
              require!(
                  account_info.owner == ctx.program_id,
                  ErrorCode::InvalidAccountOwner
              );
      
              // Deserialize and validate type
              let account = Account::<MyAccount>::try_from(account_info)?;
      
              // Process account...
          }
      
          Ok(())
      }
      ```
      
      ### 8.3 Instruction Data Validation
      
      **SECURE - Validate all inputs:**
      ```rust
      pub fn create_proposal(
          ctx: Context<CreateProposal>,
          title: String,
          description: String,
          execution_delay: i64,
      ) -> Result<()> {
          // Validate string lengths
          require!(
              title.len() > 0 && title.len() <= 100,
              ErrorCode::InvalidTitleLength
          );
      
          require!(
              description.len() <= 1000,
              ErrorCode::DescriptionTooLong
          );
      
          // Validate numeric ranges
          require!(
              execution_delay >= MIN_DELAY && execution_delay <= MAX_DELAY,
              ErrorCode::InvalidExecutionDelay
          );
      
          // Validate against overflow
          let execution_time = Clock::get()?
              .unix_timestamp
              .checked_add(execution_delay)
              .ok_or(ErrorCode::Overflow)?;
      
          ctx.accounts.proposal.title = title;
          ctx.accounts.proposal.description = description;
          ctx.accounts.proposal.execution_time = execution_time;
      
          Ok(())
      }
      ```
      
      ### 8.4 Upgradability Considerations
      
      **SECURE - Handle program upgrades safely:**
      
      ```rust
      #[account]
      #[derive(InitSpace)]
      pub struct ProgramConfig {
          pub version: u8,
          pub upgrade_authority: Pubkey,
          pub paused: bool,
      }
      
      pub fn migrate(ctx: Context<Migrate>) -> Result<()> {
          let config = &mut ctx.accounts.config;
      
          // Check current version
          require!(
              config.version < CURRENT_VERSION,
              ErrorCode::AlreadyMigrated
          );
      
          // Perform version-specific migrations
          match config.version {
              0 => {
                  // Migrate from v0 to v1
                  // Add new fields, transform data, etc.
              }
              1 => {
                  // Migrate from v1 to v2
              }
              _ => return Err(ErrorCode::UnsupportedVersion.into()),
          }
      
          config.version = CURRENT_VERSION;
          Ok(())
      }
      ```
      
      **Emergency pause pattern:**
      ```rust
      #[derive(Accounts)]
      pub struct SensitiveOperation<'info> {
          #[account(
              constraint = !config.paused @ ErrorCode::ProgramPaused
          )]
          pub config: Account<'info, ProgramConfig>,
          // ... other accounts
      }
      ```
      
      This ensures you can pause the program in case of emergencies during or after upgrades.
      
    • anchor.md 34.7 KB
      # Anchor Framework Reference
      
      This reference covers Anchor-specific features and patterns. For general Solana concepts (accounts, PDAs, CPIs, etc.), see the other reference files in this directory.
      
      ## Table of Contents
      
      - [Installation and Setup](#installation-and-setup)
      - [Anchor Macros](#anchor-macros)
      - [Program Structure](#program-structure)
      - [Account Validation Constraints](#account-validation-constraints)
      - [IDL (Interface Description Language)](#idl-interface-description-language)
      - [TypeScript Client](#typescript-client)
      - [Rust Client](#rust-client)
      - [Anchor CLI Commands](#anchor-cli-commands)
      - [Token Integration (anchor-spl)](#token-integration-anchor-spl)
      - [Testing with Anchor](#testing-with-anchor)
      - [Anchor Features](#anchor-features)
      - [Common Patterns](#common-patterns)
      - [Error Handling](#error-handling)
      
      ---
      
      ## Installation and Setup
      
      ### Quick Install (Mac/Linux)
      
      ```bash
      # Install all dependencies (Rust, Solana CLI, Anchor)
      curl --proto '=https' --tlsv1.2 -sSfL https://solana-install.solana.workers.dev | bash
      ```
      
      ### Install Anchor with AVM
      
      Anchor Version Manager (avm) manages multiple Anchor CLI versions:
      
      ```bash
      # Install AVM
      cargo install --git https://github.com/coral-xyz/anchor avm --force
      
      # Install latest Anchor
      avm install latest
      avm use latest
      
      # Install specific version
      avm install 0.32.1
      avm use 0.32.1
      
      # Install from commit hash
      avm install 0.30.1-cfe82aa682138f7c6c58bf7a78f48f7d63e9e466
      avm use 0.30.1-cfe82aa
      ```
      
      ### Verify Installation
      
      ```bash
      anchor --version  # Should output: anchor-cli 0.32.1
      solana --version  # Recommended: solana-cli 2.3.0+
      rustc --version   # Required: 1.89.0+ for IDL builds
      ```
      
      ### Solana Playground (No Install)
      
      Develop in browser at https://beta.solpg.io/
      
      ---
      
      ## Anchor Macros
      
      ### Core Macros Overview
      
      1. **`declare_id!`** - Declares the program's on-chain address
      2. **`#[program]`** - Defines the program module containing instructions
      3. **`#[derive(Accounts)]`** - Defines account validation structs
      4. **`#[account]`** - Defines custom account types
      5. **`#[error_code]`** - Defines custom error enums
      6. **`#[event]`** - Defines event structs for logging
      
      ### declare_id! Macro
      
      ```rust
      use anchor_lang::prelude::*;
      
      // Program ID from /target/deploy/program_name.json
      declare_id!("11111111111111111111111111111111");
      ```
      
      Sync program IDs after building:
      
      ```bash
      anchor keys sync
      ```
      
      ### #[program] Macro
      
      Marks the module containing instruction handlers:
      
      ```rust
      #[program]
      pub mod my_program {
          use super::*;
      
          pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
              ctx.accounts.new_account.data = data;
              msg!("Data set to: {}!", data);
              Ok(())
          }
      
          pub fn update(ctx: Context<Update>, new_data: u64) -> Result<()> {
              ctx.accounts.account.data = new_data;
              Ok(())
          }
      }
      ```
      
      **Context<T> provides:**
      - `ctx.accounts` - Validated accounts (type T)
      - `ctx.program_id` - Current program's ID
      - `ctx.remaining_accounts` - Additional accounts not in struct
      - `ctx.bumps` - PDA bump seeds (struct with fields matching PDA account names)
      
      ### #[derive(Accounts)] Macro
      
      Defines account validation structs:
      
      ```rust
      #[derive(Accounts)]
      pub struct Initialize<'info> {
          #[account(init, payer = signer, space = 8 + 8)]
          pub new_account: Account<'info, NewAccount>,
      
          #[account(mut)]
          pub signer: Signer<'info>,
      
          pub system_program: Program<'info, System>,
      }
      ```
      
      **Validation happens in two ways:**
      1. **Account Types** - Signer, Account<'info, T>, Program<'info, T>, etc.
      2. **Account Constraints** - `#[account(...)]` attribute constraints
      
      ### #[account] Macro
      
      Defines custom account data structures:
      
      ```rust
      #[account]
      pub struct NewAccount {
          pub data: u64,      // 8 bytes
          pub owner: Pubkey,  // 32 bytes
          pub bump: u8,       // 1 byte
      }
      ```
      
      **Automatically implements:**
      - Account discriminator (first 8 bytes)
      - Serialization/deserialization (Borsh)
      - Owner validation (owned by program)
      
      **Account discriminator** = first 8 bytes of SHA256(`"account:NewAccount"`)
      
      ### #[error_code] Macro
      
      Defines custom program errors:
      
      ```rust
      #[error_code]
      pub enum ErrorCode {
          #[msg("Amount must be greater than zero")]
          InvalidAmount,
      
          #[msg("Insufficient funds")]
          InsufficientFunds,
      }
      ```
      
      Usage:
      
      ```rust
      require!(amount > 0, ErrorCode::InvalidAmount);
      ```
      
      ### #[event] Macro
      
      Defines event structs for logging:
      
      ```rust
      #[event]
      pub struct TransferEvent {
          pub from: Pubkey,
          pub to: Pubkey,
          pub amount: u64,
      }
      
      // Emit via program logs
      pub fn transfer(ctx: Context<Transfer>, amount: u64) -> Result<()> {
          emit!(TransferEvent {
              from: ctx.accounts.from.key(),
              to: ctx.accounts.to.key(),
              amount,
          });
          Ok(())
      }
      ```
      
      ---
      
      ## Program Structure
      
      ### Complete Example
      
      ```rust
      use anchor_lang::prelude::*;
      
      declare_id!("YourProgramIdHere11111111111111111111111");
      
      #[program]
      mod my_program {
          use super::*;
      
          pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
              ctx.accounts.new_account.data = data;
              ctx.accounts.new_account.authority = ctx.accounts.authority.key();
              Ok(())
          }
      
          pub fn update(ctx: Context<Update>, new_data: u64) -> Result<()> {
              ctx.accounts.account.data = new_data;
              Ok(())
          }
      }
      
      #[derive(Accounts)]
      pub struct Initialize<'info> {
          #[account(init, payer = authority, space = 8 + 8 + 32)]
          pub new_account: Account<'info, MyAccount>,
      
          #[account(mut)]
          pub authority: Signer<'info>,
      
          pub system_program: Program<'info, System>,
      }
      
      #[derive(Accounts)]
      pub struct Update<'info> {
          #[account(
              mut,
              has_one = authority
          )]
          pub account: Account<'info, MyAccount>,
      
          pub authority: Signer<'info>,
      }
      
      #[account]
      pub struct MyAccount {
          pub data: u64,
          pub authority: Pubkey,
      }
      ```
      
      ### Space Calculation
      
      Use `InitSpace` derive macro:
      
      ```rust
      #[account]
      #[derive(InitSpace)]
      pub struct MyAccount {
          pub data: u64,                    // 8 bytes
          #[max_len(50)]
          pub name: String,                 // 4 + 50 bytes
          pub authority: Pubkey,            // 32 bytes
      }
      
      // INIT_SPACE = 8 + 4 + 50 + 32 = 94 bytes
      
      #[account(init, payer = payer, space = 8 + MyAccount::INIT_SPACE)]
      pub account: Account<'info, MyAccount>,
      ```
      
      **Space = 8 (discriminator) + account data size**
      
      ---
      
      ## Account Validation Constraints
      
      ### Common Constraints
      
      #### init - Create New Account
      
      ```rust
      #[account(
          init,
          payer = payer,
          space = 8 + 8
      )]
      pub new_account: Account<'info, Counter>,
      ```
      
      #### init_if_needed - Create if Doesn't Exist
      
      ```rust
      #[account(
          init_if_needed,
          payer = payer,
          space = 8 + 8
      )]
      pub account: Account<'info, Counter>,
      ```
      
      Requires `init-if-needed` feature in Cargo.toml:
      
      ```toml
      [dependencies]
      anchor-lang = { version = "0.32.1", features = ["init-if-needed"] }
      ```
      
      #### mut - Mutable Account
      
      ```rust
      #[account(mut)]
      pub account: Account<'info, Counter>,
      ```
      
      #### signer - Requires Signature
      
      ```rust
      #[account(signer)]
      pub authority: AccountInfo<'info>,
      // Or use Signer<'info> type
      pub authority: Signer<'info>,
      ```
      
      #### close - Close Account
      
      ```rust
      #[account(
          mut,
          close = receiver  // Send lamports to receiver
      )]
      pub account_to_close: Account<'info, MyAccount>,
      
      #[account(mut)]
      pub receiver: SystemAccount<'info>,
      ```
      
      ### PDA Constraints
      
      #### seeds + bump - Validate PDA
      
      ```rust
      #[account(
          seeds = [b"vault", authority.key().as_ref()],
          bump
      )]
      pub vault: Account<'info, Vault>,
      ```
      
      Access bump in instruction:
      
      ```rust
      pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
          let bump = ctx.bumps.vault;
          ctx.accounts.vault.bump = bump;
          Ok(())
      }
      ```
      
      #### seeds + bump + init - Create PDA Account
      
      ```rust
      #[account(
          init,
          payer = payer,
          space = 8 + 32 + 1,
          seeds = [b"vault", authority.key().as_ref()],
          bump
      )]
      pub vault: Account<'info, Vault>,
      ```
      
      ### Validation Constraints
      
      #### has_one - Field Matches Account
      
      ```rust
      #[account(
          mut,
          has_one = authority  // Checks account.authority == authority.key()
      )]
      pub account: Account<'info, MyAccount>,
      pub authority: Signer<'info>,
      ```
      
      #### address - Matches Specific Address
      
      ```rust
      #[account(address = admin_pubkey)]
      pub admin: Signer<'info>,
      ```
      
      #### owner - Validates Owner Program
      
      ```rust
      #[account(owner = token::ID)]
      pub token_account: AccountInfo<'info>,
      ```
      
      #### constraint - Custom Validation
      
      ```rust
      #[account(
          constraint = account.data > 0 @ ErrorCode::InvalidData
      )]
      pub account: Account<'info, MyAccount>,
      ```
      
      ### Token Constraints
      
      #### mint - Create/Validate Mint
      
      ```rust
      use anchor_spl::token_interface::{Mint, TokenInterface};
      
      #[account(
          init,
          payer = payer,
          mint::decimals = 6,
          mint::authority = mint_authority,
          mint::freeze_authority = mint_authority,
      )]
      pub mint: InterfaceAccount<'info, Mint>,
      pub token_program: Interface<'info, TokenInterface>,
      ```
      
      #### token - Create/Validate Token Account
      
      ```rust
      use anchor_spl::token_interface::{TokenAccount, TokenInterface};
      
      #[account(
          init,
          payer = payer,
          token::mint = mint,
          token::authority = owner,
          token::token_program = token_program,
          seeds = [b"vault"],
          bump
      )]
      pub vault: InterfaceAccount<'info, TokenAccount>,
      ```
      
      #### associated_token - Create/Validate ATA
      
      ```rust
      use anchor_spl::{
          associated_token::AssociatedToken,
          token_interface::{Mint, TokenAccount, TokenInterface},
      };
      
      #[account(
          init,
          payer = payer,
          associated_token::mint = mint,
          associated_token::authority = owner,
          associated_token::token_program = token_program,
      )]
      pub token_account: InterfaceAccount<'info, TokenAccount>,
      
      pub mint: InterfaceAccount<'info, Mint>,
      pub owner: SystemAccount<'info>,
      pub token_program: Interface<'info, TokenInterface>,
      pub associated_token_program: Program<'info, AssociatedToken>,
      pub system_program: Program<'info, System>,
      ```
      
      ---
      
      ## IDL (Interface Description Language)
      
      ### What is the IDL?
      
      The IDL is a JSON file describing your program's interface:
      - Instructions (name, accounts, arguments)
      - Account types (structs)
      - Custom types (enums, type aliases)
      - Events
      - Errors
      - Discriminators
      
      ### IDL Generation
      
      Enable IDL build feature in `Cargo.toml`:
      
      ```toml
      [features]
      idl-build = ["anchor-lang/idl-build"]
      ```
      
      Build program and generate IDL:
      
      ```bash
      anchor build        # Builds program + IDL
      anchor idl build    # Only builds IDL
      ```
      
      IDL output location: `target/idl/<program_name>.json`
      
      ### IDL Structure Example
      
      ```json
      {
        "address": "8HupNBr7SBhBLcBsLhbtes3tCarBm6Bvpqp5AfVjHuj8",
        "metadata": {
          "name": "example",
          "version": "0.1.0",
          "spec": "0.1.0"
        },
        "instructions": [
          {
            "name": "initialize",
            "discriminator": [175, 175, 109, 31, 13, 152, 155, 237],
            "accounts": [
              {
                "name": "new_account",
                "writable": true,
                "signer": true
              },
              {
                "name": "signer",
                "writable": true,
                "signer": true
              },
              {
                "name": "system_program",
                "address": "11111111111111111111111111111111"
              }
            ],
            "args": [
              {
                "name": "data",
                "type": "u64"
              }
            ]
          }
        ],
        "accounts": [
          {
            "name": "NewAccount",
            "discriminator": [123, 45, 67, 89, 101, 112, 131, 145]
          }
        ],
        "types": [
          {
            "name": "NewAccount",
            "type": {
              "kind": "struct",
              "fields": [
                {
                  "name": "data",
                  "type": "u64"
                }
              ]
            }
          }
        ]
      }
      ```
      
      ### Instruction Discriminator
      
      8-byte identifier for each instruction:
      
      ```
      discriminator = SHA256("global:initialize")[0..8]
      ```
      
      Automatically handled by Anchor client.
      
      ### Account Discriminator
      
      8-byte identifier for each account type:
      
      ```
      discriminator = SHA256("account:NewAccount")[0..8]
      ```
      
      Used for:
      - Account type verification on deserialization
      - Type safety checks
      
      ### IDL Deployment
      
      Deploy IDL on-chain:
      
      ```bash
      anchor deploy              # Deploys program + IDL
      anchor deploy --no-idl     # Deploy program only
      anchor idl init <PROGRAM_ID> -f target/idl/program.json
      ```
      
      Fetch IDL from chain:
      
      ```bash
      anchor idl fetch <PROGRAM_ID>
      ```
      
      ---
      
      ## TypeScript Client
      
      ### Installation
      
      ```bash
      npm install @coral-xyz/anchor @solana/web3.js
      # or
      yarn add @coral-xyz/anchor @solana/web3.js
      ```
      
      > **Legacy dependency:** Anchor's TS client requires `@solana/web3.js` v1 as a peer dependency. This is the only reason to install web3.js - it is in maintenance mode. For non-Anchor client code, use [`@solana/kit`](https://solanakit.org) instead. Use [`@solana/compat`](https://www.npmjs.com/package/@solana/compat) to bridge between Kit and legacy web3.js types when needed.
      >
      > **For new dApps:** Use framework-kit (`@solana/client` + `@solana/react-hooks`) instead of `@solana/wallet-adapter-react` for wallet connections. Anchor's `.methods()` API works with either approach. See [client-development.md](client-development.md).
      
      ### Setup Program Instance
      
      #### With Wallet (Frontend)
      
      > **Legacy pattern** - uses `@solana/wallet-adapter-react`. For new projects, use framework-kit for wallet management. See [client-development.md](client-development.md). The Anchor `.methods()` API below works the same regardless.
      
      ```typescript
      import { Program, AnchorProvider, setProvider } from "@coral-xyz/anchor";
      import { useAnchorWallet, useConnection } from "@solana/wallet-adapter-react";
      import type { MyProgram } from "./types/my_program";
      import idl from "./idl/my_program.json";
      
      const { connection } = useConnection();
      const wallet = useAnchorWallet();
      
      const provider = new AnchorProvider(connection, wallet, {});
      setProvider(provider);
      
      const program = new Program(idl as MyProgram, { connection });
      ```
      
      #### Without Wallet (Read-Only)
      
      ```typescript
      import { Connection, PublicKey } from "@solana/web3.js";
      import { Program } from "@coral-xyz/anchor";
      import idl from "./idl/my_program.json";
      
      const connection = new Connection("https://api.devnet.solana.com");
      const program = new Program(idl, { connection });
      ```
      
      ### Invoke Instructions
      
      #### Using .rpc() - Send Transaction
      
      ```typescript
      import { Keypair, SystemProgram } from "@solana/web3.js";
      import BN from "bn.js";
      
      const newAccountKp = new Keypair();
      const data = new BN(42);
      
      const txSignature = await program.methods
        .initialize(data)
        .accounts({
          newAccount: newAccountKp.publicKey,
          signer: wallet.publicKey,
          systemProgram: SystemProgram.programId,
        })
        .signers([newAccountKp])
        .rpc();
      
      console.log("Transaction:", txSignature);
      ```
      
      #### Using .instruction() - Build Instruction
      
      ```typescript
      const ix = await program.methods
        .initialize(data)
        .accounts({
          newAccount: newAccountKp.publicKey,
          signer: wallet.publicKey,
          systemProgram: SystemProgram.programId,
        })
        .instruction();
      
      // Add to transaction
      const tx = new Transaction().add(ix);
      ```
      
      #### Using .transaction() - Build Transaction
      
      ```typescript
      const tx = await program.methods
        .initialize(data)
        .accounts({ /* ... */ })
        .transaction();
      
      // Sign and send
      tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
      tx.sign(wallet, newAccountKp);
      const signature = await connection.sendRawTransaction(tx.serialize());
      ```
      
      ### Fetch Accounts
      
      #### Fetch Single Account
      
      ```typescript
      const accountData = await program.account.myAccount.fetch(accountPubkey);
      console.log("Data:", accountData.data.toString());
      ```
      
      #### Fetch All Accounts
      
      ```typescript
      const accounts = await program.account.myAccount.all();
      accounts.forEach((account) => {
        console.log("Pubkey:", account.publicKey.toString());
        console.log("Data:", account.account.data);
      });
      ```
      
      #### Fetch with Filters
      
      ```typescript
      const accounts = await program.account.myAccount.all([
        {
          memcmp: {
            offset: 8,  // After discriminator
            bytes: authority.toBase58(),
          },
        },
      ]);
      ```
      
      ### Event Listeners
      
      ```typescript
      const listenerId = program.addEventListener(
        "TransferEvent",
        (event, slot) => {
          console.log("From:", event.from.toString());
          console.log("To:", event.to.toString());
          console.log("Amount:", event.amount.toString());
        }
      );
      
      // Remove listener
      program.removeEventListener(listenerId);
      ```
      
      ---
      
      ## Rust Client
      
      ### Dependencies
      
      Add to `Cargo.toml`:
      
      ```toml
      [dependencies]
      anchor-client = { version = "0.32.1", features = ["async"] }
      anchor-lang = "0.32.1"
      solana-sdk = "2.3.0"
      tokio = { version = "1.0", features = ["full"] }
      ```
      
      ### Generate Client with declare_program!
      
      Place IDL in `/idls/program_name.json`:
      
      ```rust
      use anchor_lang::prelude::*;
      
      declare_program!(example);
      use example::{
          accounts::Counter,
          client::{accounts, args},
      };
      ```
      
      ### Example Client
      
      ```rust
      use anchor_client::{
          solana_client::rpc_client::RpcClient,
          solana_sdk::{
              commitment_config::CommitmentConfig,
              signature::Keypair,
              signer::Signer,
              system_program,
          },
          Client, Cluster,
      };
      use std::rc::Rc;
      
      #[tokio::main]
      async fn main() -> anyhow::Result<()> {
          let connection = RpcClient::new_with_commitment(
              "http://127.0.0.1:8899",
              CommitmentConfig::confirmed(),
          );
      
          let payer = Keypair::new();
          let counter = Keypair::new();
      
          // Create program client
          let provider = Client::new_with_options(
              Cluster::Localnet,
              Rc::new(payer),
              CommitmentConfig::confirmed(),
          );
          let program = provider.program(example::ID)?;
      
          // Build instruction
          let ix = program
              .request()
              .accounts(accounts::Initialize {
                  counter: counter.pubkey(),
                  payer: program.payer(),
                  system_program: system_program::ID,
              })
              .args(args::Initialize)
              .instructions()?
              .remove(0);
      
          // Send transaction
          let signature = program
              .request()
              .instruction(ix)
              .signer(&counter)
              .send()
              .await?;
      
          println!("Transaction: {}", signature);
      
          // Fetch account
          let account: Counter = program.account::<Counter>(counter.pubkey()).await?;
          println!("Count: {}", account.count);
      
          Ok(())
      }
      ```
      
      ---
      
      ## Anchor CLI Commands
      
      ### Project Commands
      
      ```bash
      # Initialize new project
      anchor init my-project
      anchor init my-project --test-template rust  # Rust tests
      anchor init my-project --test-template mollusk  # Mollusk tests
      
      # Create new program in workspace
      anchor new my-program
      ```
      
      ### Build Commands
      
      ```bash
      # Build all programs
      anchor build
      
      # Build specific program
      anchor build --program-name my-program
      
      # Build without IDL generation
      anchor build --no-idl
      
      # Verifiable build (uses solana-verify)
      anchor build --verifiable
      ```
      
      ### Deploy Commands
      
      ```bash
      # Deploy to cluster in Anchor.toml
      anchor deploy
      
      # Deploy specific program
      anchor deploy --program-name my-program
      
      # Deploy without IDL
      anchor deploy --no-idl
      
      # Deploy with additional program args
      anchor deploy -- --max-len 200000
      ```
      
      ### Test Commands
      
      ```bash
      # Build + deploy + test
      anchor test
      
      # Skip local validator (use running validator)
      anchor test --skip-local-validator
      
      # Test specific program
      anchor test --program-name my-program
      
      # Skip IDL build
      anchor test --no-idl
      ```
      
      ### IDL Commands
      
      ```bash
      # Build IDL only
      anchor idl build
      
      # Initialize IDL on-chain
      anchor idl init <PROGRAM_ID> -f target/idl/program.json
      
      # Fetch IDL from chain
      anchor idl fetch <PROGRAM_ID>
      
      # Upgrade on-chain IDL
      anchor idl upgrade <PROGRAM_ID> -f target/idl/program.json
      
      # Get IDL authority
      anchor idl authority <PROGRAM_ID>
      
      # Set new IDL authority
      anchor idl set-authority <PROGRAM_ID> --new-authority <NEW_AUTHORITY>
      ```
      
      ### Other Commands
      
      ```bash
      # Sync program IDs
      anchor keys sync
      
      # List program keypairs
      anchor keys list
      
      # Expand macros
      anchor expand
      anchor expand --program-name my-program
      
      # Verify deployed program
      anchor verify -p <program-name> <PROGRAM_ID>
      
      # Run migration script
      anchor migrate
      
      # Close deployed program (reclaim rent)
      solana program close <PROGRAM_ID>
      ```
      
      ### Local Validator
      
      ```bash
      # Start local validator
      solana-test-validator
      
      # Start with program loaded
      solana-test-validator --bpf-program <PROGRAM_ID> target/deploy/program.so
      
      # Configure in Anchor.toml
      [test.validator]
      url = "https://api.devnet.solana.com"
      
      [[test.validator.clone]]
      address = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"  # Clone USDC mint
      ```
      
      ---
      
      ## Token Integration (anchor-spl)
      
      ### Dependencies
      
      ```toml
      [dependencies]
      anchor-spl = { version = "0.32.1", features = ["metadata"] }
      ```
      
      ### Token Interface (Token + Token-2022)
      
      Use `token_interface` for compatibility with both Token Program and Token Extensions:
      
      ```rust
      use anchor_spl::token_interface::{
          self, Mint, MintTo, TokenAccount, TokenInterface, TransferChecked
      };
      ```
      
      ### Create Mint
      
      ```rust
      #[derive(Accounts)]
      pub struct CreateMint<'info> {
          #[account(mut)]
          pub payer: Signer<'info>,
      
          #[account(
              init,
              payer = payer,
              mint::decimals = 6,
              mint::authority = mint_authority,
          )]
          pub mint: InterfaceAccount<'info, Mint>,
      
          /// CHECK: Mint authority
          pub mint_authority: UncheckedAccount<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
          pub system_program: Program<'info, System>,
      }
      ```
      
      ### Create Token Account (ATA)
      
      ```rust
      use anchor_spl::associated_token::AssociatedToken;
      
      #[derive(Accounts)]
      pub struct CreateTokenAccount<'info> {
          #[account(mut)]
          pub payer: Signer<'info>,
      
          #[account(
              init,
              payer = payer,
              associated_token::mint = mint,
              associated_token::authority = owner,
              associated_token::token_program = token_program,
          )]
          pub token_account: InterfaceAccount<'info, TokenAccount>,
      
          pub mint: InterfaceAccount<'info, Mint>,
          pub owner: SystemAccount<'info>,
          pub token_program: Interface<'info, TokenInterface>,
          pub associated_token_program: Program<'info, AssociatedToken>,
          pub system_program: Program<'info, System>,
      }
      ```
      
      ### Mint Tokens
      
      ```rust
      pub fn mint_tokens(ctx: Context<MintTokens>, amount: u64) -> Result<()> {
          let cpi_accounts = MintTo {
              mint: ctx.accounts.mint.to_account_info(),
              to: ctx.accounts.token_account.to_account_info(),
              authority: ctx.accounts.authority.to_account_info(),
          };
      
          let cpi_program = ctx.accounts.token_program.to_account_info();
          let cpi_context = CpiContext::new(cpi_program, cpi_accounts);
      
          token_interface::mint_to(cpi_context, amount)?;
          Ok(())
      }
      ```
      
      ### Transfer Tokens
      
      ```rust
      pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {
          let decimals = ctx.accounts.mint.decimals;
      
          let cpi_accounts = TransferChecked {
              from: ctx.accounts.from.to_account_info(),
              mint: ctx.accounts.mint.to_account_info(),
              to: ctx.accounts.to.to_account_info(),
              authority: ctx.accounts.authority.to_account_info(),
          };
      
          let cpi_program = ctx.accounts.token_program.to_account_info();
          let cpi_context = CpiContext::new(cpi_program, cpi_accounts);
      
          token_interface::transfer_checked(cpi_context, amount, decimals)?;
          Ok(())
      }
      ```
      
      ### PDA as Token Authority
      
      ```rust
      #[derive(Accounts)]
      pub struct MintWithPDA<'info> {
          #[account(mut)]
          pub payer: Signer<'info>,
      
          #[account(
              init,
              payer = payer,
              mint::decimals = 6,
              mint::authority = mint,  // PDA is authority
              seeds = [b"mint"],
              bump
          )]
          pub mint: InterfaceAccount<'info, Mint>,
      
          pub token_program: Interface<'info, TokenInterface>,
          pub system_program: Program<'info, System>,
      }
      
      pub fn mint_with_pda(ctx: Context<MintWithPDA>, amount: u64) -> Result<()> {
          let seeds = &[b"mint".as_ref(), &[ctx.bumps.mint]];
          let signer_seeds = &[&seeds[..]];
      
          let cpi_accounts = MintTo {
              mint: ctx.accounts.mint.to_account_info(),
              to: ctx.accounts.token_account.to_account_info(),
              authority: ctx.accounts.mint.to_account_info(),
          };
      
          let cpi_context = CpiContext::new(
              ctx.accounts.token_program.to_account_info(),
              cpi_accounts
          ).with_signer(signer_seeds);
      
          token_interface::mint_to(cpi_context, amount)?;
          Ok(())
      }
      ```
      
      ---
      
      ## Testing with Anchor
      
      ### TypeScript Tests (Default)
      
      Test file location: `tests/my-program.ts`
      
      ```typescript
      import * as anchor from "@coral-xyz/anchor";
      import { Program } from "@coral-xyz/anchor";
      import { MyProgram } from "../target/types/my_program";
      import { expect } from "chai";
      
      describe("my-program", () => {
        const provider = anchor.AnchorProvider.env();
        anchor.setProvider(provider);
      
        const program = anchor.workspace.MyProgram as Program<MyProgram>;
      
        it("Initializes account", async () => {
          const newAccount = anchor.web3.Keypair.generate();
      
          await program.methods
            .initialize(new anchor.BN(42))
            .accounts({
              newAccount: newAccount.publicKey,
              signer: provider.wallet.publicKey,
              systemProgram: anchor.web3.SystemProgram.programId,
            })
            .signers([newAccount])
            .rpc();
      
          const account = await program.account.myAccount.fetch(
            newAccount.publicKey
          );
          expect(account.data.toNumber()).to.equal(42);
        });
      });
      ```
      
      ### Rust Tests with LiteSVM
      
      Initialize project with Rust tests:
      
      ```bash
      anchor init my-project --test-template rust
      ```
      
      Test file: `tests/src/test_initialize.rs`
      
      ```rust
      use anchor_client::anchor_lang::prelude::*;
      use anchor_client::anchor_lang::solana_program::system_program;
      use anchor_lang_lite_svm::LiteSVM;
      
      #[test]
      fn test_initialize() {
          let mut svm = LiteSVM::new();
      
          let program_id = svm.deploy_program("target/deploy/my_program.so");
          let payer = Keypair::new();
          let counter = Keypair::new();
      
          svm.airdrop(&payer.pubkey(), 10_000_000_000).unwrap();
      
          let ix = my_program::instruction::Initialize {
              new_account: counter.pubkey(),
              signer: payer.pubkey(),
              system_program: system_program::ID,
          };
      
          let tx = svm.send_transaction(vec![ix], &[&payer, &counter]).unwrap();
      
          // Fetch and verify account
          let account_data = svm.get_account(&counter.pubkey()).unwrap();
          // Verify data...
      }
      ```
      
      ### Test Configuration (Anchor.toml)
      
      ```toml
      [test]
      # Startup timeout for validator
      startup_wait = 10000
      
      [test.validator]
      # URL to clone accounts from
      url = "https://api.mainnet-beta.solana.com"
      
      # Clone accounts
      [[test.validator.clone]]
      address = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      
      # Load account from JSON
      [[test.validator.account]]
      address = "MyAccount111111111111111111111111111111111"
      filename = "tests/fixtures/my-account.json"
      
      # Set program as upgradeable
      [test.validator.upgradeable]
      my_program = true
      ```
      
      ---
      
      ## Anchor Features
      
      ### Custom Errors
      
      ```rust
      #[error_code]
      pub enum ErrorCode {
          #[msg("Amount must be greater than zero")]
          InvalidAmount,
      
          #[msg("Authority mismatch")]
          Unauthorized,
      }
      
      // Usage
      require!(amount > 0, ErrorCode::InvalidAmount);
      require_keys_eq!(account.owner, authority.key(), ErrorCode::Unauthorized);
      ```
      
      **Error macros:**
      - `require!(condition, error)` - Condition must be true
      - `require_eq!(a, b, error)` - Values must be equal
      - `require_neq!(a, b, error)` - Values must not be equal
      - `require_keys_eq!(a, b, error)` - Pubkeys must match
      - `require_keys_neq!(a, b, error)` - Pubkeys must not match
      - `require_gt!(a, b, error)` - a > b
      - `require_gte!(a, b, error)` - a >= b
      
      ### Events
      
      #### emit! (Program Logs)
      
      ```rust
      #[event]
      pub struct TransferEvent {
          pub from: Pubkey,
          pub to: Pubkey,
          pub amount: u64,
      }
      
      pub fn transfer(ctx: Context<Transfer>, amount: u64) -> Result<()> {
          emit!(TransferEvent {
              from: ctx.accounts.from.key(),
              to: ctx.accounts.to.key(),
              amount,
          });
          Ok(())
      }
      ```
      
      #### emit_cpi! (CPI Data)
      
      Enable feature:
      
      ```toml
      [dependencies]
      anchor-lang = { version = "0.32.1", features = ["event-cpi"] }
      ```
      
      Usage:
      
      ```rust
      #[event_cpi]
      #[derive(Accounts)]
      pub struct EmitEvent {}
      
      pub fn emit_event(ctx: Context<EmitEvent>, msg: String) -> Result<()> {
          emit_cpi!(CustomEvent { message: msg });
          Ok(())
      }
      ```
      
      ### Zero-Copy Accounts
      
      For large accounts (>10KB):
      
      ```toml
      [dependencies]
      bytemuck = { version = "1.20.0", features = ["min_const_generics"] }
      ```
      
      ```rust
      #[account(zero_copy)]
      pub struct LargeData {
          pub data: [u8; 10000],
      }
      
      #[derive(Accounts)]
      pub struct Initialize<'info> {
          #[account(
              init,
              payer = payer,
              space = 8 + std::mem::size_of::<LargeData>()
          )]
          pub large_account: AccountLoader<'info, LargeData>,
      
          #[account(mut)]
          pub payer: Signer<'info>,
          pub system_program: Program<'info, System>,
      }
      
      pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
          let mut large_account = ctx.accounts.large_account.load_init()?;
          large_account.data[0] = 42;
          Ok(())
      }
      
      pub fn update(ctx: Context<Update>) -> Result<()> {
          let mut large_account = ctx.accounts.large_account.load_mut()?;
          large_account.data[0] = 100;
          Ok(())
      }
      ```
      
      **For >10240 bytes**, use `zero` constraint + create account separately:
      
      ```rust
      #[account(zero)]  // Instead of init
      pub large_account: AccountLoader<'info, LargeData>,
      ```
      
      ### declare_program! (Dependency-Free CPI)
      
      Place IDL in `/idls/program_name.json`:
      
      ```rust
      declare_program!(example);
      
      use example::{
          accounts::Counter,
          cpi::{self, accounts::Initialize},
          program::Example,
      };
      
      // CPI to other program
      pub fn call_example(ctx: Context<CallExample>) -> Result<()> {
          let cpi_ctx = CpiContext::new(
              ctx.accounts.example_program.to_account_info(),
              Initialize {
                  counter: ctx.accounts.counter.to_account_info(),
                  payer: ctx.accounts.payer.to_account_info(),
                  system_program: ctx.accounts.system_program.to_account_info(),
              },
          );
      
          cpi::initialize(cpi_ctx)?;
          Ok(())
      }
      ```
      
      ---
      
      ## Common Patterns
      
      ### Store Bump Seed
      
      ```rust
      #[account]
      pub struct Vault {
          pub authority: Pubkey,
          pub bump: u8,
      }
      
      pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
          ctx.accounts.vault.authority = ctx.accounts.authority.key();
          ctx.accounts.vault.bump = ctx.bumps.vault;
          Ok(())
      }
      
      // Use stored bump
      let seeds = &[
          b"vault",
          ctx.accounts.vault.authority.as_ref(),
          &[ctx.accounts.vault.bump]
      ];
      ```
      
      ### Multi-Seed PDAs
      
      ```rust
      #[account(
          init,
          payer = payer,
          space = 8 + UserAccount::INIT_SPACE,
          seeds = [
              b"user",
              user.key().as_ref(),
              &counter.to_le_bytes()
          ],
          bump
      )]
      pub user_account: Account<'info, UserAccount>,
      ```
      
      ### CPI with PDA Signer
      
      ```rust
      pub fn transfer_with_pda(ctx: Context<Transfer>, amount: u64) -> Result<()> {
          let seeds = &[
              b"vault",
              &[ctx.bumps.vault]
          ];
          let signer_seeds = &[&seeds[..]];
      
          let cpi_accounts = TransferChecked {
              from: ctx.accounts.from.to_account_info(),
              mint: ctx.accounts.mint.to_account_info(),
              to: ctx.accounts.to.to_account_info(),
              authority: ctx.accounts.vault.to_account_info(),
          };
      
          let cpi_ctx = CpiContext::new(
              ctx.accounts.token_program.to_account_info(),
              cpi_accounts
          ).with_signer(signer_seeds);
      
          token_interface::transfer_checked(cpi_ctx, amount, decimals)?;
          Ok(())
      }
      ```
      
      ### Remaining Accounts
      
      ```rust
      pub fn process_multiple(ctx: Context<Process>) -> Result<()> {
          for account_info in ctx.remaining_accounts.iter() {
              let account = Account::<SomeAccount>::try_from(account_info)?;
              msg!("Processing: {}", account_info.key());
              // Process account...
          }
          Ok(())
      }
      
      #[derive(Accounts)]
      pub struct Process<'info> {
          pub authority: Signer<'info>,
          // Additional accounts in remaining_accounts
      }
      ```
      
      ### Close Account Pattern
      
      ```rust
      #[derive(Accounts)]
      pub struct CloseAccount<'info> {
          #[account(
              mut,
              close = receiver,  // Sends lamports to receiver
              has_one = authority
          )]
          pub account: Account<'info, MyAccount>,
      
          pub authority: Signer<'info>,
      
          #[account(mut)]
          /// CHECK: Receives lamports
          pub receiver: UncheckedAccount<'info>,
      }
      ```
      
      ### Dynamic Account Space
      
      ```rust
      #[derive(Accounts)]
      #[instruction(name: String)]
      pub struct Create<'info> {
          #[account(
              init,
              payer = payer,
              space = 8 + 4 + name.len() + 8
          )]
          pub item: Account<'info, Item>,
      
          #[account(mut)]
          pub payer: Signer<'info>,
          pub system_program: Program<'info, System>,
      }
      
      #[account]
      pub struct Item {
          pub name: String,
          pub count: u64,
      }
      ```
      
      ---
      
      ## Error Handling
      
      ### Built-in Anchor Errors
      
      Anchor provides built-in errors in `ErrorCode` enum. Examples:
      - `ConstraintHasOne` - has_one constraint failed
      - `ConstraintSigner` - Account not a signer
      - `ConstraintMut` - Account not mutable
      - `ConstraintSeeds` - Seeds constraint failed
      - `AccountNotInitialized` - Account discriminator is zero
      
      ### Custom Error Implementation
      
      ```rust
      #[error_code]
      pub enum ErrorCode {
          #[msg("Amount must be greater than zero")]
          InvalidAmount,
      
          #[msg("Insufficient balance: required {}, available {}")]
          InsufficientBalance,
      
          #[msg("Unauthorized access")]
          Unauthorized,
      }
      
      pub fn validate_amount(ctx: Context<Validate>, amount: u64) -> Result<()> {
          require!(amount > 0, ErrorCode::InvalidAmount);
      
          require!(
              ctx.accounts.account.balance >= amount,
              ErrorCode::InsufficientBalance
          );
      
          require_keys_eq!(
              ctx.accounts.account.owner,
              ctx.accounts.authority.key(),
              ErrorCode::Unauthorized
          );
      
          Ok(())
      }
      ```
      
      ### Error Numbers
      
      Anchor errors use this numbering:
      - `0-1000` - Internal Anchor errors
      - `1000-2000` - Reserved
      - `2000-3000` - Custom program errors (from #[error_code])
      - `3000+` - Additional custom errors
      
      ### TypeScript Error Handling
      
      ```typescript
      try {
        await program.methods
          .transfer(amount)
          .accounts({ /* ... */ })
          .rpc();
      } catch (error) {
        if (error.code === 6000) {  // Custom error code
          console.log("Custom error:", error.msg);
        }
        console.log("Error logs:", error.logs);
      }
      ```
      
      ---
      
      ## Anchor.toml Configuration
      
      ```toml
      [toolchain]
      anchor_version = "0.32.1"
      solana_version = "2.3.0"
      
      [features]
      resolution = true  # IDL account resolution
      seeds = false
      skip-lint = false
      
      [programs.localnet]
      my_program = "YourProgramIdHere11111111111111111111111"
      
      [programs.devnet]
      my_program = "YourProgramIdHere11111111111111111111111"
      
      [programs.mainnet]
      my_program = "YourProgramIdHere11111111111111111111111"
      
      [provider]
      cluster = "localnet"  # or devnet, mainnet-beta
      wallet = "~/.config/solana/id.json"
      
      [scripts]
      test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
      
      [test]
      startup_wait = 10000
      
      [test.validator]
      url = "https://api.devnet.solana.com"
      
      [[test.validator.clone]]
      address = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      
      [test.validator.upgradeable]
      my_program = true
      
      [workspace]
      types = "app/src/idl/"
      members = ["programs/*"]
      
      package_manager = "yarn"  # npm, yarn, pnpm, bun
      ```
      
      ---
      
      ## Additional Resources
      
      - **Official Docs**: https://www.anchor-lang.com
      - **GitHub**: https://github.com/coral-xyz/anchor
      - **Examples**: https://github.com/coral-xyz/anchor/tree/master/tests
      - **Discord**: https://discord.gg/anchor
      
      For general Solana concepts (accounts, PDAs, CPIs, transactions, etc.), refer to other reference files in this directory.
      
    • builtin-programs.md 21.8 KB
      # Built-in Programs
      
      This reference provides comprehensive coverage of Solana's built-in programs for native Rust development, focusing on the System Program and Compute Budget Program.
      
      ## Table of Contents
      
      1. [Overview of Built-in Programs](#overview-of-built-in-programs)
      2. [System Program](#system-program)
      3. [Compute Budget Program](#compute-budget-program)
      4. [Other Built-in Programs](#other-built-in-programs)
      5. [CPI Patterns](#cpi-patterns)
      6. [Best Practices](#best-practices)
      
      ---
      
      ## Overview of Built-in Programs
      
      **Built-in programs** (also called native programs) are fundamental Solana programs that provide core blockchain functionality.
      
      ### Key Built-in Programs
      
      | Program | Program ID | Purpose |
      |---------|-----------|---------|
      | **System Program** | `11111111111111111111111111111111` | Account creation, transfers, allocation |
      | **Compute Budget** | `ComputeBudget111111111111111111111111111111` | CU limits, heap size, priority fees |
      | **BPF Loader** | Various | Loading and executing programs |
      | **Config Program** | `Config1111111111111111111111111111111111111` | Validator configuration |
      | **Stake Program** | `Stake11111111111111111111111111111111111111` | Staking and delegation |
      | **Vote Program** | `Vote111111111111111111111111111111111111111` | Validator voting |
      
      This reference focuses on the two most commonly used in program development: **System Program** and **Compute Budget Program**.
      
      ---
      
      ## System Program
      
      **Program ID:** `solana_program::system_program::ID` (`11111111111111111111111111111111`)
      
      The System Program is responsible for account creation, lamport transfers, and account management.
      
      ### Core Functionality
      
      1. **Create accounts** (regular and PDAs)
      2. **Transfer lamports** between accounts
      3. **Allocate space** for account data
      4. **Assign ownership** to programs
      5. **Create nonce accounts** for durable transactions
      
      ### System Program Instructions
      
      ```rust
      use solana_program::system_instruction;
      
      pub enum SystemInstruction {
          CreateAccount,        // Create new account
          Assign,               // Assign account to program
          Transfer,             // Transfer lamports
          CreateAccountWithSeed,// Create account with seed
          AdvanceNonceAccount,  // Advance nonce
          WithdrawNonceAccount, // Withdraw from nonce
          InitializeNonceAccount, // Initialize nonce
          Allocate,             // Allocate account space
          AllocateWithSeed,     // Allocate with seed
          AssignWithSeed,       // Assign with seed
          TransferWithSeed,     // Transfer with seed
          UpgradeNonceAccount,  // Upgrade nonce (v4)
      }
      ```
      
      ---
      
      ### CreateAccount
      
      **Creates a new account with lamports and data space.**
      
      #### Function Signature
      
      ```rust
      pub fn create_account(
          from_pubkey: &Pubkey,      // Funding account (must be signer)
          to_pubkey: &Pubkey,        // New account address
          lamports: u64,             // Lamports to fund account
          space: u64,                // Bytes of data space
          owner: &Pubkey,            // Program that will own the account
      ) -> Instruction
      ```
      
      #### Usage in Native Rust
      
      ```rust
      use solana_program::{
          system_instruction,
          program::invoke,
      };
      
      pub fn create_new_account(
          payer: &AccountInfo,
          new_account: &AccountInfo,
          system_program: &AccountInfo,
          program_id: &Pubkey,
      ) -> ProgramResult {
          let space = 100;  // Account data size
          let rent = Rent::get()?;
          let lamports = rent.minimum_balance(space);
      
          let create_account_ix = system_instruction::create_account(
              payer.key,
              new_account.key,
              lamports,
              space as u64,
              program_id,
          );
      
          invoke(
              &create_account_ix,
              &[
                  payer.clone(),
                  new_account.clone(),
                  system_program.clone(),
              ],
          )?;
      
          msg!("Created account with {} bytes", space);
          Ok(())
      }
      ```
      
      #### Creating PDA Accounts
      
      ```rust
      use solana_program::program::invoke_signed;
      
      pub fn create_pda_account(
          payer: &AccountInfo,
          pda_account: &AccountInfo,
          system_program: &AccountInfo,
          program_id: &Pubkey,
          seeds: &[&[u8]],
          bump: u8,
      ) -> ProgramResult {
          // Verify PDA
          let (expected_pda, _bump) = Pubkey::find_program_address(seeds, program_id);
          if expected_pda != *pda_account.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          let space = 200;
          let rent = Rent::get()?;
          let lamports = rent.minimum_balance(space);
      
          let create_account_ix = system_instruction::create_account(
              payer.key,
              pda_account.key,
              lamports,
              space as u64,
              program_id,
          );
      
          // Create full seeds with bump
          let mut full_seeds = seeds.to_vec();
          full_seeds.push(&[bump]);
          let signer_seeds: &[&[&[u8]]] = &[&full_seeds];
      
          invoke_signed(
              &create_account_ix,
              &[payer.clone(), pda_account.clone(), system_program.clone()],
              signer_seeds,
          )?;
      
          msg!("Created PDA account at {}", pda_account.key);
          Ok(())
      }
      ```
      
      ---
      
      ### Transfer
      
      **Transfers lamports from one account to another.**
      
      #### Function Signature
      
      ```rust
      pub fn transfer(
          from_pubkey: &Pubkey,     // Source account (must be signer)
          to_pubkey: &Pubkey,       // Destination account
          lamports: u64,            // Amount to transfer
      ) -> Instruction
      ```
      
      #### Usage in Native Rust
      
      ```rust
      pub fn transfer_lamports(
          from: &AccountInfo,
          to: &AccountInfo,
          system_program: &AccountInfo,
          amount: u64,
      ) -> ProgramResult {
          let transfer_ix = system_instruction::transfer(
              from.key,
              to.key,
              amount,
          );
      
          invoke(
              &transfer_ix,
              &[from.clone(), to.clone(), system_program.clone()],
          )?;
      
          msg!("Transferred {} lamports from {} to {}",
              amount, from.key, to.key);
          Ok(())
      }
      ```
      
      #### Transfer from PDA
      
      ```rust
      pub fn transfer_from_pda(
          pda: &AccountInfo,
          to: &AccountInfo,
          system_program: &AccountInfo,
          amount: u64,
          seeds: &[&[u8]],
          bump: u8,
      ) -> ProgramResult {
          let transfer_ix = system_instruction::transfer(
              pda.key,
              to.key,
              amount,
          );
      
          let mut full_seeds = seeds.to_vec();
          full_seeds.push(&[bump]);
          let signer_seeds: &[&[&[u8]]] = &[&full_seeds];
      
          invoke_signed(
              &transfer_ix,
              &[pda.clone(), to.clone(), system_program.clone()],
              signer_seeds,
          )?;
      
          Ok(())
      }
      ```
      
      ---
      
      ### Allocate
      
      **Allocates space for an account's data.**
      
      #### Function Signature
      
      ```rust
      pub fn allocate(
          pubkey: &Pubkey,          // Account to allocate (must be signer)
          space: u64,               // Bytes to allocate
      ) -> Instruction
      ```
      
      #### Usage in Native Rust
      
      ```rust
      pub fn allocate_account_space(
          account: &AccountInfo,
          system_program: &AccountInfo,
          space: u64,
      ) -> ProgramResult {
          let allocate_ix = system_instruction::allocate(
              account.key,
              space,
          );
      
          invoke(
              &allocate_ix,
              &[account.clone(), system_program.clone()],
          )?;
      
          msg!("Allocated {} bytes for account", space);
          Ok(())
      }
      ```
      
      **⚠️ Note:** The account must be owned by the System Program before allocating. Most programs use `create_account` instead, which combines allocation with ownership assignment.
      
      ---
      
      ### Assign
      
      **Assigns an account to a program (changes owner).**
      
      #### Function Signature
      
      ```rust
      pub fn assign(
          pubkey: &Pubkey,          // Account to assign (must be signer)
          owner: &Pubkey,           // New owner program
      ) -> Instruction
      ```
      
      #### Usage in Native Rust
      
      ```rust
      pub fn assign_to_program(
          account: &AccountInfo,
          system_program: &AccountInfo,
          new_owner: &Pubkey,
      ) -> ProgramResult {
          let assign_ix = system_instruction::assign(
              account.key,
              new_owner,
          );
      
          invoke(
              &assign_ix,
              &[account.clone(), system_program.clone()],
          )?;
      
          msg!("Assigned account to program {}", new_owner);
          Ok(())
      }
      ```
      
      **⚠️ Note:** Most programs use `create_account` which handles assignment during creation.
      
      ---
      
      ### Complete Example: Account Lifecycle
      
      ```rust
      use solana_program::{
          account_info::{next_account_info, AccountInfo},
          entrypoint::ProgramResult,
          program::invoke_signed,
          pubkey::Pubkey,
          system_instruction,
          sysvar::{rent::Rent, Sysvar},
      };
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct UserData {
          pub user: Pubkey,
          pub balance: u64,
          pub created_at: i64,
      }
      
      pub fn create_user_account(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          user_pubkey: Pubkey,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let payer = next_account_info(account_info_iter)?;
          let user_account = next_account_info(account_info_iter)?;
          let system_program = next_account_info(account_info_iter)?;
      
          // 1. Derive PDA
          let seeds = &[b"user", user_pubkey.as_ref()];
          let (pda, bump) = Pubkey::find_program_address(seeds, program_id);
      
          if pda != *user_account.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // 2. Calculate space and rent
          let space = std::mem::size_of::<UserData>();
          let rent = Rent::get()?;
          let lamports = rent.minimum_balance(space);
      
          // 3. Create account via System Program CPI
          let create_ix = system_instruction::create_account(
              payer.key,
              user_account.key,
              lamports,
              space as u64,
              program_id,
          );
      
          let signer_seeds: &[&[&[u8]]] = &[&[b"user", user_pubkey.as_ref(), &[bump]]];
      
          invoke_signed(
              &create_ix,
              &[payer.clone(), user_account.clone(), system_program.clone()],
              signer_seeds,
          )?;
      
          // 4. Initialize account data
          let clock = Clock::get()?;
          let user_data = UserData {
              user: user_pubkey,
              balance: 0,
              created_at: clock.unix_timestamp,
          };
      
          user_data.serialize(&mut &mut user_account.data.borrow_mut()[..])?;
      
          msg!("Created user account for {}", user_pubkey);
          Ok(())
      }
      ```
      
      ---
      
      ## Compute Budget Program
      
      **Program ID:** `solana_program::compute_budget::ID` (`ComputeBudget111111111111111111111111111111`)
      
      The Compute Budget Program allows transactions to request specific compute unit limits, heap sizes, and priority fees.
      
      ### Core Functionality
      
      1. **Set compute unit limit** - Maximum CUs for transaction
      2. **Set compute unit price** - Priority fee per CU
      3. **Request heap size** - Heap memory allocation
      
      ### Compute Budget Instructions
      
      ```rust
      use solana_program::compute_budget::ComputeBudgetInstruction;
      
      pub enum ComputeBudgetInstruction {
          RequestUnitsDeprecated,      // Deprecated
          RequestHeapFrame(u32),       // Request heap frame (bytes)
          SetComputeUnitLimit(u32),    // Set max CUs
          SetComputeUnitPrice(u64),    // Set priority fee (microlamports per CU)
          SetLoadedAccountsDataSizeLimit(u32), // Set loaded accounts data limit
      }
      ```
      
      ---
      
      ### SetComputeUnitLimit
      
      **Sets the maximum compute units available to the transaction.**
      
      #### Function Signature
      
      ```rust
      pub fn set_compute_unit_limit(units: u32) -> Instruction
      ```
      
      #### Default Limits
      
      - **Default per instruction:** 200,000 CUs
      - **Default per transaction:** 1,400,000 CUs (with requested CU limit)
      - **Maximum:** 1,400,000 CUs
      
      #### Usage in Native Rust
      
      **Important:** Compute Budget instructions are added to the transaction by the **client**, not inside the program.
      
      **Client-side example (for reference):**
      
      ```rust
      // This code runs CLIENT-SIDE, not in the program
      use solana_sdk::{
          compute_budget::ComputeBudgetInstruction,
          transaction::Transaction,
      };
      
      let compute_budget_ix = ComputeBudgetInstruction::set_compute_unit_limit(400_000);
      
      let transaction = Transaction::new_signed_with_payer(
          &[
              compute_budget_ix,  // Must be first
              your_program_ix,
          ],
          Some(&payer.pubkey()),
          &[&payer],
          recent_blockhash,
      );
      ```
      
      **⚠️ Note:** Programs cannot modify their own compute budget. These instructions must be added client-side before sending the transaction.
      
      ---
      
      ### SetComputeUnitPrice
      
      **Sets the priority fee per compute unit (for transaction prioritization).**
      
      #### Function Signature
      
      ```rust
      pub fn set_compute_unit_price(microlamports: u64) -> Instruction
      ```
      
      #### Priority Fee Calculation
      
      ```
      Total Priority Fee = (CUs Used × microlamports) / 1,000,000
      ```
      
      **Example:**
      - CUs used: 50,000
      - Price: 10,000 microlamports per CU
      - Fee: (50,000 × 10,000) / 1,000,000 = 500 lamports
      
      #### Usage (Client-side)
      
      ```rust
      // Client-side code
      let compute_unit_price_ix = ComputeBudgetInstruction::set_compute_unit_price(20_000);
      
      let transaction = Transaction::new_signed_with_payer(
          &[
              compute_unit_price_ix,  // Set priority fee
              your_program_ix,
          ],
          Some(&payer.pubkey()),
          &[&payer],
          recent_blockhash,
      );
      ```
      
      **Use cases:**
      - High-priority transactions (arbitrage, liquidations)
      - Congested network periods
      - Time-sensitive operations
      
      ---
      
      ### RequestHeapFrame
      
      **Requests additional heap memory for the transaction.**
      
      #### Function Signature
      
      ```rust
      pub fn request_heap_frame(bytes: u32) -> Instruction
      ```
      
      #### Default Heap
      
      - **Default:** 32 KB
      - **Maximum:** 256 KB
      
      #### Usage (Client-side)
      
      ```rust
      // Client-side code
      let heap_size_ix = ComputeBudgetInstruction::request_heap_frame(256 * 1024); // 256 KB
      
      let transaction = Transaction::new_signed_with_payer(
          &[
              heap_size_ix,       // Request more heap
              your_program_ix,
          ],
          Some(&payer.pubkey()),
          &[&payer],
          recent_blockhash,
      );
      ```
      
      **When to use:**
      - Large data structures
      - Complex deserialization
      - Temporary buffers
      
      **⚠️ Cost:** Requesting heap increases CU consumption.
      
      ---
      
      ### SetLoadedAccountsDataSizeLimit
      
      **Sets the maximum total size of loaded account data.**
      
      #### Function Signature
      
      ```rust
      pub fn set_loaded_accounts_data_size_limit(bytes: u32) -> Instruction
      ```
      
      #### Default Limit
      
      - **Default:** 64 MB per transaction
      
      #### Usage (Client-side)
      
      ```rust
      // Client-side code
      let accounts_data_limit_ix =
          ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(128 * 1024 * 1024);
      
      let transaction = Transaction::new_signed_with_payer(
          &[
              accounts_data_limit_ix,
              your_program_ix,
          ],
          Some(&payer.pubkey()),
          &[&payer],
          recent_blockhash,
      );
      ```
      
      **Use cases:**
      - Transactions with many large accounts
      - Bulk processing operations
      
      ---
      
      ### Complete Client-side Example
      
      ```rust
      use solana_sdk::{
          compute_budget::ComputeBudgetInstruction,
          transaction::Transaction,
          signature::{Keypair, Signer},
          pubkey::Pubkey,
      };
      
      pub fn build_optimized_transaction(
          payer: &Keypair,
          program_id: &Pubkey,
          program_ix_data: &[u8],
          accounts: Vec<AccountMeta>,
          recent_blockhash: Hash,
      ) -> Transaction {
          // 1. Set compute unit limit (if default 200k is insufficient)
          let compute_limit_ix = ComputeBudgetInstruction::set_compute_unit_limit(300_000);
      
          // 2. Set priority fee (for faster processing)
          let compute_price_ix = ComputeBudgetInstruction::set_compute_unit_price(10_000);
      
          // 3. Request additional heap if needed
          let heap_size_ix = ComputeBudgetInstruction::request_heap_frame(128 * 1024); // 128 KB
      
          // 4. Your program instruction
          let program_ix = Instruction {
              program_id: *program_id,
              accounts,
              data: program_ix_data.to_vec(),
          };
      
          // 5. Build transaction (compute budget instructions FIRST)
          Transaction::new_signed_with_payer(
              &[
                  compute_limit_ix,
                  compute_price_ix,
                  heap_size_ix,
                  program_ix,
              ],
              Some(&payer.pubkey()),
              &[payer],
              recent_blockhash,
          )
      }
      ```
      
      ---
      
      ## Other Built-in Programs
      
      ### BPF Loader
      
      **Purpose:** Loads and executes Solana programs.
      
      **Program IDs:**
      - `BPFLoader1111111111111111111111111111111111` (deprecated)
      - `BPFLoader2111111111111111111111111111111111` (upgradeable)
      - `BPFLoaderUpgradeab1e11111111111111111111111` (current)
      
      **Usage:** Primarily used by the runtime. Programs rarely interact with BPF Loader directly.
      
      ### Stake Program
      
      **Program ID:** `Stake11111111111111111111111111111111111111`
      
      **Purpose:** Staking SOL to validators.
      
      **Common operations:**
      - Create stake accounts
      - Delegate stake
      - Deactivate stake
      - Withdraw stake
      
      **Use case:** Staking pools, liquid staking protocols.
      
      ### Vote Program
      
      **Program ID:** `Vote111111111111111111111111111111111111111`
      
      **Purpose:** Validator voting and consensus.
      
      **Use case:** Validator operations, rarely used by general programs.
      
      ---
      
      ## CPI Patterns
      
      ### System Program CPI Pattern
      
      **Standard pattern for calling System Program:**
      
      ```rust
      use solana_program::{
          program::invoke,
          system_instruction,
      };
      
      pub fn system_program_cpi(
          from: &AccountInfo,
          to: &AccountInfo,
          system_program: &AccountInfo,
      ) -> ProgramResult {
          // 1. Verify System Program
          if system_program.key != &solana_program::system_program::ID {
              return Err(ProgramError::IncorrectProgramId);
          }
      
          // 2. Create instruction
          let ix = system_instruction::transfer(from.key, to.key, 1_000_000);
      
          // 3. Invoke
          invoke(&ix, &[from.clone(), to.clone(), system_program.clone()])?;
      
          Ok(())
      }
      ```
      
      ### PDA Signing Pattern
      
      **When PDAs need to sign:**
      
      ```rust
      pub fn pda_system_cpi(
          pda: &AccountInfo,
          to: &AccountInfo,
          system_program: &AccountInfo,
          program_id: &Pubkey,
          seeds: &[&[u8]],
          bump: u8,
      ) -> ProgramResult {
          // 1. Verify PDA
          let (expected_pda, _) = Pubkey::find_program_address(seeds, program_id);
          if expected_pda != *pda.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // 2. Create instruction
          let ix = system_instruction::transfer(pda.key, to.key, 500_000);
      
          // 3. Prepare signer seeds
          let mut full_seeds = seeds.to_vec();
          full_seeds.push(&[bump]);
          let signer_seeds: &[&[&[u8]]] = &[&full_seeds];
      
          // 4. Invoke with PDA signature
          invoke_signed(
              &ix,
              &[pda.clone(), to.clone(), system_program.clone()],
              signer_seeds,
          )?;
      
          Ok(())
      }
      ```
      
      ### Validation Pattern
      
      **Always validate accounts before CPI:**
      
      ```rust
      pub fn safe_system_cpi(
          from: &AccountInfo,
          to: &AccountInfo,
          system_program: &AccountInfo,
          amount: u64,
      ) -> ProgramResult {
          // ✅ Validate System Program
          if system_program.key != &solana_program::system_program::ID {
              msg!("Invalid System Program");
              return Err(ProgramError::IncorrectProgramId);
          }
      
          // ✅ Validate signer
          if !from.is_signer {
              msg!("From account must be signer");
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          // ✅ Validate sufficient balance
          if from.lamports() < amount {
              msg!("Insufficient balance");
              return Err(ProgramError::InsufficientFunds);
          }
      
          // Execute CPI
          let ix = system_instruction::transfer(from.key, to.key, amount);
          invoke(&ix, &[from.clone(), to.clone(), system_program.clone()])?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Best Practices
      
      ### 1. Always Validate Program IDs
      
      ```rust
      // ✅ Validate before CPI
      if system_program.key != &solana_program::system_program::ID {
          return Err(ProgramError::IncorrectProgramId);
      }
      ```
      
      ### 2. Use Rent Exemption
      
      ```rust
      // ✅ Always create accounts with rent exemption
      let rent = Rent::get()?;
      let lamports = rent.minimum_balance(space);
      
      // ❌ Don't use arbitrary amounts
      let lamports = 1_000_000; // May not be rent-exempt!
      ```
      
      ### 3. Verify PDA Before Creation
      
      ```rust
      // ✅ Verify PDA derivation
      let (expected_pda, bump) = Pubkey::find_program_address(seeds, program_id);
      if expected_pda != *pda_account.key {
          return Err(ProgramError::InvalidSeeds);
      }
      ```
      
      ### 4. Use invoke_signed for PDAs
      
      ```rust
      // ✅ PDAs sign with invoke_signed
      invoke_signed(&ix, accounts, signer_seeds)?;
      
      // ❌ Regular invoke won't work for PDA signers
      invoke(&ix, accounts)?; // Fails if PDA needs to sign
      ```
      
      ### 5. Set Compute Budget Client-side
      
      ```rust
      // ✅ Add compute budget instructions in client
      let ixs = vec![
          ComputeBudgetInstruction::set_compute_unit_limit(400_000),
          your_program_ix,
      ];
      
      // ❌ Cannot set from within program
      // Programs cannot modify their own compute budget
      ```
      
      ### 6. Order Compute Budget Instructions First
      
      ```rust
      // ✅ Compute budget instructions FIRST
      let ixs = vec![
          compute_limit_ix,
          compute_price_ix,
          heap_size_ix,
          program_ix,
      ];
      
      // ❌ Wrong order - may not apply
      let ixs = vec![
          program_ix,
          compute_limit_ix,  // Too late!
      ];
      ```
      
      ### 7. Check Account Ownership Before Transfer
      
      ```rust
      // ✅ Validate ownership for security
      if from_account.owner != &solana_program::system_program::ID {
          msg!("Can only transfer from System-owned accounts");
          return Err(ProgramError::IllegalOwner);
      }
      ```
      
      ---
      
      ## Summary
      
      **Key Takeaways:**
      
      1. **System Program** handles account creation, transfers, and allocation
      2. **Compute Budget Program** instructions are added **client-side**, not in programs
      3. **Always validate** program IDs before CPI
      4. **Use rent exemption** when creating accounts
      5. **PDAs require invoke_signed** for signing operations
      
      **Most Common Operations:**
      
      | Operation | Instruction | Use Case |
      |-----------|------------|----------|
      | Create account | `create_account` | New program accounts |
      | Transfer lamports | `transfer` | SOL transfers |
      | Set CU limit | `set_compute_unit_limit` | High-CU transactions |
      | Set priority fee | `set_compute_unit_price` | Fast transaction processing |
      | Request heap | `request_heap_frame` | Large data operations |
      
      **System Program CPI Template:**
      
      ```rust
      // Validate
      if system_program.key != &solana_program::system_program::ID {
          return Err(ProgramError::IncorrectProgramId);
      }
      
      // Create instruction
      let ix = system_instruction::transfer(from.key, to.key, amount);
      
      // Invoke (or invoke_signed for PDAs)
      invoke(&ix, &[from.clone(), to.clone(), system_program.clone()])?;
      ```
      
      **Compute Budget Client Template:**
      
      ```rust
      // Client-side
      let ixs = vec![
          ComputeBudgetInstruction::set_compute_unit_limit(300_000),
          ComputeBudgetInstruction::set_compute_unit_price(10_000),
          your_program_ix,
      ];
      ```
      
      Master these built-in programs for efficient account management and transaction optimization in production Solana programs.
      
    • caveats.md 11.1 KB
      # Important Caveats
      
      Critical limitations, quirks, and gotchas in Solana and Anchor development that every security reviewer must know.
      
      ## Anchor Framework Limitations
      
      ### 1. `init_if_needed` Re-initialization Risk
      
      ```rust
      // Dangerous: Can bypass initialization logic
      #[account(init_if_needed, payer = user, space = ...)]
      pub user_account: Account<'info, UserAccount>,
      ```
      
      **Issue:** If account already exists, initialization is skipped entirely. Existing malicious or inconsistent data is not validated.
      
      **When to use:** Only when you explicitly validate existing accounts in instruction logic.
      
      ### 2. `AccountLoader` Missing Discriminator Check
      
      ```rust
      // Does NOT validate discriminator by default!
      #[account(mut)]
      pub user: AccountLoader<'info, User>,
      ```
      
      **Issue:** `AccountLoader` is for zero-copy accounts and doesn't check the account discriminator automatically. Enables type cosplay attacks.
      
      **Solution:** Use `Account<'info, T>` when possible, or add manual discriminator check.
      
      ### 3. `close` Constraint Ordering
      
      ```rust
      // ❌ Wrong: close must be last
      #[account(
          close = receiver,
          mut,
          has_one = authority
      )]
      
      // ✅ Correct: close is last
      #[account(
          mut,
          has_one = authority,
          close = receiver
      )]
      ```
      
      **Issue:** Anchor processes constraints in order. If `close` isn't last, subsequent constraints may check zeroed account.
      
      ### 4. Space Calculation Errors Are Permanent
      
      ```rust
      // If this space is wrong, account is unusable!
      #[account(
          init,
          payer = user,
          space = 8 + 32  // Too small = can't deserialize later!
      )]
      pub user_account: Account<'info, UserAccount>,
      ```
      
      **Issue:** Once initialized, account size is fixed. Too small = deserialization fails. Too large = wasted rent.
      
      **Solution:** Always use `InitSpace` derive macro:
      ```rust
      #[account]
      #[derive(InitSpace)]
      pub struct UserAccount {
          pub authority: Pubkey,
          #[max_len(100)]
          pub name: String,
      }
      
      // Then use:
      space = 8 + UserAccount::INIT_SPACE
      ```
      
      ### 5. `constraint` Expression Limitations
      
      ```rust
      // constraint expressions can't call functions that return Results!
      #[account(
          constraint = some_validation(account.value)? @ ErrorCode::Invalid  // Compile error!
      )]
      ```
      
      **Issue:** Constraint expressions must be simple boolean checks. Cannot use `?` operator.
      
      **Solution:** Validate in instruction body for complex checks.
      
      ## Solana Runtime Quirks
      
      ### 1. Account Data Persists After Zeroing Lamports
      
      ```rust
      // Within same transaction:
      **account.lamports.borrow_mut() = 0;
      let data = account.try_borrow_data()?;  // Still readable!
      ```
      
      **Issue:** Account data remains accessible within the transaction even after lamports are zeroed. Only garbage collected after transaction completes.
      
      **Implication:** Always check lamports before reading account data.
      
      ### 2. Non-Canonical PDA Bumps
      
      ```rust
      // Multiple PDAs possible with different bumps!
      let (pda_255, bump_255) = Pubkey::find_program_address(seeds, program_id);  // bump = 255
      let (pda_254, bump_254) = Pubkey::create_program_address(&[seeds, &[254]], program_id);  // Also valid!
      ```
      
      **Issue:** Same seeds can derive multiple PDAs with different bumps. Creates confusion and potential exploits.
      
      **Solution:** Always use canonical bump (255 counting down to first valid). Anchor's `bump` constraint enforces this.
      
      ### 3. Compute Budget Limits
      
      | Network | Base Compute Units | With Optimization |
      |---------|-------------------|-------------------|
      | Mainnet | 200,000 | Up to 1,400,000 (with request) |
      | Devnet  | 200,000 | Up to 1,400,000 |
      
      **Issue:** Complex programs can exceed compute budget, causing transaction failure.
      
      **Optimization strategies:**
      - Minimize CPIs (each costs ~1000 CU)
      - Use `AccountLoader` for large accounts
      - Avoid loops with variable length
      - Request higher compute budget: `ComputeBudgetProgram::set_compute_unit_limit()`
      
      ### 4. Transaction Size Limit
      
      **Hard limit:** ~1232 bytes for transaction
      
      **Implications:**
      - Limits number of accounts (~35-40 accounts typical max)
      - Large instructions need Account Compression or chunking
      - Can't pass large data directly in instruction
      
      **Solutions:**
      - Use PDAs to store large data
      - Break operations into multiple transactions
      - Use lookup tables for frequent accounts
      
      ### 5. Account Snapshot Loading
      
      ```rust
      let balance_before = ctx.accounts.vault.balance;
      // CPI happens here
      // balance_before is STALE - account was loaded before CPI
      ```
      
      **Issue:** Accounts are loaded as snapshots at transaction start. Modifications during transaction (via CPIs) don't update the loaded data.
      
      **Solution:** Call `.reload()` after any CPI that might modify the account.
      
      ## Token Program Gotchas
      
      ### 1. ATA Addresses Are Deterministic But Not Guaranteed
      
      ```rust
      let ata = get_associated_token_address(&owner, &mint);
      // ata address is deterministic but account might not exist!
      ```
      
      **Issue:** ATA address can be calculated but account may not be initialized.
      
      **Solution:** Check account exists and is initialized before use, or use `init_if_needed` with proper validation.
      
      ### 2. Delegates Don't Automatically Reset
      
      ```rust
      // After transfer of ownership:
      token_account.owner = new_owner;
      // BUT: delegate and delegated_amount are NOT reset!
      ```
      
      **Issue:** Changing owner doesn't clear delegate/close authority. Old delegate can still spend.
      
      **Solution:** Explicitly reset authorities when changing ownership:
      ```rust
      account.delegate = COption::None;
      account.delegated_amount = 0;
      if account.is_native() {
          account.close_authority = COption::None;
      }
      ```
      
      ### 3. Token-2022 Extension Rent
      
      **Issue:** Each extension adds rent cost. Account size varies by extensions enabled.
      
      **Extensions and their sizes:**
      - Transfer Fee: ~83 bytes
      - Transfer Hook: ~107 bytes
      - Permanent Delegate: ~36 bytes
      - Interest Bearing: ~40 bytes
      
      **Solution:** Calculate rent based on all enabled extensions.
      
      ### 4. Token-2022 Transfer Hooks Can Be Malicious
      
      ```rust
      // Transfer hook can call arbitrary program!
      pub struct TransferHookAccount {
          pub program_id: Pubkey,  // Could be malicious
      }
      ```
      
      **Issue:** Transfer hook extensions allow calling external program during transfers. Malicious hook can fail transaction or drain funds.
      
      **Solution:**
      - Validate transfer hook program if accepting specific tokens
      - Consider disallowing tokens with transfer hooks
      - Use Anchor's `TransferChecked` instruction
      
      ## Testing Blind Spots
      
      ### 1. Concurrent Transaction Ordering
      
      **Issue:** Tests typically run transactions sequentially. In production, concurrent transactions can interleave in unexpected ways.
      
      **Vulnerability example:**
      ```rust
      // Transaction 1: Check balance = 100
      // Transaction 2: Withdraw 80 (balance now 20)
      // Transaction 1: Withdraw 80 (uses stale check, balance now -60!)
      ```
      
      **Mitigation:**
      - Use atomic operations
      - Reload accounts before critical operations
      - Design for idempotency
      
      ### 2. Account Rent Reclaim Attacks
      
      **Issue:** When account rent falls below minimum, validator can reclaim the account. Tests don't simulate this.
      
      **Solution:** Ensure all accounts are rent-exempt (2+ years of rent).
      
      ### 3. Sysvar Manipulation in Tests
      
      ```rust
      // In tests, you can set arbitrary clock values
      ctx.accounts.clock = Clock { unix_timestamp: attacker_value, ... };
      ```
      
      **Issue:** Tests may not catch reliance on tamper-resistant sysvars.
      
      **Solution:** In production, always load sysvars from official sysvar accounts:
      ```rust
      pub clock: Sysvar<'info, Clock>,  // Validated address
      ```
      
      ### 4. Devnet vs Mainnet Differences
      
      | Aspect | Devnet | Mainnet |
      |--------|--------|---------|
      | Oracle prices | Often stale/fake | Real-time |
      | Program versions | May differ | Stable versions |
      | Compute limits | More lenient | Strict |
      | Congestion | Minimal | Can be high |
      | Token availability | Test tokens | Real value |
      
      **Issue:** Programs tested only on devnet may fail on mainnet.
      
      **Solution:** Test on mainnet-fork or mainnet with small amounts before full deployment.
      
      ## Rust-Specific Gotchas
      
      ### 1. `unwrap()` Panics
      
      ```rust
      // Panics kill the entire transaction!
      let value = some_option.unwrap();  // ❌ Never do this
      ```
      
      **Solution:** Always use proper error handling:
      ```rust
      let value = some_option.ok_or(ErrorCode::MissingValue)?;
      ```
      
      ### 2. Integer Division Truncation
      
      ```rust
      let result = 5 / 2;  // result = 2, not 2.5!
      ```
      
      **Issue:** Integer division truncates, potentially causing precision loss in financial calculations.
      
      **Solution:** Use `Decimal` type for precise calculations, or multiply before divide:
      ```rust
      let result = (5 * PRECISION) / 2 / PRECISION;
      ```
      
      ### 3. Overflow in Debug vs Release
      
      ```rust
      // Debug mode: panics on overflow
      // Release mode: wraps silently!
      let x: u8 = 255;
      let y = x + 1;  // Debug: panic, Release: y = 0
      ```
      
      **Solution:** Always use `checked_*` methods - they work same in debug and release.
      
      ## Cross-Program Invocation (CPI) Gotchas
      
      ### 1. CPI Success Doesn't Guarantee Correct State
      
      ```rust
      // CPI returns success but state may be unexpected
      invoke(&transfer_instruction, &accounts)?;
      // Transfer succeeded but amount might be different due to fees!
      ```
      
      **Solution:** Reload and validate account state after CPI.
      
      ### 2. Signer Seeds Must Be Exact
      
      ```rust
      // Seeds for signing must match PDA derivation exactly
      let seeds = &[
          b"vault",
          user.key().as_ref(),
          &[bump],  // Must be same bump used to derive PDA
      ];
      
      invoke_signed(&instruction, &accounts, &[seeds])?;
      ```
      
      **Issue:** Wrong seeds = "signature verification failed" error.
      
      ### 3. CPI Depth Limit
      
      **Limit:** 4 levels of CPI depth
      
      **Issue:** Program A → Program B → Program C → Program D → Program E (fails!)
      
      **Solution:** Design programs to minimize CPI depth.
      
      ## Common Misunderstandings
      
      ### 1. "Anchor Prevents All Security Issues"
      
      **False:** Anchor prevents some common issues (missing discriminators, wrong account types) but doesn't validate business logic, arithmetic, or authorization.
      
      ### 2. "Devnet Testing Is Sufficient"
      
      **False:** Mainnet has different compute limits, real oracle data, congestion, and MEV considerations.
      
      ### 3. "One Audit Makes Code Secure"
      
      **False:** Audits find issues in a snapshot. Code changes after audit reintroduce risk. Need continuous security review.
      
      ### 4. "`checked_*` Methods Are Slower"
      
      **False:** Rust compiler optimizes these similarly to unchecked arithmetic. Always use checked methods.
      
      ### 5. "PDAs Can't Sign"
      
      **True for external transactions, false for CPIs:** PDAs can sign CPIs using `invoke_signed` but can't sign transactions directly.
      
      ## Version-Specific Issues
      
      ### Anchor Version Compatibility
      
      - **< 0.28**: No `InitSpace` derive, manual space calculation error-prone
      - **< 0.29**: Different constraint syntax
      - **0.30+**: Breaking changes in error handling and account initialization
      
      **Solution:** Check `Cargo.toml` for version and consult [Anchor Changelog](https://github.com/coral-xyz/anchor/blob/master/CHANGELOG.md).
      
      ### Solana Version Differences
      
      - **Pre-1.14**: Different fee structure
      - **Pre-1.16**: No Address Lookup Tables
      - **Pre-1.17**: No Token-2022
      
      **Solution:** Verify target Solana version matches deployment network.
      
      ---
      
      **Key Takeaway:** Many "obvious" assumptions about blockchain behavior don't hold in Solana. Always validate against actual runtime behavior, not assumptions from other chains.
      
    • client-development.md 8.5 KB
      # Client-Side Development
      
      Guide for building Solana dApp frontends: wallet connections, balance queries, token transfers, and transaction management.
      
      ## Which Library?
      
      | Building | Use | Install |
      |----------|-----|---------|
      | React/Next.js dApp | framework-kit (`@solana/client` + `@solana/react-hooks`) | `npm i @solana/client @solana/react-hooks` |
      | Non-React frontend (Svelte, Vue) | `@solana/client` standalone | `npm i @solana/client` |
      | Server-side scripts, bots, CLIs | `@solana/kit` 6.x | `npm i @solana/kit` |
      | Anchor program TS client | `@coral-xyz/anchor` (requires `@solana/web3.js` v1) | `npm i @coral-xyz/anchor @solana/web3.js` |
      | Migrating from web3.js v1 | `@solana/web3-compat` | `npm i @solana/web3-compat` |
      
      > **Default recommendation:** Use framework-kit for any frontend. It handles wallet discovery, state management, and caching out of the box. Use `@solana/kit` 6.x only when you need raw RPC control without framework-kit's abstractions (server-side scripts, bots, CLIs).
      
      ## Framework-Kit
      
      Solana Foundation's official dApp framework. Built on `@solana/kit` 5.x + Zustand (state) + SWR (React caching).
      
      - [Documentation](https://www.framework-kit.com/) | [GitHub](https://github.com/solana-foundation/framework-kit)
      
      ### Setup (React)
      
      ```tsx
      import { autoDiscover, createClient } from "@solana/client";
      import { SolanaProvider, useWalletConnection, useBalance } from "@solana/react-hooks";
      
      const client = createClient({
        cluster: "devnet", // or "mainnet" | "testnet" | "localnet"
        walletConnectors: autoDiscover(),
      });
      
      export function App() {
        return (
          <SolanaProvider client={client}>
            <WalletPanel />
          </SolanaProvider>
        );
      }
      ```
      
      Cluster monikers auto-resolve RPC + WebSocket URLs. Use `endpoint` for custom RPC:
      
      ```ts
      const client = createClient({
        endpoint: "https://your-rpc.example.com",
        walletConnectors: autoDiscover(),
      });
      ```
      
      Filter which wallets appear:
      
      ```ts
      import { autoDiscover, filterByNames } from "@solana/client";
      
      const client = createClient({
        cluster: "devnet",
        walletConnectors: autoDiscover({ filter: filterByNames("phantom", "solflare") }),
      });
      ```
      
      > **Next.js / RSC:** Components using these hooks must be marked with `'use client'`.
      
      ### Wallet Connection
      
      ```tsx
      function WalletPanel() {
        const { connectors, connect, disconnect, wallet, status, currentConnector } =
          useWalletConnection();
        const address = wallet?.account.address;
        const balance = useBalance(address);
      
        if (status === "connected") {
          return (
            <div>
              <p>Connected via {currentConnector?.name}</p>
              <p>{address?.toString()}</p>
              <p>Lamports: {balance.lamports?.toString() ?? "loading..."}</p>
              <button onClick={disconnect}>Disconnect</button>
            </div>
          );
        }
      
        return connectors.map((c) => (
          <button key={c.id} onClick={() => connect(c.id)}>
            Connect {c.name}
          </button>
        ));
      }
      ```
      
      ### Send SOL
      
      ```tsx
      import { useSolTransfer } from "@solana/react-hooks";
      
      function SendSol({ destination }: { destination: string }) {
        const { send, isSending, status, signature, error } = useSolTransfer();
        return (
          <div>
            <button
              disabled={isSending}
              onClick={() => send({ destination, amount: 100_000_000n /* 0.1 SOL */ })}
            >
              {isSending ? "Sending..." : "Send 0.1 SOL"}
            </button>
            {signature ? <p>Signature: {signature}</p> : null}
            {error ? <p>Error: {String(error)}</p> : null}
          </div>
        );
      }
      ```
      
      ### SPL Token Balance + Transfer
      
      ```tsx
      import { useSplToken } from "@solana/react-hooks";
      
      function TokenPanel({ mint, destinationOwner }: { mint: string; destinationOwner: string }) {
        const { balance, send, isSending, status, error, sendSignature } = useSplToken(mint);
      
        if (status === "disconnected") return <p>Connect wallet to view balance</p>;
        if (status === "loading") return <p>Loading balance...</p>;
      
        return (
          <div>
            <p>Balance: {balance?.uiAmount ?? "0"}</p>
            <button
              disabled={isSending}
              onClick={() => send({ amount: 1n, destinationOwner, amountInBaseUnits: true })}
            >
              {isSending ? "Sending..." : "Send 1 token"}
            </button>
            {sendSignature ? <p>Signature: {sendSignature}</p> : null}
          </div>
        );
      }
      ```
      
      Token 2022 mints: pass `config: { tokenProgram: "auto" }` as second argument to `useSplToken`.
      
      ### Arbitrary Transactions
      
      ```tsx
      import type { TransactionInstructionInput } from "@solana/client";
      import { useTransactionPool, useWalletSession } from "@solana/react-hooks";
      
      function TransactionFlow({ ix }: { ix: TransactionInstructionInput }) {
        const session = useWalletSession();
        const { addInstruction, prepareAndSend, isSending, sendSignature, sendError } =
          useTransactionPool();
      
        return (
          <div>
            <button onClick={() => addInstruction(ix)}>Add instruction</button>
            <button
              disabled={isSending || !session}
              onClick={() => prepareAndSend({ authority: session })}
            >
              {isSending ? "Sending..." : "Prepare & Send"}
            </button>
            {sendSignature ? <p>Signature: {sendSignature}</p> : null}
            {sendError ? <p>{String(sendError)}</p> : null}
          </div>
        );
      }
      ```
      
      For simpler cases where you already have instructions:
      
      ```tsx
      import { useSendTransaction } from "@solana/react-hooks";
      
      const { send, isSending, signature, error } = useSendTransaction();
      await send({ instructions });
      ```
      
      ### Non-React Usage (`@solana/client` standalone)
      
      `@solana/client` works without React in any JS runtime:
      
      ```ts
      import { autoDiscover, createClient } from "@solana/client";
      
      const client = createClient({
        cluster: "devnet",
        walletConnectors: autoDiscover(),
      });
      
      // Connect wallet
      await client.actions.connectWallet("wallet-standard:phantom");
      
      // Read balance
      const wallet = client.store.getState().wallet;
      if (wallet.status === "connected") {
        const lamports = await client.actions.fetchBalance(wallet.session.account.address);
        console.log(`Lamports: ${lamports.toString()}`);
      }
      
      // Send SOL
      const signature = await client.solTransfer.sendTransfer({
        amount: 100_000_000n,
        authority: wallet.session,
        destination: "Ff34MXWdgNsEJ1kJFj9cXmrEe7y2P93b95mGu5CJjBQJ",
      });
      
      // SPL token
      const usdc = client.splToken({ mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" });
      const balance = await usdc.fetchBalance(wallet.session.account.address);
      
      // Arbitrary transaction
      import { getTransferSolInstruction } from "@solana-program/system";
      
      const prepared = await client.transaction.prepare({
        authority: wallet.session,
        instructions: [
          getTransferSolInstruction({
            destination: "Ff34MXWdgNsEJ1kJFj9cXmrEe7y2P93b95mGu5CJjBQJ",
            lamports: 10_000n,
            source: wallet.session.account.address,
          }),
        ],
      });
      const sig = await client.transaction.send(prepared);
      ```
      
      ### Additional Hooks
      
      | Hook | Purpose |
      |------|---------|
      | `useAccount(address)` | Fetch + watch account data (lamports, owner, slot) |
      | `useWaitForSignature(sig, opts)` | Track confirmation status |
      | `useProgramAccounts(program)` | Query all accounts owned by a program (wrap in `SolanaQueryProvider`) |
      | `useSimulateTransaction(wire)` | Simulate transaction before sending |
      | `useLookupTable(address)` | Fetch address lookup table |
      | `useNonceAccount(address)` | Fetch durable nonce account |
      | `useClientStore(selector)` | Direct access to Zustand store |
      
      ## @solana/kit 6.x (Server-Side / Direct Control)
      
      For server-side scripts, bots, and CLIs where you don't need wallet connection UI or React state management. Kit 6.x provides functional, tree-shakeable primitives for RPC, transaction building, and signing.
      
      - [Documentation](https://solanakit.org) | [npm](https://www.npmjs.com/package/@solana/kit)
      
      This is a lower-level SDK. If you're building a frontend dApp, use framework-kit instead.
      
      ## Version Compatibility
      
      - **Framework-kit** uses `@solana/kit ^5.0.0` internally. You don't manage this - it's a framework-kit dependency.
      - **`@solana/kit` 6.x** exists as a standalone SDK for direct use (server-side, scripts).
      - These don't conflict: framework-kit handles its own Kit 5.x; if you also need Kit 6.x for server-side code, they're separate packages.
      - **`@solana/web3-compat`** bridges legacy `@solana/web3.js` v1 code to Kit primitives for incremental migration.
      
      ## Resources
      
      - [Framework-kit docs](https://www.framework-kit.com/)
      - [Framework-kit GitHub](https://github.com/solana-foundation/framework-kit)
      - [Vite + React example](https://github.com/solana-foundation/framework-kit/tree/main/examples/vite-react)
      - [Next.js example](https://github.com/solana-foundation/framework-kit/tree/main/examples/nextjs)
      - [@solana/kit docs](https://solanakit.org)
      
    • client-integration.md 11.2 KB
      # Client Integration
      
      Integrate ZK Compression into TypeScript/JavaScript and Rust applications using Light Protocol SDKs and Helius/Photon RPC.
      
      ## TypeScript Setup
      
      ### Installation
      
      ```bash
      npm install @lightprotocol/stateless.js @lightprotocol/compressed-token helius-sdk
      ```
      
      ### RPC Connection
      
      ```typescript
      import { createRpc, Rpc } from '@lightprotocol/stateless.js';
      
      // Local development (requires light test-validator)
      const rpc = createRpc();
      
      // Helius mainnet
      const rpc = createRpc('https://mainnet.helius-rpc.com?api-key=YOUR_API_KEY');
      
      // Helius devnet
      const rpc = createRpc('https://devnet.helius-rpc.com?api-key=YOUR_API_KEY');
      
      // Custom Photon endpoint
      const rpc = createRpc('https://your-photon-instance.com');
      ```
      
      ### Using Helius SDK
      
      ```typescript
      import { Helius } from 'helius-sdk';
      
      const helius = new Helius('YOUR_API_KEY');
      
      // All ZK compression methods available via helius.zk
      const account = await helius.zk.getCompressedAccount({ address });
      const accounts = await helius.zk.getCompressedAccountsByOwner(owner);
      const tokenAccounts = await helius.zk.getCompressedTokenAccountsByOwner(owner);
      ```
      
      ## RPC Methods Reference
      
      ### Account Methods
      
      #### getCompressedAccount
      
      Fetch a single compressed account by address or hash.
      
      ```typescript
      // By address (for accounts with persistent address)
      const account = await rpc.getCompressedAccount({
          address: new PublicKey('...'),
      });
      
      // By hash
      const account = await rpc.getCompressedAccount({
          hash: '...',  // Base58 encoded hash
      });
      
      // Response
      interface CompressedAccount {
          hash: string;
          address?: string;
          owner: string;
          lamports: number;
          data?: {
              discriminator: number[];
              data: string;      // Base64 encoded
              dataHash: string;
          };
          leafIndex: number;
          tree: string;
          seq: number;
      }
      ```
      
      #### getCompressedAccountsByOwner
      
      Fetch all compressed accounts owned by a program for a specific owner.
      
      ```typescript
      const accounts = await rpc.getCompressedAccountsByOwner(
          ownerPubkey,
          {
              cursor?: string,      // Pagination cursor
              limit?: number,       // Max results (default 100)
              dataSlice?: {         // Return partial data
                  offset: number,
                  length: number,
              },
          }
      );
      
      // Response
      interface PaginatedResult<T> {
          items: T[];
          cursor?: string;  // For next page
      }
      ```
      
      #### getMultipleCompressedAccounts
      
      Batch fetch multiple accounts.
      
      ```typescript
      const accounts = await rpc.getMultipleCompressedAccounts({
          addresses: [addr1, addr2],  // Or use hashes
      });
      ```
      
      ### Token Methods
      
      #### getCompressedTokenAccountsByOwner
      
      ```typescript
      const tokenAccounts = await rpc.getCompressedTokenAccountsByOwner(
          owner,
          {
              mint?: PublicKey,  // Filter by mint
              cursor?: string,
              limit?: number,
          }
      );
      
      // Response item
      interface CompressedTokenAccount {
          parsed: {
              mint: string;
              owner: string;
              amount: string;      // BigInt as string
              delegate?: string;
              state: 'initialized' | 'frozen';
          };
          hash: string;
          // ... account metadata
      }
      ```
      
      #### getCompressedTokenBalancesByOwner
      
      Get aggregated balances across all token accounts.
      
      ```typescript
      const balances = await rpc.getCompressedTokenBalancesByOwner(owner);
      
      // Response
      interface TokenBalance {
          mint: string;
          balance: string;  // Total across all accounts
      }
      ```
      
      #### getCompressedMintTokenHolders
      
      ```typescript
      const holders = await rpc.getCompressedMintTokenHolders(mint, {
          cursor?: string,
          limit?: number,
      });
      ```
      
      ### Proof Methods
      
      #### getValidityProof
      
      Fetch ZK proof for account inclusion.
      
      ```typescript
      const proof = await rpc.getValidityProof({
          hashes: [hash1, hash2],           // Existing accounts
          newAddresses?: [address1],        // New addresses being created
          newAddressesV2?: [...],           // V2 format addresses
      });
      
      // Response
      interface ValidityProof {
          compressedProof: {
              a: number[];   // 64 bytes
              b: number[];   // 128 bytes
              c: number[];   // 64 bytes
          };
          roots: string[];
          rootIndices: number[];
          leafIndices: number[];
          leaves: string[];
          merkleTrees: string[];
          nullifierQueues: string[];
          // Address proof data if newAddresses provided
      }
      ```
      
      #### getMultipleCompressedAccountProofs
      
      Get Merkle proofs for multiple accounts.
      
      ```typescript
      const proofs = await rpc.getMultipleCompressedAccountProofs({
          hashes: [hash1, hash2],
      });
      ```
      
      ### Signature Methods
      
      #### getCompressionSignaturesForAccount
      
      ```typescript
      const signatures = await rpc.getCompressionSignaturesForAccount(
          accountHash,
          {
              cursor?: string,
              limit?: number,
          }
      );
      ```
      
      #### getCompressionSignaturesForOwner
      
      ```typescript
      const signatures = await rpc.getCompressionSignaturesForOwner(owner);
      ```
      
      #### getLatestCompressionSignatures
      
      ```typescript
      const signatures = await rpc.getLatestCompressionSignatures({
          cursor?: string,
          limit?: number,
      });
      ```
      
      ### Indexer Health
      
      ```typescript
      // Check indexer status
      const health = await rpc.getIndexerHealth();
      
      // Get current indexed slot
      const slot = await rpc.getIndexerSlot();
      ```
      
      ## Building Transactions
      
      ### Transaction with Compressed Accounts
      
      > Light Protocol SDK uses `@solana/web3.js` v1 types internally. These imports are required by the SDK - for non-Light client code, use [`@solana/kit`](https://solanakit.org) instead.
      
      ```typescript
      import {
          createRpc,
          buildAndSignTx,
          sendAndConfirmTx,
      } from '@lightprotocol/stateless.js';
      import {
          TransactionInstruction,
          PublicKey,
          Keypair,
      } from '@solana/web3.js'; // Required by Light SDK
      
      async function executeCompressedTransaction(
          rpc: Rpc,
          payer: Keypair,
          programId: PublicKey,
          accounts: CompressedAccountMeta[],
      ) {
          // 1. Fetch validity proof
          const hashes = accounts.map(a => a.hash);
          const { compressedProof, ...proofMetadata } = await rpc.getValidityProof({
              hashes,
          });
      
          // 2. Build instruction with proof and account data
          const instruction = new TransactionInstruction({
              programId,
              keys: [
                  { pubkey: payer.publicKey, isSigner: true, isWritable: true },
                  // Add Light Protocol accounts from proofMetadata
                  ...buildLightAccountMetas(proofMetadata),
              ],
              data: Buffer.from([
                  // Your instruction data + serialized proof
              ]),
          });
      
          // 3. Build and send transaction
          const { blockhash } = await rpc.getLatestBlockhash();
          const tx = buildAndSignTx(
              [instruction],
              payer,
              blockhash,
          );
      
          const signature = await sendAndConfirmTx(rpc, tx);
          return signature;
      }
      ```
      
      ### Using Anchor Client
      
      ```typescript
      import { Program, AnchorProvider } from '@coral-xyz/anchor';
      import { createRpc } from '@lightprotocol/stateless.js';
      
      // Setup
      const rpc = createRpc();
      const provider = new AnchorProvider(rpc, wallet, {});
      const program = new Program(idl, programId, provider);
      
      // Fetch account and proof
      const account = await rpc.getCompressedAccount({ address });
      const proof = await rpc.getValidityProof({ hashes: [account.hash] });
      
      // Call program instruction
      await program.methods
          .updateAccount(
              proof.compressedProof,
              currentValue,
              accountMeta,
          )
          .remainingAccounts([
              // Light Protocol accounts from proof
          ])
          .rpc();
      ```
      
      ## Rust Client
      
      ### Setup
      
      ```toml
      [dependencies]
      light-client = "0.22"
      solana-sdk = "2.2"
      tokio = { version = "1", features = ["full"] }
      ```
      
      ### Usage
      
      ```rust
      use light_client::{
          rpc::RpcConnection,
          indexer::Indexer,
      };
      use solana_sdk::pubkey::Pubkey;
      
      #[tokio::main]
      async fn main() -> Result<()> {
          // Connect to RPC
          let rpc = RpcConnection::new("https://devnet.helius-rpc.com?api-key=KEY");
      
          // Fetch compressed accounts
          let accounts = rpc.get_compressed_accounts_by_owner(&owner).await?;
      
          // Get validity proof
          let hashes: Vec<_> = accounts.iter().map(|a| a.hash).collect();
          let proof = rpc.get_validity_proof(&hashes, &[]).await?;
      
          // Build and send transaction
          // ...
      
          Ok(())
      }
      ```
      
      ## Error Handling
      
      ### Common Errors
      
      ```typescript
      try {
          const account = await rpc.getCompressedAccount({ address });
      } catch (error) {
          if (error.message.includes('Account not found')) {
              // Account doesn't exist or was closed
          } else if (error.message.includes('Invalid proof')) {
              // Proof verification failed - state may have changed
          } else if (error.message.includes('Rate limit')) {
              // Too many requests - implement backoff
          }
      }
      ```
      
      ### Retry Logic
      
      ```typescript
      async function withRetry<T>(
          fn: () => Promise<T>,
          maxRetries = 3,
          delayMs = 1000,
      ): Promise<T> {
          for (let i = 0; i < maxRetries; i++) {
              try {
                  return await fn();
              } catch (error) {
                  if (i === maxRetries - 1) throw error;
                  await new Promise(r => setTimeout(r, delayMs * (i + 1)));
              }
          }
          throw new Error('Max retries exceeded');
      }
      
      // Usage
      const account = await withRetry(() =>
          rpc.getCompressedAccount({ address })
      );
      ```
      
      ## Caching Strategies
      
      ### Account Cache
      
      ```typescript
      class CompressedAccountCache {
          private cache = new Map<string, { account: any; timestamp: number }>();
          private ttl = 5000; // 5 seconds
      
          async get(rpc: Rpc, address: PublicKey): Promise<CompressedAccount> {
              const key = address.toBase58();
              const cached = this.cache.get(key);
      
              if (cached && Date.now() - cached.timestamp < this.ttl) {
                  return cached.account;
              }
      
              const account = await rpc.getCompressedAccount({ address });
              this.cache.set(key, { account, timestamp: Date.now() });
              return account;
          }
      
          invalidate(address: PublicKey) {
              this.cache.delete(address.toBase58());
          }
      }
      ```
      
      ### Proof Caching
      
      Proofs should generally NOT be cached because:
      - Account state (and thus proofs) can change between transactions
      - Proofs include specific Merkle roots that become stale
      
      Instead, always fetch fresh proofs immediately before transaction submission.
      
      ## Pagination
      
      ```typescript
      async function fetchAllAccounts(rpc: Rpc, owner: PublicKey) {
          const allAccounts = [];
          let cursor: string | undefined;
      
          do {
              const result = await rpc.getCompressedAccountsByOwner(owner, {
                  cursor,
                  limit: 100,
              });
      
              allAccounts.push(...result.items);
              cursor = result.cursor;
          } while (cursor);
      
          return allAccounts;
      }
      ```
      
      ## Running Local Photon
      
      For development without Helius:
      
      ```bash
      # Install Photon
      cargo install photon-indexer
      
      # Run against local validator
      photon
      
      # Or connect to devnet
      photon --rpc-url=https://api.devnet.solana.com
      ```
      
      Then use `createRpc('http://localhost:8784')` to connect.
      
      ## Best Practices
      
      1. **Batch requests** - Use `getMultipleCompressedAccounts` when fetching multiple accounts
      2. **Handle state changes** - Account hashes change on write; re-fetch after transactions
      3. **Fresh proofs** - Always fetch proofs immediately before transaction submission
      4. **Pagination** - Use cursors for large result sets
      5. **Error handling** - Implement retries for transient failures
      6. **Rate limiting** - Respect RPC rate limits, implement backoff
      7. **Caching** - Cache account data briefly, never cache proofs
      
    • compressed-accounts.md 8.2 KB
      # Compressed Account Model
      
      Compressed accounts store state on Solana's ledger instead of in the AccountsDB, eliminating rent requirements while maintaining security through zero-knowledge proofs.
      
      ## Account Structure
      
      ### Compressed Account Layout
      
      ```rust
      pub struct CompressedAccount {
          pub owner: Pubkey,           // Program that owns this account
          pub lamports: u64,           // Lamport balance
          pub address: Option<[u8; 32]>, // Optional persistent address (PDA-like)
          pub data: Option<CompressedAccountData>,
      }
      
      pub struct CompressedAccountData {
          pub discriminator: [u8; 8],  // Account type identifier
          pub data: Vec<u8>,           // Serialized account data
          pub data_hash: [u8; 32],     // Poseidon hash of data
      }
      ```
      
      ### Comparison with Regular Accounts
      
      | Field | Regular Account | Compressed Account |
      |-------|-----------------|-------------------|
      | owner | Program ID | Program ID |
      | lamports | Balance | Balance |
      | data | Raw bytes | Structured with discriminator + hash |
      | executable | Boolean | Not applicable |
      | rent_epoch | Epoch number | Not applicable |
      | **address** | 32-byte pubkey (permanent) | Optional, hash changes on write |
      
      ## Account Identification
      
      ### Hash-Based Identification
      
      Every compressed account has a unique hash computed from its contents:
      
      ```rust
      // Hash computation (Poseidon)
      hash = poseidon_hash([
          owner,
          lamports,
          address,
          data_hash,
      ])
      ```
      
      **Important**: The hash changes on every write operation, so you cannot use the hash as a permanent identifier.
      
      ### Address-Based Identification (Optional)
      
      For accounts needing persistent identification (like PDAs), set an address:
      
      ```rust
      // Derive address (similar to PDA derivation)
      let (address, address_seed) = derive_address(
          &[b"my_seed", user.key().as_ref()],
          &address_tree_pubkey,
          &program_id,
      );
      ```
      
      Addresses:
      - Are permanent unique identifiers
      - Cannot be reused after account closure
      - Are stored in separate address trees
      - Add computational overhead (use only when needed)
      
      ## State Trees
      
      ### Merkle Tree Structure
      
      Compressed accounts are stored in concurrent Merkle trees:
      
      ```
                          Root (on-chain)
                         /              \
                      Hash              Hash
                     /    \            /    \
                  Hash    Hash      Hash    Hash
                 /  \    /  \      /  \    /  \
                A1  A2  A3  A4    A5  A6  A7  A8  (account hashes)
      ```
      
      - Only the root is stored on-chain
      - Leaves contain account hashes
      - Trees use Poseidon hashing (ZK-friendly)
      - Concurrent access supported via versioned roots
      
      ### Tree Types
      
      | Tree Type | Purpose | Contents |
      |-----------|---------|----------|
      | State Tree (V1) | Store account hashes | Compressed account leaves (individual updates) |
      | State Tree (V2) | Store account hashes | Compressed account leaves (batched updates, ~70% less CU) |
      | Address Tree | Track unique addresses | Address leaves (indexed) |
      | Nullifier Queue | Track spent accounts | Nullified account hashes |
      
      V2 batched Merkle trees (mainnet January 2026) batch multiple insertions and verify them with ZK proofs, reducing state root update costs by ~250x. New deployments use V2 trees by default. Tree type is tracked via a `TreeType` enum in the SDK (`StateV1` or `StateV2`); tree selection is handled automatically by SDK helpers like `selectStateTreeInfo()`.
      
      ## Account Operations
      
      ### Creating Accounts
      
      ```rust
      use light_sdk::account::LightAccount;
      
      // new_init creates account with only output state (no prior state)
      let mut account = LightAccount::<MyAccount>::new_init(
          &program_id,           // Owner program
          Some(address),         // Optional persistent address
          output_state_tree_index, // Which tree to store in
      );
      
      // Set initial values
      account.owner = signer.key();
      account.value = 0;
      
      // Invoke Light System Program to create
      LightSystemProgramCpi::new_cpi(CPI_SIGNER, proof)
          .with_light_account(account)?
          .with_new_addresses(&[new_address_params])
          .invoke(cpi_accounts)?;
      ```
      
      ### Reading Accounts
      
      Accounts are read via the compression RPC API:
      
      ```typescript
      // By address (if set)
      const account = await rpc.getCompressedAccount({ address });
      
      // By hash
      const account = await rpc.getCompressedAccount({ hash });
      
      // All accounts for owner
      const accounts = await rpc.getCompressedAccountsByOwner(owner);
      ```
      
      ### Updating Accounts
      
      ```rust
      // new_mut creates account with input state (to be consumed) and output state (new)
      let mut account = LightAccount::<MyAccount>::new_mut(
          &program_id,
          &account_meta,      // Metadata about existing account
          MyAccount {         // Current state (must match on-chain)
              owner: signer.key(),
              value: current_value,
          },
      )?;
      
      // Modify the account
      account.value = account.value.checked_add(1).unwrap();
      
      // Invoke to apply changes
      LightSystemProgramCpi::new_cpi(CPI_SIGNER, proof)
          .with_light_account(account)?
          .invoke(cpi_accounts)?;
      ```
      
      ### Closing Accounts
      
      ```rust
      // new_close creates account with only input state (consumed, no output)
      let account = LightAccount::<MyAccount>::new_close(
          &program_id,
          &account_meta,
          MyAccount {
              owner: signer.key(),
              value: current_value,
          },
      )?;
      
      LightSystemProgramCpi::new_cpi(CPI_SIGNER, proof)
          .with_light_account(account)?
          .invoke(cpi_accounts)?;
      ```
      
      **Note**: Closed account addresses can be reinitialized. Use `LightAccount::new_empty()` to reconstruct the closed account hash, then optionally chain `LightAccount::new_mut()` to set custom values in the same transaction. See [reinitialize guide](https://www.zkcompression.com/compressed-pdas/guides/how-to-reinitialize-compressed-accounts).
      
      ## Account Metadata
      
      When referencing existing accounts, provide metadata:
      
      ```rust
      pub struct CompressedAccountMeta {
          pub merkle_context: PackedMerkleContext,
          pub leaf_index: u32,
          pub hash: [u8; 32],
      }
      
      pub struct PackedMerkleContext {
          pub merkle_tree_pubkey_index: u8,
          pub nullifier_queue_pubkey_index: u8,
          pub leaf_index: u32,
          pub queue_index: Option<QueueIndex>,
      }
      ```
      
      This metadata is fetched from the indexer and included in transactions.
      
      ## Hashing with LightHasher
      
      Define hashable account structures:
      
      ```rust
      use light_sdk::{LightDiscriminator, LightHasher};
      
      #[derive(Clone, Debug, Default, LightDiscriminator, LightHasher)]
      pub struct MyAccount {
          #[hash]  // Include in hash computation
          pub owner: Pubkey,
          pub value: u64,  // Not hashed (but still serialized)
      }
      ```
      
      - `#[hash]` marks fields included in the Poseidon hash
      - All fields are serialized with Borsh
      - Discriminator auto-derived from struct name
      
      ## Validity Proofs
      
      ### What Proofs Verify
      
      - Account exists as a leaf in specified state tree
      - Account hash matches provided data
      - Address (if set) exists in address tree
      - State transition is valid
      
      ### Proof Structure
      
      ```rust
      pub struct ValidityProof {
          pub a: [u8; 64],      // Groth16 proof element A
          pub b: [u8; 128],     // Groth16 proof element B
          pub c: [u8; 64],      // Groth16 proof element C
      }
      // Total: 256 bytes (compressed to 128 bytes in transactions)
      ```
      
      ### Fetching Proofs
      
      ```typescript
      // Get proof for multiple accounts
      const proof = await rpc.getValidityProof({
          hashes: [accountHash1, accountHash2],
          newAddresses: [newAddress1], // If creating new addressed accounts
      });
      ```
      
      ## Compute Units
      
      | Operation | Approximate CU |
      |-----------|---------------|
      | Validity proof verification | ~100,000 |
      | System program overhead | ~100,000 |
      | Per account read/write | ~6,000 |
      | Poseidon hash (syscall) | ~1,500 |
      
      **Example**: Transaction with 2 accounts ≈ 212,000 CU
      
      ## Trust Assumptions
      
      1. **Data Availability**: At least one node must store raw account data for proof generation
      2. **Forester Liveness**: Nullifier queues must be emptied to prevent state tree congestion
      3. **Program Upgradeability**: Light System Program is currently upgradeable (will be frozen)
      
      ## Best Practices
      
      1. **Use addresses sparingly** - Only when persistent identification needed
      2. **Batch operations** - Multiple accounts in one transaction share proof overhead
      3. **Cache account state** - Reduce RPC calls by caching recent fetches
      4. **Handle hash changes** - Account hashes change on every write; re-fetch after updates
      5. **Plan for CU limits** - Account for ~200k CU base + 6k per account
      
    • compressed-pdas.md 12.1 KB
      # Compressed PDAs
      
      Build Solana programs with compressed Program Derived Addresses (PDAs) for rent-free accounts with persistent identification.
      
      ## Overview
      
      | Aspect | Regular PDA | Compressed PDA |
      |--------|-------------|----------------|
      | 100-byte account | ~1,600,000 lamports | ~15,000 lamports |
      | Rent | Required | None |
      | Address derivation | `find_program_address` | `derive_address` |
      | State updates | In-place mutation | Consume + create new |
      
      ## Prerequisites
      
      ### Required Versions
      
      - Rust: 1.89.0+
      - Solana CLI: 2.3.0+
      - Anchor CLI: 0.32.1+
      - ZK Compression CLI: latest (`npm install -g @lightprotocol/zk-compression-cli`)
      - Node.js: 23.5.0+
      
      ### Installation
      
      ```bash
      # Install ZK compression CLI
      npm install -g @lightprotocol/zk-compression-cli
      
      # Initialize new project with compression support
      light init my-program
      cd my-program
      ```
      
      ## Project Structure
      
      ```
      my-program/
      ├── Anchor.toml
      ├── Cargo.toml
      ├── programs/
      │   └── my-program/
      │       ├── Cargo.toml
      │       └── src/
      │           └── lib.rs
      ├── tests/
      │   └── my-program.ts
      └── package.json
      ```
      
      ### Cargo.toml Dependencies
      
      ```toml
      [dependencies]
      anchor-lang = "0.32.1"
      light-sdk = "0.22"
      
      [dev-dependencies]
      light-program-test = "1.2"
      light-client = "0.22"
      ```
      
      > **Note**: Light Protocol SDK versions change frequently. Check [crates.io](https://crates.io/crates/light-sdk) for the latest compatible versions.
      
      ## Program Structure
      
      ### Basic Anchor Program with Compression
      
      ```rust
      #![allow(unexpected_cfgs)]
      
      use anchor_lang::prelude::*;
      use light_sdk::{
          account::LightAccount,
          address::v1::derive_address,
          cpi::{v1::CpiAccounts, CpiSigner},
          derive_light_cpi_signer,
          instruction::{account_meta::CompressedAccountMeta, PackedAddressTreeInfo, ValidityProof},
          LightDiscriminator, LightHasher,
      };
      
      declare_id!("YourProgramID11111111111111111111111111111");
      
      // Derive CPI signer for Light System Program calls
      pub const LIGHT_CPI_SIGNER: CpiSigner =
          derive_light_cpi_signer!("YourProgramID11111111111111111111111111111");
      
      #[program]
      pub mod my_program {
          use super::*;
          use light_sdk::cpi::{v1::LightSystemProgramCpi, InvokeLightSystemProgram};
      
          // Instructions here...
      }
      
      // Account context (minimal for compressed accounts)
      #[derive(Accounts)]
      pub struct MyAccounts<'info> {
          #[account(mut)]
          pub signer: Signer<'info>,
      }
      
      // Compressed account data structure
      #[event]
      #[derive(Clone, Debug, Default, LightDiscriminator, LightHasher)]
      pub struct MyAccount {
          #[hash]
          pub owner: Pubkey,
          pub data: u64,
      }
      ```
      
      ## Creating Compressed PDAs
      
      ### Create Instruction
      
      ```rust
      pub fn create_account<'info>(
          ctx: Context<'_, '_, '_, 'info, MyAccounts<'info>>,
          proof: ValidityProof,
          address_tree_info: PackedAddressTreeInfo,
          output_state_tree_index: u8,
      ) -> Result<()> {
          // 1. Setup CPI accounts from remaining_accounts
          let light_cpi_accounts = CpiAccounts::new(
              ctx.accounts.signer.as_ref(),
              ctx.remaining_accounts,
              crate::LIGHT_CPI_SIGNER,
          );
      
          // 2. Derive the compressed PDA address
          let (address, address_seed) = derive_address(
              &[b"my_account", ctx.accounts.signer.key().as_ref()],
              &address_tree_info
                  .get_tree_pubkey(&light_cpi_accounts)
                  .map_err(|_| ErrorCode::AccountNotEnoughKeys)?,
              &crate::ID,
          );
      
          // 3. Prepare address params for new address creation
          let new_address_params = address_tree_info.into_new_address_params_packed(address_seed);
      
          // 4. Create the compressed account
          let mut account = LightAccount::<MyAccount>::new_init(
              &crate::ID,
              Some(address),
              output_state_tree_index,
          );
      
          // 5. Initialize account data
          account.owner = ctx.accounts.signer.key();
          account.data = 0;
      
          // 6. Invoke Light System Program
          LightSystemProgramCpi::new_cpi(LIGHT_CPI_SIGNER, proof)
              .with_light_account(account)?
              .with_new_addresses(&[new_address_params])
              .invoke(light_cpi_accounts)?;
      
          Ok(())
      }
      ```
      
      ### Address Derivation
      
      ```rust
      use light_sdk::address::v1::derive_address;
      
      // Seeds work like regular PDAs
      let (address, address_seed) = derive_address(
          &[
              b"my_prefix",           // Static seed
              user.key().as_ref(),   // Dynamic seed
              &[counter],            // Additional data
          ],
          &address_tree_pubkey,      // Address tree (from CPI accounts)
          &program_id,               // Your program ID
      );
      ```
      
      ## Updating Compressed Accounts
      
      ### Update Instruction
      
      ```rust
      pub fn update_account<'info>(
          ctx: Context<'_, '_, '_, 'info, MyAccounts<'info>>,
          proof: ValidityProof,
          // Current state must be provided (fetched from indexer)
          current_data: u64,
          account_meta: CompressedAccountMeta,
      ) -> Result<()> {
          // 1. Create mutable account (consumes input, creates output)
          let mut account = LightAccount::<MyAccount>::new_mut(
              &crate::ID,
              &account_meta,
              MyAccount {
                  owner: ctx.accounts.signer.key(),
                  data: current_data,
              },
          )?;
      
          // 2. Validate ownership
          require!(
              account.owner == ctx.accounts.signer.key(),
              CustomError::Unauthorized
          );
      
          // 3. Modify state
          account.data = account.data.checked_add(1).ok_or(CustomError::Overflow)?;
      
          // 4. Setup CPI and invoke
          let light_cpi_accounts = CpiAccounts::new(
              ctx.accounts.signer.as_ref(),
              ctx.remaining_accounts,
              crate::LIGHT_CPI_SIGNER,
          );
      
          LightSystemProgramCpi::new_cpi(LIGHT_CPI_SIGNER, proof)
              .with_light_account(account)?
              .invoke(light_cpi_accounts)?;
      
          Ok(())
      }
      ```
      
      ## Closing Compressed Accounts
      
      ### Close Instruction
      
      ```rust
      pub fn close_account<'info>(
          ctx: Context<'_, '_, '_, 'info, MyAccounts<'info>>,
          proof: ValidityProof,
          current_data: u64,
          account_meta: CompressedAccountMeta,
      ) -> Result<()> {
          // new_close only has input state (no output created)
          let account = LightAccount::<MyAccount>::new_close(
              &crate::ID,
              &account_meta,
              MyAccount {
                  owner: ctx.accounts.signer.key(),
                  data: current_data,
              },
          )?;
      
          require!(
              account.owner == ctx.accounts.signer.key(),
              CustomError::Unauthorized
          );
      
          let light_cpi_accounts = CpiAccounts::new(
              ctx.accounts.signer.as_ref(),
              ctx.remaining_accounts,
              crate::LIGHT_CPI_SIGNER,
          );
      
          LightSystemProgramCpi::new_cpi(LIGHT_CPI_SIGNER, proof)
              .with_light_account(account)?
              .invoke(light_cpi_accounts)?;
      
          Ok(())
      }
      ```
      
      **Important**: Closed account addresses can be reinitialized. Use `LightAccount::new_empty()` to reconstruct the closed account hash, then optionally chain `LightAccount::new_mut()` to set custom values in the same transaction. See [reinitialize guide](https://www.zkcompression.com/compressed-pdas/guides/how-to-reinitialize-compressed-accounts).
      
      ## Account Data Structures
      
      ### Defining Hashable Structs
      
      ```rust
      use light_sdk::{LightDiscriminator, LightHasher};
      
      #[event]  // Makes struct available in IDL
      #[derive(Clone, Debug, Default, LightDiscriminator, LightHasher)]
      pub struct GamePlayer {
          #[hash]
          pub wallet: Pubkey,     // Included in hash
          #[hash]
          pub game_id: [u8; 32],  // Included in hash
          pub score: u64,         // Not hashed (but serialized)
          pub level: u8,          // Not hashed (but serialized)
      }
      ```
      
      ### Hash Attribute
      
      - `#[hash]` marks fields for Poseidon hash computation
      - All fields are Borsh serialized regardless of hash attribute
      - Hash determines account identity; hashed fields should be immutable or tracked carefully
      
      ## Multiple Accounts in One Transaction
      
      ```rust
      pub fn batch_create<'info>(
          ctx: Context<'_, '_, '_, 'info, MyAccounts<'info>>,
          proof: ValidityProof,
          address_tree_infos: Vec<PackedAddressTreeInfo>,
          output_state_tree_index: u8,
          count: u8,
      ) -> Result<()> {
          let light_cpi_accounts = CpiAccounts::new(
              ctx.accounts.signer.as_ref(),
              ctx.remaining_accounts,
              crate::LIGHT_CPI_SIGNER,
          );
      
          let mut accounts = Vec::new();
          let mut address_params = Vec::new();
      
          for i in 0..count {
              let (address, seed) = derive_address(
                  &[b"batch", &[i]],
                  &address_tree_infos[i as usize].get_tree_pubkey(&light_cpi_accounts)?,
                  &crate::ID,
              );
      
              address_params.push(
                  address_tree_infos[i as usize].into_new_address_params_packed(seed)
              );
      
              let mut account = LightAccount::<MyAccount>::new_init(
                  &crate::ID,
                  Some(address),
                  output_state_tree_index,
              );
              account.owner = ctx.accounts.signer.key();
              account.data = i as u64;
              accounts.push(account);
          }
      
          let mut cpi = LightSystemProgramCpi::new_cpi(LIGHT_CPI_SIGNER, proof)
              .with_new_addresses(&address_params);
      
          for account in accounts {
              cpi = cpi.with_light_account(account)?;
          }
      
          cpi.invoke(light_cpi_accounts)?;
      
          Ok(())
      }
      ```
      
      ## Testing
      
      ### Unit Tests with light-program-test
      
      ```rust
      #[cfg(test)]
      mod tests {
          use super::*;
          use light_program_test::{LightProgramTest, TestRpc};
      
          #[tokio::test]
          async fn test_create_account() {
              let mut test = LightProgramTest::new(&[("my_program", crate::ID)]).await;
              let rpc = test.rpc();
      
              // Create account
              let result = test.create_compressed_account(
                  &crate::ID,
                  &[b"test"],
              ).await;
      
              assert!(result.is_ok());
          }
      }
      ```
      
      ### Integration Tests (TypeScript)
      
      > Light Protocol SDK uses `@solana/web3.js` v1 types. For non-Light client code, use [`@solana/kit`](https://solanakit.org).
      
      ```typescript
      import { createRpc } from '@lightprotocol/stateless.js';
      import { Keypair } from '@solana/web3.js'; // Required by Light SDK
      
      describe('My Program', () => {
          const rpc = createRpc();
      
          it('creates compressed account', async () => {
              const payer = Keypair.generate();
              await rpc.requestAirdrop(payer.publicKey, 1000000000);
      
              // Build and send transaction
              // ...
          });
      });
      ```
      
      ## Build and Deploy
      
      ```bash
      # Build program
      anchor build
      
      # Run tests
      cargo test-sbf
      
      # Deploy to devnet
      anchor deploy --provider.cluster devnet
      ```
      
      ## Common Patterns
      
      ### Read-Only Access
      
      For instructions that only read compressed accounts without modifying:
      
      ```rust
      pub fn read_account<'info>(
          ctx: Context<'_, '_, '_, 'info, MyAccounts<'info>>,
          proof: ValidityProof,
          account_data: MyAccount,
          account_meta: CompressedAccountMeta,
      ) -> Result<()> {
          // Validate the account exists with this data
          let account = LightAccount::<MyAccount>::new_mut(
              &crate::ID,
              &account_meta,
              account_data,
          )?;
      
          // Read-only logic
          msg!("Account data: {}", account.data);
      
          // Re-create same state (no modification)
          let light_cpi_accounts = CpiAccounts::new(
              ctx.accounts.signer.as_ref(),
              ctx.remaining_accounts,
              crate::LIGHT_CPI_SIGNER,
          );
      
          LightSystemProgramCpi::new_cpi(LIGHT_CPI_SIGNER, proof)
              .with_light_account(account)?
              .invoke(light_cpi_accounts)?;
      
          Ok(())
      }
      ```
      
      ### CPI Between Programs
      
      ```rust
      // Call another program that uses compressed accounts
      pub fn cpi_example<'info>(
          ctx: Context<'_, '_, '_, 'info, CpiAccounts<'info>>,
          // ... params
      ) -> Result<()> {
          // Build CPI to other program
          // Include Light System Program accounts in remaining_accounts
      
          Ok(())
      }
      ```
      
      ## Error Handling
      
      ```rust
      #[error_code]
      pub enum CustomError {
          #[msg("Unauthorized access")]
          Unauthorized,
          #[msg("Arithmetic overflow")]
          Overflow,
          #[msg("Arithmetic underflow")]
          Underflow,
          #[msg("Invalid account state")]
          InvalidState,
      }
      ```
      
      ## Best Practices
      
      1. **Validate ownership** - Always check account owner matches expected signer
      2. **Use checked arithmetic** - Prevent overflow/underflow errors
      3. **Minimize state size** - Smaller accounts = lower costs
      4. **Hash immutable fields** - Fields marked `#[hash]` affect identity
      5. **Handle concurrent access** - Account hashes change on every write
      6. **Test with real proofs** - Use `light-program-test` for accurate testing
      
    • compressed-tokens.md 8.7 KB
      # Compressed Tokens
      
      Compressed tokens are SPL tokens stored as compressed accounts, enabling 5000x cheaper token distribution while maintaining full SPL token compatibility.
      
      ## Overview
      
      | Aspect | Regular SPL Token | Compressed Token |
      |--------|-------------------|------------------|
      | Storage | Token account (~0.002 SOL rent) | Compressed account (no rent) |
      | 100 accounts | ~0.2 SOL | ~0.00004 SOL |
      | Mint | Standard SPL mint | SPL mint + token pool |
      | Transfers | Account-to-account | Consume input, create outputs |
      
      ## Setup
      
      ### Installation
      
      ```bash
      npm install @lightprotocol/stateless.js @lightprotocol/compressed-token
      ```
      
      ### Create RPC Connection
      
      ```typescript
      import { createRpc } from '@lightprotocol/stateless.js';
      
      // Local development
      const rpc = createRpc(); // defaults to localhost:8899
      
      // Helius mainnet
      const rpc = createRpc('https://mainnet.helius-rpc.com?api-key=YOUR_KEY');
      
      // Helius devnet
      const rpc = createRpc('https://devnet.helius-rpc.com?api-key=YOUR_KEY');
      ```
      
      ## Token Operations
      
      ### Create Mint with Token Pool
      
      A token pool enables compression for an SPL mint:
      
      ```typescript
      import { createMint } from '@lightprotocol/compressed-token';
      
      // Creates SPL mint + token pool in one transaction
      const { mint, transactionSignature } = await createMint(
          rpc,
          payer,           // Fee payer and mint authority
          payer.publicKey, // Mint authority
          9,               // Decimals
          // Optional: keypair for deterministic mint address
      );
      
      console.log('Mint:', mint.toBase58());
      ```
      
      ### Add Token Pool to Existing Mint
      
      ```typescript
      import { createTokenPool } from '@lightprotocol/compressed-token';
      
      // Add compression support to existing SPL mint
      const transactionSignature = await createTokenPool(
          rpc,
          payer,
          existingMint,
      );
      ```
      
      > **Note**: `createTokenPool` is being renamed to `createSplInterface` in newer SDK versions. Both work currently; prefer `createSplInterface` in new code.
      
      ### Mint Compressed Tokens
      
      ```typescript
      import { mintTo } from '@lightprotocol/compressed-token';
      
      // Mint to single recipient
      const sig = await mintTo(
          rpc,
          payer,
          mint,
          recipient,        // toPubkey
          mintAuthority,    // Must have mint authority
          1_000_000_000,    // Amount (with decimals)
      );
      
      // Mint to multiple recipients
      const recipients = [addr1, addr2, addr3];
      const amounts = [1_000_000_000, 2_000_000_000, 500_000_000];
      
      const sig = await mintTo(
          rpc,
          payer,
          mint,
          recipients,
          mintAuthority,
          amounts,
      );
      ```
      
      ### Transfer Compressed Tokens
      
      ```typescript
      import { transfer } from '@lightprotocol/compressed-token';
      
      // Basic transfer
      const sig = await transfer(
          rpc,
          payer,
          mint,
          amount,
          owner,       // Token owner (keypair)
          recipient,   // Destination address
      );
      ```
      
      **How transfers work**:
      1. Input compressed token accounts are consumed (nullified)
      2. New output accounts created for recipient and sender (change)
      3. Balances recomputed and stored in new accounts
      
      ### Query Token Accounts
      
      ```typescript
      // Get all compressed token accounts for owner
      const accounts = await rpc.getCompressedTokenAccountsByOwner(
          owner,
          { mint } // Optional: filter by mint
      );
      
      for (const account of accounts.items) {
          console.log('Amount:', account.parsed.amount);
          console.log('Mint:', account.parsed.mint);
          console.log('Owner:', account.parsed.owner);
      }
      
      // Get token balances summary
      const balances = await rpc.getCompressedTokenBalancesByOwner(owner);
      ```
      
      ### Get Token Account Balance
      
      ```typescript
      // Get balance for specific compressed token account
      const balance = await rpc.getCompressedTokenAccountBalance(accountHash);
      ```
      
      ## Compress/Decompress SPL Tokens
      
      ### Compress SPL Tokens
      
      Convert regular SPL token account to compressed:
      
      ```typescript
      import { compress } from '@lightprotocol/compressed-token';
      
      const sig = await compress(
          rpc,
          payer,
          mint,
          amount,
          owner,           // SPL token account owner
          recipient,       // Compressed token recipient (can be same)
      );
      ```
      
      ### Decompress to SPL Tokens
      
      Convert compressed tokens back to regular SPL:
      
      ```typescript
      import { decompress } from '@lightprotocol/compressed-token';
      
      const sig = await decompress(
          rpc,
          payer,
          mint,
          amount,
          owner,           // Compressed token owner
          recipient,       // SPL token account (or ATA created)
      );
      ```
      
      ## Delegate Authority
      
      ### Approve Delegate
      
      ```typescript
      import { approve } from '@lightprotocol/compressed-token';
      
      const sig = await approve(
          rpc,
          payer,
          mint,
          amount,          // Delegated amount
          owner,           // Token owner
          delegate,        // Delegate pubkey
      );
      ```
      
      ### Revoke Delegate
      
      ```typescript
      import { revoke } from '@lightprotocol/compressed-token';
      
      const sig = await revoke(
          rpc,
          payer,
          mint,
          owner,
      );
      ```
      
      ### Transfer with Delegate
      
      ```typescript
      import { transfer } from '@lightprotocol/compressed-token';
      
      // When delegate is transferring on behalf of owner
      const sig = await transfer(
          rpc,
          payer,
          mint,
          amount,
          delegate,        // Delegate performing transfer
          recipient,
          owner,           // Original owner (optional, for delegate transfers)
      );
      ```
      
      ## Merge Token Accounts
      
      Combine multiple compressed token accounts:
      
      ```typescript
      import { mergeTokenAccounts } from '@lightprotocol/compressed-token';
      
      // Merge all token accounts for a mint into fewer accounts
      const sig = await mergeTokenAccounts(
          rpc,
          payer,
          mint,
          owner,
      );
      ```
      
      **When to merge**:
      - Owner has many small token accounts
      - Reduce number of inputs needed for transfers
      - Simplify account management
      
      ## Batch Operations
      
      ### Airdrop to Many Recipients
      
      ```typescript
      import { mintTo } from '@lightprotocol/compressed-token';
      
      // Prepare recipients and amounts
      const recipients: PublicKey[] = [];
      const amounts: number[] = [];
      
      for (const user of users) {
          recipients.push(user.address);
          amounts.push(airdropAmount);
      }
      
      // Batch mint (limited by transaction size)
      const BATCH_SIZE = 5; // Adjust based on transaction limits
      
      for (let i = 0; i < recipients.length; i += BATCH_SIZE) {
          const batchRecipients = recipients.slice(i, i + BATCH_SIZE);
          const batchAmounts = amounts.slice(i, i + BATCH_SIZE);
      
          await mintTo(rpc, payer, mint, batchRecipients, mintAuthority, batchAmounts);
      }
      ```
      
      ## Token Pool Info
      
      ```typescript
      import { getTokenPoolInfos } from '@lightprotocol/compressed-token';
      
      // Get token pool details for a mint
      const poolInfo = await getTokenPoolInfos(rpc, mint);
      
      console.log('Pool address:', poolInfo.address);
      console.log('Pool token account:', poolInfo.tokenAccount);
      ```
      
      ## Common Errors
      
      ### TokenPool not found
      
      ```typescript
      // Error: TokenPool not found for mint
      // Solution: Create token pool first
      import { createTokenPool } from '@lightprotocol/compressed-token';
      await createTokenPool(rpc, payer, mint);
      ```
      
      ### Insufficient balance
      
      ```typescript
      // Error: Insufficient balance for transfer
      // Check balance before transfer
      const accounts = await rpc.getCompressedTokenAccountsByOwner(owner, { mint });
      const totalBalance = accounts.items.reduce(
          (sum, acc) => sum + BigInt(acc.parsed.amount),
          0n
      );
      ```
      
      ### Array length mismatch
      
      ```typescript
      // Error: Amount and toPubkey arrays must have same length
      // Ensure arrays match
      const recipients = [addr1, addr2, addr3];
      const amounts = [100, 200, 300]; // Must be same length
      ```
      
      ## Rust SDK (On-Chain Programs)
      
      For programs that need to interact with compressed tokens:
      
      ```rust
      use light_compressed_token::{
          process_transfer,
          InputTokenDataWithContext,
          PackedTokenTransferOutputData,
      };
      use light_sdk::instruction::ValidityProof;
      
      // Transfer compressed tokens in your program
      pub fn transfer_compressed_tokens(
          ctx: Context<MyContext>,
          inputs: Vec<InputTokenDataWithContext>,
          outputs: Vec<PackedTokenTransferOutputData>,
          proof: ValidityProof,
      ) -> Result<()> {
          // ... validation logic
      
          // CPI to compressed token program
          process_transfer(
              ctx.accounts.into(),
              inputs,
              outputs,
              proof,
          )?;
      
          Ok(())
      }
      ```
      
      ## Cost Comparison
      
      | Operation | Regular SPL | Compressed Token |
      |-----------|-------------|------------------|
      | Create 1 token account | ~0.002 SOL | ~0.000001 SOL |
      | Create 1000 accounts | ~2 SOL | ~0.001 SOL |
      | Create 1M accounts | ~2000 SOL | ~1 SOL |
      | Transfer | ~0.000005 SOL | ~0.00001 SOL |
      
      **Note**: Compressed tokens have higher compute costs (~200k CU per transfer) but much lower state costs.
      
      ## Best Practices
      
      1. **Use for distribution** - Ideal for airdrops, rewards, gaming items
      2. **Batch mints** - Multiple recipients in one transaction
      3. **Merge periodically** - Consolidate accounts for cleaner state
      4. **Cache balances** - Reduce RPC calls in UI
      5. **Handle multiple accounts** - Users may have multiple compressed accounts for same mint
      
    • compute-optimization.md 19.4 KB
      # Compute Unit Optimization Guide
      
      This guide provides comprehensive techniques for optimizing compute unit (CU) usage in Solana native Rust programs, compiled from official Solana documentation, community repositories, and expert resources.
      
      ## Understanding Compute Units
      
      ### Compute Limits
      
      Solana enforces strict compute budgets to ensure network performance:
      
      - **Max CU per block**: 60 million CU
      - **Max CU per account per block**: 12 million CU
      - **Max CU per transaction**: 1.4 million CU
      - **Default soft cap per transaction**: 200,000 CU
      
      Programs can request higher compute budgets using the Compute Budget program, up to the 1.4M hard limit.
      
      ### Transaction Fees
      
      Transaction fees consist of two components:
      
      1. **Base fee**: 5,000 lamports per signature (fixed, independent of CU usage)
      2. **Priority fee**: Optional additional fee to prioritize transaction inclusion
      
      Priority fees are calculated as:
      ```
      priority_fee = microLamports_per_CU × requested_compute_units
      ```
      
      ### Why Optimize CU Usage?
      
      Even though current fees don't scale with CU usage within the budget, optimization matters:
      
      1. **Block inclusion probability**: Smaller transactions are more likely to fit in congested blocks
      2. **Composability**: When your program is called via CPI, it shares the caller's CU budget
      3. **Efficient resource usage**: Better utilization of limited block space
      4. **Future-proofing**: Fee structures may change to account for actual CU consumption
      5. **User experience**: Faster transaction execution and lower rejection rates
      
      ## Common Optimization Techniques
      
      ### 1. Logging Optimization (Highest Impact)
      
      Logging is one of the most expensive operations in Solana programs.
      
      **Anti-patterns:**
      
      ```rust
      // EXPENSIVE: 11,962 CU
      // Base58 encoding + string concatenation
      msg!("A string {0}", ctx.accounts.counter.to_account_info().key());
      
      // EXPENSIVE: 357 CU
      // String concatenation
      msg!("A string {0}", "5w6z5PWvtkCd4PaAV7avxE6Fy5brhZsFdbRLMt8UefRQ");
      ```
      
      **Best practices:**
      
      ```rust
      // EFFICIENT: 262 CU
      // Use .key().log() directly
      ctx.accounts.counter.to_account_info().key().log();
      
      // BETTER: 206 CU
      // Store in variable first
      let pubkey = ctx.accounts.counter.to_account_info().key();
      pubkey.log();
      
      // CHEAPEST: 204 CU
      // Simple string logging
      msg!("Compute units");
      ```
      
      **Recommendation**: Avoid logging in production unless absolutely necessary for debugging. Remove or conditionally compile logging for mainnet deployments.
      
      ### 2. Data Type Optimization
      
      Smaller data types consume fewer compute units.
      
      **Comparison:**
      
      ```rust
      // 618 CU - u64
      let mut a: Vec<u64> = Vec::new();
      for _ in 0..6 {
          a.push(1);
      }
      
      // 600 CU - i32 (default integer type)
      let mut a = Vec::new();
      for _ in 0..6 {
          a.push(1);
      }
      
      // 459 CU - u8 (best for small values)
      let mut a: Vec<u8> = Vec::new();
      for _ in 0..6 {
          a.push(1);
      }
      ```
      
      **Initialization vs pushing:**
      
      ```rust
      // 357 CU - Pushing elements one by one
      let mut a: Vec<u64> = Vec::new();
      for _ in 0..6 {
          a.push(1);
      }
      
      // 125 CU - Direct initialization (65% savings!)
      let _a: Vec<u64> = vec![1, 1, 1, 1, 1, 1];
      ```
      
      **Best practice**: Use the smallest data type that fits your requirements (u8 > u16 > u32 > u64), and prefer `vec![]` initialization over repeated `push()` calls.
      
      ### 3. Serialization: Zero-Copy vs Borsh
      
      Zero-copy deserialization can provide massive CU savings for account operations.
      
      **Standard Borsh serialization:**
      
      ```rust
      // 6,302 CU - Standard account initialization
      pub fn initialize(_ctx: Context<InitializeCounter>) -> Result<()> {
          Ok(())
      }
      
      // 2,600 CU total for increment (including serialization overhead)
      pub fn increment(ctx: Context<Increment>) -> Result<()> {
          let counter = &mut ctx.accounts.counter;
          counter.count = counter.count.checked_add(1).unwrap(); // 108 CU for operation
          Ok(())
      }
      ```
      
      **Zero-copy optimization:**
      
      ```rust
      // 5,020 CU - Zero-copy initialization (20% savings)
      pub fn initialize_zero_copy(_ctx: Context<InitializeCounterZeroCopy>) -> Result<()> {
          Ok(())
      }
      
      // 1,254 CU total for increment (52% savings!)
      pub fn increment_zero_copy(ctx: Context<IncrementZeroCopy>) -> Result<()> {
          let counter = &mut ctx.accounts.counter_zero_copy.load_mut()?;
          counter.count = counter.count.checked_add(1).unwrap(); // 151 CU for operation
          Ok(())
      }
      ```
      
      **Zero-copy account definition:**
      
      ```rust
      #[account(zero_copy)]
      #[repr(C)]
      #[derive(InitSpace)]
      pub struct CounterZeroCopy {
          count: u64,
          authority: Pubkey,
          big_struct: BigStruct,  // Can include large structs without stack overflow
      }
      ```
      
      **Benefits of zero-copy:**
      - 50%+ CU savings on serialization/deserialization
      - Avoids stack frame violations with large account structures
      - Direct memory access without intermediate copying
      - Particularly valuable for frequently updated accounts
      
      **Trade-off**: Slightly more complex API (`load()`, `load_mut()`) and requires `#[repr(C)]` for memory layout guarantees.
      
      ### 4. Program Derived Addresses (PDAs)
      
      PDA operations vary significantly in cost depending on the method used.
      
      **Finding PDAs:**
      
      ```rust
      // EXPENSIVE: 12,136 CU
      // Iterates through nonces to find valid bump seed
      let (pda, bump) = Pubkey::find_program_address(&[b"counter"], ctx.program_id);
      
      // EFFICIENT: 1,651 CU (87% savings!)
      // Uses known bump seed directly
      let pda = Pubkey::create_program_address(&[b"counter", &[248_u8]], &program_id).unwrap();
      ```
      
      **Optimization strategy:**
      
      1. Use `find_program_address()` **once** during account initialization
      2. Save the bump seed in the account data
      3. Use `create_program_address()` with the saved bump for all subsequent operations
      
      **Anchor implementation:**
      
      ```rust
      // Account structure - save the bump
      #[account]
      pub struct CounterData {
          pub count: u64,
          pub bump: u8,  // Store the bump seed here
      }
      
      // EXPENSIVE: 12,136 CU - Without saved bump
      #[account(
          seeds = [b"counter"],
          bump  // Anchor finds it every time
      )]
      pub counter_checked: Account<'info, CounterData>,
      
      // EFFICIENT: 1,600 CU - With saved bump (87% savings!)
      #[account(
          seeds = [b"counter"],
          bump = counter_checked.bump  // Use the saved bump
      )]
      pub counter_checked: Account<'info, CounterData>,
      ```
      
      ### 5. Cross-Program Invocations (CPIs)
      
      CPIs add significant overhead compared to direct operations.
      
      **CPI to System Program:**
      
      ```rust
      // 2,215 CU - CPI for SOL transfer
      let cpi_context = CpiContext::new(
          ctx.accounts.system_program.to_account_info(),
          system_program::Transfer {
              from: ctx.accounts.payer.to_account_info().clone(),
              to: ctx.accounts.counter.to_account_info().clone(),
          },
      );
      system_program::transfer(cpi_context, 1_000_000)?;
      ```
      
      **Direct lamport manipulation:**
      
      ```rust
      // 251 CU - Direct operation (90% savings!)
      let counter_account_info = ctx.accounts.counter.to_account_info();
      let mut counter_lamports = counter_account_info.try_borrow_mut_lamports()?;
      **counter_lamports += 1_000_000;
      
      let payer_account_info = ctx.accounts.payer.to_account_info();
      let mut payer_lamports = payer_account_info.try_borrow_mut_lamports()?;
      **payer_lamports -= 1_000_000;
      ```
      
      **Important caveats:**
      
      1. **Error handling overhead**: Error paths add ~1,199 CU if triggered
      2. **Safety**: Direct manipulation bypasses safety checks in the System Program
      3. **Ownership**: Only safe when you control both accounts
      4. **Rent exemption**: You're responsible for maintaining rent exemption
      
      **Best practice**: Use CPIs for safety and correctness by default. Only optimize to direct manipulation when:
      - You have tight CU constraints
      - You fully understand the safety implications
      - Both accounts are controlled by your program
      
      ### 6. Pass by Reference vs Clone
      
      Solana's bump allocator doesn't free memory, making unnecessary cloning particularly problematic.
      
      **Comparison:**
      
      ```rust
      let balances = vec![10_u64; 100];
      
      // EFFICIENT: 47,683 CU - Pass by reference
      fn sum_by_reference(data: &Vec<u64>) -> u64 {
          data.iter().sum()
      }
      
      for _ in 0..39 {
          sum_reference += sum_by_reference(&balances);
      }
      
      // INEFFICIENT: 49,322 CU - Clone data (3.5% more expensive)
      // WARNING: Runs out of memory at 40+ iterations!
      fn sum_by_value(data: Vec<u64>) -> u64 {
          data.iter().sum()
      }
      
      for _ in 0..39 {
          sum_clone += sum_by_value(balances.clone());
      }
      ```
      
      **Memory concern**: Solana programs have a 32KB heap using a bump allocator that **never frees memory** during transaction execution. Excessive cloning leads to out-of-memory errors.
      
      **Best practice**: Always pass by reference (`&T`) unless you explicitly need ownership transfer. Use `Copy` types for small data.
      
      ### 7. Checked Math vs Unchecked Operations
      
      Checked arithmetic adds safety at the cost of compute units.
      
      **Comparison:**
      
      ```rust
      let mut count: u64 = 1;
      
      // 97,314 CU - Checked multiplication with overflow protection
      for _ in 0..12000 {
          count = count.checked_mul(2).expect("overflow");
      }
      
      // 85,113 CU - Bit shift operation (12% savings)
      // Equivalent to multiply by 2, but unchecked
      for _ in 0..12000 {
          count = count << 1;
      }
      ```
      
      **Trade-off**: Unchecked operations are faster but risk overflow bugs that can lead to serious security vulnerabilities.
      
      **Best practice**:
      - Use checked math by default for safety
      - Profile your program to identify hot paths
      - Only switch to unchecked math when:
        - You've proven overflow is impossible
        - CU savings are critical
        - You've added overflow tests
      
      **Compiler configuration** (in Cargo.toml):
      
      ```toml
      [profile.release]
      overflow-checks = true  # Keep overflow checks even in release mode
      ```
      
      ## Framework Comparison
      
      Different implementation approaches offer varying trade-offs between developer experience, safety, and performance.
      
      | Implementation | Binary Size | Deploy Cost | Init CU | Increment CU |
      |---------------|-------------|-------------|---------|--------------|
      | **Anchor** | 265,677 bytes | 1.85 SOL | 6,302 | 946 |
      | **Anchor Zero-Copy** | Same | 1.85 SOL | 5,020 | ~1,254 |
      | **Native Rust** | 48,573 bytes | 0.34 SOL | - | 843 |
      | **Unsafe Rust** | 973 bytes | 0.008 SOL | - | 5 |
      | **Assembly (SBPF)** | 1,389 bytes | 0.01 SOL | - | 4 |
      | **C** | 1,333 bytes | 0.01 SOL | - | 5 |
      
      **Key insights:**
      
      - **Anchor**: Best developer experience, automatic account validation, but highest CU and deployment costs
      - **Anchor Zero-Copy**: Significant CU improvement over standard Anchor with minimal code changes
      - **Native Rust**: 11% CU savings over Anchor, 82% smaller deployment size, moderate complexity
      - **Unsafe Rust**: 99% CU savings, minimal size, but requires extreme care and deep expertise
      - **Assembly/C**: Maximum optimization possible, but very difficult to develop and maintain
      
      **Recommendation**: Start with Anchor or native Rust. Optimize hot paths with zero-copy. Only consider unsafe Rust or lower-level languages for critical performance bottlenecks after profiling.
      
      ## Advanced Optimization Techniques
      
      ### 1. Compiler Flags
      
      Configure optimization in `Cargo.toml`:
      
      ```toml
      [profile.release]
      opt-level = 3           # Maximum optimization
      lto = "fat"             # Full link-time optimization
      codegen-units = 1       # Single codegen unit for better optimization
      overflow-checks = true  # Keep safety checks despite performance cost
      ```
      
      **Trade-offs**:
      - `overflow-checks = false`: Saves CU but removes critical safety checks
      - Higher `opt-level`: Better performance but slower compilation
      - `lto = "fat"`: Maximum optimization but much slower builds
      
      ### 2. Function Inlining
      
      Control function inlining to balance CU usage and stack space:
      
      ```rust
      // Force inlining - saves CU by eliminating function call overhead
      #[inline(always)]
      fn add(a: u64, b: u64) -> u64 {
          a + b
      }
      
      // Prevent inlining - saves stack space at the cost of CU
      #[inline(never)]
      pub fn complex_operation() {
          // Large function body
      }
      ```
      
      **Trade-off**: Inlining saves CU but increases stack usage. Solana has a 4KB stack limit, so excessive inlining can cause stack overflow.
      
      ### 3. Alternative Entry Points
      
      The standard Solana entry point adds overhead. Alternatives:
      
      **Standard entry point:**
      ```rust
      use solana_program::entrypoint;
      entrypoint!(process_instruction);
      ```
      
      **Minimal entry points:**
      - [solana-nostd-entrypoint](https://github.com/cavemanloverboy/solana-nostd-entrypoint): Ultra-minimal entry using unsafe Rust
      - [eisodos](https://github.com/anza-xyz/eisodos): Alternative minimal entry point
      
      **Warning**: These require deep understanding of Solana internals and unsafe Rust. Only use for extreme optimization needs.
      
      ### 4. Custom Heap Allocators
      
      Solana's default bump allocator never frees memory during transaction execution.
      
      **Problem:**
      ```rust
      // This will eventually run out of heap space (32KB limit)
      for _ in 0..1000 {
          let v = vec![0u8; 1024];  // Each iteration uses more heap
          // Memory is never freed!
      }
      ```
      
      **Solution - Custom allocators:**
      
      - **smalloc**: Used by Metaplex programs, provides better memory management
      - Prevents out-of-memory errors in memory-intensive operations
      
      **Implementation** (advanced):
      ```rust
      #[global_allocator]
      static ALLOCATOR: custom_allocator::CustomAllocator = custom_allocator::CustomAllocator;
      ```
      
      ### 5. Boxing and Heap Allocation
      
      Heap operations cost more CU than stack operations.
      
      ```rust
      // Stack allocation - faster
      let data = [0u8; 100];
      
      // Heap allocation - slower, uses more CU
      let data = Box::new([0u8; 100]);
      ```
      
      **Best practice**: Avoid `Box`, `Vec`, and other heap allocations when stack allocation is possible and doesn't risk overflow.
      
      ## Measuring Compute Units
      
      ### Using sol_log_compute_units()
      
      Built-in logging function to track CU consumption:
      
      ```rust
      use solana_program::log::sol_log_compute_units;
      
      pub fn my_instruction(ctx: Context<MyContext>) -> Result<()> {
          sol_log_compute_units(); // Log remaining CU
      
          // ... do some work ...
      
          sol_log_compute_units(); // Log remaining CU again
          Ok(())
      }
      ```
      
      **Output in transaction logs:**
      ```
      Program consumption: 200000 units remaining
      Program consumption: 195432 units remaining
      ```
      
      **CU used = 200000 - 195432 = 4,568 CU**
      
      ### compute_fn! Macro
      
      Convenient macro for measuring specific code blocks (costs 409 CU overhead):
      
      ```rust
      #[macro_export]
      macro_rules! compute_fn {
          ($msg:expr=> $($tt:tt)*) => {
              ::solana_program::msg!(concat!($msg, " {"));
              ::solana_program::log::sol_log_compute_units();
              let res = { $($tt)* };
              ::solana_program::log::sol_log_compute_units();
              ::solana_program::msg!(concat!(" } // ", $msg));
              res
          };
      }
      ```
      
      **Usage:**
      
      ```rust
      let result = compute_fn! { "My expensive operation" =>
          expensive_computation()
      };
      ```
      
      **Output:**
      ```
      Program log: My expensive operation {
      Program consumption: 195432 units remaining
      Program consumption: 180123 units remaining
      Program log: } // My expensive operation
      ```
      
      **Actual CU = (195432 - 180123) - 409 (macro overhead) = 14,900 CU**
      
      ### Using Mollusk Bencher
      
      For native Rust programs, use Mollusk's built-in benchmarking (see main SKILL.md for details).
      
      ## Anti-Patterns to Avoid
      
      ### 1. Excessive Logging
      
      ```rust
      // BAD: Logging in production
      msg!("Processing user {}", user_pubkey);
      msg!("Amount: {}", amount);
      msg!("Timestamp: {}", Clock::get()?.unix_timestamp);
      ```
      
      **Solution**: Remove logging or use conditional compilation:
      
      ```rust
      #[cfg(feature = "debug")]
      msg!("Processing user {}", user_pubkey);
      ```
      
      ### 2. Large Data Types for Small Values
      
      ```rust
      // BAD: Using u64 when u8 suffices
      pub struct Config {
          pub fee_percentage: u64,  // Only 0-100
          pub max_items: u64,       // Only 0-255
      }
      
      // GOOD: Use smallest type
      pub struct Config {
          pub fee_percentage: u8,   // 0-100
          pub max_items: u8,        // 0-255
      }
      ```
      
      ### 3. Cloning Large Structures
      
      ```rust
      // BAD: Unnecessary clone
      fn process_data(data: Vec<u8>) -> Result<()> {
          let copy = data.clone();  // Wastes CU and heap
          // ...
      }
      
      // GOOD: Pass by reference
      fn process_data(data: &[u8]) -> Result<()> {
          // Work directly with reference
      }
      ```
      
      ### 4. Repeated PDA Derivation
      
      ```rust
      // BAD: Finding bump every time
      #[account(
          seeds = [b"vault"],
          bump  // Finds bump on every call!
      )]
      pub vault: Account<'info, Vault>,
      
      // GOOD: Use saved bump
      #[account(
          seeds = [b"vault"],
          bump = vault.bump  // Uses saved bump
      )]
      pub vault: Account<'info, Vault>,
      ```
      
      ### 5. Unnecessary Boxing
      
      ```rust
      // BAD: Boxing adds heap overhead
      let value = Box::new(calculate_value());
      
      // GOOD: Keep on stack
      let value = calculate_value();
      ```
      
      ### 6. String Operations
      
      ```rust
      // BAD: String concatenation and formatting
      let message = format!("User {} sent {} tokens", user, amount);
      msg!(&message);
      
      // GOOD: Use separate logs or remove entirely
      user.log();
      amount.log();
      ```
      
      ### 7. Deep CPI Chains
      
      Each CPI adds significant overhead. Avoid unnecessary indirection:
      
      ```rust
      // BAD: Unnecessary CPI
      invoke(
          &my_helper_program::process(),
          &accounts,
      )?;
      
      // GOOD: Direct implementation
      process_directly(&accounts)?;
      ```
      
      ### 8. Not Using Zero-Copy for Large Accounts
      
      ```rust
      // BAD: Large account with standard serialization
      #[account]
      pub struct LargeData {
          pub items: [u64; 1000],  // Expensive to serialize/deserialize
      }
      
      // GOOD: Use zero-copy
      #[account(zero_copy)]
      #[repr(C)]
      pub struct LargeData {
          pub items: [u64; 1000],  // Direct memory access
      }
      ```
      
      ## Best Practices Summary
      
      1. **Minimize or eliminate logging** in production code
      2. **Use zero-copy** for accounts with large data structures
      3. **Cache PDA bumps** - derive once, store in account, reuse
      4. **Choose smallest data types** that meet your requirements
      5. **Pass by reference** instead of cloning data
      6. **Profile before optimizing** - measure CU usage to identify bottlenecks
      7. **Consider native Rust** over Anchor for performance-critical programs
      8. **Use `vec![]` initialization** instead of repeated `push()` calls
      9. **Avoid unnecessary CPIs** - use direct operations when safe
      10. **Balance safety vs performance** - don't sacrifice security without careful analysis
      11. **Test CU usage** regularly - include benchmarks in your test suite
      12. **Use checked math by default** - only optimize to unchecked when proven safe
      13. **Minimize heap allocations** - prefer stack when possible
      14. **Remove or conditionally compile debug code** for production builds
      15. **Consider zero-copy for frequently updated accounts** - 50%+ CU savings
      
      ## Additional Resources
      
      ### Official Documentation
      - [How to Optimize Compute](https://solana.com/developers/guides/advanced/how-to-optimize-compute)
      - [Solana Compute Budget Documentation](https://github.com/solana-labs/solana/blob/090e11210aa7222d8295610a6ccac4acda711bb9/program-runtime/src/compute_budget.rs#L26-L87)
      
      ### Code Examples and Tools
      - [solana-developers/cu_optimizations](https://github.com/solana-developers/cu_optimizations) - Official examples with benchmarks
      - [hetdagli234/optimising-solana-programs](https://github.com/hetdagli234/optimising-solana-programs) - Community optimization examples
      
      ### Video Guides
      - [How to optimize CU in programs](https://www.youtube.com/watch?v=7CbAK7Oq_o4)
      - [Program optimization Part 1](https://www.youtube.com/watch?v=xoJ-3NkYXfY)
      - [Program optimization Part 2 - Advanced](https://www.youtube.com/watch?v=Pwly1cOa2hg)
      - [Writing Solana programs in Assembly](https://www.youtube.com/watch?v=eacDC0VgyxI)
      
      ### Technical Articles
      - [RareSkills: Solana Compute Unit Price](https://rareskills.io/post/solana-compute-unit-price)
      - [Understanding Solana Compute Units](https://www.helius.dev/blog/priority-fees-understanding-solanas-transaction-fee-mechanics)
      
      ### Advanced Tools
      - [solana-nostd-entrypoint](https://github.com/cavemanloverboy/solana-nostd-entrypoint) - Minimal entry point
      - [Mollusk](https://github.com/anza-xyz/mollusk) - Fast testing with CU benchmarking
      
    • cpi.md 20.4 KB
      # Cross-Program Invocation (CPI)
      
      This reference provides comprehensive coverage of Cross-Program Invocation (CPI) for native Rust Solana program development, including invoke patterns, account privilege propagation, and security considerations.
      
      ## Table of Contents
      
      1. [What is CPI](#what-is-cpi)
      2. [CPI Fundamentals](#cpi-fundamentals)
      3. [invoke vs invoke_signed](#invoke-vs-invoke_signed)
      4. [Account Privilege Propagation](#account-privilege-propagation)
      5. [Common CPI Patterns](#common-cpi-patterns)
      6. [CPI Limits and Constraints](#cpi-limits-and-constraints)
      7. [Security Considerations](#security-considerations)
      8. [Best Practices](#best-practices)
      
      ---
      
      ## What is CPI
      
      **Cross-Program Invocation (CPI) is when one Solana program directly calls instructions on another program.**
      
      ### Conceptual Model
      
      If you think of a Solana instruction as an API endpoint, a CPI is like one API endpoint internally calling another.
      
      ```
      User Transaction
           │
           ▼
      ┌────────────────────┐
      │   Your Program     │
      │                    │
      │   ┌──────────────┐ │
      │   │ Instruction  │ │
      │   │   Handler    │ │
      │   └──────┬───────┘ │
      │          │ CPI     │
      └──────────┼─────────┘
                 │
                 ▼
      ┌────────────────────┐
      │ System Program     │
      │  create_account    │
      └────────────────────┘
      ```
      
      ### Why CPI is Essential
      
      **Composability**: Programs can leverage functionality from other programs without reimplementing it.
      
      **Common Use Cases:**
      - Create accounts (System Program CPI)
      - Transfer tokens (Token Program CPI)
      - Interact with DeFi protocols
      - Call custom program logic
      - Complex multi-step operations
      
      ### CPI vs Direct Instruction
      
      | Aspect | Direct Instruction | CPI |
      |--------|-------------------|-----|
      | Who initiates | User wallet | Another program |
      | Signer source | User's private key | Program or PDA |
      | Call depth | 1 (top-level) | 2-5 (nested) |
      | Use case | Entry point | Program-to-program |
      
      ---
      
      ## CPI Fundamentals
      
      ### The Two CPI Functions
      
      Solana provides two functions for making CPIs:
      
      ```rust
      use solana_program::program::{invoke, invoke_signed};
      
      // 1. invoke: For regular account signers
      pub fn invoke(
          instruction: &Instruction,
          account_infos: &[AccountInfo],
      ) -> ProgramResult
      
      // 2. invoke_signed: For PDA signers
      pub fn invoke_signed(
          instruction: &Instruction,
          account_infos: &[AccountInfo],
          signers_seeds: &[&[&[u8]]],
      ) -> ProgramResult
      ```
      
      ### Required Imports
      
      ```rust
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          instruction::{AccountMeta, Instruction},
          program::{invoke, invoke_signed},
          pubkey::Pubkey,
      };
      ```
      
      ### Instruction Structure
      
      Before making a CPI, you must construct an `Instruction`:
      
      ```rust
      pub struct Instruction {
          /// Program ID of the program being invoked
          pub program_id: Pubkey,
      
          /// Accounts required by the instruction
          pub accounts: Vec<AccountMeta>,
      
          /// Serialized instruction data
          pub data: Vec<u8>,
      }
      
      pub struct AccountMeta {
          /// Account public key
          pub pubkey: Pubkey,
      
          /// Is this account a signer?
          pub is_signer: bool,
      
          /// Is this account writable?
          pub is_writable: bool,
      }
      ```
      
      ---
      
      ## invoke vs invoke_signed
      
      ### invoke: Regular Signers
      
      Use `invoke` when all required signers are regular accounts (not PDAs).
      
      **Example: User transfers SOL**
      
      ```rust
      use solana_program::{
          account_info::{next_account_info, AccountInfo},
          entrypoint::ProgramResult,
          program::invoke,
          program_error::ProgramError,
          pubkey::Pubkey,
          system_instruction,
      };
      
      pub fn user_transfer_sol(
          _program_id: &Pubkey,
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
      
          let sender = next_account_info(account_info_iter)?;
          let recipient = next_account_info(account_info_iter)?;
          let system_program = next_account_info(account_info_iter)?;
      
          // Verify sender signed the transaction
          if !sender.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          // Create transfer instruction
          let transfer_ix = system_instruction::transfer(
              sender.key,
              recipient.key,
              amount,
          );
      
          // Execute CPI (sender already signed the transaction)
          invoke(
              &transfer_ix,
              &[
                  sender.clone(),
                  recipient.clone(),
                  system_program.clone(),
              ],
          )?;
      
          Ok(())
      }
      ```
      
      **Key Points:**
      - `sender.is_signer` must be true (verified at transaction level)
      - No `signers_seeds` needed
      - `invoke` internally calls `invoke_signed` with empty seeds
      
      ### invoke_signed: PDA Signers
      
      Use `invoke_signed` when a PDA needs to sign the instruction.
      
      **Example: PDA transfers SOL**
      
      ```rust
      use solana_program::{
          account_info::{next_account_info, AccountInfo},
          entrypoint::ProgramResult,
          program::invoke_signed,
          program_error::ProgramError,
          pubkey::Pubkey,
          system_instruction,
      };
      
      pub fn pda_transfer_sol(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
      
          let pda_account = next_account_info(account_info_iter)?;
          let recipient = next_account_info(account_info_iter)?;
          let system_program = next_account_info(account_info_iter)?;
      
          // Derive PDA and verify
          let (pda, bump_seed) = Pubkey::find_program_address(
              &[b"vault", recipient.key.as_ref()],
              program_id,
          );
      
          if pda != *pda_account.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // Create transfer instruction
          let transfer_ix = system_instruction::transfer(
              pda_account.key,  // From PDA (needs signing!)
              recipient.key,
              amount,
          );
      
          // PDA signing seeds (must match derivation)
          let signer_seeds: &[&[&[u8]]] = &[&[
              b"vault",
              recipient.key.as_ref(),
              &[bump_seed],  // Critical: bump must be included
          ]];
      
          // Execute CPI with PDA signature
          invoke_signed(
              &transfer_ix,
              &[
                  pda_account.clone(),
                  recipient.clone(),
                  system_program.clone(),
              ],
              signer_seeds,  // Runtime verifies and grants signing authority
          )?;
      
          Ok(())
      }
      ```
      
      **How Runtime Handles PDA Signing:**
      
      1. Runtime receives `signers_seeds`
      2. Calls `create_program_address(signers_seeds, calling_program_id)`
      3. Verifies derived PDA matches an account in the instruction
      4. Grants signing authority for that account
      5. Executes the CPI
      
      **Critical:** Seeds must exactly match the PDA derivation, including the bump.
      
      ---
      
      ## Account Privilege Propagation
      
      ### Privilege Extension
      
      When making a CPI, account privileges **extend** from the caller to the callee.
      
      ```
      User Transaction
           │ (provides: signer=true, writable=true)
           ▼
      ┌─────────────────────┐
      │  Program A          │
      │  Receives accounts: │
      │  - user (signer)    │──┐ Privileges
      │  - vault (writable) │  │ propagate
      └─────────────────────┘  │
                               ▼
                          ┌─────────────────────┐
                          │  Program B (via CPI)│
                          │  Can use:           │
                          │  - user (signer)    │
                          │  - vault (writable) │
                          └─────────────────────┘
      ```
      
      ### Propagation Rules
      
      **Rule 1:** If an account is a signer in Program A, it remains a signer in Program B (via CPI)
      
      **Rule 2:** If an account is writable in Program A, it remains writable in Program B (via CPI)
      
      **Rule 3:** Programs can add PDA signers via `invoke_signed`
      
      **Rule 4:** Programs cannot escalate privileges (can't make non-signer a signer without PDA derivation)
      
      ### Example: Privilege Propagation Chain
      
      ```rust
      // User calls Program A
      // Accounts: [user (signer, writable), vault (writable), data_account]
      
      // Program A → CPI to Program B
      invoke(
          &instruction_for_program_b,
          &[user.clone(), vault.clone()],  // Both retain privileges
      )?;
      
      // Program B → CPI to Program C
      invoke(
          &instruction_for_program_c,
          &[user.clone()],  // user still a signer!
      )?;
      ```
      
      **Depth**: Up to 4 levels of CPI (5 total stack height including initial transaction)
      
      ---
      
      ## Common CPI Patterns
      
      ### 1. System Program: Create Account
      
      **Most common CPI**: Creating new accounts.
      
      ```rust
      use solana_program::{
          program::invoke_signed,
          rent::Rent,
          system_instruction,
          sysvar::Sysvar,
      };
      
      pub fn create_pda_account(
          program_id: &Pubkey,
          payer: &AccountInfo,
          pda_account: &AccountInfo,
          system_program: &AccountInfo,
          space: usize,
          seeds: &[&[u8]],
          bump: u8,
      ) -> ProgramResult {
          // Calculate rent
          let rent = Rent::get()?;
          let rent_lamports = rent.minimum_balance(space);
      
          // Create account instruction
          let create_account_ix = system_instruction::create_account(
              payer.key,
              pda_account.key,
              rent_lamports,
              space as u64,
              program_id,
          );
      
          // Prepare signer seeds
          let mut full_seeds = seeds.to_vec();
          full_seeds.push(&[bump]);
          let signer_seeds: &[&[&[u8]]] = &[&full_seeds];
      
          // Execute CPI
          invoke_signed(
              &create_account_ix,
              &[payer.clone(), pda_account.clone(), system_program.clone()],
              signer_seeds,
          )?;
      
          Ok(())
      }
      ```
      
      ### 2. System Program: Transfer SOL
      
      ```rust
      use solana_program::system_instruction;
      
      // From regular account
      let transfer_ix = system_instruction::transfer(from_key, to_key, lamports);
      invoke(&transfer_ix, &[from_account, to_account, system_program])?;
      
      // From PDA
      let transfer_ix = system_instruction::transfer(pda_key, to_key, lamports);
      let signer_seeds: &[&[&[u8]]] = &[&[seeds, &[bump]]];
      invoke_signed(&transfer_ix, &[pda_account, to_account, system_program], signer_seeds)?;
      ```
      
      ### 3. Custom Program CPI
      
      **Calling another custom program:**
      
      ```rust
      use borsh::BorshSerialize;
      
      #[derive(BorshSerialize)]
      struct CustomInstructionData {
          amount: u64,
          memo: String,
      }
      
      pub fn call_custom_program(
          custom_program_id: &Pubkey,
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let user = next_account_info(account_info_iter)?;
          let target_account = next_account_info(account_info_iter)?;
          let custom_program = next_account_info(account_info_iter)?;
      
          // Serialize instruction data
          let instruction_data = CustomInstructionData {
              amount,
              memo: "Hello from CPI".to_string(),
          };
          let data = instruction_data.try_to_vec()?;
      
          // Build instruction
          let instruction = Instruction {
              program_id: *custom_program_id,
              accounts: vec![
                  AccountMeta::new(*user.key, true),           // signer, writable
                  AccountMeta::new(*target_account.key, false), // writable
              ],
              data,
          };
      
          // Execute CPI
          invoke(
              &instruction,
              &[user.clone(), target_account.clone(), custom_program.clone()],
          )?;
      
          Ok(())
      }
      ```
      
      ### 4. Multiple PDAs Signing
      
      ```rust
      pub fn multi_pda_cpi(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let pda1_seeds = &[b"pda1", &[bump1]];
          let pda2_seeds = &[b"pda2", &[bump2]];
      
          // Multiple PDA signers
          let signer_seeds: &[&[&[u8]]] = &[
              pda1_seeds,  // First PDA
              pda2_seeds,  // Second PDA
          ];
      
          invoke_signed(&instruction, &accounts, signer_seeds)?;
      
          Ok(())
      }
      ```
      
      ### 5. Chained CPIs
      
      **Program A calls Program B, which calls Program C:**
      
      ```rust
      // In Program A
      pub fn program_a_handler(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          // Call Program B
          let instruction_for_b = build_program_b_instruction();
          invoke(&instruction_for_b, accounts)?;
      
          Ok(())
      }
      
      // In Program B
      pub fn program_b_handler(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          // Call Program C
          let instruction_for_c = build_program_c_instruction();
          invoke(&instruction_for_c, accounts)?;
      
          Ok(())
      }
      ```
      
      **Depth tracking**: User→A→B→C = stack depth 4 (within limit)
      
      ---
      
      ## CPI Limits and Constraints
      
      ### Stack Depth Limit
      
      **Maximum call depth:** 5 (including initial transaction)
      
      ```
      Depth 1: User Transaction
      Depth 2: Program A (first CPI)
      Depth 3: Program B (second CPI)
      Depth 4: Program C (third CPI)
      Depth 5: Program D (fourth CPI)
      Depth 6: ❌ ERROR - MAX_INSTRUCTION_STACK_DEPTH exceeded
      ```
      
      **Constant:**
      ```rust
      // From agave source
      pub const MAX_INSTRUCTION_STACK_DEPTH: usize = 5;
      ```
      
      **Error when exceeded:**
      ```
      Error: CallDepth(5)
      ```
      
      ### Account Limits
      
      - **Max accounts per instruction:** 256 (practical limit ~64 without ALTs)
      - **Max writable accounts:** Limited by transaction size
      - **Duplicate accounts:** Allowed but share state (mutations visible to all references)
      
      ### Compute Unit Costs
      
      CPI operations consume compute units:
      
      | Operation | Approximate CU Cost |
      |-----------|---------------------|
      | `invoke` base cost | ~1,000 CU |
      | `invoke_signed` base cost | ~1,000 CU |
      | Per account passed | ~50-100 CU |
      | PDA derivation in runtime | ~1,500 CU |
      | Actual callee logic | Variable |
      
      **Tip:** Pre-derive PDAs and store bumps to save CU.
      
      ### Data Size Limits
      
      - **Instruction data:** No hard limit, but affects transaction size (1232 bytes max for non-ALT transactions)
      - **Account data modification:** Accounts can be resized via `realloc` (up to 10 MiB)
      
      ---
      
      ## Security Considerations
      
      ### 1. Validate PDA Derivation Before CPI
      
      **❌ Vulnerable:**
      ```rust
      pub fn vulnerable_cpi(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let pda_account = &accounts[0];
      
          // No validation!
          let signer_seeds: &[&[&[u8]]] = &[&[b"vault", &[bump]]];
      
          invoke_signed(&instruction, &[pda_account.clone()], signer_seeds)?;
          Ok(())
      }
      ```
      
      **✅ Secure:**
      ```rust
      pub fn secure_cpi(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          bump: u8,
      ) -> ProgramResult {
          let pda_account = &accounts[0];
      
          // Validate PDA before CPI
          let (expected_pda, _) = Pubkey::find_program_address(&[b"vault"], program_id);
          if expected_pda != *pda_account.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          let signer_seeds: &[&[&[u8]]] = &[&[b"vault", &[bump]]];
          invoke_signed(&instruction, &[pda_account.clone()], signer_seeds)?;
          Ok(())
      }
      ```
      
      ### 2. Verify Signer Requirements
      
      **Always check `is_signer` before making CPIs that transfer value:**
      
      ```rust
      if !user.is_signer {
          return Err(ProgramError::MissingRequiredSignature);
      }
      
      let transfer_ix = system_instruction::transfer(user.key, vault.key, amount);
      invoke(&transfer_ix, &[user.clone(), vault.clone(), system_program.clone()])?;
      ```
      
      ### 3. Program ID Verification
      
      **Verify the program being called is the expected program:**
      
      ```rust
      const EXPECTED_PROGRAM: &str = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
      
      if token_program.key.to_string() != EXPECTED_PROGRAM {
          return Err(ProgramError::IncorrectProgramId);
      }
      ```
      
      ### 4. Privilege Leakage
      
      **Be careful about which accounts you pass in CPIs:**
      
      ```rust
      // ❌ Dangerous - passes admin with signer privilege
      invoke(
          &untrusted_program_instruction,
          &[admin.clone(), user_data.clone()],  // Admin is a signer!
      )?;
      
      // ✅ Safe - only pass necessary accounts
      invoke(
          &untrusted_program_instruction,
          &[user_data.clone()],  // Admin not included
      )?;
      ```
      
      ### 5. Reent rancy Considerations
      
      **Solana programs are generally safe from reentrancy** because:
      - Accounts are locked during instruction execution
      - Runtime prevents concurrent modifications
      
      **However, be cautious with:**
      - State assumptions across CPI boundaries
      - Read-modify-write patterns split across CPIs
      
      ### 6. Error Handling
      
      **CPI errors propagate to the caller:**
      
      ```rust
      // If CPI fails, entire transaction reverts
      match invoke(&instruction, &accounts) {
          Ok(()) => msg!("CPI succeeded"),
          Err(e) => {
              msg!("CPI failed: {:?}", e);
              return Err(e);  // Propagate error
          }
      }
      ```
      
      **All state changes are atomic** - if CPI fails, all changes rollback.
      
      ---
      
      ## Best Practices
      
      ### 1. Derive PDAs Once
      
      ```rust
      // ❌ Wasteful - derives multiple times
      pub fn wasteful(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
          let (pda, bump) = Pubkey::find_program_address(&[b"data"], program_id);
          // ... use pda
      
          let (pda_again, bump_again) = Pubkey::find_program_address(&[b"data"], program_id);
          // ... use pda_again (same as pda!)
      }
      
      // ✅ Efficient - derive once, reuse
      pub fn efficient(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
          let (pda, bump) = Pubkey::find_program_address(&[b"data"], program_id);
          // Reuse pda and bump
      }
      ```
      
      ### 2. Store and Reuse Bumps
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct VaultData {
          pub bump: u8,  // Store on creation
          // ... other fields
      }
      
      // On CPI: use stored bump
      let vault_data = VaultData::try_from_slice(&vault_pda.data.borrow())?;
      let signer_seeds: &[&[&[u8]]] = &[&[b"vault", &[vault_data.bump]]];
      ```
      
      **Benefit:** Saves ~2,700 CU per operation.
      
      ### 3. Helper Functions for Common CPIs
      
      ```rust
      pub mod cpi_helpers {
          use super::*;
      
          pub fn transfer_sol(
              from: &AccountInfo,
              to: &AccountInfo,
              system_program: &AccountInfo,
              amount: u64,
          ) -> ProgramResult {
              let ix = system_instruction::transfer(from.key, to.key, amount);
              invoke(&ix, &[from.clone(), to.clone(), system_program.clone()])
          }
      
          pub fn transfer_sol_from_pda(
              from_pda: &AccountInfo,
              to: &AccountInfo,
              system_program: &AccountInfo,
              amount: u64,
              signer_seeds: &[&[&[u8]]],
          ) -> ProgramResult {
              let ix = system_instruction::transfer(from_pda.key, to.key, amount);
              invoke_signed(&ix, &[from_pda.clone(), to.clone(), system_program.clone()], signer_seeds)
          }
      }
      ```
      
      ### 4. Validate All CPI Inputs
      
      **Checklist before CPI:**
      - ✅ Verify signer requirements (`is_signer`)
      - ✅ Validate PDA derivation
      - ✅ Check program IDs match expectations
      - ✅ Verify account ownership
      - ✅ Validate data integrity
      
      ### 5. Document CPI Dependencies
      
      ```rust
      /// Transfers SOL from program vault to recipient.
      ///
      /// # Accounts
      /// 0. `[writable]` vault_pda - Program vault (PDA, signer)
      /// 1. `[writable]` recipient - Receives SOL
      /// 2. `[]` system_program - System Program (11111...)
      ///
      /// # CPIs Made
      /// - System Program: transfer (from vault to recipient)
      pub fn withdraw_from_vault(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          // ...
      }
      ```
      
      ### 6. Error Context
      
      ```rust
      invoke(&instruction, &accounts).map_err(|e| {
          msg!("CPI to System Program failed");
          e
      })?;
      ```
      
      ### 7. Minimize CPI Depth
      
      **Keep call chains shallow:**
      - Reduces compute units
      - Easier to debug
      - Lower risk of hitting stack limit
      - Better user experience (simpler transactions)
      
      ---
      
      ## Summary
      
      **Key Takeaways:**
      
      1. **CPI enables composability** - programs call other programs
      2. **Use `invoke` for regular signers**, `invoke_signed` for PDAs
      3. **Privileges propagate** - signers and writable flags extend through CPIs
      4. **Maximum depth is 5** - including initial transaction
      5. **Always validate PDAs** before using in `invoke_signed`
      6. **Verify signer requirements** to prevent unauthorized operations
      7. **Store bumps** in account data to save compute units
      8. **CPIs are atomic** - failures rollback all changes
      
      **Security Checklist:**
      - ✅ Validate PDA derivation with canonical bump
      - ✅ Verify `is_signer` for value transfers
      - ✅ Check program IDs match expectations
      - ✅ Only pass necessary accounts (avoid privilege leakage)
      - ✅ Handle CPI errors appropriately
      
      **Common Pattern:**
      ```rust
      // 1. Validate inputs
      if !user.is_signer {
          return Err(ProgramError::MissingRequiredSignature);
      }
      
      // 2. Derive and validate PDA if needed
      let (pda, bump) = Pubkey::find_program_address(&seeds, program_id);
      if pda != *pda_account.key {
          return Err(ProgramError::InvalidSeeds);
      }
      
      // 3. Build instruction
      let ix = build_instruction();
      
      // 4. Execute CPI
      invoke_signed(&ix, &accounts, &[&[seeds, &[bump]]])?;
      ```
      
      CPI is the foundation of program composability on Solana. Master it to build powerful, modular programs that leverage the entire ecosystem.
      
    • deployment.md 40.1 KB
      # Solana Program Deployment Reference
      
      This reference covers deployment workflows, best practices, and troubleshooting for both Anchor and native Rust Solana programs.
      
      ## Table of Contents
      
      - [Deployment Overview](#deployment-overview)
      - [Pre-Deployment Checklist](#pre-deployment-checklist)
      - [Building Programs](#building-programs)
      - [Deploying to Networks](#deploying-to-networks)
      - [Program Upgrades](#program-upgrades)
      - [Verified Builds](#verified-builds)
      - [Program Authority Management](#program-authority-management)
      - [Multisig Deployments](#multisig-deployments)
      - [Network-Specific Considerations](#network-specific-considerations)
      - [Post-Deployment](#post-deployment)
      - [Common Issues and Troubleshooting](#common-issues-and-troubleshooting)
      - [Best Practices](#best-practices)
      
      ---
      
      ## Deployment Overview
      
      ### Solana Networks
      
      Solana has three primary networks:
      
      **Localhost (127.0.0.1:8899)**
      - Local test validator running on your machine
      - Fastest iteration, no cost
      - Full control over network state
      - Use for rapid development and debugging
      
      **Devnet**
      - Public development network
      - Free SOL via airdrops
      - Resets periodically
      - Use for integration testing
      
      **Mainnet-beta**
      - Production network with real economic value
      - Requires real SOL for deployment and transactions
      - Immutable deployed programs (unless upgradeable)
      - Use for production deployments
      
      ### Network Configuration
      
      **Anchor** - Edit `Anchor.toml`:
      
      ```toml
      [toolchain]
      
      [features]
      seeds = false
      skip-lint = false
      
      [programs.localnet]
      my_program = "11111111111111111111111111111111"
      
      [programs.devnet]
      my_program = "YourDevnetProgramID"
      
      [programs.mainnet]
      my_program = "YourMainnetProgramID"
      
      [registry]
      url = "https://api.apr.dev"
      
      [provider]
      cluster = "Localnet"  # Change to "Devnet" or "Mainnet"
      wallet = "~/.config/solana/id.json"
      
      [scripts]
      test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
      ```
      
      **Native Rust** - Use Solana CLI:
      
      ```bash
      # View current config
      solana config get
      
      # Set network
      solana config set --url https://api.devnet.solana.com  # Devnet
      solana config set --url https://api.mainnet-beta.solana.com  # Mainnet
      solana config set --url http://localhost:8899  # Localnet
      
      # Set wallet
      solana config set --keypair ~/.config/solana/id.json
      ```
      
      ### Cost Considerations
      
      Program deployment requires rent-exempt balance for:
      
      1. **Program Account** - Stores program metadata (small cost)
      2. **Program Data Account** - Stores the executable bytecode (major cost)
      
      **Calculate deployment cost:**
      
      ```bash
      # Get program size
      ls -l target/deploy/my_program.so
      # Example: 363960 bytes
      
      # Check rent for program data account (1x program size in newer versions)
      solana rent 363960
      # Output:
      # Rent-exempt minimum: ~2.5 SOL
      
      # Add transaction fees (~0.002 SOL) for deployment transactions
      ```
      
      **Cost breakdown:**
      - **363KB program** ≈ 2.5 SOL rent + 0.002 SOL tx fees = **~2.502 SOL**
      - **800KB program** ≈ 5.5 SOL rent + 0.002 SOL tx fees = **~5.502 SOL**
      
      **Important:** Since Solana CLI v1.18+, program accounts are sized to 1x the .so file (previously 2x), reducing costs by approximately 50%.
      
      ---
      
      ## Pre-Deployment Checklist
      
      ### 1. Build Verification
      
      **Anchor:**
      ```bash
      # Clean build
      anchor clean
      anchor build
      
      # Verify build succeeded
      ls -la target/deploy/
      # Should see: my_program.so and my_program-keypair.json
      ```
      
      **Native Rust:**
      ```bash
      # Clean build
      cargo clean
      cargo build-sbf
      
      # Verify build
      ls -la target/deploy/
      # Should see: my_program.so
      ```
      
      ### 2. Testing Completeness
      
      **Anchor:**
      ```bash
      # Run all tests on local validator
      anchor test
      
      # Run tests without redeploying
      anchor test --skip-deploy
      
      # Run specific test file
      anchor test tests/my-test.ts
      ```
      
      **Native Rust:**
      ```bash
      # Run Mollusk unit tests
      cargo test
      
      # Run integration tests
      cargo test-sbf
      ```
      
      ### 3. Security Review
      
      - [ ] All account validations implemented (owner checks, signer checks)
      - [ ] No missing arithmetic overflow checks
      - [ ] PDA seeds properly validated
      - [ ] No uninitialized account usage
      - [ ] Authority checks on privileged operations
      - [ ] CPI security (check target program IDs)
      - [ ] Consider professional audit for mainnet
      
      ### 4. Program Size Optimization
      
      **Check current size:**
      ```bash
      ls -lh target/deploy/my_program.so
      ```
      
      **Optimization techniques:**
      
      ```toml
      # Cargo.toml - Release profile optimization
      [profile.release]
      overflow-checks = true
      lto = "fat"              # Link-time optimization
      codegen-units = 1        # Single codegen unit
      opt-level = "z"          # Optimize for size (use "3" for speed)
      strip = true             # Strip symbols
      
      [profile.release.build-override]
      opt-level = 3
      incremental = false
      codegen-units = 1
      ```
      
      **Remove unused dependencies:**
      ```bash
      # Check dependency tree
      cargo tree
      
      # Remove unused features
      # Instead of:
      # solana-program = "2.1.0"
      # Use:
      solana-program = { version = "2.1.0", default-features = false }
      ```
      
      **Current size limits:**
      - Maximum program size: ~1 MB (actual limit varies by compute budget)
      - Recommended: Keep under 500KB for reliable deployment
      
      ### 5. Rent Calculation
      
      ```bash
      # Calculate exact rent needed
      PROGRAM_SIZE=$(wc -c < target/deploy/my_program.so)
      solana rent $PROGRAM_SIZE
      
      # Fund deployment wallet
      solana balance
      # If insufficient, request airdrop (devnet) or transfer SOL
      ```
      
      ---
      
      ## Building Programs
      
      ### Anchor Programs
      
      **Standard build:**
      ```bash
      anchor build
      ```
      
      **What it produces:**
      - `target/deploy/my_program.so` - Executable binary
      - `target/deploy/my_program-keypair.json` - Program ID keypair
      - `target/idl/my_program.json` - Interface definition
      - `target/types/my_program.ts` - TypeScript types
      
      **Build specific program in workspace:**
      ```bash
      anchor build --program-name my_program
      ```
      
      **Verifiable build (Docker-based):**
      ```bash
      # Install solana-verify CLI
      cargo install solana-verify
      
      # Build verifiably
      solana-verify build
      
      # Build specific program
      solana-verify build --library-name my_program
      ```
      
      **Sync program IDs:**
      ```bash
      # After first build, sync declared IDs with keypair
      anchor keys sync
      ```
      
      ### Native Rust Programs
      
      **Standard build:**
      ```bash
      cargo build-sbf
      ```
      
      **What it produces:**
      - `target/deploy/my_program.so` - Executable binary
      - No automatic keypair generation (must provide or use deployed ID)
      
      **Verifiable build:**
      ```bash
      solana-verify build --library-name my_program
      ```
      
      **Build with specific Solana version:**
      ```bash
      # Set platform tools version
      cargo build-sbf --sbf-sdk ~/.local/share/solana/install/releases/2.1.0/solana-release/bin/sdk/sbf
      ```
      
      ### Understanding Build Outputs
      
      **.so file:**
      - Compiled BPF bytecode
      - This is what gets deployed on-chain
      - Hash determines if program matches source
      
      **Program ID keypair (Anchor):**
      - Generated on first build
      - Defines program's on-chain address
      - **CRITICAL:** Back this up before deploying
      
      **IDL (Anchor only):**
      - JSON describing program interface
      - Used by clients to interact with program
      - Can be uploaded on-chain for discovery
      
      ---
      
      ## Deploying to Networks
      
      ### Anchor Deployment
      
      **Deploy to configured network:**
      ```bash
      # Deploy to network specified in Anchor.toml [provider] cluster
      anchor deploy
      
      # Specify network explicitly
      anchor deploy --provider.cluster devnet
      anchor deploy --provider.cluster mainnet
      ```
      
      **Deploy with specific program ID:**
      ```bash
      # First deployment - uses keypair from target/deploy/
      anchor deploy
      
      # Redeploy to same address (upgrade)
      anchor deploy
      ```
      
      **Deploy with priority fees (congested networks):**
      ```bash
      # Set priority fee in micro-lamports per compute unit
      anchor deploy --provider.cluster mainnet \
        --program-name my_program \
        -- --with-compute-unit-price 50000
      ```
      
      **What `anchor deploy` does:**
      1. Reads program from `target/deploy/my_program.so`
      2. Creates or uses existing program account
      3. Uploads program data via multiple write transactions
      4. Sets executable flag on program account
      5. Optionally uploads IDL to on-chain account
      
      ### Native Rust Deployment
      
      **Deploy new program:**
      ```bash
      # Deploy with auto-generated program ID
      solana program deploy target/deploy/my_program.so
      
      # Deploy to specific program ID (first time)
      solana program deploy target/deploy/my_program.so \
        --program-id my_program-keypair.json
      ```
      
      **Deploy with priority fees:**
      ```bash
      solana program deploy target/deploy/my_program.so \
        --program-id <PROGRAM_ID> \
        --with-compute-unit-price 50000 \
        --max-sign-attempts 100 \
        --use-rpc
      ```
      
      **Flags explained:**
      - `--with-compute-unit-price` - Priority fee (micro-lamports per CU)
      - `--max-sign-attempts` - Retries for recent blockhash expiration
      - `--use-rpc` - Send transactions individually vs in batches
      - `-u <URL>` - Specify RPC endpoint
      
      **Check deployment cost before deploying:**
      ```bash
      # Dry run to estimate cost
      solana program deploy target/deploy/my_program.so --dry-run
      ```
      
      ### Deploying with Specific Program ID
      
      **Generate deterministic program ID:**
      ```bash
      # Create new keypair
      solana-keygen new -o my-program-keypair.json
      
      # View address
      solana-keygen pubkey my-program-keypair.json
      ```
      
      **For Anchor:**
      ```bash
      # Update lib.rs with new program ID
      declare_id!("YourNewProgramID");
      
      # Update Anchor.toml
      [programs.devnet]
      my_program = "YourNewProgramID"
      
      # Rebuild and deploy
      anchor build
      anchor keys sync  # Verify IDs match
      anchor deploy
      ```
      
      **For Native Rust:**
      ```bash
      # Deploy using keypair
      solana program deploy target/deploy/my_program.so \
        --program-id my-program-keypair.json
      ```
      
      ### Deployment Costs and Funding
      
      **Check balance before deployment:**
      ```bash
      solana balance
      ```
      
      **Fund wallet for devnet:**
      ```bash
      # Request airdrop (2 SOL max per request)
      solana airdrop 2
      
      # For larger programs, request multiple times
      solana airdrop 2 && solana airdrop 2
      ```
      
      **Fund wallet for mainnet:**
      ```bash
      # Transfer SOL from exchange or another wallet
      # No airdrops available on mainnet
      ```
      
      **Cost estimation:**
      ```bash
      # Program size
      PROGRAM_SIZE=$(wc -c < target/deploy/my_program.so)
      
      # Rent cost
      solana rent $PROGRAM_SIZE
      
      # Add ~0.002-0.01 SOL for transaction fees
      # Add priority fees if network is congested
      ```
      
      ---
      
      ## Program Upgrades
      
      ### How Upgrades Work
      
      Solana programs deployed via `solana program deploy` or `anchor deploy` are **upgradeable by default**.
      
      **Upgrade mechanism:**
      1. Program data lives in separate account from program account
      2. Upgrade authority (wallet) can replace program data
      3. Program address stays the same
      4. All accounts/PDAs remain valid
      
      **Check if program is upgradeable:**
      ```bash
      solana program show <PROGRAM_ID>
      
      # Output shows:
      # Program Id: <PROGRAM_ID>
      # Owner: BPFLoaderUpgradeable1111111111111111111111111
      # ProgramData Address: <DATA_ACCOUNT>
      # Authority: <UPGRADE_AUTHORITY>  # If upgradeable
      # Last Deployed In Slot: ...
      # Data Length: ...
      ```
      
      ### Anchor Upgrades
      
      **Upgrade deployed program:**
      ```bash
      anchor upgrade target/deploy/my_program.so \
        --program-id <PROGRAM_ID> \
        --provider.cluster devnet
      ```
      
      **Note:** In newer Anchor versions, `anchor deploy` automatically upgrades if program exists.
      
      **Upgrade with priority fees:**
      ```bash
      anchor upgrade target/deploy/my_program.so \
        --program-id <PROGRAM_ID> \
        --provider.cluster mainnet \
        -- --with-compute-unit-price 50000
      ```
      
      ### Native Rust Upgrades
      
      **Upgrade using same deploy command:**
      ```bash
      solana program deploy target/deploy/my_program.so \
        --program-id <PROGRAM_ID> \
        --upgrade-authority ~/.config/solana/id.json
      ```
      
      ### Extending Program Accounts
      
      **Problem:** If new program is larger than allocated space:
      ```
      Error: account data too small for instruction
      ```
      
      **Solution:** Extend program account before upgrading:
      
      ```bash
      # Check current program size
      solana program show <PROGRAM_ID>
      # Shows: Data Length: 363960 bytes
      
      # New build is larger
      NEW_SIZE=$(wc -c < target/deploy/my_program.so)
      echo $NEW_SIZE
      # Shows: 380000 bytes
      
      # Calculate additional bytes needed
      ADDITIONAL_BYTES=$((NEW_SIZE - 363960))
      echo $ADDITIONAL_BYTES
      # Shows: 16040 bytes
      
      # Extend program account
      solana program extend <PROGRAM_ID> $ADDITIONAL_BYTES
      
      # Check rent cost for extension
      solana rent $ADDITIONAL_BYTES
      # Example: 0.2 SOL
      
      # Now upgrade works
      solana program deploy target/deploy/my_program.so --program-id <PROGRAM_ID>
      ```
      
      **Note:** Program extension support added in Solana CLI v1.18+
      
      ### Data Migration Strategies
      
      **Account structure changes:**
      
      When upgrading programs that change account layouts:
      
      **Option 1: Version field**
      ```rust
      #[account]
      pub struct MyAccount {
          pub version: u8,  // Add version field
          pub data: u64,
          // New fields in v2
          pub new_field: Option<String>,
      }
      
      // In instruction handler
      if account.version == 1 {
          // Migrate from v1 to v2
          account.new_field = Some("default".to_string());
          account.version = 2;
      }
      ```
      
      **Option 2: Separate migration instruction**
      ```rust
      pub fn migrate_account_v1_to_v2(ctx: Context<MigrateAccount>) -> Result<()> {
          let account = &mut ctx.accounts.account;
      
          // Perform migration logic
          account.new_field = compute_new_field(&account.data);
          account.version = 2;
      
          Ok(())
      }
      ```
      
      **Option 3: New program version with migration path**
      - Deploy new program ID
      - Create migration instructions that move data
      - Deprecate old program gradually
      
      ---
      
      ## Verified Builds
      
      Verified builds prove deployed bytecode matches public source code.
      
      ### Why Verify?
      
      - **Transparency:** Users can audit your program's source
      - **Trust:** Proves deployed program matches GitHub repository
      - **Security:** Enables community security reviews
      - **Ecosystem:** Explorers display verified status
      - **Wallets:** May whitelist verified programs
      
      ### Tools for Verification
      
      **Solana Verify CLI:**
      ```bash
      cargo install solana-verify
      ```
      
      **Docker (required for deterministic builds):**
      - Install Docker: https://docs.docker.com/engine/install/
      - Ensure Docker daemon is running
      
      ### Building Verifiable Programs
      
      **Verifiable build process:**
      ```bash
      # Navigate to project root (with Cargo.toml)
      cd my-project
      
      # Build in Docker container for deterministic output
      solana-verify build
      
      # For workspace with specific program
      solana-verify build --library-name my_program
      
      # Get hash of built executable
      solana-verify get-executable-hash target/deploy/my_program.so
      ```
      
      **What makes builds verifiable:**
      1. **Docker environment:** Ensures consistent build environment
      2. **Locked dependencies:** `Cargo.lock` pins exact versions
      3. **Same toolchain:** Uses specific Rust/Solana version
      4. **Deterministic compilation:** Same input → same output
      
      **Project structure requirements:**
      
      ```
      my-project/
      ├── Cargo.toml          # Workspace manifest
      ├── Cargo.lock          # Locked dependencies (required!)
      ├── programs/
      │   └── my_program/
      │       ├── Cargo.toml
      │       └── src/
      │           └── lib.rs
      ```
      
      **Workspace Cargo.toml example:**
      ```toml
      [workspace]
      members = ["programs/*"]
      resolver = "2"
      
      [workspace.dependencies]
      solana-program = "2.1.0"
      
      [profile.release]
      overflow-checks = true
      lto = "fat"
      codegen-units = 1
      
      [profile.release.build-override]
      opt-level = 3
      incremental = false
      codegen-units = 1
      ```
      
      ### Deploying Verifiable Programs
      
      **Deploy verified build:**
      ```bash
      # IMPORTANT: Use the binary from solana-verify build
      # DO NOT run `anchor build` or `cargo build-sbf` after verification build
      
      # Deploy with priority fees for reliability
      solana program deploy target/deploy/my_program.so \
        --program-id <PROGRAM_ID> \
        -u https://api.mainnet-beta.solana.com \
        --with-compute-unit-price 50000 \
        --max-sign-attempts 100 \
        --use-rpc
      ```
      
      **Verify deployed program matches built executable:**
      ```bash
      # Get on-chain program hash
      solana-verify get-program-hash -u mainnet-beta <PROGRAM_ID>
      
      # Get local executable hash
      solana-verify get-executable-hash target/deploy/my_program.so
      
      # Hashes must match!
      ```
      
      ### Verifying Against Repository
      
      **Verify from GitHub repository:**
      ```bash
      solana-verify verify-from-repo \
        -u mainnet-beta \
        --program-id <PROGRAM_ID> \
        https://github.com/your-org/your-repo \
        --commit-hash <COMMIT_HASH> \
        --library-name my_program \
        --mount-path programs/my_program
      ```
      
      **Parameters explained:**
      - `--program-id`: On-chain program address
      - `--commit-hash`: Git commit to build from (optional, uses latest if omitted)
      - `--library-name`: Crate name from Cargo.toml `[lib]` section
      - `--mount-path`: Path to program directory in repo (for workspaces)
      
      **Upload verification data on-chain:**
      
      When prompted during verification:
      ```
      Would you like to upload verification data on-chain? (y/n)
      ```
      
      Select **yes** to write verification PDA. This enables:
      - Solana Explorer verification badge
      - OtterSec API verification
      - SolanaFM verification display
      
      ### Remote Verification with OtterSec API
      
      **Submit verification job:**
      ```bash
      solana-verify verify-from-repo \
        --remote \
        -u mainnet-beta \
        --program-id <PROGRAM_ID> \
        https://github.com/your-org/your-repo
      ```
      
      **Manual job submission:**
      ```bash
      solana-verify remote submit-job \
        --program-id <PROGRAM_ID> \
        --uploader <UPGRADE_AUTHORITY>
      ```
      
      **Check verification status:**
      ```bash
      solana-verify remote get-job-status --job-id <JOB_ID>
      ```
      
      **Verification API endpoint:**
      ```
      https://verify.osec.io/status/<PROGRAM_ID>
      ```
      
      **Where verified status appears:**
      - [Solana Explorer](https://explorer.solana.com)
      - [SolanaFM](https://solana.fm)
      - [SolScan](https://solscan.io)
      - [SolanaVerify.org](https://solanaverify.org)
      - [OtterSec API](https://verify.osec.io/verified-programs)
      
      ### security.txt Integration
      
      **Add security contact info:**
      
      ```rust
      #[cfg(not(feature = "no-entrypoint"))]
      solana_security_txt::security_txt! {
          name: "My Program",
          project_url: "https://myproject.com",
          contacts: "email:security@myproject.com,discord:myproject",
          policy: "https://github.com/myproject/security/blob/main/SECURITY.md",
          preferred_languages: "en",
          source_code: "https://github.com/myproject/program",
          source_release: "v1.0.0",
          auditors: "Audit Firm Name"
      }
      ```
      
      **Benefits:**
      - Security researchers know how to contact you
      - Shows commitment to security
      - Standard across Solana ecosystem
      
      ---
      
      ## Program Authority Management
      
      ### Viewing Program Information
      
      **Check program authority:**
      ```bash
      solana program show <PROGRAM_ID>
      
      # Output:
      # Program Id: YourProgramId
      # Owner: BPFLoaderUpgradeable1111111111111111111111111
      # ProgramData Address: <DATA_ACCOUNT_ADDRESS>
      # Authority: <CURRENT_AUTHORITY>  # Current upgrade authority
      # Last Deployed In Slot: 123456789
      # Data Length: 363960 bytes (0x58e38 bytes)
      ```
      
      **View all your deployed programs:**
      ```bash
      solana program show --programs
      
      # Shows all programs where your wallet is authority
      ```
      
      ### Transferring Upgrade Authority
      
      **Transfer to new authority:**
      ```bash
      solana program set-upgrade-authority <PROGRAM_ID> \
        --new-upgrade-authority <NEW_AUTHORITY_PUBKEY>
      ```
      
      **Common use cases:**
      - Transfer to multisig (Squads Protocol)
      - Transfer to governance program
      - Transfer to team member
      - Transfer to DAO
      
      **Transfer to multisig (Squads):**
      ```bash
      # Get Squads vault address from https://v4.squads.so/
      SQUADS_VAULT="YourSquadsVaultAddress"
      
      solana program set-upgrade-authority <PROGRAM_ID> \
        --new-upgrade-authority $SQUADS_VAULT
      ```
      
      ### Making Programs Immutable
      
      **WARNING:** This is irreversible!
      
      ```bash
      solana program set-upgrade-authority <PROGRAM_ID> --final
      
      # Confirms immutability - program can NEVER be upgraded
      ```
      
      **Use cases for immutability:**
      - DeFi protocols requiring immutable guarantees
      - After extensive auditing, lock the program
      - Community trust through code permanence
      
      **Considerations:**
      - Cannot fix bugs after making immutable
      - Cannot add features
      - Ensure thorough testing and auditing first
      - Consider governance/multisig instead
      
      ### Buffer Accounts for Deployment
      
      **Understanding buffers:**
      
      When deploying, the Solana CLI creates temporary buffer accounts:
      
      1. Creates buffer account
      2. Writes program data to buffer
      3. Deploys buffer to program account
      4. Closes buffer (if successful)
      
      **View your buffer accounts:**
      ```bash
      solana program show --buffers
      
      # Output:
      # Buffer Address                                | Authority                                      | Balance
      # Abc123...                                     | YourWallet...                                  | 2.5 SOL
      ```
      
      **Close buffer manually (failed deployment):**
      ```bash
      solana program close <BUFFER_ADDRESS>
      
      # Recovers rent SOL back to wallet
      ```
      
      **Common scenarios:**
      - Deployment failed mid-process
      - Want to cancel deployment
      - Need to reclaim SOL from old buffers
      
      ---
      
      ## Multisig Deployments
      
      Deploying with multisig (Squads Protocol) provides security for production programs.
      
      ### Why Use Multisig?
      
      - **Security:** No single point of failure
      - **Governance:** Team/DAO approval for upgrades
      - **Transparency:** On-chain approval trail
      - **Best practice:** Standard for serious projects
      
      ### Workflow Overview
      
      1. Build verifiable program
      2. Deploy with temporary authority
      3. Verify against repository
      4. Transfer authority to multisig
      5. Export PDA transaction for verification upload
      6. Submit through Squads UI
      7. Remote verification
      
      ### Detailed Multisig Deployment
      
      **1. Build verifiable program:**
      ```bash
      solana-verify build --library-name my_program
      ```
      
      **2. Deploy with your wallet as initial authority:**
      ```bash
      solana program deploy target/deploy/my_program.so \
        --program-id <PROGRAM_ID> \
        -u mainnet-beta \
        --with-compute-unit-price 50000
      ```
      
      **3. Verify locally:**
      ```bash
      solana-verify verify-from-repo \
        -u mainnet-beta \
        --program-id <PROGRAM_ID> \
        https://github.com/your-org/repo \
        --commit-hash <COMMIT>
      ```
      
      **4. Transfer authority to Squads multisig:**
      
      Get Squads vault address from https://v4.squads.so/
      
      ```bash
      SQUADS_VAULT="YourSquadsVaultAddress"
      
      solana program set-upgrade-authority <PROGRAM_ID> \
        --new-upgrade-authority $SQUADS_VAULT
      ```
      
      **5. Export verification PDA transaction:**
      
      ```bash
      solana-verify verify-from-repo \
        -u mainnet-beta \
        --program-id <PROGRAM_ID> \
        https://github.com/your-org/repo \
        --export-pda-tx verification_tx.json
      ```
      
      **6. Submit transaction in Squads:**
      
      - Go to https://v4.squads.so/
      - Navigate to your multisig
      - Create new transaction
      - Import `verification_tx.json`
      - Get approval from multisig members
      - Execute transaction
      
      **7. Submit remote verification:**
      
      After Squads transaction executes:
      
      ```bash
      solana-verify remote submit-job \
        --program-id <PROGRAM_ID> \
        --uploader <SQUADS_VAULT>
      ```
      
      **8. Monitor verification:**
      
      ```bash
      # Check job status
      solana-verify remote get-job-status --job-id <JOB_ID>
      
      # Or visit
      https://verify.osec.io/status/<PROGRAM_ID>
      ```
      
      ### Upgrading via Multisig
      
      **Create upgrade buffer:**
      ```bash
      # Build new version
      solana-verify build --library-name my_program
      
      # Write to buffer (not direct upgrade)
      solana program write-buffer target/deploy/my_program.so
      
      # Output: Buffer address: <BUFFER_ADDRESS>
      ```
      
      **Transfer buffer authority to multisig:**
      ```bash
      solana program set-buffer-authority <BUFFER_ADDRESS> \
        --new-buffer-authority <SQUADS_VAULT>
      ```
      
      **Create Squads transaction for upgrade:**
      
      Use Squads CLI or UI to propose:
      ```bash
      # Using Squads SDK/CLI
      npx ts-node scripts/program-upgrade.ts \
        --rpc "https://api.mainnet-beta.solana.com" \
        --program "<PROGRAM_ID>" \
        --buffer "<BUFFER_ADDRESS>" \
        --multisig "<MULTISIG_ADDRESS>" \
        --member "<YOUR_PUBKEY>" \
        --name "Upgrade my_program v2"
      ```
      
      **Close buffer via Squads (if needed):**
      ```bash
      npx ts-node scripts/squad-closebuffer.ts \
        --rpc "https://api.mainnet-beta.solana.com" \
        --multisig "<MULTISIG_ADDRESS>" \
        --buffer "<BUFFER_ADDRESS>" \
        --program "<PROGRAM_ID>"
      ```
      
      ---
      
      ## Network-Specific Considerations
      
      ### Localhost Development
      
      **Start test validator:**
      ```bash
      # Basic
      solana-test-validator
      
      # With program pre-deployed
      solana-test-validator --bpf-program <PROGRAM_ID> target/deploy/my_program.so
      
      # With cloned mainnet accounts
      solana-test-validator \
        --clone <ACCOUNT_ADDRESS> \
        --url mainnet-beta
      
      # Reset ledger on restart
      solana-test-validator --reset
      ```
      
      **Deploy to local validator:**
      ```bash
      # Anchor
      anchor localnet  # Starts validator and deploys
      # Or
      anchor deploy --provider.cluster localnet
      
      # Native Rust
      solana program deploy target/deploy/my_program.so -ul
      ```
      
      **Benefits:**
      - Instant transaction confirmation
      - Unlimited free SOL
      - Full control over clock and state
      - Fast iteration
      
      **Limitations:**
      - No network effects
      - Single validator (no consensus)
      - State doesn't persist (unless configured)
      
      ### Devnet Deployment
      
      **Configure network:**
      ```bash
      solana config set --url devnet
      ```
      
      **Fund wallet:**
      ```bash
      solana airdrop 2
      # Repeat as needed, max 2 SOL per request
      ```
      
      **Deploy:**
      ```bash
      # Anchor
      anchor deploy --provider.cluster devnet
      
      # Native Rust
      solana program deploy target/deploy/my_program.so \
        -u devnet \
        --with-compute-unit-price 1000
      ```
      
      **Benefits:**
      - Real network conditions
      - Free SOL via airdrops
      - Test integrations with other programs
      - Longer-lived state than localnet
      
      **Limitations:**
      - Network resets occasionally
      - Potential rate limiting
      - Slower than localnet
      - Public network (anyone can interact)
      
      **Best practices:**
      - Test all upgrade paths on devnet first
      - Monitor transaction success rates
      - Test with realistic compute budgets
      - Validate against cloned mainnet accounts
      
      ### Testnet Deployment
      
      Testnet is less commonly used but available for staging:
      
      ```bash
      solana config set --url testnet
      solana airdrop 2  # If available
      ```
      
      **Use cases:**
      - Staging environment before mainnet
      - Testing between devnet and mainnet
      - Longer-term testing without mainnet costs
      
      ### Mainnet Deployment
      
      **Pre-deployment checklist:**
      - [ ] Thoroughly tested on devnet
      - [ ] Security audit completed (for DeFi/high-value programs)
      - [ ] Verified build prepared
      - [ ] Upgrade authority configured (multisig recommended)
      - [ ] Sufficient SOL for deployment (check `solana rent`)
      - [ ] Monitoring/alerting setup
      - [ ] Rollback plan documented
      - [ ] Team coordination for deployment time
      
      **Configure mainnet:**
      ```bash
      solana config set --url mainnet-beta
      
      # Use paid RPC for reliability (recommended)
      solana config set --url https://your-rpc-provider.com
      ```
      
      **Fund wallet:**
      ```bash
      # Transfer from exchange or another wallet
      # Calculate needed SOL:
      PROGRAM_SIZE=$(wc -c < target/deploy/my_program.so)
      solana rent $PROGRAM_SIZE
      # Add ~0.5 SOL buffer for transaction fees and priority fees
      ```
      
      **Deploy with appropriate priority fees:**
      
      Check current priority fee recommendations:
      - https://www.quicknode.com/gas-tracker/solana
      - https://solanacompass.com/gas-fees
      
      ```bash
      # Typical priority fee: 50,000-300,000 micro-lamports per CU
      solana program deploy target/deploy/my_program.so \
        --program-id <PROGRAM_ID> \
        -u mainnet-beta \
        --with-compute-unit-price 100000 \
        --max-sign-attempts 100 \
        --use-rpc
      ```
      
      **Post-deployment verification:**
      ```bash
      # Verify deployment
      solana program show <PROGRAM_ID> -u mainnet-beta
      
      # Verify hash matches
      solana-verify get-program-hash -u mainnet-beta <PROGRAM_ID>
      
      # Submit verification job
      solana-verify verify-from-repo \
        --remote \
        -u mainnet-beta \
        --program-id <PROGRAM_ID> \
        https://github.com/your-org/repo
      ```
      
      **Cost considerations:**
      - 200KB program: ~1.5 SOL
      - 500KB program: ~3.5 SOL
      - 800KB program: ~5.5 SOL
      - Plus transaction fees: ~0.01-0.05 SOL
      - Priority fees during congestion: +0.1-1 SOL
      
      ---
      
      ## Post-Deployment
      
      ### Verifying Deployment Success
      
      **Check program was deployed:**
      ```bash
      solana program show <PROGRAM_ID>
      
      # Verify output shows:
      # - Correct ProgramData address
      # - Your authority
      # - Expected data length
      # - Recent slot number
      ```
      
      **Compare program hash:**
      ```bash
      # On-chain hash
      solana-verify get-program-hash -u <NETWORK> <PROGRAM_ID>
      
      # Local build hash
      solana-verify get-executable-hash target/deploy/my_program.so
      
      # Must match exactly
      ```
      
      ### Testing On-Chain
      
      **Anchor:**
      ```bash
      # Run tests against deployed program
      anchor test --skip-deploy --provider.cluster devnet
      
      # Or run specific test
      anchor run test-on-devnet
      ```
      
      **Native Rust / TypeScript client (Anchor - requires legacy `@solana/web3.js` v1):**
      ```typescript
      import { Connection, PublicKey } from '@solana/web3.js';
      
      const connection = new Connection('https://api.devnet.solana.com');
      const programId = new PublicKey('YourProgramId');
      
      // Send test transaction
      const tx = await program.methods
        .yourInstruction()
        .accounts({ /* ... */ })
        .rpc();
      
      console.log('Transaction:', tx);
      ```
      
      **Smoke tests:**
      - Call each instruction with valid inputs
      - Verify account state changes
      - Check events are emitted correctly
      - Test error cases return expected errors
      
      ### Monitoring Program Usage
      
      **View recent transactions:**
      ```bash
      # Get recent transactions for program
      solana transaction-history <PROGRAM_ID> --limit 10
      
      # Or use explorers:
      # https://explorer.solana.com/address/<PROGRAM_ID>
      # https://solscan.io/account/<PROGRAM_ID>
      ```
      
      **Set up monitoring:**
      
      Use services like:
      - [Helius webhooks](https://www.helius.dev/)
      - [QuickNode functions](https://www.quicknode.com/)
      - [SolanaFM API](https://solana.fm/)
      
      **Monitor for:**
      - Transaction success rate
      - Compute unit usage
      - Error frequency
      - Unusual activity patterns
      
      ### Publishing IDL (Anchor)
      
      **Upload IDL to on-chain account:**
      ```bash
      anchor idl init <PROGRAM_ID> \
        --filepath target/idl/my_program.json \
        --provider.cluster devnet
      ```
      
      **Upgrade IDL after program upgrade:**
      ```bash
      anchor idl upgrade <PROGRAM_ID> \
        --filepath target/idl/my_program.json \
        --provider.cluster devnet
      ```
      
      **Fetch published IDL:**
      ```bash
      anchor idl fetch <PROGRAM_ID> \
        --provider.cluster devnet \
        --out fetched_idl.json
      ```
      
      **Benefits of publishing IDL:**
      - Clients can auto-discover your interface
      - Explorers can decode instructions
      - Reduces integration friction
      - Standard for Anchor programs
      
      ### Closing Programs and Reclaiming SOL
      
      **Close buffer accounts:**
      ```bash
      # View all buffers
      solana program show --buffers
      
      # Close specific buffer
      solana program close <BUFFER_ADDRESS>
      
      # Reclaims rent SOL to wallet
      ```
      
      **Close entire program (irreversible!):**
      ```bash
      solana program close <PROGRAM_ID>
      
      # WARNING: This deletes the program permanently
      # Cannot be undone
      # Only do this for test programs
      ```
      
      **When to close:**
      - Failed test deployments on devnet
      - Obsolete test programs
      - Reclaim SOL from old projects
      
      **When NOT to close:**
      - Any program with active users
      - Programs other contracts depend on
      - Mainnet programs (almost never)
      
      ---
      
      ## Common Issues and Troubleshooting
      
      ### Insufficient Balance
      
      **Error:**
      ```
      Error: Account <WALLET> has insufficient funds for spend (1.5 SOL) + fee (0.002 SOL)
      ```
      
      **Solution:**
      ```bash
      # Check current balance
      solana balance
      
      # Devnet - request airdrop
      solana airdrop 2
      
      # Mainnet - transfer SOL
      # Calculate needed amount:
      PROGRAM_SIZE=$(wc -c < target/deploy/my_program.so)
      solana rent $PROGRAM_SIZE
      # Add 0.5 SOL buffer
      ```
      
      ### Program Too Large
      
      **Error:**
      ```
      Error: Program too large. Maximum size: 1048576 bytes
      ```
      
      **Solutions:**
      
      **1. Optimize build:**
      ```toml
      [profile.release]
      lto = "fat"
      codegen-units = 1
      opt-level = "z"  # Optimize for size
      strip = true
      ```
      
      **2. Remove unused dependencies:**
      ```bash
      cargo tree  # Identify large dependencies
      ```
      
      **3. Use feature flags to exclude optional code:**
      ```toml
      [dependencies]
      solana-program = { version = "2.1.0", default-features = false }
      ```
      
      **4. Split program into multiple programs:**
      - Separate complex logic into multiple programs
      - Use CPI to communicate between them
      
      ### Account Data Too Small for Instruction
      
      **Error:**
      ```
      Error: account data too small for instruction
      ```
      
      **Cause:** Upgrade would exceed allocated program size.
      
      **Solution:**
      ```bash
      # Check current size
      solana program show <PROGRAM_ID>
      # Data Length: 363960 bytes
      
      # Check new size
      NEW_SIZE=$(wc -c < target/deploy/my_program.so)
      echo $NEW_SIZE
      # 380000 bytes
      
      # Calculate difference
      DIFF=$((NEW_SIZE - 363960))
      
      # Extend program
      solana program extend <PROGRAM_ID> $DIFF
      
      # Check rent for extension
      solana rent $DIFF
      
      # Now deploy upgrade
      solana program deploy target/deploy/my_program.so --program-id <PROGRAM_ID>
      ```
      
      ### Network Congestion / Blockhash Expiration
      
      **Error:**
      ```
      Error: Transaction simulation failed: Blockhash not found
      ```
      
      **Cause:** High network congestion or large program deployment.
      
      **Solutions:**
      
      **1. Increase priority fees:**
      ```bash
      solana program deploy target/deploy/my_program.so \
        --with-compute-unit-price 300000 \  # Higher priority
        --max-sign-attempts 100 \
        --use-rpc
      ```
      
      **2. Use paid RPC endpoint:**
      ```bash
      solana config set --url https://your-premium-rpc.com
      
      # Paid RPCs often have:
      # - Higher rate limits
      # - Better transaction success rates
      # - Priority queue access
      ```
      
      **3. Deploy during low traffic:**
      - Avoid peak hours (US/Europe daytime)
      - Early morning UTC often less congested
      
      **4. Break into smaller chunks:**
      
      For very large programs, manually create buffer and write in batches.
      
      ### Keypair Issues
      
      **Error:**
      ```
      Error: Dynamic program error: Invalid keypair file
      ```
      
      **Solutions:**
      
      **1. Verify keypair format:**
      ```bash
      # Should be JSON array of numbers
      cat program-keypair.json
      # [123, 45, 67, ...]
      
      # Or base58 string
      ```
      
      **2. Regenerate if corrupted:**
      ```bash
      solana-keygen new -o program-keypair.json --force
      ```
      
      **3. Check file permissions:**
      ```bash
      chmod 600 program-keypair.json
      ```
      
      ### Anchor Build vs Deploy Mismatch
      
      **Error:**
      ```
      Error: Program <ID> does not match declared program id in lib.rs
      ```
      
      **Solution:**
      ```bash
      # Sync program IDs
      anchor keys sync
      
      # Rebuilds and updates declare_id! to match keypair
      ```
      
      ### Native Rust: Missing Program ID
      
      **Error:**
      ```
      Error: No program keypair found
      ```
      
      **Solution:**
      
      Native Rust doesn't auto-generate keypairs. Either:
      
      **Option 1: Create keypair:**
      ```bash
      solana-keygen new -o target/deploy/my_program-keypair.json
      solana program deploy target/deploy/my_program.so \
        --program-id target/deploy/my_program-keypair.json
      ```
      
      **Option 2: Use existing program ID:**
      ```bash
      solana program deploy target/deploy/my_program.so \
        --program-id <EXISTING_PROGRAM_ID>
      ```
      
      ### Verification Failures
      
      **Error:**
      ```
      Verification failed: Hash mismatch
      On-chain: abc123...
      Local: def456...
      ```
      
      **Causes and solutions:**
      
      **1. Built outside Docker:**
      ```bash
      # Must use solana-verify build for deterministic build
      solana-verify build
      ```
      
      **2. Cargo.lock mismatch:**
      ```bash
      # Ensure Cargo.lock committed to git
      git add Cargo.lock
      git commit -m "Add Cargo.lock for verifiable builds"
      ```
      
      **3. Rebuild after verification:**
      ```bash
      # Don't run `anchor build` or `cargo build-sbf` after solana-verify build
      # This regenerates binary with different hash
      
      # If you did, rebuild verifiably:
      solana-verify build
      solana program deploy target/deploy/my_program.so --program-id <PROGRAM_ID>
      ```
      
      **4. Wrong commit hash:**
      ```bash
      # Ensure you're verifying against correct commit
      git log  # Find exact commit used for deployment
      solana-verify verify-from-repo \
        --commit-hash <EXACT_COMMIT> \
        ...
      ```
      
      ---
      
      ## Best Practices
      
      ### Deployment Workflow
      
      **Recommended deployment process:**
      
      1. **Local development**
         - Develop on localnet
         - Unit test with Mollusk (native) or Bankrun/Anchor tests
         - Iterate quickly
      
      2. **Devnet testing**
         - Deploy to devnet
         - Integration testing
         - Test upgrade paths
         - Load testing if applicable
      
      3. **Devnet verification**
         - Build verifiable
         - Deploy and verify on devnet
         - Ensure verification succeeds
      
      4. **Security review**
         - Internal code review
         - Automated analysis (Soteria, Sec3, etc.)
         - Professional audit (for mainnet)
      
      5. **Mainnet staging**
         - Build final verifiable version
         - Generate program ID
         - Set up multisig/governance
      
      6. **Mainnet deployment**
         - Deploy during low-traffic period
         - Use paid RPC
         - Set appropriate priority fees
         - Monitor closely
      
      7. **Post-deployment**
         - Submit verification
         - Smoke test critical functions
         - Set up monitoring
         - Transfer authority to multisig
      
      ### Version Control
      
      **Always commit:**
      - `Cargo.lock` (required for verified builds)
      - Program keypairs (for test programs only)
      - IDL files
      - Migration scripts
      
      **Never commit:**
      - Mainnet keypairs (use environment variables)
      - Wallet private keys
      - RPC API keys
      
      **Tag deployments:**
      ```bash
      git tag -a v1.0.0-mainnet -m "Mainnet deployment v1.0.0"
      git push origin v1.0.0-mainnet
      ```
      
      **Link verification to tags:**
      ```bash
      solana-verify verify-from-repo \
        --commit-hash v1.0.0-mainnet \
        ...
      ```
      
      ### Backup Strategies
      
      **Critical to back up:**
      
      1. **Program keypairs**
         ```bash
         # Mainnet program keypairs
         cp target/deploy/my_program-keypair.json ~/secure-backup/
      
         # Encrypted backup
         gpg -c target/deploy/my_program-keypair.json
         ```
      
      2. **Upgrade authority keypairs**
         ```bash
         # If not using multisig
         cp ~/.config/solana/id.json ~/secure-backup/upgrade-authority.json
         ```
      
      3. **Buffer accounts during deployment**
         ```bash
         # Save buffer address immediately after creating
         echo "Buffer: <ADDRESS>" >> deployment-log.txt
         ```
      
      4. **Deployment artifacts**
         - Built .so files
         - IDL files
         - Verification data
         - Transaction signatures
      
      **Backup locations:**
      - Encrypted cloud storage (Google Drive, Dropbox)
      - Hardware wallet (for keypairs)
      - Offline USB drives (encrypted)
      - Team password manager (1Password, Bitwarden)
      
      **Test backup restoration:**
      ```bash
      # Periodically verify backups work
      cp ~/secure-backup/my_program-keypair.json /tmp/test-restore.json
      solana-keygen pubkey /tmp/test-restore.json
      # Should output expected program ID
      ```
      
      ### Framework-Specific Best Practices
      
      **Anchor:**
      - Always run `anchor keys sync` after first build
      - Keep `Anchor.toml` in source control
      - Use workspace for multi-program projects
      - Upload IDL on-chain for discoverability
      - Version your IDL files alongside code
      
      **Native Rust:**
      - Use `no-entrypoint` feature for testing
      - Implement security.txt for contact info
      - Document your instruction format
      - Provide client SDK (TypeScript/Rust) for integrations
      - Include example transaction builders
      
      ### Testing Before Deployment
      
      **Progressive testing strategy:**
      
      ```bash
      # 1. Unit tests (Mollusk for native, Anchor tests)
      cargo test
      
      # 2. Integration tests on localnet
      anchor test  # or cargo test-sbf
      
      # 3. Devnet deployment test
      anchor deploy --provider.cluster devnet
      # Test all functions on devnet
      
      # 4. Upgrade test on devnet
      # Make small change, rebuild, upgrade
      anchor build
      anchor upgrade <PROGRAM_ID> target/deploy/program.so --provider.cluster devnet
      # Verify upgrade worked
      
      # 5. Verified build test
      solana-verify build
      solana-verify verify-from-repo --program-id <DEVNET_ID> https://github.com/...
      
      # 6. Final smoke tests on devnet
      # Run critical user flows
      
      # 7. Mainnet deployment
      # Only after all above pass
      ```
      
      ### Documentation
      
      **Document your deployment:**
      
      Create `DEPLOYMENT.md` in your repo:
      
      ```markdown
      # Deployment Guide
      
      ## Program IDs
      
      - Devnet: ABC123...
      - Mainnet: DEF456...
      
      ## Build
      
      ```bash
      solana-verify build --library-name my_program
      ```
      
      ## Deploy
      
      ### Devnet
      ```bash
      anchor deploy --provider.cluster devnet
      ```
      
      ### Mainnet
      ```bash
      solana program deploy target/deploy/my_program.so \
        --program-id <PROGRAM_ID> \
        -u mainnet-beta \
        --with-compute-unit-price 100000
      ```
      
      ## Verify
      
      ```bash
      solana-verify verify-from-repo \
        --remote \
        -u mainnet-beta \
        --program-id <PROGRAM_ID> \
        https://github.com/your-org/repo \
        --commit-hash v1.0.0
      ```
      
      ## Upgrade Authority
      
      Mainnet: Squads Multisig `GHI789...`
      
      ## Last Deployment
      
      - Date: 2025-01-15
      - Version: v1.0.0
      - Commit: abc123def456
      - Deployed by: @deployer
      - Verification: https://verify.osec.io/status/<PROGRAM_ID>
      ```
      
      ---
      
      ## Summary
      
      **Key takeaways:**
      
      1. **Always test on devnet first** - Never deploy untested code to mainnet
      2. **Use verified builds for mainnet** - Transparency builds trust
      3. **Calculate costs before deploying** - Use `solana rent` to estimate
      4. **Set up multisig for mainnet** - Prevents single points of failure
      5. **Monitor after deployment** - Watch for errors and unusual activity
      6. **Back up keypairs** - Lose keypair = lose upgrade authority
      7. **Document your deployments** - Future you will thank you
      8. **Use priority fees on mainnet** - Ensures reliable deployment
      9. **Test upgrade paths** - Practice on devnet first
      10. **Never make programs immutable hastily** - Irreversible decision
      
      **Resources:**
      - Solana CLI docs: https://docs.solana.com/cli
      - Anchor docs: https://www.anchor-lang.com/
      - Solana Verify: https://github.com/Ellipsis-Labs/solana-verifiable-build
      - OtterSec Verify API: https://verify.osec.io/
      - Squads Protocol: https://squads.so/
      - Security.txt: https://github.com/neodyme-labs/solana-security-txt
      
      Deploy with confidence!
      
    • durable-nonces.md 26.4 KB
      # Durable Transaction Nonces
      
      This guide covers Solana's durable transaction nonces, which enable transactions to remain valid indefinitely by replacing the time-limited recent blockhash mechanism. Essential for offline signing, multi-signature coordination, and scheduled transaction execution.
      
      ## Introduction
      
      ### The Expiration Problem
      
      Solana transactions normally include a `recent_blockhash` field that serves two purposes:
      1. **Double-spend prevention**: Ensures each transaction is unique and can only be processed once
      2. **Transaction freshness**: Limits transaction validity to prevent spam and stale transactions
      
      **The limitation:**
      - Recent blockhashes expire after **150 blocks** (~60-90 seconds)
      - Transactions with expired blockhashes are permanently rejected
      - Cannot be re-validated, even with identical content
      
      **Critical constraint:**
      ```
      Transaction must be:
      1. Signed with recent blockhash
      2. Submitted to network
      3. Processed by validator
      4. Confirmed in block
      
      All within ~60-90 seconds!
      ```
      
      ### Problems This Creates
      
      **Hardware Wallet Users:**
      - Fetch blockhash from network
      - Transfer to air-gapped device
      - User reviews and signs (can take minutes)
      - Transfer back to online device
      - Submit to network
      - **Risk**: Blockhash expires during manual review
      
      **Multi-Signature Wallets (DAOs, Squads, Realms):**
      - Create transaction with blockhash
      - Send to signer 1 for approval (hours/days)
      - Send to signer 2 for approval (hours/days)
      - Send to signer N for approval
      - **Risk**: Blockhash expires while collecting signatures
      
      **Scheduled Transactions:**
      - Want to pre-sign transaction for future execution
      - E.g., vesting unlock, scheduled payment, conditional trade
      - **Risk**: Cannot pre-sign hours/days in advance
      
      **Cross-Chain Bridges:**
      - Wait for finality on source chain (minutes/hours)
      - Sign transaction on destination chain
      - **Risk**: Blockhash expires during cross-chain confirmation
      
      ### The Solution: Durable Nonces
      
      Durable nonces replace `recent_blockhash` with a **stored on-chain value** that:
      - ✅ Never expires (remains valid indefinitely)
      - ✅ Changes with each use (prevents replay attacks)
      - ✅ Enables offline signing without time pressure
      - ✅ Supports multi-signature coordination
      - ✅ Allows pre-signing transactions for future execution
      
      **Key insight**: Instead of using the blockchain's recent history (blockhashes) to ensure uniqueness, durable nonces use a **dedicated account** that stores a nonce value and advances it after each transaction.
      
      ## How Durable Nonces Work
      
      ### Core Mechanism
      
      1. **Nonce Account**: On-chain account (owned by System Program) storing a 32-byte nonce value
      2. **Transaction Structure**: Use nonce value as `recent_blockhash` field
      3. **Nonce Advancement**: First instruction MUST advance the nonce to prevent replay
      4. **Authority Control**: Only nonce authority can advance nonce or authorize transactions
      
      ### Transaction Flow
      
      ```
      Normal Transaction:
      1. Fetch recent blockhash (expires in 90s)
      2. Build transaction with blockhash
      3. Sign transaction
      4. Submit (must be within 90s)
      5. Process and confirm
      
      Durable Nonce Transaction:
      1. Create nonce account (one-time setup)
      2. Fetch current nonce value (no expiration!)
      3. Build transaction with nonce as blockhash
      4. Add advance_nonce instruction (MUST be first)
      5. Sign transaction (no time pressure)
      6. Submit anytime (minutes, hours, days later)
      7. Process: advances nonce, executes instructions
      ```
      
      ### Double-Spend Prevention
      
      **Without expiration, how does it prevent double-spending?**
      
      The nonce value **changes** after each transaction:
      ```rust
      // Transaction 1 with nonce value "ABC123..."
      {
          recent_blockhash: "ABC123...",  // Current nonce value
          instructions: [
              advance_nonce_account(...),  // Changes nonce to "XYZ789..."
              transfer(...),
          ]
      }
      
      // If you try to submit Transaction 1 again:
      // Runtime checks: Is "ABC123..." the current nonce?
      // NO! It's now "XYZ789..."
      // Transaction REJECTED (nonce mismatch)
      ```
      
      **Critical**: The runtime **always** advances the nonce, even if the transaction fails after the advance instruction. This prevents replay attacks.
      
      ## Nonce Account Structure
      
      ### Account Layout
      
      Nonce accounts are owned by the System Program and have this structure:
      
      ```rust
      pub struct NonceState {
          pub version: NonceVersion,
      }
      
      pub enum NonceVersion {
          Legacy(Box<NonceData>),
          Current(Box<NonceData>),
      }
      
      pub struct NonceData {
          pub authority: Pubkey,         // Who can authorize nonce operations
          pub durable_nonce: Hash,       // The actual nonce value (32 bytes)
          pub fee_calculator: FeeCalculator,  // Historic fee data
      }
      ```
      
      **Account requirements:**
      - **Owner**: System Program (`11111111111111111111111111111111`)
      - **Size**: 80 bytes
      - **Rent exemption**: Required (minimum ~0.00144768 SOL)
      - **Authority**: Can be any pubkey (keypair or PDA)
      
      ### Nonce Authority
      
      The authority pubkey controls the nonce account:
      - **Can**: Advance nonce, withdraw funds, authorize nonce transactions, change authority
      - **Cannot**: Execute other instructions without nonce advancement (runtime enforces this)
      
      **Authority options:**
      - **Keypair**: Direct control (hot wallet, cold wallet)
      - **PDA**: Program-controlled nonces (advanced use case)
      - **Multisig**: Multiple signers required (DAO wallets)
      
      ## Creating Nonce Accounts
      
      ### Using Native Rust
      
      ```rust
      use solana_sdk::{
          instruction::Instruction,
          pubkey::Pubkey,
          signature::{Keypair, Signer},
          system_instruction,
          sysvar::rent::Rent,
          transaction::Transaction,
      };
      use solana_client::rpc_client::RpcClient;
      
      fn create_nonce_account(
          rpc_client: &RpcClient,
          payer: &Keypair,
          nonce_account: &Keypair,
          authority: &Pubkey,
      ) -> Result<(), Box<dyn std::error::Error>> {
          // Calculate rent-exempt balance for nonce account
          let rent = rpc_client.get_minimum_balance_for_rent_exemption(80)?;
      
          // Create account instruction
          let create_account_ix = system_instruction::create_account(
              &payer.pubkey(),
              &nonce_account.pubkey(),
              rent,                     // Lamports (rent-exempt minimum)
              80,                       // Space (nonce account size)
              &solana_program::system_program::id(),  // Owner (System Program)
          );
      
          // Initialize nonce instruction
          let initialize_nonce_ix = system_instruction::initialize_nonce_account(
              &nonce_account.pubkey(),
              authority,                // Nonce authority
          );
      
          // Build transaction
          let recent_blockhash = rpc_client.get_latest_blockhash()?;
          let transaction = Transaction::new_signed_with_payer(
              &[create_account_ix, initialize_nonce_ix],
              Some(&payer.pubkey()),
              &[payer, nonce_account],  // Both payer and nonce account must sign
              recent_blockhash,
          );
      
          // Send transaction
          let signature = rpc_client.send_and_confirm_transaction(&transaction)?;
          println!("Created nonce account: {}", signature);
      
          Ok(())
      }
      ```
      
      ### Single-Step Creation
      
      There's also a convenience function that combines both steps:
      
      ```rust
      let instruction = system_instruction::create_nonce_account(
          &payer.pubkey(),
          &nonce_account.pubkey(),
          authority,
          rent_lamports,
      );
      
      // This creates a single instruction that:
      // 1. Creates the account
      // 2. Initializes it as a nonce account
      ```
      
      ### Using CLI
      
      ```bash
      # Generate keypair for nonce account
      solana-keygen new -o nonce-account.json
      
      # Create nonce account
      solana create-nonce-account nonce-account.json 0.0015
      
      # Verify creation
      solana nonce nonce-account.json
      # Output: Current nonce value (32-byte hash)
      ```
      
      ## Querying Nonce Accounts
      
      ### Fetching Nonce Value
      
      ```rust
      use solana_sdk::account::Account;
      use solana_program::system_program;
      
      fn get_nonce_value(
          rpc_client: &RpcClient,
          nonce_pubkey: &Pubkey,
      ) -> Result<Hash, Box<dyn std::error::Error>> {
          // Fetch account data
          let account = rpc_client.get_account(nonce_pubkey)?;
      
          // Verify it's a nonce account
          if account.owner != system_program::id() {
              return Err("Account is not owned by System Program".into());
          }
      
          // Deserialize nonce data
          let nonce_data = bincode::deserialize::<NonceState>(&account.data)?;
      
          match nonce_data {
              NonceState::Current(data) => Ok(data.durable_nonce),
              NonceState::Legacy(data) => Ok(data.durable_nonce),
          }
      }
      ```
      
      ### Parsing Nonce Account
      
      ```rust
      use solana_program::nonce::state::{Data, State};
      
      fn parse_nonce_account(account_data: &[u8]) -> Result<Data, Box<dyn std::error::Error>> {
          let state: State = bincode::deserialize(account_data)?;
      
          match state {
              State::Initialized(data) => Ok(data),
              State::Uninitialized => Err("Nonce account not initialized".into()),
          }
      }
      
      // Access nonce components
      fn display_nonce_info(nonce_data: &Data) {
          println!("Authority: {}", nonce_data.authority);
          println!("Nonce value: {}", nonce_data.blockhash);
          println!("Fee calculator: {:?}", nonce_data.fee_calculator);
      }
      ```
      
      ## Building Transactions with Durable Nonces
      
      ### Transaction Structure
      
      **Critical requirements:**
      1. **First instruction** MUST be `advance_nonce_account`
      2. Use nonce value as `recent_blockhash`
      3. Sign with nonce authority (in addition to other required signers)
      
      ```rust
      use solana_sdk::{
          hash::Hash,
          instruction::Instruction,
          message::Message,
          signature::{Keypair, Signer},
          system_instruction,
          transaction::Transaction,
      };
      
      fn build_nonce_transaction(
          nonce_pubkey: &Pubkey,
          nonce_authority: &Keypair,
          nonce_value: Hash,
          instructions: Vec<Instruction>,
          payer: &Keypair,
      ) -> Transaction {
          // 1. Create advance_nonce instruction (MUST BE FIRST)
          let advance_nonce_ix = system_instruction::advance_nonce_account(
              nonce_pubkey,
              &nonce_authority.pubkey(),
          );
      
          // 2. Combine with your instructions
          let mut all_instructions = vec![advance_nonce_ix];
          all_instructions.extend(instructions);
      
          // 3. Build message with nonce as blockhash
          let message = Message::new_with_blockhash(
              &all_instructions,
              Some(&payer.pubkey()),
              &nonce_value,  // Use nonce value instead of recent blockhash!
          );
      
          // 4. Sign with both payer and nonce authority
          let mut signers = vec![payer];
          if nonce_authority.pubkey() != payer.pubkey() {
              signers.push(nonce_authority);
          }
      
          Transaction::new(&signers, message, nonce_value)
      }
      ```
      
      ### Complete Example: Transfer with Durable Nonce
      
      ```rust
      fn transfer_with_nonce(
          rpc_client: &RpcClient,
          nonce_account: &Pubkey,
          nonce_authority: &Keypair,
          payer: &Keypair,
          recipient: &Pubkey,
          amount: u64,
      ) -> Result<(), Box<dyn std::error::Error>> {
          // 1. Fetch current nonce value
          let nonce_value = get_nonce_value(rpc_client, nonce_account)?;
      
          // 2. Create transfer instruction
          let transfer_ix = system_instruction::transfer(
              &payer.pubkey(),
              recipient,
              amount,
          );
      
          // 3. Build transaction with nonce
          let transaction = build_nonce_transaction(
              nonce_account,
              nonce_authority,
              nonce_value,
              vec![transfer_ix],
              payer,
          );
      
          // 4. Can now submit immediately or store for later
          // No expiration pressure!
          let signature = rpc_client.send_and_confirm_transaction(&transaction)?;
          println!("Transfer completed: {}", signature);
      
          Ok(())
      }
      ```
      
      ### Serializing for Offline Signing
      
      ```rust
      use base58::ToBase58;
      
      fn serialize_for_offline_signing(transaction: &Transaction) -> String {
          // Serialize transaction to bytes
          let serialized = bincode::serialize(transaction).unwrap();
      
          // Encode as base58 for transport
          serialized.to_base58()
      }
      
      fn deserialize_signed_transaction(base58_tx: &str) -> Transaction {
          use base58::FromBase58;
      
          let bytes = base58_tx.from_base58().unwrap();
          bincode::deserialize(&bytes).unwrap()
      }
      ```
      
      ## Managing Nonce Accounts
      
      ### Advancing Nonce
      
      **Automatic advancement**: When you submit a transaction with a durable nonce, the runtime automatically advances the nonce as part of processing the `advance_nonce_account` instruction.
      
      **Manual advancement** (without submitting transaction):
      
      ```rust
      fn advance_nonce_manually(
          rpc_client: &RpcClient,
          nonce_account: &Pubkey,
          nonce_authority: &Keypair,
          payer: &Keypair,
      ) -> Result<(), Box<dyn std::error::Error>> {
          let advance_ix = system_instruction::advance_nonce_account(
              nonce_account,
              &nonce_authority.pubkey(),
          );
      
          let recent_blockhash = rpc_client.get_latest_blockhash()?;
          let transaction = Transaction::new_signed_with_payer(
              &[advance_ix],
              Some(&payer.pubkey()),
              &[payer, nonce_authority],
              recent_blockhash,
          );
      
          rpc_client.send_and_confirm_transaction(&transaction)?;
          Ok(())
      }
      ```
      
      **When to manually advance:**
      - Before reusing nonce for a new transaction
      - To invalidate a previously signed transaction
      - Regular rotation for security
      
      ### Withdrawing from Nonce Account
      
      ```rust
      fn withdraw_from_nonce(
          rpc_client: &RpcClient,
          nonce_account: &Pubkey,
          nonce_authority: &Keypair,
          recipient: &Pubkey,
          amount: u64,
          payer: &Keypair,
      ) -> Result<(), Box<dyn std::error::Error>> {
          let withdraw_ix = system_instruction::withdraw_nonce_account(
              nonce_account,
              &nonce_authority.pubkey(),
              recipient,
              amount,
          );
      
          let recent_blockhash = rpc_client.get_latest_blockhash()?;
          let transaction = Transaction::new_signed_with_payer(
              &[withdraw_ix],
              Some(&payer.pubkey()),
              &[payer, nonce_authority],
              recent_blockhash,
          );
      
          rpc_client.send_and_confirm_transaction(&transaction)?;
          Ok(())
      }
      ```
      
      **Important**: Must maintain rent-exempt minimum balance. Can only withdraw to zero if closing the account.
      
      ### Changing Nonce Authority
      
      ```rust
      fn change_nonce_authority(
          rpc_client: &RpcClient,
          nonce_account: &Pubkey,
          current_authority: &Keypair,
          new_authority: &Pubkey,
          payer: &Keypair,
      ) -> Result<(), Box<dyn std::error::Error>> {
          let authorize_ix = system_instruction::authorize_nonce_account(
              nonce_account,
              &current_authority.pubkey(),
              new_authority,
          );
      
          let recent_blockhash = rpc_client.get_latest_blockhash()?;
          let transaction = Transaction::new_signed_with_payer(
              &[authorize_ix],
              Some(&payer.pubkey()),
              &[payer, current_authority],
              recent_blockhash,
          );
      
          rpc_client.send_and_confirm_transaction(&transaction)?;
          Ok(())
      }
      ```
      
      **Use cases:**
      - Transfer control to PDA for program-managed nonces
      - Rotate keys for security
      - Transfer to multisig for DAO control
      
      ## Offline Signing Workflows
      
      ### Hardware Wallet Flow
      
      **Setup (online device):**
      ```rust
      // 1. Create nonce account (one-time)
      create_nonce_account(&rpc_client, &payer, &nonce_account, &hw_wallet_pubkey)?;
      
      // 2. Fetch nonce value
      let nonce_value = get_nonce_value(&rpc_client, &nonce_account.pubkey())?;
      
      // 3. Build unsigned transaction
      let unsigned_tx = build_nonce_transaction(
          &nonce_account.pubkey(),
          &hw_wallet_keypair,  // Will be replaced with actual signature
          nonce_value,
          vec![transfer_ix],
          &payer,
      );
      
      // 4. Serialize for hardware wallet
      let serialized = serialize_for_offline_signing(&unsigned_tx);
      
      // 5. Transfer to hardware wallet (USB, QR code, etc.)
      ```
      
      **Signing (air-gapped hardware wallet):**
      ```rust
      // 1. Receive serialized transaction
      let tx = deserialize_signed_transaction(&serialized);
      
      // 2. Display to user for review (no time pressure!)
      // User reviews: recipient, amount, etc.
      
      // 3. Sign with hardware wallet private key
      // (Hardware wallet handles this internally)
      
      // 4. Export signed transaction
      let signed_serialized = serialize_for_offline_signing(&signed_tx);
      
      // 5. Transfer back to online device
      ```
      
      **Submission (online device):**
      ```rust
      // 1. Receive signed transaction
      let signed_tx = deserialize_signed_transaction(&signed_serialized);
      
      // 2. Submit to network (can be hours/days after signing!)
      let signature = rpc_client.send_and_confirm_transaction(&signed_tx)?;
      ```
      
      ### Multi-Signature Coordination
      
      **DAO Proposal Execution Flow:**
      
      ```rust
      // 1. Proposer creates transaction with nonce
      let nonce_value = get_nonce_value(&rpc_client, &dao_nonce_account)?;
      let proposal_tx = build_nonce_transaction(
          &dao_nonce_account,
          &dao_authority,  // PDA controlled by governance
          nonce_value,
          vec![execute_proposal_ix],
          &proposer,
      );
      
      // 2. Serialize and store in DAO state
      let tx_data = bincode::serialize(&proposal_tx)?;
      // Store tx_data in proposal account
      
      // 3. Members vote over time (hours/days)
      // Each vote increments approval count
      
      // 4. When threshold reached, anyone can execute
      let stored_tx: Transaction = bincode::deserialize(&proposal.tx_data)?;
      
      // 5. Submit (nonce ensures it's still valid!)
      rpc_client.send_and_confirm_transaction(&stored_tx)?;
      ```
      
      ### CLI Multi-Sig Example
      
      **First co-signer (offline):**
      ```bash
      solana transfer \
        --from sender.json \
        --sign-only \
        --nonce nonce-account.json \
        --nonce-authority nonce-authority.json \
        --blockhash <NONCE_VALUE> \
        --fee-payer co-sender.json \
        receiver.json 0.1
      
      # Output:
      # Pubkey=Signature
      # 5nZ8nY5...=4SBv7Xp...
      ```
      
      **Second co-signer (online, hours/days later):**
      ```bash
      solana transfer \
        --from sender.json \
        --nonce nonce-account.json \
        --nonce-authority nonce-authority.json \
        --blockhash <NONCE_VALUE> \
        --fee-payer sender.json \
        --signer 5nZ8nY5...=4SBv7Xp... \
        receiver.json 0.1
      ```
      
      ## Security Considerations
      
      ### The Neodyme Vulnerability (2020)
      
      **Historic issue**: Before Solana v1.3, there was a critical vulnerability in how durable nonce transactions were processed:
      
      **The bug:**
      1. Transaction with durable nonce starts processing
      2. Runtime advances nonce (changes state)
      3. Later instruction in transaction fails
      4. Runtime rolls back ALL state changes
      5. **BUG**: Nonce advancement was rolled back too!
      6. Attacker could replay the transaction
      
      **The exploit:**
      ```rust
      // Malicious transaction:
      {
          instructions: [
              advance_nonce(...),        // Advances nonce
              write_arbitrary_data(...), // Attacker's payload
              fail_intentionally(...),   // Forces transaction to fail
          ]
      }
      
      // After rollback:
      // - Nonce reverted to original value
      // - Arbitrary data write WAS NOT rolled back
      // - Can replay transaction infinitely!
      ```
      
      **Impact**: Could write arbitrary data to any account by replaying failed transactions.
      
      **Fix** (Solana v1.3+): Nonce advancement is now **permanent** even on transaction failure. The runtime explicitly handles nonce accounts separately from normal rollback logic.
      
      **Lesson**: This demonstrates why nonce advancement MUST happen regardless of transaction success/failure.
      
      ### Best Practices
      
      **1. Never reuse nonce without advancing**
      
      ```rust
      // BAD: Reusing nonce value
      let nonce = get_nonce_value(&rpc, &nonce_account)?;
      let tx1 = build_nonce_transaction(&nonce_account, &auth, nonce, vec![ix1], &payer);
      let tx2 = build_nonce_transaction(&nonce_account, &auth, nonce, vec![ix2], &payer);
      // If tx1 fails, tx2 might also fail with "nonce mismatch"
      
      // GOOD: Advance between uses
      let nonce1 = get_nonce_value(&rpc, &nonce_account)?;
      let tx1 = build_nonce_transaction(&nonce_account, &auth, nonce1, vec![ix1], &payer);
      rpc.send_and_confirm_transaction(&tx1)?;
      
      // Fetch fresh nonce (it was advanced)
      let nonce2 = get_nonce_value(&rpc, &nonce_account)?;
      let tx2 = build_nonce_transaction(&nonce_account, &auth, nonce2, vec![ix2], &payer);
      ```
      
      **2. Protect nonce authority**
      
      ```rust
      // Use cold storage for nonce authority
      // OR use PDA with program logic to restrict usage
      let authority_pda = Pubkey::find_program_address(
          &[b"nonce_authority", dao.key().as_ref()],
          program_id,
      );
      ```
      
      **3. Maintain rent exemption**
      
      ```rust
      // Check before withdrawal
      let nonce_account = rpc.get_account(&nonce_pubkey)?;
      let rent = rpc.get_minimum_balance_for_rent_exemption(80)?;
      
      if nonce_account.lamports - withdraw_amount < rent {
          return Err("Would violate rent exemption".into());
      }
      ```
      
      **4. Verify nonce advancement in transaction**
      
      ```rust
      // In your program that uses nonce transactions:
      pub fn process_instruction(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          instruction_data: &[u8],
      ) -> ProgramResult {
          // First account should be nonce account
          let nonce_account = &accounts[0];
      
          // Verify it's a valid nonce account
          if nonce_account.owner != &system_program::id() {
              return Err(ProgramError::InvalidAccountData);
          }
      
          // Verify advance_nonce was called
          // (Runtime enforces this, but you can add checks)
      
          Ok(())
      }
      ```
      
      **5. Monitor nonce account balance**
      
      ```rust
      // Periodic check (e.g., daily job)
      fn check_nonce_health(rpc: &RpcClient, nonce: &Pubkey) -> Result<(), String> {
          let account = rpc.get_account(nonce)
              .map_err(|_| "Nonce account not found")?;
      
          let rent = rpc.get_minimum_balance_for_rent_exemption(80)
              .map_err(|_| "Failed to fetch rent")?;
      
          if account.lamports < rent {
              return Err(format!(
                  "Nonce account below rent exemption: {} < {}",
                  account.lamports, rent
              ));
          }
      
          Ok(())
      }
      ```
      
      ## Use Cases
      
      ### 1. Scheduled Payments (Vesting)
      
      ```rust
      // Pre-sign monthly vesting releases
      fn create_vesting_schedule(
          rpc: &RpcClient,
          nonce_account: &Pubkey,
          nonce_authority: &Keypair,
          recipient: &Pubkey,
          amount_per_month: u64,
          months: usize,
      ) -> Result<Vec<Transaction>, Box<dyn std::error::Error>> {
          let mut transactions = Vec::new();
      
          for month in 0..months {
              // Fetch current nonce
              let nonce = get_nonce_value(rpc, nonce_account)?;
      
              // Create transfer
              let transfer_ix = system_instruction::transfer(
                  &nonce_authority.pubkey(),
                  recipient,
                  amount_per_month,
              );
      
              // Build nonce transaction
              let tx = build_nonce_transaction(
                  nonce_account,
                  nonce_authority,
                  nonce,
                  vec![transfer_ix],
                  nonce_authority,
              );
      
              transactions.push(tx);
      
              // Advance nonce for next month's transaction
              advance_nonce_manually(rpc, nonce_account, nonce_authority, nonce_authority)?;
          }
      
          Ok(transactions)
      }
      
      // Executor submits each month
      fn execute_vesting_payment(
          rpc: &RpcClient,
          pre_signed_tx: &Transaction,
      ) -> Result<(), Box<dyn std::error::Error>> {
          // No time pressure - can submit anytime!
          rpc.send_and_confirm_transaction(pre_signed_tx)?;
          Ok(())
      }
      ```
      
      ### 2. Conditional Trades (Limit Orders)
      
      ```rust
      // Pre-sign trade execution at specific price
      fn create_limit_order(
          nonce: &Pubkey,
          authority: &Keypair,
          swap_instruction: Instruction,  // Execute when price reached
      ) -> Transaction {
          let nonce_value = /* fetch nonce */;
      
          build_nonce_transaction(
              nonce,
              authority,
              nonce_value,
              vec![swap_instruction],
              authority,
          )
      }
      
      // Bot monitors price and submits when condition met
      fn execute_limit_order(rpc: &RpcClient, current_price: f64, limit_tx: &Transaction) {
          if current_price >= target_price {
              rpc.send_transaction(limit_tx).ok();  // Submit pre-signed transaction
          }
      }
      ```
      
      ### 3. Cross-Chain Bridges
      
      ```rust
      // Sign Solana transaction while waiting for Ethereum finality
      async fn bridge_from_ethereum_to_solana(
          eth_tx_hash: H256,
          solana_mint_ix: Instruction,
          nonce_account: &Pubkey,
          nonce_authority: &Keypair,
      ) -> Result<(), Box<dyn std::error::Error>> {
          // 1. Pre-sign Solana mint transaction
          let nonce = get_nonce_value(&solana_rpc, nonce_account)?;
          let mint_tx = build_nonce_transaction(
              nonce_account,
              nonce_authority,
              nonce,
              vec![solana_mint_ix],
              nonce_authority,
          );
      
          // 2. Wait for Ethereum finality (12+ minutes)
          wait_for_ethereum_finality(eth_tx_hash).await?;
      
          // 3. Submit Solana transaction (still valid!)
          solana_rpc.send_and_confirm_transaction(&mint_tx)?;
      
          Ok(())
      }
      ```
      
      ### 4. DAO Governance Execution
      
      Already covered in multi-sig example above - proposals can be voted on over days/weeks, then executed with pre-signed transaction.
      
      ## CLI Reference
      
      **Create nonce account:**
      ```bash
      solana create-nonce-account <KEYPAIR_PATH> <AMOUNT>
      ```
      
      **Get current nonce:**
      ```bash
      solana nonce <NONCE_ACCOUNT>
      ```
      
      **Manually advance nonce:**
      ```bash
      solana new-nonce <NONCE_ACCOUNT>
      ```
      
      **Get nonce account info:**
      ```bash
      solana nonce-account <NONCE_ACCOUNT>
      ```
      
      **Withdraw from nonce:**
      ```bash
      solana withdraw-from-nonce-account <NONCE_ACCOUNT> <DESTINATION> <AMOUNT>
      ```
      
      **Change nonce authority:**
      ```bash
      solana authorize-nonce-account <NONCE_ACCOUNT> <NEW_AUTHORITY>
      ```
      
      **Sign transaction offline:**
      ```bash
      solana <COMMAND> \
        --sign-only \
        --nonce <NONCE_ACCOUNT> \
        --nonce-authority <AUTHORITY_KEYPAIR> \
        --blockhash <NONCE_VALUE>
      ```
      
      **Submit pre-signed transaction:**
      ```bash
      solana <COMMAND> \
        --nonce <NONCE_ACCOUNT> \
        --nonce-authority <AUTHORITY_KEYPAIR> \
        --blockhash <NONCE_VALUE> \
        --signer <PUBKEY=SIGNATURE>
      ```
      
      ## Limitations and Considerations
      
      **Transaction size:**
      - Adding `advance_nonce_account` instruction adds ~40 bytes
      - May push transaction over size limit if already near maximum
      
      **Extra signature requirement:**
      - Nonce authority must sign (if different from fee payer)
      - Increases transaction complexity
      
      **Rent cost:**
      - Each nonce account requires ~0.0015 SOL rent-exempt minimum
      - For many scheduled transactions, can become expensive
      
      **Nonce advancement overhead:**
      - Compute units to advance nonce (~few hundred CU)
      - Minimal but worth considering for CU-constrained transactions
      
      **Cannot mix recent blockhashes and nonces:**
      - Transaction uses either recent blockhash OR durable nonce
      - Cannot use both in the same transaction
      
      ## Resources
      
      ### Official Documentation
      - [Introduction to Durable Nonces](https://solana.com/developers/guides/advanced/introduction-to-durable-nonces)
      - [Durable Transaction Nonces Proposal](https://docs.anza.xyz/implemented-proposals/durable-tx-nonces)
      - [CLI Nonce Examples](https://docs.anza.xyz/cli/examples/durable-nonce)
      
      ### Code Examples
      - [Durable Nonces Repository](https://github.com/0xproflupin/solana-durable-nonces)
      - [System Program Source](https://github.com/solana-labs/solana/blob/master/sdk/program/src/system_instruction.rs)
      
      ### Security Analysis
      - [Neodyme: Nonce Upon a Time](https://neodyme.io/en/blog/nonce-upon-a-time/) - Historic vulnerability analysis
      
      ### Technical References
      - [solana-sdk NonceState](https://docs.rs/solana-sdk/latest/solana_sdk/nonce/state/enum.State.html)
      - [System Program Instructions](https://docs.rs/solana-sdk/latest/solana_sdk/system_instruction/)
      
    • error-handling.md 18.6 KB
      # Error Handling in Solana Programs
      
      This reference provides comprehensive coverage of error handling patterns for native Rust Solana program development, including custom error types, error propagation, and best practices.
      
      ## Table of Contents
      
      1. [Error Handling Fundamentals](#error-handling-fundamentals)
      2. [ProgramError](#programerror)
      3. [Custom Error Types](#custom-error-types)
      4. [Error Propagation](#error-propagation)
      5. [Error Context and Logging](#error-context-and-logging)
      6. [Client-Side Error Handling](#client-side-error-handling)
      7. [Best Practices](#best-practices)
      
      ---
      
      ## Error Handling Fundamentals
      
      ### Why Error Handling Matters
      
      **In Solana programs, errors serve multiple purposes:**
      
      1. **Security:** Prevent invalid state transitions
      2. **User Experience:** Provide meaningful feedback
      3. **Debugging:** Identify issues quickly
      4. **Transaction Validation:** Fail fast when invariants are violated
      
      **Key Principle:** Errors should cause the entire transaction to fail and rollback, maintaining atomicity.
      
      ### The Result Type
      
      All Solana program instructions return `ProgramResult`:
      
      ```rust
      use solana_program::{
          entrypoint::ProgramResult,
          program_error::ProgramError,
      };
      
      pub type ProgramResult = Result<(), ProgramError>;
      
      // Success
      pub fn successful_operation() -> ProgramResult {
          Ok(())
      }
      
      // Failure
      pub fn failed_operation() -> ProgramResult {
          Err(ProgramError::Custom(42))
      }
      ```
      
      **When an instruction returns `Err`:**
      - Transaction fails immediately
      - All state changes rollback
      - Error code returned to client
      - Transaction fee still charged (for processing cost)
      
      ---
      
      ## ProgramError
      
      ### The Built-in Error Type
      
      Solana provides `ProgramError` enum with common error variants:
      
      ```rust
      use solana_program::program_error::ProgramError;
      
      pub enum ProgramError {
          // Common errors
          Custom(u32),                           // Custom error code
          InvalidArgument,                       // Invalid instruction argument
          InvalidInstructionData,                // Failed to deserialize instruction data
          InvalidAccountData,                    // Invalid account data
          AccountDataTooSmall,                   // Account data too small
          InsufficientFunds,                     // Not enough lamports
          IncorrectProgramId,                    // Wrong program ID
          MissingRequiredSignature,              // Required signer missing
          AccountAlreadyInitialized,             // Account already initialized
          UninitializedAccount,                  // Account not initialized
          NotEnoughAccountKeys,                  // Not enough accounts provided
          AccountBorrowFailed,                   // Failed to borrow account data
          MaxSeedLengthExceeded,                 // PDA seed too long
          InvalidSeeds,                          // Invalid PDA derivation
          BorshIoError(String),                  // Borsh serialization error
          AccountNotRentExempt,                  // Account not rent-exempt
          IllegalOwner,                          // Wrong account owner
          ArithmeticOverflow,                    // Arithmetic overflow
          // ... and more
      }
      ```
      
      ### Common ProgramError Usage
      
      ```rust
      use solana_program::program_error::ProgramError;
      
      pub fn validate_inputs(
          amount: u64,
          max_amount: u64,
      ) -> ProgramResult {
          // InvalidArgument: Input doesn't meet requirements
          if amount == 0 {
              return Err(ProgramError::InvalidArgument);
          }
      
          // InsufficientFunds: Not enough balance
          if amount > max_amount {
              return Err(ProgramError::InsufficientFunds);
          }
      
          // ArithmeticOverflow: Math operation failed
          let _result = amount.checked_mul(2)
              .ok_or(ProgramError::ArithmeticOverflow)?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Custom Error Types
      
      ### Why Custom Errors?
      
      **Built-in `ProgramError` is generic.** Custom errors provide:
      
      - **Specific error codes** for different failure modes
      - **Better debugging** with descriptive messages
      - **Client clarity** - clients know exactly what went wrong
      - **Documentation** - errors serve as API documentation
      
      ### Defining Custom Errors
      
      Use the `thiserror` crate to define custom error enums:
      
      ```rust
      use solana_program::program_error::ProgramError;
      use thiserror::Error;
      
      #[derive(Error, Debug, Copy, Clone)]
      pub enum NoteError {
          #[error("You do not own this note")]
          Forbidden,
      
          #[error("Note text is too long")]
          InvalidLength,
      
          #[error("Rating must be between 1 and 5")]
          InvalidRating,
      
          #[error("Note title cannot be empty")]
          EmptyTitle,
      
          #[error("Maximum notes limit reached")]
          MaxNotesExceeded,
      }
      ```
      
      **Attributes explained:**
      - `#[derive(Error)]` - Implements `std::error::Error` trait
      - `#[derive(Debug)]` - Allows `{:?}` formatting
      - `#[derive(Copy, Clone)]` - Makes errors copyable (recommended)
      - `#[error("...")]` - Error message string
      
      ### Converting to ProgramError
      
      Implement `From<CustomError> for ProgramError`:
      
      ```rust
      impl From<NoteError> for ProgramError {
          fn from(e: NoteError) -> Self {
              ProgramError::Custom(e as u32)
          }
      }
      ```
      
      **How it works:**
      1. Custom error is converted to `u32` (using `as u32` cast)
      2. Wrapped in `ProgramError::Custom(u32)`
      3. Returned to client as error code
      
      **Error code mapping:**
      ```rust
      NoteError::Forbidden      → ProgramError::Custom(0)
      NoteError::InvalidLength  → ProgramError::Custom(1)
      NoteError::InvalidRating  → ProgramError::Custom(2)
      NoteError::EmptyTitle     → ProgramError::Custom(3)
      NoteError::MaxNotesExceeded → ProgramError::Custom(4)
      ```
      
      ### Using Custom Errors
      
      ```rust
      pub fn create_note(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          title: String,
          content: String,
          rating: u8,
      ) -> ProgramResult {
          // Validation with custom errors
          if title.is_empty() {
              return Err(NoteError::EmptyTitle.into());
          }
      
          if content.len() > 1000 {
              return Err(NoteError::InvalidLength.into());
          }
      
          if rating < 1 || rating > 5 {
              return Err(NoteError::InvalidRating.into());
          }
      
          // Continue processing...
          Ok(())
      }
      ```
      
      **The `.into()` method** automatically converts `NoteError` to `ProgramError`.
      
      ### Advanced Custom Error Types
      
      **With additional context:**
      
      ```rust
      #[derive(Error, Debug)]
      pub enum GameError {
          #[error("Insufficient mana: have {current}, need {required}")]
          InsufficientMana { current: u32, required: u32 },
      
          #[error("Invalid move: {0}")]
          InvalidMove(String),
      
          #[error("Player not found: {0}")]
          PlayerNotFound(String),
      }
      ```
      
      **Note:** Errors with fields cannot derive `Copy`, only `Clone`.
      
      ---
      
      ## Error Propagation
      
      ### The `?` Operator
      
      The `?` operator is Rust's error propagation mechanism:
      
      ```rust
      pub fn complex_operation(
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          // If validation fails, error is returned immediately
          validate_accounts(accounts)?;
      
          // If deserialization fails, error is propagated
          let data = AccountData::try_from_slice(&accounts[0].data.borrow())?;
      
          // If checked math fails, ArithmeticOverflow is returned
          let result = data.value.checked_add(100)
              .ok_or(ProgramError::ArithmeticOverflow)?;
      
          Ok(())
      }
      ```
      
      **What `?` does:**
      1. If `Result` is `Ok(value)`, unwraps to `value`
      2. If `Result` is `Err(e)`, converts `e` and returns early
      3. Conversion happens via `From` trait
      
      ### Error Conversion Chain
      
      ```rust
      // Step 1: Borsh deserialization fails
      let data = MyData::try_from_slice(bytes)?;
      // Returns: Err(std::io::Error)
      
      // Step 2: ? operator converts via From trait
      // std::io::Error → ProgramError::BorshIoError
      
      // Step 3: Custom error conversion
      return Err(MyError::InvalidData.into());
      // MyError → ProgramError::Custom(n)
      ```
      
      ### Manual Error Handling
      
      ```rust
      // Without ?
      pub fn manual_error_handling(
          account: &AccountInfo,
      ) -> ProgramResult {
          match validate_account(account) {
              Ok(()) => {
                  // Continue processing
              }
              Err(e) => {
                  msg!("Validation failed: {:?}", e);
                  return Err(e);
              }
          }
      
          Ok(())
      }
      
      // With ? (equivalent)
      pub fn automatic_error_handling(
          account: &AccountInfo,
      ) -> ProgramResult {
          validate_account(account)?;
          Ok(())
      }
      ```
      
      ### Mapping Errors
      
      Transform one error type to another:
      
      ```rust
      pub fn map_errors(
          account: &AccountInfo,
      ) -> ProgramResult {
          // Map generic error to custom error
          let data = AccountData::try_from_slice(&account.data.borrow())
              .map_err(|_| NoteError::InvalidLength)?;
      
          // Map to different ProgramError variant
          let value = data.amount.checked_add(100)
              .ok_or(ProgramError::ArithmeticOverflow)?;
      
          Ok(())
      }
      ```
      
      ### Combining Multiple Operations
      
      ```rust
      pub fn chain_operations(
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          // All operations must succeed or transaction fails
          let account1 = validate_and_load_account(&accounts[0])?;
          let account2 = validate_and_load_account(&accounts[1])?;
      
          let combined = account1.value
              .checked_add(account2.value)
              .ok_or(ProgramError::ArithmeticOverflow)?;
      
          update_account(&accounts[2], combined)?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Error Context and Logging
      
      ### Adding Context with `msg!`
      
      Use `msg!` macro to log context before returning errors:
      
      ```rust
      use solana_program::msg;
      
      pub fn transfer_tokens(
          from: &AccountInfo,
          to: &AccountInfo,
          amount: u64,
      ) -> ProgramResult {
          if amount == 0 {
              msg!("Transfer amount cannot be zero");
              return Err(ProgramError::InvalidArgument);
          }
      
          let from_balance = get_balance(from)?;
      
          if from_balance < amount {
              msg!("Insufficient balance: have {}, need {}", from_balance, amount);
              return Err(ProgramError::InsufficientFunds);
          }
      
          // Perform transfer...
          Ok(())
      }
      ```
      
      ### Logging Best Practices
      
      **✅ Good logging:**
      ```rust
      msg!("Invalid rating: got {}, expected 1-5", rating);
      msg!("PDA derivation failed: expected {}, got {}", expected, actual);
      msg!("Account {} not owned by program {}", account.key, program_id);
      ```
      
      **❌ Poor logging:**
      ```rust
      msg!("Error");  // Not helpful
      msg!("Failed");  // What failed?
      // (no logging)  // Can't debug issues
      ```
      
      ### Conditional Logging
      
      ```rust
      pub fn debug_operation(
          account: &AccountInfo,
          debug_mode: bool,
      ) -> ProgramResult {
          if debug_mode {
              msg!("Processing account: {}", account.key);
              msg!("Owner: {}", account.owner);
              msg!("Lamports: {}", account.lamports());
          }
      
          // Process...
          Ok(())
      }
      ```
      
      ### Error with Recovery
      
      ```rust
      pub fn try_with_fallback(
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          // Try primary method
          match process_primary(accounts) {
              Ok(()) => {
                  msg!("Primary method succeeded");
                  Ok(())
              }
              Err(e) => {
                  msg!("Primary method failed: {:?}, trying fallback", e);
      
                  // Try fallback
                  process_fallback(accounts).map_err(|fallback_err| {
                      msg!("Fallback also failed: {:?}", fallback_err);
                      fallback_err
                  })
              }
          }
      }
      ```
      
      ---
      
      ## Client-Side Error Handling
      
      ### Error Code Interpretation
      
      **Client receives:**
      ```json
      {
        "error": {
          "InstructionError": [
            0,
            {
              "Custom": 2
            }
          ]
        }
      }
      ```
      
      **Decoding:**
      - Instruction index: `0` (first instruction)
      - Error type: `Custom`
      - Error code: `2`
      
      ### TypeScript Error Mapping
      
      ```typescript
      // Define error codes matching Rust enum
      enum NoteError {
          Forbidden = 0,
          InvalidLength = 1,
          InvalidRating = 2,
          EmptyTitle = 3,
          MaxNotesExceeded = 4,
      }
      
      // Error messages
      const NOTE_ERROR_MESSAGES = {
          [NoteError.Forbidden]: "You do not own this note",
          [NoteError.InvalidLength]: "Note text is too long",
          [NoteError.InvalidRating]: "Rating must be between 1 and 5",
          [NoteError.EmptyTitle]: "Note title cannot be empty",
          [NoteError.MaxNotesExceeded]: "Maximum notes limit reached",
      };
      
      // Parse error
      function parseNoteError(error: any): string {
          if (error?.InstructionError) {
              const [_, instructionError] = error.InstructionError;
      
              if (instructionError?.Custom !== undefined) {
                  const errorCode = instructionError.Custom;
                  return NOTE_ERROR_MESSAGES[errorCode] || `Unknown error: ${errorCode}`;
              }
          }
      
          return "Transaction failed";
      }
      
      // Usage
      try {
          await program.methods.createNote(title, content, rating).rpc();
      } catch (error) {
          const message = parseNoteError(error);
          console.error(message);
      }
      ```
      
      ### Anchor Error Handling
      
      **With Anchor framework:**
      
      ```typescript
      import { AnchorError } from "@coral-xyz/anchor";
      
      try {
          await program.methods.createNote(title, content, rating).rpc();
      } catch (error) {
          if (error instanceof AnchorError) {
              console.error("Error code:", error.error.errorCode.code);
              console.error("Error message:", error.error.errorMessage);
              console.error("Error number:", error.error.errorCode.number);
          }
      }
      ```
      
      ---
      
      ## Best Practices
      
      ### 1. Fail Fast
      
      **Return errors immediately when validation fails:**
      
      ```rust
      // ✅ Good - fails fast
      pub fn validate_input(rating: u8) -> ProgramResult {
          if rating < 1 || rating > 5 {
              return Err(NoteError::InvalidRating.into());
          }
      
          // Continue only if valid
          Ok(())
      }
      
      // ❌ Bad - continues with invalid state
      pub fn validate_input_bad(rating: u8) -> ProgramResult {
          if rating >= 1 && rating <= 5 {
              // Valid branch
          }
          // Continues regardless!
          Ok(())
      }
      ```
      
      ### 2. Meaningful Error Messages
      
      ```rust
      // ✅ Good - specific and actionable
      #[error("Username must be 3-20 characters, got {0}")]
      InvalidUsernameLength(usize),
      
      #[error("Insufficient mana: need {required}, have {current}")]
      InsufficientMana { required: u32, current: u32 },
      
      // ❌ Bad - vague
      #[error("Invalid input")]
      InvalidInput,
      
      #[error("Error")]
      GenericError,
      ```
      
      ### 3. Organize Errors by Category
      
      ```rust
      #[derive(Error, Debug, Copy, Clone)]
      pub enum GameError {
          // Validation errors (0-99)
          #[error("Invalid player name")]
          InvalidPlayerName,
      
          #[error("Invalid move")]
          InvalidMove,
      
          // State errors (100-199)
          #[error("Game not started")]
          GameNotStarted,
      
          #[error("Game already finished")]
          GameFinished,
      
          // Resource errors (200-299)
          #[error("Insufficient gold")]
          InsufficientGold,
      
          #[error("Inventory full")]
          InventoryFull,
      }
      ```
      
      ### 4. Consistent Error Handling Pattern
      
      ```rust
      pub fn standard_operation_pattern(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          params: Params,
      ) -> ProgramResult {
          // 1. Parse accounts
          let account_info_iter = &mut accounts.iter();
          let user = next_account_info(account_info_iter)?;
          let data_account = next_account_info(account_info_iter)?;
      
          // 2. Validate signers
          if !user.is_signer {
              msg!("User must sign the transaction");
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          // 3. Validate ownership
          if data_account.owner != program_id {
              msg!("Data account not owned by program");
              return Err(ProgramError::IllegalOwner);
          }
      
          // 4. Validate input parameters
          if params.amount == 0 {
              msg!("Amount cannot be zero");
              return Err(ProgramError::InvalidArgument);
          }
      
          // 5. Load and validate account data
          let mut data = AccountData::try_from_slice(&data_account.data.borrow())?;
      
          if !data.is_initialized {
              msg!("Account not initialized");
              return Err(ProgramError::UninitializedAccount);
          }
      
          // 6. Perform operation
          // ...
      
          Ok(())
      }
      ```
      
      ### 5. Document Error Codes
      
      ```rust
      /// Error codes for the Note program.
      ///
      /// | Code | Error | Description |
      /// |------|-------|-------------|
      /// | 0 | Forbidden | Caller does not own the note |
      /// | 1 | InvalidLength | Note text exceeds maximum length |
      /// | 2 | InvalidRating | Rating not in range 1-5 |
      /// | 3 | EmptyTitle | Note title is empty |
      /// | 4 | MaxNotesExceeded | User has reached note limit |
      #[derive(Error, Debug, Copy, Clone)]
      #[repr(u32)]
      pub enum NoteError {
          #[error("You do not own this note")]
          Forbidden = 0,
      
          #[error("Note text is too long")]
          InvalidLength = 1,
      
          #[error("Rating must be between 1 and 5")]
          InvalidRating = 2,
      
          #[error("Note title cannot be empty")]
          EmptyTitle = 3,
      
          #[error("Maximum notes limit reached")]
          MaxNotesExceeded = 4,
      }
      ```
      
      ### 6. Error Testing
      
      ```rust
      #[cfg(test)]
      mod tests {
          use super::*;
      
          #[test]
          fn test_invalid_rating() {
              let result = validate_rating(0);
              assert_eq!(
                  result.unwrap_err(),
                  NoteError::InvalidRating.into()
              );
      
              let result = validate_rating(6);
              assert_eq!(
                  result.unwrap_err(),
                  NoteError::InvalidRating.into()
              );
          }
      
          #[test]
          fn test_valid_rating() {
              for rating in 1..=5 {
                  assert!(validate_rating(rating).is_ok());
              }
          }
      }
      ```
      
      ### 7. Avoid Silent Failures
      
      ```rust
      // ❌ Bad - errors ignored
      pub fn bad_error_handling(accounts: &[AccountInfo]) -> ProgramResult {
          let _ = validate_accounts(accounts);  // Ignores error!
      
          if let Ok(data) = load_data(accounts) {
              process(data);  // What if load_data failed?
          }
      
          Ok(())  // Returns success even if operations failed!
      }
      
      // ✅ Good - errors propagated
      pub fn good_error_handling(accounts: &[AccountInfo]) -> ProgramResult {
          validate_accounts(accounts)?;
      
          let data = load_data(accounts)?;
          process(data)?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Summary
      
      **Key Takeaways:**
      
      1. **Always return `ProgramResult`** from instruction handlers
      2. **Use custom errors** for specific failure modes
      3. **Implement `From` trait** to convert custom errors to `ProgramError`
      4. **Use `?` operator** for clean error propagation
      5. **Add context with `msg!`** for better debugging
      6. **Fail fast** - return errors immediately
      7. **Document error codes** for client developers
      8. **Test error cases** as thoroughly as success cases
      
      **Error Handling Pattern:**
      
      ```rust
      use solana_program::{
          entrypoint::ProgramResult,
          program_error::ProgramError,
          msg,
      };
      use thiserror::Error;
      
      // 1. Define custom errors
      #[derive(Error, Debug, Copy, Clone)]
      pub enum MyError {
          #[error("Descriptive error message")]
          SpecificError,
      }
      
      // 2. Implement From conversion
      impl From<MyError> for ProgramError {
          fn from(e: MyError) -> Self {
              ProgramError::Custom(e as u32)
          }
      }
      
      // 3. Use in program
      pub fn my_instruction(accounts: &[AccountInfo]) -> ProgramResult {
          // Validate
          if invalid_condition {
              msg!("Detailed error context");
              return Err(MyError::SpecificError.into());
          }
      
          // Propagate errors with ?
          let data = load_data(accounts)?;
      
          Ok(())
      }
      ```
      
      **Remember:** Good error handling is not optional—it's essential for security, debugging, and user experience.
      
    • native-rust.md 44 KB
      # Native Rust Solana Programs Reference
      
      This reference covers native Rust-specific implementation patterns and workflows for building Solana programs without the Anchor framework. For general concepts (what PDAs/CPIs are), see the other reference files.
      
      ## Table of Contents
      
      - [Project Setup](#project-setup)
      - [Entrypoint Patterns](#entrypoint-patterns)
      - [Manual Account Handling](#manual-account-handling)
      - [Manual Serialization](#manual-serialization)
      - [Instruction Definition](#instruction-definition)
      - [State Management](#state-management)
      - [Manual CPI Patterns](#manual-cpi-patterns)
      - [Build and Deploy Workflow](#build-and-deploy-workflow)
      - [Testing with Mollusk](#testing-with-mollusk)
      - [Verified Builds](#verified-builds)
      - [Program Management](#program-management)
      - [Common Native Patterns](#common-native-patterns)
      
      ---
      
      ## Project Setup
      
      ### Cargo.toml Configuration
      
      Basic program configuration:
      
      ```toml
      [package]
      name = "my_program"
      version = "0.1.0"
      edition = "2021"
      
      [lib]
      crate-type = ["cdylib", "lib"]  # cdylib for .so, lib for tests
      name = "my_program"
      
      [features]
      no-entrypoint = []  # Disable entrypoint for testing/CPI
      
      [dependencies]
      solana-program = "2.1.0"
      borsh = "1.5.1"
      borsh-derive = "1.5.1"
      
      [dev-dependencies]
      mollusk-svm = "0.3.0"
      solana-sdk = "2.1.0"
      
      [profile.release]
      overflow-checks = true
      lto = "fat"
      codegen-units = 1
      
      [profile.release.build-override]
      opt-level = 3
      incremental = false
      codegen-units = 1
      ```
      
      ### Dependency Versions
      
      **Production Dependencies:**
      - `solana-program = "2.1.0"` - Core program runtime APIs
      - `borsh = "1.5.1"` - Serialization framework
      - `borsh-derive = "1.5.1"` - Derive macros for Borsh
      
      **Development Dependencies:**
      - `mollusk-svm = "0.3.0"` - Fast testing framework
      - `solana-sdk = "2.1.0"` - Client-side SDK for tests
      - `mollusk-svm-bencher = "0.3.0"` - Compute unit benchmarking
      
      **Optional Helpers:**
      - `thiserror = "2.0"` - Error type definitions
      - `num-derive = "0.4"` - Derive numeric traits
      - `num-traits = "0.2"` - Numeric trait support
      - `spl-token = "6.0"` - Token program integration
      - `spl-associated-token-account = "5.0"` - ATA integration
      - `bytemuck = "1.20"` - Zero-copy type conversions
      
      ### Workspace Setup Pattern
      
      For multi-program projects:
      
      ```toml
      # Workspace Cargo.toml
      [workspace]
      members = [
          "programs/program-one",
          "programs/program-two",
      ]
      resolver = "2"
      
      [workspace.dependencies]
      solana-program = "2.1.0"
      borsh = "1.5.1"
      
      # Program Cargo.toml
      [dependencies]
      solana-program = { workspace = true }
      borsh = { workspace = true }
      ```
      
      ### Project Structure
      
      ```
      my-program/
      ├── Cargo.toml
      ├── src/
      │   ├── lib.rs              # Entrypoint and routing
      │   ├── instruction.rs      # Instruction definitions
      │   ├── state.rs            # Account state structs
      │   ├── processor.rs        # Instruction handlers
      │   ├── error.rs            # Custom errors
      │   └── utils.rs            # Helper functions
      ├── tests/
      │   └── test.rs             # Mollusk tests
      └── target/
          └── deploy/
              ├── program.so      # Built program binary
              └── program-keypair.json  # Program keypair
      ```
      
      ---
      
      ## Entrypoint Patterns
      
      ### Basic Entrypoint
      
      The `entrypoint!` macro sets up the program entry:
      
      ```rust
      use solana_program::{
          account_info::AccountInfo,
          entrypoint,
          entrypoint::ProgramResult,
          pubkey::Pubkey,
      };
      
      // Declare the entrypoint
      entrypoint!(process_instruction);
      
      // Process instruction function signature
      pub fn process_instruction(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          instruction_data: &[u8],
      ) -> ProgramResult {
          // Route to handlers
          Ok(())
      }
      ```
      
      ### Conditional Entrypoint (for testing/CPI)
      
      Disable entrypoint when used as a dependency:
      
      ```rust
      #[cfg(not(feature = "no-entrypoint"))]
      use solana_program::entrypoint;
      
      #[cfg(not(feature = "no-entrypoint"))]
      entrypoint!(process_instruction);
      
      pub fn process_instruction(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          instruction_data: &[u8],
      ) -> ProgramResult {
          // Implementation
          Ok(())
      }
      ```
      
      ### Instruction Routing Pattern
      
      Route to different handlers based on instruction type:
      
      ```rust
      use borsh::BorshDeserialize;
      
      pub fn process_instruction(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          instruction_data: &[u8],
      ) -> ProgramResult {
          // Deserialize instruction
          let instruction = MyInstruction::try_from_slice(instruction_data)?;
      
          // Route to handler
          match instruction {
              MyInstruction::Initialize { data } => {
                  process_initialize(program_id, accounts, data)
              }
              MyInstruction::Update { new_data } => {
                  process_update(program_id, accounts, new_data)
              }
              MyInstruction::Close => {
                  process_close(program_id, accounts)
              }
          }
      }
      ```
      
      ### Multi-Module Routing
      
      For larger programs, organize handlers in modules:
      
      ```rust
      mod processor;
      
      pub fn process_instruction(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          instruction_data: &[u8],
      ) -> ProgramResult {
          let instruction = MyInstruction::try_from_slice(instruction_data)?;
      
          match instruction {
              MyInstruction::Initialize { data } => {
                  processor::initialize::process(program_id, accounts, data)
              }
              MyInstruction::Update { new_data } => {
                  processor::update::process(program_id, accounts, new_data)
              }
              MyInstruction::Close => {
                  processor::close::process(program_id, accounts)
              }
          }
      }
      ```
      
      ---
      
      ## Manual Account Handling
      
      ### Using next_account_info Iterator
      
      The standard pattern for accessing accounts:
      
      ```rust
      use solana_program::{
          account_info::{next_account_info, AccountInfo},
          entrypoint::ProgramResult,
      };
      
      fn process_transfer(accounts: &[AccountInfo]) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
      
          // Get accounts in order
          let payer = next_account_info(account_info_iter)?;
          let recipient = next_account_info(account_info_iter)?;
          let system_program = next_account_info(account_info_iter)?;
      
          // Use accounts...
          Ok(())
      }
      ```
      
      ### AccountInfo Structure and Methods
      
      Key fields and methods:
      
      ```rust
      pub struct AccountInfo<'a> {
          pub key: &'a Pubkey,              // Account public key
          pub is_signer: bool,              // Signed transaction?
          pub is_writable: bool,            // Writable account?
          pub lamports: Rc<RefCell<&'a mut u64>>,  // Account balance
          pub data: Rc<RefCell<&'a mut [u8]>>,     // Account data
          pub owner: &'a Pubkey,            // Owner program
          pub executable: bool,             // Is executable?
          pub rent_epoch: Epoch,            // Rent epoch
      }
      
      // Common methods
      impl<'a> AccountInfo<'a> {
          // Check if account signed the transaction
          pub fn is_signer(&self) -> bool;
      
          // Check if account is writable
          pub fn is_writable(&self) -> bool;
      
          // Borrow account data immutably
          pub fn data(&self) -> Ref<&mut [u8]>;
      
          // Borrow account data mutably
          pub fn data_mut(&self) -> RefMut<&mut [u8]>;
      
          // Borrow lamports immutably
          pub fn lamports(&self) -> Ref<&mut u64>;
      
          // Borrow lamports mutably
          pub fn lamports_mut(&self) -> RefMut<&mut u64>;
      
          // Get data length
          pub fn data_len(&self) -> usize;
      
          // Check if owned by program
          pub fn is_owned_by(&self, program_id: &Pubkey) -> bool;
      
          // Deserialize account data
          pub fn deserialize_data<T: BorshDeserialize>(&self) -> Result<T, Error>;
      
          // Serialize data into account
          pub fn serialize_data<T: BorshSerialize>(&self, state: &T) -> Result<(), Error>;
      }
      ```
      
      ### Explicit Account Validation Patterns
      
      **Signer Check:**
      
      ```rust
      if !account.is_signer {
          return Err(ProgramError::MissingRequiredSignature);
      }
      ```
      
      **Writable Check:**
      
      ```rust
      if !account.is_writable {
          return Err(ProgramError::InvalidAccountData);
      }
      ```
      
      **Owner Check:**
      
      ```rust
      if account.owner != program_id {
          return Err(ProgramError::IncorrectProgramId);
      }
      ```
      
      **Specific Owner Check:**
      
      ```rust
      use solana_program::system_program;
      
      if account.owner != &system_program::ID {
          return Err(ProgramError::InvalidAccountOwner);
      }
      ```
      
      **Combined Validation:**
      
      ```rust
      fn validate_account(
          account: &AccountInfo,
          expected_owner: &Pubkey,
          must_sign: bool,
          must_write: bool,
      ) -> ProgramResult {
          if must_sign && !account.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          if must_write && !account.is_writable {
              return Err(ProgramError::InvalidAccountData);
          }
      
          if account.owner != expected_owner {
              return Err(ProgramError::IncorrectProgramId);
          }
      
          Ok(())
      }
      ```
      
      **PDA Validation:**
      
      ```rust
      fn validate_pda(
          account: &AccountInfo,
          seeds: &[&[u8]],
          program_id: &Pubkey,
      ) -> ProgramResult {
          let (expected_key, _bump) = Pubkey::find_program_address(seeds, program_id);
      
          if account.key != &expected_key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          Ok(())
      }
      ```
      
      **Rent Exemption Check:**
      
      ```rust
      use solana_program::sysvar::{rent::Rent, Sysvar};
      
      fn check_rent_exempt(account: &AccountInfo) -> ProgramResult {
          let rent = Rent::get()?;
      
          if !rent.is_exempt(account.lamports(), account.data_len()) {
              return Err(ProgramError::AccountNotRentExempt);
          }
      
          Ok(())
      }
      ```
      
      ### Account Data Access Patterns
      
      **Immutable Borrow:**
      
      ```rust
      let data = account.data.borrow();
      let state = MyState::try_from_slice(&data)?;
      ```
      
      **Mutable Borrow:**
      
      ```rust
      let mut data = account.data.borrow_mut();
      let mut state = MyState::try_from_slice(&data)?;
      state.counter += 1;
      state.serialize(&mut &mut data[..])?;
      ```
      
      **Lamport Access:**
      
      ```rust
      // Read lamports
      let balance = account.lamports();
      println!("Balance: {}", *balance);
      
      // Modify lamports (for transfers)
      **account.lamports.borrow_mut() = new_balance;
      ```
      
      **Zero-Copy Data Access:**
      
      ```rust
      use bytemuck::{Pod, Zeroable};
      
      #[repr(C)]
      #[derive(Copy, Clone, Pod, Zeroable)]
      struct FastState {
          value: u64,
          flag: u8,
      }
      
      fn read_fast_state(account: &AccountInfo) -> Result<&FastState, ProgramError> {
          let data = account.try_borrow_data()?;
          bytemuck::try_from_bytes(&data[..std::mem::size_of::<FastState>()])
              .map_err(|_| ProgramError::InvalidAccountData)
      }
      ```
      
      ---
      
      ## Manual Serialization
      
      ### Borsh Derive
      
      Use `BorshSerialize` and `BorshDeserialize` for most cases:
      
      ```rust
      use borsh::{BorshSerialize, BorshDeserialize};
      
      #[derive(BorshSerialize, BorshDeserialize, Debug)]
      pub struct MyState {
          pub is_initialized: bool,
          pub counter: u64,
          pub authority: Pubkey,
          pub data: Vec<u8>,
      }
      ```
      
      ### Manual Borsh Implementation
      
      For custom serialization logic:
      
      ```rust
      use borsh::io::{Read, Write, Result as BorshResult};
      
      #[derive(Debug)]
      pub struct CustomState {
          pub flag: bool,
          pub value: u64,
      }
      
      impl BorshSerialize for CustomState {
          fn serialize<W: Write>(&self, writer: &mut W) -> BorshResult<()> {
              self.flag.serialize(writer)?;
              self.value.serialize(writer)?;
              Ok(())
          }
      }
      
      impl BorshDeserialize for CustomState {
          fn deserialize_reader<R: Read>(reader: &mut R) -> BorshResult<Self> {
              let flag = bool::deserialize_reader(reader)?;
              let value = u64::deserialize_reader(reader)?;
              Ok(Self { flag, value })
          }
      }
      ```
      
      ### Account Data Layout Planning
      
      Calculate and document exact byte offsets:
      
      ```rust
      // Account layout documentation
      // [0] is_initialized: bool (1 byte)
      // [1-8] counter: u64 (8 bytes)
      // [9-40] authority: Pubkey (32 bytes)
      // Total: 41 bytes
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct Counter {
          pub is_initialized: bool,  // 1 byte
          pub counter: u64,          // 8 bytes
          pub authority: Pubkey,     // 32 bytes
      }
      
      impl Counter {
          pub const LEN: usize = 1 + 8 + 32;  // 41 bytes
      }
      ```
      
      ### Packing and Unpacking Account Data
      
      **Deserialize (unpack):**
      
      ```rust
      use borsh::BorshDeserialize;
      
      fn get_state(account: &AccountInfo) -> Result<MyState, ProgramError> {
          let data = account.try_borrow_data()?;
          MyState::try_from_slice(&data)
              .map_err(|_| ProgramError::InvalidAccountData)
      }
      ```
      
      **Serialize (pack):**
      
      ```rust
      use borsh::BorshSerialize;
      
      fn save_state(account: &AccountInfo, state: &MyState) -> ProgramResult {
          let mut data = account.try_borrow_mut_data()?;
          state.serialize(&mut &mut data[..])
              .map_err(|_| ProgramError::InvalidAccountData)?;
          Ok(())
      }
      ```
      
      **Combined Pattern:**
      
      ```rust
      fn update_counter(account: &AccountInfo, increment: u64) -> ProgramResult {
          // Deserialize
          let mut data = account.try_borrow_mut_data()?;
          let mut state = MyState::try_from_slice(&data)?;
      
          // Modify
          state.counter += increment;
      
          // Serialize back
          state.serialize(&mut &mut data[..])?;
          Ok(())
      }
      ```
      
      ### Zero-Copy Patterns with Bytemuck
      
      For high-performance, use zero-copy with bytemuck:
      
      ```rust
      use bytemuck::{Pod, Zeroable, from_bytes_mut, bytes_of};
      
      #[repr(C)]
      #[derive(Copy, Clone, Pod, Zeroable)]
      pub struct ZeroCopyState {
          pub is_initialized: u8,  // bool as u8
          pub counter: u64,
          pub authority: [u8; 32], // Pubkey as bytes
      }
      
      impl ZeroCopyState {
          pub const LEN: usize = std::mem::size_of::<Self>();
      }
      
      // Read zero-copy
      fn get_state(account: &AccountInfo) -> Result<&ZeroCopyState, ProgramError> {
          let data = account.try_borrow_data()?;
          bytemuck::try_from_bytes(&data[..ZeroCopyState::LEN])
              .map_err(|_| ProgramError::InvalidAccountData)
      }
      
      // Write zero-copy
      fn update_state(account: &AccountInfo, new_counter: u64) -> ProgramResult {
          let mut data = account.try_borrow_mut_data()?;
          let state = bytemuck::try_from_bytes_mut::<ZeroCopyState>(
              &mut data[..ZeroCopyState::LEN]
          ).map_err(|_| ProgramError::InvalidAccountData)?;
      
          state.counter = new_counter;
          Ok(())
      }
      ```
      
      ### Variable-Length Data
      
      For dynamic data, use a header + data pattern:
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct VarLenState {
          pub is_initialized: bool,
          pub data_len: u32,
          // Followed by data_len bytes
      }
      
      impl VarLenState {
          pub const HEADER_LEN: usize = 1 + 4;  // bool + u32
      
          pub fn unpack(data: &[u8]) -> Result<(Self, &[u8]), ProgramError> {
              if data.len() < Self::HEADER_LEN {
                  return Err(ProgramError::InvalidAccountData);
              }
      
              let header = Self::try_from_slice(&data[..Self::HEADER_LEN])?;
              let data_slice = &data[Self::HEADER_LEN..Self::HEADER_LEN + header.data_len as usize];
      
              Ok((header, data_slice))
          }
      }
      ```
      
      ---
      
      ## Instruction Definition
      
      ### Borsh-Serializable Instruction Enums
      
      Define instructions as enums:
      
      ```rust
      use borsh::{BorshSerialize, BorshDeserialize};
      use solana_program::pubkey::Pubkey;
      
      #[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]
      pub enum MyInstruction {
          /// Initialize a new account
          ///
          /// Accounts expected:
          /// 0. `[writable, signer]` Account to initialize
          /// 1. `[signer]` Authority
          /// 2. `[]` System Program
          Initialize {
              initial_value: u64,
          },
      
          /// Update account data
          ///
          /// Accounts expected:
          /// 0. `[writable]` Account to update
          /// 1. `[signer]` Authority
          Update {
              new_value: u64,
          },
      
          /// Transfer ownership
          ///
          /// Accounts expected:
          /// 0. `[writable]` Account
          /// 1. `[signer]` Current authority
          /// 2. `[]` New authority
          TransferOwnership {
              new_authority: Pubkey,
          },
      
          /// Close account and reclaim rent
          ///
          /// Accounts expected:
          /// 0. `[writable]` Account to close
          /// 1. `[writable]` Rent recipient
          /// 2. `[signer]` Authority
          Close,
      }
      ```
      
      ### Instruction Data Layout
      
      **Fixed-Size Instructions:**
      
      ```rust
      // Discriminator (1 byte) + data
      // [0] = 0 -> Initialize
      // [1] = 1 -> Update
      // etc.
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub enum SimpleInstruction {
          Initialize = 0,
          Update = 1,
          Close = 2,
      }
      ```
      
      **Instructions with Parameters:**
      
      ```rust
      // Manual discriminator pattern
      pub enum MyInstruction {
          // Discriminator 0: [0, value_bytes[0..8]]
          Initialize { value: u64 },
      
          // Discriminator 1: [1, amount_bytes[0..8]]
          Transfer { amount: u64 },
      }
      
      impl MyInstruction {
          pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
              let (&discriminator, rest) = input.split_first()
                  .ok_or(ProgramError::InvalidInstructionData)?;
      
              Ok(match discriminator {
                  0 => {
                      let value = u64::from_le_bytes(rest[..8].try_into().unwrap());
                      Self::Initialize { value }
                  }
                  1 => {
                      let amount = u64::from_le_bytes(rest[..8].try_into().unwrap());
                      Self::Transfer { amount }
                  }
                  _ => return Err(ProgramError::InvalidInstructionData),
              })
          }
      }
      ```
      
      ### Dispatching Instructions
      
      **Pattern 1: Direct Match**
      
      ```rust
      pub fn process_instruction(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          instruction_data: &[u8],
      ) -> ProgramResult {
          let instruction = MyInstruction::try_from_slice(instruction_data)?;
      
          match instruction {
              MyInstruction::Initialize { initial_value } => {
                  msg!("Instruction: Initialize");
                  process_initialize(program_id, accounts, initial_value)
              }
              MyInstruction::Update { new_value } => {
                  msg!("Instruction: Update");
                  process_update(program_id, accounts, new_value)
              }
              MyInstruction::Close => {
                  msg!("Instruction: Close");
                  process_close(program_id, accounts)
              }
          }
      }
      ```
      
      **Pattern 2: Handler Functions**
      
      ```rust
      impl MyInstruction {
          pub fn process(
              &self,
              program_id: &Pubkey,
              accounts: &[AccountInfo],
          ) -> ProgramResult {
              match self {
                  Self::Initialize { initial_value } => {
                      Self::process_initialize(program_id, accounts, *initial_value)
                  }
                  Self::Update { new_value } => {
                      Self::process_update(program_id, accounts, *new_value)
                  }
                  Self::Close => {
                      Self::process_close(program_id, accounts)
                  }
              }
          }
      
          fn process_initialize(
              program_id: &Pubkey,
              accounts: &[AccountInfo],
              initial_value: u64,
          ) -> ProgramResult {
              // Implementation
              Ok(())
          }
      }
      ```
      
      ---
      
      ## State Management
      
      ### Defining Account State Structs
      
      ```rust
      use borsh::{BorshSerialize, BorshDeserialize};
      use solana_program::pubkey::Pubkey;
      
      #[derive(BorshSerialize, BorshDeserialize, Debug)]
      pub struct UserAccount {
          pub is_initialized: bool,
          pub authority: Pubkey,
          pub balance: u64,
          pub last_updated: i64,
      }
      
      impl UserAccount {
          pub const LEN: usize = 1 + 32 + 8 + 8;  // 49 bytes
      }
      ```
      
      ### Calculating Account Sizes
      
      **Fixed-Size Accounts:**
      
      ```rust
      impl MyState {
          // Method 1: Manual calculation
          pub const LEN: usize =
              1 +   // is_initialized: bool
              32 +  // authority: Pubkey
              8 +   // counter: u64
              4 +   // data_len: u32
              100;  // data: [u8; 100]
      
          // Method 2: Use size_of
          pub const LEN_ALT: usize = std::mem::size_of::<Self>();
      }
      ```
      
      **Variable-Size Accounts:**
      
      ```rust
      impl DynamicState {
          pub const BASE_LEN: usize = 1 + 32 + 8;  // Fixed fields
      
          pub fn calculate_size(data_len: usize) -> usize {
              Self::BASE_LEN + 4 + data_len  // +4 for length prefix
          }
      }
      ```
      
      **With Borsh:**
      
      ```rust
      use borsh::BorshSerialize;
      
      let state = MyState { /* ... */ };
      let serialized = state.try_to_vec()?;
      let size = serialized.len();  // Actual size needed
      ```
      
      ### Initializing Accounts Manually with System Program CPI
      
      **Complete Initialization Pattern:**
      
      ```rust
      use solana_program::{
          account_info::{next_account_info, AccountInfo},
          entrypoint::ProgramResult,
          program::invoke,
          pubkey::Pubkey,
          rent::Rent,
          system_instruction,
          sysvar::Sysvar,
      };
      
      fn process_initialize(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          initial_value: u64,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
      
          let new_account = next_account_info(account_info_iter)?;
          let payer = next_account_info(account_info_iter)?;
          let system_program = next_account_info(account_info_iter)?;
      
          // Calculate space needed
          let space = MyState::LEN;
      
          // Calculate rent
          let rent = Rent::get()?;
          let rent_lamports = rent.minimum_balance(space);
      
          // Create account via CPI to System Program
          invoke(
              &system_instruction::create_account(
                  payer.key,           // Funding account
                  new_account.key,     // New account
                  rent_lamports,       // Lamports
                  space as u64,        // Space
                  program_id,          // Owner
              ),
              &[
                  payer.clone(),
                  new_account.clone(),
                  system_program.clone(),
              ],
          )?;
      
          // Initialize account data
          let mut data = new_account.try_borrow_mut_data()?;
          let state = MyState {
              is_initialized: true,
              counter: initial_value,
              authority: *payer.key,
          };
          state.serialize(&mut &mut data[..])?;
      
          Ok(())
      }
      ```
      
      **Initialize PDA Pattern:**
      
      ```rust
      use solana_program::program::invoke_signed;
      
      fn initialize_pda(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          seeds: &[&[u8]],
          bump: u8,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
      
          let pda = next_account_info(account_info_iter)?;
          let payer = next_account_info(account_info_iter)?;
          let system_program = next_account_info(account_info_iter)?;
      
          // Verify PDA
          let (expected_pda, expected_bump) = Pubkey::find_program_address(seeds, program_id);
          if pda.key != &expected_pda || bump != expected_bump {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // Create PDA account
          let space = MyState::LEN;
          let rent = Rent::get()?;
          let lamports = rent.minimum_balance(space);
      
          let bump_seed = &[bump];
          let seeds_with_bump = &[seeds, &[bump_seed.as_slice()]].concat();
      
          invoke_signed(
              &system_instruction::create_account(
                  payer.key,
                  pda.key,
                  lamports,
                  space as u64,
                  program_id,
              ),
              &[payer.clone(), pda.clone(), system_program.clone()],
              &[seeds_with_bump],  // Signer seeds
          )?;
      
          // Initialize data
          let mut data = pda.try_borrow_mut_data()?;
          let state = MyState::default();
          state.serialize(&mut &mut data[..])?;
      
          Ok(())
      }
      ```
      
      ### Account Reallocation
      
      Resize account data:
      
      ```rust
      use solana_program::program::invoke;
      
      fn reallocate_account(
          account: &AccountInfo,
          payer: &AccountInfo,
          new_size: usize,
          program_id: &Pubkey,
      ) -> ProgramResult {
          // Verify ownership
          if account.owner != program_id {
              return Err(ProgramError::IncorrectProgramId);
          }
      
          // Reallocate
          account.realloc(new_size, false)?;
      
          // Fund additional rent if needed
          let rent = Rent::get()?;
          let new_minimum_balance = rent.minimum_balance(new_size);
          let current_balance = account.lamports();
      
          if *current_balance < new_minimum_balance {
              let additional = new_minimum_balance - *current_balance;
      
              **payer.lamports.borrow_mut() -= additional;
              **account.lamports.borrow_mut() += additional;
          }
      
          Ok(())
      }
      ```
      
      ---
      
      ## Manual CPI Patterns
      
      ### Using invoke
      
      For CPIs without PDA signers:
      
      ```rust
      use solana_program::{
          account_info::AccountInfo,
          instruction::{AccountMeta, Instruction},
          program::invoke,
          pubkey::Pubkey,
          system_instruction,
      };
      
      fn transfer_sol(
          from: &AccountInfo,
          to: &AccountInfo,
          system_program: &AccountInfo,
          amount: u64,
      ) -> ProgramResult {
          invoke(
              &system_instruction::transfer(from.key, to.key, amount),
              &[from.clone(), to.clone(), system_program.clone()],
          )
      }
      ```
      
      ### Using invoke_signed
      
      For CPIs with PDA signers:
      
      ```rust
      use solana_program::program::invoke_signed;
      
      fn pda_transfer(
          pda: &AccountInfo,
          recipient: &AccountInfo,
          system_program: &AccountInfo,
          amount: u64,
          seeds: &[&[u8]],
          bump: u8,
      ) -> ProgramResult {
          let bump_seed = &[bump];
          let signer_seeds: &[&[&[u8]]] = &[
              &[seeds, &[bump_seed]].concat()
          ];
      
          invoke_signed(
              &system_instruction::transfer(pda.key, recipient.key, amount),
              &[pda.clone(), recipient.clone(), system_program.clone()],
              signer_seeds,
          )
      }
      ```
      
      ### Building AccountMeta Arrays
      
      Manually construct account metadata:
      
      ```rust
      use solana_program::instruction::AccountMeta;
      
      let account_metas = vec![
          AccountMeta::new(*writable_account.key, false),        // Writable, not signer
          AccountMeta::new(*writable_signer.key, true),          // Writable, signer
          AccountMeta::new_readonly(*readonly_account.key, false), // Read-only, not signer
          AccountMeta::new_readonly(*readonly_signer.key, true),  // Read-only, signer
      ];
      ```
      
      ### Creating Instruction Structs
      
      Build instructions for CPI:
      
      ```rust
      use solana_program::instruction::Instruction;
      
      fn build_custom_instruction(
          program_id: &Pubkey,
          account1: &Pubkey,
          account2: &Pubkey,
          data: Vec<u8>,
      ) -> Instruction {
          Instruction {
              program_id: *program_id,
              accounts: vec![
                  AccountMeta::new(*account1, true),
                  AccountMeta::new(*account2, false),
              ],
              data,
          }
      }
      
      // Use in CPI
      fn call_custom_program(
          program: &AccountInfo,
          account1: &AccountInfo,
          account2: &AccountInfo,
          data: Vec<u8>,
      ) -> ProgramResult {
          let instruction = build_custom_instruction(
              program.key,
              account1.key,
              account2.key,
              data,
          );
      
          invoke(
              &instruction,
              &[account1.clone(), account2.clone()],
          )
      }
      ```
      
      ### SPL Token CPI Pattern
      
      Transfer tokens via CPI:
      
      ```rust
      use spl_token::instruction as token_instruction;
      
      fn transfer_tokens(
          token_program: &AccountInfo,
          source: &AccountInfo,
          destination: &AccountInfo,
          authority: &AccountInfo,
          amount: u64,
      ) -> ProgramResult {
          invoke(
              &token_instruction::transfer(
                  token_program.key,
                  source.key,
                  destination.key,
                  authority.key,
                  &[],  // No multisig signers
                  amount,
              )?,
              &[source.clone(), destination.clone(), authority.clone()],
          )
      }
      
      fn transfer_tokens_with_pda(
          token_program: &AccountInfo,
          source: &AccountInfo,
          destination: &AccountInfo,
          pda_authority: &AccountInfo,
          amount: u64,
          seeds: &[&[u8]],
          bump: u8,
      ) -> ProgramResult {
          let bump_seed = &[bump];
          let signer_seeds: &[&[&[u8]]] = &[
              &[seeds, &[bump_seed]].concat()
          ];
      
          invoke_signed(
              &token_instruction::transfer(
                  token_program.key,
                  source.key,
                  destination.key,
                  pda_authority.key,
                  &[],
                  amount,
              )?,
              &[source.clone(), destination.clone(), pda_authority.clone()],
              signer_seeds,
          )
      }
      ```
      
      ---
      
      ## Build and Deploy Workflow
      
      ### cargo build-sbf Command
      
      Build the program for Solana:
      
      ```bash
      # Basic build
      cargo build-sbf
      
      # Build with specific Solana version
      cargo build-sbf --solana-version 2.1.0
      
      # Build for mainnet (with optimizations)
      cargo build-sbf --release
      
      # Specify output directory
      cargo build-sbf --sbf-out-dir ./output
      ```
      
      ### Understanding .so and -keypair.json Files
      
      After building:
      
      ```
      target/deploy/
      ├── my_program.so              # Compiled program binary
      └── my_program-keypair.json    # Program's keypair (address)
      ```
      
      **Program ID:**
      
      ```bash
      # Get program ID from keypair
      solana address -k target/deploy/my_program-keypair.json
      ```
      
      **Update Program ID in Code:**
      
      ```rust
      // In lib.rs
      declare_id!("YourProgramID11111111111111111111111111111");
      ```
      
      ### solana program deploy Commands
      
      **Deploy to Devnet:**
      
      ```bash
      # Set cluster
      solana config set --url devnet
      
      # Fund deployer account
      solana airdrop 2
      
      # Deploy program
      solana program deploy target/deploy/my_program.so
      
      # Deploy to specific program ID
      solana program deploy \
          target/deploy/my_program.so \
          --program-id target/deploy/my_program-keypair.json
      
      # Deploy with custom keypair
      solana program deploy \
          target/deploy/my_program.so \
          --program-id custom-keypair.json \
          --upgrade-authority ~/.config/solana/id.json
      ```
      
      **Deploy to Mainnet:**
      
      ```bash
      solana config set --url mainnet-beta
      
      # Deploy (costs SOL based on program size)
      solana program deploy target/deploy/my_program.so
      ```
      
      ### Program Size and Cost Calculation
      
      **Check Program Size:**
      
      ```bash
      ls -lh target/deploy/my_program.so
      
      # Or get detailed info
      solana program show <PROGRAM_ID>
      ```
      
      **Calculate Deployment Cost:**
      
      Program cost formula: `rent_exemption(program_size)`
      
      ```bash
      # Get rent for specific size
      solana rent <SIZE_IN_BYTES>
      
      # Example for 200KB program
      solana rent 204800
      # Output: Rent-exempt minimum: 1.42607328 SOL
      ```
      
      **Typical Sizes:**
      - Simple programs: 50-100 KB
      - Medium programs: 100-300 KB
      - Large programs: 300-500 KB
      - Maximum: ~1 MB (hard limit)
      
      **Reduce Program Size:**
      
      ```toml
      # In Cargo.toml
      [profile.release]
      opt-level = "z"        # Optimize for size
      lto = true            # Link-time optimization
      codegen-units = 1     # Better optimization
      strip = true          # Strip symbols
      ```
      
      ---
      
      ## Testing with Mollusk
      
      ### Test Structure with mollusk-svm
      
      Basic test setup:
      
      ```rust
      #[cfg(test)]
      mod tests {
          use {
              mollusk_svm::Mollusk,
              solana_sdk::{
                  account::Account,
                  instruction::{AccountMeta, Instruction},
                  pubkey::Pubkey,
              },
          };
      
          #[test]
          fn test_initialize() {
              // Create Mollusk instance
              let program_id = Pubkey::new_unique();
              let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
      
              // Test implementation...
          }
      }
      ```
      
      ### Creating Test Accounts
      
      **System-Owned Account:**
      
      ```rust
      let user = Pubkey::new_unique();
      let user_account = Account {
          lamports: 1_000_000,
          data: vec![],
          owner: solana_sdk::system_program::id(),
          executable: false,
          rent_epoch: 0,
      };
      ```
      
      **Program-Owned Account:**
      
      ```rust
      let state_account = Pubkey::new_unique();
      let state = Account {
          lamports: rent_lamports,
          data: vec![0; MyState::LEN],
          owner: program_id,
          executable: false,
          rent_epoch: 0,
      };
      ```
      
      **Pre-Initialized Account:**
      
      ```rust
      use borsh::BorshSerialize;
      
      let mut data = vec![0; MyState::LEN];
      let initial_state = MyState {
          is_initialized: true,
          counter: 42,
          authority: user,
      };
      initial_state.serialize(&mut data.as_mut_slice()).unwrap();
      
      let initialized_account = Account {
          lamports: rent_lamports,
          data,
          owner: program_id,
          executable: false,
          rent_epoch: 0,
      };
      ```
      
      ### Process Instructions and Validate Results
      
      **Basic Process and Check:**
      
      ```rust
      use mollusk_svm::result::Check;
      
      #[test]
      fn test_instruction() {
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
      
          let user = Pubkey::new_unique();
          let instruction = Instruction::new_with_bytes(
              program_id,
              &[0],  // Instruction data
              vec![
                  AccountMeta::new(user, true),
              ],
          );
      
          let accounts = vec![
              (user, Account {
                  lamports: 1_000_000,
                  data: vec![],
                  owner: solana_sdk::system_program::id(),
                  executable: false,
                  rent_epoch: 0,
              }),
          ];
      
          let checks = vec![
              Check::success(),
              Check::account(&user)
                  .lamports(1_000_000)
                  .build(),
          ];
      
          mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);
      }
      ```
      
      **Validate Account Data:**
      
      ```rust
      let expected_data = MyState {
          is_initialized: true,
          counter: 10,
          authority: user,
      }.try_to_vec().unwrap();
      
      let checks = vec![
          Check::success(),
          Check::account(&state_account)
              .data(&expected_data)
              .lamports(rent_lamports)
              .owner(&program_id)
              .build(),
      ];
      ```
      
      **Check Specific Data Slice:**
      
      ```rust
      let checks = vec![
          Check::success(),
          Check::account(&account)
              .data_slice(0, &[1])  // Check first byte is 1 (initialized)
              .data_slice(8, &10u64.to_le_bytes())  // Check counter at offset 8
              .build(),
      ];
      ```
      
      **Test Error Conditions:**
      
      ```rust
      use solana_sdk::instruction::InstructionError;
      
      let checks = vec![
          Check::instruction_err(InstructionError::InvalidInstructionData),
      ];
      
      mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);
      ```
      
      ### Compute Unit Benchmarking
      
      **Basic Benchmark:**
      
      ```rust
      use mollusk_svm_bencher::MolluskComputeUnitBencher;
      
      fn main() {
          let program_id = Pubkey::new_unique();
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
      
          let instruction = /* build instruction */;
          let accounts = /* setup accounts */;
      
          MolluskComputeUnitBencher::new(mollusk)
              .bench(("my_instruction", &instruction, &accounts))
              .must_pass(true)
              .out_dir("./benches")
              .execute();
      }
      ```
      
      **Run Benchmark:**
      
      ```bash
      # Build first
      cargo build-sbf
      
      # Run benchmark
      cargo run --bin bench
      ```
      
      **Benchmark Output:**
      
      ```
      ╭──────────────────────────────┬────────────────────╮
      │ Instruction                  │ Compute Units      │
      ├──────────────────────────────┼────────────────────┤
      │ my_instruction               │ 1,234              │
      ╰──────────────────────────────┴────────────────────╯
      
      Results written to: ./benches/compute_units.json
      ```
      
      ---
      
      ## Verified Builds
      
      ### solana-verify Workflow
      
      Verify programs on-chain match source code:
      
      **Install solana-verify:**
      
      ```bash
      cargo install solana-verify
      ```
      
      **Verify a Program:**
      
      ```bash
      # Verify remote build
      solana-verify verify-from-repo \
          --program-id <PROGRAM_ID> \
          --remote https://github.com/user/repo \
          --commit-hash <COMMIT_HASH> \
          --library-name program_name
      
      # Verify with mount path (for workspace)
      solana-verify verify-from-repo \
          --program-id <PROGRAM_ID> \
          --remote https://github.com/user/repo \
          --commit-hash <COMMIT_HASH> \
          --mount-path programs/my-program \
          --library-name my_program
      ```
      
      ### Docker-Based Builds
      
      Build in Docker for reproducibility:
      
      **Dockerfile:**
      
      ```dockerfile
      FROM --platform=linux/amd64 projectserum/build:v0.29.0
      
      WORKDIR /build
      COPY . .
      
      RUN cargo build-sbf --release
      ```
      
      **Build Command:**
      
      ```bash
      docker build -t my-program-build .
      docker create --name extract my-program-build
      docker cp extract:/build/target/deploy/my_program.so ./my_program-verifiable.so
      docker rm extract
      ```
      
      **Verify Deterministic:**
      
      ```bash
      # Compare hashes
      sha256sum target/deploy/my_program.so
      sha256sum my_program-verifiable.so
      # Should match!
      ```
      
      ### Buffer Uploads for Multisig
      
      Deploy via buffer for multisig upgrade authority:
      
      ```bash
      # Write program to buffer
      solana program write-buffer target/deploy/my_program.so
      
      # Output: Buffer: <BUFFER_ADDRESS>
      
      # Set buffer authority to multisig
      solana program set-buffer-authority <BUFFER_ADDRESS> --new-buffer-authority <MULTISIG_ADDRESS>
      
      # Later: Deploy from buffer (requires multisig)
      solana program deploy --buffer <BUFFER_ADDRESS> --program-id <PROGRAM_ID>
      ```
      
      **Squads Multisig Example:**
      
      ```bash
      # 1. Write buffer
      BUFFER=$(solana program write-buffer target/deploy/my_program.so | grep "Buffer:" | awk '{print $2}')
      
      # 2. Transfer buffer authority to Squads
      solana program set-buffer-authority $BUFFER --new-buffer-authority <SQUADS_ADDRESS>
      
      # 3. Create proposal in Squads UI to deploy from buffer
      ```
      
      ---
      
      ## Program Management
      
      ### solana program show
      
      Get program information:
      
      ```bash
      # Show program details
      solana program show <PROGRAM_ID>
      
      # Output:
      # Program Id: <PROGRAM_ID>
      # Owner: BPFLoaderUpgradeab1e11111111111111111111111
      # ProgramData Address: <DATA_ADDRESS>
      # Authority: <UPGRADE_AUTHORITY>
      # Last Deployed In Slot: 123456789
      # Data Length: 204800 bytes
      # Balance: 1.42607328 SOL
      ```
      
      **Show Program Data:**
      
      ```bash
      # Get upgrade authority
      solana program show <PROGRAM_ID> | grep Authority
      
      # Get program size
      solana program show <PROGRAM_ID> | grep "Data Length"
      ```
      
      ### Authority Transfers
      
      **Transfer Upgrade Authority:**
      
      ```bash
      # Transfer to new authority
      solana program set-upgrade-authority \
          <PROGRAM_ID> \
          --new-upgrade-authority <NEW_AUTHORITY>
      
      # Transfer to multisig
      solana program set-upgrade-authority \
          <PROGRAM_ID> \
          --new-upgrade-authority <MULTISIG_ADDRESS>
      ```
      
      ### Making Programs Immutable
      
      Remove upgrade authority to make program immutable:
      
      ```bash
      # Make immutable (IRREVERSIBLE!)
      solana program set-upgrade-authority <PROGRAM_ID> --final
      
      # Verify immutability
      solana program show <PROGRAM_ID>
      # Authority: none
      ```
      
      **Warning:** This is permanent. The program can never be upgraded again.
      
      ### Closing Programs
      
      Reclaim rent from closed programs:
      
      ```bash
      # Close program and reclaim rent
      solana program close <PROGRAM_ID>
      
      # Close and send rent to specific recipient
      solana program close <PROGRAM_ID> --recipient <RECIPIENT_ADDRESS>
      
      # Close program buffer
      solana program close --buffers
      ```
      
      **Requirements:**
      - Must be upgrade authority
      - Program must not be marked as final
      - Recipient receives all lamports from program account
      
      ---
      
      ## Common Native Patterns
      
      ### PDA Derivation and Signing
      
      **Find PDA:**
      
      ```rust
      use solana_program::pubkey::Pubkey;
      
      fn get_user_pda(user: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
          Pubkey::find_program_address(
              &[
                  b"user",
                  user.as_ref(),
              ],
              program_id,
          )
      }
      ```
      
      **Verify PDA:**
      
      ```rust
      fn validate_pda(
          pda: &AccountInfo,
          seeds: &[&[u8]],
          bump: u8,
          program_id: &Pubkey,
      ) -> ProgramResult {
          let expected_pda = Pubkey::create_program_address(
              &[seeds, &[&[bump]]].concat(),
              program_id,
          )?;
      
          if pda.key != &expected_pda {
              return Err(ProgramError::InvalidSeeds);
          }
      
          Ok(())
      }
      ```
      
      **Sign with PDA:**
      
      ```rust
      use solana_program::program::invoke_signed;
      
      fn pda_invoke(
          instruction: &Instruction,
          accounts: &[AccountInfo],
          user: &Pubkey,
          bump: u8,
      ) -> ProgramResult {
          let signer_seeds: &[&[&[u8]]] = &[
              &[b"user", user.as_ref(), &[bump]]
          ];
      
          invoke_signed(instruction, accounts, signer_seeds)
      }
      ```
      
      ### Rent Calculation
      
      **Calculate Minimum Balance:**
      
      ```rust
      use solana_program::{
          rent::Rent,
          sysvar::Sysvar,
      };
      
      fn get_rent_exempt_balance(data_len: usize) -> Result<u64, ProgramError> {
          let rent = Rent::get()?;
          Ok(rent.minimum_balance(data_len))
      }
      ```
      
      **Check if Rent Exempt:**
      
      ```rust
      fn is_rent_exempt(account: &AccountInfo) -> Result<bool, ProgramError> {
          let rent = Rent::get()?;
          Ok(rent.is_exempt(account.lamports(), account.data_len()))
      }
      ```
      
      ### Lamport Transfers
      
      **Direct Transfer (modify lamports):**
      
      ```rust
      fn transfer_lamports(
          from: &AccountInfo,
          to: &AccountInfo,
          amount: u64,
      ) -> ProgramResult {
          // Borrow and update lamports
          **from.try_borrow_mut_lamports()? -= amount;
          **to.try_borrow_mut_lamports()? += amount;
      
          Ok(())
      }
      ```
      
      **Via System Program:**
      
      ```rust
      use solana_program::{
          program::invoke,
          system_instruction,
      };
      
      fn transfer_via_system_program(
          from: &AccountInfo,
          to: &AccountInfo,
          system_program: &AccountInfo,
          amount: u64,
      ) -> ProgramResult {
          invoke(
              &system_instruction::transfer(from.key, to.key, amount),
              &[from.clone(), to.clone(), system_program.clone()],
          )
      }
      ```
      
      ### Error Handling with ProgramError
      
      **Using Built-in Errors:**
      
      ```rust
      use solana_program::program_error::ProgramError;
      
      if !account.is_signer {
          return Err(ProgramError::MissingRequiredSignature);
      }
      
      if account.owner != program_id {
          return Err(ProgramError::IncorrectProgramId);
      }
      
      if account.data_len() < MyState::LEN {
          return Err(ProgramError::AccountDataTooSmall);
      }
      ```
      
      **Custom Errors:**
      
      ```rust
      use solana_program::program_error::ProgramError;
      use thiserror::Error;
      
      #[derive(Error, Debug, Copy, Clone)]
      pub enum MyError {
          #[error("Account already initialized")]
          AlreadyInitialized,
      
          #[error("Invalid authority")]
          InvalidAuthority,
      
          #[error("Arithmetic overflow")]
          Overflow,
      }
      
      impl From<MyError> for ProgramError {
          fn from(e: MyError) -> Self {
              ProgramError::Custom(e as u32)
          }
      }
      
      // Usage
      if state.is_initialized {
          return Err(MyError::AlreadyInitialized.into());
      }
      ```
      
      **With num_derive:**
      
      ```rust
      use num_derive::FromPrimitive;
      use num_traits::FromPrimitive;
      use solana_program::{
          decode_error::DecodeError,
          program_error::{PrintProgramError, ProgramError},
      };
      use thiserror::Error;
      
      #[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
      pub enum MyError {
          #[error("Already initialized")]
          AlreadyInitialized,
      
          #[error("Invalid authority")]
          InvalidAuthority,
      }
      
      impl From<MyError> for ProgramError {
          fn from(e: MyError) -> Self {
              ProgramError::Custom(e as u32)
          }
      }
      
      impl<T> DecodeError<T> for MyError {
          fn type_of() -> &'static str {
              "MyError"
          }
      }
      
      impl PrintProgramError for MyError {
          fn print<E>(&self)
          where
              E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
          {
              match self {
                  MyError::AlreadyInitialized => msg!("Error: Already initialized"),
                  MyError::InvalidAuthority => msg!("Error: Invalid authority"),
              }
          }
      }
      ```
      
      ### Logging and Debugging
      
      **Basic Logging:**
      
      ```rust
      use solana_program::msg;
      
      msg!("Processing instruction");
      msg!("Counter value: {}", counter);
      msg!("Account: {}, balance: {}", account.key, account.lamports());
      ```
      
      **Compute Units Logging:**
      
      ```rust
      use solana_program::log::sol_log_compute_units;
      
      sol_log_compute_units();  // Log current compute units used
      ```
      
      **Data Logging:**
      
      ```rust
      use solana_program::log::sol_log_data;
      
      // Log data for off-chain processing
      sol_log_data(&[b"event", &event_data]);
      ```
      
      ### Clock Access
      
      Get current timestamp and slot:
      
      ```rust
      use solana_program::{
          clock::Clock,
          sysvar::Sysvar,
      };
      
      fn get_current_time() -> Result<i64, ProgramError> {
          let clock = Clock::get()?;
          Ok(clock.unix_timestamp)
      }
      
      fn get_current_slot() -> Result<u64, ProgramError> {
          let clock = Clock::get()?;
          Ok(clock.slot)
      }
      ```
      
      ### Account Closure Pattern
      
      Properly close accounts and reclaim rent:
      
      ```rust
      fn close_account(
          account_to_close: &AccountInfo,
          destination: &AccountInfo,
      ) -> ProgramResult {
          // Transfer all lamports
          let dest_starting_lamports = destination.lamports();
          **destination.lamports.borrow_mut() = dest_starting_lamports
              .checked_add(account_to_close.lamports())
              .ok_or(ProgramError::ArithmeticOverflow)?;
      
          **account_to_close.lamports.borrow_mut() = 0;
      
          // Zero out data
          let mut data = account_to_close.try_borrow_mut_data()?;
          data.fill(0);
      
          Ok(())
      }
      ```
      
      ### Discriminator Pattern
      
      Add discriminator to distinguish account types:
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub enum AccountType {
          Uninitialized,
          User,
          Config,
      }
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct UserAccount {
          pub account_type: AccountType,  // Discriminator
          pub authority: Pubkey,
          pub balance: u64,
      }
      
      impl UserAccount {
          pub const LEN: usize = 1 + 32 + 8;
      
          pub fn validate_type(account: &AccountInfo) -> ProgramResult {
              let data = account.try_borrow_data()?;
              let account_type = AccountType::try_from_slice(&data[..1])?;
      
              match account_type {
                  AccountType::User => Ok(()),
                  _ => Err(ProgramError::InvalidAccountData),
              }
          }
      }
      ```
      
      ---
      
      ## Additional Resources
      
      - **Solana Program Examples**: https://github.com/solana-developers/program-examples
      - **Mollusk Testing**: https://github.com/anza-xyz/mollusk
      - **solana-program Docs**: https://docs.rs/solana-program
      - **Solana Cookbook**: https://solanacookbook.com/
      - **SPL Token**: https://spl.solana.com/token
      - **Solana Verify**: https://github.com/Ellipsis-Labs/solana-verifiable-build
      
      ---
      
      *This reference focuses on native Rust implementation patterns. For conceptual understanding of Solana primitives (PDAs, CPIs, accounts, etc.), see the other reference files in this directory.*
      
    • native-security.md 27.6 KB
      # Native Rust Security Patterns for Solana Programs
      
      This reference covers security vulnerabilities and best practices specific to Solana programs built with native Rust (without Anchor framework).
      
      ## Table of Contents
      
      1. [Manual Account Validation](#manual-account-validation)
      2. [Account Discriminator Patterns](#account-discriminator-patterns)
      3. [PDA Security in Native Rust](#pda-security-in-native-rust)
      4. [Manual CPI Security](#manual-cpi-security)
      5. [Manual Serialization Security](#manual-serialization-security)
      6. [Rent and Space Management](#rent-and-space-management)
      7. [Error Handling in Native Rust](#error-handling-in-native-rust)
      8. [Token Program Integration](#token-program-integration)
      9. [Low-Level Security Patterns](#low-level-security-patterns)
      10. [Native Rust Best Practices](#native-rust-best-practices)
      
      ---
      
      ## Manual Account Validation
      
      In native Rust programs, ALL account validation must be performed manually. Missing any check can lead to critical vulnerabilities.
      
      ### Signer Checks
      
      **Vulnerable:**
      ```rust
      pub fn process_instruction(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          _instruction_data: &[u8],
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let authority = next_account_info(account_info_iter)?;
      
          // Missing signer check - anyone can call this!
          // Perform privileged operation
          Ok(())
      }
      ```
      
      **Secure:**
      ```rust
      pub fn process_instruction(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          _instruction_data: &[u8],
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let authority = next_account_info(account_info_iter)?;
      
          if !authority.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          // Now safe to perform privileged operation
          Ok(())
      }
      ```
      
      ### Owner Validation
      
      **Vulnerable:**
      ```rust
      pub fn update_config(accounts: &[AccountInfo]) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let config_account = next_account_info(account_info_iter)?;
      
          // Missing owner check - could be any account!
          let mut config_data = Config::try_from_slice(&config_account.data.borrow())?;
          config_data.value = 42;
          config_data.serialize(&mut *config_account.data.borrow_mut())?;
      
          Ok(())
      }
      ```
      
      **Secure:**
      ```rust
      pub fn update_config(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let config_account = next_account_info(account_info_iter)?;
      
          // Verify this account is owned by our program
          if config_account.owner != program_id {
              return Err(ProgramError::IncorrectProgramId);
          }
      
          let mut config_data = Config::try_from_slice(&config_account.data.borrow())?;
          config_data.value = 42;
          config_data.serialize(&mut *config_account.data.borrow_mut())?;
      
          Ok(())
      }
      ```
      
      ### Writable Checks
      
      **Vulnerable:**
      ```rust
      pub fn transfer_tokens(accounts: &[AccountInfo]) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let source = next_account_info(account_info_iter)?;
      
          // Missing writable check - runtime will panic!
          let mut data = source.try_borrow_mut_data()?;
          // Modify data...
          Ok(())
      }
      ```
      
      **Secure:**
      ```rust
      pub fn transfer_tokens(accounts: &[AccountInfo]) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let source = next_account_info(account_info_iter)?;
      
          if !source.is_writable {
              return Err(ProgramError::InvalidAccountData);
          }
      
          let mut data = source.try_borrow_mut_data()?;
          // Safe to modify data
          Ok(())
      }
      ```
      
      ### Comprehensive Validation Function
      
      **Best Practice:**
      ```rust
      pub struct AccountValidation<'a, 'info> {
          account: &'a AccountInfo<'info>,
      }
      
      impl<'a, 'info> AccountValidation<'a, 'info> {
          pub fn new(account: &'a AccountInfo<'info>) -> Self {
              Self { account }
          }
      
          pub fn owner(self, expected_owner: &Pubkey) -> Result<Self, ProgramError> {
              if self.account.owner != expected_owner {
                  return Err(ProgramError::IncorrectProgramId);
              }
              Ok(self)
          }
      
          pub fn signer(self) -> Result<Self, ProgramError> {
              if !self.account.is_signer {
                  return Err(ProgramError::MissingRequiredSignature);
              }
              Ok(self)
          }
      
          pub fn writable(self) -> Result<Self, ProgramError> {
              if !self.account.is_writable {
                  return Err(ProgramError::InvalidAccountData);
              }
              Ok(self)
          }
      
          pub fn key(self, expected_key: &Pubkey) -> Result<Self, ProgramError> {
              if self.account.key != expected_key {
                  return Err(ProgramError::InvalidAccountData);
              }
              Ok(self)
          }
      
          pub fn initialized(self) -> Result<Self, ProgramError> {
              if self.account.data_is_empty() {
                  return Err(ProgramError::UninitializedAccount);
              }
              Ok(self)
          }
      }
      
      // Usage:
      pub fn process(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let authority = next_account_info(account_info_iter)?;
          let config = next_account_info(account_info_iter)?;
      
          AccountValidation::new(authority)
              .signer()?;
      
          AccountValidation::new(config)
              .owner(program_id)?
              .writable()?
              .initialized()?;
      
          // All validations passed
          Ok(())
      }
      ```
      
      ---
      
      ## Account Discriminator Patterns
      
      Without Anchor's automatic discriminators, you must manually implement account type safety.
      
      ### Why Discriminators Matter
      
      **Vulnerable:**
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct ConfigAccount {
          pub admin: Pubkey,
          pub value: u64,
      }
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct UserAccount {
          pub owner: Pubkey,
          pub balance: u64,
      }
      
      pub fn update_config(accounts: &[AccountInfo]) -> ProgramResult {
          let config = next_account_info(&mut accounts.iter())?;
      
          // No discriminator check - UserAccount has same layout!
          let mut data = ConfigAccount::try_from_slice(&config.data.borrow())?;
          data.value = 999;
          // Could be writing to a UserAccount!
      
          Ok(())
      }
      ```
      
      ### Implementing Discriminators
      
      **Secure:**
      ```rust
      use borsh::{BorshDeserialize, BorshSerialize};
      
      pub const CONFIG_DISCRIMINATOR: u64 = 0x1234567890ABCDEF;
      pub const USER_DISCRIMINATOR: u64 = 0xFEDCBA0987654321;
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct ConfigAccount {
          pub discriminator: u64,
          pub admin: Pubkey,
          pub value: u64,
      }
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct UserAccount {
          pub discriminator: u64,
          pub owner: Pubkey,
          pub balance: u64,
      }
      
      impl ConfigAccount {
          pub const LEN: usize = 8 + 32 + 8;
      
          pub fn new(admin: Pubkey, value: u64) -> Self {
              Self {
                  discriminator: CONFIG_DISCRIMINATOR,
                  admin,
                  value,
              }
          }
      
          pub fn from_account_info(account: &AccountInfo) -> Result<Self, ProgramError> {
              let data = account.data.borrow();
              if data.len() < 8 {
                  return Err(ProgramError::InvalidAccountData);
              }
      
              let discriminator = u64::from_le_bytes(data[0..8].try_into().unwrap());
              if discriminator != CONFIG_DISCRIMINATOR {
                  return Err(ProgramError::InvalidAccountData);
              }
      
              Self::try_from_slice(&data).map_err(|_| ProgramError::InvalidAccountData)
          }
      }
      
      pub fn update_config(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let config_account = next_account_info(&mut accounts.iter())?;
      
          // Discriminator validated during deserialization
          let mut config = ConfigAccount::from_account_info(config_account)?;
          config.value = 999;
          config.serialize(&mut *config_account.data.borrow_mut())?;
      
          Ok(())
      }
      ```
      
      ### Alternative: String-Based Discriminators
      
      ```rust
      pub const ACCOUNT_TYPE_LEN: usize = 8;
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct TaggedAccount {
          pub account_type: [u8; ACCOUNT_TYPE_LEN], // "CONFIG\0\0"
          pub data: AccountData,
      }
      
      impl TaggedAccount {
          pub fn new_config(data: AccountData) -> Self {
              let mut account_type = [0u8; ACCOUNT_TYPE_LEN];
              account_type[..6].copy_from_slice(b"CONFIG");
              Self { account_type, data }
          }
      
          pub fn assert_config(&self) -> ProgramResult {
              let mut expected = [0u8; ACCOUNT_TYPE_LEN];
              expected[..6].copy_from_slice(b"CONFIG");
      
              if self.account_type != expected {
                  return Err(ProgramError::InvalidAccountData);
              }
              Ok(())
          }
      }
      ```
      
      ---
      
      ## PDA Security in Native Rust
      
      ### find_program_address vs create_program_address
      
      **Vulnerable:**
      ```rust
      pub fn init_pda(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          bump: u8,
      ) -> ProgramResult {
          let pda_account = next_account_info(&mut accounts.iter())?;
      
          // Using user-provided bump without validation!
          let pda = Pubkey::create_program_address(
              &[b"config", &[bump]],
              program_id,
          )?;
      
          if pda_account.key != &pda {
              return Err(ProgramError::InvalidAccountData);
          }
      
          // Attacker could find non-canonical bump
          Ok(())
      }
      ```
      
      **Secure:**
      ```rust
      pub fn init_pda(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let pda_account = next_account_info(&mut accounts.iter())?;
      
          // Always use find_program_address to get canonical bump
          let (pda, bump) = Pubkey::find_program_address(
              &[b"config"],
              program_id,
          );
      
          if pda_account.key != &pda {
              return Err(ProgramError::InvalidAccountData);
          }
      
          // Store the canonical bump for later use
          let mut data = ConfigPda::new(bump);
          data.serialize(&mut *pda_account.data.borrow_mut())?;
      
          Ok(())
      }
      ```
      
      ### Storing and Using Canonical Bumps
      
      **Best Practice:**
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct VaultPda {
          pub discriminator: u64,
          pub bump: u8,
          pub authority: Pubkey,
          pub balance: u64,
      }
      
      impl VaultPda {
          pub fn seeds<'a>(&'a self, authority: &'a Pubkey) -> [&'a [u8]; 3] {
              [b"vault", authority.as_ref(), &[self.bump]]
          }
      
          pub fn verify_pda(
              &self,
              pda_account: &AccountInfo,
              authority: &Pubkey,
              program_id: &Pubkey,
          ) -> ProgramResult {
              let expected_pda = Pubkey::create_program_address(
                  &self.seeds(authority),
                  program_id,
              )?;
      
              if pda_account.key != &expected_pda {
                  return Err(ProgramError::InvalidSeeds);
              }
      
              Ok(())
          }
      }
      ```
      
      ### PDA Signing with invoke_signed
      
      **Secure Pattern:**
      ```rust
      use solana_program::program::invoke_signed;
      
      pub fn transfer_from_pda(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let vault_pda = next_account_info(account_info_iter)?;
          let destination = next_account_info(account_info_iter)?;
          let authority = next_account_info(account_info_iter)?;
          let system_program = next_account_info(account_info_iter)?;
      
          // Load and validate PDA data
          let vault = VaultPda::from_account_info(vault_pda)?;
          vault.verify_pda(vault_pda, authority.key, program_id)?;
      
          // Sign with PDA's seeds
          let seeds = vault.seeds(authority.key);
          let signer_seeds = &[&seeds[..]];
      
          let ix = solana_program::system_instruction::transfer(
              vault_pda.key,
              destination.key,
              amount,
          );
      
          invoke_signed(
              &ix,
              &[vault_pda.clone(), destination.clone(), system_program.clone()],
              signer_seeds,
          )?;
      
          Ok(())
      }
      ```
      
      ### Preventing PDA Substitution
      
      **Vulnerable:**
      ```rust
      pub fn withdraw(accounts: &[AccountInfo]) -> ProgramResult {
          let vault = next_account_info(&mut accounts.iter())?;
      
          // No validation that this is the CORRECT vault PDA
          let vault_data = VaultPda::from_account_info(vault)?;
      
          // Attacker could substitute a different vault!
          Ok(())
      }
      ```
      
      **Secure:**
      ```rust
      pub fn withdraw(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let vault = next_account_info(account_info_iter)?;
          let authority = next_account_info(account_info_iter)?;
      
          // Derive expected PDA
          let (expected_vault, _bump) = Pubkey::find_program_address(
              &[b"vault", authority.key.as_ref()],
              program_id,
          );
      
          // Validate this is the correct PDA
          if vault.key != &expected_vault {
              return Err(ProgramError::InvalidAccountData);
          }
      
          let vault_data = VaultPda::from_account_info(vault)?;
          // Safe to proceed
      
          Ok(())
      }
      ```
      
      ---
      
      ## Manual CPI Security
      
      ### Building AccountMeta Arrays Securely
      
      **Vulnerable:**
      ```rust
      pub fn dangerous_cpi(accounts: &[AccountInfo]) -> ProgramResult {
          let target_program = next_account_info(&mut accounts.iter())?;
          let account1 = next_account_info(&mut accounts.iter())?;
      
          // Missing validation - could be any program!
          let ix = Instruction {
              program_id: *target_program.key,
              accounts: vec![
                  AccountMeta::new(*account1.key, false), // Wrong signer flag!
              ],
              data: vec![],
          };
      
          invoke(&ix, &[target_program.clone(), account1.clone()])?;
          Ok(())
      }
      ```
      
      **Secure:**
      ```rust
      use solana_program::program::invoke;
      
      pub const EXPECTED_PROGRAM_ID: Pubkey = solana_program::pubkey!("YourProgramID111111111111111111111111111111");
      
      pub fn secure_cpi(accounts: &[AccountInfo]) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let target_program = next_account_info(account_info_iter)?;
          let account1 = next_account_info(account_info_iter)?;
      
          // Validate target program ID
          if target_program.key != &EXPECTED_PROGRAM_ID {
              return Err(ProgramError::IncorrectProgramId);
          }
      
          // Correctly propagate signer/writable flags
          let account_metas = vec![
              AccountMeta {
                  pubkey: *account1.key,
                  is_signer: account1.is_signer,
                  is_writable: account1.is_writable,
              },
          ];
      
          let ix = Instruction {
              program_id: *target_program.key,
              accounts: account_metas,
              data: vec![],
          };
      
          invoke(&ix, &[target_program.clone(), account1.clone()])?;
          Ok(())
      }
      ```
      
      ### Checking CPI Success
      
      **Best Practice:**
      ```rust
      pub fn cpi_with_validation(accounts: &[AccountInfo]) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let token_program = next_account_info(account_info_iter)?;
          let source = next_account_info(account_info_iter)?;
          let destination = next_account_info(account_info_iter)?;
          let authority = next_account_info(account_info_iter)?;
      
          // Get balances before CPI
          let source_before = source.lamports();
          let dest_before = destination.lamports();
      
          let ix = spl_token::instruction::transfer(
              token_program.key,
              source.key,
              destination.key,
              authority.key,
              &[],
              1000,
          )?;
      
          invoke(&ix, &[source.clone(), destination.clone(), authority.clone()])?;
      
          // Verify state changed as expected (for native SOL transfers)
          // Note: For SPL tokens, you'd need to deserialize token accounts
      
          Ok(())
      }
      ```
      
      ---
      
      ## Manual Serialization Security
      
      ### Borsh Serialization Pitfalls
      
      **Vulnerable:**
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct Config {
          pub value: u64,
          pub items: Vec<Item>, // Variable length!
      }
      
      pub fn deserialize_config(account: &AccountInfo) -> ProgramResult {
          // No size validation - could run out of compute!
          let config = Config::try_from_slice(&account.data.borrow())?;
      
          // Attacker could create huge Vec causing OOM
          for item in &config.items {
              // Process item
          }
      
          Ok(())
      }
      ```
      
      **Secure:**
      ```rust
      pub const MAX_ITEMS: usize = 100;
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct Config {
          pub value: u64,
          pub item_count: u32,
          pub items: Vec<Item>,
      }
      
      impl Config {
          pub fn from_account_info(account: &AccountInfo) -> Result<Self, ProgramError> {
              let data = account.data.borrow();
      
              // Validate minimum size
              if data.len() < 8 + 4 {
                  return Err(ProgramError::InvalidAccountData);
              }
      
              let config = Self::try_from_slice(&data)
                  .map_err(|_| ProgramError::InvalidAccountData)?;
      
              // Validate item count matches actual length
              if config.item_count as usize != config.items.len() {
                  return Err(ProgramError::InvalidAccountData);
              }
      
              // Enforce maximum items
              if config.items.len() > MAX_ITEMS {
                  return Err(ProgramError::InvalidAccountData);
              }
      
              Ok(config)
          }
      }
      ```
      
      ### Account Data Layout Validation
      
      **Best Practice:**
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct UserAccount {
          pub discriminator: u64,
          pub owner: Pubkey,
          pub balance: u64,
          pub created_at: i64,
      }
      
      impl UserAccount {
          pub const LEN: usize = 8 + 32 + 8 + 8;
      
          pub fn from_account_info(account: &AccountInfo) -> Result<Self, ProgramError> {
              let data = account.data.borrow();
      
              // Exact size check prevents truncation attacks
              if data.len() != Self::LEN {
                  return Err(ProgramError::InvalidAccountData);
              }
      
              Self::try_from_slice(&data)
                  .map_err(|_| ProgramError::InvalidAccountData)
          }
      
          pub fn to_account_info(&self, account: &AccountInfo) -> ProgramResult {
              let mut data = account.data.borrow_mut();
      
              if data.len() != Self::LEN {
                  return Err(ProgramError::InvalidAccountData);
              }
      
              self.serialize(&mut *data)
                  .map_err(|_| ProgramError::InvalidAccountData)
          }
      }
      ```
      
      ---
      
      ## Rent and Space Management
      
      ### Rent Exemption Validation
      
      **Secure Pattern:**
      ```rust
      use solana_program::rent::Rent;
      use solana_program::sysvar::Sysvar;
      
      pub fn create_account(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          space: usize,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let new_account = next_account_info(account_info_iter)?;
          let payer = next_account_info(account_info_iter)?;
          let system_program = next_account_info(account_info_iter)?;
      
          // Get rent sysvar
          let rent = Rent::get()?;
      
          // Calculate required lamports for rent exemption
          let required_lamports = rent.minimum_balance(space);
      
          // Validate account has enough lamports
          if new_account.lamports() < required_lamports {
              return Err(ProgramError::AccountNotRentExempt);
          }
      
          // Additional validation: account is rent exempt
          if !rent.is_exempt(new_account.lamports(), new_account.data_len()) {
              return Err(ProgramError::AccountNotRentExempt);
          }
      
          Ok(())
      }
      ```
      
      ### Account Size Calculation
      
      **Vulnerable:**
      ```rust
      pub fn init_account(space: usize) -> ProgramResult {
          // No validation - attacker could request huge space
          let ix = solana_program::system_instruction::create_account(
              &payer.key,
              &new_account.key,
              lamports,
              space as u64, // Could overflow!
              program_id,
          );
      
          Ok(())
      }
      ```
      
      **Secure:**
      ```rust
      pub const MIN_ACCOUNT_SIZE: usize = 128;
      pub const MAX_ACCOUNT_SIZE: usize = 10_240; // 10KB
      
      pub fn init_account(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          requested_space: usize,
      ) -> ProgramResult {
          // Validate space within reasonable bounds
          if requested_space < MIN_ACCOUNT_SIZE || requested_space > MAX_ACCOUNT_SIZE {
              return Err(ProgramError::InvalidAccountData);
          }
      
          // Ensure space alignment
          let space = requested_space
              .checked_next_multiple_of(8)
              .ok_or(ProgramError::InvalidAccountData)?;
      
          let rent = Rent::get()?;
          let lamports = rent.minimum_balance(space);
      
          // Safe to create account
          Ok(())
      }
      ```
      
      ---
      
      ## Error Handling in Native Rust
      
      ### Custom Error Types
      
      **Best Practice:**
      ```rust
      use thiserror::Error;
      use solana_program::program_error::ProgramError;
      
      #[derive(Error, Debug, Copy, Clone)]
      pub enum MyProgramError {
          #[error("Invalid authority")]
          InvalidAuthority,
      
          #[error("Insufficient balance")]
          InsufficientBalance,
      
          #[error("Account already initialized")]
          AlreadyInitialized,
      
          #[error("Arithmetic overflow")]
          ArithmeticOverflow,
      }
      
      impl From<MyProgramError> for ProgramError {
          fn from(e: MyProgramError) -> Self {
              ProgramError::Custom(e as u32)
          }
      }
      
      pub fn process(accounts: &[AccountInfo]) -> Result<(), MyProgramError> {
          let authority = next_account_info(&mut accounts.iter())
              .map_err(|_| MyProgramError::InvalidAuthority)?;
      
          if !authority.is_signer {
              return Err(MyProgramError::InvalidAuthority);
          }
      
          Ok(())
      }
      ```
      
      ### Avoiding unwrap() and expect()
      
      **Vulnerable:**
      ```rust
      pub fn process(accounts: &[AccountInfo]) -> ProgramResult {
          let account = accounts.get(0).unwrap(); // Panics if no accounts!
          let data = account.data.borrow();
          let value = u64::from_le_bytes(data[0..8].try_into().unwrap()); // Panics if not 8 bytes!
      
          Ok(())
      }
      ```
      
      **Secure:**
      ```rust
      pub fn process(accounts: &[AccountInfo]) -> ProgramResult {
          let account = accounts
              .get(0)
              .ok_or(ProgramError::NotEnoughAccountKeys)?;
      
          let data = account.data.borrow();
      
          if data.len() < 8 {
              return Err(ProgramError::InvalidAccountData);
          }
      
          let value = u64::from_le_bytes(
              data[0..8]
                  .try_into()
                  .map_err(|_| ProgramError::InvalidAccountData)?
          );
      
          Ok(())
      }
      ```
      
      ---
      
      ## Token Program Integration
      
      ### Manual Token CPI Construction
      
      **Secure Pattern:**
      ```rust
      use spl_token::instruction as token_instruction;
      
      pub fn transfer_tokens(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let source_token = next_account_info(account_info_iter)?;
          let dest_token = next_account_info(account_info_iter)?;
          let authority = next_account_info(account_info_iter)?;
          let token_program = next_account_info(account_info_iter)?;
      
          // Validate token program
          if token_program.key != &spl_token::id() {
              return Err(ProgramError::IncorrectProgramId);
          }
      
          // Validate authority is signer
          if !authority.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          // Build transfer instruction
          let transfer_ix = token_instruction::transfer(
              token_program.key,
              source_token.key,
              dest_token.key,
              authority.key,
              &[],
              amount,
          )?;
      
          invoke(
              &transfer_ix,
              &[
                  source_token.clone(),
                  dest_token.clone(),
                  authority.clone(),
                  token_program.clone(),
              ],
          )?;
      
          Ok(())
      }
      ```
      
      ### Token Account Validation
      
      **Secure Pattern:**
      ```rust
      use spl_token::state::Account as TokenAccount;
      
      pub fn validate_token_account(
          token_account_info: &AccountInfo,
          expected_owner: &Pubkey,
          expected_mint: &Pubkey,
      ) -> Result<TokenAccount, ProgramError> {
          // Verify owned by token program
          if token_account_info.owner != &spl_token::id() {
              return Err(ProgramError::IncorrectProgramId);
          }
      
          // Deserialize token account
          let token_account = TokenAccount::unpack(&token_account_info.data.borrow())?;
      
          // Validate owner
          if &token_account.owner != expected_owner {
              return Err(ProgramError::InvalidAccountData);
          }
      
          // Validate mint
          if &token_account.mint != expected_mint {
              return Err(ProgramError::InvalidAccountData);
          }
      
          Ok(token_account)
      }
      ```
      
      ---
      
      ## Low-Level Security Patterns
      
      ### Account Reloading After External Calls
      
      **Vulnerable:**
      ```rust
      pub fn vulnerable_pattern(accounts: &[AccountInfo]) -> ProgramResult {
          let account = next_account_info(&mut accounts.iter())?;
      
          let balance_before = account.lamports();
      
          // External CPI call
          invoke(&some_instruction, &[account.clone()])?;
      
          // Account data not reloaded - still using stale reference!
          let balance_after = account.lamports();
      
          Ok(())
      }
      ```
      
      **Secure:**
      ```rust
      pub fn secure_pattern(accounts: &[AccountInfo]) -> ProgramResult {
          let account = next_account_info(&mut accounts.iter())?;
      
          let balance_before = account.lamports();
      
          // External CPI call
          invoke(&some_instruction, &[account.clone()])?;
      
          // AccountInfo automatically reflects changes - lamports(), data, etc.
          // are fresh after CPI
          let balance_after = account.lamports();
      
          // But if you cached deserialized data, you must reload:
          let fresh_data = MyData::from_account_info(account)?;
      
          Ok(())
      }
      ```
      
      ### Clock and Timestamp Validation
      
      **Secure Pattern:**
      ```rust
      use solana_program::clock::Clock;
      use solana_program::sysvar::Sysvar;
      
      pub fn time_locked_operation(
          accounts: &[AccountInfo],
          unlock_timestamp: i64,
      ) -> ProgramResult {
          // Get clock sysvar
          let clock = Clock::get()?;
      
          // Validate unlock time has passed
          if clock.unix_timestamp < unlock_timestamp {
              return Err(ProgramError::InvalidArgument);
          }
      
          // Proceed with operation
          Ok(())
      }
      ```
      
      ---
      
      ## Native Rust Best Practices
      
      ### Account Iteration Patterns
      
      **Best Practice:**
      ```rust
      pub fn process_instruction(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          instruction_data: &[u8],
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
      
          let authority = next_account_info(account_info_iter)?;
          let config = next_account_info(account_info_iter)?;
          let system_program = next_account_info(account_info_iter)?;
      
          // Validate all expected accounts consumed
          if account_info_iter.next().is_some() {
              return Err(ProgramError::InvalidAccountData);
          }
      
          // Validate accounts
          AccountValidation::new(authority).signer()?;
          AccountValidation::new(config)
              .owner(program_id)?
              .writable()?;
      
          Ok(())
      }
      ```
      
      ### State Management Patterns
      
      **Best Practice:**
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct ProgramState {
          pub version: u8,
          pub is_initialized: bool,
          pub authority: Pubkey,
          // Add new fields at the end for upgradability
          pub feature_flags: u64,
      }
      
      impl ProgramState {
          pub const CURRENT_VERSION: u8 = 1;
      
          pub fn initialize(authority: Pubkey) -> Self {
              Self {
                  version: Self::CURRENT_VERSION,
                  is_initialized: true,
                  authority,
                  feature_flags: 0,
              }
          }
      
          pub fn validate(&self) -> ProgramResult {
              if !self.is_initialized {
                  return Err(ProgramError::UninitializedAccount);
              }
      
              if self.version != Self::CURRENT_VERSION {
                  return Err(ProgramError::InvalidAccountData);
              }
      
              Ok(())
          }
      }
      ```
      
      ### Security.txt Integration
      
      **Best Practice:**
      ```rust
      #[cfg(not(feature = "no-entrypoint"))]
      solana_security_txt::security_txt! {
          name: "My Solana Program",
          project_url: "https://github.com/myorg/myprogram",
          contacts: "email:security@myorg.com,discord:myorg",
          policy: "https://github.com/myorg/myprogram/blob/main/SECURITY.md",
          preferred_languages: "en",
          source_code: "https://github.com/myorg/myprogram",
          auditors: "Auditor1, Auditor2"
      }
      ```
      
      ---
      
      ## Summary
      
      Native Rust Solana programs require meticulous manual validation of all security properties:
      
      1. **Always validate**: signer, owner, writable, key equality
      2. **Use discriminators** to prevent account type confusion
      3. **Store canonical bumps** and validate PDA derivation
      4. **Validate CPI targets** and propagate account flags correctly
      5. **Validate sizes** before deserialization
      6. **Check rent exemption** for all accounts
      7. **Use Result types** - never unwrap or expect
      8. **Validate token accounts** completely before use
      9. **Reload account data** after external calls if cached
      10. **Version your state** and validate initialization
      
      For each pattern, create reusable validation functions and leverage Rust's type system to enforce security invariants at compile time where possible.
      
    • pda.md 20.5 KB
      # Program Derived Addresses (PDAs)
      
      This reference provides comprehensive coverage of Program Derived Addresses (PDAs) for native Rust Solana program development, including derivation mechanics, security implications, and best practices.
      
      ## Table of Contents
      
      1. [What are PDAs](#what-are-pdas)
      2. [PDA Derivation Mechanics](#pda-derivation-mechanics)
      3. [Canonical Bump Seeds](#canonical-bump-seeds)
      4. [Creating PDA Accounts](#creating-pda-accounts)
      5. [PDA Signing](#pda-signing)
      6. [Common PDA Patterns](#common-pda-patterns)
      7. [Security Considerations](#security-considerations)
      8. [Best Practices](#best-practices)
      
      ---
      
      ## What are PDAs
      
      **Program Derived Addresses (PDAs) are deterministic account addresses derived from a program ID and optional seeds.**
      
      ### Key Characteristics
      
      1. **Deterministic**: Same inputs always produce the same PDA
      2. **No private key**: PDAs are intentionally off the Ed25519 curve
      3. **Program-signable**: The deriving program can sign for PDAs
      4. **Hashmap-like**: Enable key-value storage patterns on-chain
      
      ### Why PDAs Exist
      
      PDAs solve critical problems in Solana program development:
      
      **Problem 1: State Storage**
      - How do you store program state without tracking account addresses?
      - Solution: Derive addresses from user pubkeys + seeds
      
      **Problem 2: Program Signing**
      - How can a program sign transactions without a private key?
      - Solution: Runtime enables programs to sign for their PDAs
      
      **Problem 3: Account Discovery**
      - How do clients find accounts created by programs?
      - Solution: Derive PDAs client-side using known seeds
      
      ### PDA vs Regular Account
      
      | Property | Regular Account | PDA |
      |----------|----------------|-----|
      | Address derivation | Random (from keypair) | Deterministic (from seeds) |
      | Has private key | ✅ Yes | ❌ No (off-curve) |
      | Can sign transactions | ✅ Yes (with private key) | ✅ Yes (via program) |
      | Who can sign | Holder of private key | Only the deriving program |
      | Use case | User wallets | Program state storage |
      
      ---
      
      ## PDA Derivation Mechanics
      
      ### How PDAs are Derived
      
      PDAs are created using a hash function that combines:
      1. Program ID
      2. Optional seeds (strings, numbers, pubkeys)
      3. Bump seed (0-255)
      
      The process intentionally finds an address that falls **off** the Ed25519 elliptic curve.
      
      ```
      ┌──────────────────────────────────────────────┐
      │ Input Seeds                                  │
      ├──────────────────────────────────────────────┤
      │ - Program ID                                 │
      │ - Optional Seed 1 (e.g., "user_data")       │
      │ - Optional Seed 2 (e.g., user pubkey)       │
      │ - Bump seed (starts at 255)                 │
      └──────────────────────────────────────────────┘
                          │
                          ▼
               ┌──────────────────────┐
               │ Hash Function        │
               │ (SHA256 + checks)    │
               └──────────────────────┘
                          │
                          ▼
               ┌──────────────────────┐
               │ Is address off-curve?│
               └──────────────────────┘
                    │            │
                    │ No         │ Yes
                    ▼            ▼
          Decrement bump    Return (PDA, bump)
      ```
      
      ### Native Rust API
      
      ```rust
      use solana_program::pubkey::Pubkey;
      
      // Find PDA with canonical bump
      let (pda, bump_seed) = Pubkey::find_program_address(
          &[
              b"user_data",           // Seed 1: static string
              user_pubkey.as_ref(),   // Seed 2: user's public key
          ],
          program_id,
      );
      
      // pda: The derived address (off-curve)
      // bump_seed: The canonical bump (first valid bump found, starting from 255)
      ```
      
      ### Manual PDA Creation (Advanced)
      
      You can manually create a PDA with a specific bump using `create_program_address`:
      
      ```rust
      use solana_program::pubkey::Pubkey;
      
      // This may fail if the bump doesn't produce a valid off-curve address
      let pda = Pubkey::create_program_address(
          &[
              b"user_data",
              user_pubkey.as_ref(),
              &[bump_seed],  // Specific bump
          ],
          program_id,
      )?;
      ```
      
      **⚠️ Warning:** Only use `create_program_address` when you're certain the bump is valid. Prefer `find_program_address` for safety.
      
      ---
      
      ## Canonical Bump Seeds
      
      ### What is a Canonical Bump?
      
      The **canonical bump** is the first bump seed (starting from 255, decrementing) that produces a valid off-curve address.
      
      ```rust
      // Example: Finding all valid bumps
      for bump in (0..=255).rev() {
          if let Ok(pda) = Pubkey::create_program_address(
              &[b"data", user.as_ref(), &[bump]],
              program_id,
          ) {
              println!("Bump {}: {}", bump, pda);
          }
      }
      
      // Typical output:
      // Bump 255: Error (on-curve)
      // Bump 254: AValidPDAAddress...  ← CANONICAL BUMP
      // Bump 253: AnotherValidPDA...
      // Bump 252: AThirdValidPDA...
      // ...
      ```
      
      ### Why Use the Canonical Bump?
      
      **Security Reason:** Multiple bumps can derive different valid PDAs for the same seeds. Accepting arbitrary bumps enables PDA substitution attacks.
      
      **Attack Scenario:**
      ```rust
      // ❌ Vulnerable - accepts any bump
      pub fn update_user_balance(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          bump: u8,  // User provides bump
      ) -> ProgramResult {
          let user = &accounts[0];
          let user_pda = &accounts[1];
      
          // Creates PDA with user-provided bump
          let expected_pda = Pubkey::create_program_address(
              &[b"balance", user.key.as_ref(), &[bump]],
              program_id,
          )?;
      
          // Attacker can provide bump 253 instead of canonical 254
          // This derives a DIFFERENT PDA the attacker controls!
          // ...
      }
      ```
      
      **Secure Pattern:**
      ```rust
      // ✅ Secure - uses canonical bump only
      pub fn update_user_balance(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let user = &accounts[0];
          let user_pda = &accounts[1];
      
          // Derive with canonical bump
          let (expected_pda, _bump) = Pubkey::find_program_address(
              &[b"balance", user.key.as_ref()],
              program_id,
          );
      
          // Validate
          if expected_pda != *user_pda.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // Safe to proceed
          // ...
      }
      ```
      
      ### Storing the Canonical Bump
      
      **Best Practice:** Store the canonical bump in the account data:
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct UserAccount {
          pub bump: u8,           // Store canonical bump
          pub user: Pubkey,
          pub balance: u64,
      }
      
      // On creation
      let (pda, bump) = Pubkey::find_program_address(&[b"user", user.key.as_ref()], program_id);
      let account_data = UserAccount {
          bump,  // Save for future operations
          user: *user.key,
          balance: 0,
      };
      ```
      
      **Why store it?**
      - Saves compute units on subsequent operations
      - `find_program_address` iterates from 255, costs ~3,000 CU
      - Using stored bump with `create_program_address` costs ~300 CU (10x cheaper!)
      
      ---
      
      ## Creating PDA Accounts
      
      ### Creation Process
      
      PDAs cannot create themselves. Accounts at PDA addresses must be created by:
      1. Invoking the System Program via CPI
      2. Using `invoke_signed` to sign with the PDA
      3. The System Program creates the account and transfers ownership
      
      ### Native Rust Pattern
      
      ```rust
      use solana_program::{
          account_info::{next_account_info, AccountInfo},
          entrypoint::ProgramResult,
          program::{invoke_signed},
          program_error::ProgramError,
          pubkey::Pubkey,
          rent::Rent,
          system_instruction,
          sysvar::Sysvar,
      };
      
      pub fn create_user_account(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          user_id: u64,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
      
          let payer = next_account_info(account_info_iter)?;           // Pays for account
          let user_pda = next_account_info(account_info_iter)?;        // PDA to create
          let system_program = next_account_info(account_info_iter)?; // System Program
      
          // Signer check
          if !payer.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          // Derive PDA
          let user_id_bytes = user_id.to_le_bytes();
          let (pda, bump_seed) = Pubkey::find_program_address(
              &[b"user", payer.key.as_ref(), user_id_bytes.as_ref()],
              program_id,
          );
      
          // Validate provided PDA matches derivation
          if pda != *user_pda.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // Calculate space and rent
          let account_size: usize = 1 + 32 + 8;  // bump + pubkey + u64
          let rent = Rent::get()?;
          let rent_lamports = rent.minimum_balance(account_size);
      
          // Create account via CPI
          let create_account_ix = system_instruction::create_account(
              payer.key,              // Payer
              user_pda.key,           // New account address (the PDA)
              rent_lamports,          // Lamports
              account_size as u64,    // Space
              program_id,             // Owner (our program)
          );
      
          // Sign with PDA using bump seed
          let signer_seeds: &[&[&[u8]]] = &[&[
              b"user",
              payer.key.as_ref(),
              user_id_bytes.as_ref(),
              &[bump_seed],  // Critical: Include bump in signer seeds
          ]];
      
          invoke_signed(
              &create_account_ix,
              &[payer.clone(), user_pda.clone(), system_program.clone()],
              signer_seeds,  // PDA signs here
          )?;
      
          // Initialize account data
          let mut account_data = UserAccount::try_from_slice(&user_pda.data.borrow())?;
          account_data.bump = bump_seed;
          account_data.owner = *payer.key;
          account_data.user_id = user_id;
          account_data.serialize(&mut &mut user_pda.data.borrow_mut()[..])?;
      
          Ok(())
      }
      
      #[derive(BorshSerialize, BorshDeserialize)]
      struct UserAccount {
          bump: u8,
          owner: Pubkey,
          user_id: u64,
      }
      ```
      
      ### Key Points
      
      1. **Signer Seeds Format**: `&[&[&[u8]]]` (3 levels of slicing)
         - Outer: Array of seed sets (for multiple PDAs)
         - Middle: Single seed set (one PDA)
         - Inner: Individual seed slices
      
      2. **Bump Must Be Included**: Always append `&[bump_seed]` to signer seeds
      
      3. **System Program Required**: Must pass System Program account for CPI
      
      4. **Ownership Transfer**: Account starts owned by System Program, transfers to your program
      
      ---
      
      ## PDA Signing
      
      ### How Programs Sign for PDAs
      
      When a program makes a CPI with `invoke_signed`, the runtime:
      1. Receives the signer seeds
      2. Derives the PDA using seeds + calling program's ID
      3. Verifies the derived PDA matches an account in the instruction
      4. Grants signing authority to that PDA
      
      ### invoke_signed vs invoke
      
      ```rust
      // invoke: No PDA signing
      pub fn invoke(
          instruction: &Instruction,
          account_infos: &[AccountInfo],
      ) -> ProgramResult
      
      // invoke_signed: With PDA signing
      pub fn invoke_signed(
          instruction: &Instruction,
          account_infos: &[AccountInfo],
          signers_seeds: &[&[&[u8]]],  // PDA seeds
      ) -> ProgramResult
      ```
      
      ### Practical Example: PDA Transfers SOL
      
      ```rust
      pub fn pda_transfer_sol(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let pda_account = next_account_info(account_info_iter)?;
          let recipient = next_account_info(account_info_iter)?;
          let system_program = next_account_info(account_info_iter)?;
      
          // Derive PDA and verify
          let (pda, bump_seed) = Pubkey::find_program_address(
              &[b"vault", recipient.key.as_ref()],
              program_id,
          );
      
          if pda != *pda_account.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // Create transfer instruction
          let transfer_ix = system_instruction::transfer(
              pda_account.key,  // From: PDA (needs signing!)
              recipient.key,    // To: recipient
              amount,
          );
      
          // PDA signs the transfer
          let signer_seeds: &[&[&[u8]]] = &[&[
              b"vault",
              recipient.key.as_ref(),
              &[bump_seed],
          ]];
      
          invoke_signed(
              &transfer_ix,
              &[pda_account.clone(), recipient.clone(), system_program.clone()],
              signer_seeds,  // Runtime verifies and grants signing authority
          )?;
      
          Ok(())
      }
      ```
      
      ### Multiple PDA Signers
      
      You can sign with multiple PDAs in a single CPI:
      
      ```rust
      let signer_seeds: &[&[&[u8]]] = &[
          &[b"pda1", &[bump1]],  // First PDA
          &[b"pda2", &[bump2]],  // Second PDA
      ];
      
      invoke_signed(&instruction, &accounts, signer_seeds)?;
      ```
      
      ---
      
      ## Common PDA Patterns
      
      ### 1. User-Specific Accounts
      
      **Pattern:** One PDA per user for storing user data.
      
      ```rust
      // Seeds: ["user_data", user_pubkey]
      let (user_pda, bump) = Pubkey::find_program_address(
          &[b"user_data", user.key.as_ref()],
          program_id,
      );
      ```
      
      **Use case:** User profiles, balances, inventory
      
      **Advantages:**
      - Easy client-side discovery
      - One account per user
      - User's pubkey acts as unique identifier
      
      ### 2. Global State
      
      **Pattern:** Single PDA for program-wide state.
      
      ```rust
      // Seeds: ["global_state"]
      let (global_pda, bump) = Pubkey::find_program_address(
          &[b"global_state"],
          program_id,
      );
      ```
      
      **Use case:** Program configuration, global counters, admin settings
      
      **Advantages:**
      - Single source of truth
      - Easy to find (no variable seeds)
      - Reduced account proliferation
      
      ### 3. Association Pattern
      
      **Pattern:** PDA associates two entities.
      
      ```rust
      // Seeds: ["escrow", seller_pubkey, buyer_pubkey]
      let (escrow_pda, bump) = Pubkey::find_program_address(
          &[b"escrow", seller.key.as_ref(), buyer.key.as_ref()],
          program_id,
      );
      ```
      
      **Use case:** Escrow accounts, peer-to-peer trades, relationships
      
      **Advantages:**
      - Unique per relationship
      - Deterministic discovery
      - Prevents duplicate associations
      
      ### 4. Index/Counter Pattern
      
      **Pattern:** PDA with numeric index for multiple instances.
      
      ```rust
      // Seeds: ["note", author_pubkey, note_id]
      let note_id: u64 = 42;
      let (note_pda, bump) = Pubkey::find_program_address(
          &[b"note", author.key.as_ref(), note_id.to_le_bytes().as_ref()],
          program_id,
      );
      ```
      
      **Use case:** Notes, posts, items, sequential data
      
      **Advantages:**
      - Multiple accounts per user
      - Enumerable (iterate by incrementing ID)
      - Scalable
      
      **Implementation:**
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct UserState {
          pub note_count: u64,  // Track next available ID
      }
      
      pub fn create_note(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          content: String,
      ) -> ProgramResult {
          let user = &accounts[0];
          let user_state_pda = &accounts[1];
          let note_pda = &accounts[2];
      
          // Load user state
          let mut user_state = UserState::try_from_slice(&user_state_pda.data.borrow())?;
      
          // Derive PDA for new note
          let note_id = user_state.note_count;
          let (expected_note_pda, bump) = Pubkey::find_program_address(
              &[b"note", user.key.as_ref(), note_id.to_le_bytes().as_ref()],
              program_id,
          );
      
          if expected_note_pda != *note_pda.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // Create note account...
          // Initialize note data...
      
          // Increment counter
          user_state.note_count += 1;
          user_state.serialize(&mut &mut user_state_pda.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      ### 5. Vault/Treasury Pattern
      
      **Pattern:** PDA holds funds for the program.
      
      ```rust
      // Seeds: ["vault"]
      let (vault_pda, bump) = Pubkey::find_program_address(
          &[b"vault"],
          program_id,
      );
      ```
      
      **Use case:** Staking pools, treasuries, escrow
      
      **Advantages:**
      - Program controls funds
      - No external keypair needed
      - Can't lose "private key"
      
      ---
      
      ## Security Considerations
      
      ### 1. Always Validate PDAs
      
      **❌ Vulnerable:**
      ```rust
      pub fn update_balance(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let user_pda = &accounts[0];
      
          // No PDA validation!
          let mut user_data = UserData::try_from_slice(&user_pda.data.borrow())?;
          user_data.balance += 100;
          user_data.serialize(&mut &mut user_pda.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      **✅ Secure:**
      ```rust
      pub fn update_balance(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let user = &accounts[0];
          let user_pda = &accounts[1];
      
          // Derive and validate
          let (expected_pda, _) = Pubkey::find_program_address(
              &[b"user", user.key.as_ref()],
              program_id,
          );
      
          if expected_pda != *user_pda.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // Safe to proceed
          let mut user_data = UserData::try_from_slice(&user_pda.data.borrow())?;
          user_data.balance += 100;
          user_data.serialize(&mut &mut user_pda.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      ### 2. Non-Canonical Bump Attack
      
      **Vulnerability:** Accepting user-provided bumps allows PDA substitution.
      
      **Impact:** Attacker can manipulate which account is used.
      
      **Prevention:**
      - Always use `find_program_address` (canonical bump)
      - Never accept bump as instruction parameter
      - Store bump in account data after creation
      
      ### 3. Seed Confusion
      
      **Vulnerability:** Ambiguous seed ordering can create collisions.
      
      ```rust
      // ❌ Problematic - seeds can collide
      let seed1 = "hello";
      let seed2 = "world";
      
      // These derive the SAME PDA:
      Pubkey::find_program_address(&[b"helloworld"], program_id);
      Pubkey::find_program_address(&[b"hello", b"world"], program_id);
      ```
      
      **Prevention:**
      ```rust
      // ✅ Use fixed-size types and clear separators
      Pubkey::find_program_address(
          &[
              b"prefix_",        // Fixed prefix
              user.key.as_ref(), // 32 bytes (fixed)
              &id.to_le_bytes(), // 8 bytes (fixed)
          ],
          program_id,
      );
      ```
      
      ### 4. Ownership Verification
      
      **Always verify PDA ownership:**
      
      ```rust
      // ✅ Check ownership after PDA validation
      if user_pda.owner != program_id {
          return Err(ProgramError::IllegalOwner);
      }
      ```
      
      ---
      
      ## Best Practices
      
      ### 1. Seed Design
      
      **Good Seed Patterns:**
      - Use descriptive prefixes: `b"user_profile"`, `b"escrow"`, `b"vault"`
      - Include entity identifiers: user pubkeys, IDs
      - Use fixed-size types: `u64.to_le_bytes()`, `Pubkey::as_ref()`
      - Maintain logical ordering: most general → most specific
      
      **Example:**
      ```rust
      &[
          b"note",               // What type of account
          author.key.as_ref(),   // Who owns it
          note_id.to_le_bytes(), // Which instance
      ]
      ```
      
      ### 2. Always Store the Bump
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct PdaAccount {
          pub bump: u8,  // Always first field for efficiency
          // ... other fields
      }
      ```
      
      **Benefits:**
      - Saves ~2,700 CU per operation
      - Enables efficient re-derivation
      - Documents canonical bump
      
      ### 3. Validate Everything
      
      **Security Checklist:**
      - ✅ Derive PDA with canonical bump
      - ✅ Compare derived PDA to provided account
      - ✅ Verify PDA owner is your program
      - ✅ Check initialization status
      - ✅ Validate signer requirements
      
      ### 4. Document Your Seed Schema
      
      ```rust
      /// Derives a user profile PDA.
      ///
      /// Seeds: ["user_profile", user_pubkey]
      /// Bump: Stored in account.bump
      pub fn derive_user_profile_pda(
          user: &Pubkey,
          program_id: &Pubkey,
      ) -> (Pubkey, u8) {
          Pubkey::find_program_address(
              &[b"user_profile", user.as_ref()],
              program_id,
          )
      }
      ```
      
      ### 5. Use Helper Functions
      
      ```rust
      pub struct PdaDerivation;
      
      impl PdaDerivation {
          pub fn user_profile(user: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
              Pubkey::find_program_address(&[b"user", user.as_ref()], program_id)
          }
      
          pub fn note(
              author: &Pubkey,
              note_id: u64,
              program_id: &Pubkey,
          ) -> (Pubkey, u8) {
              Pubkey::find_program_address(
                  &[b"note", author.as_ref(), note_id.to_le_bytes().as_ref()],
                  program_id,
              )
          }
      }
      
      // Usage
      let (user_pda, bump) = PdaDerivation::user_profile(user.key, program_id);
      ```
      
      ---
      
      ## Summary
      
      **Key Takeaways:**
      
      1. **PDAs are deterministic addresses** derived from program ID + seeds
      2. **No private key exists** for PDAs (they're off-curve by design)
      3. **Only the deriving program can sign** for its PDAs
      4. **Always use canonical bump** to prevent substitution attacks
      5. **Validate PDAs before use** - never trust client-provided accounts
      6. **Store the bump** in account data for compute efficiency
      7. **Design clear seed schemas** to prevent collisions and confusion
      
      **Security Mantra:**
      ```rust
      // Always follow this pattern
      let (expected_pda, bump) = Pubkey::find_program_address(&seeds, program_id);
      if expected_pda != *provided_pda.key {
          return Err(ProgramError::InvalidSeeds);
      }
      if provided_pda.owner != program_id {
          return Err(ProgramError::IllegalOwner);
      }
      ```
      
      PDAs are the foundation of state management in Solana programs. Master them, validate them religiously, and your programs will be secure and efficient.
      
    • production-deployment.md 13.1 KB
      # Production Deployment Guide for Solana Programs
      
      **Best practices for deploying verified, production-ready Solana programs to mainnet and serious devnet environments.**
      
      ---
      
      ## Overview
      
      Production deployments require verified builds that prove deployed bytecode matches public source code. This guide covers the proper workflow for production deployments, particularly with Anchor framework.
      
      **Key principle:** Transparency and verifiability build trust. Always use deterministic builds for production.
      
      ---
      
      ## Why Verified Builds Matter
      
      **Without verified builds:**
      - Users cannot verify deployed code matches GitHub source
      - Audits cannot confirm they reviewed the exact deployed binary
      - No transparency into what code actually runs on-chain
      - Security researchers cannot validate the program
      
      **With verified builds:**
      - ✅ Provably deterministic builds (Docker-based)
      - ✅ Anyone can verify deployed bytecode matches source
      - ✅ Explorer verification badges (Solana Explorer, SolanaFM)
      - ✅ Audit reports apply to exact deployed binary
      - ✅ Standard for all serious Solana projects
      
      **All major Solana protocols use verified builds:** Jupiter, Marinade, Orca, Metaplex, etc.
      
      ---
      
      ## The Problem with `anchor deploy`
      
      ### Anchor 0.32.1 and Earlier
      
      **⚠️ CRITICAL: Do NOT use `anchor deploy` for production deployments**
      
      **Why `anchor deploy` is unsuitable for production:**
      
      1. **Non-deterministic builds**
         - Build output varies by local Rust version
         - Different on macOS vs Linux
         - Depends on installed toolchain
         - Same source → different binaries on different machines
      
      2. **Cannot be verified**
         - No way to prove deployed code matches GitHub
         - Verification tools cannot reproduce the build
         - Breaks audit trail
      
      3. **Lacks transparency**
         - Users must trust deployer
         - No verification badges on explorers
         - Goes against Solana ecosystem standards
      
      **When Anchor v1 may improve this:**
      - Anchor v1 is expected to have better support for verified builds
      - May integrate `solana-verify` directly
      - Check Anchor docs for updates when v1 releases
      
      **For now (Anchor 0.32.1):** Use the verified deployment workflow below.
      
      ---
      
      ## Production Deployment Workflow
      
      ### Step 1: Build Verifiably
      
      Use `solana-verify build` instead of `anchor build` for the final production build:
      
      ```bash
      # Install solana-verify if not already installed
      cargo install solana-verify
      
      # Navigate to project root (where Cargo.toml with workspace is)
      cd my-project
      
      # Build verifiably in Docker (deterministic)
      solana-verify build --library-name my_program
      
      # Verify the build succeeded
      ls -la target/deploy/my_program.so
      ```
      
      **What this does:**
      - Builds in Docker container (consistent environment)
      - Uses exact dependencies from `Cargo.lock`
      - Same input → same output (deterministic)
      - Anyone can reproduce this exact binary
      
      **Important:** Do NOT run `anchor build` after `solana-verify build` - it will regenerate a different binary!
      
      ### Step 2: Deploy the Verified Binary
      
      Use `solana program deploy` directly (NOT `anchor deploy`):
      
      **For devnet:**
      ```bash
      solana program deploy target/deploy/my_program.so \
        --program-id target/deploy/my_program-keypair.json \
        -u devnet \
        --with-compute-unit-price 1000
      ```
      
      **For mainnet:**
      ```bash
      # Use your deployer keypair and appropriate priority fees
      solana program deploy target/deploy/my_program.so \
        --program-id target/deploy/my_program-keypair.json \
        --keypair ~/.config/solana/deployer.json \
        -u mainnet-beta \
        --with-compute-unit-price 100000 \
        --max-sign-attempts 100 \
        --use-rpc
      ```
      
      **Why use `solana program deploy` directly:**
      - Works with verified builds
      - More control over deployment parameters
      - Standard across all Solana programs
      - Same tool for Anchor and native Rust
      
      ### Step 3: Verify Against Repository
      
      After deployment, verify the on-chain program matches your source:
      
      ```bash
      solana-verify verify-from-repo \
        -u devnet \
        --program-id <PROGRAM_ID> \
        https://github.com/your-org/your-repo \
        --library-name my_program
      
      # Or specify exact commit
      solana-verify verify-from-repo \
        -u mainnet-beta \
        --program-id <PROGRAM_ID> \
        https://github.com/your-org/your-repo \
        --commit-hash <COMMIT_HASH> \
        --library-name my_program
      ```
      
      **When prompted, upload verification data on-chain:**
      ```
      Would you like to upload verification data on-chain? (y/n)
      ```
      
      Select **yes** to enable:
      - Verification badge on Solana Explorer
      - OtterSec verification API listing
      - SolanaFM verification display
      
      ### Step 4: Verify Hash Match (Sanity Check)
      
      Before step 3, you can manually verify hashes match:
      
      ```bash
      # Get on-chain program hash
      solana-verify get-program-hash -u devnet <PROGRAM_ID>
      
      # Get local executable hash
      solana-verify get-executable-hash target/deploy/my_program.so
      
      # These MUST match exactly
      ```
      
      ---
      
      ## Complete Production Deployment Checklist
      
      ### Pre-Deployment
      
      - [ ] All tests pass (`cargo test`, `anchor test`)
      - [ ] Security audit completed (for mainnet)
      - [ ] `Cargo.lock` committed to git
      - [ ] Git tag created for release (e.g., `v1.0.0`)
      - [ ] Sufficient SOL in deployer wallet
      - [ ] Multisig or governance ready (mainnet)
      
      ### Build
      
      - [ ] Run `solana-verify build --library-name my_program`
      - [ ] Verify `.so` file exists in `target/deploy/`
      - [ ] Do NOT run `anchor build` after this
      - [ ] Get hash: `solana-verify get-executable-hash target/deploy/my_program.so`
      
      ### Deploy
      
      - [ ] Use `solana program deploy` (NOT `anchor deploy`)
      - [ ] Specify correct program ID keypair
      - [ ] Use appropriate priority fees
      - [ ] Verify deployment: `solana program show <PROGRAM_ID>`
      
      ### Verify
      
      - [ ] Run `solana-verify verify-from-repo` with your GitHub URL
      - [ ] Upload verification data on-chain when prompted
      - [ ] Check verification appears on explorer
      - [ ] Optional: Submit remote verification job
      
      ### Post-Deployment
      
      - [ ] Transfer upgrade authority to multisig (mainnet)
      - [ ] Smoke test critical instructions on-chain
      - [ ] Set up monitoring
      - [ ] Announce deployment with verification link
      
      ---
      
      ## Example: Complete Mainnet Deployment
      
      ```bash
      # 1. Prepare
      git tag v1.0.0
      git push origin v1.0.0
      
      # 2. Build verifiably
      solana-verify build --library-name cascade_splits
      
      # 3. Check hash
      solana-verify get-executable-hash target/deploy/cascade_splits.so
      # Output: abc123def456...
      
      # 4. Deploy to mainnet
      solana program deploy target/deploy/cascade_splits.so \
        --program-id target/deploy/cascade_splits-keypair.json \
        --keypair ~/.config/solana/mainnet-deployer.json \
        -u mainnet-beta \
        --with-compute-unit-price 100000 \
        --max-sign-attempts 100 \
        --use-rpc
      
      # Output: Program Id: SPL1T3rERcu6P6dyBiG7K8LUr21CssZqDAszwANzNMB
      
      # 5. Verify on-chain hash matches
      solana-verify get-program-hash -u mainnet-beta SPL1T3rERcu6P6dyBiG7K8LUr21CssZqDAszwANzNMB
      # Output: abc123def456... (must match step 3!)
      
      # 6. Verify against repository
      solana-verify verify-from-repo \
        -u mainnet-beta \
        --program-id SPL1T3rERcu6P6dyBiG7K8LUr21CssZqDAszwANzNMB \
        https://github.com/cascade-protocol/splits \
        --commit-hash v1.0.0 \
        --library-name cascade_splits
      
      # When prompted: Upload verification data on-chain? → YES
      
      # 7. Transfer authority to multisig
      SQUADS_VAULT="YourSquadsVaultAddress"
      solana program set-upgrade-authority SPL1T3rERcu6P6dyBiG7K8LUr21CssZqDAszwANzNMB \
        --new-upgrade-authority $SQUADS_VAULT
      
      # 8. Verify on explorer
      # Visit: https://explorer.solana.com/address/SPL1T3rERcu6P6dyBiG7K8LUr21CssZqDAszwANzNMB
      # Should show verification badge
      
      # 9. Check OtterSec verification
      # Visit: https://verify.osec.io/status/SPL1T3rERcu6P6dyBiG7K8LUr21CssZqDAszwANzNMB
      ```
      
      ---
      
      ## Program Upgrades with Verified Builds
      
      ### Upgrade Workflow
      
      ```bash
      # 1. Make changes, test, commit
      git add .
      git commit -m "feat: add new feature"
      git tag v1.1.0
      git push origin main v1.1.0
      
      # 2. Build verifiably
      solana-verify build --library-name my_program
      
      # 3. Check if program size increased
      OLD_SIZE=$(solana program show <PROGRAM_ID> | grep "Data Length" | awk '{print $3}')
      NEW_SIZE=$(wc -c < target/deploy/my_program.so)
      
      # 4. Extend if needed
      if [ $NEW_SIZE -gt $OLD_SIZE ]; then
        DIFF=$((NEW_SIZE - OLD_SIZE))
        solana program extend <PROGRAM_ID> $DIFF
      fi
      
      # 5. Deploy upgrade
      solana program deploy target/deploy/my_program.so \
        --program-id <PROGRAM_ID> \
        --upgrade-authority ~/.config/solana/deployer.json \
        -u mainnet-beta \
        --with-compute-unit-price 100000
      
      # 6. Verify new version
      solana-verify verify-from-repo \
        -u mainnet-beta \
        --program-id <PROGRAM_ID> \
        https://github.com/your-org/your-repo \
        --commit-hash v1.1.0 \
        --library-name my_program
      ```
      
      ### Upgrades via Multisig
      
      If upgrade authority is a Squads multisig:
      
      ```bash
      # 1. Build verifiably
      solana-verify build --library-name my_program
      
      # 2. Create buffer (not direct upgrade)
      solana program write-buffer target/deploy/my_program.so
      # Output: Buffer: <BUFFER_ADDRESS>
      
      # 3. Transfer buffer to multisig
      solana program set-buffer-authority <BUFFER_ADDRESS> \
        --new-buffer-authority <SQUADS_VAULT>
      
      # 4. Create upgrade proposal in Squads UI
      # - Navigate to https://v4.squads.so/
      # - Create transaction for BPF Upgradeable Loader upgrade
      # - Reference buffer address
      # - Get approval from multisig members
      # - Execute
      
      # 5. After execution, verify
      solana-verify verify-from-repo \
        -u mainnet-beta \
        --program-id <PROGRAM_ID> \
        https://github.com/your-org/your-repo \
        --commit-hash v1.1.0 \
        --library-name my_program
      ```
      
      ---
      
      ## Troubleshooting
      
      ### Hash Mismatch After Deployment
      
      **Problem:** On-chain hash doesn't match local hash
      
      **Causes:**
      1. Ran `anchor build` or `cargo build-sbf` after `solana-verify build`
      2. Deployed wrong file
      3. `Cargo.lock` not committed or out of sync
      
      **Solution:**
      ```bash
      # 1. Clean everything
      cargo clean
      
      # 2. Ensure Cargo.lock is committed
      git add Cargo.lock
      git commit -m "Add Cargo.lock"
      
      # 3. Rebuild verifiably
      solana-verify build --library-name my_program
      
      # 4. Redeploy
      solana program deploy target/deploy/my_program.so \
        --program-id <PROGRAM_ID>
      
      # 5. Verify again
      solana-verify verify-from-repo ...
      ```
      
      ### Verification Fails: "Could not build from repository"
      
      **Problem:** `solana-verify verify-from-repo` cannot build
      
      **Causes:**
      1. Missing `Cargo.lock` in repository
      2. Wrong commit hash
      3. Workspace configuration issue
      4. Missing dependencies in Docker build
      
      **Solution:**
      ```bash
      # 1. Verify Cargo.lock exists in git
      git ls-files | grep Cargo.lock
      
      # 2. Check commit hash is correct
      git log --oneline
      
      # 3. Ensure workspace Cargo.toml exists at root
      cat Cargo.toml  # Should have [workspace]
      
      # 4. Try local verification first
      solana-verify verify-from-repo \
        --program-id <PROGRAM_ID> \
        file://$(pwd) \
        --library-name my_program
      ```
      
      ### "anchor deploy" Used by Accident
      
      **Problem:** Deployed with `anchor deploy` instead of verified build
      
      **Solution:** Redeploy properly:
      ```bash
      # 1. Build verifiably
      solana-verify build --library-name my_program
      
      # 2. Redeploy (upgrade) with verified binary
      solana program deploy target/deploy/my_program.so \
        --program-id <PROGRAM_ID>
      
      # 3. Verify
      solana-verify verify-from-repo \
        -u <NETWORK> \
        --program-id <PROGRAM_ID> \
        https://github.com/your-org/your-repo
      ```
      
      ---
      
      ## Version-Specific Notes
      
      ### Anchor 0.32.1
      
      - **Status:** Current stable version as of November 2024
      - **Issue:** `anchor deploy` does not produce verifiable builds
      - **Workaround:** Use workflow in this guide (solana-verify + solana program deploy)
      - **Uses:** Solana SDK 2.2.x
      
      ### Anchor 0.30.x
      
      - **Status:** Older stable version
      - **Issue:** Same as 0.32.1
      - **Workaround:** Same workflow applies
      - **Uses:** Solana SDK 2.1.x
      
      ### Future: Anchor 1.0.0
      
      - **Expected:** Better integration with verified builds
      - **Possible:** `anchor deploy --verifiable` flag
      - **Check:** Official Anchor docs when v1 releases
      - **Until then:** Use this guide
      
      ---
      
      ## Best Practices Summary
      
      ### Always ✅
      
      - Use `solana-verify build` for production builds
      - Commit `Cargo.lock` to git
      - Tag releases with git tags
      - Deploy with `solana program deploy` directly
      - Verify against repository after deployment
      - Upload verification data on-chain
      - Transfer mainnet authority to multisig
      - Test entire flow on devnet first
      
      ### Never ❌
      
      - Use `anchor deploy` for production/mainnet
      - Run `anchor build` or `cargo build-sbf` after `solana-verify build`
      - Deploy without verifying
      - Deploy mainnet without devnet testing first
      - Deploy mainnet without security audit
      - Keep upgrade authority as individual wallet (mainnet)
      - Skip uploading verification data
      
      ### Development Only
      
      `anchor deploy` is fine for:
      - Local validator testing
      - Rapid iteration during development
      - Devnet experiments
      - Non-production testing
      
      ---
      
      ## Additional Resources
      
      - **Solana Verify CLI**: https://github.com/Ellipsis-Labs/solana-verifiable-build
      - **Verified Programs List**: https://verify.osec.io/verified-programs
      - **Solana Explorer**: https://explorer.solana.com
      - **Squads Protocol**: https://squads.so
      - **Anchor Documentation**: https://www.anchor-lang.com/docs
      
      ---
      
      ## Summary
      
      **For production Solana program deployments:**
      
      1. Use `solana-verify build` (NOT `anchor deploy`)
      2. Deploy with `solana program deploy` directly
      3. Verify with `solana-verify verify-from-repo`
      4. Upload verification data on-chain
      
      This ensures transparency, verifiability, and trust in your deployed programs.
      
    • resources.md 15.3 KB
      # Resources
      
      Comprehensive collection of official documentation, development tools, security guides, audit reports, learning paths, and community resources for Solana program development and security.
      
      ## Official Documentation
      
      ### Solana Core
      - [Solana Docs](https://solana.com/docs/) - Official Solana documentation
      - [Solana Cookbook](https://solana.com/developers/cookbook) - Recipes for common Solana tasks
      - [Solana Courses](https://solana.com/developers/courses/) - Official learning paths
      - [Program Examples](https://github.com/solana-developers/program-examples) - Multi-framework examples
      - [Developer Bootcamp 2024](https://github.com/solana-developers/developer-bootcamp-2024)
      
      ### Anchor Framework
      - [Anchor Docs](https://www.anchor-lang.com/docs) - Official Anchor documentation
      - [Anchor Book](https://book.anchor-lang.com/) - Comprehensive Anchor guide
      - [Anchor by Example](https://examples.anchor-lang.com/) - Example programs
      - [Anchor Lang Docs](https://docs.rs/anchor-lang) - API documentation
      - [Anchor SPL Docs](https://docs.rs/anchor-spl) - SPL integration helpers
      - [Anchor GitHub](https://github.com/coral-xyz/anchor) - Framework source code
      
      ### SPL Programs
      - [SPL Documentation](https://spl.solana.com/) - Solana Program Library docs
      - [Token Program](https://github.com/solana-program/token) - SPL Token source
      - [Token-2022](https://github.com/solana-program/token-2022) - Next-gen token program
      - [Associated Token Account](https://github.com/solana-program/associated-token-account)
      - [Token Metadata](https://github.com/solana-program/token-metadata)
      - [Metaplex Token Metadata](https://github.com/metaplex-foundation/mpl-token-metadata)
      
      ## Example Programs
      
      ### Official Examples
      - [Program Examples](https://github.com/solana-developers/program-examples) - Comprehensive examples in multiple frameworks
      - [Anchor Examples](https://github.com/coral-xyz/anchor/tree/master/tests) - Official Anchor test programs
      - [Developer Bootcamp](https://github.com/solana-developers/developer-bootcamp-2024) - Workshop materials
      
      ### Production Protocols (for studying)
      - [Anchor Framework](https://github.com/coral-xyz/anchor) - The framework source itself
      - [Raydium AMM](https://github.com/raydium-io/raydium-cp-swap) - DEX protocol example
      - [Kamino Lending](https://github.com/Kamino-Finance/klend) - Lending protocol
      - [Squads Multisig](https://github.com/Squads-Protocol/v4) - Multisig protocol
      
      ## Development Tools
      
      ### IDEs & Playgrounds
      - [Solana Playground](https://beta.solpg.io/) - Browser-based IDE for Solana programs
      - [Anchor Playground](https://www.anchor-lang.com/playground) - Test Anchor programs online
      - [Rust Playground](https://play.rust-lang.org/) - Test Rust snippets
      
      ### CLI & Tooling
      - [Solana CLI](https://docs.solana.com/cli) - Command-line tools reference
      - [Anchor CLI](https://www.anchor-lang.com/docs/cli) - Anchor command reference
      - [Solana Explorer](https://explorer.solana.com/) - View transactions and accounts
      - [Solana FM](https://solana.fm/) - Alternative explorer with better UX
      - [Solscan](https://solscan.io/) - Popular block explorer
      - [XRAY](https://xray.helius.dev/) - Transaction viewer by Helius
      
      ### Testing Frameworks
      - [Mollusk](https://github.com/anza-xyz/mollusk) - Lightweight test harness for SVM programs
      - [Mollusk Docs](https://solana.com/docs/programs/testing/mollusk) - Official Mollusk documentation
      - [Solana Test Validator](https://docs.solana.com/developing/test-validator) - Local validator for testing
      - [Anchor Testing](https://book.anchor-lang.com/anchor_in_depth/testing.html) - Anchor test framework
      
      ### Deployment & Verification
      - [Solana Verify](https://github.com/Ellipsis-Labs/solana-verifiable-build) - Verifiable builds
      - [Verified Builds Docs](https://solana.com/docs/programs/verified-builds) - Official guide
      
      ## Learning Paths
      
      ### Official Courses
      - [Native Rust Development](https://solana.com/developers/courses/native-onchain-development) - Build with native Rust
      - [Anchor Development](https://solana.com/developers/courses/onchain-development) - Build with Anchor
      - [Program Security](https://solana.com/developers/courses/program-security) - Security fundamentals
      
      ### Community Tutorials
      - [RareSkills Solana Course](https://www.rareskills.io/solana-tutorial) - Comprehensive course for EVM developers
      - [Anchor for EVM Developers](https://0xkowloon.gitbook.io/anchor-for-evm-developers) - Quick Anchor intro
      - [Ackee Solana Handbook](https://ackee.xyz/solana/book/latest/) - Development guide
      
      ### Rust Learning
      - [Rust Book](https://doc.rust-lang.org/book/) - Official Rust programming language book
      - [Rust by Example](https://doc.rust-lang.org/rust-by-example/) - Learn Rust through examples
      
      ### Advanced Topics
      - [Solana Architecture](https://docs.solana.com/cluster/overview) - How Solana works
      - [Sealevel Runtime](https://docs.solana.com/developing/programming-model/overview) - SVM execution model
      - [Account Model](https://solana.com/docs/core/accounts) - Deep dive into accounts
      
      ## Community & Support
      
      ### Q&A Platforms
      - [Solana Stack Exchange](https://solana.stackexchange.com/) - Q&A for Solana development
      - [Anchor Discussions](https://github.com/coral-xyz/anchor/discussions) - GitHub discussions
      
      ### Chat & Forums
      - [Solana Discord](https://discord.gg/solana) - Official Solana community
      - [Anchor Discord](https://discord.gg/srmqvxf) - Anchor-specific support
      - [Solana Tech Discord](https://discord.gg/solana) - Technical discussions
      
      ### Blogs & Newsletters
      - [Helius Blog](https://www.helius.dev/blog) - Frequent Solana developer content
      - [Solana Foundation Blog](https://solana.com/news) - Official updates
      - [Pine Analytics Substack](https://substack.com/@pineanalytics1) - Protocol deep dives
      
      ## Developer Tools & Libraries
      
      ### Rust Crates
      - [solana-program](https://docs.rs/solana-program) - Core program library
      - [anchor-lang](https://docs.rs/anchor-lang) - Anchor framework
      - [anchor-spl](https://docs.rs/anchor-spl) - SPL token integration
      - [borsh](https://docs.rs/borsh) - Binary serialization
      - [spl-token](https://docs.rs/spl-token) - Token program library
      - [spl-token-2022](https://docs.rs/spl-token-2022) - Token Extensions program
      
      ### TypeScript/JavaScript
      
      **dApp Client (recommended for frontends):**
      - [@solana/client](https://www.npmjs.com/package/@solana/client) + [@solana/react-hooks](https://www.npmjs.com/package/@solana/react-hooks) - Framework-kit: wallet connection, React hooks, SOL/SPL transfers, transaction pool ([GitHub](https://github.com/solana-foundation/framework-kit))
      - [@solana/web3-compat](https://www.npmjs.com/package/@solana/web3-compat) - Drop-in bridge for migrating `@solana/web3.js` v1 code to Kit primitives
      
      **Low-level SDK:**
      - [@solana/kit](https://solanakit.org) - Solana JavaScript SDK v6 (functional, tree-shakeable). Use for server-side scripts, bots, CLIs
      - [@solana/compat](https://www.npmjs.com/package/@solana/compat) - Bridge between Kit 6.x and legacy `@solana/web3.js` v1 types
      
      **Framework-specific:**
      - [@coral-xyz/anchor](https://www.npmjs.com/package/@coral-xyz/anchor) - Anchor TypeScript client (requires `@solana/web3.js` v1 as peer dependency)
      - [@solana/spl-token](https://www.npmjs.com/package/@solana/spl-token) - SPL Token JS library
      - [Umi Framework](https://github.com/metaplex-foundation/umi) - Modular framework by Metaplex
      
      > **Note:** `@solana/web3.js` v1 is in maintenance mode. For new dApps, use framework-kit (`@solana/client` + `@solana/react-hooks`). For server-side/CLI code, use `@solana/kit` 6.x. The only reason to install `@solana/web3.js` is as a peer dependency required by Anchor or Light Protocol SDKs. See [client-development.md](client-development.md) for the full decision guide.
      
      ### Python
      - [solana-py](https://github.com/michaelhly/solana-py) - Solana Python SDK
      - [anchorpy](https://github.com/kevinheavey/anchorpy) - Anchor Python client
      
      ## RPC Providers
      
      ### Free Tier Available
      - [Helius](https://www.helius.dev/) - Developer-friendly RPC with generous free tier
      - [QuickNode](https://www.quicknode.com/) - Global RPC network
      - [Alchemy](https://www.alchemy.com/solana) - RPC with enhanced APIs
      - [Triton](https://triton.one/) - High-performance RPC
      - [Public RPC Endpoints](https://docs.solana.com/cluster/rpc-endpoints) - Free public endpoints
      
      ## Developer Communities
      
      ### Learning Communities
      - [Solana Developers](https://github.com/solana-developers) - Official developer org
      - [Superteam](https://superteam.fun/) - Global Solana community
      - [Blueshift](https://learn.blueshift.gg/) - Interactive learning platform
      
      ### Regional Communities
      - [Superteam Germany](https://superteam.fun/germany)
      - [Superteam India](https://superteam.fun/india)
      - [Superteam Vietnam](https://superteam.fun/vietnam)
      - [Superteam LatAm](https://superteam.fun/latam)
      
      ## Additional Resources
      
      ### Developer Guides
      - [Solana Developer Guide](https://solana.com/developers/guides) - How-to guides
      - [Solana Bootcamp](https://github.com/solana-developers/developer-bootcamp-2024) - Workshop materials
      - [Anchor Examples Repo](https://github.com/coral-xyz/anchor/tree/master/tests) - Anchor test programs
      
      ### Tool Documentation
      - [Cargo Build SBF](https://docs.solana.com/cli/deploy-a-program) - Building programs
      - [Solana Program Deploy](https://docs.solana.com/cli/deploy-a-program) - Deployment guide
      - [Solana Keygen](https://docs.solana.com/cli/wallets/paper) - Keypair management
      
      ### Ecosystem Tools
      - [Metaplex](https://www.metaplex.com/) - NFT infrastructure
      - [Squads](https://squads.so/) - Multisig and treasury management
      - [Dialect](https://www.dialect.to/) - Messaging and notifications
      
      ## Security Resources
      
      ### Curated Security Lists
      - [Awesome Solana Security (0xMacro)](https://github.com/0xMacro/awesome-solana-security) - **Actively maintained**, comprehensive resource list
      - [Rektoff Security Roadmap](https://github.com/Rektoff/Security-Roadmap-for-Solana-applications) - Full lifecycle security strategy
      - [SlowMist Best Practices](https://github.com/slowmist/solana-smart-contract-security-best-practices) - Common pitfalls with examples
      - [Ackee Solana Handbook](https://ackee.xyz/solana/book/latest/) - Comprehensive development guide
      
      ### Security Guides & Articles
      - [Helius Security Guide](https://www.helius.dev/blog/a-hitchhikers-guide-to-solana-program-security) - Common vulnerabilities explained
      - [Neodyme Breakpoint Workshop](https://github.com/neodyme-labs/neodyme-breakpoint-workshop) - Hands-on security training
      - [Solana Security Course](https://solana.com/developers/courses/program-security) - Official security course
      - [Asymmetric Research CPI Vulnerabilities](https://blog.asymmetric.re/invocation-security-navigating-vulnerabilities-in-solana-cpis/)
      - [Ottersec Lamport Transfers](https://osec.io/blog/2025-05-14-king-of-the-sol) - SOL transfer vulnerabilities
      - [Infect3d Auditing Essentials](https://www.infect3d.xyz/blog/solana-quick-start)
      
      ### Vulnerability Collections
      - [Urataps Audit Examples](https://github.com/urataps/solana-audit-examples) - Programs with vulnerabilities
      - [ImmuneBytes Attack Vectors](https://github.com/ImmuneBytes-Security-Audit/Blockchain-Attack-Vectors/tree/main/Solana%20Attack%20Vectors)
      - [Exvul Security Guide](https://exvul.com/rust-smart-contract-security-guide-in-solana/)
      - [Nirlin Advanced Vulnerabilities](https://substack.com/inbox/post/164534668)
      
      ### Video Tutorials
      - [Zigtur Security Walkthrough](https://www.youtube.com/watch?v=xd6qfY-GDYY)
      - [M4rio Security Walkthrough](https://www.youtube.com/watch?v=q4z8tIi43lg)
      
      ### Token-2022 Security
      - [Offside Token-2022 Part 1](https://blog.offside.io/p/token-2022-security-best-practices-part-1)
      - [Offside Token-2022 Part 2](https://blog.offside.io/p/token-2022-security-best-practices-part-2)
      - [Neodyme Token-2022 Security](https://neodyme.io/en/blog/token-2022)
      
      ### Deep Dives & Research
      - [r0bre's 100 Daily Solana Tips](https://accretionxyz.substack.com/p/r0bres-100-daily-solana-tips)
      - [Accretion Hidden IDL Instructions](https://accretionxyz.substack.com/p/hidden-idl-instructions-and-how-to)
      - [Farouk ELALEM Under the Hood](https://ubermensch.blog/under-the-hood-of-solana-program-execution-from-rust-code-to-sbf-bytecode)
      - [Lucrative_Panda Security History](https://medium.com/@lucrativepanda/a-comprehensive-analysis-of-solanas-security-history-all-incidents-impacts-and-evolution-up-to-1b1564c7ddfe)
      
      ## Audit Reports
      
      Study real security audits to learn from actual vulnerabilities:
      
      ### Code4rena
      - [Pump Science](https://code4rena.com/reports/2025-01-pump-science) - 2 High, 3 Medium
      
      ### Sherlock
      - [Orderly](https://audits.sherlock.xyz/contests/524/report) - 2 High, 1 Medium
      - [WOOFi](https://audits.sherlock.xyz/contests/535/report) - 2 High, 3 Medium
      
      ### Cantina
      Contact `0xmorph` in Cantina Discord for read access:
      - [Grass](https://cantina.xyz/competitions/3211ee0d-133f-43a0-837e-8dc1ecfaa424) - 13 High, 6 Medium
      - [Olas](https://cantina.xyz/competitions/829164bf-7fba-4b84-a6b8-76652205bd97) - 2 High, 3 Medium
      - [Tensor](https://cantina.xyz/competitions/21787352-de2c-4a77-af09-cc0a250d1f04) - 5 High, 10 Medium
      - [ZetaChain](https://cantina.xyz/competitions/80a33cf0-ad69-4163-a269-d27756aacb5e) - 6 High, 27 Medium
      - [Inclusive Finance](https://cantina.xyz/competitions/3eff5a8f-b73a-4cfe-8c54-546b475548f0) - 45 High, 25 Medium
      - [Reserve Index](https://cantina.xyz/code/8b94becd-54e7-41cd-88e6-caae7becc76a) - 10 High, 11 Medium
      
      ## Security & Analysis Tools
      
      - [Trident](https://github.com/Ackee-Blockchain/trident) - Fuzz testing framework (v0.12.0+: Token-2022 program support, Manually Guided Fuzzing for targeted instruction sequences, solana-sdk 2.3+ compatibility)
      - [Certora Prover](https://docs.certora.com/en/latest/docs/solana/index.html) - Formal verification
      - [Sec3 IDL Guesser](https://github.com/sec3-service/IDLGuesser) - Reverse engineer IDLs
      - [Anchor X-ray](https://github.com/crytic/anchorx-ray) - Visualize accounts (Trail of Bits)
      - [Anchor Version Detector](https://github.com/johnsaigle/anchor-version-detector) - Compatibility checker
      
      ## CTFs & Practice
      
      ### Capture The Flag
      - [Ackee Solana CTF](https://github.com/Ackee-Blockchain/Solana-Auditors-Bootcamp/tree/master/Capture-the-Flag)
      
      ### Bootcamps
      - [Rektoff 6-Week Bootcamp](https://www.rektoff.xyz/bootcamp) - Free, Solana Foundation supported
      - [Ackee Auditors Bootcamp](https://ackee.xyz/solana-auditors-bootcamp)
      
      ## Security Firms
      
      Top firms for Solana security audits:
      - [Runtime Verification](https://runtimeverification.com/)
      - [OtterSec](https://osec.io/)
      - [Neodyme](https://neodyme.io/en/)
      - [Sec3](https://www.sec3.dev/)
      - [Zellic](https://www.zellic.io/)
      - [Ackee Blockchain](https://ackee.xyz/)
      - [Hexens](https://hexens.io/)
      - [Trail of Bits](https://www.trailofbits.com/)
      - [Kudelski Security](https://kudelskisecurity.com/)
      - [Cantina](https://cantina.xyz/)
      - [Certora](https://www.certora.com/)
      - [Sherlock](https://www.sherlock.xyz/)
      
      ## Version Information
      
      **Current versions (as of February 2026):**
      - Latest Anchor: 0.32.1
      - Recommended Solana CLI: 2.3.0+ (Agave)
      - Rust minimum: 1.89.0+ (required for Anchor 0.32.0+)
      - @solana/client (framework-kit): 1.7.0
      - @solana/react-hooks: 1.4.1
      - @solana/kit: 6.1.0 (standalone) / 5.x (used internally by framework-kit)
      - Solana program library: 2.0+
      
      **Updating tools:**
      ```bash
      # Update Solana CLI
      solana-install update
      
      # Update Anchor
      avm install latest
      avm use latest
      
      # Update Rust
      rustup update
      ```
      
      ---
      
      **Note:** Security resources above are a curated collection from the Awesome Solana Security repository and other trusted sources, selected for quality, maintenance status, and relevance to modern Solana development and auditing.
      
    • security-checklists.md 9 KB
      # Security Checklists
      
      Comprehensive validation checklists for Solana program security reviews.
      
      ## Account Validation Checklist
      
      For every account in every instruction:
      
      - [ ] **Signer validation**: Uses `Signer<'info>` or `is_signer` check when needed
      - [ ] **Owner validation**: Uses `#[account(owner = ...)]` or manual owner check
      - [ ] **Writable checks**: Properly marked `mut` when account data will be modified
      - [ ] **Account initialization**: Checks if account is initialized before use
      - [ ] **PDA validation**: Validates seeds and uses canonical bump
      - [ ] **Discriminator check**: For `AccountLoader`, validates account type
      - [ ] **Account relationships**: Uses `has_one` for related accounts
      
      ```rust
      // Complete account validation example
      #[derive(Accounts)]
      pub struct SecureInstruction<'info> {
          #[account(
              mut,
              has_one = authority,  // Relationship validation
              seeds = [b"vault", authority.key().as_ref()],
              bump,  // Canonical bump
          )]
          pub vault: Account<'info, Vault>,
      
          pub authority: Signer<'info>,  // Signer required
      
          #[account(
              mut,
              constraint = token_account.owner == authority.key(),  // Custom validation
          )]
          pub token_account: Account<'info, TokenAccount>,
      
          pub token_program: Program<'info, Token>,  // Program validation
      }
      ```
      
      ## Arithmetic Safety Checklist
      
      For all mathematical operations:
      
      - [ ] **Addition**: Uses `checked_add()` instead of `+`
      - [ ] **Subtraction**: Uses `checked_sub()` instead of `-`
      - [ ] **Multiplication**: Uses `checked_mul()` instead of `*`
      - [ ] **Division**: Uses `checked_div()` instead of `/`
      - [ ] **Division by zero**: Validates divisor is non-zero
      - [ ] **Precision loss**: Uses `try_floor_u64()` instead of `try_round_u64()` to prevent arbitrage
      - [ ] **Avoid saturating**: Does not use `saturating_*` methods (they hide errors)
      - [ ] **Proper error handling**: All arithmetic wrapped in `ok_or(error)?`
      
      ```rust
      // Secure arithmetic examples
      let total = balance
          .checked_add(amount)
          .ok_or(ErrorCode::Overflow)?;
      
      let share = total
          .checked_div(denominator)
          .ok_or(ErrorCode::DivisionByZero)?;
      
      // For Decimal types (token amounts)
      let liquidity = Decimal::from(collateral_amount)
          .try_div(rate)?
          .try_floor_u64()?;  // Not try_round_u64()!
      ```
      
      ## PDA and Account Security Checklist
      
      - [ ] **Canonical bump**: PDAs use `bump` in seeds constraint (not hardcoded)
      - [ ] **Unique seeds**: Seeds include unique identifier (user pubkey, mint, etc.)
      - [ ] **No duplicate accounts**: Same account not used twice as mutable
      - [ ] **Init vs init_if_needed**: Uses `init` with proper validation, not `init_if_needed`
      - [ ] **has_one constraints**: Related accounts validated with `has_one`
      - [ ] **Custom constraints**: Complex validation uses `constraint` expression
      - [ ] **Seed collision**: Seeds designed to prevent collisions
      
      ```rust
      // Secure PDA patterns
      #[account(
          init,
          payer = authority,
          space = 8 + UserAccount::INIT_SPACE,
          seeds = [
              b"user",
              authority.key().as_ref(),  // Unique to user
              mint.key().as_ref(),        // Unique to mint
          ],
          bump
      )]
      pub user_account: Account<'info, UserAccount>,
      ```
      
      ## CPI Security Checklist
      
      For all Cross-Program Invocations:
      
      - [ ] **Program validation**: Target program is validated (uses `Program<'info, T>`)
      - [ ] **Signer seeds**: PDA signers pass seeds correctly in `invoke_signed`
      - [ ] **Return value checking**: CPI success doesn't guarantee correct state
      - [ ] **Account reloading**: Reload accounts after CPI that may modify them
      - [ ] **No arbitrary CPI**: Program account is not user-controlled
      - [ ] **Privilege escalation**: CPI doesn't grant unexpected permissions
      
      ```rust
      // Secure CPI pattern
      #[derive(Accounts)]
      pub struct SecureCPI<'info> {
          pub token_program: Program<'info, Token>,  // Type-validated
          // ... other accounts
      }
      
      pub fn secure_cpi(ctx: Context<SecureCPI>) -> Result<()> {
          // CPI with validated program
          token::transfer(
              CpiContext::new(
                  ctx.accounts.token_program.to_account_info(),
                  Transfer {
                      from: ctx.accounts.from.to_account_info(),
                      to: ctx.accounts.to.to_account_info(),
                      authority: ctx.accounts.authority.to_account_info(),
                  },
              ),
              amount,
          )?;
      
          // Reload account after CPI
          ctx.accounts.from.reload()?;
      
          // Validate expected state
          require!(
              ctx.accounts.from.amount == expected_amount,
              ErrorCode::InvalidState
          );
      
          Ok(())
      }
      ```
      
      ## Oracle and External Data Checklist
      
      For Pyth, Switchboard, or other oracles:
      
      - [ ] **Oracle status**: Validates oracle is in valid state (Trading status for Pyth)
      - [ ] **Price staleness**: Checks timestamp is recent enough
      - [ ] **Oracle owner**: Validates oracle account owner is correct program
      - [ ] **Confidence interval**: For Pyth, checks confidence is acceptable
      - [ ] **Price validity**: Validates price is within reasonable bounds
      - [ ] **Fallback handling**: Has strategy for oracle failure
      
      ```rust
      // Pyth oracle validation
      pub fn validate_pyth_price(
          pyth_account: &AccountInfo,
          clock: &Clock,
      ) -> Result<i64> {
          // Validate owner
          require_keys_eq!(
              *pyth_account.owner,
              PYTH_PROGRAM_ID,
              ErrorCode::InvalidOracle
          );
      
          let price_data = pyth_account.try_borrow_data()?;
          let price_feed = load_price_feed_from_account_info(pyth_account)?;
      
          // Check status
          require!(
              price_feed.agg.status == PriceStatus::Trading,
              ErrorCode::InvalidOracleStatus
          );
      
          // Check staleness (e.g., max 60 seconds old)
          let max_age = 60;
          require!(
              clock.unix_timestamp - price_feed.agg.publish_time <= max_age,
              ErrorCode::StalePrice
          );
      
          // Check confidence (example: max 1% of price)
          let confidence_threshold = price_feed.agg.price / 100;
          require!(
              price_feed.agg.conf <= confidence_threshold as u64,
              ErrorCode::OracleConfidenceTooLow
          );
      
          Ok(price_feed.agg.price)
      }
      ```
      
      ## Token Program Security Checklist
      
      ### SPL Token Checks
      
      - [ ] **ATA validation**: Associated Token Accounts validated correctly
      - [ ] **Mint authority**: Proper checks on mint authority for minting operations
      - [ ] **Freeze authority**: Handles frozen accounts appropriately
      - [ ] **Delegate handling**: Resets delegate when needed
      - [ ] **Close authority**: Resets close authority on owner change
      
      ### Token-2022 Specific Checks
      
      - [ ] **Transfer hooks**: Handles transfer hook extensions correctly
      - [ ] **Extension data**: Validates all active extensions
      - [ ] **Confidential transfers**: Properly handles confidential transfer extension
      - [ ] **Transfer fees**: Respects transfer fee extension
      - [ ] **Permanent delegate**: Checks for permanent delegate extension
      - [ ] **Additional rent**: Accounts for extension rent requirements
      
      ```rust
      // Token-2022 with extensions
      use spl_token_2022::extension::{
          BaseStateWithExtensions,
          StateWithExtensions,
      };
      
      pub fn safe_token_2022_transfer(
          /* accounts */
      ) -> Result<()> {
          // Check for transfer hook
          let mint_data = mint.try_borrow_data()?;
          let mint_with_extensions = StateWithExtensions::<Mint>::unpack(&mint_data)?;
      
          if let Ok(transfer_hook) = mint_with_extensions.get_extension::<TransferHook>() {
              // Handle transfer hook properly
              // ... transfer hook logic
          }
      
          // Check for transfer fee
          if let Ok(transfer_fee_config) = mint_with_extensions.get_extension::<TransferFeeConfig>() {
              // Calculate and handle fees
              // ... fee logic
          }
      
          // Proceed with transfer
          Ok(())
      }
      ```
      
      ## Architecture Review Checklist
      
      - [ ] **PDA design**: PDAs used appropriately vs keypair accounts
      - [ ] **Account space**: Space calculation uses `InitSpace` derive
      - [ ] **Error handling**: Custom errors with descriptive messages
      - [ ] **Event emission**: Critical state changes emit events
      - [ ] **Rent exemption**: All accounts are rent-exempt
      - [ ] **Transaction size**: Stays within ~1232 byte limit
      - [ ] **Compute budget**: Optimized to stay under compute limits
      - [ ] **Upgradeability**: Considers upgrade path and account versioning
      
      ## Testing Checklist
      
      - [ ] **Unit tests**: Each instruction has unit tests
      - [ ] **Fuzz tests**: Arithmetic operations have fuzz tests (Trident)
      - [ ] **Integration tests**: Realistic multi-instruction scenarios
      - [ ] **Negative tests**: Tests for expected failures
      - [ ] **PDA tests**: Tests for seed collisions
      - [ ] **Edge cases**: Zero amounts, max values, overflow boundaries
      - [ ] **Concurrency**: Tests for transaction ordering issues
      - [ ] **Devnet testing**: Deployed and tested on devnet
      
      ```rust
      // Example test structure
      #[cfg(test)]
      mod tests {
          use super::*;
      
          #[test]
          fn test_normal_case() {
              // Test expected behavior
          }
      
          #[test]
          #[should_panic(expected = "Overflow")]
          fn test_overflow() {
              // Test arithmetic overflow protection
          }
      
          #[test]
          fn test_unauthorized_access() {
              // Test fails with wrong signer
          }
      
          #[test]
          fn test_edge_case_zero_amount() {
              // Test zero amount handling
          }
      }
      ```
      
    • security-fundamentals.md 26.9 KB
      # Solana Program Security & Validation
      
      This reference provides comprehensive security guidance for native Rust Solana program development, covering validation patterns, common vulnerabilities, and defensive programming practices.
      
      ## Table of Contents
      
      1. [Security Mindset](#security-mindset)
      2. [Core Validation Patterns](#core-validation-patterns)
      3. [Common Vulnerabilities](#common-vulnerabilities)
      4. [Input Validation](#input-validation)
      5. [State Management Security](#state-management-security)
      6. [Arithmetic Safety](#arithmetic-safety)
      7. [Re-entrancy Protection](#re-entrancy-protection)
      8. [Security Checklist](#security-checklist)
      
      ---
      
      ## Security Mindset
      
      ### Think Like an Attacker
      
      **The fundamental principle of secure programming: ask "How do I break this?"**
      
      Presented at Breakpoint 2021 by [Neodyme](https://workshop.neodyme.io/), this mindset shift is critical:
      
      - **Don't just test expected functionality** - explore how it can be broken
      - **All programs can be exploited** - the goal is to make it as difficult as possible
      - **You control nothing** - once deployed, you can't control what transactions are sent
      - **Assume malicious input** - every account, every parameter, every edge case
      
      ### The Harsh Reality
      
      ```
      ┌─────────────────────────────────────────┐
      │ Your Program (Deployed)                 │
      ├─────────────────────────────────────────┤
      │ • No control over incoming transactions │
      │ • No control over accounts passed in    │
      │ • No control over instruction data      │
      │ • No control over timing                │
      └─────────────────────────────────────────┘
                 ▲            ▲            ▲
                 │            │            │
           Legitimate    Malicious     Buggy
              User        Attacker     Client
      ```
      
      **Your only control:** How your program handles inputs.
      
      ### Security is Not Optional
      
      **Example Impact:**
      
      Without proper validation, a simple "update note" function becomes:
      - ❌ Anyone can update anyone's notes
      - ❌ Drain program funds
      - ❌ Corrupt global state
      - ❌ Brick the entire program
      
      **With validation:**
      - ✅ Only note author can update
      - ✅ Funds are protected
      - ✅ State remains consistent
      - ✅ Program operates as intended
      
      ---
      
      ## Core Validation Patterns
      
      ### 1. Signer Checks
      
      **Purpose:** Verify that an account signed the transaction, authorizing the operation.
      
      **When Required:**
      - Transferring funds from an account
      - Modifying user-specific data
      - Any privileged operation
      
      **Pattern:**
      
      ```rust
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          program_error::ProgramError,
          msg,
      };
      
      pub fn check_signer(account: &AccountInfo) -> ProgramResult {
          if !account.is_signer {
              msg!("Missing required signature");
              return Err(ProgramError::MissingRequiredSignature);
          }
          Ok(())
      }
      ```
      
      **Real-World Example:**
      
      ```rust
      pub fn update_user_profile(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          new_name: String,
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let user = next_account_info(account_info_iter)?;
          let profile_pda = next_account_info(account_info_iter)?;
      
          // CRITICAL: Verify user signed the transaction
          if !user.is_signer {
              msg!("User must sign to update profile");
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          // Validate PDA belongs to this user
          let (expected_pda, _) = Pubkey::find_program_address(
              &[b"profile", user.key.as_ref()],
              program_id,
          );
      
          if expected_pda != *profile_pda.key {
              msg!("Profile PDA doesn't match user");
              return Err(ProgramError::InvalidAccountData);
          }
      
          // Safe to update
          let mut profile = UserProfile::try_from_slice(&profile_pda.data.borrow())?;
          profile.name = new_name;
          profile.serialize(&mut &mut profile_pda.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      ### 2. Ownership Checks
      
      **Purpose:** Verify an account is owned by the expected program.
      
      **When Required:**
      - Before reading/writing account data
      - When validating PDAs
      - Before performing any account-specific operations
      
      **Pattern:**
      
      ```rust
      pub fn check_ownership(
          account: &AccountInfo,
          expected_owner: &Pubkey,
      ) -> ProgramResult {
          if account.owner != expected_owner {
              msg!("Account owner mismatch");
              return Err(ProgramError::IllegalOwner);
          }
          Ok(())
      }
      ```
      
      **Common Use Cases:**
      
      ```rust
      // 1. Verify program owns its PDA
      if note_pda.owner != program_id {
          msg!("Note account not owned by this program");
          return Err(ProgramError::IllegalOwner);
      }
      
      // 2. Verify account owned by System Program (user wallet)
      use solana_program::system_program;
      
      if wallet.owner != &system_program::ID {
          msg!("Expected a system account (wallet)");
          return Err(ProgramError::IllegalOwner);
      }
      
      // 3. Verify account owned by Token Program
      use spl_token::ID as TOKEN_PROGRAM_ID;
      
      if token_account.owner != &TOKEN_PROGRAM_ID {
          msg!("Expected a token account");
          return Err(ProgramError::IllegalOwner);
      }
      ```
      
      ### 3. PDA Validation
      
      **Purpose:** Ensure a provided PDA matches the expected derivation.
      
      **Critical for Security:** Multiple bumps can derive different PDAs. Always use canonical bump.
      
      **Pattern:**
      
      ```rust
      pub fn validate_pda(
          pda_account: &AccountInfo,
          seeds: &[&[u8]],
          program_id: &Pubkey,
      ) -> Result<u8, ProgramError> {
          // Derive expected PDA with canonical bump
          let (expected_pda, bump_seed) = Pubkey::find_program_address(seeds, program_id);
      
          // Validate match
          if expected_pda != *pda_account.key {
              msg!("Invalid PDA derivation");
              return Err(ProgramError::InvalidSeeds);
          }
      
          Ok(bump_seed)
      }
      ```
      
      **Complete Validation:**
      
      ```rust
      pub fn validate_user_vault(
          program_id: &Pubkey,
          user: &AccountInfo,
          vault_pda: &AccountInfo,
      ) -> ProgramResult {
          // 1. Derive expected PDA
          let (expected_pda, _bump) = Pubkey::find_program_address(
              &[b"vault", user.key.as_ref()],
              program_id,
          );
      
          // 2. Validate address match
          if expected_pda != *vault_pda.key {
              msg!("Vault PDA seeds don't match");
              return Err(ProgramError::InvalidSeeds);
          }
      
          // 3. Validate ownership
          if vault_pda.owner != program_id {
              msg!("Vault not owned by program");
              return Err(ProgramError::IllegalOwner);
          }
      
          // 4. Validate initialization
          let vault_data = VaultAccount::try_from_slice(&vault_pda.data.borrow())?;
          if !vault_data.is_initialized {
              msg!("Vault not initialized");
              return Err(ProgramError::UninitializedAccount);
          }
      
          Ok(())
      }
      ```
      
      ### 4. Initialization Checks
      
      **Purpose:** Prevent re-initialization or use of uninitialized accounts.
      
      **Pattern: Discriminator Field**
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct AccountData {
          pub is_initialized: bool,
          // ... other fields
      }
      
      // On creation - ensure NOT initialized
      if account_data.is_initialized {
          msg!("Account already initialized");
          return Err(ProgramError::AccountAlreadyInitialized);
      }
      
      account_data.is_initialized = true;
      
      // On update - ensure IS initialized
      if !account_data.is_initialized {
          msg!("Account not initialized");
          return Err(ProgramError::UninitializedAccount);
      }
      ```
      
      **Advanced: Enum Discriminator**
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize, PartialEq)]
      pub enum AccountState {
          Uninitialized,
          Initialized,
          Frozen,
          Closed,
      }
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct GameAccount {
          pub state: AccountState,
          pub player: Pubkey,
          pub score: u64,
      }
      
      // Validation
      let account = GameAccount::try_from_slice(&account_info.data.borrow())?;
      
      match account.state {
          AccountState::Uninitialized => {
              msg!("Account not initialized");
              return Err(ProgramError::UninitializedAccount);
          }
          AccountState::Frozen => {
              msg!("Account is frozen");
              return Err(ProgramError::InvalidAccountData);
          }
          AccountState::Closed => {
              msg!("Account is closed");
              return Err(ProgramError::InvalidAccountData);
          }
          AccountState::Initialized => {
              // Proceed
          }
      }
      ```
      
      ### 5. Account Type Validation
      
      **Purpose:** Ensure account contains the expected data structure.
      
      **Pattern: Type Discriminator**
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize, PartialEq)]
      #[repr(u8)]
      pub enum AccountType {
          Uninitialized = 0,
          UserProfile = 1,
          GameState = 2,
          Leaderboard = 3,
      }
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct GenericAccount {
          pub account_type: AccountType,
          // ... rest of data varies by type
      }
      
      // Validation
      pub fn validate_account_type(
          account_info: &AccountInfo,
          expected_type: AccountType,
      ) -> ProgramResult {
          let account = GenericAccount::try_from_slice(&account_info.data.borrow())?;
      
          if account.account_type != expected_type {
              msg!("Unexpected account type");
              return Err(ProgramError::InvalidAccountData);
          }
      
          Ok(())
      }
      ```
      
      ### 6. Writable Validation
      
      **Purpose:** Ensure accounts that need modification are marked writable.
      
      **Pattern:**
      
      ```rust
      pub fn check_writable(account: &AccountInfo) -> ProgramResult {
          if !account.is_writable {
              msg!("Account must be writable");
              return Err(ProgramError::InvalidAccountData);
          }
          Ok(())
      }
      ```
      
      **Note:** Runtime enforces this, but explicit checks improve clarity and error messages.
      
      ---
      
      ## Common Vulnerabilities
      
      ### 1. Missing Signer Check
      
      **Vulnerability:**
      
      ```rust
      // ❌ VULNERABLE - no signer check
      pub fn withdraw_funds(
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let user = &accounts[0];
          let vault = &accounts[1];
      
          // Anyone can call this to withdraw anyone's funds!
          **user.lamports.borrow_mut() += amount;
          **vault.lamports.borrow_mut() -= amount;
      
          Ok(())
      }
      ```
      
      **Exploit:**
      ```
      Attacker creates transaction:
      - Passes victim's account as user
      - Drains vault to victim's account
      - Profits by intercepting the transaction or social engineering
      ```
      
      **Fix:**
      
      ```rust
      // ✅ SECURE - with signer check
      pub fn withdraw_funds(
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let user = &accounts[0];
          let vault = &accounts[1];
      
          if !user.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          **user.lamports.borrow_mut() += amount;
          **vault.lamports.borrow_mut() -= amount;
      
          Ok(())
      }
      ```
      
      ### 2. Missing Ownership Check
      
      **Vulnerability:**
      
      ```rust
      // ❌ VULNERABLE - no ownership check
      pub fn update_score(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          new_score: u64,
      ) -> ProgramResult {
          let player_account = &accounts[0];
      
          // Could be ANY account with matching data structure!
          let mut player = PlayerData::try_from_slice(&player_account.data.borrow())?;
          player.score = new_score;
          player.serialize(&mut &mut player_account.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      **Exploit:**
      ```
      Attacker creates a fake account:
      - Owned by attacker's program
      - Has same data structure
      - Passes it to victim program
      - Victim program modifies attacker's account!
      ```
      
      **Fix:**
      
      ```rust
      // ✅ SECURE - with ownership check
      pub fn update_score(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          new_score: u64,
      ) -> ProgramResult {
          let player_account = &accounts[0];
      
          // Verify ownership
          if player_account.owner != program_id {
              return Err(ProgramError::IllegalOwner);
          }
      
          let mut player = PlayerData::try_from_slice(&player_account.data.borrow())?;
          player.score = new_score;
          player.serialize(&mut &mut player_account.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      ### 3. PDA Substitution Attack
      
      **Vulnerability:**
      
      ```rust
      // ❌ VULNERABLE - accepts any PDA
      pub fn claim_reward(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let user = &accounts[0];
          let reward_pda = &accounts[1];
      
          // No PDA validation!
          let mut reward = RewardData::try_from_slice(&reward_pda.data.borrow())?;
          reward.claimed = true;
          reward.serialize(&mut &mut reward_pda.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      **Exploit:**
      ```
      Attacker passes someone else's reward PDA:
      - Creates transaction with victim's reward PDA
      - Claims victim's rewards
      - Victim loses rewards
      ```
      
      **Fix:**
      
      ```rust
      // ✅ SECURE - validates PDA derivation
      pub fn claim_reward(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let user = &accounts[0];
          let reward_pda = &accounts[1];
      
          // Validate PDA belongs to this user
          let (expected_pda, _) = Pubkey::find_program_address(
              &[b"reward", user.key.as_ref()],
              program_id,
          );
      
          if expected_pda != *reward_pda.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          let mut reward = RewardData::try_from_slice(&reward_pda.data.borrow())?;
          reward.claimed = true;
          reward.serialize(&mut &mut reward_pda.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      ### 4. Non-Canonical Bump
      
      **Vulnerability:**
      
      ```rust
      // ❌ VULNERABLE - accepts user-provided bump
      pub fn update_data(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          bump: u8,  // User provides bump!
      ) -> ProgramResult {
          let user = &accounts[0];
          let data_pda = &accounts[1];
      
          // Uses user's bump - could derive DIFFERENT PDA!
          let derived_pda = Pubkey::create_program_address(
              &[b"data", user.key.as_ref(), &[bump]],
              program_id,
          )?;
      
          if derived_pda != *data_pda.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // Proceeds with potentially wrong PDA
          // ...
      }
      ```
      
      **Exploit:**
      ```
      Multiple bumps derive different valid PDAs:
      - Canonical bump 254: User A's PDA
      - Bump 253: User B's PDA (also valid!)
      - Attacker uses bump 253 to access User B's data
      ```
      
      **Fix:**
      
      ```rust
      // ✅ SECURE - uses canonical bump only
      pub fn update_data(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let user = &accounts[0];
          let data_pda = &accounts[1];
      
          // Always use find_program_address (canonical bump)
          let (expected_pda, _bump) = Pubkey::find_program_address(
              &[b"data", user.key.as_ref()],
              program_id,
          );
      
          if expected_pda != *data_pda.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // Safe - validated with canonical bump
          // ...
      }
      ```
      
      ### 5. Type Cosplay Attack
      
      **Vulnerability:**
      
      ```rust
      // ❌ VULNERABLE - assumes account type
      pub fn admin_withdraw(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let admin_config = &accounts[0];
      
          // No type validation!
          let config = AdminConfig::try_from_slice(&admin_config.data.borrow())?;
      
          // Proceeds assuming it's actually an AdminConfig
          // ...
      }
      ```
      
      **Exploit:**
      ```
      Attacker creates fake account:
      - UserProfile with same memory layout as AdminConfig
      - First field happens to match admin pubkey format
      - Deserializes successfully as AdminConfig
      - Attacker gains admin privileges!
      ```
      
      **Fix:**
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct AdminConfig {
          pub discriminator: [u8; 8],  // Type identifier
          pub admin: Pubkey,
          // ... other fields
      }
      
      const ADMIN_CONFIG_DISCRIMINATOR: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
      
      // ✅ SECURE - validates type
      pub fn admin_withdraw(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let admin_config = &accounts[0];
      
          let config = AdminConfig::try_from_slice(&admin_config.data.borrow())?;
      
          // Validate discriminator
          if config.discriminator != ADMIN_CONFIG_DISCRIMINATOR {
              msg!("Invalid account type");
              return Err(ProgramError::InvalidAccountData);
          }
      
          // Safe - type validated
          // ...
      }
      ```
      
      ### 6. Uninitialized Account Reuse
      
      **Vulnerability:**
      
      ```rust
      // ❌ VULNERABLE - no initialization check
      pub fn update_balance(
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let balance_account = &accounts[0];
      
          let mut balance = BalanceData::try_from_slice(&balance_account.data.borrow())?;
      
          // What if this account was never initialized?
          // Default values could lead to undefined behavior
          balance.amount += amount;
      
          balance.serialize(&mut &mut balance_account.data.borrow_mut()[..])?;
          Ok(())
      }
      ```
      
      **Fix:**
      
      ```rust
      // ✅ SECURE - checks initialization
      pub fn update_balance(
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let balance_account = &accounts[0];
      
          let mut balance = BalanceData::try_from_slice(&balance_account.data.borrow())?;
      
          if !balance.is_initialized {
              msg!("Account not initialized");
              return Err(ProgramError::UninitializedAccount);
          }
      
          balance.amount += amount;
          balance.serialize(&mut &mut balance_account.data.borrow_mut()[..])?;
          Ok(())
      }
      ```
      
      ---
      
      ## Input Validation
      
      ### Validate All Input Data
      
      **Never trust instruction data.** Always validate constraints.
      
      ```rust
      pub fn allocate_stat_points(
          accounts: &[AccountInfo],
          strength: u8,
          agility: u8,
          intelligence: u8,
      ) -> ProgramResult {
          let character_account = &accounts[0];
          let mut character = Character::try_from_slice(&character_account.data.borrow())?;
      
          // 1. Validate individual stat caps
          let new_strength = character.strength.checked_add(strength)
              .ok_or(ProgramError::ArithmeticOverflow)?;
      
          if new_strength > 100 {
              msg!("Strength cannot exceed 100");
              return Err(ProgramError::InvalidArgument);
          }
      
          // 2. Validate total points spent
          let total_spent = (strength as u64)
              .checked_add(agility as u64)
              .and_then(|sum| sum.checked_add(intelligence as u64))
              .ok_or(ProgramError::ArithmeticOverflow)?;
      
          if total_spent > character.available_points {
              msg!("Insufficient available points");
              return Err(ProgramError::InsufficientFunds);
          }
      
          // 3. Safe to apply
          character.strength = new_strength;
          character.agility += agility;
          character.intelligence += intelligence;
          character.available_points -= total_spent;
      
          character.serialize(&mut &mut character_account.data.borrow_mut()[..])?;
          Ok(())
      }
      ```
      
      ### String Length Validation
      
      ```rust
      pub fn set_username(
          accounts: &[AccountInfo],
          username: String,
      ) -> ProgramResult {
          // Validate length
          if username.len() < 3 {
              msg!("Username too short (min 3 characters)");
              return Err(ProgramError::InvalidArgument);
          }
      
          if username.len() > 20 {
              msg!("Username too long (max 20 characters)");
              return Err(ProgramError::InvalidArgument);
          }
      
          // Validate characters (alphanumeric only)
          if !username.chars().all(|c| c.is_alphanumeric()) {
              msg!("Username must be alphanumeric");
              return Err(ProgramError::InvalidArgument);
          }
      
          // Safe to use
          // ...
      }
      ```
      
      ### Enum Validation
      
      ```rust
      #[derive(BorshDeserialize)]
      #[repr(u8)]
      pub enum Rarity {
          Common = 0,
          Uncommon = 1,
          Rare = 2,
          Epic = 3,
          Legendary = 4,
      }
      
      pub fn create_item(
          accounts: &[AccountInfo],
          rarity_value: u8,
      ) -> ProgramResult {
          // Validate enum range
          if rarity_value > 4 {
              msg!("Invalid rarity value");
              return Err(ProgramError::InvalidArgument);
          }
      
          let rarity: Rarity = unsafe {
              std::mem::transmute(rarity_value)
          };
      
          // Safe to use
          // ...
      }
      ```
      
      ---
      
      ## State Management Security
      
      ### Avoid Race Conditions
      
      **Problem:** Multiple transactions modifying shared state.
      
      **Solution:** Use account-level locking and atomic operations.
      
      ```rust
      pub fn claim_limited_reward(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let user = &accounts[0];
          let global_pool = &accounts[1];
          let user_claim = &accounts[2];
      
          // Load global state
          let mut pool = RewardPool::try_from_slice(&global_pool.data.borrow())?;
      
          // Check availability
          if pool.claimed >= pool.total_rewards {
              msg!("No rewards remaining");
              return Err(ProgramError::InsufficientFunds);
          }
      
          // Check user hasn't claimed
          let mut claim = UserClaim::try_from_slice(&user_claim.data.borrow())?;
          if claim.has_claimed {
              msg!("User already claimed");
              return Err(ProgramError::Custom(0));
          }
      
          // Atomically update both accounts
          pool.claimed += 1;
          claim.has_claimed = true;
      
          pool.serialize(&mut &mut global_pool.data.borrow_mut()[..])?;
          claim.serialize(&mut &mut user_claim.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      **Note:** Solana's account locking prevents true race conditions within a single transaction, but be aware of state assumptions across multiple transactions.
      
      ### Prevent State Corruption
      
      **Always validate state transitions:**
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize, PartialEq)]
      pub enum GameState {
          NotStarted,
          InProgress,
          Finished,
      }
      
      pub fn start_game(
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let game_account = &accounts[0];
          let mut game = Game::try_from_slice(&game_account.data.borrow())?;
      
          // Validate current state
          if game.state != GameState::NotStarted {
              msg!("Game already started or finished");
              return Err(ProgramError::InvalidAccountData);
          }
      
          // Transition state
          game.state = GameState::InProgress;
          game.start_time = Clock::get()?.unix_timestamp;
      
          game.serialize(&mut &mut game_account.data.borrow_mut()[..])?;
          Ok(())
      }
      ```
      
      ---
      
      ## Arithmetic Safety
      
      ### Always Use Checked Math
      
      **Rust default:** Integer overflow/underflow panics in debug, wraps in release.
      
      **Solana requirement:** Use checked operations to prevent wrapping.
      
      ```rust
      // ❌ DANGEROUS - can overflow/underflow
      let total = a + b;
      let remaining = balance - withdrawal;
      
      // ✅ SAFE - returns error on overflow/underflow
      let total = a.checked_add(b)
          .ok_or(ProgramError::ArithmeticOverflow)?;
      
      let remaining = balance.checked_sub(withdrawal)
          .ok_or(ProgramError::InsufficientFunds)?;
      ```
      
      ### Common Checked Operations
      
      ```rust
      // Addition
      let sum = a.checked_add(b)
          .ok_or(ProgramError::ArithmeticOverflow)?;
      
      // Subtraction
      let diff = a.checked_sub(b)
          .ok_or(ProgramError::InsufficientFunds)?;
      
      // Multiplication
      let product = a.checked_mul(b)
          .ok_or(ProgramError::ArithmeticOverflow)?;
      
      // Division
      let quotient = a.checked_div(b)
          .ok_or(ProgramError::InvalidArgument)?;  // b could be 0
      
      // Power
      let power = base.checked_pow(exponent)
          .ok_or(ProgramError::ArithmeticOverflow)?;
      ```
      
      ### Compound Operations
      
      ```rust
      // Calculate: (a + b) * c / d
      let result = a.checked_add(b)
          .and_then(|sum| sum.checked_mul(c))
          .and_then(|product| product.checked_div(d))
          .ok_or(ProgramError::ArithmeticOverflow)?;
      ```
      
      ### Precision Loss
      
      **Be careful with division:**
      
      ```rust
      // ❌ Loses precision
      let fee = amount / 100;  // 1.5% becomes 1%
      
      // ✅ Better - multiply first, then divide
      let fee = amount.checked_mul(15)
          .and_then(|v| v.checked_div(1000))
          .ok_or(ProgramError::ArithmeticOverflow)?;
      ```
      
      ---
      
      ## Re-entrancy Protection
      
      ### Solana's Built-in Protection
      
      **Good news:** Solana provides strong protection against traditional re-entrancy:
      
      - **Account locking:** Accounts are locked during transaction execution
      - **No concurrent modification:** Same account can't be modified by multiple instructions simultaneously
      - **Atomic transactions:** Either all instructions succeed or all fail
      
      ### Residual Risks
      
      **Cross-program state assumptions:**
      
      ```rust
      // ❌ RISKY - state can change between checks
      pub fn risky_operation(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let vault = &accounts[0];
          let mut vault_data = VaultData::try_from_slice(&vault.data.borrow())?;
      
          // Check balance
          let balance = **vault.lamports.borrow();
          if balance < 1000 {
              return Err(ProgramError::InsufficientFunds);
          }
      
          // CPI that might modify vault
          invoke(&some_instruction, accounts)?;
      
          // Balance might have changed!
          // Don't rely on previous check
          **vault.lamports.borrow_mut() -= 1000;  // Could underflow!
      
          Ok(())
      }
      ```
      
      **✅ Better:**
      
      ```rust
      pub fn safe_operation(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let vault = &accounts[0];
      
          // CPI first
          invoke(&some_instruction, accounts)?;
      
          // Check and modify atomically
          let balance = **vault.lamports.borrow();
          let new_balance = balance.checked_sub(1000)
              .ok_or(ProgramError::InsufficientFunds)?;
      
          **vault.lamports.borrow_mut() = new_balance;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Security Checklist
      
      ### Pre-Deployment Checklist
      
      **Account Validation:**
      - ✅ All signers verified with `is_signer`
      - ✅ All account owners checked
      - ✅ All PDAs validated with canonical bump
      - ✅ All accounts checked for initialization
      - ✅ Account types validated (discriminators)
      - ✅ Writable accounts verified
      
      **Input Validation:**
      - ✅ All numeric inputs range-checked
      - ✅ All string inputs length-limited
      - ✅ All enum values validated
      - ✅ All business logic constraints enforced
      
      **Arithmetic:**
      - ✅ All additions use `checked_add`
      - ✅ All subtractions use `checked_sub`
      - ✅ All multiplications use `checked_mul`
      - ✅ All divisions check for zero
      - ✅ No unsafe casting that could overflow
      
      **State Management:**
      - ✅ State transitions validated
      - ✅ Initialization flags checked
      - ✅ No assumptions across CPI boundaries
      - ✅ Atomicity maintained
      
      **Error Handling:**
      - ✅ All errors properly propagated
      - ✅ Meaningful error messages
      - ✅ No silent failures
      - ✅ Proper cleanup on errors
      
      ### Testing Checklist
      
      **Security Testing:**
      - ✅ Test with missing signers
      - ✅ Test with wrong account owners
      - ✅ Test with wrong PDAs (non-canonical bumps)
      - ✅ Test with uninitialized accounts
      - ✅ Test with re-initialized accounts
      - ✅ Test integer overflow/underflow
      - ✅ Test boundary conditions
      - ✅ Test with maximum values
      - ✅ Test with malicious input
      
      **Fuzzing:**
      - ✅ Random account combinations
      - ✅ Random instruction data
      - ✅ Random ordering
      - ✅ Edge case values
      
      ---
      
      ## Summary
      
      **Core Security Principles:**
      
      1. **Validate Everything** - Assume all inputs are malicious
      2. **Fail Fast** - Return errors immediately when validation fails
      3. **Use Checked Math** - Prevent integer overflow/underflow
      4. **Think Like an Attacker** - Ask "How do I break this?"
      5. **Test Malicious Cases** - Don't just test happy paths
      
      **The Three Pillars of Account Security:**
      
      ```rust
      // 1. Signer Check
      if !account.is_signer {
          return Err(ProgramError::MissingRequiredSignature);
      }
      
      // 2. Ownership Check
      if account.owner != expected_owner {
          return Err(ProgramError::IllegalOwner);
      }
      
      // 3. PDA Validation (if applicable)
      let (expected_pda, _) = Pubkey::find_program_address(&seeds, program_id);
      if expected_pda != *account.key {
          return Err(ProgramError::InvalidSeeds);
      }
      ```
      
      **Remember:** Once deployed, you have no control over what transactions are sent to your program. Your only defense is rigorous validation.
      
      Security is not a feature—it's a requirement.
      
    • security.md 14.2 KB
      # Security Best Practices for Solana Development
      
      Essential security principles and defensive programming patterns for building secure Solana programs with Anchor or native Rust.
      
      > **Note:** This guide focuses on defensive programming during development. For comprehensive security audits, vulnerability analysis, and attack vectors, see the auditing references in this skill: [security-fundamentals.md](security-fundamentals.md), [vulnerability-patterns.md](vulnerability-patterns.md), [security-checklists.md](security-checklists.md), [anchor-security.md](anchor-security.md), and [native-security.md](native-security.md).
      
      ## Table of Contents
      
      1. [Security Mindset](#security-mindset)
      2. [Core Security Rules](#core-security-rules)
      3. [Account Validation](#account-validation)
      4. [Arithmetic Safety](#arithmetic-safety)
      5. [PDA Security](#pda-security)
      6. [CPI Security](#cpi-security)
      7. [Common Pitfalls](#common-pitfalls)
      8. [Pre-Deployment Checklist](#pre-deployment-checklist)
      
      ---
      
      ## Security Mindset
      
      ### Think Like an Attacker
      
      **Fundamental principle:** Attackers control everything they send to your program.
      
      - ❌ Don't assume: "Users won't do that"
      - ❌ Don't assume: "The client validates this"
      - ❌ Don't assume: "This account must be correct"
      - ✅ Do validate: Every account, every parameter, every assumption
      
      ### You Control Nothing
      
      Once deployed, your program:
      - Cannot control which accounts are passed in
      - Cannot control instruction data
      - Cannot control timing or ordering
      - Cannot prevent malicious clients
      
      **Your only control:** How your program validates and handles inputs.
      
      ---
      
      ## Core Security Rules
      
      ### Rule 1: Validate Every Account
      
      **Always verify:**
      
      **Anchor:**
      ```rust
      #[derive(Accounts)]
      pub struct SecureInstruction<'info> {
          // ✅ Signer required
          pub authority: Signer<'info>,
      
          // ✅ Owner validation + relationship
          #[account(
              mut,
              has_one = authority,  // vault.authority == authority.key()
          )]
          pub vault: Account<'info, Vault>,
      
          // ✅ Program ID validation
          pub token_program: Program<'info, Token>,
      }
      ```
      
      **Native Rust:**
      ```rust
      // ✅ Signer check
      if !authority.is_signer {
          return Err(ProgramError::MissingRequiredSignature);
      }
      
      // ✅ Owner check
      if vault.owner != program_id {
          return Err(ProgramError::IllegalOwner);
      }
      
      // ✅ Program ID check
      if *token_program.key != spl_token::id() {
          return Err(ProgramError::IncorrectProgramId);
      }
      ```
      
      ### Rule 2: Use Checked Arithmetic
      
      **Never use:**
      - `+`, `-`, `*`, `/` operators directly
      - `saturating_*` methods (hide errors)
      - `unwrap()` or `expect()` on arithmetic
      
      **Always use:**
      ```rust
      // ✅ Checked operations
      let total = balance
          .checked_add(amount)
          .ok_or(ErrorCode::Overflow)?;
      
      let remaining = total
          .checked_sub(withdrawal)
          .ok_or(ErrorCode::InsufficientFunds)?;
      
      let product = price
          .checked_mul(quantity)
          .ok_or(ErrorCode::Overflow)?;
      
      let share = total
          .checked_div(parts)
          .ok_or(ErrorCode::DivisionByZero)?;
      ```
      
      ### Rule 3: Validate PDAs Properly
      
      **Anchor:**
      ```rust
      #[derive(Accounts)]
      pub struct SecurePDA<'info> {
          // ✅ Use canonical bump
          #[account(
              seeds = [b"vault", user.key().as_ref()],
              bump,  // Automatically validates canonical bump
          )]
          pub vault: Account<'info, Vault>,
      }
      ```
      
      **Native Rust:**
      ```rust
      // ✅ Find canonical bump
      let (expected_pda, bump) = Pubkey::find_program_address(
          &[b"vault", user.key.as_ref()],
          program_id,
      );
      
      // ✅ Validate PDA matches
      if expected_pda != *vault.key {
          return Err(ProgramError::InvalidSeeds);
      }
      
      // Store bump for future use with create_program_address
      ```
      
      ### Rule 4: Secure Cross-Program Invocations
      
      **Anchor:**
      ```rust
      // ✅ Program type validation
      pub token_program: Program<'info, Token>,
      
      // ✅ Use CpiContext
      let cpi_ctx = CpiContext::new(
          ctx.accounts.token_program.to_account_info(),
          transfer_accounts,
      );
      
      token::transfer(cpi_ctx, amount)?;
      ```
      
      **Native Rust:**
      ```rust
      // ✅ Validate program ID before CPI
      if *token_program.key != spl_token::id() {
          return Err(ProgramError::IncorrectProgramId);
      }
      
      // ✅ Build instruction safely
      let ix = spl_token::instruction::transfer(
          token_program.key,
          source.key,
          destination.key,
          authority.key,
          &[],
          amount,
      )?;
      
      invoke(&ix, &[source, destination, authority, token_program])?;
      ```
      
      ### Rule 5: Handle Errors Gracefully
      
      **Never:**
      ```rust
      // ❌ Don't panic or unwrap
      let value = some_operation().unwrap();
      
      // ❌ Don't ignore errors
      some_operation();
      ```
      
      **Always:**
      ```rust
      // ✅ Propagate errors
      let value = some_operation()
          .ok_or(ErrorCode::OperationFailed)?;
      
      // ✅ Or handle explicitly
      let value = match some_operation() {
          Some(v) => v,
          None => return Err(ErrorCode::OperationFailed.into()),
      };
      ```
      
      ---
      
      ## Account Validation
      
      ### Essential Checks
      
      For every account, verify:
      
      1. **Signer** - Does this account need to sign?
      2. **Owner** - Who owns this account? Is it our program?
      3. **Writable** - Does this need `mut`?
      4. **Type** - Is this the right account type?
      5. **Relationships** - Do related accounts match?
      
      ### Validation Pattern
      
      ```rust
      // Native Rust comprehensive validation
      pub fn validate_account(
          account: &AccountInfo,
          expected_owner: &Pubkey,
          must_be_signer: bool,
          must_be_writable: bool,
      ) -> ProgramResult {
          // Check signer
          if must_be_signer && !account.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          // Check owner
          if account.owner != expected_owner {
              return Err(ProgramError::IllegalOwner);
          }
      
          // Check writable
          if must_be_writable && !account.is_writable {
              return Err(ProgramError::InvalidAccountData);
          }
      
          Ok(())
      }
      ```
      
      ---
      
      ## Arithmetic Safety
      
      ### Common Vulnerabilities
      
      **Overflow example:**
      ```rust
      // ❌ VULNERABLE: Can overflow
      pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
          ctx.accounts.vault.balance = ctx.accounts.vault.balance + amount;
          Ok(())
      }
      
      // If vault.balance = u64::MAX - 100 and amount = 200
      // Result wraps to 99, losing 18.4 quintillion tokens!
      ```
      
      **Fix:**
      ```rust
      // ✅ SECURE: Checked arithmetic
      pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
          ctx.accounts.vault.balance = ctx.accounts.vault.balance
              .checked_add(amount)
              .ok_or(ErrorCode::Overflow)?;
          Ok(())
      }
      ```
      
      ### Precision Loss
      
      **Multiply before divide:**
      ```rust
      // ❌ WRONG: Loses precision
      let fee = amount / 100;  // 1.5% becomes 1%
      
      // ✅ CORRECT: Multiply first
      let fee = amount
          .checked_mul(15)
          .and_then(|v| v.checked_div(1000))
          .ok_or(ErrorCode::Overflow)?;  // Exact 1.5%
      ```
      
      ---
      
      ## PDA Security
      
      ### Use Canonical Bumps
      
      **Always find the canonical bump:**
      
      ```rust
      // ✅ Find canonical bump
      let (pda, bump) = Pubkey::find_program_address(
          &[b"vault", user.key.as_ref()],
          program_id,
      );
      
      // Store bump in account for later use
      vault.bump = bump;
      ```
      
      **Never hardcode or accept bumps from clients:**
      ```rust
      // ❌ VULNERABLE: Accepts any bump
      #[derive(Accounts)]
      pub struct BadPDA<'info> {
          #[account(seeds = [b"vault"], bump = user_provided_bump)]
          pub vault: Account<'info, Vault>,
      }
      ```
      
      ### Unique Seeds
      
      Ensure seeds create unique PDAs:
      
      ```rust
      // ✅ GOOD: Unique per user
      seeds = [b"vault", user.key().as_ref()]
      
      // ❌ BAD: Same PDA for everyone
      seeds = [b"vault"]
      ```
      
      ---
      
      ## CPI Security
      
      ### Validate Target Programs
      
      **Never accept arbitrary program IDs:**
      
      ```rust
      // ❌ VULNERABLE
      pub fn bad_cpi(ctx: Context<BadCPI>) -> Result<()> {
          // Attacker can pass any program!
          let cpi_ctx = CpiContext::new(
              ctx.accounts.any_program.to_account_info(),
              accounts,
          );
          // ... make CPI
      }
      
      // ✅ SECURE
      #[derive(Accounts)]
      pub struct SecureCPI<'info> {
          pub token_program: Program<'info, Token>,  // Type-checked!
      }
      ```
      
      ### Reload Accounts After CPIs
      
      If a CPI might modify an account you're using:
      
      ```rust
      // ✅ Reload account after external call
      let balance_before = token_account.amount;
      
      // Make CPI that might change the account
      token::transfer(cpi_ctx, amount)?;
      
      // Reload to get fresh data
      token_account.reload()?;
      
      let balance_after = token_account.amount;
      ```
      
      ---
      
      ## Common Pitfalls
      
      ### 1. init_if_needed (Anchor)
      
      **Dangerous pattern:**
      ```rust
      // ❌ Can be exploited
      #[account(init_if_needed, payer = user, space = 8 + 32)]
      pub config: Account<'info, Config>,
      ```
      
      **Problem:** Attacker creates the account first with malicious data.
      
      **Fix:**
      ```rust
      // ✅ Use init or check if exists
      #[account(init, payer = user, space = 8 + 32)]
      pub config: Account<'info, Config>,
      
      // Or explicitly check
      if config.is_initialized {
          return Err(ErrorCode::AlreadyInitialized.into());
      }
      ```
      
      ### 2. Missing Signer Checks
      
      ```rust
      // ❌ Anyone can withdraw!
      pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
          ctx.accounts.vault.balance -= amount;
          Ok(())
      }
      
      // ✅ Authority must sign
      #[derive(Accounts)]
      pub struct Withdraw<'info> {
          #[account(mut, has_one = authority)]
          pub vault: Account<'info, Vault>,
          pub authority: Signer<'info>,  // Required!
      }
      ```
      
      ### 3. Account Confusion
      
      ```rust
      // ❌ No validation - any accounts work!
      pub struct Transfer<'info> {
          pub from: Account<'info, TokenAccount>,
          pub to: Account<'info, TokenAccount>,
      }
      
      // ✅ Validate relationships
      pub struct Transfer<'info> {
          #[account(
              mut,
              constraint = from.owner == authority.key(),
              constraint = from.mint == to.mint,
          )]
          pub from: Account<'info, TokenAccount>,
      
          #[account(mut)]
          pub to: Account<'info, TokenAccount>,
      
          pub authority: Signer<'info>,
      }
      ```
      
      ### 4. Unchecked Account Types
      
      ```rust
      // ❌ Uses raw AccountInfo - no type safety
      pub fn bad(ctx: Context<Bad>) -> Result<()> {
          let data = ctx.accounts.account.try_borrow_data()?;
          // What if attacker passes wrong account type?
      }
      
      // ✅ Use typed Account
      pub fn good(ctx: Context<Good>) -> Result<()> {
          // Anchor verifies discriminator automatically
          let vault = &ctx.accounts.vault;
      }
      ```
      
      ---
      
      ## Pre-Deployment Checklist
      
      Before deploying to mainnet:
      
      ### Code Review
      
      - [ ] All accounts validated (signer, owner, writable)
      - [ ] All arithmetic uses `checked_*` methods
      - [ ] All PDAs use canonical bumps
      - [ ] All CPIs validate target programs
      - [ ] No `unwrap()` or `expect()` in production code
      - [ ] No `init_if_needed` without additional checks
      - [ ] All error cases handled gracefully
      
      ### Testing
      
      - [ ] Unit tests cover all instructions
      - [ ] Integration tests cover instruction interactions
      - [ ] Edge cases tested (zero amounts, max values, overflow)
      - [ ] Error conditions tested (invalid accounts, unauthorized access)
      - [ ] Fuzz testing with Trident (if possible)
      
      ### Security Audit
      
      - [ ] Internal code review completed
      - [ ] External security audit (recommended for >$100k TVL)
      - [ ] Run the systematic audit workflow (see [security-checklists.md](security-checklists.md) and [vulnerability-patterns.md](vulnerability-patterns.md))
      - [ ] All critical/high severity findings resolved
      - [ ] Medium findings assessed and documented
      
      ### Documentation
      
      - [ ] Account structures documented
      - [ ] Instruction requirements documented
      - [ ] Known limitations documented
      - [ ] Upgrade strategy documented
      - [ ] Emergency procedures documented
      
      ### Deployment
      
      - [ ] Tested on devnet extensively
      - [ ] Tested on mainnet-beta with small amounts
      - [ ] Upgrade authority secured (multisig recommended)
      - [ ] Monitoring and alerts configured
      - [ ] Emergency pause mechanism (if applicable)
      
      ---
      
      ## Development vs Auditing
      
      This guide covers **defensive programming during development** - secure coding patterns, implementation guidance, and development workflows (testing, deployment, optimization).
      
      For **comprehensive security auditing**, use the auditing references in this skill:
      
      - 🔍 **Systematic audits** - [security-checklists.md](security-checklists.md): category-by-category validation
      - 🐛 **Vulnerability analysis** - [vulnerability-patterns.md](vulnerability-patterns.md): exploit scenarios and attack vectors
      - 🛡️ **Framework-specific patterns** - [anchor-security.md](anchor-security.md) and [native-security.md](native-security.md)
      - 📚 **Threat modeling and fundamentals** - [security-fundamentals.md](security-fundamentals.md)
      - ⚠️ **Known gotchas** - [caveats.md](caveats.md)
      
      ---
      
      ## Quick Security Reference
      
      ### Anchor Security Checklist
      
      ```rust
      #[derive(Accounts)]
      pub struct Secure<'info> {
          // ✅ Signer
          pub authority: Signer<'info>,
      
          // ✅ Validation + relationships
          #[account(
              mut,
              has_one = authority,
              seeds = [b"vault", user.key().as_ref()],
              bump,
          )]
          pub vault: Account<'info, Vault>,
      
          // ✅ Program validation
          pub token_program: Program<'info, Token>,
      }
      
      pub fn secure_fn(ctx: Context<Secure>, amount: u64) -> Result<()> {
          // ✅ Checked arithmetic
          ctx.accounts.vault.balance = ctx.accounts.vault.balance
              .checked_add(amount)
              .ok_or(ErrorCode::Overflow)?;
      
          Ok(())
      }
      ```
      
      ### Native Rust Security Checklist
      
      ```rust
      pub fn secure_fn(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let accounts = &mut accounts.iter();
          let authority = next_account_info(accounts)?;
          let vault = next_account_info(accounts)?;
      
          // ✅ Signer check
          if !authority.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          // ✅ Owner check
          if vault.owner != program_id {
              return Err(ProgramError::IllegalOwner);
          }
      
          // ✅ PDA validation
          let (expected_pda, _) = Pubkey::find_program_address(
              &[b"vault", authority.key.as_ref()],
              program_id,
          );
          if *vault.key != expected_pda {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // ✅ Deserialize
          let mut vault_data = Vault::try_from_slice(&vault.data.borrow())?;
      
          // ✅ Checked arithmetic
          vault_data.balance = vault_data.balance
              .checked_add(amount)
              .ok_or(ProgramError::ArithmeticOverflow)?;
      
          // ✅ Serialize back
          vault_data.serialize(&mut &mut vault.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Remember
      
      **Security is not optional.** Every line of code is a potential vulnerability. Validate everything, trust nothing, and when in doubt, use the `solana-security` skill for a comprehensive audit.
      
    • serialization.md 13.9 KB
      # Serialization and Data Handling
      
      This reference provides comprehensive coverage of data serialization and deserialization patterns for native Rust Solana program development, focusing on Borsh and account data layout best practices.
      
      ## Table of Contents
      
      1. [Why Borsh for Solana](#why-borsh-for-solana)
      2. [Basic Borsh Usage](#basic-borsh-usage)
      3. [Account Data Layout Design](#account-data-layout-design)
      4. [Serialization Patterns](#serialization-patterns)
      5. [Zero-Copy Deserialization](#zero-copy-deserialization)
      6. [Data Versioning](#data-versioning)
      7. [Performance Considerations](#performance-considerations)
      8. [Common Pitfalls](#common-pitfalls)
      
      ---
      
      ## Why Borsh for Solana
      
      **Borsh (Binary Object Representation Serializer for Hashing)** is the recommended serialization format for Solana programs.
      
      ### Advantages
      
      1. **Deterministic:** Same data always produces same bytes
      2. **Compact:** Efficient binary encoding
      3. **Fast:** Lower compute unit cost than alternatives
      4. **Strict Schema:** Type-safe serialization/deserialization
      5. **No Metadata:** Unlike JSON, no field names in output
      
      ### vs Alternatives
      
      | Format | CU Cost | Size | Type Safety | Deterministic |
      |--------|---------|------|-------------|---------------|
      | **Borsh** | ✅ Low | ✅ Compact | ✅ Yes | ✅ Yes |
      | bincode | ❌ High | ✅ Compact | ✅ Yes | ⚠️ Config-dependent |
      | JSON | ❌ Very High | ❌ Large | ❌ No | ❌ No |
      | MessagePack | ⚠️ Medium | ✅ Compact | ⚠️ Partial | ⚠️ Mostly |
      
      **Recommendation:** Use Borsh for all program account data.
      
      ---
      
      ## Basic Borsh Usage
      
      ### Dependencies
      
      ```toml
      [dependencies]
      borsh = { version = "1.5", features = ["derive"] }
      ```
      
      ### Deriving Borsh Traits
      
      ```rust
      use borsh::{BorshDeserialize, BorshSerialize};
      
      #[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]
      pub struct UserAccount {
          pub user: Pubkey,
          pub balance: u64,
          pub created_at: i64,
      }
      ```
      
      ### Serialization
      
      **To bytes:**
      
      ```rust
      let account_data = UserAccount {
          user: Pubkey::new_unique(),
          balance: 1000,
          created_at: 1234567890,
      };
      
      // Serialize to Vec<u8>
      let bytes = account_data.try_to_vec()?;
      
      // Serialize to existing buffer
      let mut buffer = vec![0u8; 100];
      account_data.serialize(&mut buffer.as_mut_slice())?;
      ```
      
      ### Deserialization
      
      **From bytes:**
      
      ```rust
      // Deserialize from slice
      let account_data = UserAccount::try_from_slice(&bytes)?;
      
      // Deserialize with BorshDeserialize
      let mut cursor = &bytes[..];
      let account_data = UserAccount::deserialize(&mut cursor)?;
      ```
      
      ---
      
      ## Account Data Layout Design
      
      ### Basic Structure
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct AccountData {
          // 1. Discriminator / Type Field (1 byte)
          pub account_type: u8,
      
          // 2. Flags / State (1 byte)
          pub is_initialized: bool,
      
          // 3. Fixed-size fields (predictable layout)
          pub owner: Pubkey,           // 32 bytes
          pub created_at: i64,         // 8 bytes
          pub counter: u64,            // 8 bytes
      
          // 4. Variable-size fields (at end)
          pub name: String,            // 4 + length
          pub metadata: Vec<u8>,       // 4 + length
      }
      ```
      
      **Size calculation:**
      ```
      1 (type) + 1 (flag) + 32 (pubkey) + 8 (i64) + 8 (u64) + 4 (string len) + N (string) + 4 (vec len) + M (vec)
      = 58 + N + M bytes
      ```
      
      ### Size Calculation Helper
      
      ```rust
      impl AccountData {
          pub const FIXED_SIZE: usize = 58;  // All fixed fields
      
          pub fn calculate_size(name_len: usize, metadata_len: usize) -> usize {
              Self::FIXED_SIZE + name_len + metadata_len
          }
      
          pub fn max_size(max_name: usize, max_metadata: usize) -> usize {
              Self::calculate_size(max_name, max_metadata)
          }
      }
      
      // Usage
      let account_size = AccountData::max_size(32, 256);  // 346 bytes
      ```
      
      ### Fixed-Size Accounts
      
      **Best for performance:**
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct FixedAccount {
          pub is_initialized: bool,
          pub owner: Pubkey,
          pub balance: u64,
          pub last_updated: i64,
          // Fixed-size array instead of Vec
          pub data: [u8; 256],
      }
      
      impl FixedAccount {
          pub const SIZE: usize = 1 + 32 + 8 + 8 + 256;  // 305 bytes
      }
      ```
      
      ---
      
      ## Serialization Patterns
      
      ### Pattern 1: try_from_slice (Recommended)
      
      **Most common pattern for account deserialization:**
      
      ```rust
      use borsh::BorshDeserialize;
      
      pub fn load_account_data(
          account_info: &AccountInfo,
      ) -> Result<UserAccount, ProgramError> {
          let data = UserAccount::try_from_slice(&account_info.data.borrow())?;
          Ok(data)
      }
      ```
      
      **Error handling:**
      ```rust
      let data = UserAccount::try_from_slice(&account_info.data.borrow())
          .map_err(|e| {
              msg!("Failed to deserialize account: {}", e);
              ProgramError::InvalidAccountData
          })?;
      ```
      
      ### Pattern 2: Unchecked Deserialization
      
      **Use when you've already validated the account:**
      
      ```rust
      use borsh::try_from_slice_unchecked;
      
      // After validation checks
      let mut data = try_from_slice_unchecked::<UserAccount>(&account_info.data.borrow())
          .unwrap();  // Safe because we validated
      ```
      
      **⚠️ Warning:** Only use after thorough validation. Skips some safety checks.
      
      ### Pattern 3: Partial Deserialization
      
      **Read only what you need:**
      
      ```rust
      #[derive(BorshDeserialize)]
      pub struct AccountHeader {
          pub account_type: u8,
          pub is_initialized: bool,
          pub owner: Pubkey,
      }
      
      // Deserialize just the header
      let header = AccountHeader::try_from_slice(&account_info.data.borrow()[..42])?;
      
      if !header.is_initialized {
          return Err(ProgramError::UninitializedAccount);
      }
      ```
      
      ### Pattern 4: In-Place Modification
      
      **Efficient for large accounts:**
      
      ```rust
      pub fn update_balance(
          account_info: &AccountInfo,
          new_balance: u64,
      ) -> ProgramResult {
          let mut data = account_info.data.borrow_mut();
      
          // Deserialize
          let mut account = UserAccount::try_from_slice(&data)?;
      
          // Modify
          account.balance = new_balance;
          account.last_updated = Clock::get()?.unix_timestamp;
      
          // Serialize back
          account.serialize(&mut &mut data[..])?;
      
          Ok(())
      }
      ```
      
      ### Pattern 5: Bulk Operations
      
      **Processing multiple accounts:**
      
      ```rust
      pub fn process_accounts(
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let account_data: Vec<UserAccount> = accounts
              .iter()
              .map(|acc| UserAccount::try_from_slice(&acc.data.borrow()))
              .collect::<Result<Vec<_>, _>>()?;
      
          // Process all accounts
          for (i, data) in account_data.iter().enumerate() {
              msg!("Account {}: balance = {}", i, data.balance);
          }
      
          Ok(())
      }
      ```
      
      ---
      
      ## Zero-Copy Deserialization
      
      ### When to Use Zero-Copy
      
      **Benefits:**
      - Avoids memory allocation
      - Reduces compute units (50%+ savings for large structs)
      - Direct access to account data
      
      **Use when:**
      - Account data is large (> 100 bytes)
      - Frequent reads
      - Performance-critical paths
      
      ### Bytemuck Pattern
      
      ```toml
      [dependencies]
      bytemuck = { version = "1.14", features = ["derive"] }
      ```
      
      ```rust
      use bytemuck::{Pod, Zeroable};
      
      #[repr(C)]
      #[derive(Copy, Clone, Pod, Zeroable)]
      pub struct ZeroCopyAccount {
          pub is_initialized: u8,      // bool as u8
          pub owner: [u8; 32],         // Pubkey as bytes
          pub balance: u64,
          pub counter: u64,
      }
      
      impl ZeroCopyAccount {
          pub const SIZE: usize = std::mem::size_of::<Self>();
      
          pub fn from_account_info(account_info: &AccountInfo) -> Result<&Self, ProgramError> {
              let data = account_info.data.borrow();
              bytemuck::try_from_bytes(&data)
                  .map_err(|_| ProgramError::InvalidAccountData)
          }
      
          pub fn from_account_info_mut(
              account_info: &AccountInfo,
          ) -> Result<&mut Self, ProgramError> {
              let data = account_info.data.borrow_mut();
              bytemuck::try_from_bytes_mut(&mut data)
                  .map_err(|_| ProgramError::InvalidAccountData)
          }
      }
      
      // Usage
      let account = ZeroCopyAccount::from_account_info(account_info)?;
      msg!("Balance: {}", account.balance);
      
      // Mutable access
      let account = ZeroCopyAccount::from_account_info_mut(account_info)?;
      account.balance += 100;
      ```
      
      **⚠️ Limitations:**
      - Only works with types that are `Pod` (Plain Old Data)
      - No `String`, `Vec`, or other heap-allocated types
      - Must be `#[repr(C)]` for stable layout
      
      ---
      
      ## Data Versioning
      
      ### Pattern 1: Version Field
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct VersionedAccount {
          pub version: u8,
          pub data: AccountDataEnum,
      }
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub enum AccountDataEnum {
          V1(AccountDataV1),
          V2(AccountDataV2),
      }
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct AccountDataV1 {
          pub balance: u64,
      }
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct AccountDataV2 {
          pub balance: u64,
          pub last_updated: i64,  // New field
      }
      
      // Deserialization with version handling
      pub fn load_versioned_account(
          account_info: &AccountInfo,
      ) -> ProgramResult {
          let versioned = VersionedAccount::try_from_slice(&account_info.data.borrow())?;
      
          match versioned.data {
              AccountDataEnum::V1(data_v1) => {
                  msg!("V1 account: balance = {}", data_v1.balance);
              }
              AccountDataEnum::V2(data_v2) => {
                  msg!("V2 account: balance = {}, updated = {}",
                      data_v2.balance, data_v2.last_updated);
              }
          }
      
          Ok(())
      }
      ```
      
      ### Pattern 2: Optional Fields
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct Account {
          pub balance: u64,
      
          // V2: Added optional field
          pub metadata: Option<Metadata>,
      }
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct Metadata {
          pub name: String,
          pub url: String,
      }
      
      // Old accounts: metadata = None
      // New accounts: metadata = Some(Metadata { ... })
      ```
      
      ### Pattern 3: Migration Function
      
      ```rust
      pub fn migrate_account_v1_to_v2(
          account_info: &AccountInfo,
      ) -> ProgramResult {
          // Load V1
          let data_v1 = AccountDataV1::try_from_slice(&account_info.data.borrow())?;
      
          // Convert to V2
          let data_v2 = AccountDataV2 {
              balance: data_v1.balance,
              last_updated: Clock::get()?.unix_timestamp,
          };
      
          // Reallocate if needed
          let new_size = data_v2.try_to_vec()?.len();
          account_info.realloc(new_size, false)?;
      
          // Serialize V2
          data_v2.serialize(&mut &mut account_info.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Performance Considerations
      
      ### Compute Unit Costs
      
      **Serialization costs (approximate):**
      
      | Operation | CU Cost |
      |-----------|---------|
      | Serialize small struct (< 100 bytes) | ~500 CU |
      | Serialize large struct (> 1KB) | ~2,000 CU |
      | Deserialize small struct | ~800 CU |
      | Deserialize large struct | ~3,000 CU |
      | Zero-copy access | ~100 CU |
      
      ### Optimization Tips
      
      **1. Minimize serialization frequency:**
      
      ```rust
      // ❌ Wasteful - serializes twice
      let mut data = load_data(account)?;
      data.field1 = value1;
      save_data(account, &data)?;
      
      data.field2 = value2;
      save_data(account, &data)?;  // Serialize again!
      
      // ✅ Efficient - serialize once
      let mut data = load_data(account)?;
      data.field1 = value1;
      data.field2 = value2;
      save_data(account, &data)?;
      ```
      
      **2. Use fixed-size fields:**
      
      ```rust
      // ❌ Variable size - more expensive
      pub struct Account {
          pub name: String,        // 4 + N bytes
      }
      
      // ✅ Fixed size - cheaper
      pub struct Account {
          pub name: [u8; 32],      // Exactly 32 bytes
      }
      ```
      
      **3. Order fields by size:**
      
      ```rust
      // ✅ Optimized layout (largest first)
      #[derive(BorshSerialize, BorshDeserialize)]
      #[repr(C)]
      pub struct OptimizedAccount {
          pub pubkey1: Pubkey,     // 32 bytes
          pub pubkey2: Pubkey,     // 32 bytes
          pub amount: u64,         // 8 bytes
          pub timestamp: i64,      // 8 bytes
          pub flags: u8,           // 1 byte
      }
      ```
      
      ---
      
      ## Common Pitfalls
      
      ### 1. Buffer Too Small
      
      ```rust
      // ❌ Error: buffer too small
      let mut buffer = vec![0u8; 10];
      large_struct.serialize(&mut buffer.as_mut_slice())?;  // Fails!
      
      // ✅ Correct: proper size
      let size = large_struct.try_to_vec()?.len();
      let mut buffer = vec![0u8; size];
      large_struct.serialize(&mut buffer.as_mut_slice())?;
      ```
      
      ### 2. Forgetting to Borrow
      
      ```rust
      // ❌ Error: data moved
      let data = account_info.data;
      UserAccount::try_from_slice(&data)?;  // Fails!
      
      // ✅ Correct: borrow data
      let data = account_info.data.borrow();
      UserAccount::try_from_slice(&data)?;
      ```
      
      ### 3. Mismatched Schema
      
      ```rust
      // Account created with V1
      #[derive(BorshSerialize)]
      pub struct AccountV1 {
          pub balance: u64,
      }
      
      // Later, trying to deserialize as V2
      #[derive(BorshDeserialize)]
      pub struct AccountV2 {
          pub balance: u64,
          pub timestamp: i64,  // New field!
      }
      
      // ❌ Fails: not enough bytes
      let data = AccountV2::try_from_slice(&bytes)?;  // Error!
      ```
      
      **Solution:** Use versioning or optional fields.
      
      ### 4. String/Vec Limits
      
      ```rust
      // ❌ No validation
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct Account {
          pub name: String,  // Could be 10MB!
      }
      
      // ✅ Validate before deserializing
      pub fn validate_name(name: &str) -> ProgramResult {
          if name.len() > 32 {
              return Err(ProgramError::InvalidArgument);
          }
          Ok(())
      }
      ```
      
      ### 5. Incorrect Size Calculation
      
      ```rust
      // ❌ Wrong: ignores vector length prefix
      let size = my_vec.len();
      
      // ✅ Correct: includes 4-byte length prefix
      let size = 4 + my_vec.len();
      ```
      
      ---
      
      ## Summary
      
      **Key Takeaways:**
      
      1. **Use Borsh** for all Solana program serialization
      2. **Design fixed-size layouts** when possible for predictability
      3. **Validate before deserializing** to prevent errors
      4. **Use zero-copy** for large, frequently-accessed data
      5. **Plan for versioning** from the start
      6. **Minimize serialization frequency** to save compute units
      
      **Common Patterns:**
      ```rust
      // Deserialize
      let data = AccountData::try_from_slice(&account_info.data.borrow())?;
      
      // Modify
      let mut data = data;
      data.field = new_value;
      
      // Serialize
      data.serialize(&mut &mut account_info.data.borrow_mut()[..])?;
      ```
      
      **Size Calculation:**
      ```rust
      // Fixed fields
      const FIXED_SIZE: usize = 1 + 32 + 8;
      
      // Variable fields
      let total_size = FIXED_SIZE + 4 + string.len() + 4 + vec.len();
      ```
      
      Proper serialization patterns are fundamental to efficient and correct Solana programs. Master Borsh for production-ready data handling.
      
    • surfpool.md 12 KB
      # Surfpool Local Development
      
      Surfpool is a drop-in replacement for `solana-test-validator` built on LiteSVM. It provides instant mainnet forking, powerful cheatcodes for testing, and Infrastructure as Code for reproducible deployments.
      
      ## Installation
      
      ```bash
      # macOS (Homebrew)
      brew install txtx/taps/surfpool
      
      # Update existing installation
      brew tap txtx/taps
      brew reinstall surfpool
      
      # From source
      git clone https://github.com/txtx/surfpool.git
      cd surfpool
      cargo surfpool-install
      
      # Docker
      docker run surfpool/surfpool --version
      
      # Verify
      surfpool --version
      ```
      
      ## Quick Start
      
      ```bash
      # Start local network (in Anchor project directory)
      surfpool start
      
      # Start with custom options
      surfpool start --port 8899 --slot-time 400
      
      # Start with airdrops to specific addresses
      surfpool start --airdrop <PUBKEY1> --airdrop <PUBKEY2> --airdrop-amount 100000000000
      ```
      
      When run in an Anchor project, Surfpool automatically:
      - Generates Infrastructure as Code runbooks
      - Deploys programs to the local network
      - Provides structured environment for iteration
      
      ## CLI Options
      
      ```bash
      surfpool start [OPTIONS]
      
      Options:
        -m, --manifest-file-path <PATH>   Path to manifest [default: ./Surfpool.toml]
        -p, --port <PORT>                 RPC port [default: 8899]
        -o, --host <HOST>                 Host address [default: 127.0.0.1]
        -s, --slot-time <MS>              Slot time in ms [default: 400]
        -u, --rpc-url <URL>               Mainnet RPC for forking [default: https://api.mainnet-beta.solana.com]
        --no-tui                          Disable terminal UI, show log streams
        --no-deploy                       Disable auto deployments
        --watch                           Watch programs for changes
        -r, --runbook <ID>                Runbooks to execute [default: deployment]
        -a, --airdrop <PUBKEY>            Addresses to airdrop SOL
        -q, --airdrop-amount <LAMPORTS>   Airdrop amount [default: 10000000000000]
        -k, --airdrop-keypair-path <PATH> Keypair paths to airdrop [default: ~/.config/solana/id.json]
        -g, --geyser-plugin-config <PATH> Geyser plugins to load
        --no-explorer                     Disable explorer
      ```
      
      ## Mainnet Forking (Just-in-Time)
      
      Surfpool fetches mainnet accounts on-demand without downloading snapshots:
      
      ```bash
      # Fork mainnet state
      surfpool start --rpc-url https://api.mainnet-beta.solana.com
      
      # Use custom RPC (recommended for rate limits)
      surfpool start --rpc-url https://mainnet.helius-rpc.com?api-key=YOUR_KEY
      ```
      
      **Use cases:**
      - Test CPIs into Jupiter, Raydium, Meteora without manual account dumps
      - Simulate swaps with real liquidity pool state
      - Test oracle integrations with live price feeds
      
      ## Cheatcodes
      
      Special RPC methods for testing. Call via any Solana RPC client:
      
      ### Account Manipulation
      
      **`surfnet_setAccount`** - Set any account's state:
      ```typescript
      await connection._rpcRequest('surfnet_setAccount', [
        pubkey.toBase58(),
        {
          lamports: 1_000_000_000,
          data: Buffer.from([...]).toString('hex'),
          owner: SystemProgram.programId.toBase58(),
          executable: false,
        }
      ]);
      ```
      
      **`surfnet_setTokenAccount`** - Set token balances directly:
      ```typescript
      await connection._rpcRequest('surfnet_setTokenAccount', [
        owner.toBase58(),
        mint.toBase58(),
        {
          amount: 1_000_000_000_000,  // Set balance without minting
          delegate: null,
          state: 'initialized',
        }
      ]);
      ```
      
      **`surfnet_cloneProgramAccount`** - Clone programs between IDs:
      ```typescript
      await connection._rpcRequest('surfnet_cloneProgramAccount', [
        sourceProgramId.toBase58(),
        destinationProgramId.toBase58()
      ]);
      ```
      
      **`surfnet_setProgramAuthority`** - Modify upgrade authority:
      ```typescript
      await connection._rpcRequest('surfnet_setProgramAuthority', [
        programId.toBase58(),
        newAuthority.toBase58()  // or null for immutable
      ]);
      ```
      
      ### Time Manipulation
      
      **`surfnet_timeTravel`** - Jump to future epoch/slot/timestamp:
      ```typescript
      // Move to specific slot
      await connection._rpcRequest('surfnet_timeTravel', [{ slot: 300_000_000 }]);
      
      // Move to specific epoch
      await connection._rpcRequest('surfnet_timeTravel', [{ epoch: 500 }]);
      ```
      
      **`surfnet_pauseClock`** / **`surfnet_resumeClock`** - Control time:
      ```typescript
      await connection._rpcRequest('surfnet_pauseClock', []);
      // ... perform tests at frozen time
      await connection._rpcRequest('surfnet_resumeClock', []);
      ```
      
      ### Transaction Profiling
      
      **`surfnet_profileTransaction`** - Detailed CU analysis:
      ```typescript
      const result = await connection._rpcRequest('surfnet_profileTransaction', [
        transaction.serialize().toString('base64'),
        'my-test-tag',  // optional tag for grouping
        { depth: 'instruction', encoding: 'base64' }
      ]);
      
      // Result includes:
      // - Total CU consumed
      // - Per-instruction breakdown
      // - Account changes
      // - Execution logs
      ```
      
      **`surfnet_getProfileResults`** - Retrieve tagged profiles:
      ```typescript
      const profiles = await connection._rpcRequest('surfnet_getProfileResults', [
        'my-test-tag'
      ]);
      ```
      
      **`surfnet_getTransactionProfile`** - Get profile by signature:
      ```typescript
      const profile = await connection._rpcRequest('surfnet_getTransactionProfile', [
        { signature: txSignature }
      ]);
      ```
      
      ### IDL Management
      
      **`surfnet_registerIdl`** - Register IDL for account parsing:
      ```typescript
      await connection._rpcRequest('surfnet_registerIdl', [
        idlJson,  // Full IDL object
        slot      // Optional: slot when IDL becomes active
      ]);
      ```
      
      **`surfnet_getIdl`** - Retrieve registered IDL:
      ```typescript
      const idl = await connection._rpcRequest('surfnet_getIdl', [
        programId.toBase58()
      ]);
      ```
      
      ## Scenarios (Protocol Overrides)
      
      Test against specific protocol states with slot-by-slot account overrides.
      
      **Natively supported protocols (v1.0.0-rc1):**
      - **Pyth v2** - Price feeds (SOL/USD, BTC/USD, ETH/BTC, ETH/USD)
      - **Jupiter v6** - DEX aggregator, TokenLedger manipulation
      - **Switchboard On-Demand** - Oracle quote overrides
      - **Kamino** - Lending reserve liquidity, risk config, obligation health
      - **Drift v2** - Perp/spot markets, user state, global state
      - **Raydium AMM-v3** - AMM pool states
      - **Meteora DLMM** - Dynamic liquidity market maker
      - **Whirlpool** - Concentrated liquidity pools
      
      **Register custom scenarios:**
      ```typescript
      await connection._rpcRequest('surfnet_registerScenario', [
        {
          name: 'liquidation-test',
          description: 'Test liquidation at 80% LTV',
          overrides: [
            {
              slot: 1,
              accounts: {
                [obligationPubkey]: {
                  borrowedValue: 80_000_000,
                  depositedValue: 100_000_000
                }
              }
            }
          ]
        }
      ]);
      ```
      
      **Use cases:**
      - Simulate oracle price crashes
      - Test liquidation scenarios
      - Replay historical market conditions
      - Stress test protocol edge cases
      
      ## Infrastructure as Code (IaC)
      
      Surfpool uses `.tx` runbooks for declarative deployments:
      
      ```hcl
      # deployment.tx
      addon "svm" {
        network_id = input.network_id
        rpc_api_url = input.rpc_api_url
      }
      
      signer "deployer" "svm::web_wallet" {
        expected_address = input.deployer_address
      }
      
      action "deploy_program" "svm::deploy_program" {
        description = "Deploy the program"
        program = svm::get_program_from_anchor_project("my_program")
        authority = signer.deployer
        payer = signer.deployer
      }
      
      output "program_id" {
        value = action.deploy_program.program_id
      }
      ```
      
      **Run runbooks:**
      ```bash
      # Supervised mode (web UI)
      surfpool run deployment.tx
      
      # Unsupervised mode (CI/CD)
      surfpool run deployment.tx --unsupervised
      
      # With environment
      surfpool run deployment.tx --env mainnet
      ```
      
      **Manifest file (`txtx.yml`):**
      ```yaml
      name: my-protocol
      runbooks:
        - name: Deploy Protocol
          location: ./deployment
          state:
            location: states
      
      environments:
        devnet:
          network_id: devnet
          rpc_api_url: https://api.devnet.solana.com
        mainnet:
          network_id: mainnet
          rpc_api_url: https://api.mainnet-beta.solana.com
      ```
      
      ## IDL-to-SQL
      
      Auto-generate SQL schemas from program IDLs:
      
      ```bash
      # Surfpool automatically generates tables for registered IDLs
      # Query account data via local SQLite or Postgres
      
      # Example: Query all token accounts
      SELECT * FROM token_accounts WHERE owner = 'YOUR_PUBKEY';
      ```
      
      ## MCP Integration
      
      Add Surfpool as an MCP server for AI-assisted development:
      
      **Claude Code / Cursor configuration:**
      ```json
      {
        "mcpServers": {
          "surfpool": {
            "command": "surfpool",
            "args": ["mcp"]
          }
        }
      }
      ```
      
      **Available MCP tools:**
      - `start_surfnet` - Start a local network
      - `set_token_account` - Set token balances for testing
      
      **Example prompt:**
      > "Start a local network with 10 users loaded with SOL, USDC, JUP and TRUMP tokens"
      
      ## Integration with Anchor
      
      Surfpool works seamlessly with existing Anchor workflows:
      
      ```bash
      # In Anchor project directory
      cd my-anchor-project
      
      # Start Surfpool (auto-deploys programs)
      surfpool start
      
      # Run Anchor tests against Surfpool
      anchor test --skip-local-validator
      
      # Watch for program changes
      surfpool start --watch
      ```
      
      **anchor.toml configuration:**
      ```toml
      [provider]
      cluster = "localnet"  # Points to Surfpool on 8899
      
      [programs.localnet]
      my_program = "YourProgramID"
      ```
      
      ## Integration with Native Rust
      
      ```bash
      # Build program
      cargo build-sbf
      
      # Start Surfpool
      surfpool start --no-deploy
      
      # Deploy manually
      solana program deploy target/deploy/my_program.so
      
      # Or use runbook
      surfpool run deploy.tx
      ```
      
      ## Surfpool Studio
      
      Local web dashboard for visualization:
      
      ```bash
      # Start with Studio enabled (default)
      surfpool start
      
      # Access at http://localhost:8899/studio
      ```
      
      **Features:**
      - Real-time transaction monitoring
      - Account state inspection
      - Scenario builder (drag-and-drop protocol overrides)
      - IDL-parsed account views
      - Transaction profiling results
      
      ## Best Practices
      
      ### Testing with Cheatcodes
      
      ```typescript
      describe('Liquidation Tests', () => {
        beforeEach(async () => {
          // Set up specific account states
          await connection._rpcRequest('surfnet_setTokenAccount', [
            userWallet.toBase58(),
            usdcMint.toBase58(),
            { amount: 1_000_000_000 }
          ]);
      
          // Pause clock for deterministic tests
          await connection._rpcRequest('surfnet_pauseClock', []);
        });
      
        it('should liquidate at 80% LTV', async () => {
          // Time travel to simulate price change
          await connection._rpcRequest('surfnet_timeTravel', [{ slot: currentSlot + 1000 }]);
      
          // Test liquidation logic
          // ...
        });
      
        afterEach(async () => {
          await connection._rpcRequest('surfnet_resumeClock', []);
        });
      });
      ```
      
      ### CU Optimization Workflow
      
      1. **Profile baseline:**
         ```typescript
         const baseline = await connection._rpcRequest('surfnet_profileTransaction', [
           tx.serialize().toString('base64'),
           'optimization-baseline'
         ]);
         ```
      
      2. **Make optimizations** (see [compute-optimization.md](compute-optimization.md))
      
      3. **Profile again and compare:**
         ```typescript
         const optimized = await connection._rpcRequest('surfnet_profileTransaction', [
           optimizedTx.serialize().toString('base64'),
           'optimization-v1'
         ]);
         ```
      
      4. **Review in Studio** for per-instruction breakdown
      
      ### CI/CD Integration
      
      ```yaml
      # GitHub Actions example
      jobs:
        test:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v4
      
            - name: Install Surfpool
              run: cargo install --git https://github.com/txtx/surfpool surfpool-cli
      
            - name: Start Surfpool
              run: surfpool start --no-tui &
      
            - name: Wait for Surfpool
              run: sleep 5
      
            - name: Run tests
              run: anchor test --skip-local-validator
      ```
      
      ## Troubleshooting
      
      **Port already in use:**
      ```bash
      surfpool start --port 8900
      ```
      
      **Mainnet RPC rate limits:**
      ```bash
      # Use dedicated RPC
      surfpool start --rpc-url https://mainnet.helius-rpc.com?api-key=YOUR_KEY
      
      # Or set environment variable
      export SURFPOOL_DATASOURCE_RPC_URL=https://your-rpc.com
      ```
      
      **Program not deploying:**
      ```bash
      # Check deployment logs
      surfpool start --debug
      
      # Manually deploy
      surfpool start --no-deploy
      solana program deploy target/deploy/program.so
      ```
      
      ## Resources
      
      - [Surfpool Documentation](https://docs.surfpool.run)
      - [GitHub Repository](https://github.com/txtx/surfpool)
      - [Surfpool 101 Video Series](https://www.youtube.com/playlist?list=PL0FMgRjJMRzO1FdunpMS-aUS4GNkgyr3T)
      - [Discord Community](https://discord.gg/rqXmWsn2ja)
      - [Solana Docs: Surfpool CLI Basics](https://solana.com/docs/intro/installation/surfpool-cli-basics)
      
    • sysvars.md 23.1 KB
      # Sysvars (System Variables)
      
      This reference provides comprehensive coverage of Solana System Variables (sysvars) for native Rust program development, including access patterns, use cases, and performance implications.
      
      ## Table of Contents
      
      1. [What are Sysvars](#what-are-sysvars)
      2. [Clock Sysvar](#clock-sysvar)
      3. [Rent Sysvar](#rent-sysvar)
      4. [EpochSchedule Sysvar](#epochschedule-sysvar)
      5. [SlotHashes Sysvar](#slothashes-sysvar)
      6. [Other Sysvars](#other-sysvars)
      7. [Access Patterns](#access-patterns)
      8. [Performance Implications](#performance-implications)
      9. [Best Practices](#best-practices)
      
      ---
      
      ## What are Sysvars
      
      **System Variables (sysvars)** are special accounts that provide programs with access to blockchain state and cluster information.
      
      ### Key Characteristics
      
      1. **Cluster-wide state:** Same values for all programs in the same slot
      2. **Updated automatically:** Runtime maintains values
      3. **Predictable addresses:** Well-known pubkeys
      4. **Read-only:** Programs cannot modify sysvars
      5. **Low CU cost:** Cheaper than account reads
      
      ### When to Use Sysvars
      
      **Use sysvars when you need:**
      - Current timestamp or slot number
      - Rent exemption calculations
      - Epoch and slot timing information
      - Recent block hashes (for verification)
      - Stake history or epoch rewards
      
      **Don't use sysvars for:**
      - User-specific data (use accounts)
      - Program state (use PDAs)
      - Cross-program communication (use CPIs)
      
      ---
      
      ## Clock Sysvar
      
      **Address:** `solana_program::sysvar::clock::ID`
      
      The Clock sysvar provides timing information about the blockchain.
      
      ### Clock Structure
      
      ```rust
      use solana_program::clock::Clock;
      
      pub struct Clock {
          pub slot: Slot,                    // Current slot
          pub epoch_start_timestamp: i64,    // Timestamp of epoch start (approximate)
          pub epoch: Epoch,                  // Current epoch
          pub leader_schedule_epoch: Epoch,  // Epoch for which leader schedule is valid
          pub unix_timestamp: UnixTimestamp, // Estimated wall-clock Unix timestamp
      }
      ```
      
      ### Accessing Clock
      
      **Pattern 1: get() (Recommended)**
      
      ```rust
      use solana_program::clock::Clock;
      use solana_program::sysvar::Sysvar;
      
      pub fn process_instruction(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          _instruction_data: &[u8],
      ) -> ProgramResult {
          // Get Clock directly (no account needed)
          let clock = Clock::get()?;
      
          msg!("Current slot: {}", clock.slot);
          msg!("Current timestamp: {}", clock.unix_timestamp);
          msg!("Current epoch: {}", clock.epoch);
      
          Ok(())
      }
      ```
      
      **Pattern 2: From account**
      
      ```rust
      use solana_program::sysvar::clock;
      
      pub fn process_with_account(
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let clock_account = next_account_info(account_info_iter)?;
      
          // Verify it's the Clock sysvar
          if clock_account.key != &clock::ID {
              return Err(ProgramError::InvalidArgument);
          }
      
          let clock = Clock::from_account_info(clock_account)?;
          msg!("Timestamp: {}", clock.unix_timestamp);
      
          Ok(())
      }
      ```
      
      **⚠️ Recommendation:** Use `Clock::get()` unless you specifically need the account for validation.
      
      ### Common Clock Use Cases
      
      **1. Timestamping events:**
      
      ```rust
      use solana_program::clock::Clock;
      use solana_program::sysvar::Sysvar;
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct Event {
          pub created_at: i64,
          pub data: Vec<u8>,
      }
      
      pub fn create_event(
          event_account: &AccountInfo,
          data: Vec<u8>,
      ) -> ProgramResult {
          let clock = Clock::get()?;
      
          let event = Event {
              created_at: clock.unix_timestamp,
              data,
          };
      
          event.serialize(&mut &mut event_account.data.borrow_mut()[..])?;
          Ok(())
      }
      ```
      
      **2. Time-based logic (vesting, expiration):**
      
      ```rust
      pub fn check_vesting(
          vesting_account: &AccountInfo,
      ) -> ProgramResult {
          let clock = Clock::get()?;
          let vesting = VestingSchedule::try_from_slice(&vesting_account.data.borrow())?;
      
          if clock.unix_timestamp < vesting.unlock_timestamp {
              msg!("Tokens still locked until {}", vesting.unlock_timestamp);
              return Err(ProgramError::Custom(1)); // Locked
          }
      
          msg!("Vesting unlocked!");
          Ok(())
      }
      ```
      
      **3. Slot-based mechanics:**
      
      ```rust
      pub fn process_epoch_transition(
          state_account: &AccountInfo,
      ) -> ProgramResult {
          let clock = Clock::get()?;
          let mut state = State::try_from_slice(&state_account.data.borrow())?;
      
          if clock.epoch > state.last_processed_epoch {
              msg!("Processing epoch transition: {} -> {}",
                  state.last_processed_epoch, clock.epoch);
      
              // Process epoch rewards, resets, etc.
              state.last_processed_epoch = clock.epoch;
              state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
          }
      
          Ok(())
      }
      ```
      
      ### Clock Gotchas
      
      **⚠️ unix_timestamp is approximate:**
      
      ```rust
      // ❌ Don't use for precise timing
      if clock.unix_timestamp == expected_timestamp {  // Risky!
          // Might miss by seconds
      }
      
      // ✅ Use ranges for time checks
      if clock.unix_timestamp >= unlock_time {
          // Safe
      }
      ```
      
      **⚠️ Timestamps can vary across validators:**
      
      The `unix_timestamp` is based on validator voting and may differ slightly between validators in the same slot. Don't assume exact precision.
      
      ---
      
      ## Rent Sysvar
      
      **Address:** `solana_program::sysvar::rent::ID`
      
      The Rent sysvar provides rent calculation parameters.
      
      ### Rent Structure
      
      ```rust
      use solana_program::rent::Rent;
      
      pub struct Rent {
          pub lamports_per_byte_year: u64,  // Base rent rate
          pub exemption_threshold: f64,      // Multiplier for exemption (2.0 = 2 years)
          pub burn_percent: u8,              // Percentage of rent burned
      }
      ```
      
      ### Accessing Rent
      
      **Pattern 1: get() (Recommended)**
      
      ```rust
      use solana_program::rent::Rent;
      use solana_program::sysvar::Sysvar;
      
      pub fn calculate_rent_exemption(
          data_size: usize,
      ) -> Result<u64, ProgramError> {
          let rent = Rent::get()?;
      
          // Calculate minimum balance for rent exemption
          let min_balance = rent.minimum_balance(data_size);
      
          msg!("Minimum balance for {} bytes: {} lamports", data_size, min_balance);
          Ok(min_balance)
      }
      ```
      
      **Pattern 2: From account**
      
      ```rust
      use solana_program::sysvar::rent;
      
      pub fn check_rent_exemption(
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          let account_info_iter = &mut accounts.iter();
          let data_account = next_account_info(account_info_iter)?;
          let rent_account = next_account_info(account_info_iter)?;
      
          if rent_account.key != &rent::ID {
              return Err(ProgramError::InvalidArgument);
          }
      
          let rent = Rent::from_account_info(rent_account)?;
      
          if !rent.is_exempt(data_account.lamports(), data_account.data_len()) {
              msg!("Account is not rent-exempt!");
              return Err(ProgramError::AccountNotRentExempt);
          }
      
          Ok(())
      }
      ```
      
      ### Common Rent Use Cases
      
      **1. Account creation with rent exemption:**
      
      ```rust
      use solana_program::rent::Rent;
      use solana_program::system_instruction;
      use solana_program::program::invoke_signed;
      
      pub fn create_account_rent_exempt(
          payer: &AccountInfo,
          new_account: &AccountInfo,
          system_program: &AccountInfo,
          program_id: &Pubkey,
          seeds: &[&[u8]],
          space: usize,
      ) -> ProgramResult {
          let rent = Rent::get()?;
          let min_balance = rent.minimum_balance(space);
      
          msg!("Creating account with {} lamports for {} bytes", min_balance, space);
      
          let create_account_ix = system_instruction::create_account(
              payer.key,
              new_account.key,
              min_balance,
              space as u64,
              program_id,
          );
      
          invoke_signed(
              &create_account_ix,
              &[payer.clone(), new_account.clone(), system_program.clone()],
              &[seeds],
          )?;
      
          Ok(())
      }
      ```
      
      **2. Validating account has sufficient balance:**
      
      ```rust
      pub fn validate_rent_exempt_account(
          account: &AccountInfo,
      ) -> ProgramResult {
          let rent = Rent::get()?;
      
          if !rent.is_exempt(account.lamports(), account.data_len()) {
              let required = rent.minimum_balance(account.data_len());
              let current = account.lamports();
      
              msg!("Account not rent-exempt: has {} lamports, needs {}",
                  current, required);
      
              return Err(ProgramError::AccountNotRentExempt);
          }
      
          Ok(())
      }
      ```
      
      **3. Calculating required lamports for reallocation:**
      
      ```rust
      pub fn reallocate_account(
          account: &AccountInfo,
          new_size: usize,
      ) -> ProgramResult {
          let rent = Rent::get()?;
      
          let old_size = account.data_len();
          let current_lamports = account.lamports();
      
          let new_min_balance = rent.minimum_balance(new_size);
      
          if new_size > old_size {
              // Growing account - ensure sufficient lamports
              if current_lamports < new_min_balance {
                  msg!("Need {} more lamports for reallocation",
                      new_min_balance - current_lamports);
                  return Err(ProgramError::InsufficientFunds);
              }
          }
      
          account.realloc(new_size, false)?;
          Ok(())
      }
      ```
      
      ---
      
      ## EpochSchedule Sysvar
      
      **Address:** `solana_program::sysvar::epoch_schedule::ID`
      
      The EpochSchedule sysvar provides information about epoch timing and slot calculations.
      
      ### EpochSchedule Structure
      
      ```rust
      use solana_program::epoch_schedule::EpochSchedule;
      
      pub struct EpochSchedule {
          pub slots_per_epoch: u64,              // Slots per epoch after warmup
          pub leader_schedule_slot_offset: u64,  // Offset for leader schedule
          pub warmup: bool,                      // Whether in warmup period
          pub first_normal_epoch: Epoch,         // First non-warmup epoch
          pub first_normal_slot: Slot,           // First slot of first normal epoch
      }
      ```
      
      ### Accessing EpochSchedule
      
      ```rust
      use solana_program::sysvar::epoch_schedule::EpochSchedule;
      use solana_program::sysvar::Sysvar;
      
      pub fn get_epoch_info() -> ProgramResult {
          let epoch_schedule = EpochSchedule::get()?;
      
          msg!("Slots per epoch: {}", epoch_schedule.slots_per_epoch);
          msg!("First normal epoch: {}", epoch_schedule.first_normal_epoch);
          msg!("Warmup: {}", epoch_schedule.warmup);
      
          Ok(())
      }
      ```
      
      ### Common EpochSchedule Use Cases
      
      **1. Calculating epoch from slot:**
      
      ```rust
      use solana_program::clock::Clock;
      use solana_program::epoch_schedule::EpochSchedule;
      
      pub fn calculate_epoch_from_slot(
          slot: u64,
      ) -> Result<u64, ProgramError> {
          let epoch_schedule = EpochSchedule::get()?;
      
          let epoch = epoch_schedule.get_epoch(slot);
          msg!("Slot {} is in epoch {}", slot, epoch);
      
          Ok(epoch)
      }
      ```
      
      **2. Determining slots remaining in epoch:**
      
      ```rust
      pub fn slots_until_epoch_end() -> Result<u64, ProgramError> {
          let clock = Clock::get()?;
          let epoch_schedule = EpochSchedule::get()?;
      
          let current_slot = clock.slot;
          let current_epoch = clock.epoch;
      
          // Get first slot of next epoch
          let next_epoch_start = epoch_schedule.get_first_slot_in_epoch(current_epoch + 1);
      
          let remaining = next_epoch_start - current_slot;
          msg!("Slots remaining in epoch: {}", remaining);
      
          Ok(remaining)
      }
      ```
      
      **3. Epoch-based reward distribution:**
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct RewardState {
          pub last_distribution_epoch: u64,
          pub total_distributed: u64,
      }
      
      pub fn distribute_epoch_rewards(
          reward_state_account: &AccountInfo,
      ) -> ProgramResult {
          let clock = Clock::get()?;
          let mut state = RewardState::try_from_slice(&reward_state_account.data.borrow())?;
      
          if clock.epoch > state.last_distribution_epoch {
              let epochs_passed = clock.epoch - state.last_distribution_epoch;
      
              msg!("Distributing rewards for {} epochs", epochs_passed);
      
              // Distribute rewards
              let reward_amount = epochs_passed * 1000; // Example
              state.total_distributed += reward_amount;
              state.last_distribution_epoch = clock.epoch;
      
              state.serialize(&mut &mut reward_state_account.data.borrow_mut()[..])?;
          }
      
          Ok(())
      }
      ```
      
      ---
      
      ## SlotHashes Sysvar
      
      **Address:** `solana_program::sysvar::slot_hashes::ID`
      
      The SlotHashes sysvar contains recent slot hashes for verification purposes.
      
      ### SlotHashes Structure
      
      ```rust
      use solana_program::slot_hashes::SlotHashes;
      
      // SlotHashes contains up to 512 recent (slot, hash) pairs
      pub struct SlotHashes {
          // Vector of (slot, hash) tuples
          // Most recent first, up to MAX_ENTRIES (512)
      }
      ```
      
      ### Accessing SlotHashes
      
      ```rust
      use solana_program::sysvar::slot_hashes::SlotHashes;
      use solana_program::sysvar::Sysvar;
      
      pub fn verify_recent_slot(
          claimed_slot: u64,
          claimed_hash: &[u8; 32],
      ) -> ProgramResult {
          let slot_hashes = SlotHashes::get()?;
      
          // Check if slot is in recent history
          for (slot, hash) in slot_hashes.iter() {
              if *slot == claimed_slot {
                  if hash.as_ref() == claimed_hash {
                      msg!("Slot hash verified!");
                      return Ok(());
                  } else {
                      msg!("Slot hash mismatch!");
                      return Err(ProgramError::InvalidArgument);
                  }
              }
          }
      
          msg!("Slot not found in recent history");
          Err(ProgramError::InvalidArgument)
      }
      ```
      
      ### Common SlotHashes Use Cases
      
      **1. Verifying transaction recency:**
      
      ```rust
      pub fn verify_transaction_recent(
          slot_hashes_account: &AccountInfo,
          claimed_slot: u64,
      ) -> ProgramResult {
          let slot_hashes = SlotHashes::from_account_info(slot_hashes_account)?;
      
          // Check if claimed slot is in recent 512 slots
          let is_recent = slot_hashes.iter().any(|(slot, _)| *slot == claimed_slot);
      
          if !is_recent {
              msg!("Transaction too old or slot invalid");
              return Err(ProgramError::Custom(1));
          }
      
          Ok(())
      }
      ```
      
      **2. Preventing replay attacks:**
      
      ```rust
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct ProcessedSlot {
          pub slot: u64,
          pub hash: [u8; 32],
      }
      
      pub fn process_once_per_slot(
          state_account: &AccountInfo,
      ) -> ProgramResult {
          let slot_hashes = SlotHashes::get()?;
          let mut state = ProcessedSlot::try_from_slice(&state_account.data.borrow())?;
      
          // Get current slot and hash
          let (current_slot, current_hash) = slot_hashes.iter().next()
              .ok_or(ProgramError::InvalidArgument)?;
      
          if state.slot == *current_slot {
              msg!("Already processed in this slot!");
              return Err(ProgramError::Custom(2)); // Already processed
          }
      
          // Update state
          state.slot = *current_slot;
          state.hash = current_hash.to_bytes();
          state.serialize(&mut &mut state_account.data.borrow_mut()[..])?;
      
          Ok(())
      }
      ```
      
      **⚠️ Note:** SlotHashes only maintains the most recent 512 slots. For older verification, use a different approach.
      
      ---
      
      ## Other Sysvars
      
      ### StakeHistory
      
      **Address:** `solana_program::sysvar::stake_history::ID`
      
      Provides historical stake activation and deactivation information.
      
      ```rust
      use solana_program::sysvar::stake_history::StakeHistory;
      
      pub fn get_stake_history() -> ProgramResult {
          let stake_history = StakeHistory::get()?;
      
          // Access historical stake data by epoch
          msg!("Stake history available");
          Ok(())
      }
      ```
      
      **Use cases:**
      - Stake pool programs
      - Historical stake analysis
      - Reward calculations
      
      ### EpochRewards
      
      **Address:** `solana_program::sysvar::epoch_rewards::ID`
      
      Provides information about epoch rewards distribution (if active).
      
      ```rust
      use solana_program::sysvar::epoch_rewards::EpochRewards;
      
      pub fn check_epoch_rewards() -> ProgramResult {
          let epoch_rewards = EpochRewards::get()?;
      
          msg!("Epoch rewards data available");
          Ok(())
      }
      ```
      
      **Use cases:**
      - Stake reward programs
      - Validator reward tracking
      
      ### Instructions
      
      **Address:** `solana_program::sysvar::instructions::ID`
      
      Provides access to instructions in the current transaction.
      
      ```rust
      use solana_program::sysvar::instructions;
      
      pub fn validate_transaction_instructions(
          instructions_account: &AccountInfo,
      ) -> ProgramResult {
          // Check if current instruction is not the first
          let current_index = instructions::load_current_index_checked(instructions_account)?;
      
          msg!("Current instruction index: {}", current_index);
      
          // Load a specific instruction
          if current_index > 0 {
              let prev_ix = instructions::load_instruction_at_checked(
                  (current_index - 1) as usize,
                  instructions_account,
              )?;
      
              msg!("Previous instruction program: {}", prev_ix.program_id);
          }
      
          Ok(())
      }
      ```
      
      **Use cases:**
      - Cross-instruction validation
      - Ensuring instruction order
      - Detecting sandwich attacks
      
      ---
      
      ## Access Patterns
      
      ### Pattern 1: get() - Direct Access (Recommended)
      
      **Advantages:**
      - No account needed in instruction
      - Saves account space
      - Lower CU cost (~100 CU)
      - Cleaner code
      
      **Disadvantages:**
      - Not supported for all sysvars
      - Can't be passed to CPIs
      
      ```rust
      use solana_program::sysvar::Sysvar;
      
      pub fn use_sysvar_direct() -> ProgramResult {
          let clock = Clock::get()?;
          let rent = Rent::get()?;
      
          msg!("Clock: {}", clock.unix_timestamp);
          msg!("Rent: {}", rent.lamports_per_byte_year);
      
          Ok(())
      }
      ```
      
      **Supported sysvars:**
      - Clock
      - Rent
      - EpochSchedule
      - EpochRewards
      - Fees (deprecated)
      
      ### Pattern 2: from_account_info - Account Access
      
      **Advantages:**
      - Works for all sysvars
      - Can be validated
      - Can be passed to CPIs
      - Required for some sysvars (SlotHashes, Instructions)
      
      **Disadvantages:**
      - Account must be passed in instruction
      - Slightly higher CU cost (~300 CU)
      - More boilerplate
      
      ```rust
      use solana_program::sysvar::clock;
      
      pub fn use_sysvar_from_account(
          clock_account: &AccountInfo,
      ) -> ProgramResult {
          // Validate account address
          if clock_account.key != &clock::ID {
              return Err(ProgramError::InvalidArgument);
          }
      
          let clock = Clock::from_account_info(clock_account)?;
          msg!("Clock: {}", clock.unix_timestamp);
      
          Ok(())
      }
      ```
      
      **Required for:**
      - SlotHashes
      - StakeHistory
      - Instructions
      - Any sysvar passed to CPI
      
      ### Pattern 3: Hybrid Approach
      
      **Use get() when possible, account when needed:**
      
      ```rust
      pub fn hybrid_sysvar_access(
          accounts: &[AccountInfo],
          need_cpi: bool,
      ) -> ProgramResult {
          if need_cpi {
              // Need account for CPI
              let account_info_iter = &mut accounts.iter();
              let clock_account = next_account_info(account_info_iter)?;
      
              let clock = Clock::from_account_info(clock_account)?;
      
              // Can pass clock_account to CPI
              msg!("Using account access");
          } else {
              // Direct access is cheaper
              let clock = Clock::get()?;
              msg!("Using direct access");
          }
      
          Ok(())
      }
      ```
      
      ---
      
      ## Performance Implications
      
      ### Compute Unit Costs
      
      | Access Method | Approximate CU Cost |
      |--------------|---------------------|
      | Clock::get() | ~100 CU |
      | Rent::get() | ~100 CU |
      | EpochSchedule::get() | ~100 CU |
      | Clock::from_account_info() | ~300 CU |
      | SlotHashes::from_account_info() | ~500 CU |
      
      ### Optimization Tips
      
      **1. Use get() when possible:**
      
      ```rust
      // ✅ Efficient - 100 CU
      let clock = Clock::get()?;
      
      // ❌ Wasteful - 300 CU (unless needed for CPI)
      let clock = Clock::from_account_info(clock_account)?;
      ```
      
      **2. Cache sysvar values:**
      
      ```rust
      // ❌ Wasteful - calls get() multiple times
      for i in 0..10 {
          let clock = Clock::get()?;  // 100 CU × 10 = 1000 CU
          process_item(i, clock.unix_timestamp)?;
      }
      
      // ✅ Efficient - call once
      let clock = Clock::get()?;  // 100 CU
      let timestamp = clock.unix_timestamp;
      for i in 0..10 {
          process_item(i, timestamp)?;
      }
      ```
      
      **3. Avoid unnecessary sysvar access:**
      
      ```rust
      // ❌ Wasteful - reading sysvar in every call
      pub fn update_balance(account: &AccountInfo, amount: u64) -> ProgramResult {
          let clock = Clock::get()?;  // Not needed!
          // ... no clock usage
          Ok(())
      }
      
      // ✅ Efficient - only access when needed
      pub fn update_with_timestamp(account: &AccountInfo, amount: u64) -> ProgramResult {
          let clock = Clock::get()?;  // Used below
          let timestamp = clock.unix_timestamp;
          // ... use timestamp
          Ok(())
      }
      ```
      
      ---
      
      ## Best Practices
      
      ### 1. Prefer get() Over from_account_info()
      
      **Unless you need the account for CPI or validation:**
      
      ```rust
      // ✅ Default choice
      let clock = Clock::get()?;
      
      // Only if needed for CPI
      let clock = Clock::from_account_info(clock_account)?;
      invoke(&ix, &[..., clock_account])?;
      ```
      
      ### 2. Validate Sysvar Accounts
      
      **When accepting sysvar accounts, always validate:**
      
      ```rust
      pub fn validate_clock_account(
          clock_account: &AccountInfo,
      ) -> ProgramResult {
          // ✅ Always validate sysvar address
          if clock_account.key != &solana_program::sysvar::clock::ID {
              msg!("Invalid Clock account");
              return Err(ProgramError::InvalidArgument);
          }
      
          Ok(())
      }
      ```
      
      ### 3. Use Clock for Timestamps, Not Slot Hashes
      
      **For simple time-based logic:**
      
      ```rust
      // ✅ Simple and efficient
      let clock = Clock::get()?;
      if clock.unix_timestamp >= unlock_time {
          // unlock
      }
      
      // ❌ Overkill - SlotHashes is for verification, not timing
      let slot_hashes = SlotHashes::get()?;
      // Complex slot-based timing logic
      ```
      
      ### 4. Cache Sysvar Values
      
      **Read once, use multiple times:**
      
      ```rust
      pub fn process_multiple_accounts(
          accounts: &[AccountInfo],
      ) -> ProgramResult {
          // ✅ Read once
          let clock = Clock::get()?;
          let timestamp = clock.unix_timestamp;
      
          for account in accounts {
              update_account_timestamp(account, timestamp)?;
          }
      
          Ok(())
      }
      ```
      
      ### 5. Document Sysvar Dependencies
      
      **Be explicit about which sysvars your program uses:**
      
      ```rust
      /// Processes user staking
      ///
      /// # Sysvars
      /// - Clock: for stake timestamp
      /// - Rent: for account validation
      ///
      /// # Accounts
      /// - `[writable]` stake_account
      /// - `[signer]` user
      pub fn process_stake(
          program_id: &Pubkey,
          accounts: &[AccountInfo],
          amount: u64,
      ) -> ProgramResult {
          let clock = Clock::get()?;
          let rent = Rent::get()?;
      
          // ...
          Ok(())
      }
      ```
      
      ### 6. Handle Clock Drift
      
      **Don't assume unix_timestamp is perfectly accurate:**
      
      ```rust
      // ❌ Risky - exact timestamp match
      if clock.unix_timestamp == expected_time {
          // May never trigger
      }
      
      // ✅ Safe - use ranges
      if clock.unix_timestamp >= expected_time {
          // Reliable
      }
      
      // ✅ Best - add tolerance for early/late
      const TOLERANCE: i64 = 60; // 60 seconds
      if clock.unix_timestamp >= expected_time - TOLERANCE {
          // Handles clock drift
      }
      ```
      
      ---
      
      ## Summary
      
      **Key Takeaways:**
      
      1. **Use get() when possible** for lower CU costs and simpler code
      2. **Use from_account_info()** when passing to CPIs or for sysvars without get()
      3. **Always validate** sysvar account addresses when accepting them
      4. **Cache sysvar values** to avoid redundant reads
      5. **Understand timing limitations** - unix_timestamp is approximate
      
      **Most Common Sysvars:**
      
      | Sysvar | Primary Use | Access Method |
      |--------|------------|---------------|
      | **Clock** | Timestamps, epochs, slots | `Clock::get()` |
      | **Rent** | Rent exemption calculations | `Rent::get()` |
      | **EpochSchedule** | Epoch/slot calculations | `EpochSchedule::get()` |
      | **SlotHashes** | Recent slot verification | `from_account_info()` only |
      | **Instructions** | Transaction introspection | `from_account_info()` only |
      
      **Common Patterns:**
      
      ```rust
      // Timestamp current event
      let clock = Clock::get()?;
      event.created_at = clock.unix_timestamp;
      
      // Validate rent exemption
      let rent = Rent::get()?;
      if !rent.is_exempt(account.lamports(), account.data_len()) {
          return Err(ProgramError::AccountNotRentExempt);
      }
      
      // Calculate rent for new account
      let rent = Rent::get()?;
      let min_balance = rent.minimum_balance(space);
      ```
      
      Sysvars provide essential cluster state to your programs. Master their access patterns for efficient, production-ready Solana development.
      
    • testing-frameworks.md 29.7 KB
      # Solana Program Testing Frameworks
      
      **Detailed guide for Mollusk, LiteSVM, and Anchor testing frameworks**
      
      This file provides comprehensive documentation for the main testing frameworks used in Solana program development. For an overview of the testing strategy and pyramid, see the related files.
      
      ---
      
      ## Related Testing Documentation
      
      - **[Testing Overview](./testing-overview.md)** - Testing pyramid structure and types of tests
      - **[Testing Best Practices](./testing-practices.md)** - Best practices, common patterns, and additional resources
      
      ---
      
      ## Table of Contents
      
      1. [Mollusk Testing](#mollusk-testing)
      2. [Anchor-Specific Testing](#anchor-specific-testing)
      3. [Native Rust Testing](#native-rust-testing)
      
      ---
      
      ## Mollusk Testing
      
      ### What is Mollusk?
      
      Mollusk is a lightweight test harness that provides a minified Solana Virtual Machine (SVM) environment for program testing. It creates a program execution pipeline directly from low-level SVM components without the overhead of a full validator.
      
      **Key characteristics:**
      - No validator runtime (no AccountsDB, Bank, or other large components)
      - Exceptionally fast test execution
      - Direct program ELF execution via BPF Loader
      - Requires explicit account lists (can't load from storage)
      - Configurable compute budget, feature set, and sysvars
      
      ### Setup and Dependencies
      
      #### Version Compatibility
      
      **IMPORTANT:** Mollusk versions must match your Solana SDK version.
      
      **For Anchor 0.32.1 (Solana SDK 2.2.x):**
      ```toml
      [dev-dependencies]
      mollusk-svm = "0.5.1"
      mollusk-svm-bencher = "0.5.1"
      mollusk-svm-programs-token = "0.5.1"
      solana-sdk = "2.2"
      spl-token = "7.0"
      spl-associated-token-account = "6.0"
      ```
      
      **Why 0.5.1?**
      - Anchor 0.32.1 uses Solana SDK 2.2.x internally
      - Mollusk 0.5.1 is the last version compatible with Solana 2.x
      - Mollusk 0.6.0+ uses Solana 3.0 and won't compile with Anchor 0.32.1
      
      **For Native Rust programs (Solana SDK 2.1.x or 2.2.x):**
      ```toml
      [dev-dependencies]
      mollusk-svm = "0.5.1"
      solana-sdk = "2.2"  # Or "2.1" depending on your program
      ```
      
      **For newer Solana versions (3.0+):**
      ```toml
      [dev-dependencies]
      mollusk-svm = "0.9"  # Latest version
      solana-sdk = "3.0"
      ```
      
      **How to check your Solana SDK version:**
      ```bash
      # For Anchor projects
      grep solana-program programs/*/Cargo.toml
      
      # For native Rust
      grep solana-program Cargo.toml
      
      # Check Anchor's internal SDK version
      cargo tree | grep solana-sdk
      ```
      
      #### Standard Dependencies
      
      For testing with Token program:
      ```toml
      [dev-dependencies]
      mollusk-svm-programs-token = "0.5.1"  # Match mollusk-svm version
      spl-token = "7.0"                      # For Solana 2.x
      ```
      
      For compute unit benchmarking:
      ```toml
      [dev-dependencies]
      mollusk-svm-bencher = "0.5.1"  # Match mollusk-svm version
      ```
      
      ### Basic Test Structure
      
      ```rust
      use {
          mollusk_svm::Mollusk,
          solana_sdk::{
              account::Account,
              instruction::{AccountMeta, Instruction},
              pubkey::Pubkey,
          },
      };
      
      #[test]
      fn test_my_instruction() {
          // 1. Initialize Mollusk with your program
          let program_id = Pubkey::new_unique();
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
      
          // 2. Setup accounts
          let user = Pubkey::new_unique();
          let accounts = vec![
              (user, Account {
                  lamports: 1_000_000,
                  data: vec![],
                  owner: program_id,
                  executable: false,
                  rent_epoch: 0,
              }),
          ];
      
          // 3. Create instruction
          let instruction = Instruction::new_with_bytes(
              program_id,
              &[0, 1, 2, 3],  // instruction data
              vec![AccountMeta::new(user, true)],
          );
      
          // 4. Process instruction
          let result = mollusk.process_instruction(&instruction, &accounts);
      
          // 5. Assert success
          assert!(result.is_ok());
      }
      ```
      
      ### Four Main API Methods
      
      Mollusk provides four core testing methods:
      
      **1. `process_instruction`** - Execute single instruction, return result
      ```rust
      let result = mollusk.process_instruction(&instruction, &accounts);
      ```
      
      **2. `process_and_validate_instruction`** - Execute and validate with checks
      ```rust
      mollusk.process_and_validate_instruction(
          &instruction,
          &accounts,
          &checks,
      );
      ```
      
      **3. `process_instruction_chain`** - Execute multiple instructions sequentially
      ```rust
      let result = mollusk.process_instruction_chain(
          &[instruction1, instruction2, instruction3],
          &accounts,
      );
      ```
      
      **4. `process_and_validate_instruction_chain`** - Execute chain with per-instruction checks
      ```rust
      mollusk.process_and_validate_instruction_chain(
          &[
              (&instruction1, &[Check::success()]),
              (&instruction2, &[Check::success()]),
          ],
          &accounts,
      );
      ```
      
      ### Creating Test Accounts
      
      Test accounts must be created explicitly with all required fields:
      
      ```rust
      use solana_sdk::account::Account;
      
      // Basic account
      let account = Account {
          lamports: 1_000_000,           // Account balance
          data: vec![0; 100],             // Account data
          owner: program_id,              // Owner program
          executable: false,              // Not executable
          rent_epoch: 0,                  // Rent epoch
      };
      
      // System account
      let system_account = Account {
          lamports: 1_000_000,
          data: vec![],
          owner: system_program::id(),
          executable: false,
          rent_epoch: 0,
      };
      
      // Rent-exempt account
      let rent = mollusk.sysvars.rent;
      let rent_exempt_account = Account {
          lamports: rent.minimum_balance(data_len),
          data: vec![0; data_len],
          owner: program_id,
          executable: false,
          rent_epoch: 0,
      };
      ```
      
      ### Processing Instructions
      
      **Simple execution:**
      ```rust
      let result = mollusk.process_instruction(&instruction, &accounts);
      assert!(result.is_ok());
      ```
      
      **With result inspection:**
      ```rust
      let result = mollusk.process_instruction(&instruction, &accounts);
      match result {
          Ok(result) => {
              println!("Compute units: {}", result.compute_units_consumed);
              // Access modified accounts from result
          }
          Err(err) => panic!("Instruction failed: {:?}", err),
      }
      ```
      
      ### Validation with Check API
      
      The `Check` enum provides common validation patterns:
      
      **Success checks:**
      ```rust
      use mollusk_svm::result::Check;
      
      let checks = vec![
          Check::success(),                          // Instruction succeeded
          Check::compute_units(5000),                // Exact compute units
      ];
      ```
      
      **Account state checks:**
      ```rust
      let checks = vec![
          Check::account(&pubkey)
              .lamports(1_000_000)                   // Check lamports
              .data(&[1, 2, 3, 4])                   // Check full data
              .data_slice(8, &[1, 2, 3, 4])          // Check data slice at offset
              .owner(&program_id)                     // Check owner
              .executable(false)                      // Check executable flag
              .space(100)                             // Check data length
              .rent_exempt()                          // Check rent exempt
              .build(),
      ];
      ```
      
      **Error checks:**
      ```rust
      use solana_sdk::instruction::InstructionError;
      
      let checks = vec![
          Check::instruction_err(InstructionError::InvalidInstructionData),
      ];
      ```
      
      **Complete validation example:**
      ```rust
      use {
          mollusk_svm::{Mollusk, result::Check},
          solana_sdk::{
              account::Account,
              instruction::Instruction,
              pubkey::Pubkey,
              system_instruction,
              system_program,
          },
      };
      
      #[test]
      fn test_system_transfer() {
          let sender = Pubkey::new_unique();
          let recipient = Pubkey::new_unique();
      
          let base_lamports = 100_000_000;
          let transfer_amount = 42_000;
      
          let instruction = system_instruction::transfer(&sender, &recipient, transfer_amount);
          let accounts = [
              (
                  sender,
                  Account::new(base_lamports, 0, &system_program::id()),
              ),
              (
                  recipient,
                  Account::new(base_lamports, 0, &system_program::id()),
              ),
          ];
      
          let checks = vec![
              Check::success(),
              Check::account(&sender)
                  .lamports(base_lamports - transfer_amount)
                  .build(),
              Check::account(&recipient)
                  .lamports(base_lamports + transfer_amount)
                  .build(),
          ];
      
          Mollusk::default().process_and_validate_instruction(
              &instruction,
              &accounts,
              &checks,
          );
      }
      ```
      
      ### Compute Unit Benchmarking
      
      Monitor compute unit usage to catch performance regressions:
      
      **Basic benchmark:**
      ```rust
      use mollusk_svm_bencher::MolluskComputeUnitBencher;
      
      fn main() {
          let program_id = Pubkey::new_unique();
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
      
          MolluskComputeUnitBencher::new(mollusk)
              .bench(("my_instruction", &instruction, &accounts))
              .must_pass(true)
              .out_dir("./target/benches")
              .execute();
      }
      ```
      
      **Benchmark multiple instructions:**
      ```rust
      fn main() {
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
          let bencher = MolluskComputeUnitBencher::new(mollusk);
      
          bencher.bench(("initialize", &init_ix, &init_accounts))
              .must_pass(true);
      
          bencher.bench(("update", &update_ix, &update_accounts))
              .must_pass(true);
      
          bencher.bench(("close", &close_ix, &close_accounts))
              .must_pass(true)
              .out_dir("./target/benches")
              .execute();
      }
      ```
      
      Run benchmarks with:
      ```bash
      cargo bench
      ```
      
      Output includes:
      - Current compute units consumed
      - Previous benchmark value
      - Delta (increase/decrease)
      - Pass/fail status
      
      ### Advanced Patterns
      
      #### Stateful Context Testing
      
      Use `MolluskContext` to persist account state across multiple instructions:
      
      ```rust
      use std::collections::HashMap;
      
      #[test]
      fn test_sequential_transfers() {
          let mollusk = Mollusk::default();
      
          // Create initial account store
          let mut account_store = HashMap::new();
          let alice = Pubkey::new_unique();
          let bob = Pubkey::new_unique();
      
          account_store.insert(
              alice,
              Account {
                  lamports: 1_000_000,
                  data: vec![],
                  owner: system_program::id(),
                  executable: false,
                  rent_epoch: 0,
              },
          );
      
          account_store.insert(
              bob,
              Account {
                  lamports: 0,
                  data: vec![],
                  owner: system_program::id(),
                  executable: false,
                  rent_epoch: 0,
              },
          );
      
          // Create stateful context
          let context = mollusk.with_context(account_store);
      
          // First transfer - state persists automatically
          let instruction1 = system_instruction::transfer(&alice, &bob, 200_000);
          context.process_instruction(&instruction1);
      
          // Second transfer - uses updated state from first transfer
          let instruction2 = system_instruction::transfer(&alice, &bob, 100_000);
          context.process_instruction(&instruction2);
      
          // Access final account state
          let store = context.account_store.borrow();
          assert_eq!(store.get(&alice).unwrap().lamports, 700_000);
          assert_eq!(store.get(&bob).unwrap().lamports, 300_000);
      }
      ```
      
      #### Instruction Chains with Validation
      
      Process multiple instructions and validate state after each:
      
      ```rust
      #[test]
      fn test_instruction_chain_with_checks() {
          let mollusk = Mollusk::default();
      
          let alice = Pubkey::new_unique();
          let bob = Pubkey::new_unique();
          let carol = Pubkey::new_unique();
      
          let starting_lamports = 1_000_000;
      
          mollusk.process_and_validate_instruction_chain(
              &[
                  (
                      &system_instruction::transfer(&alice, &bob, 300_000),
                      &[
                          Check::success(),
                          Check::account(&alice).lamports(700_000).build(),
                          Check::account(&bob).lamports(300_000).build(),
                      ],
                  ),
                  (
                      &system_instruction::transfer(&bob, &carol, 100_000),
                      &[
                          Check::success(),
                          Check::account(&bob).lamports(200_000).build(),
                          Check::account(&carol).lamports(100_000).build(),
                      ],
                  ),
              ],
              &[
                  (alice, system_account(starting_lamports)),
                  (bob, system_account(0)),
                  (carol, system_account(0)),
              ],
          );
      }
      ```
      
      **Important:** Instruction chains are NOT equivalent to Solana transactions. Mollusk doesn't impose transaction constraints like loaded account keys or size limits. Chains are primarily for testing program execution flows.
      
      #### Time-Dependent Testing with warp_to_slot
      
      Test logic that depends on clock or slot:
      
      ```rust
      use solana_sdk::clock::Clock;
      
      #[test]
      fn test_time_dependent_logic() {
          let mut mollusk = Mollusk::default();
      
          // Warp to a specific slot
          mollusk.warp_to_slot(1000);
      
          // Test logic that depends on clock.slot
          let result1 = mollusk.process_instruction(&time_check_ix, &accounts);
          assert!(result1.is_ok());
      
          // Warp forward in time
          mollusk.warp_to_slot(2000);
      
          // Test again with new slot
          let result2 = mollusk.process_instruction(&time_check_ix, &accounts);
          assert!(result2.is_ok());
      }
      ```
      
      #### Custom Sysvar Configuration
      
      Modify sysvars to test specific conditions:
      
      ```rust
      use solana_sdk::rent::Rent;
      
      #[test]
      fn test_with_custom_rent() {
          let mut mollusk = Mollusk::default();
      
          // Customize rent parameters
          mollusk.sysvars.rent = Rent {
              lamports_per_byte_year: 1,
              exemption_threshold: 1.0,
              burn_percent: 0,
          };
      
          // Test with custom rent configuration
          let result = mollusk.process_instruction(&instruction, &accounts);
          assert!(result.is_ok());
      }
      ```
      
      #### Testing with Built-in Programs
      
      **Default builtins:**
      ```rust
      // Mollusk::default() includes subset of builtin programs
      let mollusk = Mollusk::default();  // Includes System, BPF Loader, etc.
      ```
      
      **All builtins:**
      ```toml
      [dev-dependencies]
      mollusk-svm = { version = "0.9", features = ["all-builtins"] }
      ```
      
      **Adding specific programs:**
      ```rust
      use mollusk_svm_programs_token::token;
      
      let mut mollusk = Mollusk::default();
      token::add_program(&mut mollusk);  // Add Token program
      ```
      
      ---
      
      ## Anchor-Specific Testing
      
      ### anchor test Command and Workflow
      
      Anchor provides integrated testing via the `anchor test` command:
      
      ```bash
      # Run all tests
      anchor test
      
      # Run tests without rebuilding
      anchor test --skip-build
      
      # Run tests without deploying (use existing deployment)
      anchor test --skip-deploy
      
      # Run specific test file
      anchor test -- --test test_initialize
      
      # Show program logs
      anchor test -- --nocapture
      ```
      
      **Standard workflow:**
      1. `anchor build` - Build program
      2. `anchor test` - Deploy to local validator and run TypeScript tests
      3. Test files run against deployed program
      4. Validator shuts down after tests complete
      
      ### TypeScript Tests with @coral-xyz/anchor
      
      **Basic test structure:**
      
      ```typescript
      import * as anchor from "@coral-xyz/anchor";
      import { Program } from "@coral-xyz/anchor";
      import { MyProgram } from "../target/types/my_program";
      import { expect } from "chai";
      
      describe("my-program", () => {
        // Configure the client to use the local cluster
        anchor.setProvider(anchor.AnchorProvider.env());
      
        const program = anchor.workspace.MyProgram as Program<MyProgram>;
      
        it("Initializes the program", async () => {
          // Test implementation
        });
      });
      ```
      
      ### Setting Up Test Environment
      
      ```typescript
      describe("my-program", () => {
        const provider = anchor.AnchorProvider.env();
        anchor.setProvider(provider);
      
        const program = anchor.workspace.MyProgram as Program<MyProgram>;
        const wallet = provider.wallet as anchor.Wallet;
      
        // Generate keypairs
        const user = anchor.web3.Keypair.generate();
        const account = anchor.web3.Keypair.generate();
      
        before(async () => {
          // Airdrop SOL for testing
          const airdropSig = await provider.connection.requestAirdrop(
            user.publicKey,
            2 * anchor.web3.LAMPORTS_PER_SOL
          );
          await provider.connection.confirmTransaction(airdropSig);
        });
      
        it("runs test", async () => {
          // Test code
        });
      });
      ```
      
      ### Invoking Instructions
      
      ```typescript
      it("initializes account", async () => {
        const [pda, bump] = anchor.web3.PublicKey.findProgramAddressSync(
          [Buffer.from("seed"), user.publicKey.toBuffer()],
          program.programId
        );
      
        const tx = await program.methods
          .initialize(bump)
          .accounts({
            user: user.publicKey,
            account: pda,
            systemProgram: anchor.web3.SystemProgram.programId,
          })
          .signers([user])
          .rpc();
      
        console.log("Transaction signature:", tx);
      });
      ```
      
      **With custom transaction options:**
      ```typescript
      const tx = await program.methods
        .initialize(bump)
        .accounts({ /* ... */ })
        .signers([user])
        .rpc({
          skipPreflight: false,
          commitment: "confirmed",
        });
      ```
      
      ### Reading Account State
      
      ```typescript
      it("reads account data", async () => {
        // Fetch account data
        const accountData = await program.account.myAccount.fetch(accountPubkey);
      
        // Assert values
        expect(accountData.value).to.equal(42);
        expect(accountData.owner.toString()).to.equal(user.publicKey.toString());
      });
      
      // Fetch multiple accounts
      const accounts = await program.account.myAccount.all();
      console.log("Found accounts:", accounts.length);
      
      // Fetch with filters
      const filtered = await program.account.myAccount.all([
        {
          memcmp: {
            offset: 8,  // Skip discriminator
            bytes: user.publicKey.toBase58(),
          },
        },
      ]);
      ```
      
      ### Event Listeners
      
      ```typescript
      it("listens for events", async () => {
        let eventReceived = false;
      
        // Set up event listener
        const listener = program.addEventListener(
          "MyEvent",
          (event, slot) => {
            console.log("Event received in slot:", slot);
            console.log("Event data:", event);
            eventReceived = true;
          }
        );
      
        // Trigger event
        await program.methods
          .triggerEvent()
          .accounts({ /* ... */ })
          .rpc();
      
        // Wait for event
        await new Promise((resolve) => setTimeout(resolve, 1000));
      
        expect(eventReceived).to.be.true;
      
        // Clean up listener
        await program.removeEventListener(listener);
      });
      ```
      
      ### LiteSVM for Fast Anchor Tests
      
      LiteSVM provides a faster alternative to the full validator for Anchor tests:
      
      **Installation:**
      ```bash
      cargo add litesvm --dev
      ```
      
      **Basic usage:**
      ```rust
      use {
          litesvm::LiteSVM,
          solana_sdk::{
              message::Message,
              pubkey::Pubkey,
              signature::{Keypair, Signer},
              system_instruction::transfer,
              transaction::Transaction,
          },
      };
      
      #[test]
      fn test_with_litesvm() {
          let from_keypair = Keypair::new();
          let from = from_keypair.pubkey();
          let to = Pubkey::new_unique();
      
          let mut svm = LiteSVM::new();
          svm.airdrop(&from, 10_000).unwrap();
      
          let instruction = transfer(&from, &to, 64);
          let tx = Transaction::new(
              &[&from_keypair],
              Message::new(&[instruction], Some(&from)),
              svm.latest_blockhash(),
          );
          let tx_res = svm.send_transaction(tx).unwrap();
      
          let from_account = svm.get_account(&from);
          let to_account = svm.get_account(&to);
          assert_eq!(from_account.unwrap().lamports, 4936);
          assert_eq!(to_account.unwrap().lamports, 64);
      }
      ```
      
      **Deploying programs:**
      ```rust
      use solana_sdk::pubkey;
      
      #[test]
      fn test_program() {
          let program_id = pubkey!("Logging111111111111111111111111111111111111");
          let mut svm = LiteSVM::new();
      
          // Load program from file
          let bytes = include_bytes!("../target/deploy/my_program.so");
          svm.add_program(program_id, bytes);
      
          // Test program
          // ...
      }
      ```
      
      **Time travel with LiteSVM:**
      ```rust
      use solana_sdk::clock::Clock;
      
      #[test]
      fn test_set_clock() {
          let mut svm = LiteSVM::new();
      
          // Get current clock
          let mut clock = svm.get_sysvar::<Clock>();
      
          // Set specific timestamp
          clock.unix_timestamp = 1735689600;  // January 1st 2025
          svm.set_sysvar::<Clock>(&clock);
      
          // Test time-dependent logic
          // ...
      
          // Warp to specific slot
          svm.warp_to_slot(1000);
      }
      ```
      
      **Writing arbitrary accounts:**
      ```rust
      use {
          solana_sdk::account::Account,
          spl_token::state::Account as TokenAccount,
      };
      
      #[test]
      fn test_with_token_account() {
          let mut svm = LiteSVM::new();
      
          let user = Pubkey::new_unique();
          let usdc_mint = pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
      
          // Create fake USDC balance
          let token_account_data = /* serialize TokenAccount with balance */;
      
          svm.set_account(
              user,
              Account {
                  lamports: 1_000_000,
                  data: token_account_data,
                  owner: spl_token::id(),
                  executable: false,
                  rent_epoch: 0,
              },
          );
      
          // Test with USDC balance
          // ...
      }
      ```
      
      ### Anchor.toml Test Configuration
      
      Configure testing behavior in `Anchor.toml`:
      
      ```toml
      [toolchain]
      anchor_version = "0.30.1"
      
      [features]
      resolution = true
      skip-lint = false
      
      [programs.localnet]
      my_program = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
      
      [registry]
      url = "https://api.apr.dev"
      
      [provider]
      cluster = "Localnet"
      wallet = "~/.config/solana/id.json"
      
      [scripts]
      test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
      
      [test]
      startup_wait = 5000  # Wait before running tests (ms)
      shutdown_wait = 2000  # Wait before shutting down validator (ms)
      upgradeable = false  # Deploy as upgradeable program
      
      [test.validator]
      url = "https://api.mainnet-beta.solana.com"  # Clone from mainnet
      ledger = ".anchor/test-ledger"
      bind_address = "0.0.0.0"
      
      [[test.validator.clone]]
      address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"  # Clone Metaplex
      
      [[test.validator.clone]]
      address = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"  # Clone Token program
      
      [[test.validator.account]]
      address = "..."  # Clone specific account
      filename = "account.json"
      ```
      
      ### Anchor Testing Best Practices
      
      1. **Use `anchor.workspace`**: Automatically loads program IDL
      2. **Airdrop SOL in `before()` hooks**: Set up test accounts before tests
      3. **Use proper commitment levels**: `confirmed` or `finalized` for reliability
      4. **Test error conditions**: Use `.simulate()` to test expected failures
      5. **Clean up between tests**: Reset account state or use fresh keypairs
      6. **Use `--skip-build` during iteration**: Speed up test runs
      7. **Test with realistic data**: Don't just test happy paths
      
      ---
      
      ## Native Rust Testing
      
      ### Cargo Test Setup
      
      Native Rust programs use standard Rust testing with Mollusk:
      
      **Project structure:**
      ```
      my-program/
      ├── Cargo.toml
      ├── src/
      │   ├── lib.rs
      │   ├── processor.rs
      │   └── instruction.rs
      └── tests/
          ├── test_initialize.rs
          ├── test_update.rs
          └── test_close.rs
      ```
      
      **Cargo.toml configuration:**
      ```toml
      [package]
      name = "my-program"
      version = "0.1.0"
      edition = "2021"
      
      [lib]
      crate-type = ["cdylib", "lib"]
      
      [dependencies]
      solana-program = "2.1"
      
      [dev-dependencies]
      mollusk-svm = "0.9"
      mollusk-svm-programs-token = "0.9"
      solana-sdk = "2.1"
      
      [[bench]]
      name = "compute_units"
      harness = false
      
      [profile.release]
      overflow-checks = true
      lto = "fat"
      codegen-units = 1
      
      [profile.release.build-override]
      opt-level = 3
      incremental = false
      codegen-units = 1
      ```
      
      ### Mollusk with Native Programs
      
      **Basic test example:**
      
      ```rust
      // tests/test_initialize.rs
      use {
          mollusk_svm::Mollusk,
          my_program::{instruction::initialize, ID},
          solana_sdk::{
              account::Account,
              instruction::{AccountMeta, Instruction},
              pubkey::Pubkey,
          },
      };
      
      #[test]
      fn test_initialize() {
          let program_id = ID;
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
      
          let user = Pubkey::new_unique();
          let account = Pubkey::new_unique();
      
          let instruction = Instruction {
              program_id,
              accounts: vec![
                  AccountMeta::new(user, true),
                  AccountMeta::new(account, false),
                  AccountMeta::new_readonly(solana_sdk::system_program::id(), false),
              ],
              data: initialize().data,
          };
      
          let accounts = vec![
              (user, Account {
                  lamports: 10_000_000,
                  data: vec![],
                  owner: solana_sdk::system_program::id(),
                  executable: false,
                  rent_epoch: 0,
              }),
              (account, Account {
                  lamports: 0,
                  data: vec![],
                  owner: solana_sdk::system_program::id(),
                  executable: false,
                  rent_epoch: 0,
              }),
          ];
      
          let result = mollusk.process_instruction(&instruction, &accounts);
          assert!(result.is_ok());
      }
      ```
      
      ### Manual Account Setup
      
      Native Rust tests require explicit account setup:
      
      ```rust
      use solana_sdk::account::Account;
      
      // Helper: Create system account
      fn system_account(lamports: u64) -> Account {
          Account {
              lamports,
              data: vec![],
              owner: solana_sdk::system_program::id(),
              executable: false,
              rent_epoch: 0,
          }
      }
      
      // Helper: Create program-owned account
      fn program_account(lamports: u64, data: Vec<u8>, owner: Pubkey) -> Account {
          Account {
              lamports,
              data,
              owner,
              executable: false,
              rent_epoch: 0,
          }
      }
      
      // Helper: Create rent-exempt account
      fn rent_exempt_account(data_len: usize, owner: Pubkey, mollusk: &Mollusk) -> Account {
          let lamports = mollusk.sysvars.rent.minimum_balance(data_len);
          Account {
              lamports,
              data: vec![0; data_len],
              owner,
              executable: false,
              rent_epoch: 0,
          }
      }
      
      // Usage
      #[test]
      fn test_with_helpers() {
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
      
          let user = Pubkey::new_unique();
          let data_account = Pubkey::new_unique();
      
          let accounts = vec![
              (user, system_account(10_000_000)),
              (data_account, rent_exempt_account(100, program_id, &mollusk)),
          ];
      
          // Test
          // ...
      }
      ```
      
      ### Testing CPIs
      
      Use `mollusk-svm-programs-token` for testing cross-program invocations:
      
      ```rust
      use {
          mollusk_svm::{result::Check, Mollusk},
          mollusk_svm_programs_token::token,
          solana_sdk::{
              account::Account,
              program_pack::Pack,
              pubkey::Pubkey,
          },
          spl_token::state::{Account as TokenAccount, AccountState, Mint},
      };
      
      #[test]
      fn test_token_transfer_cpi() {
          // Initialize Mollusk with Token program
          let mut mollusk = Mollusk::default();
          token::add_program(&mut mollusk);
      
          // Setup mint
          let mint = Pubkey::new_unique();
          let decimals = 6;
      
          let mut mint_data = vec![0u8; Mint::LEN];
          Mint::pack(
              Mint {
                  mint_authority: Some(authority).into(),
                  supply: 1_000_000,
                  decimals,
                  is_initialized: true,
                  freeze_authority: None.into(),
              },
              &mut mint_data,
          ).unwrap();
      
          // Setup source token account
          let source = Pubkey::new_unique();
          let mut source_data = vec![0u8; TokenAccount::LEN];
          TokenAccount::pack(
              TokenAccount {
                  mint,
                  owner: authority,
                  amount: 1_000_000,
                  delegate: None.into(),
                  state: AccountState::Initialized,
                  is_native: None.into(),
                  delegated_amount: 0,
                  close_authority: None.into(),
              },
              &mut source_data,
          ).unwrap();
      
          // Setup destination token account
          let destination = Pubkey::new_unique();
          let mut dest_data = vec![0u8; TokenAccount::LEN];
          TokenAccount::pack(
              TokenAccount {
                  mint,
                  owner: recipient,
                  amount: 0,
                  delegate: None.into(),
                  state: AccountState::Initialized,
                  is_native: None.into(),
                  delegated_amount: 0,
                  close_authority: None.into(),
              },
              &mut dest_data,
          ).unwrap();
      
          let mint_rent = mollusk.sysvars.rent.minimum_balance(Mint::LEN);
          let account_rent = mollusk.sysvars.rent.minimum_balance(TokenAccount::LEN);
      
          let accounts = vec![
              (source, Account {
                  lamports: account_rent,
                  data: source_data,
                  owner: token::ID,
                  executable: false,
                  rent_epoch: 0,
              }),
              (mint, Account {
                  lamports: mint_rent,
                  data: mint_data,
                  owner: token::ID,
                  executable: false,
                  rent_epoch: 0,
              }),
              (destination, Account {
                  lamports: account_rent,
                  data: dest_data,
                  owner: token::ID,
                  executable: false,
                  rent_epoch: 0,
              }),
          ];
      
          // Create transfer instruction
          use spl_token::instruction::transfer_checked;
      
          let instruction = transfer_checked(
              &token::ID,
              &source,
              &mint,
              &destination,
              &authority,
              &[],
              500_000,
              decimals,
          ).unwrap();
      
          // Validate transfer
          let checks = vec![
              Check::success(),
              Check::account(&source)
                  .data_slice(64, &(500_000u64).to_le_bytes())
                  .build(),
              Check::account(&destination)
                  .data_slice(64, &(500_000u64).to_le_bytes())
                  .build(),
          ];
      
          mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);
      }
      ```
      
      ### Validation Patterns
      
      **Account state validation:**
      ```rust
      use mollusk_svm::result::Check;
      
      let checks = vec![
          Check::success(),
          Check::account(&account_pubkey)
              .lamports(expected_lamports)
              .data(&expected_data)
              .owner(&expected_owner)
              .build(),
      ];
      
      mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);
      ```
      
      **Error validation:**
      ```rust
      use solana_sdk::instruction::InstructionError;
      
      let checks = vec![
          Check::instruction_err(InstructionError::InvalidAccountData),
      ];
      
      mollusk.process_and_validate_instruction(&bad_instruction, &accounts, &checks);
      ```
      
      **Compute unit validation:**
      ```rust
      let checks = vec![
          Check::success(),
          Check::compute_units(5000),  // Exactly 5000 CU
      ];
      ```
      
      **Data slice validation:**
      ```rust
      // Check specific bytes without loading full account data
      let checks = vec![
          Check::account(&account)
              .data_slice(8, &[1, 2, 3, 4])  // Check bytes 8-11
              .build(),
      ];
      ```
      
      ---
      
      ## Next Steps
      
      - For the testing strategy overview and pyramid structure, see **[Testing Overview](./testing-overview.md)**
      - For best practices, common patterns, and additional resources, see **[Testing Best Practices](./testing-practices.md)**
      
    • testing-overview.md 12.5 KB
      # Solana Program Testing Overview
      
      **High-level guide to testing Solana programs with the test pyramid structure**
      
      This file provides an overview of Solana program testing, the testing pyramid structure, and the types of tests you should write. For specific implementation details and framework-specific guidance, see the related files.
      
      ---
      
      ## Related Testing Documentation
      
      - **[Testing Frameworks](./testing-frameworks.md)** - Mollusk, LiteSVM, and Anchor testing implementations
      - **[Testing Best Practices](./testing-practices.md)** - Best practices, common patterns, and additional resources
      
      ---
      
      ## Table of Contents
      
      1. [Why Testing Matters](#why-testing-matters)
      2. [Types of Tests](#types-of-tests)
      3. [Testing Frameworks Available](#testing-frameworks-available)
      4. [Test Structure Pyramid](#test-structure-pyramid)
      
      ---
      
      ## Why Testing Matters for Solana Programs
      
      Solana programs are immutable after deployment and handle real financial assets. Comprehensive testing is critical to:
      
      - **Prevent loss of funds**: Bugs in deployed programs can lead to irreversible financial losses
      - **Ensure correctness**: Verify program logic works as intended under all conditions
      - **Optimize performance**: Monitor compute unit usage to stay within Solana's limits (1.4M CU cap)
      - **Build confidence**: Thorough testing enables safer deployments and upgrades
      - **Catch edge cases**: Test boundary conditions, error handling, and attack vectors
      
      ---
      
      ## Types of Tests
      
      **Unit Tests**
      - Test individual functions and instruction handlers in isolation
      - Fast, focused validation of specific logic
      - Run frequently during development
      
      **Integration Tests**
      - Test complete instruction flows with realistic account setups
      - Validate cross-program invocations (CPIs)
      - Ensure proper state transitions
      
      **Fuzz Tests**
      - Generate random inputs to find edge cases and vulnerabilities
      - Discover unexpected failure modes
      - Test input validation thoroughly
      
      **Compute Unit Benchmarks**
      - Monitor compute unit consumption for each instruction
      - Track performance regressions
      - Ensure programs stay within CU limits
      
      ---
      
      ## Testing Frameworks Available
      
      **Mollusk** (Recommended for both Anchor and Native Rust)
      - Lightweight SVM test harness
      - Exceptionally fast (no validator overhead)
      - Works with both Anchor and native Rust programs
      - Direct program execution via BPF loader
      - Requires explicit account setup (no AccountsDB)
      
      **LiteSVM** (Alternative for integration tests)
      - In-process Solana VM for testing
      - Available in Rust, TypeScript, and Python
      - Faster than solana-program-test
      - Supports RPC-like interactions
      - Good for complex integration scenarios
      
      **Anchor Test** (Anchor framework)
      - TypeScript-based testing using @coral-xyz/anchor
      - Integrates with local validator or LiteSVM
      - Natural for testing Anchor programs from client perspective
      - Slower but more realistic end-to-end tests
      
      **solana-program-test** (Legacy)
      - Full validator simulation
      - More realistic but much slower
      - Generally replaced by Mollusk and LiteSVM
      
      **Recommendation**: Use Mollusk for fast unit and integration tests. Use LiteSVM or Anchor tests for end-to-end validation when needed.
      
      ---
      
      ## Test Structure Pyramid
      
      ### Overview
      
      A production-grade Solana program should have a multi-level testing strategy. Each level serves a specific purpose and catches different types of bugs.
      
      ```
                          ┌─────────────────────┐
                          │  Devnet/Mainnet     │  ← Smoke tests
                          │  Smoke Tests        │    (Manual, slow)
                          └─────────────────────┘
                        ┌───────────────────────────┐
                        │   SDK Integration Tests   │  ← Full transaction flow
                        │   (LiteSVM/TypeScript)    │    (Seconds per test)
                        └───────────────────────────┘
                    ┌─────────────────────────────────────┐
                    │      Mollusk Program Tests          │  ← Instruction-level
                    │   (Unit + Integration in Rust)      │    (~100ms per test)
                    └─────────────────────────────────────┘
                ┌───────────────────────────────────────────────┐
                │        Inline Unit Tests (#[cfg(test)])       │  ← Pure functions
                │    (Math, validation, transformations)        │    (Milliseconds)
                └───────────────────────────────────────────────┘
      ```
      
      ### Level 1: Inline Unit Tests
      
      **Purpose:** Test pure functions in isolation - math, validation logic, data transformations.
      
      **Location:** Inside your program code with `#[cfg(test)]`
      
      **Why needed:**
      - Instant feedback (milliseconds)
      - Runs with `cargo test` - no build artifacts needed
      - Catches arithmetic edge cases before they reach the SVM
      - Documents expected behavior inline with code
      
      **What belongs here:**
      - Share calculations: `1_000_000 * 5000 / 10000 = 500_000`
      - Overflow detection: `u64::MAX * 10000 = None`
      - Rounding behavior: `100 * 1 / 10000 = 0` (floors)
      - BPS (basis points) sum validation
      - Data serialization/deserialization helpers
      
      **What doesn't belong:**
      - Account validation (needs ownership checks)
      - CPI logic
      - Full instruction execution
      - State transitions
      
      **Example:**
      ```rust
      // In your program code (e.g., src/math.rs)
      pub fn calculate_fee(amount: u64, fee_bps: u16) -> Option<u64> {
          let fee = (amount as u128)
              .checked_mul(fee_bps as u128)?
              .checked_div(10_000)?;
      
          Some(fee as u64)
      }
      
      #[cfg(test)]
      mod tests {
          use super::*;
      
          #[test]
          fn test_calculate_fee_basic() {
              assert_eq!(calculate_fee(1_000_000, 250), Some(25_000)); // 2.5%
              assert_eq!(calculate_fee(1_000_000, 5000), Some(500_000)); // 50%
          }
      
          #[test]
          fn test_calculate_fee_rounding() {
              assert_eq!(calculate_fee(100, 1), Some(0)); // Rounds down
              assert_eq!(calculate_fee(10_000, 1), Some(1)); // 0.01%
          }
      
          #[test]
          fn test_calculate_fee_overflow() {
              assert_eq!(calculate_fee(u64::MAX, 10000), None); // Would overflow
          }
      }
      ```
      
      ### Level 2: Mollusk Program Tests
      
      **Purpose:** Test individual instructions with full account setup but without validator overhead.
      
      **Location:** `tests/` directory or `#[cfg(test)]` modules
      
      **Why needed:**
      - Tests actual program binary execution
      - Validates account constraints, signer checks, ownership
      - ~100ms per test vs ~1s for full validator
      - Catches instruction-level bugs
      - Compute unit benchmarking
      
      **What belongs here:**
      - Each instruction handler (initialize, create_split, execute_split, etc.)
      - Error conditions (wrong signer, invalid account owner)
      - Account state transitions
      - Cross-program invocations (CPIs)
      - PDA derivation and signing
      - Rent exemption validation
      
      **Example:**
      ```rust
      // tests/test_initialize.rs
      use {
          mollusk_svm::Mollusk,
          my_program::{instruction::initialize, ID},
          solana_sdk::{
              account::Account,
              instruction::Instruction,
              pubkey::Pubkey,
          },
      };
      
      #[test]
      fn test_initialize_success() {
          let mollusk = Mollusk::new(&ID, "target/deploy/my_program");
      
          let user = Pubkey::new_unique();
          let account = Pubkey::new_unique();
      
          let instruction = initialize(&user, &account);
          let accounts = vec![
              (user, system_account(10_000_000)),
              (account, Account::default()),
          ];
      
          let result = mollusk.process_instruction(&instruction, &accounts);
          assert!(result.is_ok());
      }
      
      #[test]
      fn test_initialize_wrong_signer_fails() {
          let mollusk = Mollusk::new(&ID, "target/deploy/my_program");
      
          let user = Pubkey::new_unique();
          let wrong_signer = Pubkey::new_unique();
      
          let mut instruction = initialize(&user, &Pubkey::new_unique());
          instruction.accounts[0].is_signer = false; // Missing signature
      
          let accounts = vec![(user, system_account(10_000_000))];
      
          let checks = vec![Check::instruction_err(
              InstructionError::MissingRequiredSignature
          )];
      
          mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);
      }
      ```
      
      ### Level 3: SDK Integration Tests
      
      **Purpose:** Test that SDK produces correct instructions that work end-to-end.
      
      **Location:** Separate SDK package (`sdk/tests/`) or TypeScript tests
      
      **Why needed:**
      - Validates serialization matches program expectations
      - Tests full transaction flow (multiple instructions)
      - Catches SDK bugs before users hit them
      - Client-perspective testing
      - Ensures TypeScript/Rust SDK matches program
      
      **What belongs here:**
      - SDK instruction builders produce valid transactions
      - Full flows: create → deposit → execute
      - Multiple instructions in one transaction
      - Account resolution (finding PDAs from SDK)
      - Error handling from client side
      - Event parsing and decoding
      
      **Example (LiteSVM):**
      ```rust
      // sdk/tests/integration_test.rs
      use {
          litesvm::LiteSVM,
          my_program_sdk::{instructions, MyProgramClient},
          solana_sdk::{
              signature::Keypair,
              signer::Signer,
          },
      };
      
      #[test]
      fn test_full_flow_create_and_execute() {
          let mut svm = LiteSVM::new();
      
          // Add program
          let program_bytes = include_bytes!("../../target/deploy/my_program.so");
          svm.add_program(MY_PROGRAM_ID, program_bytes);
      
          // Create client
          let payer = Keypair::new();
          svm.airdrop(&payer.pubkey(), 10_000_000_000).unwrap();
      
          let client = MyProgramClient::new(&svm, &payer);
      
          // Step 1: Initialize
          let tx1 = client.initialize().unwrap();
          svm.send_transaction(tx1).unwrap();
      
          // Step 2: Deposit
          let tx2 = client.deposit(1_000_000).unwrap();
          svm.send_transaction(tx2).unwrap();
      
          // Step 3: Execute
          let tx3 = client.execute().unwrap();
          let result = svm.send_transaction(tx3).unwrap();
      
          // Verify final state
          let account = client.get_account().unwrap();
          assert_eq!(account.balance, 1_000_000);
      }
      ```
      
      ### Level 4: Devnet/Mainnet Smoke Tests
      
      **Purpose:** Final validation in real environment.
      
      **Location:** Manual testing or automated CI scripts
      
      **Why needed:**
      - Real RPC, real fees, real constraints
      - Validates deployment configuration
      - Tests against actual on-chain state
      - Catches environment-specific issues
      - Verifies upgrades work correctly
      
      **What belongs here:**
      - Post-deployment smoke tests (critical paths only)
      - Upgrade validation (new version works)
      - Integration with other mainnet programs
      - Performance under real network conditions
      
      **Example (Manual script):**
      ```bash
      #!/bin/bash
      # scripts/smoke-test-devnet.sh
      
      echo "Running devnet smoke tests..."
      
      # Test 1: Initialize
      solana-keygen new --no-bpf-loader-deprecated --force -o /tmp/test-user.json
      solana airdrop 2 /tmp/test-user.json --url devnet
      
      my-program-cli initialize \
        --program-id $PROGRAM_ID \
        --payer /tmp/test-user.json \
        --url devnet
      
      # Test 2: Execute main flow
      my-program-cli execute \
        --amount 1000000 \
        --payer /tmp/test-user.json \
        --url devnet
      
      echo "✅ Smoke tests passed"
      ```
      
      ### How to Use This Pyramid
      
      **During development:**
      1. Write inline tests as you implement math/validation
      2. Write Mollusk tests for each instruction
      3. Run frequently: `cargo test`
      
      **Before PR/merge:**
      1. Ensure all inline + Mollusk tests pass
      2. Add SDK integration tests if SDK changed
      3. Run compute unit benchmarks
      
      **Before deployment:**
      1. All tests pass on devnet-compatible build
      2. Deploy to devnet
      3. Run manual smoke tests on devnet
      4. If pass, proceed to mainnet
      
      **After deployment:**
      1. Run smoke tests on mainnet
      2. Monitor for errors
      3. Keep tests updated as program evolves
      
      ### Benefits of This Structure
      
      **Fast feedback loop:**
      - Level 1 tests run in milliseconds
      - Catch bugs early without slow iteration
      
      **Comprehensive coverage:**
      - Pure logic (Level 1)
      - Program execution (Level 2)
      - Client integration (Level 3)
      - Real environment (Level 4)
      
      **Efficient CI/CD:**
      - Level 1-2 in every PR (fast)
      - Level 3 on merge to main
      - Level 4 post-deployment
      
      **Clear responsibilities:**
      - Each level tests different concerns
      - No redundant tests
      - Easier to maintain
      
      ---
      
      ## Next Steps
      
      - For implementation details on Mollusk, LiteSVM, and Anchor testing, see **[Testing Frameworks](./testing-frameworks.md)**
      - For best practices, common patterns, and additional resources, see **[Testing Best Practices](./testing-practices.md)**
      
    • testing-practices.md 12.6 KB
      # Solana Program Testing Best Practices
      
      **Common patterns, best practices, and additional testing resources**
      
      This file provides best practices for organizing tests, testing common scenarios, and efficiently running your test suite. For framework-specific details and the testing pyramid structure, see the related files.
      
      ---
      
      ## Related Testing Documentation
      
      - **[Testing Overview](./testing-overview.md)** - Testing pyramid structure and types of tests
      - **[Testing Frameworks](./testing-frameworks.md)** - Mollusk, LiteSVM, and Anchor testing implementations
      
      ---
      
      ## Table of Contents
      
      1. [Testing Best Practices](#testing-best-practices)
      2. [Common Testing Patterns](#common-testing-patterns)
      3. [Additional Resources](#additional-resources)
      
      ---
      
      ## Testing Best Practices
      
      ### Test Organization
      
      **Organize by instruction:**
      ```
      tests/
      ├── test_initialize.rs
      ├── test_update.rs
      ├── test_transfer.rs
      ├── test_close.rs
      └── helpers/
          ├── mod.rs
          ├── accounts.rs
          └── instructions.rs
      ```
      
      **Use helper modules:**
      ```rust
      // tests/helpers/accounts.rs
      use solana_sdk::{account::Account, pubkey::Pubkey};
      
      pub fn system_account(lamports: u64) -> Account {
          Account {
              lamports,
              data: vec![],
              owner: solana_sdk::system_program::id(),
              executable: false,
              rent_epoch: 0,
          }
      }
      
      pub fn token_account(/* ... */) -> Account {
          // ...
      }
      ```
      
      ```rust
      // tests/test_initialize.rs
      mod helpers;
      use helpers::accounts::*;
      
      #[test]
      fn test_initialize() {
          let accounts = vec![
              (user, system_account(10_000_000)),
              // ...
          ];
      }
      ```
      
      ### Edge Cases to Test
      
      **Account validation:**
      - Missing accounts
      - Wrong account owner
      - Account not writable when required
      - Account not signer when required
      - Uninitialized accounts
      - Already initialized accounts
      
      **Numeric boundaries:**
      - Zero values
      - Maximum values (u64::MAX)
      - Overflow conditions
      - Underflow conditions
      - Negative results (when using signed integers)
      
      **Authorization:**
      - Missing signer
      - Wrong signer
      - Multiple signers
      - PDA signer validation
      
      **State transitions:**
      - Invalid state transitions
      - Idempotent operations
      - Concurrent operations
      - State rollback on error
      
      **Resource limits:**
      - Rent exemption
      - Maximum account size
      - Compute unit limits
      - Stack depth limits (CPI)
      
      ### Error Condition Testing
      
      **Test expected failures:**
      ```rust
      #[test]
      fn test_insufficient_funds_fails() {
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
      
          let user = Pubkey::new_unique();
          let accounts = vec![
              (user, system_account(100)),  // Not enough lamports
          ];
      
          let instruction = /* create transfer instruction for 1000 lamports */;
      
          let checks = vec![
              Check::instruction_err(InstructionError::InsufficientFunds),
          ];
      
          mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);
      }
      ```
      
      **Test invalid data:**
      ```rust
      #[test]
      fn test_invalid_instruction_data() {
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
      
          let instruction = Instruction {
              program_id,
              accounts: /* ... */,
              data: vec![255, 255, 255],  // Invalid instruction data
          };
      
          let checks = vec![
              Check::instruction_err(InstructionError::InvalidInstructionData),
          ];
      
          mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);
      }
      ```
      
      ### Compute Unit Monitoring
      
      **Set up continuous monitoring:**
      ```rust
      // benches/compute_units.rs
      use mollusk_svm_bencher::MolluskComputeUnitBencher;
      
      fn main() {
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
          let bencher = MolluskComputeUnitBencher::new(mollusk);
      
          // Benchmark each instruction
          bencher.bench(("initialize", &init_ix, &init_accounts));
          bencher.bench(("update", &update_ix, &update_accounts));
          bencher.bench(("close", &close_ix, &close_accounts));
      
          bencher
              .must_pass(true)
              .out_dir("./target/benches")
              .execute();
      }
      ```
      
      **Add to CI/CD:**
      ```yaml
      # .github/workflows/test.yml
      - name: Run compute unit benchmarks
        run: cargo bench
      
      - name: Check for CU regressions
        run: |
          if git diff --exit-code target/benches/; then
            echo "No compute unit changes"
          else
            echo "Compute unit usage changed - review carefully"
            git diff target/benches/
          fi
      ```
      
      ### Running Tests Efficiently
      
      **Build before testing:**
      ```bash
      # Native Rust
      cargo build-sbf && cargo test
      
      # Anchor
      anchor build && anchor test
      ```
      
      **Run specific tests:**
      ```bash
      # Native Rust
      cargo test test_initialize
      
      # Anchor
      anchor test -- --test test_initialize
      ```
      
      **Show program output:**
      ```bash
      # Native Rust
      cargo test -- --nocapture
      
      # Anchor
      anchor test -- --nocapture
      ```
      
      **Run tests in parallel (be careful with shared state):**
      ```bash
      cargo test -- --test-threads=4
      ```
      
      ---
      
      ## Common Testing Patterns
      
      ### Testing PDAs
      
      **Anchor approach:**
      ```typescript
      it("derives PDA correctly", async () => {
        const [pda, bump] = anchor.web3.PublicKey.findProgramAddressSync(
          [Buffer.from("seed"), user.publicKey.toBuffer()],
          program.programId
        );
      
        await program.methods
          .initialize(bump)
          .accounts({
            pda: pda,
            user: user.publicKey,
            systemProgram: anchor.web3.SystemProgram.programId,
          })
          .signers([user])
          .rpc();
      
        const accountData = await program.account.myAccount.fetch(pda);
        expect(accountData.bump).to.equal(bump);
      });
      ```
      
      **Native Rust approach:**
      ```rust
      #[test]
      fn test_pda_derivation() {
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
      
          let user = Pubkey::new_unique();
          let seeds = &[b"seed", user.as_ref()];
          let (pda, bump) = Pubkey::find_program_address(seeds, &program_id);
      
          let instruction = Instruction {
              program_id,
              accounts: vec![
                  AccountMeta::new(user, true),
                  AccountMeta::new(pda, false),
                  AccountMeta::new_readonly(system_program::id(), false),
              ],
              data: vec![0, bump],  // Initialize instruction with bump
          };
      
          let accounts = vec![
              (user, system_account(10_000_000)),
              (pda, Account::default()),
          ];
      
          let checks = vec![
              Check::success(),
              Check::account(&pda)
                  .owner(&program_id)
                  .build(),
          ];
      
          mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);
      }
      ```
      
      ### Testing Token Operations
      
      **Anchor with SPL Token:**
      ```typescript
      import { TOKEN_PROGRAM_ID, createMint, createAccount, mintTo } from "@solana/spl-token";
      
      it("transfers tokens", async () => {
        // Create mint
        const mint = await createMint(
          provider.connection,
          wallet.payer,
          wallet.publicKey,
          null,
          6
        );
      
        // Create token accounts
        const sourceAccount = await createAccount(
          provider.connection,
          wallet.payer,
          mint,
          user.publicKey
        );
      
        const destAccount = await createAccount(
          provider.connection,
          wallet.payer,
          mint,
          recipient.publicKey
        );
      
        // Mint tokens
        await mintTo(
          provider.connection,
          wallet.payer,
          mint,
          sourceAccount,
          wallet.publicKey,
          1_000_000
        );
      
        // Transfer via program
        await program.methods
          .transferTokens(new anchor.BN(500_000))
          .accounts({
            source: sourceAccount,
            destination: destAccount,
            authority: user.publicKey,
            tokenProgram: TOKEN_PROGRAM_ID,
          })
          .signers([user])
          .rpc();
      
        // Verify balances
        const sourceData = await getAccount(provider.connection, sourceAccount);
        const destData = await getAccount(provider.connection, destAccount);
      
        expect(sourceData.amount).to.equal(500_000n);
        expect(destData.amount).to.equal(500_000n);
      });
      ```
      
      **Native Rust with Mollusk:**
      See the [Testing CPIs](./testing-frameworks.md#testing-cpis) section in Testing Frameworks for a complete token transfer example.
      
      ### Testing Associated Token Accounts
      
      **Create ATA:**
      ```typescript
      import { getAssociatedTokenAddress } from "@solana/spl-token";
      
      it("creates associated token account", async () => {
        const ata = await getAssociatedTokenAddress(
          mint,
          user.publicKey
        );
      
        await program.methods
          .createAta()
          .accounts({
            ata: ata,
            mint: mint,
            owner: user.publicKey,
            payer: wallet.publicKey,
            tokenProgram: TOKEN_PROGRAM_ID,
            associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
            systemProgram: SystemProgram.programId,
          })
          .rpc();
      
        const account = await getAccount(provider.connection, ata);
        expect(account.owner.toString()).to.equal(user.publicKey.toString());
        expect(account.mint.toString()).to.equal(mint.toString());
      });
      ```
      
      ### Testing Account Validation
      
      **Validate account owner:**
      ```rust
      #[test]
      fn test_wrong_owner_fails() {
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
      
          let account = Pubkey::new_unique();
          let wrong_owner = Pubkey::new_unique();
      
          let accounts = vec![
              (account, Account {
                  lamports: 1_000_000,
                  data: vec![0; 100],
                  owner: wrong_owner,  // Wrong owner!
                  executable: false,
                  rent_epoch: 0,
              }),
          ];
      
          let instruction = /* create instruction */;
      
          let checks = vec![
              Check::instruction_err(InstructionError::InvalidAccountOwner),
          ];
      
          mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);
      }
      ```
      
      **Validate signer:**
      ```rust
      #[test]
      fn test_missing_signer_fails() {
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
      
          let user = Pubkey::new_unique();
      
          let instruction = Instruction {
              program_id,
              accounts: vec![
                  AccountMeta::new(user, false),  // Should be signer!
              ],
              data: vec![],
          };
      
          let accounts = vec![
              (user, system_account(1_000_000)),
          ];
      
          let checks = vec![
              Check::instruction_err(InstructionError::MissingRequiredSignature),
          ];
      
          mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);
      }
      ```
      
      ### Testing Rent Exemption
      
      ```rust
      #[test]
      fn test_account_is_rent_exempt() {
          let mollusk = Mollusk::new(&program_id, "target/deploy/my_program");
      
          let account = Pubkey::new_unique();
          let data_len = 100;
          let rent = mollusk.sysvars.rent;
          let rent_exempt_lamports = rent.minimum_balance(data_len);
      
          let accounts = vec![
              (account, Account {
                  lamports: rent_exempt_lamports,
                  data: vec![0; data_len],
                  owner: program_id,
                  executable: false,
                  rent_epoch: 0,
              }),
          ];
      
          let instruction = /* create instruction */;
      
          let checks = vec![
              Check::success(),
              Check::account(&account)
                  .rent_exempt()
                  .build(),
          ];
      
          mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);
      }
      ```
      
      ---
      
      ## Additional Resources
      
      ### Documentation
      
      - **Mollusk GitHub**: https://github.com/anza-xyz/mollusk
      - **Mollusk Examples**: https://github.com/anza-xyz/mollusk/tree/main/harness/tests
      - **Mollusk API Docs**: https://docs.rs/mollusk-svm/latest/mollusk_svm/
      - **Anchor Testing Guide**: https://www.anchor-lang.com/docs/testing
      - **LiteSVM**: https://github.com/amilz/litesvm
      - **Solana Testing Docs**: https://solana.com/docs/programs/testing
      
      ### Key Takeaways
      
      1. **Use Mollusk for fast, focused tests** - It's the recommended approach for both Anchor and native Rust programs
      2. **Test early and often** - Catching bugs before deployment saves time and money
      3. **Test error conditions** - Don't just test happy paths
      4. **Monitor compute units** - Use benchmarking to catch performance regressions
      5. **Organize tests logically** - Group by instruction, use helper modules
      6. **Build before testing** - Always run `cargo build-sbf` or `anchor build` before tests
      7. **Use validation checks** - Leverage the `Check` API for comprehensive validation
      8. **Test with realistic data** - Use proper rent-exempt balances and realistic account states
      
      ### Quick Reference Commands
      
      ```bash
      # Native Rust
      cargo build-sbf                    # Build program
      cargo test                         # Run tests
      cargo test -- --nocapture         # Run tests with output
      cargo test test_name              # Run specific test
      cargo bench                       # Run compute unit benchmarks
      
      # Anchor
      anchor build                      # Build program
      anchor test                       # Build, deploy, and test
      anchor test --skip-build          # Test without rebuilding
      anchor test -- --nocapture        # Test with logs
      anchor test -- --test test_name   # Run specific test
      ```
      
      ---
      
      ## Next Steps
      
      - For the testing strategy overview and pyramid structure, see **[Testing Overview](./testing-overview.md)**
      - For framework-specific implementation details, see **[Testing Frameworks](./testing-frameworks.md)**
      
    • tokens-2022.md 5.2 KB
      # SPL Token-2022 (Token Extensions Program)
      
      Token Extensions Program (Token-2022) guide covering extension types, setup for both Anchor and Native Rust, and practical examples including transfer hooks. Includes extension configuration, space calculation, and initialization patterns.
      
      **For related topics, see:**
      - **[tokens-overview.md](tokens-overview.md)** - Token fundamentals and account structures
      - **[tokens-operations.md](tokens-operations.md)** - Create, mint, transfer, burn, close operations
      - **[tokens-validation.md](tokens-validation.md)** - Account validation patterns
      - **[tokens-patterns.md](tokens-patterns.md)** - Common patterns and security
      
      ## Table of Contents
      
      1. [What are Token Extensions?](#what-are-token-extensions)
      2. [Available Extensions](#available-extensions)
      3. [Using Token-2022 in Anchor](#using-token-2022-in-anchor)
      4. [Using Token-2022 in Native Rust](#using-token-2022-in-native-rust)
      5. [Transfer Hook Extension Example](#transfer-hook-extension-example-anchor)
      
      ---
      
      ## What are Token Extensions?
      
      The Token Extensions Program (Token-2022) provides additional features through extensions. Extensions are optional functionality that can be added to a token mint or token account.
      
      **Key Points:**
      - Extensions must be enabled during account creation
      - Cannot add extensions after creation
      - Some extensions are incompatible with each other
      - Extensions add state to the `tlv_data` field
      
      ---
      
      ## Available Extensions
      
      ```rust
      pub enum ExtensionType {
          TransferFeeConfig,           // Transfer fees
          TransferFeeAmount,           // Withheld fees
          MintCloseAuthority,          // Close mint accounts
          ConfidentialTransferMint,    // Confidential transfers
          DefaultAccountState,         // Default state for new accounts
          ImmutableOwner,              // Cannot change owner
          MemoTransfer,                // Require memos
          NonTransferable,             // Cannot transfer tokens
          InterestBearingConfig,       // Tokens accrue interest
          PermanentDelegate,           // Permanent delegate authority
          TransferHook,                // Custom transfer logic
          MetadataPointer,             // Point to metadata
          TokenMetadata,               // On-chain metadata
          GroupPointer,                // Token groups
          TokenGroup,                  // Group config
          GroupMemberPointer,          // Group membership
          TokenGroupMember,            // Member config
          // ... and more
      }
      ```
      
      ---
      
      ## Using Token-2022 in Anchor
      
      ```rust
      use anchor_spl::token_2022::{self, Token2022};
      use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
      
      #[derive(Accounts)]
      pub struct CreateToken2022Mint<'info> {
          #[account(
              init,
              payer = payer,
              mint::decimals = 9,
              mint::authority = mint_authority,
              mint::token_program = token_program,
          )]
          pub mint: InterfaceAccount<'info, Mint>,
      
          /// CHECK: Mint authority
          pub mint_authority: UncheckedAccount<'info>,
      
          #[account(mut)]
          pub payer: Signer<'info>,
      
          pub token_program: Program<'info, Token2022>,
          pub system_program: Program<'info, System>,
      }
      ```
      
      **Note:** The `anchor-spl` crate includes the `token_2022_extensions` module for working with extensions, but not all extension instructions are fully implemented yet. You may need to manually implement CPI calls for some extensions.
      
      ---
      
      ## Using Token-2022 in Native Rust
      
      ```rust
      use spl_token_2022::{
          extension::ExtensionType,
          instruction::initialize_mint2,
      };
      
      pub fn create_token_2022_mint(
          payer: &AccountInfo,
          mint: &AccountInfo,
          mint_authority: &Pubkey,
          decimals: u8,
          extensions: &[ExtensionType],
      ) -> ProgramResult {
          // Calculate space needed for extensions
          let mut space = 82; // Base mint size
          for extension in extensions {
              space += extension.get_account_len();
          }
      
          // Create account with proper size
          // ... (similar to regular mint creation)
      
          // Initialize extensions
          // Each extension has its own initialization instruction
      
          // Finally initialize mint
          invoke(
              &initialize_mint2(
                  &spl_token_2022::ID,
                  mint.key,
                  mint_authority,
                  None,
                  decimals,
              )?,
              &[mint.clone()],
          )?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Transfer Hook Extension Example (Anchor)
      
      ```rust
      use anchor_lang::prelude::*;
      use anchor_spl::token_interface::{TokenAccount, TokenInterface};
      
      #[program]
      pub mod transfer_hook {
          use super::*;
      
          #[interface(spl_transfer_hook_interface::execute)]
          pub fn execute_transfer_hook(
              ctx: Context<TransferHook>,
              amount: u64,
          ) -> Result<()> {
              msg!("Transfer hook called! Amount: {}", amount);
              // Custom transfer logic here
              Ok(())
          }
      }
      
      #[derive(Accounts)]
      pub struct TransferHook<'info> {
          pub source: InterfaceAccount<'info, TokenAccount>,
          pub destination: InterfaceAccount<'info, TokenAccount>,
          /// CHECK: authority
          pub authority: UncheckedAccount<'info>,
      }
      ```
      
      ---
      
      ## Next Steps
      
      - **Common Patterns**: See [tokens-patterns.md](tokens-patterns.md) for escrow, staking, NFT creation patterns
      - **Security**: See [tokens-patterns.md](tokens-patterns.md) for comprehensive security best practices
      
    • tokens-operations.md 22.6 KB
      # SPL Token Program - Operations
      
      Complete guide to SPL Token operations including creating mints, minting tokens, transferring (with transfer_checked), burning, and closing token accounts. Shows both Anchor and Native Rust implementations side-by-side.
      
      **For related topics, see:**
      - **[tokens-overview.md](tokens-overview.md)** - Token fundamentals and account structures
      - **[tokens-validation.md](tokens-validation.md)** - Account validation patterns
      - **[tokens-2022.md](tokens-2022.md)** - Token Extensions Program features
      - **[tokens-patterns.md](tokens-patterns.md)** - Common patterns and security
      
      ## Table of Contents
      
      1. [Creating Tokens](#creating-tokens)
      2. [Minting Tokens](#minting-tokens)
      3. [Transferring Tokens](#transferring-tokens)
      4. [Burning Tokens](#burning-tokens)
      5. [Closing Token Accounts](#closing-token-accounts)
      
      ---
      
      ## Creating Tokens
      
      ### Initialize a New Mint
      
      #### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{Mint, TokenInterface};
      
      #[derive(Accounts)]
      pub struct CreateMint<'info> {
          #[account(
              init,
              payer = payer,
              mint::decimals = 9,
              mint::authority = mint_authority,
              mint::freeze_authority = freeze_authority,
              mint::token_program = token_program,
          )]
          pub mint: InterfaceAccount<'info, Mint>,
      
          /// CHECK: Can be any account
          pub mint_authority: UncheckedAccount<'info>,
      
          /// CHECK: Can be any account (optional)
          pub freeze_authority: UncheckedAccount<'info>,
      
          #[account(mut)]
          pub payer: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
          pub system_program: Program<'info, System>,
      }
      
      pub fn create_mint(ctx: Context<CreateMint>) -> Result<()> {
          // Mint is automatically created and initialized by Anchor constraints
          msg!("Mint created: {}", ctx.accounts.mint.key());
          Ok(())
      }
      ```
      
      **Key Anchor Constraints:**
      - `init` - Creates and initializes the account
      - `mint::decimals` - Number of decimal places
      - `mint::authority` - Who can mint tokens
      - `mint::freeze_authority` - Who can freeze token accounts (optional)
      - `mint::token_program` - Which token program to use
      
      #### Using Native Rust
      
      ```rust
      use spl_token::instruction::initialize_mint;
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          program::invoke,
          rent::Rent,
          system_instruction,
          sysvar::Sysvar,
      };
      
      pub fn create_mint(
          payer: &AccountInfo,
          mint_account: &AccountInfo,
          mint_authority: &Pubkey,
          freeze_authority: Option<&Pubkey>,
          decimals: u8,
          system_program: &AccountInfo,
          token_program: &AccountInfo,
          rent_sysvar: &AccountInfo,
      ) -> ProgramResult {
          // Mint account size
          let mint_size = 82;
      
          // Calculate rent
          let rent = Rent::get()?;
          let rent_lamports = rent.minimum_balance(mint_size);
      
          // Create mint account via System Program
          invoke(
              &system_instruction::create_account(
                  payer.key,
                  mint_account.key,
                  rent_lamports,
                  mint_size as u64,
                  &spl_token::ID,
              ),
              &[payer.clone(), mint_account.clone(), system_program.clone()],
          )?;
      
          // Initialize mint
          invoke(
              &initialize_mint(
                  token_program.key,
                  mint_account.key,
                  mint_authority,
                  freeze_authority,
                  decimals,
              )?,
              &[
                  mint_account.clone(),
                  rent_sysvar.clone(),
                  token_program.clone(),
              ],
          )?;
      
          Ok(())
      }
      ```
      
      ### Initialize a Token Account (Non-ATA)
      
      #### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
      
      #[derive(Accounts)]
      pub struct CreateTokenAccount<'info> {
          #[account(
              init,
              payer = payer,
              token::mint = mint,
              token::authority = owner,
              token::token_program = token_program,
          )]
          pub token_account: InterfaceAccount<'info, TokenAccount>,
      
          pub mint: InterfaceAccount<'info, Mint>,
      
          /// CHECK: Can be any account
          pub owner: UncheckedAccount<'info>,
      
          #[account(mut)]
          pub payer: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
          pub system_program: Program<'info, System>,
      }
      
      pub fn create_token_account(ctx: Context<CreateTokenAccount>) -> Result<()> {
          // Token account is automatically created and initialized
          Ok(())
      }
      ```
      
      #### Using Native Rust
      
      ```rust
      use spl_token::instruction::initialize_account3;
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          program::invoke,
          rent::Rent,
          system_instruction,
          sysvar::Sysvar,
      };
      
      pub fn create_token_account(
          payer: &AccountInfo,
          token_account: &AccountInfo,
          mint: &AccountInfo,
          owner: &Pubkey,
          system_program: &AccountInfo,
          token_program: &AccountInfo,
      ) -> ProgramResult {
          // Token account size
          let token_account_size = 165;
      
          // Calculate rent
          let rent = Rent::get()?;
          let rent_lamports = rent.minimum_balance(token_account_size);
      
          // Create token account
          invoke(
              &system_instruction::create_account(
                  payer.key,
                  token_account.key,
                  rent_lamports,
                  token_account_size as u64,
                  &spl_token::ID,
              ),
              &[payer.clone(), token_account.clone(), system_program.clone()],
          )?;
      
          // Initialize token account
          invoke(
              &initialize_account3(
                  token_program.key,
                  token_account.key,
                  mint.key,
                  owner,
              )?,
              &[token_account.clone(), mint.clone(), token_program.clone()],
          )?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Minting Tokens
      
      ### Basic Minting (User Authority)
      
      #### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{self, Mint, MintTo, TokenAccount, TokenInterface};
      
      #[derive(Accounts)]
      pub struct MintTokens<'info> {
          #[account(mut)]
          pub mint: InterfaceAccount<'info, Mint>,
      
          #[account(mut)]
          pub token_account: InterfaceAccount<'info, TokenAccount>,
      
          pub mint_authority: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
      }
      
      pub fn mint_tokens(ctx: Context<MintTokens>, amount: u64) -> Result<()> {
          let cpi_accounts = MintTo {
              mint: ctx.accounts.mint.to_account_info(),
              to: ctx.accounts.token_account.to_account_info(),
              authority: ctx.accounts.mint_authority.to_account_info(),
          };
      
          let cpi_program = ctx.accounts.token_program.to_account_info();
          let cpi_context = CpiContext::new(cpi_program, cpi_accounts);
      
          token_interface::mint_to(cpi_context, amount)?;
          Ok(())
      }
      ```
      
      #### Using Native Rust
      
      ```rust
      use spl_token::instruction::mint_to;
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          program::invoke,
          program_error::ProgramError,
      };
      
      pub fn mint_tokens(
          mint: &AccountInfo,
          destination: &AccountInfo,
          mint_authority: &AccountInfo,
          amount: u64,
          token_program: &AccountInfo,
      ) -> ProgramResult {
          // Mint authority must be a signer
          if !mint_authority.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          invoke(
              &mint_to(
                  token_program.key,
                  mint.key,
                  destination.key,
                  mint_authority.key,
                  &[],  // No multisig signers
                  amount,
              )?,
              &[
                  mint.clone(),
                  destination.clone(),
                  mint_authority.clone(),
                  token_program.clone(),
              ],
          )?;
      
          Ok(())
      }
      ```
      
      ### Minting with PDA Authority
      
      #### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{self, Mint, MintTo, TokenAccount, TokenInterface};
      
      #[derive(Accounts)]
      pub struct MintWithPDA<'info> {
          #[account(
              mut,
              mint::authority = mint_authority,
          )]
          pub mint: InterfaceAccount<'info, Mint>,
      
          #[account(mut)]
          pub token_account: InterfaceAccount<'info, TokenAccount>,
      
          #[account(
              seeds = [b"mint-authority"],
              bump,
          )]
          /// CHECK: PDA signer
          pub mint_authority: UncheckedAccount<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
      }
      
      pub fn mint_with_pda(ctx: Context<MintWithPDA>, amount: u64) -> Result<()> {
          let seeds = &[
              b"mint-authority",
              &[ctx.bumps.mint_authority],
          ];
          let signer_seeds = &[&seeds[..]];
      
          let cpi_accounts = MintTo {
              mint: ctx.accounts.mint.to_account_info(),
              to: ctx.accounts.token_account.to_account_info(),
              authority: ctx.accounts.mint_authority.to_account_info(),
          };
      
          let cpi_context = CpiContext::new(
              ctx.accounts.token_program.to_account_info(),
              cpi_accounts
          ).with_signer(signer_seeds);
      
          token_interface::mint_to(cpi_context, amount)?;
          Ok(())
      }
      ```
      
      #### Using Native Rust
      
      ```rust
      use spl_token::instruction::mint_to;
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          program::invoke_signed,
          program_error::ProgramError,
          pubkey::Pubkey,
      };
      
      pub fn mint_tokens_from_pda(
          program_id: &Pubkey,
          mint: &AccountInfo,
          destination: &AccountInfo,
          mint_authority_pda: &AccountInfo,
          token_program: &AccountInfo,
          amount: u64,
          pda_seeds: &[&[u8]],
          bump: u8,
      ) -> ProgramResult {
          // Validate PDA
          let (expected_pda, _) = Pubkey::find_program_address(pda_seeds, program_id);
          if expected_pda != *mint_authority_pda.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          // Prepare signer seeds
          let mut full_seeds = pda_seeds.to_vec();
          full_seeds.push(&[bump]);
          let signer_seeds: &[&[&[u8]]] = &[&full_seeds];
      
          invoke_signed(
              &mint_to(
                  token_program.key,
                  mint.key,
                  destination.key,
                  mint_authority_pda.key,
                  &[],
                  amount,
              )?,
              &[
                  mint.clone(),
                  destination.clone(),
                  mint_authority_pda.clone(),
                  token_program.clone(),
              ],
              signer_seeds,
          )?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Transferring Tokens
      
      ### Basic Transfer
      
      #### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{self, TokenAccount, TokenInterface, Transfer};
      
      #[derive(Accounts)]
      pub struct TransferTokens<'info> {
          #[account(mut)]
          pub from: InterfaceAccount<'info, TokenAccount>,
      
          #[account(mut)]
          pub to: InterfaceAccount<'info, TokenAccount>,
      
          pub authority: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
      }
      
      pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {
          let cpi_accounts = Transfer {
              from: ctx.accounts.from.to_account_info(),
              to: ctx.accounts.to.to_account_info(),
              authority: ctx.accounts.authority.to_account_info(),
          };
      
          let cpi_context = CpiContext::new(
              ctx.accounts.token_program.to_account_info(),
              cpi_accounts
          );
      
          token_interface::transfer(cpi_context, amount)?;
          Ok(())
      }
      ```
      
      #### Using Native Rust
      
      ```rust
      use spl_token::instruction::transfer;
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          program::invoke,
          program_error::ProgramError,
      };
      
      pub fn transfer_tokens(
          source: &AccountInfo,
          destination: &AccountInfo,
          authority: &AccountInfo,
          amount: u64,
          token_program: &AccountInfo,
      ) -> ProgramResult {
          // Authority must be a signer
          if !authority.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          invoke(
              &transfer(
                  token_program.key,
                  source.key,
                  destination.key,
                  authority.key,
                  &[],  // No multisig signers
                  amount,
              )?,
              &[
                  source.clone(),
                  destination.clone(),
                  authority.clone(),
                  token_program.clone(),
              ],
          )?;
      
          Ok(())
      }
      ```
      
      ### Transfer with Checks (Recommended)
      
      #### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{self, Mint, TokenAccount, TokenInterface, TransferChecked};
      
      #[derive(Accounts)]
      pub struct TransferTokensChecked<'info> {
          #[account(mut)]
          pub from: InterfaceAccount<'info, TokenAccount>,
      
          #[account(mut)]
          pub to: InterfaceAccount<'info, TokenAccount>,
      
          pub mint: InterfaceAccount<'info, Mint>,
      
          pub authority: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
      }
      
      pub fn transfer_tokens_checked(
          ctx: Context<TransferTokensChecked>,
          amount: u64
      ) -> Result<()> {
          token_interface::transfer_checked(
              CpiContext::new(
                  ctx.accounts.token_program.to_account_info(),
                  TransferChecked {
                      from: ctx.accounts.from.to_account_info(),
                      mint: ctx.accounts.mint.to_account_info(),
                      to: ctx.accounts.to.to_account_info(),
                      authority: ctx.accounts.authority.to_account_info(),
                  },
              ),
              amount,
              ctx.accounts.mint.decimals,
          )?;
          Ok(())
      }
      ```
      
      #### Using Native Rust
      
      ```rust
      use spl_token::instruction::transfer_checked;
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          program::invoke,
          program_error::ProgramError,
      };
      
      pub fn transfer_tokens_checked(
          source: &AccountInfo,
          mint: &AccountInfo,
          destination: &AccountInfo,
          authority: &AccountInfo,
          amount: u64,
          decimals: u8,
          token_program: &AccountInfo,
      ) -> ProgramResult {
          if !authority.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          invoke(
              &transfer_checked(
                  token_program.key,
                  source.key,
                  mint.key,
                  destination.key,
                  authority.key,
                  &[],
                  amount,
                  decimals,
              )?,
              &[
                  source.clone(),
                  mint.clone(),
                  destination.clone(),
                  authority.clone(),
                  token_program.clone(),
              ],
          )?;
      
          Ok(())
      }
      ```
      
      ### Transfer with PDA Signer
      
      #### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{self, TokenAccount, TokenInterface, Transfer};
      
      #[derive(Accounts)]
      pub struct TransferWithPDA<'info> {
          #[account(
              mut,
              token::authority = authority,
          )]
          pub from: InterfaceAccount<'info, TokenAccount>,
      
          #[account(mut)]
          pub to: InterfaceAccount<'info, TokenAccount>,
      
          #[account(
              seeds = [b"authority"],
              bump,
          )]
          /// CHECK: PDA signer
          pub authority: UncheckedAccount<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
      }
      
      pub fn transfer_with_pda(ctx: Context<TransferWithPDA>, amount: u64) -> Result<()> {
          let seeds = &[
              b"authority",
              &[ctx.bumps.authority],
          ];
          let signer_seeds = &[&seeds[..]];
      
          let cpi_accounts = Transfer {
              from: ctx.accounts.from.to_account_info(),
              to: ctx.accounts.to.to_account_info(),
              authority: ctx.accounts.authority.to_account_info(),
          };
      
          let cpi_context = CpiContext::new(
              ctx.accounts.token_program.to_account_info(),
              cpi_accounts
          ).with_signer(signer_seeds);
      
          token_interface::transfer(cpi_context, amount)?;
          Ok(())
      }
      ```
      
      #### Using Native Rust
      
      ```rust
      use spl_token::instruction::transfer;
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          program::invoke_signed,
          program_error::ProgramError,
          pubkey::Pubkey,
      };
      
      pub fn transfer_tokens_from_pda(
          program_id: &Pubkey,
          source: &AccountInfo,
          destination: &AccountInfo,
          authority_pda: &AccountInfo,
          token_program: &AccountInfo,
          amount: u64,
          pda_seeds: &[&[u8]],
          bump: u8,
      ) -> ProgramResult {
          let (expected_pda, _) = Pubkey::find_program_address(pda_seeds, program_id);
          if expected_pda != *authority_pda.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          let mut full_seeds = pda_seeds.to_vec();
          full_seeds.push(&[bump]);
          let signer_seeds: &[&[&[u8]]] = &[&full_seeds];
      
          invoke_signed(
              &transfer(
                  token_program.key,
                  source.key,
                  destination.key,
                  authority_pda.key,
                  &[],
                  amount,
              )?,
              &[
                  source.clone(),
                  destination.clone(),
                  authority_pda.clone(),
                  token_program.clone(),
              ],
              signer_seeds,
          )?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Burning Tokens
      
      ### Basic Burn
      
      #### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{self, Burn, Mint, TokenAccount, TokenInterface};
      
      #[derive(Accounts)]
      pub struct BurnTokens<'info> {
          #[account(mut)]
          pub mint: InterfaceAccount<'info, Mint>,
      
          #[account(mut)]
          pub token_account: InterfaceAccount<'info, TokenAccount>,
      
          pub authority: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
      }
      
      pub fn burn_tokens(ctx: Context<BurnTokens>, amount: u64) -> Result<()> {
          let cpi_accounts = Burn {
              mint: ctx.accounts.mint.to_account_info(),
              from: ctx.accounts.token_account.to_account_info(),
              authority: ctx.accounts.authority.to_account_info(),
          };
      
          let cpi_context = CpiContext::new(
              ctx.accounts.token_program.to_account_info(),
              cpi_accounts
          );
      
          token_interface::burn(cpi_context, amount)?;
          Ok(())
      }
      ```
      
      #### Using Native Rust
      
      ```rust
      use spl_token::instruction::burn;
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          program::invoke,
          program_error::ProgramError,
      };
      
      pub fn burn_tokens(
          token_account: &AccountInfo,
          mint: &AccountInfo,
          authority: &AccountInfo,
          amount: u64,
          token_program: &AccountInfo,
      ) -> ProgramResult {
          if !authority.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          invoke(
              &burn(
                  token_program.key,
                  token_account.key,
                  mint.key,
                  authority.key,
                  &[],
                  amount,
              )?,
              &[
                  token_account.clone(),
                  mint.clone(),
                  authority.clone(),
                  token_program.clone(),
              ],
          )?;
      
          Ok(())
      }
      ```
      
      ### Burn with PDA Authority
      
      #### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{self, Burn, Mint, TokenAccount, TokenInterface};
      
      #[derive(Accounts)]
      pub struct BurnWithPDA<'info> {
          #[account(mut)]
          pub mint: InterfaceAccount<'info, Mint>,
      
          #[account(
              mut,
              token::authority = authority,
          )]
          pub token_account: InterfaceAccount<'info, TokenAccount>,
      
          #[account(
              seeds = [b"burn-authority"],
              bump,
          )]
          /// CHECK: PDA signer
          pub authority: UncheckedAccount<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
      }
      
      pub fn burn_with_pda(ctx: Context<BurnWithPDA>, amount: u64) -> Result<()> {
          let seeds = &[
              b"burn-authority",
              &[ctx.bumps.authority],
          ];
          let signer_seeds = &[&seeds[..]];
      
          let cpi_accounts = Burn {
              mint: ctx.accounts.mint.to_account_info(),
              from: ctx.accounts.token_account.to_account_info(),
              authority: ctx.accounts.authority.to_account_info(),
          };
      
          let cpi_context = CpiContext::new(
              ctx.accounts.token_program.to_account_info(),
              cpi_accounts
          ).with_signer(signer_seeds);
      
          token_interface::burn(cpi_context, amount)?;
          Ok(())
      }
      ```
      
      #### Using Native Rust
      
      ```rust
      pub fn burn_tokens_from_pda(
          program_id: &Pubkey,
          token_account: &AccountInfo,
          mint: &AccountInfo,
          authority_pda: &AccountInfo,
          token_program: &AccountInfo,
          amount: u64,
          pda_seeds: &[&[u8]],
          bump: u8,
      ) -> ProgramResult {
          let (expected_pda, _) = Pubkey::find_program_address(pda_seeds, program_id);
          if expected_pda != *authority_pda.key {
              return Err(ProgramError::InvalidSeeds);
          }
      
          let mut full_seeds = pda_seeds.to_vec();
          full_seeds.push(&[bump]);
          let signer_seeds: &[&[&[u8]]] = &[&full_seeds];
      
          invoke_signed(
              &burn(
                  token_program.key,
                  token_account.key,
                  mint.key,
                  authority_pda.key,
                  &[],
                  amount,
              )?,
              &[
                  token_account.clone(),
                  mint.clone(),
                  authority_pda.clone(),
                  token_program.clone(),
              ],
              signer_seeds,
          )?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Closing Token Accounts
      
      ### Close Token Account
      
      #### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{self, CloseAccount, TokenAccount, TokenInterface};
      
      #[derive(Accounts)]
      pub struct CloseTokenAccount<'info> {
          #[account(mut)]
          pub token_account: InterfaceAccount<'info, TokenAccount>,
      
          #[account(mut)]
          pub destination: SystemAccount<'info>,
      
          pub authority: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
      }
      
      pub fn close_token_account(ctx: Context<CloseTokenAccount>) -> Result<()> {
          let cpi_accounts = CloseAccount {
              account: ctx.accounts.token_account.to_account_info(),
              destination: ctx.accounts.destination.to_account_info(),
              authority: ctx.accounts.authority.to_account_info(),
          };
      
          let cpi_context = CpiContext::new(
              ctx.accounts.token_program.to_account_info(),
              cpi_accounts
          );
      
          token_interface::close_account(cpi_context)?;
          Ok(())
      }
      ```
      
      **Using Anchor Constraints (Simplified):**
      
      ```rust
      #[derive(Accounts)]
      pub struct CloseTokenAccount<'info> {
          #[account(
              mut,
              close = destination,
              token::authority = authority,
          )]
          pub token_account: InterfaceAccount<'info, TokenAccount>,
      
          #[account(mut)]
          pub destination: SystemAccount<'info>,
      
          pub authority: Signer<'info>,
      }
      
      pub fn close_token_account(ctx: Context<CloseTokenAccount>) -> Result<()> {
          // Account is automatically closed by Anchor constraints
          Ok(())
      }
      ```
      
      #### Using Native Rust
      
      ```rust
      use spl_token::instruction::close_account;
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          program::invoke,
          program_error::ProgramError,
      };
      
      pub fn close_token_account(
          token_account: &AccountInfo,
          destination: &AccountInfo,
          authority: &AccountInfo,
          token_program: &AccountInfo,
      ) -> ProgramResult {
          if !authority.is_signer {
              return Err(ProgramError::MissingRequiredSignature);
          }
      
          invoke(
              &close_account(
                  token_program.key,
                  token_account.key,
                  destination.key,
                  authority.key,
                  &[],
              )?,
              &[
                  token_account.clone(),
                  destination.clone(),
                  authority.clone(),
                  token_program.clone(),
              ],
          )?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Next Steps
      
      - **Validation**: See [tokens-validation.md](tokens-validation.md) for account validation patterns
      - **Token-2022**: See [tokens-2022.md](tokens-2022.md) for Token Extensions Program features
      - **Patterns & Security**: See [tokens-patterns.md](tokens-patterns.md) for common patterns and security best practices
      
    • tokens-overview.md 8.2 KB
      # SPL Token Program - Overview and Fundamentals
      
      Overview of SPL Token Program fundamentals including program types, account structures (Mint and Token accounts), and Associated Token Accounts (ATAs) with derivation and creation patterns.
      
      **For additional token topics, see:**
      - **[tokens-operations.md](tokens-operations.md)** - Create, mint, transfer, burn, close operations
      - **[tokens-validation.md](tokens-validation.md)** - Account validation patterns
      - **[tokens-2022.md](tokens-2022.md)** - Token Extensions Program features
      - **[tokens-patterns.md](tokens-patterns.md)** - Common patterns and security
      
      ## Table of Contents
      
      1. [Token Program Overview](#token-program-overview)
      2. [Token Account Structures](#token-account-structures)
      3. [Associated Token Accounts](#associated-token-accounts)
      
      ---
      
      ## Token Program Overview
      
      ### SPL Token vs Token-2022
      
      **SPL Token (Original):**
      - Program ID: `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`
      - Production-ready, stable, widely adopted
      - No new features planned
      - Use for standard fungible tokens
      
      **Token-2022 (Token Extensions Program):**
      - Program ID: `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`
      - Backwards-compatible with SPL Token
      - Supports extensions (transfer fees, confidential transfers, metadata pointers, etc.)
      - Use for advanced token features
      
      ### Key Concepts
      
      ```
      ┌─────────────────────────────────────────┐
      │ Mint Account                             │
      ├─────────────────────────────────────────┤
      │ - Defines a token type                  │
      │ - Controls supply                       │
      │ - Has mint authority (can create tokens)│
      │ - Has freeze authority (can freeze accts)│
      └─────────────────────────────────────────┘
                 │
                 │ Creates
                 ▼
      ┌─────────────────────────────────────────┐
      │ Token Account                            │
      ├─────────────────────────────────────────┤
      │ - Holds token balance                   │
      │ - Owned by a wallet or program          │
      │ - Associated with specific Mint         │
      │ - Can be frozen/delegated               │
      └─────────────────────────────────────────┘
      ```
      
      ### Required Dependencies
      
      **For Anchor:**
      ```toml
      [dependencies]
      anchor-lang = "0.32.1"
      anchor-spl = "0.32.1"
      
      [features]
      idl-build = [
          "anchor-lang/idl-build",
          "anchor-spl/idl-build",
      ]
      ```
      
      **For Native Rust:**
      ```toml
      [dependencies]
      spl-token = "6.0"
      spl-associated-token-account = "6.0"
      solana-program = "2.1"
      ```
      
      ---
      
      ## Token Account Structures
      
      ### Mint Account
      
      **Size:** 82 bytes
      
      ```rust
      pub struct Mint {
          /// Optional authority to mint new tokens (Pubkey or None)
          pub mint_authority: COption<Pubkey>,       // 36 bytes
      
          /// Total supply of tokens
          pub supply: u64,                           // 8 bytes
      
          /// Number of decimals (0 for NFTs, typically 6-9 for fungible)
          pub decimals: u8,                          // 1 byte
      
          /// Is initialized?
          pub is_initialized: bool,                  // 1 byte
      
          /// Optional authority to freeze token accounts
          pub freeze_authority: COption<Pubkey>,     // 36 bytes
      }
      ```
      
      **COption Format:**
      ```rust
      pub enum COption<T> {
          None,      // Represented as [0, 0, 0, 0, ...]
          Some(T),   // Represented as [1, followed by T bytes]
      }
      ```
      
      ### Token Account
      
      **Size:** 165 bytes
      
      ```rust
      pub struct Account {
          /// The mint associated with this account
          pub mint: Pubkey,                    // 32 bytes
      
          /// The owner of this account
          pub owner: Pubkey,                   // 32 bytes
      
          /// The amount of tokens this account holds
          pub amount: u64,                     // 8 bytes
      
          /// If `delegate` is `Some` then `delegated_amount` represents
          /// the amount authorized by the delegate
          pub delegate: COption<Pubkey>,       // 36 bytes
      
          /// The account's state
          pub state: AccountState,             // 1 byte
      
          /// If is_native.is_some, this is a native token, and the value logs the
          /// rent-exempt reserve
          pub is_native: COption<u64>,         // 12 bytes
      
          /// The amount delegated
          pub delegated_amount: u64,           // 8 bytes
      
          /// Optional authority to close the account
          pub close_authority: COption<Pubkey>, // 36 bytes
      }
      
      pub enum AccountState {
          Uninitialized,
          Initialized,
          Frozen,
      }
      ```
      
      ---
      
      ## Associated Token Accounts
      
      ### What are ATAs?
      
      **Associated Token Accounts (ATAs)** are PDAs that map a wallet address to a token account for a specific mint.
      
      **Derivation:**
      ```rust
      ATA = PDA(
          seeds: [wallet_address, TOKEN_PROGRAM_ID, mint_address],
          program: ASSOCIATED_TOKEN_PROGRAM_ID
      )
      ```
      
      **Benefits:**
      - **Deterministic**: Same wallet + mint always produces same ATA
      - **Discoverable**: Easy to find a user's token accounts
      - **Standard**: All wallets use this convention
      
      **Constants:**
      ```rust
      // Token Program ID
      pub const TOKEN_PROGRAM_ID: Pubkey = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
      
      // Associated Token Program ID
      pub const ASSOCIATED_TOKEN_PROGRAM_ID: Pubkey = pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
      ```
      
      ### Finding ATA Address
      
      #### Using Anchor
      
      ```rust
      use anchor_spl::associated_token::get_associated_token_address;
      
      // In client code or tests
      let ata_address = get_associated_token_address(
          &wallet_address,
          &mint_address,
      );
      ```
      
      #### Using Native Rust
      
      ```rust
      use spl_associated_token_account::get_associated_token_address;
      
      // Derive ATA address
      let ata_address = get_associated_token_address(
          &wallet_address,
          &mint_address,
      );
      ```
      
      ### Creating Associated Token Accounts
      
      #### Using Anchor
      
      ```rust
      use anchor_spl::associated_token::AssociatedToken;
      use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
      
      #[derive(Accounts)]
      pub struct CreateTokenAccount<'info> {
          #[account(
              init,
              payer = payer,
              associated_token::mint = mint,
              associated_token::authority = owner,
              associated_token::token_program = token_program,
          )]
          pub token_account: InterfaceAccount<'info, TokenAccount>,
      
          pub mint: InterfaceAccount<'info, Mint>,
      
          /// CHECK: Can be any account
          pub owner: UncheckedAccount<'info>,
      
          #[account(mut)]
          pub payer: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
          pub associated_token_program: Program<'info, AssociatedToken>,
          pub system_program: Program<'info, System>,
      }
      
      pub fn create_ata(ctx: Context<CreateTokenAccount>) -> Result<()> {
          // ATA is automatically created by Anchor constraints
          Ok(())
      }
      ```
      
      #### Using Native Rust
      
      ```rust
      use spl_associated_token_account::instruction::create_associated_token_account;
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          program::invoke,
      };
      
      pub fn create_ata(
          payer: &AccountInfo,
          wallet: &AccountInfo,
          mint: &AccountInfo,
          ata: &AccountInfo,
          system_program: &AccountInfo,
          token_program: &AccountInfo,
          associated_token_program: &AccountInfo,
      ) -> ProgramResult {
          invoke(
              &create_associated_token_account(
                  payer.key,
                  wallet.key,
                  mint.key,
                  token_program.key,
              ),
              &[
                  payer.clone(),
                  ata.clone(),
                  wallet.clone(),
                  mint.clone(),
                  system_program.clone(),
                  token_program.clone(),
                  associated_token_program.clone(),
              ],
          )?
      
      ;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Next Steps
      
      - **Token Operations**: See [tokens-operations.md](tokens-operations.md) for creating mints, minting, transferring, burning, and closing accounts
      - **Validation**: See [tokens-validation.md](tokens-validation.md) for account validation patterns
      - **Token-2022**: See [tokens-2022.md](tokens-2022.md) for Token Extensions Program features
      - **Patterns & Security**: See [tokens-patterns.md](tokens-patterns.md) for common patterns and security best practices
      
    • tokens-patterns.md 21.7 KB
      # SPL Token Program - Common Patterns and Security
      
      Common SPL Token patterns including escrow, staking, NFT creation, and account freezing. Comprehensive security considerations covering validation, authority checks, and defensive programming. Includes quick reference tables and security checklist.
      
      **For related topics, see:**
      - **[tokens-overview.md](tokens-overview.md)** - Token fundamentals and account structures
      - **[tokens-operations.md](tokens-operations.md)** - Create, mint, transfer, burn, close operations
      - **[tokens-validation.md](tokens-validation.md)** - Account validation patterns
      - **[tokens-2022.md](tokens-2022.md)** - Token Extensions Program features
      
      ## Table of Contents
      
      1. [Pattern 1: Token Escrow](#pattern-1-token-escrow)
      2. [Pattern 2: Token Staking](#pattern-2-token-staking)
      3. [Pattern 3: NFT Creation](#pattern-3-nft-creation)
      4. [Pattern 4: Freezing and Thawing Accounts](#pattern-4-freezing-and-thawing-accounts)
      5. [Security Considerations](#security-considerations)
      6. [Summary](#summary)
      
      ---
      
      ## Pattern 1: Token Escrow
      
      Program holds tokens temporarily on behalf of users.
      
      ### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{self, TokenAccount, TokenInterface, Transfer};
      
      #[derive(Accounts)]
      pub struct InitializeEscrow<'info> {
          #[account(
              init,
              payer = user,
              space = 8 + 32 + 8 + 1,
              seeds = [b"escrow", user.key().as_ref()],
              bump,
          )]
          pub escrow_state: Account<'info, EscrowState>,
      
          #[account(
              init,
              payer = user,
              token::mint = mint,
              token::authority = escrow_state,
              token::token_program = token_program,
          )]
          pub escrow_token_account: InterfaceAccount<'info, TokenAccount>,
      
          #[account(mut)]
          pub user_token_account: InterfaceAccount<'info, TokenAccount>,
      
          pub mint: InterfaceAccount<'info, Mint>,
      
          #[account(mut)]
          pub user: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
          pub system_program: Program<'info, System>,
      }
      
      #[account]
      pub struct EscrowState {
          pub user: Pubkey,
          pub amount: u64,
          pub bump: u8,
      }
      
      pub fn initialize_escrow(ctx: Context<InitializeEscrow>, amount: u64) -> Result<()> {
          // Transfer tokens to escrow
          token_interface::transfer(
              CpiContext::new(
                  ctx.accounts.token_program.to_account_info(),
                  Transfer {
                      from: ctx.accounts.user_token_account.to_account_info(),
                      to: ctx.accounts.escrow_token_account.to_account_info(),
                      authority: ctx.accounts.user.to_account_info(),
                  },
              ),
              amount,
          )?;
      
          // Save state
          ctx.accounts.escrow_state.user = ctx.accounts.user.key();
          ctx.accounts.escrow_state.amount = amount;
          ctx.accounts.escrow_state.bump = ctx.bumps.escrow_state;
      
          Ok(())
      }
      
      #[derive(Accounts)]
      pub struct ReleaseEscrow<'info> {
          #[account(
              mut,
              seeds = [b"escrow", escrow_state.user.as_ref()],
              bump = escrow_state.bump,
              has_one = user,
              close = user,
          )]
          pub escrow_state: Account<'info, EscrowState>,
      
          #[account(mut)]
          pub escrow_token_account: InterfaceAccount<'info, TokenAccount>,
      
          #[account(mut)]
          pub recipient_token_account: InterfaceAccount<'info, TokenAccount>,
      
          pub user: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
      }
      
      pub fn release_escrow(ctx: Context<ReleaseEscrow>) -> Result<()> {
          let seeds = &[
              b"escrow",
              ctx.accounts.user.key().as_ref(),
              &[ctx.accounts.escrow_state.bump],
          ];
          let signer_seeds = &[&seeds[..]];
      
          token_interface::transfer(
              CpiContext::new(
                  ctx.accounts.token_program.to_account_info(),
                  Transfer {
                      from: ctx.accounts.escrow_token_account.to_account_info(),
                      to: ctx.accounts.recipient_token_account.to_account_info(),
                      authority: ctx.accounts.escrow_state.to_account_info(),
                  },
              ).with_signer(signer_seeds),
              ctx.accounts.escrow_state.amount,
          )?;
      
          Ok(())
      }
      ```
      
      ### Using Native Rust
      
      ```rust
      use borsh::{BorshDeserialize, BorshSerialize};
      use spl_token::instruction::transfer;
      
      #[derive(BorshSerialize, BorshDeserialize)]
      pub struct EscrowState {
          pub user: Pubkey,
          pub amount: u64,
          pub bump: u8,
      }
      
      pub fn initialize_escrow(
          program_id: &Pubkey,
          user: &AccountInfo,
          user_token_account: &AccountInfo,
          escrow_token_account: &AccountInfo,
          escrow_state: &AccountInfo,
          amount: u64,
          token_program: &AccountInfo,
      ) -> ProgramResult {
          // Transfer tokens to escrow
          invoke(
              &transfer(
                  &spl_token::ID,
                  user_token_account.key,
                  escrow_token_account.key,
                  user.key,
                  &[],
                  amount,
              )?,
              &[user_token_account.clone(), escrow_token_account.clone(), user.clone()],
          )?;
      
          // Save escrow state
          let (pda, bump) = Pubkey::find_program_address(&[b"escrow", user.key.as_ref()], program_id);
          let escrow = EscrowState {
              user: *user.key,
              amount,
              bump,
          };
          escrow.serialize(&mut &mut escrow_state.data.borrow_mut()[..])?;
      
          Ok(())
      }
      
      pub fn release_escrow(
          program_id: &Pubkey,
          escrow_state: &AccountInfo,
          escrow_token_account: &AccountInfo,
          recipient_token_account: &AccountInfo,
          escrow_pda: &AccountInfo,
          amount: u64,
          bump: u8,
          user: &Pubkey,
      ) -> ProgramResult {
          let signer_seeds: &[&[&[u8]]] = &[&[b"escrow", user.as_ref(), &[bump]]];
      
          invoke_signed(
              &transfer(
                  &spl_token::ID,
                  escrow_token_account.key,
                  recipient_token_account.key,
                  escrow_pda.key,
                  &[],
                  amount,
              )?,
              &[escrow_token_account.clone(), recipient_token_account.clone(), escrow_pda.clone()],
              signer_seeds,
          )?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Pattern 2: Token Staking
      
      Users lock tokens to earn rewards.
      
      ### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{self, Mint, TokenAccount, TokenInterface, Transfer};
      
      #[derive(Accounts)]
      pub struct StakeTokens<'info> {
          #[account(
              init_if_needed,
              payer = user,
              space = 8 + 32 + 8 + 8 + 1,
              seeds = [b"stake", user.key().as_ref()],
              bump,
          )]
          pub stake_account: Account<'info, StakeAccount>,
      
          #[account(mut)]
          pub user_token_account: InterfaceAccount<'info, TokenAccount>,
      
          #[account(
              mut,
              seeds = [b"vault"],
              bump,
          )]
          pub vault_token_account: InterfaceAccount<'info, TokenAccount>,
      
          #[account(mut)]
          pub user: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
          pub system_program: Program<'info, System>,
      }
      
      #[account]
      pub struct StakeAccount {
          pub user: Pubkey,
          pub amount_staked: u64,
          pub stake_timestamp: i64,
          pub bump: u8,
      }
      
      pub fn stake_tokens(ctx: Context<StakeTokens>, amount: u64) -> Result<()> {
          // Transfer tokens to vault
          token_interface::transfer(
              CpiContext::new(
                  ctx.accounts.token_program.to_account_info(),
                  Transfer {
                      from: ctx.accounts.user_token_account.to_account_info(),
                      to: ctx.accounts.vault_token_account.to_account_info(),
                      authority: ctx.accounts.user.to_account_info(),
                  },
              ),
              amount,
          )?;
      
          // Update stake account
          let clock = Clock::get()?;
          ctx.accounts.stake_account.user = ctx.accounts.user.key();
          ctx.accounts.stake_account.amount_staked += amount;
          ctx.accounts.stake_account.stake_timestamp = clock.unix_timestamp;
          ctx.accounts.stake_account.bump = ctx.bumps.stake_account;
      
          Ok(())
      }
      
      #[derive(Accounts)]
      pub struct UnstakeTokens<'info> {
          #[account(
              mut,
              seeds = [b"stake", user.key().as_ref()],
              bump = stake_account.bump,
              has_one = user,
          )]
          pub stake_account: Account<'info, StakeAccount>,
      
          #[account(mut)]
          pub user_token_account: InterfaceAccount<'info, TokenAccount>,
      
          #[account(
              mut,
              seeds = [b"vault"],
              bump,
          )]
          pub vault_token_account: InterfaceAccount<'info, TokenAccount>,
      
          /// CHECK: Vault authority PDA
          #[account(
              seeds = [b"vault-authority"],
              bump,
          )]
          pub vault_authority: UncheckedAccount<'info>,
      
          pub user: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
      }
      
      pub fn unstake_tokens(ctx: Context<UnstakeTokens>, amount: u64) -> Result<()> {
          require!(
              ctx.accounts.stake_account.amount_staked >= amount,
              ErrorCode::InsufficientStake
          );
      
          let seeds = &[
              b"vault-authority",
              &[ctx.bumps.vault_authority],
          ];
          let signer_seeds = &[&seeds[..]];
      
          // Transfer tokens back to user
          token_interface::transfer(
              CpiContext::new(
                  ctx.accounts.token_program.to_account_info(),
                  Transfer {
                      from: ctx.accounts.vault_token_account.to_account_info(),
                      to: ctx.accounts.user_token_account.to_account_info(),
                      authority: ctx.accounts.vault_authority.to_account_info(),
                  },
              ).with_signer(signer_seeds),
              amount,
          )?;
      
          // Update stake account
          ctx.accounts.stake_account.amount_staked -= amount;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Pattern 3: NFT Creation
      
      Minting a non-fungible token (supply = 1, decimals = 0).
      
      ### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{self, Mint, MintTo, SetAuthority, TokenAccount, TokenInterface};
      use anchor_spl::token_interface::spl_token_2022::instruction::AuthorityType;
      
      #[derive(Accounts)]
      pub struct CreateNFT<'info> {
          #[account(
              init,
              payer = payer,
              mint::decimals = 0,
              mint::authority = mint_authority,
              mint::token_program = token_program,
          )]
          pub mint: InterfaceAccount<'info, Mint>,
      
          #[account(
              init,
              payer = payer,
              associated_token::mint = mint,
              associated_token::authority = owner,
              associated_token::token_program = token_program,
          )]
          pub token_account: InterfaceAccount<'info, TokenAccount>,
      
          /// CHECK: Owner of the NFT
          pub owner: UncheckedAccount<'info>,
      
          pub mint_authority: Signer<'info>,
      
          #[account(mut)]
          pub payer: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
          pub associated_token_program: Program<'info, AssociatedToken>,
          pub system_program: Program<'info, System>,
      }
      
      pub fn create_nft(ctx: Context<CreateNFT>) -> Result<()> {
          // Mint exactly 1 token
          token_interface::mint_to(
              CpiContext::new(
                  ctx.accounts.token_program.to_account_info(),
                  MintTo {
                      mint: ctx.accounts.mint.to_account_info(),
                      to: ctx.accounts.token_account.to_account_info(),
                      authority: ctx.accounts.mint_authority.to_account_info(),
                  },
              ),
              1,
          )?;
      
          // Remove mint authority to freeze supply
          token_interface::set_authority(
              CpiContext::new(
                  ctx.accounts.token_program.to_account_info(),
                  SetAuthority {
                      account_or_mint: ctx.accounts.mint.to_account_info(),
                      current_authority: ctx.accounts.mint_authority.to_account_info(),
                  },
              ),
              AuthorityType::MintTokens,
              None,
          )?;
      
          msg!("NFT created: {}", ctx.accounts.mint.key());
          Ok(())
      }
      ```
      
      ### Using Native Rust
      
      ```rust
      use spl_token::instruction::{mint_to, set_authority, AuthorityType};
      
      pub fn create_nft(
          mint: &AccountInfo,
          token_account: &AccountInfo,
          mint_authority: &AccountInfo,
          token_program: &AccountInfo,
      ) -> ProgramResult {
          // 1. Mint exactly 1 token
          invoke(
              &mint_to(
                  &spl_token::ID,
                  mint.key,
                  token_account.key,
                  mint_authority.key,
                  &[],
                  1,  // Exactly 1 token
              )?,
              &[mint.clone(), token_account.clone(), mint_authority.clone()],
          )?;
      
          // 2. Remove mint authority (make supply fixed)
          invoke(
              &set_authority(
                  &spl_token::ID,
                  mint.key,
                  None,  // Set to None
                  AuthorityType::MintTokens,
                  mint_authority.key,
                  &[],
              )?,
              &[mint.clone(), mint_authority.clone()],
          )?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Pattern 4: Freezing and Thawing Accounts
      
      ### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{self, FreezeAccount, Mint, ThawAccount, TokenAccount, TokenInterface};
      
      #[derive(Accounts)]
      pub struct FreezeTokenAccount<'info> {
          #[account(
              mint::freeze_authority = freeze_authority,
          )]
          pub mint: InterfaceAccount<'info, Mint>,
      
          #[account(mut)]
          pub token_account: InterfaceAccount<'info, TokenAccount>,
      
          pub freeze_authority: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
      }
      
      pub fn freeze_account(ctx: Context<FreezeTokenAccount>) -> Result<()> {
          token_interface::freeze_account(
              CpiContext::new(
                  ctx.accounts.token_program.to_account_info(),
                  FreezeAccount {
                      account: ctx.accounts.token_account.to_account_info(),
                      mint: ctx.accounts.mint.to_account_info(),
                      authority: ctx.accounts.freeze_authority.to_account_info(),
                  },
              ),
          )?;
          Ok(())
      }
      
      pub fn thaw_account(ctx: Context<FreezeTokenAccount>) -> Result<()> {
          token_interface::thaw_account(
              CpiContext::new(
                  ctx.accounts.token_program.to_account_info(),
                  ThawAccount {
                      account: ctx.accounts.token_account.to_account_info(),
                      mint: ctx.accounts.mint.to_account_info(),
                      authority: ctx.accounts.freeze_authority.to_account_info(),
                  },
              ),
          )?;
          Ok(())
      }
      ```
      
      ### Using Native Rust
      
      ```rust
      use spl_token::instruction::{freeze_account, thaw_account};
      
      pub fn freeze_token_account(
          token_account: &AccountInfo,
          mint: &AccountInfo,
          freeze_authority: &AccountInfo,
          token_program: &AccountInfo,
      ) -> ProgramResult {
          invoke(
              &freeze_account(
                  token_program.key,
                  token_account.key,
                  mint.key,
                  freeze_authority.key,
                  &[],
              )?,
              &[
                  token_account.clone(),
                  mint.clone(),
                  freeze_authority.clone(),
                  token_program.clone(),
              ],
          )?;
      
          Ok(())
      }
      
      pub fn thaw_token_account(
          token_account: &AccountInfo,
          mint: &AccountInfo,
          freeze_authority: &AccountInfo,
          token_program: &AccountInfo,
      ) -> ProgramResult {
          invoke(
              &thaw_account(
                  token_program.key,
                  token_account.key,
                  mint.key,
                  freeze_authority.key,
                  &[],
              )?,
              &[
                  token_account.clone(),
                  mint.clone(),
                  freeze_authority.clone(),
                  token_program.clone(),
              ],
          )?;
      
          Ok(())
      }
      ```
      
      ---
      
      ## Security Considerations
      
      ### 1. Always Validate Token Accounts
      
      #### Anchor Approach
      
      ```rust
      #[derive(Accounts)]
      pub struct SafeTransfer<'info> {
          #[account(
              mut,
              constraint = source.mint == mint.key() @ ErrorCode::InvalidMint,
              constraint = source.owner == authority.key() @ ErrorCode::InvalidOwner,
          )]
          pub source: InterfaceAccount<'info, TokenAccount>,
      
          #[account(
              mut,
              constraint = destination.mint == mint.key() @ ErrorCode::InvalidMint,
          )]
          pub destination: InterfaceAccount<'info, TokenAccount>,
      
          pub mint: InterfaceAccount<'info, Mint>,
      
          pub authority: Signer<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
      }
      ```
      
      #### Native Rust Approach
      
      ```rust
      // ❌ Dangerous - no validation
      pub fn unsafe_transfer(
          source: &AccountInfo,
          destination: &AccountInfo,
          authority: &AccountInfo,
      ) -> ProgramResult {
          // No checks! Attacker can pass any accounts
          invoke(&transfer_instruction, &accounts)?;
          Ok(())
      }
      
      // ✅ Safe - validates everything
      pub fn safe_transfer(
          source: &AccountInfo,
          destination: &AccountInfo,
          authority: &AccountInfo,
          expected_mint: &Pubkey,
      ) -> ProgramResult {
          // Validate source
          validate_token_account(source, authority.key, expected_mint)?;
      
          // Validate destination
          let dest_token = TokenAccount::unpack(&destination.data.borrow())?;
          if dest_token.mint != *expected_mint {
              return Err(ProgramError::InvalidAccountData);
          }
      
          invoke(&transfer_instruction, &accounts)?;
          Ok(())
      }
      ```
      
      ### 2. Check Token Program ID
      
      #### Anchor Approach
      
      ```rust
      // Anchor automatically validates via Interface type
      pub token_program: Interface<'info, TokenInterface>,
      ```
      
      #### Native Rust Approach
      
      ```rust
      pub fn validate_token_program(token_program: &AccountInfo) -> ProgramResult {
          if token_program.key != &spl_token::ID && token_program.key != &spl_token_2022::ID {
              msg!("Invalid Token Program");
              return Err(ProgramError::IncorrectProgramId);
          }
          Ok(())
      }
      ```
      
      ### 3. Verify Mint Matches
      
      **Attack scenario:** Attacker passes token account for wrong mint.
      
      #### Anchor Approach
      
      ```rust
      #[account(
          constraint = token_account.mint == expected_mint.key() @ ErrorCode::InvalidMint,
      )]
      pub token_account: InterfaceAccount<'info, TokenAccount>,
      ```
      
      #### Native Rust Approach
      
      ```rust
      // Always verify mint
      let source_token = TokenAccount::unpack(&source.data.borrow())?;
      let dest_token = TokenAccount::unpack(&dest.data.borrow())?;
      
      if source_token.mint != dest_token.mint {
          msg!("Mint mismatch between source and destination");
          return Err(ProgramError::InvalidAccountData);
      }
      ```
      
      ### 4. Authority Checks
      
      #### Anchor Approach
      
      ```rust
      #[account(
          constraint = token_account.owner == authority.key() @ ErrorCode::Unauthorized,
      )]
      pub token_account: InterfaceAccount<'info, TokenAccount>,
      
      pub authority: Signer<'info>,  // Automatically validates is_signer
      ```
      
      #### Native Rust Approach
      
      ```rust
      // Verify authority matches token account owner
      let token_account = TokenAccount::unpack(&token_account_info.data.borrow())?;
      
      if token_account.owner != *authority.key {
          msg!("Authority doesn't own token account");
          return Err(ProgramError::IllegalOwner);
      }
      
      // Verify authority signed
      if !authority.is_signer {
          msg!("Authority must sign");
          return Err(ProgramError::MissingRequiredSignature);
      }
      ```
      
      ### 5. Account State Checks
      
      #### Anchor Approach
      
      ```rust
      use spl_token::state::AccountState;
      
      pub fn check_not_frozen(ctx: Context<SomeContext>) -> Result<()> {
          let token_account = &ctx.accounts.token_account;
      
          require!(
              token_account.state == AccountState::Initialized,
              ErrorCode::AccountFrozen
          );
      
          Ok(())
      }
      ```
      
      #### Native Rust Approach
      
      ```rust
      let token_account = TokenAccount::unpack(&token_account_info.data.borrow())?;
      
      // Check not frozen
      if token_account.state == spl_token::state::AccountState::Frozen {
          msg!("Token account is frozen");
          return Err(ProgramError::InvalidAccountData);
      }
      
      // Check initialized
      if token_account.state == spl_token::state::AccountState::Uninitialized {
          msg!("Token account not initialized");
          return Err(ProgramError::UninitializedAccount);
      }
      ```
      
      ### 6. Use TransferChecked Over Transfer
      
      **Why:** `transfer_checked` validates the mint and decimals, preventing certain attack vectors.
      
      #### Anchor Approach
      
      ```rust
      // ✅ Preferred - validates mint and decimals
      token_interface::transfer_checked(
          cpi_context,
          amount,
          decimals,
      )?;
      
      // ❌ Less secure - no mint/decimal validation
      token_interface::transfer(
          cpi_context,
          amount,
      )?;
      ```
      
      #### Native Rust Approach
      
      ```rust
      // ✅ Preferred
      invoke(
          &transfer_checked(
              token_program.key,
              source.key,
              mint.key,
              destination.key,
              authority.key,
              &[],
              amount,
              decimals,
          )?,
          &accounts,
      )?;
      
      // ❌ Less secure
      invoke(
          &transfer(
              token_program.key,
              source.key,
              destination.key,
              authority.key,
              &[],
              amount,
          )?,
          &accounts,
      )?;
      ```
      
      ---
      
      ## Summary
      
      ### Key Takeaways
      
      **Anchor Advantages:**
      - Automatic account validation through constraints
      - Cleaner, more concise code
      - Built-in safety checks
      - Type-safe account structures
      - Simplified CPI with `CpiContext`
      
      **Native Rust Advantages:**
      - Full control over all operations
      - No framework overhead
      - Explicit validation (can be more transparent)
      - Useful for understanding low-level mechanics
      
      ### Common Operations Quick Reference
      
      | Operation | Anchor Module | Native Rust Crate |
      |-----------|---------------|-------------------|
      | Mint tokens | `token_interface::mint_to` | `spl_token::instruction::mint_to` |
      | Transfer tokens | `token_interface::transfer` | `spl_token::instruction::transfer` |
      | Transfer checked | `token_interface::transfer_checked` | `spl_token::instruction::transfer_checked` |
      | Burn tokens | `token_interface::burn` | `spl_token::instruction::burn` |
      | Create ATA | `associated_token` constraint | `spl_associated_token_account` |
      | Close account | `token_interface::close_account` | `spl_token::instruction::close_account` |
      | Freeze account | `token_interface::freeze_account` | `spl_token::instruction::freeze_account` |
      
      ### Security Checklist
      
      - ✅ Validate token program ID
      - ✅ Verify token account ownership
      - ✅ Check mint matches expected
      - ✅ Confirm authority is signer
      - ✅ Ensure account not frozen
      - ✅ Validate ATA derivation if applicable
      - ✅ Use `transfer_checked` instead of `transfer`
      - ✅ Validate account state (initialized/frozen)
      - ✅ Check sufficient balance before operations
      
      ### Token Account Sizes
      
      - **Mint account:** 82 bytes
      - **Token account:** 165 bytes
      - **Token-2022 with extensions:** 82/165 + extension sizes
      
      Token integration is fundamental for DeFi, NFT, and gaming programs on Solana. Whether using Anchor or native Rust, understanding both approaches provides the flexibility to choose the right tool for your use case.
      
    • tokens-validation.md 5.6 KB
      # SPL Token Program - Validation Patterns
      
      Validation patterns for SPL Token accounts including ownership verification, mint validation, ATA address derivation checks, and balance verification. Covers both Anchor constraint-based and Native Rust manual validation approaches.
      
      **For related topics, see:**
      - **[tokens-overview.md](tokens-overview.md)** - Token fundamentals and account structures
      - **[tokens-operations.md](tokens-operations.md)** - Create, mint, transfer, burn, close operations
      - **[tokens-2022.md](tokens-2022.md)** - Token Extensions Program features
      - **[tokens-patterns.md](tokens-patterns.md)** - Common patterns and security
      
      ## Table of Contents
      
      1. [Validate Token Account Ownership and Mint](#validate-token-account-ownership-and-mint)
      2. [Validate ATA Address](#validate-ata-address)
      3. [Check Token Balance](#check-token-balance)
      
      ---
      
      ## Validate Token Account Ownership and Mint
      
      ### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::{TokenAccount, Mint};
      
      #[derive(Accounts)]
      pub struct ValidateTokenAccount<'info> {
          #[account(
              constraint = token_account.owner == owner.key() @ ErrorCode::InvalidOwner,
              constraint = token_account.mint == mint.key() @ ErrorCode::InvalidMint,
          )]
          pub token_account: InterfaceAccount<'info, TokenAccount>,
      
          pub mint: InterfaceAccount<'info, Mint>,
      
          /// CHECK: Any account
          pub owner: UncheckedAccount<'info>,
      }
      
      pub fn validate_token_account(ctx: Context<ValidateTokenAccount>) -> Result<()> {
          // Validation is automatic via constraints
      
          // Additional checks if needed
          require!(
              ctx.accounts.token_account.amount >= 100,
              ErrorCode::InsufficientBalance
          );
      
          Ok(())
      }
      ```
      
      ### Using Native Rust
      
      ```rust
      use spl_token::state::Account as TokenAccount;
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          msg,
          program_error::ProgramError,
          program_pack::Pack,
          pubkey::Pubkey,
      };
      
      pub fn validate_token_account(
          token_account_info: &AccountInfo,
          expected_owner: &Pubkey,
          expected_mint: &Pubkey,
      ) -> ProgramResult {
          // 1. Verify owned by Token Program
          if token_account_info.owner != &spl_token::ID {
              msg!("Account not owned by Token Program");
              return Err(ProgramError::IllegalOwner);
          }
      
          // 2. Deserialize token account
          let token_account = TokenAccount::unpack(&token_account_info.data.borrow())?;
      
          // 3. Verify owner
          if token_account.owner != *expected_owner {
              msg!("Token account owner mismatch");
              return Err(ProgramError::IllegalOwner);
          }
      
          // 4. Verify mint
          if token_account.mint != *expected_mint {
              msg!("Token account mint mismatch");
              return Err(ProgramError::InvalidAccountData);
          }
      
          // 5. Verify not frozen
          if token_account.state != spl_token::state::AccountState::Initialized {
              msg!("Token account is frozen or uninitialized");
              return Err(ProgramError::InvalidAccountData);
          }
      
          Ok(())
      }
      ```
      
      ---
      
      ## Validate ATA Address
      
      ### Using Anchor
      
      ```rust
      use anchor_spl::associated_token::AssociatedToken;
      use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
      
      #[derive(Accounts)]
      pub struct ValidateATA<'info> {
          #[account(
              associated_token::mint = mint,
              associated_token::authority = owner,
              associated_token::token_program = token_program,
          )]
          pub ata: InterfaceAccount<'info, TokenAccount>,
      
          pub mint: InterfaceAccount<'info, Mint>,
      
          /// CHECK: Any account
          pub owner: UncheckedAccount<'info>,
      
          pub token_program: Interface<'info, TokenInterface>,
      }
      
      pub fn validate_ata(ctx: Context<ValidateATA>) -> Result<()> {
          // ATA address is automatically validated by Anchor constraints
          Ok(())
      }
      ```
      
      ### Using Native Rust
      
      ```rust
      use spl_associated_token_account::get_associated_token_address;
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          msg,
          program_error::ProgramError,
          pubkey::Pubkey,
      };
      
      pub fn validate_ata(
          ata_info: &AccountInfo,
          wallet: &Pubkey,
          mint: &Pubkey,
      ) -> ProgramResult {
          // Derive expected ATA address
          let expected_ata = get_associated_token_address(wallet, mint);
      
          // Validate match
          if expected_ata != *ata_info.key {
              msg!("Invalid ATA address");
              return Err(ProgramError::InvalidAccountData);
          }
      
          Ok(())
      }
      ```
      
      ---
      
      ## Check Token Balance
      
      ### Using Anchor
      
      ```rust
      use anchor_spl::token_interface::TokenAccount;
      
      pub fn check_balance(
          ctx: Context<SomeContext>,
          minimum_amount: u64
      ) -> Result<()> {
          let token_account = &ctx.accounts.token_account;
      
          require!(
              token_account.amount >= minimum_amount,
              ErrorCode::InsufficientBalance
          );
      
          Ok(())
      }
      ```
      
      ### Using Native Rust
      
      ```rust
      use spl_token::state::Account as TokenAccount;
      use solana_program::{
          account_info::AccountInfo,
          entrypoint::ProgramResult,
          msg,
          program_error::ProgramError,
          program_pack::Pack,
      };
      
      pub fn check_token_balance(
          token_account_info: &AccountInfo,
          minimum_amount: u64,
      ) -> ProgramResult {
          let token_account = TokenAccount::unpack(&token_account_info.data.borrow())?;
      
          if token_account.amount < minimum_amount {
              msg!("Insufficient token balance: {} < {}", token_account.amount, minimum_amount);
              return Err(ProgramError::InsufficientFunds);
          }
      
          Ok(())
      }
      ```
      
      ---
      
      ## Next Steps
      
      - **Token-2022**: See [tokens-2022.md](tokens-2022.md) for Token Extensions Program features
      - **Patterns & Security**: See [tokens-patterns.md](tokens-patterns.md) for common patterns and comprehensive security best practices
      
    • transaction-lifecycle.md 25.7 KB
      # Transaction Lifecycle: Submission, Retry, and Confirmation
      
      This guide covers the complete lifecycle of Solana transactions from submission to confirmation, including why transactions get dropped, retry strategies, commitment levels, and monitoring patterns for production systems.
      
      ## Transaction Journey Overview
      
      ### The Full Path
      
      ```
      [1] Client                     Creates and signs transaction
          ↓
      [2] RPC Node                   Validates and forwards
          ↓
      [3] Leader's TPU               Transaction Processing Unit pipeline
          ├─ Fetch Stage            Receives from network
          ├─ SigVerify Stage        Verifies signatures
          ├─ Banking Stage          Executes transactions
          ├─ PoH Service            Records in Proof of History
          └─ Broadcast Stage        Shares with cluster
          ↓
      [4] Cluster Validation         Validators vote on blocks
          ↓
      [5] Confirmation Levels
          ├─ Processed              Included in block by leader
          ├─ Confirmed              Supermajority voted (~66% stake)
          └─ Finalized              32+ confirmed blocks after (~13 seconds)
      ```
      
      ### Time
      
      line
      
      **Normal flow:**
      - Client → RPC: Instant (local network)
      - RPC → Leader: 100-400ms (network latency)
      - Leader processing: 400-600ms (slot time)
      - Confirmed: ~1-2 slots (~800-1200ms)
      - Finalized: ~32 slots (~13+ seconds)
      
      **Total time (happy path):** ~1-15 seconds
      
      ## Blockhash Expiration
      
      ### How Blockhashes Work
      
      Solana transactions include a `recent_blockhash` field for two purposes:
      1. **Uniqueness**: Ensures each transaction is unique (prevents duplicates)
      2. **Freshness**: Limits transaction validity to prevent spam
      
      **Critical constraint:**
      
      ```rust
      // Solana runtime maintains BlockhashQueue
      struct BlockhashQueue {
          last_hash: Hash,
          ages: HashMap<Hash, HashAge>,
          max_age: usize,  // Currently 151
      }
      
      // Transaction validation:
      fn is_valid_blockhash(blockhash: &Hash, queue: &BlockhashQueue) -> bool {
          queue.ages.contains_key(blockhash)  // Must be in last 151 blockhashes
      }
      ```
      
      ### The 151-Block Window
      
      **How it works:**
      1. Each slot produces a new blockhash (~400-600ms per slot)
      2. Runtime keeps last 151 blockhashes in `BlockhashQueue`
      3. Transactions checked against this queue
      4. If blockhash older than 150 blocks → **REJECTED**
      
      **Calculation:**
      ```
      151 blockhashes × ~600ms average slot time = ~90 seconds maximum
      151 blockhashes × ~400ms minimum slot time = ~60 seconds minimum
      
      Effective window: 60-90 seconds
      ```
      
      **Critical**: Once a blockhash exits the queue (>150 blocks old), transactions using it can **never** be processed. They're permanently invalid.
      
      ### Detecting Expiration
      
      **Using `lastValidBlockHeight`:**
      
      ```rust
      use solana_client::rpc_client::RpcClient;
      use solana_sdk::commitment_config::CommitmentConfig;
      
      async fn check_transaction_expiration(
          rpc_client: &RpcClient,
          last_valid_block_height: u64,
      ) -> bool {
          // Get current block height
          let current_block_height = rpc_client
              .get_block_height()
              .unwrap_or(0);
      
          // Transaction expired if current height > last valid height
          current_block_height > last_valid_block_height
      }
      ```
      
      **Getting `lastValidBlockHeight`:**
      
      ```rust
      let blockhash_response = rpc_client.get_latest_blockhash()?;
      
      let blockhash = blockhash_response.value.0;
      let last_valid_block_height = blockhash_response.value.1;  // Blocks until expiration
      
      println!("Blockhash: {}", blockhash);
      println!("Valid until block: {}", last_valid_block_height);
      ```
      
      ### Why Transactions Expire
      
      **Design rationale:**
      
      1. **Prevents replay attacks**: Old transactions can't be resubmitted years later
      2. **Manages state bloat**: Runtime doesn't need infinite blockhash history
      3. **Network spam protection**: Attackers can't flood network with ancient transactions
      4. **Simplifies fee markets**: Recent activity determines current conditions
      
      **Trade-off**: 60-90 second window requires responsive clients and reliable networking.
      
      ## How Transactions Get Dropped
      
      ### Before Processing
      
      **1. UDP Packet Loss**
      
      Solana uses UDP for transaction forwarding (performance over reliability):
      
      ```
      Client → RPC: UDP packet
      RPC → Leader: UDP packet
      
      Packet loss rate: 0.1-5% depending on network conditions
      ```
      
      **Impact**: Transaction silently dropped, never reaches leader.
      
      **Detection**: No error, no confirmation - transaction just disappears.
      
      **Solution**: Retry mechanism (RPC default behavior).
      
      **2. RPC Node Congestion**
      
      RPC nodes maintain transaction queues:
      
      ```rust
      // RPC node queue limits
      const MAX_TRANSACTIONS_QUEUE: usize = 10_000;
      
      // When queue full:
      if queue.len() >= MAX_TRANSACTIONS_QUEUE {
          return Err("Transaction queue full, try again");
      }
      ```
      
      **Impact**: New transactions rejected when queue full.
      
      **Detection**: RPC returns error immediately.
      
      **Solution**: Back off and retry, or use different RPC endpoint.
      
      **3. RPC Node Lag**
      
      RPC nodes can fall behind cluster:
      
      ```rust
      // Check RPC health
      let processed_slot = rpc_client.get_slot()?;
      let max_shred_insert_slot = rpc_client.get_max_shred_insert_slot()?;
      
      let lag = max_shred_insert_slot.saturating_sub(processed_slot);
      
      if lag > 50 {
          println!("WARNING: RPC is {} slots behind", lag);
          // Consider using different RPC node
      }
      ```
      
      **Impact**: Fetches stale blockhashes that expire quickly.
      
      **Solution**: Monitor RPC health, use multiple RPC providers.
      
      **4. Blockhash from Minority Fork**
      
      Clusters occasionally fork temporarily (~5% of slots):
      
      ```
      Majority fork: Block A → Block B → Block C
      Minority fork: Block A → Block X (abandoned)
      ```
      
      If you fetch blockhash from minority fork:
      - Blockhash is valid on minority fork
      - Majority fork has different blockhash
      - Transaction **never** valid on majority fork
      
      **Impact**: Transaction permanently invalid (never in BlockhashQueue of majority fork).
      
      **Detection**: Transaction never confirms, blockhash never appears in majority chain.
      
      **Solution**: Use `confirmed` commitment level when fetching blockhashes (not `processed`).
      
      ### After Processing But Before Finalization
      
      **5. Leader on Minority Fork**
      
      Transaction processed by leader, but leader's block abandoned by cluster:
      
      ```
      1. Leader processes transaction in slot 1000
      2. Cluster votes on slot 1000
      3. Supermajority votes for different fork
      4. Leader's block (and transaction) discarded
      ```
      
      **Impact**: Transaction processed but not confirmed. Must resubmit.
      
      **Detection**: Transaction shows as processed but never confirmed.
      
      **Solution**: Wait for `confirmed` level before assuming success.
      
      **6. Transaction Expiration During Retry**
      
      Default RPC retry behavior has limitations:
      
      ```rust
      // RPC retry logic (simplified):
      while !finalized && !expired {
          forward_to_leader();
          sleep(2_seconds);
      }
      
      // Problem: What if we can't determine expiration?
      // RPC may stop retrying early!
      ```
      
      **Impact**: RPC stops retrying before transaction actually expires.
      
      **Solution**: Implement custom retry logic with explicit expiration tracking.
      
      ## Commitment Levels
      
      ### Understanding Commitment
      
      Solana has three commitment levels representing stages of finality:
      
      ```
      Processed
          ↓ (1-2 slots later)
      Confirmed
          ↓ (32+ slots later, ~13 seconds)
      Finalized
      ```
      
      ### Processed
      
      **Definition**: Transaction processed by leader and included in a block.
      
      **Characteristics:**
      - Fastest (most recent)
      - Least safe (~5% chance of being on abandoned fork)
      - Can be rolled back if fork abandoned
      
      **When to use:**
      - Real-time UX updates (show pending state)
      - Price feeds where staleness is worse than occasional rollback
      - **NOT for blockhash fetching** (risk of minority fork blockhash)
      
      **Example:**
      ```rust
      use solana_client::rpc_config::RpcSendTransactionConfig;
      use solana_sdk::commitment_config::CommitmentLevel;
      
      let config = RpcSendTransactionConfig {
          skip_preflight: false,
          preflight_commitment: Some(CommitmentLevel::Processed),
          ..Default::default()
      };
      
      // Risky! Blockhash might be from minority fork
      let signature = rpc_client.send_transaction_with_config(&transaction, config)?;
      ```
      
      ### Confirmed
      
      **Definition**: Supermajority of validators voted for the block containing the transaction.
      
      **Characteristics:**
      - Fast (~1-2 slots, ~600-1200ms)
      - Safe (~<0.1% chance of rollback in normal conditions)
      - **RECOMMENDED for blockhash fetching**
      
      **When to use:**
      - **Default choice** for most operations
      - Blockhash fetching (balance of speed and safety)
      - Transaction submission (preflight commitment)
      - Confirmation monitoring
      
      **Example:**
      ```rust
      let commitment = CommitmentConfig::confirmed();
      
      // Fetch blockhash at confirmed level
      let recent_blockhash = rpc_client.get_latest_blockhash_with_commitment(commitment)?;
      
      // Set preflight commitment to match
      let config = RpcSendTransactionConfig {
          preflight_commitment: Some(CommitmentLevel::Confirmed),
          ..Default::default()
      };
      ```
      
      ### Finalized
      
      **Definition**: 32+ confirmed blocks have been built on top (mathematically impossible to rollback).
      
      **Characteristics:**
      - Slowest (~13+ seconds)
      - 100% safe (impossible to rollback)
      - Guaranteed by consensus algorithm
      
      **When to use:**
      - Financial settlement
      - Legal/compliance requirements
      - Cross-chain bridges
      - Critical state changes
      
      **Example:**
      ```rust
      let commitment = CommitmentConfig::finalized();
      
      // Wait for finalization
      rpc_client.confirm_transaction_with_spinner(
          &signature,
          &recent_blockhash,
          commitment,
      )?;
      ```
      
      ### Preflight Commitment Matching
      
      **Critical rule**: Preflight commitment MUST match blockhash fetch commitment.
      
      **Why:**
      
      ```rust
      // Scenario: Mismatch
      let blockhash = rpc.get_latest_blockhash_with_commitment(confirmed)?;  // confirmed
      
      let config = RpcSendTransactionConfig {
          preflight_commitment: Some(CommitmentLevel::Processed),  // processed (WRONG!)
          ..Default::default()
      };
      
      // RPC tries to simulate at processed level
      // But blockhash only exists at confirmed level
      // Result: "Blockhash not found" error
      ```
      
      **Correct approach:**
      
      ```rust
      let commitment = CommitmentConfig::confirmed();
      
      // Fetch blockhash
      let blockhash_response = rpc.get_latest_blockhash_with_commitment(commitment)?;
      let blockhash = blockhash_response.0;
      
      // Match preflight commitment
      let config = RpcSendTransactionConfig {
          preflight_commitment: Some(CommitmentLevel::Confirmed),
          ..Default::default()
      };
      
      let signature = rpc.send_transaction_with_config(&transaction, config)?;
      ```
      
      ## RPC Retry Behavior
      
      ### Default Retry Logic
      
      RPC nodes automatically retry transactions:
      
      ```rust
      // Simplified RPC retry algorithm:
      const RETRY_INTERVAL: Duration = Duration::from_secs(2);
      const MAX_QUEUE_SIZE: usize = 10_000;
      
      loop {
          if transaction.is_finalized() {
              return Ok(signature);
          }
      
          if queue.len() >= MAX_QUEUE_SIZE {
              return Err("Queue full");
          }
      
          if can_determine_expiration() {
              if transaction.is_expired() {
                  return Err("Blockhash expired");
              }
          } else {
              // Conservative: retry only once if can't determine expiration
              if retry_count > 1 {
                  return Ok(signature);  // Might not actually be finalized!
              }
          }
      
          forward_to_current_leader();
          forward_to_next_leader();
          sleep(RETRY_INTERVAL);
          retry_count += 1;
      }
      ```
      
      ### Leader Forwarding
      
      RPC forwards transactions to:
      1. **Current leader**: For immediate processing
      2. **Next leader**: In case current leader rotation happens
      
      **Why both?**
      - Leader rotation happens every 4 slots (~1.6-2.4 seconds)
      - Transaction might arrive during rotation
      - Next leader can process in upcoming slots
      
      ### Queue Pressure
      
      During congestion:
      
      ```
      Queue size: 10,000 transactions
      New transaction arrives:
          if queue.is_full():
              reject("Transaction queue full")
          else:
              queue.push(transaction)
              retry_until_finalized()
      ```
      
      **User experience:**
      - Fresh transactions rejected when queue full
      - Older transactions keep retrying
      - Can create priority inversion (old low-priority tx blocks new high-priority tx)
      
      **Solution**: Use `maxRetries: 0` to take manual control during congestion.
      
      ## Custom Retry Strategies
      
      ### Manual Retry Loop
      
      Taking full control:
      
      ```rust
      use solana_client::rpc_client::RpcClient;
      use solana_sdk::signature::Signature;
      use std::time::Duration;
      use tokio::time::sleep;
      
      async fn send_transaction_with_retry(
          rpc_client: &RpcClient,
          transaction: &Transaction,
          last_valid_block_height: u64,
      ) -> Result<Signature, Box<dyn std::error::Error>> {
          let config = RpcSendTransactionConfig {
              skip_preflight: true,  // Already validated
              max_retries: Some(0),  // Manual retry control
              ..Default::default()
          };
      
          let signature = rpc_client.send_transaction_with_config(
              transaction,
              config,
          )?;
      
          // Manual retry loop
          loop {
              // Check if transaction confirmed
              match rpc_client.get_signature_status(&signature)? {
                  Some(Ok(_)) => {
                      println!("Transaction confirmed!");
                      return Ok(signature);
                  }
                  Some(Err(e)) => {
                      return Err(format!("Transaction failed: {:?}", e).into());
                  }
                  None => {
                      // Not processed yet, continue
                  }
              }
      
              // Check expiration
              let current_block_height = rpc_client.get_block_height()?;
              if current_block_height > last_valid_block_height {
                  return Err("Transaction expired".into());
              }
      
              // Resubmit
              rpc_client.send_transaction_with_config(transaction, config)?;
      
              // Wait before next retry
              sleep(Duration::from_millis(500)).await;
          }
      }
      ```
      
      ### Exponential Backoff
      
      Reduce network load during congestion:
      
      ```rust
      async fn retry_with_exponential_backoff(
          rpc_client: &RpcClient,
          transaction: &Transaction,
          last_valid_block_height: u64,
      ) -> Result<Signature, Box<dyn std::error::Error>> {
          let signature = rpc_client.send_transaction(transaction)?;
      
          let mut retry_delay = Duration::from_millis(500);
          const MAX_DELAY: Duration = Duration::from_secs(8);
      
          loop {
              match rpc_client.get_signature_status(&signature)? {
                  Some(Ok(_)) => return Ok(signature),
                  Some(Err(e)) => return Err(e.into()),
                  None => {
                      // Check expiration
                      if rpc_client.get_block_height()? > last_valid_block_height {
                          return Err("Expired".into());
                      }
      
                      // Resubmit
                      rpc_client.send_transaction(transaction)?;
      
                      // Exponential backoff
                      sleep(retry_delay).await;
                      retry_delay = std::cmp::min(retry_delay * 2, MAX_DELAY);
                  }
              }
          }
      }
      ```
      
      ### Constant Interval (Mango Approach)
      
      Aggressive resubmission:
      
      ```rust
      async fn retry_constant_interval(
          rpc_client: &RpcClient,
          transaction: &Transaction,
          last_valid_block_height: u64,
      ) -> Result<Signature, Box<dyn std::error::Error>> {
          let signature = rpc_client.send_transaction(transaction)?;
      
          const RETRY_INTERVAL: Duration = Duration::from_millis(500);
      
          loop {
              match rpc_client.get_signature_status(&signature)? {
                  Some(Ok(_)) => return Ok(signature),
                  Some(Err(e)) => return Err(e.into()),
                  None => {
                      if rpc_client.get_block_height()? > last_valid_block_height {
                          return Err("Expired".into());
                      }
      
                      // Constant interval resubmission
                      rpc_client.send_transaction(transaction)?;
                      sleep(RETRY_INTERVAL).await;
                  }
              }
          }
      }
      ```
      
      **Trade-offs:**
      - **Exponential backoff**: Network-friendly, slower confirmation
      - **Constant interval**: Faster confirmation, more network load
      - **Choice depends on**: Application needs, RPC provider limits, congestion levels
      
      ## Confirmation Monitoring
      
      ### Polling for Confirmation
      
      **Basic polling:**
      
      ```rust
      use solana_sdk::signature::Signature;
      
      fn wait_for_confirmation(
          rpc_client: &RpcClient,
          signature: &Signature,
          commitment: CommitmentConfig,
      ) -> Result<(), Box<dyn std::error::Error>> {
          loop {
              match rpc_client.get_signature_status_with_commitment(
                  signature,
                  commitment,
              )? {
                  Some(Ok(_)) => {
                      println!("Transaction confirmed at {:?}", commitment);
                      return Ok(());
                  }
                  Some(Err(e)) => {
                      return Err(format!("Transaction failed: {:?}", e).into());
                  }
                  None => {
                      std::thread::sleep(Duration::from_millis(500));
                  }
              }
          }
      }
      ```
      
      **With timeout:**
      
      ```rust
      use std::time::{Duration, Instant};
      
      fn wait_for_confirmation_with_timeout(
          rpc_client: &RpcClient,
          signature: &Signature,
          timeout: Duration,
      ) -> Result<bool, Box<dyn std::error::Error>> {
          let start = Instant::now();
      
          while start.elapsed() < timeout {
              match rpc_client.get_signature_status(signature)? {
                  Some(Ok(_)) => return Ok(true),
                  Some(Err(e)) => return Err(e.into()),
                  None => std::thread::sleep(Duration::from_millis(500)),
              }
          }
      
          Ok(false)  // Timed out
      }
      ```
      
      ### Using `confirm_transaction`
      
      Built-in helper with expiration tracking:
      
      ```rust
      let commitment = CommitmentConfig::confirmed();
      
      // Method 1: With blockhash context
      rpc_client.confirm_transaction_with_spinner(
          &signature,
          &recent_blockhash,
          commitment,
      )?;
      
      // Method 2: With last valid block height (recommended)
      let result = rpc_client.confirm_transaction_with_commitment(
          &signature,
          commitment,
      )?;
      
      if result.value {
          println!("Transaction confirmed!");
      } else {
          println!("Transaction not confirmed (might have expired)");
      }
      ```
      
      ### WebSocket Subscriptions (Real-Time)
      
      For real-time updates without polling:
      
      ```rust
      use solana_client::pubsub_client::PubsubClient;
      use solana_sdk::commitment_config::CommitmentConfig;
      
      async fn subscribe_to_signature(
          ws_url: &str,
          signature: &Signature,
      ) -> Result<(), Box<dyn std::error::Error>> {
          let pubsub_client = PubsubClient::new(ws_url).await?;
      
          let (mut stream, unsubscribe) = pubsub_client
              .signature_subscribe(signature, Some(CommitmentConfig::confirmed()))
              .await?;
      
          // Wait for notification
          while let Some(response) = stream.next().await {
              match response.value {
                  solana_client::rpc_response::RpcSignatureResult::ProcessedSignature(_) => {
                      println!("Transaction confirmed!");
                      break;
                  }
              }
          }
      
          unsubscribe().await;
          Ok(())
      }
      ```
      
      **Advantages:**
      - Real-time notification (no polling delay)
      - Lower RPC load
      - Immediate feedback
      
      **Disadvantages:**
      - WebSocket connection overhead
      - Need to handle disconnections
      - Not all RPC providers support WebSockets
      
      ## Best Practices
      
      ### 1. Fetch Fresh Blockhashes
      
      ```rust
      // BAD: Fetch once and reuse
      let blockhash = rpc.get_latest_blockhash()?;
      for tx in transactions {
          // All use same blockhash (increases expiration risk)
          send_transaction(tx, &blockhash)?;
      }
      
      // GOOD: Fetch fresh blockhash for each transaction
      for tx in transactions {
          let blockhash = rpc.get_latest_blockhash()?;
          send_transaction(tx, &blockhash)?;
      }
      
      // BETTER: Fetch fresh blockhash right before signing
      fn prepare_and_send(user_action: Action) {
          // User initiates action
          let blockhash = rpc.get_latest_blockhash()?;  // Fetch now!
      
          // Build and sign (fast)
          let tx = build_transaction(user_action, &blockhash);
          sign_transaction(&tx);
      
          // Submit immediately
          send_transaction(&tx)?;
      }
      ```
      
      ### 2. Use Confirmed Commitment
      
      ```rust
      // RECOMMENDED: Confirmed commitment
      let commitment = CommitmentConfig::confirmed();
      let blockhash = rpc.get_latest_blockhash_with_commitment(commitment)?;
      
      // Risks minority fork
      let blockhash = rpc.get_latest_blockhash_with_commitment(
          CommitmentConfig::processed()
      )?;  // Avoid!
      ```
      
      ### 3. Match Preflight Commitment
      
      ```rust
      let commitment = CommitmentConfig::confirmed();
      
      // Fetch blockhash
      let (blockhash, last_valid_block_height) = rpc
          .get_latest_blockhash_with_commitment(commitment)?;
      
      // Match preflight commitment
      let config = RpcSendTransactionConfig {
          preflight_commitment: Some(CommitmentLevel::Confirmed),  // MATCH!
          ..Default::default()
      };
      ```
      
      ### 4. Track Expiration Explicitly
      
      ```rust
      // Get expiration info
      let (blockhash, last_valid_block_height) = rpc.get_latest_blockhash()?;
      
      // Check before retry
      fn should_retry(rpc: &RpcClient, last_valid: u64) -> bool {
          rpc.get_block_height().unwrap_or(0) <= last_valid
      }
      ```
      
      ### 5. Monitor RPC Health
      
      ```rust
      async fn check_rpc_health(rpc: &RpcClient) -> bool {
          let processed = rpc.get_slot().unwrap_or(0);
          let max_shred = rpc.get_max_shred_insert_slot().unwrap_or(0);
      
          let lag = max_shred.saturating_sub(processed);
      
          if lag > 50 {
              eprintln!("RPC lagging by {} slots", lag);
              return false;
          }
      
          true
      }
      ```
      
      ### 6. Implement Proper Error Handling
      
      ```rust
      match rpc.send_transaction(&tx) {
          Ok(signature) => {
              println!("Submitted: {}", signature);
              // Wait for confirmation
          }
          Err(e) => {
              if e.to_string().contains("BlockhashNotFound") {
                  // Blockhash expired, fetch fresh one
                  let new_blockhash = rpc.get_latest_blockhash()?;
                  // Re-sign transaction with new blockhash
              } else if e.to_string().contains("AlreadyProcessed") {
                  // Transaction already submitted (safe to ignore)
              } else {
                  // Other error, handle appropriately
                  return Err(e.into());
              }
          }
      }
      ```
      
      ### 7. Use Skip Preflight Judiciously
      
      ```rust
      // When to skip preflight:
      // - During congestion (preflight adds latency)
      // - When retrying (already validated once)
      // - When you're confident about transaction validity
      
      let config = RpcSendTransactionConfig {
          skip_preflight: true,  // Skip simulation
          preflight_commitment: Some(CommitmentLevel::Confirmed),
          max_retries: Some(0),
          ..Default::default()
      };
      
      // Still recommended: Simulate ONCE before skip_preflight
      rpc.simulate_transaction(&tx)?;  // Catch errors
      // Then submit with skip_preflight for speed
      ```
      
      ## Production Patterns
      
      ### High-Throughput System
      
      ```rust
      struct TransactionSubmitter {
          rpc_client: Arc<RpcClient>,
          retry_queue: Arc<Mutex<VecDeque<RetryableTransaction>>>,
      }
      
      struct RetryableTransaction {
          transaction: Transaction,
          signature: Signature,
          last_valid_block_height: u64,
          submitted_at: Instant,
          retry_count: usize,
      }
      
      impl TransactionSubmitter {
          async fn submit_transaction(&self, tx: Transaction) -> Result<Signature, Error> {
              let (blockhash, last_valid) = self.rpc_client.get_latest_blockhash()?;
      
              // Submit initial
              let signature = self.rpc_client.send_transaction(&tx)?;
      
              // Add to retry queue
              let retryable = RetryableTransaction {
                  transaction: tx,
                  signature,
                  last_valid_block_height: last_valid,
                  submitted_at: Instant::now(),
                  retry_count: 0,
              };
      
              self.retry_queue.lock().unwrap().push_back(retryable);
      
              Ok(signature)
          }
      
          async fn retry_worker(&self) {
              loop {
                  sleep(Duration::from_millis(500)).await;
      
                  let mut queue = self.retry_queue.lock().unwrap();
      
                  for tx in queue.iter_mut() {
                      // Check if confirmed
                      match self.rpc_client.get_signature_status(&tx.signature) {
                          Ok(Some(Ok(_))) => {
                              // Confirmed, remove from queue (handle in cleanup pass)
                              continue;
                          }
                          Ok(Some(Err(_))) => {
                              // Failed, remove from queue
                              continue;
                          }
                          _ => {
                              // Not confirmed, check expiration
                              let current_height = self.rpc_client.get_block_height().unwrap_or(0);
      
                              if current_height > tx.last_valid_block_height {
                                  // Expired, remove from queue
                                  continue;
                              }
      
                              // Retry
                              let _ = self.rpc_client.send_transaction(&tx.transaction);
                              tx.retry_count += 1;
                          }
                      }
                  }
      
                  // Cleanup confirmed/failed/expired
                  queue.retain(|tx| {
                      matches!(
                          self.rpc_client.get_signature_status(&tx.signature),
                          Ok(None)  // Still pending
                      )
                  });
              }
          }
      }
      ```
      
      ### Wallet Integration
      
      ```rust
      async fn wallet_send_transaction(
          rpc: &RpcClient,
          unsigned_tx: Transaction,
          signer: &dyn Signer,
      ) -> Result<Signature, Error> {
          // Fetch blockhash immediately before signing
          let (blockhash, last_valid) = rpc.get_latest_blockhash()?;
      
          // Update transaction with fresh blockhash
          let mut tx = unsigned_tx.clone();
          tx.message.recent_blockhash = blockhash;
      
          // Sign
          tx.sign(&[signer], blockhash);
      
          // Simulate first
          rpc.simulate_transaction(&tx)?;
      
          // Submit with retry
          let signature = tx.signatures[0];
      
          send_with_retry(rpc, &tx, last_valid).await?;
      
          Ok(signature)
      }
      ```
      
      ## Resources
      
      ### Official Documentation
      - [Transaction Retry Guide](https://solana.com/developers/guides/advanced/retry)
      - [Transaction Confirmation Guide](https://solana.com/developers/guides/advanced/confirmation)
      
      ### Technical References
      - [RpcClient Source](https://github.com/solana-labs/solana/blob/master/client/src/rpc_client.rs)
      - [Transaction Source](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction/mod.rs)
      - [BlockhashQueue Source](https://github.com/solana-labs/solana/blob/master/runtime/src/blockhash_queue.rs)
      
      ### Community Resources
      - [Solana Cookbook - Transactions](https://solanacookbook.com/references/basic-transactions.html)
      - [Solana Stack Exchange - Transaction Questions](https://solana.stackexchange.com/questions/tagged/transaction)
      
    • versioned-transactions.md 28.1 KB
      # Versioned Transactions and Address Lookup Tables
      
      This guide covers Solana's versioned transaction format and Address Lookup Tables (ALTs), which enable programs to work with more accounts per transaction by compressing account references.
      
      ## Introduction
      
      ### The Account Limit Problem
      
      Solana transactions are transmitted over UDP and must fit within the IPv6 MTU size of 1280 bytes. After accounting for headers, this leaves approximately 1232 bytes for the transaction packet data.
      
      **Legacy transaction constraints:**
      - Each account address: 32 bytes
      - Signatures and metadata: ~300-400 bytes overhead
      - **Result**: Maximum ~35 accounts per transaction
      
      This limitation became problematic as developers needed to compose multiple on-chain programs atomically, especially for complex DeFi operations like multi-hop swaps or protocol interactions.
      
      ### The Solution: Versioned Transactions
      
      Versioned transactions introduce a new transaction format that supports **Address Lookup Tables (ALTs)**, allowing accounts to be referenced by 1-byte indices instead of full 32-byte addresses.
      
      **Impact:**
      - Legacy (v0 without ALTs): ~35 accounts maximum
      - Versioned (v0 with ALTs): **64+ accounts** per transaction
      - 31-byte savings per account referenced from an ALT
      
      ## Transaction Versions
      
      ### Version Format
      
      Solana uses the high bit of the first byte to determine transaction version:
      
      ```rust
      // Version detection (first byte of transaction)
      if first_byte & 0x80 == 0 {
          // Legacy transaction (bit pattern: 0xxxxxxx)
          version = "legacy"
      } else {
          // Versioned transaction (bit pattern: 1xxxxxxx)
          // Remove version bit to get actual version number
          version = first_byte & 0x7F  // Currently only version 0 exists
      }
      ```
      
      ### Legacy Transactions
      
      **Structure:**
      ```rust
      pub struct LegacyMessage {
          pub header: MessageHeader,
          pub account_keys: Vec<Pubkey>,           // All 32-byte addresses
          pub recent_blockhash: Hash,
          pub instructions: Vec<CompiledInstruction>,
      }
      ```
      
      **Characteristics:**
      - No version byte (implicitly version "legacy")
      - All accounts must be fully specified (32 bytes each)
      - Maximum ~35 accounts due to packet size limits
      - Still supported and widely used for simple transactions
      
      ### Version 0 Transactions
      
      **Structure:**
      ```rust
      pub struct MessageV0 {
          pub header: MessageHeader,
          pub account_keys: Vec<Pubkey>,           // Directly specified accounts
          pub recent_blockhash: Hash,
          pub instructions: Vec<CompiledInstruction>,
          pub address_table_lookups: Vec<MessageAddressTableLookup>,  // NEW!
      }
      
      pub struct MessageAddressTableLookup {
          pub account_key: Pubkey,                 // ALT address (32 bytes)
          pub writable_indexes: Vec<u8>,           // Writable account indices
          pub readonly_indexes: Vec<u8>,           // Readonly account indices
      }
      ```
      
      **Characteristics:**
      - Starts with version byte: `0x80` (128 in decimal, version 0)
      - Includes `address_table_lookups` field
      - Can reference accounts from ALTs using 1-byte indices
      - Enables 64+ accounts per transaction
      
      **Transaction size calculation:**
      ```
      Version 0 overhead:
      + 1 byte (version)
      + 1 byte (number of lookup tables)
      + 34 bytes per lookup table (32-byte address + 2 length bytes)
      + 1 byte per account index referenced
      
      Example with 1 ALT referencing 30 accounts:
        1 (version) + 1 (table count) + 34 (table) + 30 (indices) = 66 bytes
      
      Equivalent legacy transaction:
        30 accounts × 32 bytes = 960 bytes
      
      Savings: 960 - 66 = 894 bytes!
      ```
      
      ## Address Lookup Tables (ALTs)
      
      ### What Are ALTs?
      
      Address Lookup Tables are **on-chain accounts** that store collections of related addresses. They act as a lookup mechanism to compress account references in transactions.
      
      **Key properties:**
      - Managed by the Address Lookup Table Program (`AddressLookupTableProgram`)
      - Store up to **256 addresses** (indexed by u8: 0-255)
      - Can be created, extended, deactivated, and closed
      - Addresses are append-only for security
      
      ### ALT Account Structure
      
      ```rust
      pub struct AddressLookupTable<'a> {
          pub meta: LookupTableMeta,
          pub addresses: Cow<'a, [Pubkey]>,
      }
      
      pub struct LookupTableMeta {
          pub deactivation_slot: Slot,           // Slot when deactivated (u64::MAX if active)
          pub last_extended_slot: Slot,          // Last slot when addresses were added
          pub last_extended_slot_start_index: u8, // Index where last extension started
          pub authority: Option<Pubkey>,         // Can add/deactivate (None = immutable)
      }
      ```
      
      **On-chain layout:**
      ```
      Bytes 0-55:   LookupTableMeta (56 bytes)
      Bytes 56+:    Raw list of Pubkey addresses (32 bytes each)
      ```
      
      ### Creating Address Lookup Tables
      
      **Step 1: Create the table**
      
      ```rust
      use solana_sdk::{
          address_lookup_table_account::instruction as alt_instruction,
          instruction::Instruction,
          pubkey::Pubkey,
          signer::Signer,
      };
      
      // Get recent slot for table derivation
      let recent_slot = rpc_client.get_slot()?;
      
      // Create lookup table instruction
      let (create_ix, lookup_table_address) = alt_instruction::create_lookup_table(
          payer.pubkey(),      // Authority
          payer.pubkey(),      // Payer
          recent_slot,         // Recent slot for PDA derivation
      );
      
      // The lookup table address is derived deterministically:
      // PDA(seeds=[authority, recent_slot], program=AddressLookupTableProgram)
      ```
      
      **Transaction to create:**
      ```rust
      let create_tx = Transaction::new_signed_with_payer(
          &[create_ix],
          Some(&payer.pubkey()),
          &[&payer],
          recent_blockhash,
      );
      
      rpc_client.send_and_confirm_transaction(&create_tx)?;
      ```
      
      **Important**: Wait for the transaction to be **finalized** before extending or using the table.
      
      **Step 2: Extend the table with addresses**
      
      ```rust
      // Addresses to add to the lookup table
      let addresses_to_add = vec![
          pubkey1,
          pubkey2,
          pubkey3,
          // ... up to ~20 addresses per transaction
      ];
      
      let extend_ix = alt_instruction::extend_lookup_table(
          lookup_table_address,
          payer.pubkey(),        // Authority
          Some(payer.pubkey()),  // Payer (optional)
          addresses_to_add,
      );
      
      let extend_tx = Transaction::new_signed_with_payer(
          &[extend_ix],
          Some(&payer.pubkey()),
          &[&payer],
          recent_blockhash,
      );
      
      rpc_client.send_and_confirm_transaction(&extend_tx)?;
      ```
      
      **Batching strategy:**
      - Each extend operation can add approximately **20 addresses** before hitting transaction size limits
      - For more addresses, send multiple extend transactions
      - Example from TeamRaccoons repo: Batch in chunks of 20
      
      ```rust
      // Batch extend for large address sets
      let batch_size = 20;
      for chunk in addresses.chunks(batch_size) {
          let extend_ix = alt_instruction::extend_lookup_table(
              lookup_table_address,
              authority.pubkey(),
              Some(payer.pubkey()),
              chunk.to_vec(),
          );
      
          // Send transaction...
          rpc_client.send_and_confirm_transaction(&tx)?;
      }
      ```
      
      **Warmup period:**
      - Newly added addresses require **1 slot** before they can be used
      - Must wait for finalization before using in v0 transactions
      - Check `last_extended_slot` to ensure addresses are ready
      
      **Step 3: Fetch the lookup table**
      
      ```rust
      use solana_client::rpc_client::RpcClient;
      use solana_sdk::address_lookup_table_account::AddressLookupTableAccount;
      
      let lookup_table_account = rpc_client
          .get_account(&lookup_table_address)?;
      
      let lookup_table = AddressLookupTableAccount::deserialize(&lookup_table_account.data)?;
      
      // Access addresses
      println!("Table contains {} addresses", lookup_table.addresses.len());
      for (index, address) in lookup_table.addresses.iter().enumerate() {
          println!("Index {}: {}", index, address);
      }
      ```
      
      ### Using ALTs in V0 Transactions
      
      **Build a v0 transaction with ALT:**
      
      ```rust
      use solana_sdk::{
          message::{v0, VersionedMessage},
          transaction::VersionedTransaction,
          address_lookup_table_account::AddressLookupTableAccount,
      };
      
      // 1. Create your instructions (can reference >35 accounts)
      let instructions = vec![
          // Your program instructions
      ];
      
      // 2. Fetch lookup table accounts
      let lookup_table_account = rpc_client.get_account(&lookup_table_address)?;
      let lookup_table = AddressLookupTableAccount::deserialize(&lookup_table_account.data)?;
      
      // 3. Build v0 message
      let v0_message = v0::Message::try_compile(
          &payer.pubkey(),
          &instructions,
          &[lookup_table],    // Pass lookup tables here
          recent_blockhash,
      )?;
      
      // 4. Create versioned transaction
      let versioned_tx = VersionedTransaction::try_new(
          VersionedMessage::V0(v0_message),
          &[&payer],          // Signers
      )?;
      
      // 5. Send transaction
      let signature = rpc_client.send_and_confirm_transaction(&versioned_tx)?;
      ```
      
      **How accounts are referenced:**
      
      When you create an instruction with accounts that exist in the ALT:
      ```rust
      use solana_sdk::instruction::{AccountMeta, Instruction};
      
      // These accounts are in the lookup table at indices 0, 1, 2
      let account_in_alt_0 = Pubkey::new_unique();
      let account_in_alt_1 = Pubkey::new_unique();
      let account_in_alt_2 = Pubkey::new_unique();
      
      let ix = Instruction::new_with_bytes(
          program_id,
          &instruction_data,
          vec![
              AccountMeta::new(account_in_alt_0, false),      // Index 0 in ALT
              AccountMeta::new_readonly(account_in_alt_1, false),  // Index 1
              AccountMeta::new(account_in_alt_2, false),      // Index 2
          ],
      );
      
      // When compiled with ALT, these become 1-byte indices instead of 32-byte addresses
      ```
      
      ### Deactivating and Closing ALTs
      
      **Deactivation:**
      
      ```rust
      let deactivate_ix = alt_instruction::deactivate_lookup_table(
          lookup_table_address,
          authority.pubkey(),
      );
      
      rpc_client.send_and_confirm_transaction(&tx)?;
      ```
      
      **Why deactivate?**
      - Prevents the table from being used in new transactions
      - Required before closing
      - Creates a safety cooldown period
      
      **Cooldown period:**
      - Must wait until the deactivation slot exits the slot hashes sysvar (~2.5 days on mainnet)
      - Prevents same-slot recreation attacks
      - Ensures no in-flight transactions reference the table
      
      **Closing:**
      
      ```rust
      let close_ix = alt_instruction::close_lookup_table(
          lookup_table_address,
          authority.pubkey(),
          recipient.pubkey(),  // Receives reclaimed rent
      );
      
      rpc_client.send_and_confirm_transaction(&tx)?;
      ```
      
      **Requirements:**
      - Table must be deactivated first
      - Deactivation slot must have exited slot hashes sysvar
      - Only authority can close
      - Rent is returned to specified recipient
      
      ### Freezing ALTs (Making Immutable)
      
      ```rust
      let freeze_ix = alt_instruction::freeze_lookup_table(
          lookup_table_address,
          authority.pubkey(),
      );
      
      rpc_client.send_and_confirm_transaction(&tx)?;
      ```
      
      **Effect:**
      - Sets authority to `None`
      - Table becomes **permanently immutable**
      - Cannot add more addresses
      - Cannot deactivate or close
      - Useful for protocol-level tables that should never change
      
      ## RPC Configuration for V0 Transactions
      
      **Critical requirement**: When fetching transactions, you must specify support for versioned transactions:
      
      ```rust
      use solana_client::rpc_config::RpcTransactionConfig;
      use solana_transaction_status::UiTransactionEncoding;
      
      let config = RpcTransactionConfig {
          encoding: Some(UiTransactionEncoding::Json),
          commitment: Some(CommitmentConfig::confirmed()),
          max_supported_transaction_version: Some(0),  // REQUIRED!
      };
      
      let tx = rpc_client.get_transaction_with_config(&signature, config)?;
      ```
      
      **Without `max_supported_transaction_version: Some(0)`:**
      - RPC calls will **fail** if they encounter a v0 transaction
      - Error: "Transaction version is not supported"
      - This affects: `getTransaction`, `getBlock`, `getSignaturesForAddress`, etc.
      
      **For account subscriptions:**
      ```rust
      use solana_client::rpc_config::RpcAccountInfoConfig;
      
      let config = RpcAccountInfoConfig {
          encoding: Some(UiAccountEncoding::JsonParsed),
          commitment: Some(CommitmentConfig::confirmed()),
          // No max_supported_transaction_version needed for account queries
      };
      ```
      
      ## Limitations and Constraints
      
      ### Hard Limits
      
      1. **256 addresses per table** (u8 index limit)
         - Tables use 1-byte indices
         - Cannot store more than 256 addresses
         - Create multiple tables if needed
      
      2. **256 unique accounts total per transaction**
         - Solana runtime limit
         - Includes both direct accounts and ALT references
         - Accounts can appear multiple times in instructions
      
      3. **~20 addresses per extend operation**
         - Limited by transaction size
         - Must batch large address sets
      
      4. **Transaction signers cannot be in ALTs**
         - All signers must be explicitly listed in the transaction
         - Cannot reference signer accounts from lookup tables
         - This is a security feature
      
      5. **No recursive lookups**
         - Cannot reference another ALT from within an ALT
         - Cannot store ALT addresses in an ALT
      
      ### Security Constraints
      
      1. **Append-only design**
         - Addresses cannot be removed or modified
         - Prevents front-running attacks
         - Once added, addresses are permanent (until table is closed)
      
      2. **Warmup requirement**
         - New addresses need 1 slot before use
         - Prevents same-slot manipulation
         - Must wait for finalization
      
      3. **Deactivation cooldown**
         - Tables cannot be closed immediately after deactivation
         - Must wait for slot to exit slot hashes sysvar
         - Protects in-flight transactions
      
      4. **Authority control**
         - Only authority can extend or deactivate
         - Set to `None` to make immutable
         - Cannot change authority after freezing
      
      ### Hardware Wallet Limitations
      
      **Issue**: Hardware wallets cannot verify accounts referenced from ALTs
      
      **Why:**
      - Hardware wallets display all transaction accounts for user verification
      - They don't have access to fetch lookup table data on-chain
      - Cannot show which addresses the indices reference
      
      **Implications:**
      - Users must trust that the correct lookup table is being used
      - Phishing risk: Malicious apps could use attacker-controlled ALTs
      - Hardware wallet UX shows: "This transaction uses address lookup tables"
      
      **Mitigations:**
      - Use well-known, immutable (frozen) ALTs when possible
      - Publish ALT addresses in protocol documentation
      - Verify ALT contents before use in client code
      - Consider adding integrity check instructions
      
      ## Security Best Practices
      
      ### 1. Wait for Finalization
      
      ```rust
      // BAD: Using immediately after creation
      let (create_ix, alt_address) = alt_instruction::create_lookup_table(...);
      rpc_client.send_transaction(&create_tx)?;  // Not confirmed!
      let extend_ix = alt_instruction::extend_lookup_table(alt_address, ...);  // FAILS!
      
      // GOOD: Wait for finalization
      rpc_client.send_and_confirm_transaction_with_spinner(&create_tx)?;
      // Now safe to extend
      
      rpc_client.send_and_confirm_transaction_with_spinner(&extend_tx)?;
      // Now safe to use in v0 transactions
      ```
      
      ### 2. Verify Lookup Table Contents
      
      ```rust
      // Fetch and verify before use
      let lookup_table = rpc_client.get_account(&alt_address)?;
      let alt = AddressLookupTableAccount::deserialize(&lookup_table.data)?;
      
      // Verify expected addresses
      assert_eq!(alt.addresses.len(), expected_count);
      assert_eq!(alt.addresses[0], expected_address_0);
      
      // Check authority if relevant
      if let Some(authority) = alt.meta.authority {
          assert_eq!(authority, expected_authority);
      }
      ```
      
      ### 3. Add Integrity Check Instructions
      
      For critical operations, add an instruction that verifies the lookup table contents:
      
      ```rust
      // Your program instruction
      pub fn verify_lookup_table(
          ctx: Context<VerifyLookupTable>,
          expected_addresses: Vec<Pubkey>,
      ) -> Result<()> {
          let lookup_table = &ctx.accounts.lookup_table;
      
          // Verify table contains expected addresses
          for (i, expected) in expected_addresses.iter().enumerate() {
              require_keys_eq!(
                  lookup_table.addresses[i],
                  *expected,
                  ErrorCode::InvalidLookupTable
              );
          }
      
          Ok(())
      }
      ```
      
      ### 4. Use Immutable Tables for Protocols
      
      ```rust
      // After fully populating a protocol-level table
      let freeze_ix = alt_instruction::freeze_lookup_table(
          protocol_alt_address,
          authority.pubkey(),
      );
      
      rpc_client.send_and_confirm_transaction(&freeze_tx)?;
      
      // Now the table is permanently immutable
      // Users can trust it won't change
      ```
      
      ### 5. Front-Running Prevention
      
      **Why ALTs are append-only:**
      
      ```rust
      // If removal were allowed, this attack would be possible:
      // 1. User submits swap transaction using ALT at index 5
      // 2. Attacker sees pending transaction
      // 3. Attacker removes legitimate address, adds malicious address at index 5
      // 4. User's transaction executes with malicious address
      
      // Append-only design prevents this:
      // - Addresses cannot be removed
      // - Indices remain stable
      // - Order cannot change
      ```
      
      ## Code Examples
      
      ### Complete Example: Multi-Swap with ALT
      
      Based on the TeamRaccoons address-lookup-table-multi-swap example:
      
      ```rust
      use solana_client::rpc_client::RpcClient;
      use solana_sdk::{
          address_lookup_table_account::instruction as alt_instruction,
          address_lookup_table_account::AddressLookupTableAccount,
          commitment_config::CommitmentConfig,
          instruction::Instruction,
          message::{v0, VersionedMessage},
          pubkey::Pubkey,
          signature::{Keypair, Signer},
          transaction::{Transaction, VersionedTransaction},
      };
      
      fn create_and_use_alt_for_swaps() -> Result<()> {
          let rpc_client = RpcClient::new_with_commitment(
              "https://api.devnet.solana.com".to_string(),
              CommitmentConfig::confirmed(),
          );
      
          let payer = Keypair::new();
          // Fund payer...
      
          // Step 1: Collect all accounts needed for swap chain
          let swap_accounts = vec![
              token_program_id,
              associated_token_program_id,
              swap_program_1,
              pool_1_address,
              pool_1_authority,
              pool_1_token_a,
              pool_1_token_b,
              swap_program_2,
              pool_2_address,
              pool_2_authority,
              pool_2_token_a,
              pool_2_token_b,
              // ... many more accounts
          ];
      
          // Step 2: Create lookup table
          let recent_slot = rpc_client.get_slot()?;
          let (create_ix, alt_address) = alt_instruction::create_lookup_table(
              payer.pubkey(),
              payer.pubkey(),
              recent_slot,
          );
      
          let recent_blockhash = rpc_client.get_latest_blockhash()?;
          let create_tx = Transaction::new_signed_with_payer(
              &[create_ix],
              Some(&payer.pubkey()),
              &[&payer],
              recent_blockhash,
          );
      
          rpc_client.send_and_confirm_transaction_with_spinner(&create_tx)?;
          println!("Created ALT at {}", alt_address);
      
          // Step 3: Extend in batches of 20
          for (batch_num, chunk) in swap_accounts.chunks(20).enumerate() {
              let extend_ix = alt_instruction::extend_lookup_table(
                  alt_address,
                  payer.pubkey(),
                  Some(payer.pubkey()),
                  chunk.to_vec(),
              );
      
              let recent_blockhash = rpc_client.get_latest_blockhash()?;
              let extend_tx = Transaction::new_signed_with_payer(
                  &[extend_ix],
                  Some(&payer.pubkey()),
                  &[&payer],
                  recent_blockhash,
              );
      
              rpc_client.send_and_confirm_transaction_with_spinner(&extend_tx)?;
              println!("Extended ALT batch {}", batch_num);
          }
      
          // Step 4: Fetch the populated lookup table
          let alt_account = rpc_client.get_account(&alt_address)?;
          let lookup_table = AddressLookupTableAccount::deserialize(&alt_account.data)?;
      
          println!("ALT contains {} addresses", lookup_table.addresses.len());
      
          // Step 5: Build multi-swap transaction using ALT
          let swap_instructions = vec![
              create_swap_instruction(0, 1, 2, 3, 4, 5, 6),   // Indices into ALT
              create_swap_instruction(7, 8, 9, 10, 11, 12, 13),
              create_swap_instruction(14, 15, 16, 17, 18, 19, 20),
              // Many more swaps...
          ];
      
          let recent_blockhash = rpc_client.get_latest_blockhash()?;
          let v0_message = v0::Message::try_compile(
              &payer.pubkey(),
              &swap_instructions,
              &[lookup_table],
              recent_blockhash,
          )?;
      
          let versioned_tx = VersionedTransaction::try_new(
              VersionedMessage::V0(v0_message),
              &[&payer],
          )?;
      
          // Step 6: Send v0 transaction
          let signature = rpc_client.send_and_confirm_transaction(&versioned_tx)?;
          println!("Multi-swap completed: {}", signature);
      
          Ok(())
      }
      
      fn create_swap_instruction(
          swap_program: u8,
          pool: u8,
          authority: u8,
          source: u8,
          dest: u8,
          pool_token_a: u8,
          pool_token_b: u8,
      ) -> Instruction {
          // Create instruction with account indices
          // These will be resolved from the ALT
          Instruction {
              program_id: /* from ALT index swap_program */,
              accounts: vec![
                  AccountMeta::new(/* ALT index pool */, false),
                  AccountMeta::new_readonly(/* ALT index authority */, false),
                  // ... etc
              ],
              data: /* swap instruction data */,
          }
      }
      ```
      
      ### Example: Protocol-Level Immutable ALT
      
      ```rust
      // Create a permanent lookup table for protocol accounts
      fn create_protocol_alt(
          authority: &Keypair,
          protocol_accounts: Vec<Pubkey>,
      ) -> Result<Pubkey> {
          let rpc_client = RpcClient::new("https://api.mainnet-beta.solana.com");
      
          // Create table
          let recent_slot = rpc_client.get_slot()?;
          let (create_ix, alt_address) = alt_instruction::create_lookup_table(
              authority.pubkey(),
              authority.pubkey(),
              recent_slot,
          );
      
          let create_tx = /* ... */;
          rpc_client.send_and_confirm_transaction_with_spinner(&create_tx)?;
      
          // Extend with all protocol accounts
          for chunk in protocol_accounts.chunks(20) {
              let extend_ix = alt_instruction::extend_lookup_table(
                  alt_address,
                  authority.pubkey(),
                  Some(authority.pubkey()),
                  chunk.to_vec(),
              );
      
              let extend_tx = /* ... */;
              rpc_client.send_and_confirm_transaction_with_spinner(&extend_tx)?;
          }
      
          // Freeze the table (make immutable)
          let freeze_ix = alt_instruction::freeze_lookup_table(
              alt_address,
              authority.pubkey(),
          );
      
          let freeze_tx = /* ... */;
          rpc_client.send_and_confirm_transaction_with_spinner(&freeze_tx)?;
      
          println!("Created immutable protocol ALT at {}", alt_address);
      
          // Publish this address in documentation
          // Users can trust it won't change
      
          Ok(alt_address)
      }
      ```
      
      ## Troubleshooting
      
      ### Common Errors and Solutions
      
      **Error: "Transaction version is not supported"**
      ```rust
      // Problem: RPC not configured for v0 transactions
      let tx = rpc_client.get_transaction(&signature)?;  // FAILS
      
      // Solution: Set max_supported_transaction_version
      let config = RpcTransactionConfig {
          max_supported_transaction_version: Some(0),
          ..Default::default()
      };
      let tx = rpc_client.get_transaction_with_config(&signature, config)?;  // Works
      ```
      
      **Error: "Address lookup table not found"**
      ```rust
      // Problem: Using table before creation is finalized
      let (create_ix, alt_address) = alt_instruction::create_lookup_table(...);
      rpc_client.send_transaction(&tx)?;  // Sent but not confirmed
      let extend_ix = alt_instruction::extend_lookup_table(alt_address, ...);  // FAILS
      
      // Solution: Wait for confirmation
      rpc_client.send_and_confirm_transaction_with_spinner(&create_tx)?;
      // Now table exists
      ```
      
      **Error: "Invalid lookup table index"**
      ```rust
      // Problem: Referencing index beyond table size
      let lookup_table = /* has 10 addresses */;
      let ix = Instruction {
          accounts: vec![
              AccountMeta::new(/* index 15 */, false),  // FAILS - index out of bounds
          ],
          // ...
      };
      
      // Solution: Verify table contents and use valid indices
      assert!(index < lookup_table.addresses.len());
      ```
      
      **Error: "Cannot deactivate lookup table"**
      ```rust
      // Problem: Not the authority
      let deactivate_ix = alt_instruction::deactivate_lookup_table(
          alt_address,
          wrong_authority.pubkey(),  // Not the actual authority
      );
      
      // Solution: Use the correct authority
      let alt = AddressLookupTableAccount::deserialize(&account.data)?;
      let correct_authority = alt.meta.authority.expect("Table has no authority");
      let deactivate_ix = alt_instruction::deactivate_lookup_table(
          alt_address,
          correct_authority,
      );
      ```
      
      **Error: "Cannot close lookup table"**
      ```rust
      // Problem 1: Table not deactivated
      let close_ix = alt_instruction::close_lookup_table(...);  // FAILS
      
      // Solution: Deactivate first, then wait
      let deactivate_ix = alt_instruction::deactivate_lookup_table(...);
      // ... send deactivate transaction ...
      // ... wait for cooldown period (~2.5 days mainnet) ...
      let close_ix = alt_instruction::close_lookup_table(...);
      
      // Problem 2: Cooldown period not complete
      // Solution: Check if deactivation slot has exited slot hashes
      let slot_hashes = rpc_client.get_slot_hashes()?;
      let oldest_slot = slot_hashes.last().unwrap().0;
      if alt.meta.deactivation_slot < oldest_slot {
          // Safe to close
      }
      ```
      
      ## Use Cases and Patterns
      
      ### 1. DEX Aggregators
      
      **Problem**: Multi-hop swaps require many accounts (pools, authorities, token accounts)
      
      **Solution**: Create ALT with all pool accounts
      
      ```rust
      // ALT contains:
      // [0-19]: Pool 1 accounts (program, pool, authority, tokens, mint, etc.)
      // [20-39]: Pool 2 accounts
      // [40-59]: Pool 3 accounts
      // [60-79]: Common accounts (token program, associated token program, etc.)
      
      // Transaction can now execute 3+ swaps atomically
      ```
      
      ### 2. Complex Protocol Interactions
      
      **Problem**: DeFi protocols compose multiple programs (lending, swapping, staking)
      
      **Solution**: Protocol-specific ALT with all contract addresses
      
      ```rust
      // Protocol ALT:
      // [0]: Program ID
      // [1]: Global config account
      // [2-10]: Pool addresses
      // [11-20]: Oracle addresses
      // [21-30]: Treasury accounts
      // etc.
      ```
      
      ### 3. NFT Minting/Trading
      
      **Problem**: Minting or trading multiple NFTs requires many metadata accounts
      
      **Solution**: Collection-specific ALT with all related accounts
      
      ```rust
      // Collection ALT:
      // [0]: Candy machine
      // [1]: Collection mint
      // [2]: Collection metadata
      // [3]: Collection master edition
      // [4-100]: Individual NFT addresses
      ```
      
      ### 4. Transaction Builder Programs
      
      **Problem**: Building very large transactions (>64 accounts)
      
      **Solution**: Multi-transaction pattern with ALTs
      
      ```rust
      // Transaction 1: Create and populate ALT
      // Transaction 2: Execute main operation using ALT
      // Transaction 3: Clean up and close ALT
      ```
      
      ## Best Practices Summary
      
      1. **Always wait for finalization** before using newly created or extended tables
      2. **Batch extend operations** in chunks of ~20 addresses
      3. **Verify table contents** before use in production
      4. **Use immutable tables** for protocol-level accounts
      5. **Set max_supported_transaction_version** in all RPC calls
      6. **Document ALT addresses** for protocol integrators
      7. **Consider hardware wallet UX** - frozen tables are more trustworthy
      8. **Add integrity checks** for critical operations
      9. **Plan for cooldown** when closing tables
      10. **Keep signers explicit** - never try to put signers in ALTs
      
      ## Program Compatibility
      
      **Important**: Programs are **completely unaware** of whether they were called via legacy or v0 transactions.
      
      From the program's perspective:
      - Account references work identically
      - No code changes needed
      - Same `AccountInfo` structures
      - Same validation logic
      
      The transaction version only affects:
      - How accounts are referenced in the transaction
      - Transaction size limits
      - Client-side transaction construction
      
      **This means:**
      - Existing programs work with v0 transactions without modification
      - New programs don't need version-specific logic
      - ALTs are purely a client-side optimization
      
      ## Resources
      
      ### Official Documentation
      - [Versioned Transactions Guide](https://solana.com/developers/guides/advanced/versions)
      - [Address Lookup Tables Guide](https://solana.com/developers/guides/advanced/lookup-tables)
      - [Versioned Transactions Proposal](https://docs.anza.xyz/proposals/versioned-transactions)
      
      ### Code Examples
      - [TeamRaccoons Multi-Swap Example](https://github.com/TeamRaccoons/address-lookup-table-multi-swap)
      - [Solana Program Library - Address Lookup Table](https://github.com/solana-labs/solana-program-library/tree/master/address-lookup-table)
      
      ### Technical References
      - [AddressLookupTableProgram Source](https://github.com/solana-labs/solana/blob/master/sdk/program/src/address_lookup_table/instruction.rs)
      - [solana-sdk VersionedTransaction](https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.VersionedTransaction.html)
      - [solana-sdk Message v0](https://docs.rs/solana-sdk/latest/solana_sdk/message/v0/struct.Message.html)
      
      ### Community Resources
      - [Solana Cookbook - Versioned Transactions](https://solanacookbook.com/references/basic-transactions.html#versioned-transactions)
      - [Solana Stack Exchange - ALT Questions](https://solana.stackexchange.com/questions/tagged/address-lookup-table)
      
    • vulnerability-patterns.md 12.5 KB
      # Common Vulnerability Patterns
      
      Detailed examples of common Solana smart contract vulnerabilities with exploit scenarios and secure alternatives.
      
      ## 1. Missing Signer Validation
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
          // No check that caller is authorized!
          let vault = &mut ctx.accounts.vault;
          vault.balance -= amount;
          Ok(())
      }
      
      #[derive(Accounts)]
      pub struct Withdraw<'info> {
          #[account(mut)]
          pub vault: Account<'info, Vault>,
          pub user: AccountInfo<'info>,  // Not a Signer!
      }
      ```
      
      ### Exploit Scenario
      Attacker can drain the vault by calling `withdraw` with any account as the `user` parameter. No signature verification means anyone can execute the instruction.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      #[derive(Accounts)]
      pub struct Withdraw<'info> {
          #[account(
              mut,
              has_one = authority,  // Ensures vault.authority == authority.key()
          )]
          pub vault: Account<'info, Vault>,
          pub authority: Signer<'info>,  // Must sign transaction
      }
      ```
      
      ## 2. Integer Overflow/Underflow
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
          let vault = &mut ctx.accounts.vault;
          vault.balance = vault.balance + amount;  // Can overflow!
          Ok(())
      }
      ```
      
      ### Exploit Scenario
      If `vault.balance = u64::MAX - 100` and `amount = 200`, the addition overflows and wraps to `99`, effectively stealing funds from the vault.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
          let vault = &mut ctx.accounts.vault;
          vault.balance = vault
              .balance
              .checked_add(amount)
              .ok_or(ErrorCode::Overflow)?;
          Ok(())
      }
      ```
      
      ## 3. PDA Substitution Attack
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      #[derive(Accounts)]
      pub struct Transfer<'info> {
          #[account(mut)]
          pub config: Account<'info, Config>,  // No PDA validation!
      
          #[account(
              mut,
              seeds = [b"vault", config.key().as_ref()],  // Uses unvalidated config
              bump
          )]
          pub vault: Account<'info, Vault>,
      }
      ```
      
      ### Exploit Scenario
      Attacker creates a fake `config` account with malicious settings. The `vault` PDA is derived from this fake config, potentially accessing wrong vault or bypassing security checks.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      #[derive(Accounts)]
      pub struct Transfer<'info> {
          #[account(
              seeds = [b"config"],  // Global config PDA
              bump,
          )]
          pub config: Account<'info, Config>,
      
          #[account(
              mut,
              seeds = [b"vault", config.key().as_ref()],
              bump
          )]
          pub vault: Account<'info, Vault>,
      }
      ```
      
      ## 4. Type Cosplay Attack
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      #[account(mut)]
      pub user: AccountLoader<'info, User>,  // Doesn't check discriminator!
      ```
      
      ### Exploit Scenario
      Attacker passes a `UserAdmin` account instead of `User`. Since `AccountLoader` doesn't check discriminators by default, the program treats the admin account as a regular user, potentially bypassing privilege checks.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      #[account(mut)]
      pub user: Account<'info, User>,  // Enforces correct discriminator
      ```
      
      ## 5. Account Reloading Issues
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      pub fn complex_operation(ctx: Context<ComplexOp>) -> Result<()> {
          let initial_balance = ctx.accounts.vault.balance;
      
          // CPI that modifies vault
          transfer_tokens(&ctx)?;
      
          // Still using stale balance!
          require!(
              ctx.accounts.vault.balance >= initial_balance,
              ErrorCode::InvalidBalance
          );
      
          Ok(())
      }
      ```
      
      ### Exploit Scenario
      The `balance` value is cached from before the CPI. If the CPI modified the vault, the check uses stale data, potentially allowing invalid state transitions.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      pub fn complex_operation(ctx: Context<ComplexOp>) -> Result<()> {
          transfer_tokens(&ctx)?;
      
          // Reload account to get fresh data
          ctx.accounts.vault.reload()?;
      
          require!(
              ctx.accounts.vault.balance >= expected_balance,
              ErrorCode::InvalidBalance
          );
      
          Ok(())
      }
      ```
      
      ## 6. Improper Account Closing
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      pub fn close_account(ctx: Context<CloseAccount>) -> Result<()> {
          **ctx.accounts.vault.to_account_info().lamports.borrow_mut() = 0;
          // Data not zeroed, authority not reset!
          Ok(())
      }
      ```
      
      ### Exploit Scenario
      Account data remains accessible within the same transaction even after lamports are zeroed. Attacker can read sensitive data or reuse the account in unexpected ways.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      #[derive(Accounts)]
      pub struct CloseAccount<'info> {
          #[account(
              mut,
              close = receiver  // Properly closes: transfers lamports, zeros data
          )]
          pub vault: Account<'info, Vault>,
          #[account(mut)]
          pub receiver: SystemAccount<'info>,
      }
      ```
      
      ## 7. Missing Lamports Check
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      pub fn process(ctx: Context<Process>) -> Result<()> {
          let data = ctx.accounts.user_data.load()?;  // Can read closed account!
          // ... use data
          Ok(())
      }
      ```
      
      ### Exploit Scenario
      Account was closed earlier in the transaction but data is still readable. Processing closed account data can lead to inconsistent state or bypass business logic.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      pub fn process(ctx: Context<Process>) -> Result<()> {
          require!(
              **ctx.accounts.user_data.to_account_info().lamports.borrow() > 0,
              ErrorCode::AccountClosed
          );
      
          let data = ctx.accounts.user_data.load()?;
          // ... use data
          Ok(())
      }
      ```
      
      ## 8. Arbitrary CPI
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      #[derive(Accounts)]
      pub struct ArbitraryCPI<'info> {
          pub token_program: AccountInfo<'info>,  // Not validated!
      }
      
      pub fn transfer(ctx: Context<ArbitraryCPI>) -> Result<()> {
          invoke(
              &transfer_instruction,
              &[
                  ctx.accounts.token_program.clone(),  // Could be malicious!
                  // ...
              ]
          )?;
          Ok(())
      }
      ```
      
      ### Exploit Scenario
      Attacker passes malicious program instead of real Token program. Malicious program can emit fake events, return success without transferring, or drain funds.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      #[derive(Accounts)]
      pub struct SecureCPI<'info> {
          pub token_program: Program<'info, Token>,  // Type-checked!
      }
      
      // Or manual validation
      require_keys_eq!(
          *ctx.accounts.token_program.key,
          spl_token::ID,
          ErrorCode::InvalidTokenProgram
      );
      ```
      
      ## 9. Duplicate Mutable Accounts
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      #[derive(Accounts)]
      pub struct Transfer<'info> {
          #[account(mut)]
          pub from: Account<'info, TokenAccount>,
          #[account(mut)]
          pub to: Account<'info, TokenAccount>,
      }
      
      pub fn transfer(ctx: Context<Transfer>, amount: u64) -> Result<()> {
          ctx.accounts.from.amount -= amount;
          ctx.accounts.to.amount += amount;  // Same account = double amount!
          Ok(())
      }
      ```
      
      ### Exploit Scenario
      If `from` and `to` are the same account, the user can double their balance by transferring to themselves.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      #[derive(Accounts)]
      pub struct Transfer<'info> {
          #[account(
              mut,
              constraint = from.key() != to.key() @ ErrorCode::SameAccount
          )]
          pub from: Account<'info, TokenAccount>,
          #[account(mut)]
          pub to: Account<'info, TokenAccount>,
      }
      ```
      
      ## 10. Bump Seed Canonicalization
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      pub fn init_vault(ctx: Context<InitVault>, bump: u8) -> Result<()> {
          // Accepts any bump from user!
          let seeds = &[b"vault", user.key().as_ref(), &[bump]];
          // Multiple PDAs possible for same seeds!
      }
      ```
      
      ### Exploit Scenario
      Attacker can create multiple vault PDAs with different bumps for the same user, fragmenting state or confusing off-chain systems.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      #[derive(Accounts)]
      pub struct InitVault<'info> {
          #[account(
              init,
              payer = user,
              space = 8 + Vault::INIT_SPACE,
              seeds = [b"vault", user.key().as_ref()],
              bump  // Anchor derives and stores canonical bump automatically
          )]
          pub vault: Account<'info, Vault>,
          #[account(mut)]
          pub user: Signer<'info>,
          pub system_program: Program<'info, System>,
      }
      ```
      
      ## 11. Missing Owner Check
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      pub fn read_data(ctx: Context<ReadData>) -> Result<()> {
          let oracle_data = ctx.accounts.oracle.try_borrow_data()?;
          // No check that oracle is owned by Pyth program!
          let price = parse_price(&oracle_data)?;
          Ok(())
      }
      ```
      
      ### Exploit Scenario
      Attacker creates fake oracle account owned by their own program, filled with manipulated price data. Program trusts the fake data.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      pub fn read_data(ctx: Context<ReadData>) -> Result<()> {
          require_keys_eq!(
              *ctx.accounts.oracle.owner,
              PYTH_PROGRAM_ID,
              ErrorCode::InvalidOracleOwner
          );
      
          let oracle_data = ctx.accounts.oracle.try_borrow_data()?;
          let price = parse_price(&oracle_data)?;
          Ok(())
      }
      ```
      
      ## 12. Precision Loss / Rounding Errors
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      pub fn calculate_shares(collateral: u64, rate: Decimal) -> Result<u64> {
          Decimal::from(collateral)
              .try_div(rate)?
              .try_round_u64()  // Rounding can be exploited!
      }
      ```
      
      ### Exploit Scenario
      Attacker repeatedly deposits/withdraws small amounts. Rounding up gives slightly more shares each time, slowly draining the pool.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      pub fn calculate_shares(collateral: u64, rate: Decimal) -> Result<u64> {
          Decimal::from(collateral)
              .try_div(rate)?
              .try_floor_u64()  // Always round down in user's favor
      }
      ```
      
      ## 13. Unchecked Error Returns
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      spl_token::instruction::transfer(
          token_program.key,
          source.key,
          destination.key,
          authority.key,
          &[],
          amount,
      );  // Return value ignored!
      ```
      
      ### Exploit Scenario
      Transfer instruction fails silently but program continues as if it succeeded. State becomes inconsistent with actual token balances.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      invoke(
          &spl_token::instruction::transfer(
              token_program.key,
              source.key,
              destination.key,
              authority.key,
              &[],
              amount,
          )?,  // Propagates error
          &[
              source.clone(),
              destination.clone(),
              authority.clone(),
          ],
      )?;
      
      // Or use Anchor's CPI helpers
      token::transfer(ctx, amount)?;
      ```
      
      ## 14. Init If Needed Vulnerability
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      #[account(
          init_if_needed,
          payer = user,
          space = 8 + Account::INIT_SPACE
      )]
      pub user_account: Account<'info, UserAccount>,
      ```
      
      ### Exploit Scenario
      If account already exists, initialization is skipped but existing data might be inconsistent or malicious. Can bypass initialization checks.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE - Explicit initialization
      #[account(
          init,
          payer = user,
          space = 8 + Account::INIT_SPACE
      )]
      pub user_account: Account<'info, UserAccount>,
      
      // Or if init_if_needed is truly needed, add validation
      pub fn init_or_validate(ctx: Context<InitAccount>) -> Result<()> {
          if ctx.accounts.user_account.is_initialized {
              // Validate existing data
              require!(
                  ctx.accounts.user_account.owner == ctx.accounts.user.key(),
                  ErrorCode::InvalidOwner
              );
          } else {
              // Initialize new account
              ctx.accounts.user_account.is_initialized = true;
              ctx.accounts.user_account.owner = ctx.accounts.user.key();
          }
          Ok(())
      }
      ```
      
      ## 15. Stale Oracle Data
      
      ### Vulnerability
      ```rust
      // ❌ VULNERABLE
      pub fn get_price(pyth_account: &AccountInfo) -> Result<i64> {
          let price_feed = load_price_feed(pyth_account)?;
          Ok(price_feed.agg.price)  // No staleness check!
      }
      ```
      
      ### Exploit Scenario
      Oracle stopped updating hours ago due to network issues. Attacker exploits stale price to buy/sell at favorable outdated rates.
      
      ### Secure Alternative
      ```rust
      // ✅ SECURE
      pub fn get_price(
          pyth_account: &AccountInfo,
          clock: &Clock
      ) -> Result<i64> {
          let price_feed = load_price_feed(pyth_account)?;
      
          // Check publishing time
          let max_age_seconds = 60;
          require!(
              clock.unix_timestamp - price_feed.agg.publish_time <= max_age_seconds,
              ErrorCode::StaleOraclePrice
          );
      
          // Check status
          require!(
              price_feed.agg.status == PriceStatus::Trading,
              ErrorCode::OracleNotTrading
          );
      
          Ok(price_feed.agg.price)
      }
      ```
      
  • LICENSE.txt 8.9 KB
    Apache License
    Version 2.0, January 2004
    https://www.apache.org/licenses/
    
    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    
    1. Definitions.
    
    "License" shall mean the terms and conditions for use, reproduction, and
    distribution as defined by Sections 1 through 9 of this document.
    
    "Licensor" shall mean the copyright owner or entity authorized by the
    copyright owner that is granting the License.
    
    "Legal Entity" shall mean the union of the acting entity and all other
    entities that control, are controlled by, or are under common control with
    that entity. For the purposes of this definition, "control" means (i) the
    power, direct or indirect, to cause the direction or management of such
    entity, whether by contract or otherwise, or (ii) ownership of fifty percent
    (50%) or more of the outstanding shares, or (iii) beneficial ownership of
    such entity.
    
    "You" (or "Your") shall mean an individual or Legal Entity exercising
    permissions granted by this License.
    
    "Source" form shall mean the preferred form for making modifications,
    including but not limited to software source code, documentation source, and
    configuration files.
    
    "Object" form shall mean any form resulting from mechanical transformation or
    translation of a Source form, including but not limited to compiled object
    code, generated documentation, and conversions to other media types.
    
    "Work" shall mean the work of authorship, whether in Source or Object form,
    made available under the License, as indicated by a copyright notice that is
    included in or attached to the work (an example is provided in the Appendix
    below).
    
    "Derivative Works" shall mean any work, whether in Source or Object form,
    that is based on (or derived from) the Work and for which the editorial
    revisions, annotations, elaborations, or other modifications represent, as a
    whole, an original work of authorship. For the purposes of this License,
    Derivative Works shall not include works that remain separable from, or
    merely link (or bind by name) to the interfaces of, the Work and Derivative
    Works thereof.
    
    "Contribution" shall mean any work of authorship, including the original
    version of the Work and any modifications or additions to that Work or
    Derivative Works thereof, that is intentionally submitted to Licensor for
    inclusion in the Work by the copyright owner or by an individual or Legal
    Entity authorized to submit on behalf of the copyright owner. For the
    purposes of this definition, "submitted" means any form of electronic, verbal,
    or written communication sent to the Licensor or its representatives,
    including but not limited to communication on electronic mailing lists, source
    code control systems, and issue tracking systems that are managed by, or on
    behalf of, the Licensor for the purpose of discussing and improving the Work,
    but excluding communication that is conspicuously marked or otherwise
    designated in writing by the copyright owner as "Not a Contribution."
    
    "Contributor" shall mean Licensor and any individual or Legal Entity on
    behalf of whom a Contribution has been received by Licensor and subsequently
    incorporated within the Work.
    
    2. Grant of Copyright License. Subject to the terms and conditions of this
    License, each Contributor hereby grants to You a perpetual, worldwide,
    non-exclusive, no-charge, royalty-free, irrevocable copyright license to
    reproduce, prepare Derivative Works of, publicly display, publicly perform,
    sublicense, and distribute the Work and such Derivative Works in Source or
    Object form.
    
    3. Grant of Patent License. Subject to the terms and conditions of this
    License, each Contributor hereby grants to You a perpetual, worldwide,
    non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this
    section) patent license to make, have made, use, offer to sell, sell, import,
    and otherwise transfer the Work, where such license applies only to those
    patent claims licensable by such Contributor that are necessarily infringed by
    their Contribution(s) alone or by combination of their Contribution(s) with
    the Work to which such Contribution(s) was submitted. If You institute patent
    litigation against any entity (including a cross-claim or counterclaim in a
    lawsuit) alleging that the Work or a Contribution incorporated within the Work
    constitutes direct or contributory patent infringement, then any patent
    licenses granted to You under this License for that Work shall terminate as of
    the date such litigation is filed.
    
    4. Redistribution. You may reproduce and distribute copies of the Work or
    Derivative Works thereof in any medium, with or without modifications, and in
    Source or Object form, provided that You meet the following conditions:
    
    (a) You must give any other recipients of the Work or Derivative Works a copy
    of this License; and
    
    (b) You must cause any modified files to carry prominent notices stating that
    You changed the files; and
    
    (c) You must retain, in the Source form of any Derivative Works that You
    distribute, all copyright, patent, trademark, and attribution notices from
    the Source form of the Work, excluding those notices that do not pertain to
    any part of the Derivative Works; and
    
    (d) If the Work includes a "NOTICE" text file as part of its distribution,
    then any Derivative Works that You distribute must include a readable copy of
    the attribution notices contained within such NOTICE file, excluding those
    notices that do not pertain to any part of the Derivative Works, in at least
    one of the following places: within a NOTICE text file distributed as part of
    the Derivative Works; within the Source form or documentation, if provided
    along with the Derivative Works; or, within a display generated by the
    Derivative Works, if and wherever such third-party notices normally appear.
    The contents of the NOTICE file are for informational purposes only and do not
    modify the License. You may add Your own attribution notices within Derivative
    Works that You distribute, alongside or as an addendum to the NOTICE text from
    the Work, provided that such additional attribution notices cannot be
    construed as modifying the License.
    
    You may add Your own copyright statement to Your modifications and may provide
    additional or different license terms and conditions for use, reproduction, or
    distribution of Your modifications, or for any such Derivative Works as a
    whole, provided Your use, reproduction, and distribution of the Work otherwise
    complies with the conditions stated in this License.
    
    5. Submission of Contributions. Unless You explicitly state otherwise, any
    Contribution intentionally submitted for inclusion in the Work by You to the
    Licensor shall be under the terms and conditions of this License, without any
    additional terms or conditions. Notwithstanding the above, nothing herein
    shall supersede or modify the terms of any separate license agreement you may
    have executed with Licensor regarding such Contributions.
    
    6. Trademarks. This License does not grant permission to use the trade names,
    trademarks, service marks, or product names of the Licensor, except as
    required for reasonable and customary use in describing the origin of the Work
    and reproducing the content of the NOTICE file.
    
    7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
    writing, Licensor provides the Work (and each Contributor provides its
    Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied, including, without limitation, any warranties
    or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
    PARTICULAR PURPOSE. You are solely responsible for determining the
    appropriateness of using or redistributing the Work and assume any risks
    associated with Your exercise of permissions under this License.
    
    8. Limitation of Liability. In no event and under no legal theory, whether in
    tort (including negligence), contract, or otherwise, unless required by
    applicable law (such as deliberate and grossly negligent acts) or agreed to in
    writing, shall any Contributor be liable to You for damages, including any
    direct, indirect, special, incidental, or consequential damages of any
    character arising as a result of this License or out of the use or inability to
    use the Work (including but not limited to damages for loss of goodwill, work
    stoppage, computer failure or malfunction, or any and all other commercial
    damages or losses), even if such Contributor has been advised of the
    possibility of such damages.
    
    9. Accepting Warranty or Additional Liability. While redistributing the Work
    or Derivative Works thereof, You may choose to offer, and charge a fee for,
    acceptance of support, warranty, indemnity, or other liability obligations
    and/or rights consistent with this License. However, in accepting such
    obligations, You may act only on Your own behalf and on Your sole
    responsibility, not on behalf of any other Contributor, and only if You agree
    to indemnify, defend, and hold each Contributor harmless for any liability
    incurred by, or claims asserted against, such Contributor by reason of your
    accepting any such warranty or additional liability.
    
    END OF TERMS AND CONDITIONS
    
  • SKILL.md 17.1 KB
    ---
    name: solana-development
    description: Build, test, deploy, and audit Solana programs with Anchor or native Rust, plus ZK Compression (Light Protocol). Use for Solana contracts, token operations, compute optimization, deployment, program audits, or compressed tokens and PDAs.
    metadata:
      version: "0.7.3"
      categories: "development"
      topics: "solana, anchor, rust, zk-compression, smart-contracts"
      openclaw:
        homepage: https://github.com/tenequm/skills/tree/main/skills/solana-development
        emoji: "☀️"
        envVars:
          - name: SURFPOOL_DATASOURCE_RPC_URL
            required: false
            description: Dedicated mainnet RPC endpoint for surfpool's datasource (avoids public RPC rate limits when forking mainnet locally).
    ---
    
    # Solana
    
    Everything for building on Solana: developing programs (Anchor or native Rust), auditing them for security, and building with ZK Compression. All three share the same core model - accounts, PDAs, CPIs, tokens - and differ only in abstraction level and goal.
    
    ## What this skill covers
    
    | Area | Use when | Jump to |
    |------|----------|---------|
    | **Development** | Writing programs, tokens, tests, deployments | [Development](#development) |
    | **Security & Auditing** | Reviewing for vulnerabilities, writing exploits, audit reports | [Security and Auditing](#security-and-auditing) |
    | **ZK Compression** | Rent-free tokens/PDAs at scale via Light Protocol | [ZK Compression](#zk-compression) |
    
    ---
    
    # Development
    
    Build Solana programs with Anchor (recommended) or native Rust. Both share accounts, PDAs, CPIs, and tokens; they differ in syntax and abstraction.
    
    ## Quick Start
    
    ### Recommended: Anchor Framework
    
    Macros and tooling that cut boilerplate and generate TypeScript clients:
    
    ```rust
    use anchor_lang::prelude::*;
    
    declare_id!("YourProgramID");
    
    #[program]
    pub mod my_program {
        use super::*;
    
        pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
            ctx.accounts.account.data = data;
            Ok(())
        }
    }
    
    #[derive(Accounts)]
    pub struct Initialize<'info> {
        #[account(init, payer = user, space = 8 + 8)]
        pub account: Account<'info, MyAccount>,
        #[account(mut)]
        pub user: Signer<'info>,
        pub system_program: Program<'info, System>,
    }
    
    #[account]
    pub struct MyAccount {
        pub data: u64,
    }
    ```
    
    ```bash
    cargo install --git https://github.com/coral-xyz/anchor avm --locked --force
    avm install latest && avm use latest
    anchor init my_project && cd my_project && anchor build && anchor test
    ```
    
    **→ See [references/anchor.md](references/anchor.md) for the complete Anchor guide**
    
    ### Advanced: Native Rust
    
    Maximum control, optimization potential, and deeper runtime understanding:
    
    ```rust
    use solana_program::{
        account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
        pubkey::Pubkey, msg,
    };
    
    entrypoint!(process_instruction);
    
    pub fn process_instruction(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        instruction_data: &[u8],
    ) -> ProgramResult {
        msg!("Processing instruction");
        // Manual account parsing, validation, and instruction routing
        Ok(())
    }
    ```
    
    ```bash
    cargo new my_program --lib
    cd my_program   # configure Cargo.toml (see native-rust.md)
    cargo build-sbf
    ```
    
    **→ See [references/native-rust.md](references/native-rust.md) for the complete native Rust guide**
    
    ## When to use which
    
    | Your need | Approach | Reason |
    |-----------|----------|--------|
    | Standard DeFi/NFT program | Anchor | Faster, proven patterns |
    | TypeScript client needed | Anchor | Auto-generates IDL + types |
    | New to Solana | Anchor | Gentler learning curve |
    | Compute optimization critical | Native Rust | Direct control, no overhead |
    | Smallest program size | Native Rust | No abstraction layer |
    | Learning fundamentals | Native Rust | Understand the platform deeply |
    
    You can also start with Anchor for speed, then optimize hot paths with native patterns. Both can coexist in one workspace.
    
    ## Reference map
    
    **Foundations**
    - [accounts.md](references/accounts.md) - Account model, ownership, rent, validation
    - [pda.md](references/pda.md) - Program Derived Addresses: derivation, canonical bumps, signing
    - [cpi.md](references/cpi.md) - Cross-Program Invocations safely
    
    **Tokens**
    - [tokens-overview.md](references/tokens-overview.md) - Token accounts and ATAs
    - [tokens-operations.md](references/tokens-operations.md) - Create, mint, transfer, burn, close
    - [tokens-validation.md](references/tokens-validation.md) - Account validation patterns
    - [tokens-2022.md](references/tokens-2022.md) - Token Extensions Program
    - [tokens-patterns.md](references/tokens-patterns.md) - Common patterns and security
    
    **Testing**
    - [testing-overview.md](references/testing-overview.md) - Test pyramid and strategy
    - [testing-frameworks.md](references/testing-frameworks.md) - Mollusk, Anchor test, native Rust
    - [testing-practices.md](references/testing-practices.md) - Best practices and patterns
    - [surfpool.md](references/surfpool.md) - Local dev with mainnet forking, cheatcodes, IaC
    
    **Deployment**
    - [deployment.md](references/deployment.md) - Deploy, upgrade, verify, manage programs
    - [production-deployment.md](references/production-deployment.md) - Verified builds (Anchor 0.32.1)
    
    **Client**
    - [client-development.md](references/client-development.md) - dApp client: wallet connections, React hooks, SOL/SPL transfers, transaction management (framework-kit and @solana/kit 6.x)
    
    **Implementation details**
    - [serialization.md](references/serialization.md) - Data layout, Borsh, zero-copy
    - [error-handling.md](references/error-handling.md) - Custom errors, propagation, client handling
    - [security.md](references/security.md) - Defensive programming patterns during development
    
    **Advanced**
    - [compute-optimization.md](references/compute-optimization.md) - CU optimization and benchmarking
    - [versioned-transactions.md](references/versioned-transactions.md) - Address Lookup Tables for 256+ accounts
    - [durable-nonces.md](references/durable-nonces.md) - Offline signing with durable nonces
    - [transaction-lifecycle.md](references/transaction-lifecycle.md) - Submission, retries, confirmations
    
    **Low-level**
    - [sysvars.md](references/sysvars.md) - Clock, Rent, EpochSchedule, SlotHashes
    - [builtin-programs.md](references/builtin-programs.md) - System Program, Compute Budget Program
    
    ## Common tasks
    
    | Task | Pointer |
    |------|---------|
    | New program | Anchor: `anchor init` / Native: `cargo new --lib` → [anchor.md](references/anchor.md), [native-rust.md](references/native-rust.md) |
    | Initialize a PDA | [pda.md](references/pda.md) |
    | Transfer SPL tokens | [tokens-operations.md](references/tokens-operations.md) |
    | Fast unit tests | Mollusk → [testing-frameworks.md](references/testing-frameworks.md) |
    | Local mainnet fork | `surfpool start` → [surfpool.md](references/surfpool.md) |
    | Deploy to devnet | [deployment.md](references/deployment.md) |
    | Production verified build | `solana-verify build` → [production-deployment.md](references/production-deployment.md) |
    | Optimize compute | [compute-optimization.md](references/compute-optimization.md) |
    | Handle 40+ accounts | Address Lookup Tables → [versioned-transactions.md](references/versioned-transactions.md) |
    | Offline signing | Durable nonces → [durable-nonces.md](references/durable-nonces.md) |
    
    ---
    
    # Security and Auditing
    
    Systematic security review for Solana programs (Anchor or native Rust). The core principle: **attackers can pass arbitrary accounts to any instruction**, so there are no implicit guarantees - validate everything, trust nothing.
    
    ## Review process
    
    1. **Initial assessment** - Framework (Anchor vs native), Anchor version, dependencies (oracles, external programs), instruction count, account types, program purpose.
    2. **Systematic review** - For each instruction, check in order: account validation (signer/owner/writable/init), arithmetic safety (`checked_*`), PDA security (canonical bumps, seed uniqueness), CPI security (validated targets), oracle/external data (staleness, status). → [security-checklists.md](references/security-checklists.md)
    3. **Vulnerability pattern detection** - Type cosplay, account reloading, improper closing, missing lamports/ownership checks, PDA substitution, arbitrary CPI, overflow/underflow. → [vulnerability-patterns.md](references/vulnerability-patterns.md)
    4. **Architecture and testing review** - PDA design, space/rent, error handling, event emission, compute budget, test coverage (unit/integration/fuzz), upgrade and authority management.
    5. **Generate report** - Findings by severity, critical first, quick wins, testing recommendations.
    
    ## Essential checks (every instruction)
    
    **Anchor:**
    ```rust
    #[derive(Accounts)]
    pub struct SecureInstruction<'info> {
        #[account(
            mut,
            has_one = authority,                          // relationship check
            seeds = [b"vault", user.key().as_ref()], bump // canonical bump
        )]
        pub vault: Account<'info, Vault>,
        pub authority: Signer<'info>,                     // signer required
        pub token_program: Program<'info, Token>,         // program validation
    }
    
    let total = balance.checked_add(amount).ok_or(ErrorCode::Overflow)?;
    ```
    
    **Native Rust:**
    ```rust
    if !authority.is_signer { return Err(ProgramError::MissingRequiredSignature); }
    if vault.owner != program_id { return Err(ProgramError::IllegalOwner); }
    let total = balance.checked_add(amount).ok_or(ProgramError::ArithmeticOverflow)?;
    ```
    
    ## Critical anti-patterns
    
    ❌ **Never:** `saturating_*` arithmetic (hides errors), `unwrap()`/`expect()` in production, `init_if_needed` without extra checks, skipped signer validation, unchecked arithmetic, arbitrary CPI targets, forgetting to reload accounts after mutations.
    
    ✅ **Always:** `checked_*` arithmetic, `ok_or(error)?` on Options, explicit `init` with validation, `Signer`/`is_signer` checks, `Program<'info, T>` for CPI targets, reload after external mutations, validate ownership + discriminator + relationships.
    
    ## Severity and finding format
    
    - 🔴 **Critical** - Funds stolen/lost, protocol broken
    - 🟠 **High** - Disruption, partial fund loss possible
    - 🟡 **Medium** - Edge cases, griefing, suboptimal behavior
    - 🔵 **Low** - Code quality, gas, best practices
    - 💡 **Informational** - Recommendations, documentation
    
    ```markdown
    ## 🔴 [CRITICAL] Title
    **Location:** `programs/vault/src/lib.rs:45-52`
    **Issue:** Brief description.
    **Vulnerable Code:** ```rust ... ```
    **Exploit Scenario:** How it's exploited, step by step.
    **Recommendation:** ```rust ... ```
    **References:** Links to docs or similar exploits.
    ```
    
    ## Reference map
    
    - [security-fundamentals.md](references/security-fundamentals.md) - Security mindset, threat modeling, core validation, input/state/arithmetic safety, re-entrancy
    - [vulnerability-patterns.md](references/vulnerability-patterns.md) - Each vuln with vulnerable code, exploit scenario, secure fix, references
    - [security-checklists.md](references/security-checklists.md) - Account, arithmetic, PDA, CPI, oracle, token checklists
    - [anchor-security.md](references/anchor-security.md) - Anchor constraints, CpiContext, events, custom errors
    - [native-security.md](references/native-security.md) - Manual validation, secure PDA signing, discriminators, rent exemption
    - [caveats.md](references/caveats.md) - Solana quirks, Anchor limitations, testing blind spots, version issues
    
    ## Key questions for every audit
    
    1. Can an attacker substitute accounts? (PDA validation, program IDs, `has_one`)
    2. Can arithmetic overflow/underflow? (checked ops, division by zero)
    3. Are all accounts validated? (owner, signer, writable, initialized)
    4. Can the program be drained? (authorization, reentrancy, account confusion)
    5. What happens in edge cases? (zero amounts, max values, closed accounts, expired data)
    6. Are external dependencies safe? (oracle staleness/status, CPI targets, token program)
    
    ## Modern practices (2025)
    
    Anchor 0.30+ • Token-2022 with proper extension handling • `InitSpace` derive • emit events for critical state changes • fuzz tests with Trident • document invariants • roadmap: Dev → Audit → Testnet → Audit → Mainnet.
    
    ---
    
    # ZK Compression
    
    ZK Compression enables rent-free tokens and PDAs by storing state on the ledger (not in accounts), using zero-knowledge proofs to validate state transitions. Built by Light Protocol, indexed by Helius Photon.
    
    ## When to use
    
    **Use when:** creating millions of token accounts (~5000x cheaper), airdrops/loyalty/gaming mints to many recipients, many infrequently-updated user accounts, low-update-frequency PDAs.
    
    **Use regular accounts when:** updated frequently (>1000 lifetime writes), large data accessed on-chain, or compute budget is critical (compression adds ~100k CU).
    
    ## Quick Start
    
    ```bash
    # TypeScript client
    npm install @lightprotocol/stateless.js @lightprotocol/compressed-token
    # Rust SDK for programs
    cargo add light-sdk
    # CLI + local validator
    npm install -g @lightprotocol/zk-compression-cli
    light test-validator      # local validator with compression
    light init my-program     # new Anchor project with compression
    ```
    
    ### Mint compressed tokens (TypeScript)
    
    ```typescript
    import { createRpc } from '@lightprotocol/stateless.js';
    import { createMint, mintTo, transfer } from '@lightprotocol/compressed-token';
    
    const rpc = createRpc(); // or createRpc('https://mainnet.helius-rpc.com?api-key=YOUR_KEY')
    
    const { mint } = await createMint(rpc, payer, payer.publicKey, 9);
    await mintTo(rpc, payer, mint, recipient, payer, 1_000_000_000);
    await transfer(rpc, payer, mint, 500_000_000, owner, recipient);
    const accounts = await rpc.getCompressedTokenAccountsByOwner(owner, { mint });
    ```
    
    **→ See [references/compressed-pdas.md](references/compressed-pdas.md) for the full Anchor program with compressed PDAs (LightAccount, validity proofs, CPI).**
    
    ## Core concepts
    
    **Compressed account model** - Like regular accounts but stored on the ledger instead of AccountsDB. No rent. Identified by content hash (changes on every write) or an optional persistent address (PDA-like). State validated by ZK proofs.
    
    **State trees** - Concurrent Merkle trees (Poseidon hashing); only the root is on-chain. **V2 batched trees** (mainnet, Jan 2026): ~250x cheaper state-root updates and ~70% less CU vs V1. New deployments use V2 automatically; V1 still supported.
    
    **Validity proofs** - Groth16 ZK proofs, constant 128 bytes regardless of account count, ~100k CU to verify.
    
    **LightAccount operations:**
    ```rust
    LightAccount::<T>::new_init(&program_id, Some(address), tree_index);     // create
    LightAccount::<T>::new_mut(&program_id, &account_meta, current_state)?;  // modify
    LightAccount::<T>::new_close(&program_id, &account_meta, current_state)?; // close
    ```
    
    ## RPC and infrastructure
    
    Query compressed state via Helius RPC or self-hosted Photon. Key methods: `getCompressedAccount`, `getCompressedAccountsByOwner`, `getCompressedTokenAccountsByOwner`, `getCompressedTokenBalancesByOwner`, `getValidityProof`, `getMultipleCompressedAccounts`, `getCompressionSignaturesForAccount`.
    
    Node types: **Photon RPC** (indexes state, serves queries - Helius or self-host), **Prover** (generates proofs), **Forester** (maintains trees, empties nullifier queues).
    
    ```bash
    cargo install photon-indexer
    photon --rpc-url=https://api.devnet.solana.com
    ```
    
    ## Trade-offs
    
    Larger transactions (+128 byte proof + account data) • higher CU (~100k proof + ~6k per account) • each write nullifies old state and appends new • requires a Photon indexer for queries. With V2 batched trees, break-even shifts well past V1's ~1000-write threshold.
    
    ## Reference map
    
    - [compressed-accounts.md](references/compressed-accounts.md) - Account model, hashing, addresses
    - [compressed-tokens.md](references/compressed-tokens.md) - Token operations, pools, batch operations
    - [compressed-pdas.md](references/compressed-pdas.md) - Building programs with compressed PDAs
    - [client-integration.md](references/client-integration.md) - TypeScript/Rust client setup, RPC methods
    
    External: [ZK Compression Docs](https://www.zkcompression.com/) • [Light Protocol](https://github.com/Lightprotocol/light-protocol) • [Helius SDK](https://github.com/helius-labs/helius-sdk) • [Photon Indexer](https://github.com/helius-labs/photon) • [Program Examples](https://github.com/Lightprotocol/program-examples)
    
    ---
    
    # Resources
    
    Official docs, tools, learning paths, security guides, audit reports, security firms, and community links:
    
    **→ See [references/resources.md](references/resources.md)**
    
    ## General best practices
    
    ✅ Validate every account (ownership, signers, mutability) • use `checked_*` arithmetic • test extensively (unit, integration, edge cases) • use PDAs for program-owned accounts • minimize and profile compute • add `security.txt` so researchers can reach you.
    
    **Anchor-specific:** `InitSpace` derive for space • `has_one` constraints • `Program<'info, T>` for CPI validation • `emit!` events • group related constraints.
    
    **Native-specific:** `next_account_info` for safe iteration • cache PDA bumps • zero-copy for large structs (50%+ CU savings) • minimize logging (pubkey formatting is expensive) • `solana-verify build` for production.
    
    ## Getting help
    
    - Anchor: [Discord](https://discord.gg/srmqvxf) • [Docs](https://www.anchor-lang.com/docs)
    - Solana: [Stack Exchange](https://solana.stackexchange.com/) • [Discord](https://discord.gg/solana)
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related