Files
gh-tenequm-claude-plugins-s…/skills/solana-development/references/deployment.md
2025-11-30 09:01:25 +08:00

40 KiB

Solana Program Deployment Reference

This reference covers deployment workflows, best practices, and troubleshooting for both Anchor and native Rust Solana programs.

Table of Contents


Deployment Overview

Solana Networks

Solana has three primary networks:

Localhost (127.0.0.1:8899)

  • Local test validator running on your machine
  • Fastest iteration, no cost
  • Full control over network state
  • Use for rapid development and debugging

Devnet

  • Public development network
  • Free SOL via airdrops
  • Resets periodically
  • Use for integration testing

Mainnet-beta

  • Production network with real economic value
  • Requires real SOL for deployment and transactions
  • Immutable deployed programs (unless upgradeable)
  • Use for production deployments

Network Configuration

Anchor - Edit Anchor.toml:

[toolchain]

[features]
seeds = false
skip-lint = false

[programs.localnet]
my_program = "11111111111111111111111111111111"

[programs.devnet]
my_program = "YourDevnetProgramID"

[programs.mainnet]
my_program = "YourMainnetProgramID"

[registry]
url = "https://api.apr.dev"

[provider]
cluster = "Localnet"  # Change to "Devnet" or "Mainnet"
wallet = "~/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

Native Rust - Use Solana CLI:

# View current config
solana config get

# Set network
solana config set --url https://api.devnet.solana.com  # Devnet
solana config set --url https://api.mainnet-beta.solana.com  # Mainnet
solana config set --url http://localhost:8899  # Localnet

# Set wallet
solana config set --keypair ~/.config/solana/id.json

Cost Considerations

Program deployment requires rent-exempt balance for:

  1. Program Account - Stores program metadata (small cost)
  2. Program Data Account - Stores the executable bytecode (major cost)

Calculate deployment cost:

# Get program size
ls -l target/deploy/my_program.so
# Example: 363960 bytes

# Check rent for program data account (1x program size in newer versions)
solana rent 363960
# Output:
# Rent-exempt minimum: ~2.5 SOL

# Add transaction fees (~0.002 SOL) for deployment transactions

Cost breakdown:

  • 363KB program ≈ 2.5 SOL rent + 0.002 SOL tx fees = ~2.502 SOL
  • 800KB program ≈ 5.5 SOL rent + 0.002 SOL tx fees = ~5.502 SOL

Important: Since Solana CLI v1.18+, program accounts are sized to 1x the .so file (previously 2x), reducing costs by approximately 50%.


Pre-Deployment Checklist

1. Build Verification

Anchor:

# Clean build
anchor clean
anchor build

# Verify build succeeded
ls -la target/deploy/
# Should see: my_program.so and my_program-keypair.json

Native Rust:

# Clean build
cargo clean
cargo build-sbf

# Verify build
ls -la target/deploy/
# Should see: my_program.so

2. Testing Completeness

Anchor:

# Run all tests on local validator
anchor test

# Run tests without redeploying
anchor test --skip-deploy

# Run specific test file
anchor test tests/my-test.ts

Native Rust:

# Run Mollusk unit tests
cargo test

# Run integration tests
cargo test-sbf

3. Security Review

  • All account validations implemented (owner checks, signer checks)
  • No missing arithmetic overflow checks
  • PDA seeds properly validated
  • No uninitialized account usage
  • Authority checks on privileged operations
  • CPI security (check target program IDs)
  • Consider professional audit for mainnet

4. Program Size Optimization

Check current size:

ls -lh target/deploy/my_program.so

Optimization techniques:

# Cargo.toml - Release profile optimization
[profile.release]
overflow-checks = true
lto = "fat"              # Link-time optimization
codegen-units = 1        # Single codegen unit
opt-level = "z"          # Optimize for size (use "3" for speed)
strip = true             # Strip symbols

[profile.release.build-override]
opt-level = 3
incremental = false
codegen-units = 1

Remove unused dependencies:

# Check dependency tree
cargo tree

# Remove unused features
# Instead of:
# solana-program = "2.1.0"
# Use:
solana-program = { version = "2.1.0", default-features = false }

Current size limits:

  • Maximum program size: ~1 MB (actual limit varies by compute budget)
  • Recommended: Keep under 500KB for reliable deployment

5. Rent Calculation

# Calculate exact rent needed
PROGRAM_SIZE=$(wc -c < target/deploy/my_program.so)
solana rent $PROGRAM_SIZE

# Fund deployment wallet
solana balance
# If insufficient, request airdrop (devnet) or transfer SOL

Building Programs

Anchor Programs

Standard build:

anchor build

What it produces:

  • target/deploy/my_program.so - Executable binary
  • target/deploy/my_program-keypair.json - Program ID keypair
  • target/idl/my_program.json - Interface definition
  • target/types/my_program.ts - TypeScript types

