Deploying Programs
Learn how to deploy programs to a LiteSVM test instance
Getting Started
let mut svm = LiteSVM::new();
This creates the basic litesvm test instance, which includes all runtime features enabled, default sysvars, precompiles, spl programs, sigverify, and all built in programs like the system program.
To interact with any other program in your tests, you must deploy that program to your test environment.
-
Build and deploy the program you want to test
-
Find all programs that the above program makes CPI calls to and deploy those programs
Example: If your program is using data feeds from Pyth, you will need to have the Pyth program deployed to your test instance.
Basic Program Deployment
Method 1: Deploy from Bytes
Deploy programs directly from byte arrays for fastest test execution:
use litesvm::LiteSVM;
use solana_sdk::signature::{read_keypair_file, Signer};
let mut svm = LiteSVM::new();
// Read the program's keypair to get correct ID
let program_keypair = read_keypair_file("target/deploy/my_program-keypair.json")
.expect("Program keypair file not found");
let program_id = program_keypair.pubkey();
// Include bytes at compile time
let program_bytes = include_bytes!("../target/deploy/my_program.so");
// Deploy from bytes
svm.add_program(program_id, program_bytes)
.expect("Failed to deploy program");
// Verify deployment
assert!(svm.get_account(&program_id).is_some(), "Program account not created");
assert!(svm.get_account(&program_id).unwrap().executable, "Program not executable");
include_bytes!
embeds the binary directly into the test executable at
compile time, eliminating I/O during test execution.
✓ Pros
- •Faster at runtime (no disk reads)
- •Self-contained test binary
- •Works even if .so file is deleted after compilation
✗ Cons
- •Larger test binary size
- •Must recompile tests when .so changes
Method 2: Deploy from File (.so)
Load programs from the file system when they change frequently:
use litesvm::LiteSVM;
use solana_sdk::signature::{read_keypair_file, Signer};
let mut svm = LiteSVM::new();
// Read keypair for correct program ID
let program_keypair = read_keypair_file("target/deploy/program-keypair.json").unwrap();
let program_id = program_keypair.pubkey();
// Deploy from file
svm.add_program_from_file(program_id, "target/deploy/program.so")
.expect("Failed to deploy program from file");
// Always verify
assert!(svm.get_account(&program_id).unwrap().executable);
✓ Pros
- •Smaller test binary
- •Can pick up .so changes without recompiling tests
- •More flexible for CI/CD pipelines
✗ Cons
- •Slower (disk I/O at runtime)
- •Requires .so file to exist at test execution time
- •File path must be correct relative to test execution directory
Pulling Programs from Mainnet/Devnet
When you need a program that you don't have the code for, use the Solana CLI to dump programs from live clusters and use them in tests:
# Dump a program from mainnet
solana program dump gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s pyth.so --url mainnet-beta
# Dump from devnet
solana program dump YourProgramID program.so --url devnet
Then load it in your tests:
svm.add_program_from_file(
pubkey!("gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s"),
"pyth.so"
).unwrap();
Best Practices
1. Choose the Right Deployment Method
-
Use
add_program()
when:- Programs are embedded in your test binary at compile time
- You want faster test execution (no runtime I/O)
- Self-contained tests
-
Use
add_program_from_file()
when:- Programs are built separately (i.e. pulling from devnet)
- Program change frequently during development (can rerun tests without recompiling)
- You have CI/CD environments where .so files are artifacts
Understanding Program Keypairs
When you build a Solana program:
- Anchor creates
target/deploy/program-keypair.json
- Native builds create a keypair file alongside the
.so
file - The program's on-chain address is this keypair's public key, which is also known as the program ID.
- For PDAs,
find_program_address()
requires the program ID as a seed - For CPIs, the program ID is used to call other programs
Common Issues and Solutions
Silent Deployment Failures
Problem: Program appears to deploy but transactions fail with "InvalidProgramForExecution".
Cause: Program ID doesn't match the keypair.
Solution:
// Always use the keypair-derived ID
let keypair = read_keypair_file("target/deploy/program-keypair.json").unwrap();
let program_id = keypair.pubkey();
svm.add_program(program_id, &program_bytes).unwrap();
// Verify with detailed checks
let account = svm.get_account(&program_id)
.expect("Program account doesn't exist");
assert!(account.executable, "Program not executable");
assert_eq!(account.owner, solana_sdk::bpf_loader::id(), "Wrong owner");
assert!(!account.data.is_empty(), "Program data is empty");
println!("✅ Program verified at {}", program_id);
Program Not Found Error
Error: TransactionError::InvalidProgramForExecution
Common Causes:
- Program not deployed
- Wrong program ID used in instruction
- Program ID mismatch with keypair
Debugging Steps:
// Step 1: Check if program exists
if let Some(account) = svm.get_account(&program_id) {
println!("Program exists");
println!(" Executable: {}", account.executable);
println!(" Owner: {}", account.owner);
println!(" Data length: {}", account.data.len());
} else {
println!("Program does not exist at {}", program_id);
}
// Step 2: Verify the ID matches keypair
let expected_keypair = read_keypair_file("target/deploy/program-keypair.json").unwrap();
let expected_id = expected_keypair.pubkey();
if program_id != expected_id {
println!(" ID mismatch!");
println!(" Used: {}", program_id);
println!(" Expected: {}", expected_id);
}
// Step 3: Check instruction program ID
println!("Instruction program_id: {}", instruction.program_id);
assert_eq!(instruction.program_id, program_id, "Instruction uses wrong program ID");
CPI to Undeployed Programs
Problem: Your program makes a CPI call to a program that is not deployed.
Solution: Deploy all dependency programs:
// Find all dependency programs and make sure they are deployed
svm.add_program_from_file(
pubkey!("gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s"),
"pyth.so"
).unwrap();
If you need to pull the program from a cluster, review this section.
Insufficient Lamports for Deployment
Note: LiteSVM automatically handles rent-exempt balance for programs.
// No need to manually fund - LiteSVM handles this
svm.add_program(program_id, &program_bytes).unwrap();
// But you can check the rent if needed
let program_len = program_bytes.len();
let required_lamports = svm.minimum_balance_for_rent_exemption(program_len);
println!("Program requires {} lamports for rent exemption", required_lamports);
Deployment Verification Checklist
Read the program keypair
let keypair = read_keypair_file("target/deploy/program-keypair.json").unwrap();
let program_id = keypair.pubkey();
Deploy with matching ID rust svm.add_program(program_id, &program_bytes).unwrap();
Verify deployment rust assert!(svm.get_account(&program_id).unwrap().executable);
Deploy any programs your program calls via CPI to the litesvm test instance
Next Steps
In the next section, we'll learn how to execute program instructions and handle complex transaction patterns.