Common Solana Program Patterns

This section covers patterns and complete implementations for common Solana program types. Each example demonstrates production-ready code with proper error handling and security.

Common Program Categories

Text
┌─────────────────────────────────────────────────────────────────┐
│                 Common Solana Program Types                     │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Token Programs                                                 │
│  ├── Vesting contracts                                          │
│  ├── Staking programs                                           │
│  ├── Token launchpads                                           │
│  └── Airdrops and distributions                                 │
│                                                                 │
│  DeFi Programs                                                  │
│  ├── AMM / DEX                                                  │
│  ├── Lending protocols                                          │
│  ├── Yield aggregators                                          │
│  └── Perpetuals / derivatives                                   │
│                                                                 │
│  NFT Programs                                                   │
│  ├── Minting contracts                                          │
│  ├── Marketplaces                                               │
│  ├── Staking / utility                                          │
│  └── Royalty enforcement                                        │
│                                                                 │
│  Gaming Programs                                                │
│  ├── On-chain games                                             │
│  ├── Achievement systems                                        │
│  ├── Reward distribution                                        │
│  └── Randomness (VRF)                                           │
│                                                                 │
│  Infrastructure                                                 │
│  ├── Multisig wallets                                           │
│  ├── Governance                                                 │
│  ├── Oracles                                                    │
│  └── Bridge programs                                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Program Architecture Patterns

Single Program vs Multiple Programs

Text
Single Program Architecture:
┌─────────────────────────────────────────┐
│              Main Program               │
├─────────────────────────────────────────┤
│  ┌─────────┐ ┌─────────┐ ┌─────────┐   │
│  │ Module  │ │ Module  │ │ Module  │   │
│  │    A    │ │    B    │ │    C    │   │
│  └─────────┘ └─────────┘ └─────────┘   │
└─────────────────────────────────────────┘

Multiple Program Architecture:
┌───────────┐   ┌───────────┐   ┌───────────┐
│ Program A │◄──│ Program B │──►│ Program C │
│ (Tokens)  │   │  (Core)   │   │  (Admin)  │
└───────────┘   └───────────┘   └───────────┘
       │               │               │
       └───────────────┼───────────────┘
                       ▼
                 ┌───────────┐
                 │  Client   │
                 └───────────┘

Account Organization

Rust
use anchor_lang::prelude::*;

// Global configuration (single instance)
#[account]
pub struct GlobalConfig {
    pub authority: Pubkey,
    pub fee_bps: u16,
    pub paused: bool,
    pub total_users: u64,
    pub bump: u8,
}

// Per-user state (PDA per user)
#[account]
pub struct UserAccount {
    pub owner: Pubkey,
    pub balance: u64,
    pub reward_debt: u64,
    pub last_stake_time: i64,
    pub bump: u8,
}

// Resource pools (multiple instances)
#[account]
pub struct Pool {
    pub id: u64,
    pub creator: Pubkey,
    pub token_mint: Pubkey,
    pub total_staked: u64,
    pub reward_rate: u64,
    pub bump: u8,
}

// Relationship/mapping accounts
#[account]
pub struct UserPoolPosition {
    pub user: Pubkey,
    pub pool: Pubkey,
    pub staked_amount: u64,
    pub entry_reward_index: u64,
    pub bump: u8,
}

Security Checklist

CategoryCheck
Access ControlAuthority verification on sensitive operations
Signer ValidationAll required accounts are signers
OwnershipAccount ownership matches expected program
PDA DerivationSeeds verified, bump stored and checked
ArithmeticChecked math or overflow protection
ReentrancyState updates before external calls
Account ClosureProper rent reclamation, zero data
Input ValidationBounds checking on all inputs

Instruction Design

Rust
// Good: Specific, atomic instructions
pub fn stake(ctx: Context<Stake>, amount: u64) -> Result<()>;
pub fn unstake(ctx: Context<Unstake>, amount: u64) -> Result<()>;
pub fn claim_rewards(ctx: Context<ClaimRewards>) -> Result<()>;

// Also good: Combined when atomicity required
pub fn unstake_and_claim(ctx: Context<UnstakeAndClaim>, amount: u64) -> Result<()>;

// Bad: Kitchen sink instruction
pub fn do_everything(
    ctx: Context<Everything>,
    action: u8,
    amount: Option<u64>,
    recipient: Option<Pubkey>,
) -> Result<()>;

Error Design

Rust
#[error_code]
pub enum ProgramError {
    // Validation errors (6000-6099)
    #[msg("Invalid amount: must be greater than zero")]
    InvalidAmount,

    #[msg("Invalid authority")]
    InvalidAuthority,

    // State errors (6100-6199)
    #[msg("Account already initialized")]
    AlreadyInitialized,

    #[msg("Account not initialized")]
    NotInitialized,

    #[msg("Program is paused")]
    ProgramPaused,

    // Operation errors (6200-6299)
    #[msg("Insufficient balance: need {0}, have {1}")]
    InsufficientBalance(u64, u64),

    #[msg("Arithmetic overflow")]
    Overflow,

    #[msg("Lockup period not elapsed")]
    LockupActive,

    // Permission errors (6300-6399)
    #[msg("Unauthorized")]
    Unauthorized,

    #[msg("Only admin can perform this action")]
    AdminOnly,
}

Events for Indexing

Rust
#[event]
pub struct Staked {
    #[index]
    pub user: Pubkey,
    #[index]
    pub pool: Pubkey,
    pub amount: u64,
    pub total_staked: u64,
    pub timestamp: i64,
}

#[event]
pub struct RewardsClaimed {
    #[index]
    pub user: Pubkey,
    pub amount: u64,
    pub timestamp: i64,
}

#[event]
pub struct PoolCreated {
    #[index]
    pub pool: Pubkey,
    pub creator: Pubkey,
    pub token_mint: Pubkey,
}

Testing Patterns

TypeScript
describe("program", () => {
  // Setup once
  before(async () => {
    await initializeGlobalState();
  });

  // Fresh state per test group
  describe("staking", () => {
    let pool: PublicKey;
    let userAccount: PublicKey;

    beforeEach(async () => {
      pool = await createPool();
      userAccount = await createUserAccount();
      await mintTokens(userTokenAccount, 1000);
    });

    it("stakes tokens", async () => {
      await program.methods
        .stake(new BN(100))
        .accounts({...})
        .rpc();

      const poolData = await program.account.pool.fetch(pool);
      expect(poolData.totalStaked.toNumber()).to.equal(100);
    });

    it("fails with zero amount", async () => {
      try {
        await program.methods.stake(new BN(0)).accounts({...}).rpc();
        expect.fail("Should throw");
      } catch (e) {
        expect(e.error.errorCode.code).to.equal("InvalidAmount");
      }
    });
  });
});

What's Next

The following pages cover specific program implementations:

  1. Escrow Contract - Token escrow with time locks and conditions
  2. Staking Program - Token staking with rewards distribution
  3. NFT Minting - NFT creation with metadata and royalties

Each includes:

  • Complete Anchor code
  • Account structures
  • Security considerations
  • TypeScript client examples
  • Testing patterns

Next: Escrow Contract - Building a secure escrow program.