Build specific program in workspace:

anchor build --program-name my_program

Verifiable build (Docker-based):

# Install solana-verify CLI
cargo install solana-verify

# Build verifiably
solana-verify build

# Build specific program
solana-verify build --library-name my_program

Sync program IDs:

# After first build, sync declared IDs with keypair
anchor keys sync

Native Rust Programs

Standard build:

cargo build-sbf

What it produces:

  • target/deploy/my_program.so - Executable binary
  • No automatic keypair generation (must provide or use deployed ID)

Verifiable build:

solana-verify build --library-name my_program

Build with specific Solana version:

# Set platform tools version
cargo build-sbf --sbf-sdk ~/.local/share/solana/install/releases/2.1.0/solana-release/bin/sdk/sbf

Understanding Build Outputs

.so file:

  • Compiled BPF bytecode
  • This is what gets deployed on-chain
  • Hash determines if program matches source

Program ID keypair (Anchor):

  • Generated on first build
  • Defines program's on-chain address
  • CRITICAL: Back this up before deploying

IDL (Anchor only):

  • JSON describing program interface
  • Used by clients to interact with program
  • Can be uploaded on-chain for discovery

Deploying to Networks

Anchor Deployment

Deploy to configured network:

# Deploy to network specified in Anchor.toml [provider] cluster
anchor deploy

# Specify network explicitly
anchor deploy --provider.cluster devnet
anchor deploy --provider.cluster mainnet

Deploy with specific program ID:

# First deployment - uses keypair from target/deploy/
anchor deploy

# Redeploy to same address (upgrade)
anchor deploy

Deploy with priority fees (congested networks):

# Set priority fee in micro-lamports per compute unit
anchor deploy --provider.cluster mainnet \
  --program-name my_program \
  -- --with-compute-unit-price 50000

What anchor deploy does:

  1. Reads program from target/deploy/my_program.so
  2. Creates or uses existing program account
  3. Uploads program data via multiple write transactions
  4. Sets executable flag on program account
  5. Optionally uploads IDL to on-chain account

Native Rust Deployment

Deploy new program:

# Deploy with auto-generated program ID
solana program deploy target/deploy/my_program.so

# Deploy to specific program ID (first time)
solana program deploy target/deploy/my_program.so \
  --program-id my_program-keypair.json

Deploy with priority fees:

solana program deploy target/deploy/my_program.so \
  --program-id <PROGRAM_ID> \
  --with-compute-unit-price 50000 \
  --max-sign-attempts 100 \
  --use-rpc

Flags explained:

  • --with-compute-unit-price - Priority fee (micro-lamports per CU)
  • --max-sign-attempts - Retries for recent blockhash expiration
  • --use-rpc - Send transactions individually vs in batches
  • -u <URL> - Specify RPC endpoint

Check deployment cost before deploying:

# Dry run to estimate cost
solana program deploy target/deploy/my_program.so --dry-run

Deploying with Specific Program ID

Generate deterministic program ID:

# Create new keypair
solana-keygen new -o my-program-keypair.json

# View address
solana-keygen pubkey my-program-keypair.json

For Anchor:

# Update lib.rs with new program ID
declare_id!("YourNewProgramID");

# Update Anchor.toml
[programs.devnet]
my_program = "YourNewProgramID"

# Rebuild and deploy
anchor build
anchor keys sync  # Verify IDs match
anchor deploy

For Native Rust:

# Deploy using keypair
solana program deploy target/deploy/my_program.so \
  --program-id my-program-keypair.json

Deployment Costs and Funding

Check balance before deployment:

solana balance

Fund wallet for devnet:

# Request airdrop (2 SOL max per request)
solana airdrop 2

# For larger programs, request multiple times
solana airdrop 2 && solana airdrop 2

Fund wallet for mainnet:

# Transfer SOL from exchange or another wallet
# No airdrops available on mainnet

Cost estimation:

# Program size
PROGRAM_SIZE=$(wc -c < target/deploy/my_program.so)

# Rent cost
solana rent $PROGRAM_SIZE

# Add ~0.002-0.01 SOL for transaction fees
# Add priority fees if network is congested

Program Upgrades

How Upgrades Work

Solana programs deployed via solana program deploy or anchor deploy are upgradeable by default.

Upgrade mechanism:

  1. Program data lives in separate account from program account
  2. Upgrade authority (wallet) can replace program data
  3. Program address stays the same
  4. All accounts/PDAs remain valid

Check if program is upgradeable:

solana program show <PROGRAM_ID>

# Output shows:
# Program Id: <PROGRAM_ID>
# Owner: BPFLoaderUpgradeable1111111111111111111111111
# ProgramData Address: <DATA_ACCOUNT>
# Authority: <UPGRADE_AUTHORITY>  # If upgradeable
# Last Deployed In Slot: ...
# Data Length: ...

