protocols/solana-anchor-guide

Solana Anchor Development Framework Guide

solanaguide🏛️ Officialconfidence highhealth -1%
v1.0.0·Updated 3/20/2026

Installation

sh -c "$(curl -sSfL https://release.solana.com/stable/install)"
cargo install --git https://github.com/coral-xyz/anchor avm --force
avm install latest && avm use latest

Initialize a Project

anchor init my-program
cd my-program
anchor build
anchor test

Basic Program Structure

use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod my_program {
  use super::*;
  
  pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
    let account = &mut ctx.accounts.my_account;
    account.data = data;
    account.authority = ctx.accounts.user.key();
    Ok(())
  }
  
  pub fn update(ctx: Context<Update>, new_data: u64) -> Result<()> {
    require!(ctx.accounts.my_account.authority == ctx.accounts.user.key(), ErrorCode::Unauthorized);
    ctx.accounts.my_account.data = new_data;
    Ok(())
  }
}

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

#[derive(Accounts)]
pub struct Update<'info> {
  #[account(mut)]
  pub my_account: Account<'info, MyAccount>,
  pub user: Signer<'info>,
}

#[account]
#[derive(InitSpace)]
pub struct MyAccount {
  pub data: u64,
  pub authority: Pubkey,
}

#[error_code]
pub enum ErrorCode {
  #[msg("Unauthorized")]
  Unauthorized,
}

PDA (Program Derived Address)

// Create a PDA account
#[derive(Accounts)]
#[instruction(seed: String)]
pub struct CreatePDA<'info> {
  #[account(
    init, payer = user,
    space = 8 + 32,
    seeds = [b"vault", user.key().as_ref(), seed.as_bytes()],
    bump
  )]
  pub vault: Account<'info, Vault>,
  #[account(mut)]
  pub user: Signer<'info>,
  pub system_program: Program<'info, System>,
}

// Use bump inside an instruction
let (pda, bump) = Pubkey::find_program_address(&[b"vault", user.key().as_ref()], ctx.program_id);

Testing (TypeScript)

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";

describe("my-program", () => {
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);
  const program = anchor.workspace.MyProgram as Program<MyProgram>;

  it("initialize", async () => {
    const myAccount = anchor.web3.Keypair.generate();
    await program.methods
      .initialize(new anchor.BN(42))
      .accounts({ myAccount: myAccount.publicKey, user: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId })
      .signers([myAccount])
      .rpc();
    
    const account = await program.account.myAccount.fetch(myAccount.publicKey);
    assert.equal(account.data.toNumber(), 42);
  });
});

Client-Side Invocation

import { Connection, PublicKey } from "@solana/web3.js";
import { AnchorProvider, Program, web3 } from "@coral-xyz/anchor";

const connection = new Connection("https://api.mainnet-beta.solana.com");
const provider = new AnchorProvider(connection, wallet, {});
const program = new Program(IDL, PROGRAM_ID, provider);

// Call an instruction
const tx = await program.methods.update(new BN(100))
  .accounts({ myAccount: accountPubkey, user: wallet.publicKey })
  .rpc();

Space Calculation (Account Size)

TypeBytes
Discriminator8
bool1
u8/i81
u64/i648
Pubkey32
String(n)4 + n
Vec<T>(n)4 + n * size_of(T)

Common Pitfalls

  • Account discriminator = 8 bytes — must be included in space calculations
  • PDAs cannot sign; use seeds + bump instead
  • init_if_needed has security risks — avoid in production

Solana Anchor Development Framework Guide

Installation

sh -c "$(curl -sSfL https://release.solana.com/stable/install)"
cargo install --git https://github.com/coral-xyz/anchor avm --force
avm install latest && avm use latest

Initialize a Project

anchor init my-program
cd my-program
anchor build
anchor test

Basic Program Structure

use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod my_program {
  use super::*;
  
  pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
    let account = &mut ctx.accounts.my_account;
    account.data = data;
    account.authority = ctx.accounts.user.key();
    Ok(())
  }
  
  pub fn update(ctx: Context<Update>, new_data: u64) -> Result<()> {
    require!(ctx.accounts.my_account.authority == ctx.accounts.user.key(), ErrorCode::Unauthorized);
    ctx.accounts.my_account.data = new_data;
    Ok(())
  }
}

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

#[derive(Accounts)]
pub struct Update<'info> {
  #[account(mut)]
  pub my_account: Account<'info, MyAccount>,
  pub user: Signer<'info>,
}

#[account]
#[derive(InitSpace)]
pub struct MyAccount {
  pub data: u64,
  pub authority: Pubkey,
}

#[error_code]
pub enum ErrorCode {
  #[msg("Unauthorized")]
  Unauthorized,
}

PDA (Program Derived Address)

// Create a PDA account
#[derive(Accounts)]
#[instruction(seed: String)]
pub struct CreatePDA<'info> {
  #[account(
    init, payer = user,
    space = 8 + 32,
    seeds = [b"vault", user.key().as_ref(), seed.as_bytes()],
    bump
  )]
  pub vault: Account<'info, Vault>,
  #[account(mut)]
  pub user: Signer<'info>,
  pub system_program: Program<'info, System>,
}

// Use bump inside an instruction
let (pda, bump) = Pubkey::find_program_address(&[b"vault", user.key().as_ref()], ctx.program_id);

Testing (TypeScript)

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";

describe("my-program", () => {
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);
  const program = anchor.workspace.MyProgram as Program<MyProgram>;

  it("initialize", async () => {
    const myAccount = anchor.web3.Keypair.generate();
    await program.methods
      .initialize(new anchor.BN(42))
      .accounts({ myAccount: myAccount.publicKey, user: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId })
      .signers([myAccount])
      .rpc();
    
    const account = await program.account.myAccount.fetch(myAccount.publicKey);
    assert.equal(account.data.toNumber(), 42);
  });
});

Client-Side Invocation

import { Connection, PublicKey } from "@solana/web3.js";
import { AnchorProvider, Program, web3 } from "@coral-xyz/anchor";

const connection = new Connection("https://api.mainnet-beta.solana.com");
const provider = new AnchorProvider(connection, wallet, {});
const program = new Program(IDL, PROGRAM_ID, provider);

// Call an instruction
const tx = await program.methods.update(new BN(100))
  .accounts({ myAccount: accountPubkey, user: wallet.publicKey })
  .rpc();

Space Calculation (Account Size)

TypeBytes
Discriminator8
bool1
u8/i81
u64/i648
Pubkey32
String(n)4 + n
Vec<T>(n)4 + n * size_of(T)

Common Pitfalls

  • Account discriminator = 8 bytes — must be included in space calculations
  • PDAs cannot sign; use seeds + bump instead
  • init_if_needed has security risks — avoid in production
  • Solana rent exemption requires ≥ 2 years of rent (roughly 0.002 SOL per 128 bytes)