1. Project Structure
Learn how to organize your Anchor program for maintainability and scalability.
Anchor Program Layout
programs/starter_program/src/
├── lib.rs # Program entry point, instruction definitions
├── constants.rs # PDA seeds and constants
├── error.rs # Custom error codes
├── events.rs # Event definitions for logging
├── state/ # Account structures
│ ├── mod.rs
│ ├── config.rs
│ ├── user.rs
│ ├── role.rs
│ ├── treasury.rs
│ └── nft.rs
└── instructions/ # Instruction handlers
├── mod.rs
├── config.rs
├── user.rs
├── token.rs
├── cpi.rs
├── rbac.rs
├── treasury.rs
└── nft.rs
File Organization
lib.rs - Program Entry Point
The main entry point defines your program ID and declares all instructions:
use anchor_lang::prelude::*;
declare_id!("gARh1g6reuvsAHB7DXqiuYzzyiJeoiJmtmCpV8Y5uWC");
#[program]
pub mod starter_program {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
instructions::config::initialize_handler(ctx)
}
pub fn create_user_account(ctx: Context<CreateUserAccount>) -> Result<()> {
instructions::user::create_user_account_handler(ctx)
}
// ... more instructions
}
Pattern: Keep lib.rs minimal - only instruction declarations. Actual logic goes in instructions/.
state/ - Account Structures
Each file defines one or more related account types:
// state/config.rs
#[account]
pub struct ProgramConfig {
pub admin: Pubkey,
pub fee_basis_points: u64,
// ...
}
// state/user.rs
#[account]
pub struct UserAccount {
pub authority: Pubkey,
pub points: u64,
// ...
}
Pattern: Group related state together. Keep state definitions separate from instruction logic.
instructions/ - Instruction Handlers
Each file contains related instruction handlers:
// instructions/user.rs
pub fn create_user_account_handler(ctx: Context<CreateUserAccount>) -> Result<()> {
// Implementation
}
pub fn update_user_account_handler(ctx: Context<UpdateUserAccount>) -> Result<()> {
// Implementation
}
Pattern: Group instructions by domain (config, user, token, etc.).
Constants Pattern
Source: constants.rs
pub const SEED_PROGRAM_CONFIG: &[u8] = b"program_config";
pub const SEED_USER_ACCOUNT: &[u8] = b"user_account";
pub const SEED_TOKEN_VAULT: &[u8] = b"token_vault";
pub const SEED_MINT_AUTHORITY: &[u8] = b"mint_authority";
pub const SEED_ROLE: &[u8] = b"role";
pub const SEED_TREASURY: &[u8] = b"treasury";
pub const SEED_NFT_COLLECTION: &[u8] = b"nft_collection";
pub const SEED_NFT_METADATA: &[u8] = b"nft_metadata";
pub const SEED_NFT_LISTING: &[u8] = b"nft_listing";
pub const SEED_NFT_OFFER: &[u8] = b"nft_offer";
Why Use Constants?
✅ Prevents typos in seed strings across multiple files
✅ Single source of truth - change once, applies everywhere
✅ Easy to refactor - rename with IDE support
✅ Better IDE support - autocomplete and type checking
Usage Example
use crate::constants::*;
#[derive(Accounts)]
pub struct CreateUserAccount<'info> {
#[account(
init,
payer = authority,
space = UserAccount::LEN,
seeds = [SEED_USER_ACCOUNT, authority.key().as_ref()],
bump
)]
pub user_account: Account<'info, UserAccount>,
// ...
}
Module Organization
state/mod.rs
pub mod config;
pub mod user;
pub mod role;
pub mod treasury;
pub mod nft;
pub use config::*;
pub use user::*;
pub use role::*;
pub use treasury::*;
pub use nft::*;
instructions/mod.rs
pub mod config;
pub mod user;
pub mod token;
pub mod cpi;
pub mod rbac;
pub mod treasury;
pub mod nft;
pub use config::*;
pub use user::*;
pub use token::*;
pub use cpi::*;
pub use rbac::*;
pub use treasury::*;
pub use nft::*;
Pattern: Re-export all public items from module root for easier imports.
Best Practices
| Practice | Why |
|---|---|
| Separate state from logic | Keep state/ and instructions/ separate for clarity |
| One responsibility per file | user.rs handles user operations only |
| Use constants for seeds | Avoid magic strings, prevent typos |
| Group related functionality | Token operations together, CPI calls together |
| Keep lib.rs minimal | Just declarations, no business logic |
Scaling Your Project
As your program grows:
- Split large files: If
instructions/token.rsgets too big, createinstructions/token/with submodules - Add utilities: Create
utils/for shared helpers - Domain-driven structure: Group by business domain, not technical layer
Example larger structure:
programs/starter_program/src/
├── lib.rs
├── constants.rs
├── error.rs
├── events.rs
├── utils/
│ ├── mod.rs
│ ├── math.rs
│ └── validation.rs
├── state/
│ ├── mod.rs
│ ├── config/
│ │ ├── mod.rs
│ │ ├── program_config.rs
│ │ └── fee_config.rs
│ └── user/
│ ├── mod.rs
│ ├── user_account.rs
│ └── user_stats.rs
└── instructions/
└── ...
Next: Account State Design →