Anchor Upgrades

Upgrade deployed program:

anchor upgrade target/deploy/my_program.so \
  --program-id <PROGRAM_ID> \
  --provider.cluster devnet

Note: In newer Anchor versions, anchor deploy automatically upgrades if program exists.

Upgrade with priority fees:

anchor upgrade target/deploy/my_program.so \
  --program-id <PROGRAM_ID> \
  --provider.cluster mainnet \
  -- --with-compute-unit-price 50000

Native Rust Upgrades

Upgrade using same deploy command:

solana program deploy target/deploy/my_program.so \
  --program-id <PROGRAM_ID> \
  --upgrade-authority ~/.config/solana/id.json

Extending Program Accounts

Problem: If new program is larger than allocated space:

Error: account data too small for instruction

Solution: Extend program account before upgrading:

# Check current program size
solana program show <PROGRAM_ID>
# Shows: Data Length: 363960 bytes

# New build is larger
NEW_SIZE=$(wc -c < target/deploy/my_program.so)
echo $NEW_SIZE
# Shows: 380000 bytes

# Calculate additional bytes needed
ADDITIONAL_BYTES=$((NEW_SIZE - 363960))
echo $ADDITIONAL_BYTES
# Shows: 16040 bytes

# Extend program account
solana program extend <PROGRAM_ID> $ADDITIONAL_BYTES

# Check rent cost for extension
solana rent $ADDITIONAL_BYTES
# Example: 0.2 SOL

# Now upgrade works
solana program deploy target/deploy/my_program.so --program-id <PROGRAM_ID>

Note: Program extension support added in Solana CLI v1.18+

Data Migration Strategies

Account structure changes:

When upgrading programs that change account layouts:

Option 1: Version field

#[account]
pub struct MyAccount {
    pub version: u8,  // Add version field
    pub data: u64,
    // New fields in v2
    pub new_field: Option<String>,
}

// In instruction handler
if account.version == 1 {
    // Migrate from v1 to v2
    account.new_field = Some("default".to_string());
    account.version = 2;
}

Option 2: Separate migration instruction

pub fn migrate_account_v1_to_v2(ctx: Context<MigrateAccount>) -> Result<()> {
    let account = &mut ctx.accounts.account;

    // Perform migration logic
    account.new_field = compute_new_field(&account.data);
    account.version = 2;

    Ok(())
}

Option 3: New program version with migration path

  • Deploy new program ID
  • Create migration instructions that move data
  • Deprecate old program gradually

Verified Builds

Verified builds prove deployed bytecode matches public source code.

Why Verify?

  • Transparency: Users can audit your program's source
  • Trust: Proves deployed program matches GitHub repository
  • Security: Enables community security reviews
  • Ecosystem: Explorers display verified status
  • Wallets: May whitelist verified programs

Tools for Verification

Solana Verify CLI:

cargo install solana-verify

Docker (required for deterministic builds):

Building Verifiable Programs

Verifiable build process:

# Navigate to project root (with Cargo.toml)
cd my-project

# Build in Docker container for deterministic output
solana-verify build

# For workspace with specific program
solana-verify build --library-name my_program

# Get hash of built executable
solana-verify get-executable-hash target/deploy/my_program.so

What makes builds verifiable:

  1. Docker environment: Ensures consistent build environment
  2. Locked dependencies: Cargo.lock pins exact versions
  3. Same toolchain: Uses specific Rust/Solana version
  4. Deterministic compilation: Same input → same output

Project structure requirements:

my-project/
├── Cargo.toml          # Workspace manifest
├── Cargo.lock          # Locked dependencies (required!)
├── programs/
│   └── my_program/
│       ├── Cargo.toml
│       └── src/
│           └── lib.rs

Workspace Cargo.toml example:

[workspace]
members = ["programs/*"]
resolver = "2"

[workspace.dependencies]
solana-program = "2.1.0"

[profile.release]
overflow-checks = true
lto = "fat"
codegen-units = 1

[profile.release.build-override]
opt-level = 3
incremental = false
codegen-units = 1

Deploying Verifiable Programs

Deploy verified build:

# IMPORTANT: Use the binary from solana-verify build
# DO NOT run `anchor build` or `cargo build-sbf` after verification build

# Deploy with priority fees for reliability
solana program deploy target/deploy/my_program.so \
  --program-id <PROGRAM_ID> \
  -u https://api.mainnet-beta.solana.com \
  --with-compute-unit-price 50000 \
  --max-sign-attempts 100 \
  --use-rpc

Verify deployed program matches built executable:

# Get on-chain program hash
solana-verify get-program-hash -u mainnet-beta <PROGRAM_ID>

# Get local executable hash
solana-verify get-executable-hash target/deploy/my_program.so

# Hashes must match!

Verifying Against Repository

Verify from GitHub repository:

