Anchor Programs
Anchor is the standard framework for writing Solana programs. It handles the boilerplate of account validation, serialization, and instruction routing so you can focus on business logic.
Install
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Install Solana CLI
sh -c "$(curl -sSfL https://release.solana.com/stable/install)"
# Install Anchor CLI
cargo install --git https://github.com/coral-xyz/anchor avm --locked
avm install latest
avm use latest
Create a New Program
anchor init my-program
cd my-program
anchor build
anchor test
Basic Program Structure
use anchor_lang::prelude::*;
declare_id!("YourProgramId11111111111111111111111111111111");
#[program]
pub mod my_program {
use super::*;
pub fn initialize(ctx: Context<Initialize>, initial_value: u64) -> Result<()> {
let account = &mut ctx.accounts.my_account;
account.value = initial_value;
account.owner = ctx.accounts.user.key();
msg!("Initialized with value: {}", initial_value);
Ok(())
}
pub fn update(ctx: Context<Update>, new_value: u64) -> Result<()> {
let account = &mut ctx.accounts.my_account;
require!(
account.owner == ctx.accounts.user.key(),
MyError::Unauthorized
);
account.value = new_value;
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 value: u64,
pub owner: Pubkey,
}
#[error_code]
pub enum MyError {
#[msg("You are not authorized to update this account")]
Unauthorized,
}
Call from Frontend (TypeScript)
Anchor generates a TypeScript client from your program IDL.
import { Program, AnchorProvider, web3, BN } from "@coral-xyz/anchor";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import IDL from "./idl/my_program.json";
const PROGRAM_ID = new web3.PublicKey("YourProgramId...");
async function initializeAccount(initialValue: number) {
const { connection } = useConnection();
const wallet = useWallet();
const provider = new AnchorProvider(connection, wallet, {});
const program = new Program(IDL, provider);
// Derive the account PDA (Program Derived Address)
const [myAccount] = web3.PublicKey.findProgramAddressSync(
[Buffer.from("my-account"), wallet.publicKey!.toBuffer()],
PROGRAM_ID
);
await program.methods
.initialize(new BN(initialValue))
.accounts({
myAccount,
user: wallet.publicKey,
systemProgram: web3.SystemProgram.programId,
})
.rpc();
}
Program Derived Addresses (PDAs)
PDAs are deterministic addresses derived from seeds. They are owned by the program and do not have a private key — only the program can sign for them.
// In the program
#[account(
seeds = [b"vault", user.key().as_ref()],
bump,
)]
pub vault: Account<'info, Vault>,
// In the frontend
const [vault, bump] = web3.PublicKey.findProgramAddressSync(
[Buffer.from("vault"), userPublicKey.toBuffer()],
PROGRAM_ID
);
SPL Token in Anchor
use anchor_spl::token::{self, Token, TokenAccount, 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();
token::transfer(CpiContext::new(cpi_program, cpi_accounts), amount)?;
Ok(())
}
Last updated: September 2026.