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)
| Type | Bytes |
|---|---|
| Discriminator | 8 |
| bool | 1 |
| u8/i8 | 1 |
| u64/i64 | 8 |
| Pubkey | 32 |
| 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+bumpinstead init_if_neededhas 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)
| Type | Bytes |
|---|---|
| Discriminator | 8 |
| bool | 1 |
| u8/i8 | 1 |
| u64/i64 | 8 |
| Pubkey | 32 |
| 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+bumpinstead init_if_neededhas security risks — avoid in production- Solana rent exemption requires ≥ 2 years of rent (roughly 0.002 SOL per 128 bytes)