solana-verify verify-from-repo \
  -u mainnet-beta \
  --program-id <PROGRAM_ID> \
  https://github.com/your-org/your-repo \
  --commit-hash <COMMIT_HASH> \
  --library-name my_program \
  --mount-path programs/my_program

Parameters explained:

  • --program-id: On-chain program address
  • --commit-hash: Git commit to build from (optional, uses latest if omitted)
  • --library-name: Crate name from Cargo.toml [lib] section
  • --mount-path: Path to program directory in repo (for workspaces)

Upload verification data on-chain:

When prompted during verification:

Would you like to upload verification data on-chain? (y/n)

Select yes to write verification PDA. This enables:

  • Solana Explorer verification badge
  • OtterSec API verification
  • SolanaFM verification display

Remote Verification with OtterSec API

Submit verification job:

solana-verify verify-from-repo \
  --remote \
  -u mainnet-beta \
  --program-id <PROGRAM_ID> \
  https://github.com/your-org/your-repo

Manual job submission:

solana-verify remote submit-job \
  --program-id <PROGRAM_ID> \
  --uploader <UPGRADE_AUTHORITY>

Check verification status:

solana-verify remote get-job-status --job-id <JOB_ID>

Verification API endpoint:

https://verify.osec.io/status/<PROGRAM_ID>

Where verified status appears:

security.txt Integration

Add security contact info:

#[cfg(not(feature = "no-entrypoint"))]
solana_security_txt::security_txt! {
    name: "My Program",
    project_url: "https://myproject.com",
    contacts: "email:security@myproject.com,discord:myproject",
    policy: "https://github.com/myproject/security/blob/main/SECURITY.md",
    preferred_languages: "en",
    source_code: "https://github.com/myproject/program",
    source_release: "v1.0.0",
    auditors: "Audit Firm Name"
}

Benefits:

  • Security researchers know how to contact you
  • Shows commitment to security
  • Standard across Solana ecosystem

Program Authority Management

Viewing Program Information

Check program authority:

solana program show <PROGRAM_ID>

# Output:
# Program Id: YourProgramId
# Owner: BPFLoaderUpgradeable1111111111111111111111111
# ProgramData Address: <DATA_ACCOUNT_ADDRESS>
# Authority: <CURRENT_AUTHORITY>  # Current upgrade authority
# Last Deployed In Slot: 123456789
# Data Length: 363960 bytes (0x58e38 bytes)

View all your deployed programs:

solana program show --programs

# Shows all programs where your wallet is authority

Transferring Upgrade Authority

Transfer to new authority:

solana program set-upgrade-authority <PROGRAM_ID> \
  --new-upgrade-authority <NEW_AUTHORITY_PUBKEY>

Common use cases:

  • Transfer to multisig (Squads Protocol)
  • Transfer to governance program
  • Transfer to team member
  • Transfer to DAO

Transfer to multisig (Squads):

# Get Squads vault address from https://v4.squads.so/
SQUADS_VAULT="YourSquadsVaultAddress"

solana program set-upgrade-authority <PROGRAM_ID> \
  --new-upgrade-authority $SQUADS_VAULT

Making Programs Immutable

WARNING: This is irreversible!

solana program set-upgrade-authority <PROGRAM_ID> --final

# Confirms immutability - program can NEVER be upgraded

Use cases for immutability:

  • DeFi protocols requiring immutable guarantees
  • After extensive auditing, lock the program
  • Community trust through code permanence

Considerations:

  • Cannot fix bugs after making immutable
  • Cannot add features
  • Ensure thorough testing and auditing first
  • Consider governance/multisig instead

Buffer Accounts for Deployment

Understanding buffers:

When deploying, the Solana CLI creates temporary buffer accounts:

  1. Creates buffer account
  2. Writes program data to buffer
  3. Deploys buffer to program account
  4. Closes buffer (if successful)

View your buffer accounts:

solana program show --buffers

# Output:
# Buffer Address                                | Authority                                      | Balance
# Abc123...                                     | YourWallet...                                  | 2.5 SOL

Close buffer manually (failed deployment):

solana program close <BUFFER_ADDRESS>

# Recovers rent SOL back to wallet

Common scenarios:

  • Deployment failed mid-process
  • Want to cancel deployment
  • Need to reclaim SOL from old buffers

Multisig Deployments

Deploying with multisig (Squads Protocol) provides security for production programs.

Why Use Multisig?

  • Security: No single point of failure
  • Governance: Team/DAO approval for upgrades
  • Transparency: On-chain approval trail
  • Best practice: Standard for serious projects

Workflow Overview

  1. Build verifiable program
  2. Deploy with temporary authority
  3. Verify against repository
  4. Transfer authority to multisig
  5. Export PDA transaction for verification upload
  6. Submit through Squads UI
  7. Remote verification

Detailed Multisig Deployment

1. Build verifiable program:

solana-verify build --library-name my_program

