SuperAPI AI Directory

solana

Testing & quality

View repository

Updated 10-16 08:41

About this skill

Solana Program Development

You are a senior Solana Anchor engineer with extensive experience using the Anchor CLI, Solana CLI, Metaplex NFTs, and Trident-based fuzz testing.

Solana Core Architecture

Account Model Mindset: Programs are stateless executables operating on accounts passed to them. Program state/data lives in those accounts. Consult references/ACCOUNT_MODEL.md for Program Derived Addresses (PDA) patterns and rent calculations.

Critical Constraints:

  • Maintain rent-exempt balance for all accounts: use #[derive(InitSpace)] when defining the account struct and [STRUCT_NAME]::INIT_SPACE - when init-ializing or realloc-ating the on-chain account in the #[derive(Accounts)] struct (consult references/ACCOUNT_MODEL.md)
  • Derive all program-owned accounts as PDAs—never keypairs for state
  • Validate all accounts before CPI calls (inherits caller's privileges)
  • Stay within 1232 bytes/tx (consult references/TRANSACTIONS.md)

Tech Stack

Anchor Framework

Anchor is the primary framework. Prefer over native solana-program for:

  • Declarative account validation via constraints
  • Automatic Borsh (de)serialization
  • IDL generation for TypeScript clients
  • Built-in security checks (discriminators, ownership validation)

Project Structure

programs/{name}/src/
├── lib.rs              # module declarations, "#[program]", instruction exports
├── instructions/
│   ├── mod.rs          # re-exports all Instructions
│   ├── *.rs            # state-changing Ixs with "#[derive(Accounts)]" + "handler()"
│   └── view/           # read-only instructions (no state mutation)
├── state/              # account structs for on-chain data
└── utils/
    ├── errors.rs       # "#[error_code]" enum
    ├── events.rs       # "#[event]" structs
    ├── constants.rs    # seeds, program IDs
    └── validations.rs  # validation functions

Program Development Patterns

Account Struct Organization

Structure account validation in logical sections (see create_with_timestamps.rs, claim.rs):

Category Description Examples
USER ACCOUNTS Signers, protocol roles and their ATAs creator: Signer, creator_ata, recipient
PROTOCOL ACCOUNTS Global protocol state (treasury, etc.) treasury: Account<Treasury>
COLLECTION ACCOUNTS NFT collection state (if applicable) nft_collection_data, nft_collection_mint
ENTITY ACCOUNTS Per-entity state (either for stream or campaign) stream_data, stream_data_ata, campaign
PROGRAM ACCOUNTS External programs token_program, associated_token_program
SYSTEM ACCOUNTS System-level system_program, rent

Key account type patterns (use #[instruction() if input parameters are needed for seeds or PDA derivation):

#[derive(Accounts)]
#[instruction(salt: u128)]
pub struct CreateWithTimestamps<'info> {
    // ------------------------------------------------------------------------ //
    //                              USER ACCOUNTS                               //
    // ------------------------------------------------------------------------ //
    #[account(mut)]
    pub creator: Signer<'info>,

    #[account(
        mut,
        associated_token::mint = deposit_token_mint,
        associated_token::authority = creator,
        associated_token::token_program = deposit_token_program
    )]
    pub creator_ata: Box<InterfaceAccount<'info, TokenAccount>>,

    /// CHECK: The recipient may be any account
    pub recipient: UncheckedAccount<'info>,

    // ------------------------------------------------------------------------ //
    //                             STREAM ACCOUNTS                              //
    // ------------------------------------------------------------------------ //
    #[account(
        init,
        payer = creator,
        space = 8 + StreamData::INIT_SPACE,
        seeds = [STREAM_DATA, stream_nft_mint.key().as_ref()],
        bump
    )]
    pub stream_data: Box<Account<'info, StreamData>>,

    // ------------------------------------------------------------------------ //
    //                            PROGRAM ACCOUNTS                              //
    // ------------------------------------------------------------------------ //
    pub deposit_token_program: Interface<'info, TokenInterface>,
}

Handler Pattern

Structure handlers with validation first, then state updates and interactions, then event emission:

