Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Solana development skill covering @solana/kit v5, Anchor programs, LiteSVM testing, and security patterns.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/programs/anchor.md
1---2title: Programs with Anchor3description: Write Solana programs using the Anchor framework for fast iteration, automatic account validation, and built-in TypeScript client generation.4---56# Programs with Anchor (default choice)78## When to use Anchor9Use Anchor by default when:10- You want fast iteration with reduced boilerplate11- You want an IDL and TypeScript client story out of the box12- You want mature testing and workspace tooling13- You need built-in security through automatic account validation1415## Core Advantages16- **Reduced Boilerplate**: Abstracts repetitive account management, instruction serialization, and error handling17- **Built-in Security**: Automatic account-ownership verification and data validation18- **IDL Generation**: Automatic interface definition for client generation1920## Core Macros2122### `declare_id!()`23Declares the onchain address where the program resides—a unique public key derived from the project's keypair.2425### `#[program]`26Marks the module containing every instruction entrypoint and business-logic function.2728### `#[derive(Accounts)]`29Lists accounts an instruction requires and automatically enforces their constraints:30- Declares all necessary accounts for specific instructions31- Enforces constraint checks automatically to block bugs and exploits32- Generates helper methods for safe account access and mutation3334### `#[error_code]`35Enables custom, human-readable error types with `#[msg(...)]` attributes for clearer debugging.3637## Account Types3839| Type | Purpose |40|------|---------|41| `Signer<'info>` | Verifies the account signed the transaction |42| `SystemAccount<'info>` | Confirms System Program ownership |43| `Program<'info, T>` | Validates executable program accounts |44| `Account<'info, T>` | Typed program account with automatic validation |45| `UncheckedAccount<'info>` | Raw account requiring manual validation |4647## Account Constraints4849### Initialization50```rust51#[account(52init,53payer = payer,54space = 8 + CustomAccount::INIT_SPACE55)]56pub account: Account<'info, CustomAccount>,57```5859### PDA Validation60```rust61#[account(62seeds = [b"vault", owner.key().as_ref()],63bump64)]65pub vault: SystemAccount<'info>,66```6768### Ownership and Relationships69```rust70#[account(71has_one = authority @ CustomError::InvalidAuthority,72constraint = account.is_active @ CustomError::AccountInactive73)]74pub account: Account<'info, CustomAccount>,75```7677### Reallocation78```rust79#[account(80mut,81realloc = new_space,82realloc::payer = payer,83realloc::zero = true // Clear old data when shrinking84)]85pub account: Account<'info, CustomAccount>,86```8788### Closing Accounts89```rust90#[account(91mut,92close = destination93)]94pub account: Account<'info, CustomAccount>,95```9697## Account Discriminators9899Default discriminators use `sha256("account:<StructName>")[0..8]`. Custom discriminators (Anchor 0.31+):100101```rust102#[account(discriminator = 1)]103pub struct Escrow { ... }104```105106**Constraints:**107- Discriminators must be unique across your program108- Using `[1]` prevents using `[1, 2, ...]` which also start with `1`109- `[0]` conflicts with uninitialized accounts110111## Instruction Patterns112113### Basic Structure114```rust115#[program]116pub mod my_program {117use super::*;118119pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {120ctx.accounts.account.data = data;121Ok(())122}123}124```125126### Context Implementation Pattern127Move logic to context struct implementations for organization and testability:128129```rust130impl<'info> Transfer<'info> {131pub fn transfer_tokens(&mut self, amount: u64) -> Result<()> {132// Implementation133Ok(())134}135}136```137138## Cross-Program Invocations (CPIs)139140### Basic CPI141```rust142let cpi_accounts = Transfer {143from: ctx.accounts.from.to_account_info(),144to: ctx.accounts.to.to_account_info(),145};146let cpi_ctx = CpiContext::new(System::id(), cpi_accounts);147148transfer(cpi_ctx, amount)?;149```150151### PDA-Signed CPIs152```rust153let seeds = &[b"vault".as_ref(), &[ctx.bumps.vault]];154let signer = &[&seeds[..]];155let cpi_ctx = CpiContext::new_with_signer(System::id(), cpi_accounts, signer);156```157158## Error Handling159160```rust161#[error_code]162pub enum MyError {163#[msg("Custom error message")]164CustomError,165#[msg("Value too large: {0}")]166ValueError(u64),167}168169// Usage170require!(value > 0, MyError::CustomError);171require!(value < 100, MyError::ValueError(value));172```173174## Token Accounts175176### SPL Token177```rust178#[account(179mint::decimals = 9,180mint::authority = authority,181)]182pub mint: Account<'info, Mint>,183184#[account(185mut,186associated_token::mint = mint,187associated_token::authority = owner,188)]189pub token_account: Account<'info, TokenAccount>,190```191192### Token2022 Compatibility193Use `InterfaceAccount` for dual compatibility:194195```rust196use anchor_spl::token_interface::{Mint, TokenAccount};197198pub mint: InterfaceAccount<'info, Mint>,199pub token_account: InterfaceAccount<'info, TokenAccount>,200pub token_program: Interface<'info, TokenInterface>,201```202203## LazyAccount (Anchor 0.31+)204205Heap-allocated, read-only account access for efficient memory usage:206207```rust208// Cargo.toml209anchor-lang = { version = "0.31.1", features = ["lazy-account"] }210211// Usage212pub account: LazyAccount<'info, CustomAccountType>,213214pub fn handler(ctx: Context<MyInstruction>) -> Result<()> {215let value = ctx.accounts.account.get_value()?;216Ok(())217}218```219220**Note:** LazyAccount is read-only. After CPIs, use `unload()` to refresh cached values.221222## Zero-Copy Accounts223224For accounts exceeding stack/heap limits:225226```rust227#[account(zero_copy)]228pub struct LargeAccount {229pub data: [u8; 10000],230}231```232233Accounts under 10,240 bytes use `init`; larger accounts require external creation then `zero` constraint initialization.234235## Remaining Accounts236237Pass dynamic accounts beyond fixed instruction structure:238239```rust240pub fn batch_operation(ctx: Context<BatchOp>, amounts: Vec<u64>) -> Result<()> {241let remaining = &ctx.remaining_accounts;242require!(remaining.len() % 2 == 0, BatchError::InvalidSchema);243244for (i, chunk) in remaining.chunks(2).enumerate() {245process_pair(&chunk[0], &chunk[1], amounts[i])?;246}247Ok(())248}249```250251## Version Management252253- Use AVM (Anchor Version Manager) for reproducible builds254- Keep Solana CLI + Anchor versions aligned in CI and developer setup255- Pin versions in `Anchor.toml`256257## Compatibility Notes for Anchor 0.32.0258259To resolve build conflicts with certain crates in Anchor 0.32.0, run these cargo update commands in your project root:260261```bash262cargo update base64ct --precise 1.6.0263cargo update constant_time_eq --precise 0.4.1264cargo update blake3 --precise 1.5.5265```266267Additionally, if you encounter warnings about `solana-program` conflicts, add `solana-program = "3"` to the `[dependencies]` section in your program's `Cargo.toml` file (e.g., `programs/your-program/Cargo.toml`).268269270## Security Best Practices271272### Account Validation273- Use typed accounts (`Account<'info, T>`) over `UncheckedAccount` when possible274- Always validate signer requirements explicitly275- Use `has_one` for ownership relationships276- Validate PDA seeds and bumps277278### CPI Safety279- Use `Program<'info, T>` to validate CPI targets (prevents arbitrary CPI attacks)280- Never pass extra privileges to CPI callees281- Prefer explicit program IDs for known CPIs282283### Common Gotchas284- **Avoid `init_if_needed`**: Permits reinitialization attacks285- **Legacy IDL formats**: Ensure tooling agrees on format (pre-0.30 vs new spec)286- **PDA seeds**: Ensure all seed material is stable and canonical287288## Testing289290- Use `NO_DNA=1 anchor test` for end-to-end tests (when run by an agent)291- Use `NO_DNA=1 anchor build` for builds (when run by an agent)292- Prefer Mollusk or LiteSVM for fast unit tests293- Use Surfpool for integration tests with mainnet state294295See [no-dna.org](https://no-dna.org) for the `NO_DNA` standard.296297## IDL and Clients298299- Treat the program's IDL as a product artifact300- Prefer generating Kit-native clients via Codama301- If using Anchor TS client in Kit-first app, put it behind web3-compat boundary302303## Migrations304305### Anchor v0.32 → v1306307- **Dependencies** — bump `anchor-lang` and `anchor-spl` to `^1`, and all `solana-*` crates to `^3`.308- **CPI context** — `CpiContext::new` now takes a program ID (`Pubkey`) instead of a program `AccountInfo`. Remove the program account from the accounts struct.309- **TypeScript** — replace `@coral-xyz/anchor` with `@anchor-lang/core`.310- **IDL** — IDL management is being moved off program, **mandatory** actions required.311312See [anchor/migrating-v0.32-to-v1.md](./anchor/migrating-v0.32-to-v1.md) for the full checklist and before/after examples.313