2. Deploy with your wallet as initial authority:

solana program deploy target/deploy/my_program.so \
  --program-id <PROGRAM_ID> \
  -u mainnet-beta \
  --with-compute-unit-price 50000

3. Verify locally:

solana-verify verify-from-repo \
  -u mainnet-beta \
  --program-id <PROGRAM_ID> \
  https://github.com/your-org/repo \
  --commit-hash <COMMIT>

4. Transfer authority to Squads multisig:

Get Squads vault address from https://v4.squads.so/

SQUADS_VAULT="YourSquadsVaultAddress"

solana program set-upgrade-authority <PROGRAM_ID> \
  --new-upgrade-authority $SQUADS_VAULT

5. Export verification PDA transaction:

solana-verify verify-from-repo \
  -u mainnet-beta \
  --program-id <PROGRAM_ID> \
  https://github.com/your-org/repo \
  --export-pda-tx verification_tx.json

6. Submit transaction in Squads:

  • Go to https://v4.squads.so/
  • Navigate to your multisig
  • Create new transaction
  • Import verification_tx.json
  • Get approval from multisig members
  • Execute transaction

7. Submit remote verification:

After Squads transaction executes:

solana-verify remote submit-job \
  --program-id <PROGRAM_ID> \
  --uploader <SQUADS_VAULT>

8. Monitor verification:

# Check job status
solana-verify remote get-job-status --job-id <JOB_ID>

# Or visit
https://verify.osec.io/status/<PROGRAM_ID>

Upgrading via Multisig

Create upgrade buffer:

# Build new version
solana-verify build --library-name my_program

# Write to buffer (not direct upgrade)
solana program write-buffer target/deploy/my_program.so

# Output: Buffer address: <BUFFER_ADDRESS>

Transfer buffer authority to multisig:

solana program set-buffer-authority <BUFFER_ADDRESS> \
  --new-buffer-authority <SQUADS_VAULT>

Create Squads transaction for upgrade:

Use Squads CLI or UI to propose:

# Using Squads SDK/CLI
npx ts-node scripts/program-upgrade.ts \
  --rpc "https://api.mainnet-beta.solana.com" \
  --program "<PROGRAM_ID>" \
  --buffer "<BUFFER_ADDRESS>" \
  --multisig "<MULTISIG_ADDRESS>" \
  --member "<YOUR_PUBKEY>" \
  --name "Upgrade my_program v2"

Close buffer via Squads (if needed):

npx ts-node scripts/squad-closebuffer.ts \
  --rpc "https://api.mainnet-beta.solana.com" \
  --multisig "<MULTISIG_ADDRESS>" \
  --buffer "<BUFFER_ADDRESS>" \
  --program "<PROGRAM_ID>"

Network-Specific Considerations

Localhost Development

Start test validator:

# Basic
solana-test-validator

# With program pre-deployed
solana-test-validator --bpf-program <PROGRAM_ID> target/deploy/my_program.so

# With cloned mainnet accounts
solana-test-validator \
  --clone <ACCOUNT_ADDRESS> \
  --url mainnet-beta

# Reset ledger on restart
solana-test-validator --reset

Deploy to local validator:

# Anchor
anchor localnet  # Starts validator and deploys
# Or
anchor deploy --provider.cluster localnet

# Native Rust
solana program deploy target/deploy/my_program.so -ul

Benefits:

  • Instant transaction confirmation
  • Unlimited free SOL
  • Full control over clock and state
  • Fast iteration

Limitations:

  • No network effects
  • Single validator (no consensus)
  • State doesn't persist (unless configured)

Devnet Deployment

Configure network:

solana config set --url devnet

Fund wallet:

solana airdrop 2
# Repeat as needed, max 2 SOL per request

Deploy:

# Anchor
anchor deploy --provider.cluster devnet

# Native Rust
solana program deploy target/deploy/my_program.so \
  -u devnet \
  --with-compute-unit-price 1000

Benefits:

  • Real network conditions
  • Free SOL via airdrops
  • Test integrations with other programs
  • Longer-lived state than localnet

Limitations:

  • Network resets occasionally
  • Potential rate limiting
  • Slower than localnet
  • Public network (anyone can interact)

Best practices:

  • Test all upgrade paths on devnet first
  • Monitor transaction success rates
  • Test with realistic compute budgets
  • Validate against cloned mainnet accounts

Testnet Deployment

Testnet is less commonly used but available for staging:

solana config set --url testnet
solana airdrop 2  # If available

Use cases:

  • Staging environment before mainnet
  • Testing between devnet and mainnet
  • Longer-term testing without mainnet costs

Mainnet Deployment

Pre-deployment checklist:

  • Thoroughly tested on devnet
  • Security audit completed (for DeFi/high-value programs)
  • Verified build prepared
  • Upgrade authority configured (multisig recommended)
  • Sufficient SOL for deployment (check solana rent)
  • Monitoring/alerting setup
  • Rollback plan documented
  • Team coordination for deployment time