pub fn handler(ctx: Context<CreateWithTimestamps>, deposit_amount: u64, ...) -> Result<()> {
    // Validate parameters
    check_create(deposit_amount, start_time, cliff_time, end_time, ...)?;

    // Update state
    ctx.accounts.stream_data.create(...)?;

    // Transfer tokens
    transfer_tokens(creator_ata, stream_data_ata, creator, ...)?;

    // Emit event for indexers
    emit!(CreateLockupLinearStream { salt, deposit_token_mint, ... });

    Ok(())
}

Key Patterns

Pattern When to Use
Box<Account<>> Large accounts to reduce stack usage
InterfaceAccount<TokenAccount> Token/Token2022 compatibility
UncheckedAccount + /// CHECK Flexible validation (document the check)
Extract to utils/validations.rs Instruction validation logic

Events & Errors

Emit events for all state changes (critical for indexers):

#[event]
pub struct StreamCreated { pub stream_id: Pubkey, pub amount: u64 }

Define contextual error messages:

#[error_code]
pub enum ErrorCode {
    #[msg("Deposit amount must be greater than zero")]
    DepositAmountZero,
}

Security Requirements

Non-negotiable security practices:

  1. Account Ownership: Validate account.owner == expected_program (automatic for Account<>)
  2. PDAs Only: Never store state in keypair-controlled accounts
  3. Signer Checks: Verify signers for all privileged operations (via the #[account(constraint)] macro)
  4. Checked Math: Use checked_add, checked_sub, checked_mul for all arithmetic

Consult references/SECURITY.md for comprehensive vulnerability patterns and audit checklist.

MPL Core (NFTs)

For NFT ownership tokens, Metaplex Core provides efficient single-account NFTs (~0.0029 SOL vs ~0.022 SOL for Token Metadata).

Consult references/MPL_CORE.md for CPI builders and collection patterns.

Build & Code Generation

Building programs automatically generates TypeScript bindings:

just build           # Build all programs + generate TS types
just build sablier_lockup  # Build specific program

The build process:

  1. anchor build → compiles Rust → generates target/idl/{program}.json
  2. just codegen → generates target/types/{program}_errors.ts and {program}_structs.ts

Generated types provide type-safe access to on-chain data in tests:

import type { StreamData } from "../target/types/sablier_lockup_structs";
import { ProgramErrorCode } from "../target/types/sablier_lockup_errors";

const streamData: StreamData = await program.account.streamData.fetch(pda);
await expectToThrow(ctx.withdraw(), ProgramErrorCode.StreamDepleted);

Consult references/CODEGEN.md for type mappings, script architecture and troubleshooting.

Testing Strategy

Unit/Integration Tests (Vitest + anchor-bankrun)

Fast, deterministic testing without validator startup. Key pattern:

class LockupTestContext extends TestContext {
  async setUp() {
    await super.setUp("sablier_lockup", programId);
    this.program = new Program<SablierLockup>(IDL, this.provider);
  }
}

Consult references/TESTING.md for complete test context patterns, time travel, and assertions.

Fuzz Testing (Trident)

Property-based fuzzing for edge case discovery:

trident-tests/fuzz_{program}/
├── test_fuzz.rs          # Entry point with flows
├── instructions/*.rs     # TridentInstruction definitions
└── helpers/*_math.rs     # Replicated math for invariants

Consult references/FUZZ_TESTING.md for instruction hooks, invariants, and time warping.


Additional Resources

Reference Files

Detailed documentation for specific domains:

File Content
references/ACCOUNT_MODEL.md PDA derivation, rent, account creation
references/TRANSACTIONS.md Tx limits, CU optimization
references/SECURITY.md Vulnerabilities, audit checklist, protections
references/TESTING.md Vitest + anchor-bankrun patterns
references/FUZZ_TESTING.md Trident setup, invariants, flows
references/MPL_CORE.md Metaplex Core NFT integration
references/CODEGEN.md TypeScript codegen from IDL, type mappings

Example Files

Working code examples demonstrating key patterns:

File Content
examples/withdraw_instruction.rs Complete Anchor instruction example
examples/test_pattern.ts Vitest + anchor-bankrun test structure

External Documentation

Fetch latest docs before implementation—the ecosystem moves fast:

If you don't find the information you're looking for in this Skill, use Context7 MCP, as a default backup, to retrieve the current documentation for Anchor, Solana, Metaplex and Trident.