Configure mainnet:

solana config set --url mainnet-beta

# Use paid RPC for reliability (recommended)
solana config set --url https://your-rpc-provider.com

Fund wallet:

# Transfer from exchange or another wallet
# Calculate needed SOL:
PROGRAM_SIZE=$(wc -c < target/deploy/my_program.so)
solana rent $PROGRAM_SIZE
# Add ~0.5 SOL buffer for transaction fees and priority fees

Deploy with appropriate priority fees:

Check current priority fee recommendations:

# Typical priority fee: 50,000-300,000 micro-lamports per CU
solana program deploy target/deploy/my_program.so \
  --program-id <PROGRAM_ID> \
  -u mainnet-beta \
  --with-compute-unit-price 100000 \
  --max-sign-attempts 100 \
  --use-rpc

Post-deployment verification:

# Verify deployment
solana program show <PROGRAM_ID> -u mainnet-beta

# Verify hash matches
solana-verify get-program-hash -u mainnet-beta <PROGRAM_ID>

# Submit verification job
solana-verify verify-from-repo \
  --remote \
  -u mainnet-beta \
  --program-id <PROGRAM_ID> \
  https://github.com/your-org/repo

Cost considerations:

  • 200KB program: ~1.5 SOL
  • 500KB program: ~3.5 SOL
  • 800KB program: ~5.5 SOL
  • Plus transaction fees: ~0.01-0.05 SOL
  • Priority fees during congestion: +0.1-1 SOL

Post-Deployment

Verifying Deployment Success

Check program was deployed:

solana program show <PROGRAM_ID>

# Verify output shows:
# - Correct ProgramData address
# - Your authority
# - Expected data length
# - Recent slot number

Compare program hash:

# On-chain hash
solana-verify get-program-hash -u <NETWORK> <PROGRAM_ID>

# Local build hash
solana-verify get-executable-hash target/deploy/my_program.so

# Must match exactly

Testing On-Chain

Anchor:

# Run tests against deployed program
anchor test --skip-deploy --provider.cluster devnet

# Or run specific test
anchor run test-on-devnet

Native Rust / TypeScript client:

import { Connection, PublicKey } from '@solana/web3.js';

const connection = new Connection('https://api.devnet.solana.com');
const programId = new PublicKey('YourProgramId');

// Send test transaction
const tx = await program.methods
  .yourInstruction()
  .accounts({ /* ... */ })
  .rpc();

console.log('Transaction:', tx);

Smoke tests:

  • Call each instruction with valid inputs
  • Verify account state changes
  • Check events are emitted correctly
  • Test error cases return expected errors

Monitoring Program Usage

View recent transactions:

# Get recent transactions for program
solana transaction-history <PROGRAM_ID> --limit 10

# Or use explorers:
# https://explorer.solana.com/address/<PROGRAM_ID>
# https://solscan.io/account/<PROGRAM_ID>

Set up monitoring:

Use services like:

Monitor for:

  • Transaction success rate
  • Compute unit usage
  • Error frequency
  • Unusual activity patterns

Publishing IDL (Anchor)

Upload IDL to on-chain account:

anchor idl init <PROGRAM_ID> \
  --filepath target/idl/my_program.json \
  --provider.cluster devnet

Upgrade IDL after program upgrade:

anchor idl upgrade <PROGRAM_ID> \
  --filepath target/idl/my_program.json \
  --provider.cluster devnet

Fetch published IDL:

anchor idl fetch <PROGRAM_ID> \
  --provider.cluster devnet \
  --out fetched_idl.json

Benefits of publishing IDL:

  • Clients can auto-discover your interface
  • Explorers can decode instructions
  • Reduces integration friction
  • Standard for Anchor programs

Closing Programs and Reclaiming SOL

Close buffer accounts:

# View all buffers
solana program show --buffers

# Close specific buffer
solana program close <BUFFER_ADDRESS>

# Reclaims rent SOL to wallet

Close entire program (irreversible!):

solana program close <PROGRAM_ID>

# WARNING: This deletes the program permanently
# Cannot be undone
# Only do this for test programs

When to close:

  • Failed test deployments on devnet
  • Obsolete test programs
  • Reclaim SOL from old projects

When NOT to close:

  • Any program with active users
  • Programs other contracts depend on
  • Mainnet programs (almost never)

Common Issues and Troubleshooting

Insufficient Balance

Error:

Error: Account <WALLET> has insufficient funds for spend (1.5 SOL) + fee (0.002 SOL)

Solution:

# Check current balance
solana balance

# Devnet - request airdrop
solana airdrop 2

# Mainnet - transfer SOL
# Calculate needed amount:
PROGRAM_SIZE=$(wc -c < target/deploy/my_program.so)
solana rent $PROGRAM_SIZE
# Add 0.5 SOL buffer

Program Too Large

Error:

Error: Program too large. Maximum size: 1048576 bytes

Solutions:

1. Optimize build:

[profile.release]
lto = "fat"
codegen-units = 1
opt-level = "z"  # Optimize for size
strip = true

2. Remove unused dependencies:

cargo tree  # Identify large dependencies

3. Use feature flags to exclude optional code:

[dependencies]
solana-program = { version = "2.1.0", default-features = false }

4. Split program into multiple programs:

  • Separate complex logic into multiple programs
  • Use CPI to communicate between them

Account Data Too Small for Instruction

Error:

Error: account data too small for instruction

Cause: Upgrade would exceed allocated program size.

Solution:

# Check current size
solana program show <PROGRAM_ID>
# Data Length: 363960 bytes

# Check new size
NEW_SIZE=$(wc -c < target/deploy/my_program.so)
echo $NEW_SIZE
# 380000 bytes

# Calculate difference
DIFF=$((NEW_SIZE - 363960))

# Extend program
solana program extend <PROGRAM_ID> $DIFF

# Check rent for extension
solana rent $DIFF

# Now deploy upgrade
solana program deploy target/deploy/my_program.so --program-id <PROGRAM_ID>

Network Congestion / Blockhash Expiration

Error:

Error: Transaction simulation failed: Blockhash not found

Cause: High network congestion or large program deployment.

Solutions:

1. Increase priority fees:

solana program deploy target/deploy/my_program.so \
  --with-compute-unit-price 300000 \  # Higher priority
  --max-sign-attempts 100 \
  --use-rpc

2. Use paid RPC endpoint:

solana config set --url https://your-premium-rpc.com

# Paid RPCs often have:
# - Higher rate limits
# - Better transaction success rates
# - Priority queue access

3. Deploy during low traffic:

  • Avoid peak hours (US/Europe daytime)
  • Early morning UTC often less congested

4. Break into smaller chunks:

For very large programs, manually create buffer and write in batches.

Keypair Issues

Error:

Error: Dynamic program error: Invalid keypair file

Solutions:

1. Verify keypair format:

# Should be JSON array of numbers
cat program-keypair.json
# [123, 45, 67, ...]

# Or base58 string

2. Regenerate if corrupted:

solana-keygen new -o program-keypair.json --force

3. Check file permissions:

chmod 600 program-keypair.json

Anchor Build vs Deploy Mismatch

Error:

Error: Program <ID> does not match declared program id in lib.rs

Solution:

# Sync program IDs
anchor keys sync

# Rebuilds and updates declare_id! to match keypair

Native Rust: Missing Program ID

Error:

Error: No program keypair found

Solution:

Native Rust doesn't auto-generate keypairs. Either:

Option 1: Create keypair:

solana-keygen new -o target/deploy/my_program-keypair.json
solana program deploy target/deploy/my_program.so \
  --program-id target/deploy/my_program-keypair.json

Option 2: Use existing program ID:

solana program deploy target/deploy/my_program.so \
  --program-id <EXISTING_PROGRAM_ID>

Verification Failures

Error:

Verification failed: Hash mismatch
On-chain: abc123...
Local: def456...

Causes and solutions:

1. Built outside Docker:

# Must use solana-verify build for deterministic build
solana-verify build

2. Cargo.lock mismatch:

# Ensure Cargo.lock committed to git
git add Cargo.lock
git commit -m "Add Cargo.lock for verifiable builds"

3. Rebuild after verification:

# Don't run `anchor build` or `cargo build-sbf` after solana-verify build
# This regenerates binary with different hash

# If you did, rebuild verifiably:
solana-verify build
solana program deploy target/deploy/my_program.so --program-id <PROGRAM_ID>

4. Wrong commit hash:

# Ensure you're verifying against correct commit
git log  # Find exact commit used for deployment
solana-verify verify-from-repo \
  --commit-hash <EXACT_COMMIT> \
  ...

Best Practices

Deployment Workflow

Recommended deployment process:

  1. Local development

    • Develop on localnet
    • Unit test with Mollusk (native) or Bankrun/Anchor tests
    • Iterate quickly
  2. Devnet testing

    • Deploy to devnet
    • Integration testing
    • Test upgrade paths
    • Load testing if applicable
  3. Devnet verification

    • Build verifiable
    • Deploy and verify on devnet
    • Ensure verification succeeds
  4. Security review

    • Internal code review
    • Automated analysis (Soteria, Sec3, etc.)
    • Professional audit (for mainnet)
  5. Mainnet staging

    • Build final verifiable version
    • Generate program ID
    • Set up multisig/governance
  6. Mainnet deployment

    • Deploy during low-traffic period
    • Use paid RPC
    • Set appropriate priority fees
    • Monitor closely
  7. Post-deployment

    • Submit verification
    • Smoke test critical functions
    • Set up monitoring
    • Transfer authority to multisig

Version Control

Always commit:

  • Cargo.lock (required for verified builds)
  • Program keypairs (for test programs only)
  • IDL files
  • Migration scripts

Never commit:

  • Mainnet keypairs (use environment variables)
  • Wallet private keys
  • RPC API keys

Tag deployments:

git tag -a v1.0.0-mainnet -m "Mainnet deployment v1.0.0"
git push origin v1.0.0-mainnet

Link verification to tags:

solana-verify verify-from-repo \
  --commit-hash v1.0.0-mainnet \
  ...

Backup Strategies

Critical to back up:

  1. Program keypairs

    # Mainnet program keypairs
    cp target/deploy/my_program-keypair.json ~/secure-backup/
    
    # Encrypted backup
    gpg -c target/deploy/my_program-keypair.json
    
  2. Upgrade authority keypairs

    # If not using multisig
    cp ~/.config/solana/id.json ~/secure-backup/upgrade-authority.json
    
  3. Buffer accounts during deployment

    # Save buffer address immediately after creating
    echo "Buffer: <ADDRESS>" >> deployment-log.txt
    
  4. Deployment artifacts

    • Built .so files
    • IDL files
    • Verification data
    • Transaction signatures

Backup locations:

  • Encrypted cloud storage (Google Drive, Dropbox)
  • Hardware wallet (for keypairs)
  • Offline USB drives (encrypted)
  • Team password manager (1Password, Bitwarden)

Test backup restoration:

# Periodically verify backups work
cp ~/secure-backup/my_program-keypair.json /tmp/test-restore.json
solana-keygen pubkey /tmp/test-restore.json
# Should output expected program ID

Framework-Specific Best Practices

Anchor:

  • Always run anchor keys sync after first build
  • Keep Anchor.toml in source control
  • Use workspace for multi-program projects
  • Upload IDL on-chain for discoverability
  • Version your IDL files alongside code

Native Rust:

  • Use no-entrypoint feature for testing
  • Implement security.txt for contact info
  • Document your instruction format
  • Provide client SDK (TypeScript/Rust) for integrations
  • Include example transaction builders

Testing Before Deployment

Progressive testing strategy:

# 1. Unit tests (Mollusk for native, Anchor tests)
cargo test

# 2. Integration tests on localnet
anchor test  # or cargo test-sbf

# 3. Devnet deployment test
anchor deploy --provider.cluster devnet
# Test all functions on devnet

# 4. Upgrade test on devnet
# Make small change, rebuild, upgrade
anchor build
anchor upgrade <PROGRAM_ID> target/deploy/program.so --provider.cluster devnet
# Verify upgrade worked

# 5. Verified build test
solana-verify build
solana-verify verify-from-repo --program-id <DEVNET_ID> https://github.com/...

# 6. Final smoke tests on devnet
# Run critical user flows

# 7. Mainnet deployment
# Only after all above pass

Documentation

Document your deployment:

Create DEPLOYMENT.md in your repo:

# Deployment Guide

## Program IDs

- Devnet: ABC123...
- Mainnet: DEF456...

## Build

```bash
solana-verify build --library-name my_program

Deploy

Devnet

anchor deploy --provider.cluster devnet

Mainnet

solana program deploy target/deploy/my_program.so \
  --program-id <PROGRAM_ID> \
  -u mainnet-beta \
  --with-compute-unit-price 100000

Verify

solana-verify verify-from-repo \
  --remote \
  -u mainnet-beta \
  --program-id <PROGRAM_ID> \
  https://github.com/your-org/repo \
  --commit-hash v1.0.0

Upgrade Authority

Mainnet: Squads Multisig GHI789...

Last Deployment


---

## Summary

**Key takeaways:**

1. **Always test on devnet first** - Never deploy untested code to mainnet
2. **Use verified builds for mainnet** - Transparency builds trust
3. **Calculate costs before deploying** - Use `solana rent` to estimate
4. **Set up multisig for mainnet** - Prevents single points of failure
5. **Monitor after deployment** - Watch for errors and unusual activity
6. **Back up keypairs** - Lose keypair = lose upgrade authority
7. **Document your deployments** - Future you will thank you
8. **Use priority fees on mainnet** - Ensures reliable deployment
9. **Test upgrade paths** - Practice on devnet first
10. **Never make programs immutable hastily** - Irreversible decision

**Resources:**
- Solana CLI docs: https://docs.solana.com/cli
- Anchor docs: https://www.anchor-lang.com/
- Solana Verify: https://github.com/Ellipsis-Labs/solana-verifiable-build
- OtterSec Verify API: https://verify.osec.io/
- Squads Protocol: https://squads.so/
- Security.txt: https://github.com/neodyme-labs/solana-security-txt

Deploy with confidence!