Installation
Make sure you have all the needed dependencies:
cargo add --dev litesvm litesvm-loader solana-keypair solana-signer
If your test invokes the deployed program directly, also add the Solana instruction and transaction crates used by your test:
cargo add --dev solana-instruction solana-message solana-transaction
What is litesvm-loader?
The litesvm-loader crate provides helpers for deploying programs through the
BPF upgradeable loader inside LiteSVM. Use it when your test needs loader-owned
program accounts, program data accounts, or upgrade authority behavior instead
of directly inserting a program with svm.add_program(...).
Upgradeable Deployment
- Creates the loader buffer account
- Writes program bytes in chunks
- Deploys the final program account with the BPF upgradeable loader
- Uses the provided program keypair as the program ID
Upgrade Authority Management - Changes the upgrade authority for an
already deployed program - Supports assigning a new authority - Supports
passing None to make the program immutable
Real Loader State
- Exercises the same loader account model your program sees on-chain
- Lets tests inspect program and program data accounts
- Helps catch mistakes hidden by direct program insertion
For most tests, svm.add_program(program_id, program_bytes) is still the
fastest and simplest way to load a program. Use litesvm-loader when the
loader account layout or upgrade authority is part of what you need to test.
Quick Example
Here's a complete example that deploys a program through the upgradeable loader and then rotates its authority:
use litesvm::LiteSVM;use litesvm_loader::{deploy_upgradeable_program, set_upgrade_authority};use solana_keypair::Keypair;use solana_signer::Signer;#[test]fn test_upgradeable_deployment() {let mut svm = LiteSVM::new();let payer = Keypair::new();svm.airdrop(&payer.pubkey(), 10_000_000_000).unwrap();// Use the keypair that should own the program ID.// For Anchor programs, this is usually target/deploy/<program>-keypair.json.let program = Keypair::new();let program_bytes = include_bytes!("../target/deploy/my_program.so");deploy_upgradeable_program(&mut svm, &payer, &program, program_bytes).unwrap();let program_account = svm.get_account(&program.pubkey()).unwrap();assert!(program_account.executable);let new_authority = Keypair::new();set_upgrade_authority(&mut svm,&payer,&program.pubkey(),&payer,Some(&new_authority.pubkey()),).unwrap();}
Choosing the Program Keypair
deploy_upgradeable_program uses the program_kp argument as the program
address. If your program declares a fixed ID, load the generated deploy keypair
instead of creating a random keypair:
use solana_keypair::read_keypair_file;let program = read_keypair_file("target/deploy/my_program-keypair.json").unwrap();deploy_upgradeable_program(&mut svm, &payer, &program, program_bytes).unwrap();
Invoking the Deployed Program
After deployment, call the program the same way you would call any
LiteSVM-loaded program. Build an instruction for your program, sign the
transaction, and send it through the same LiteSVM instance:
use solana_instruction::Instruction;use solana_message::Message;use solana_transaction::Transaction;let instruction = Instruction::new_with_bytes(program.pubkey(),&[], // instruction data for your programvec![], // account metas for your program);let message = Message::new(&[instruction], Some(&payer.pubkey()));let tx = Transaction::new(&[&payer], message, svm.latest_blockhash());svm.send_transaction(tx).unwrap();
Key Components
deploy_upgradeable_program
| Argument | Description |
|---|---|
svm | Mutable LiteSVM instance that receives the deployed program |
payer_kp | Fee payer and initial upgrade authority |
program_kp | Keypair whose public key becomes the program ID |
program_bytes | Compiled SBF program bytes, usually from target/deploy/*.so |
deploy_upgradeable_program creates a loader buffer, writes the program bytes
in 512-byte chunks, and deploys the program with room for future upgrades.
set_upgrade_authority
| Argument | Description |
|---|---|
svm | Mutable LiteSVM instance containing the deployed program |
from_keypair | Fee payer and signing authority for the transaction |
program_address | Program ID whose authority should change |
current_authority_keypair | Current upgrade authority keypair |
new_authority_address | New authority address, or None to make the program immutable |
In the current helper implementation, the generated transaction is signed by
from_keypair. Pass the current authority as from_keypair, or keep the
payer and current authority the same, when changing upgrade authority.
Common Workflows
Deploy with the declared program ID
use solana_keypair::read_keypair_file;let program = read_keypair_file("target/deploy/my_program-keypair.json").unwrap();let program_bytes = include_bytes!("../target/deploy/my_program.so");deploy_upgradeable_program(&mut svm, &payer, &program, program_bytes).unwrap();
Make a program immutable
set_upgrade_authority(&mut svm,&payer,&program.pubkey(),&payer,None,).unwrap();
Troubleshooting
Common Errors
| Error | Cause | Solution |
|---|---|---|
ProgramAccountNotFound or instruction failures against the wrong ID | The deployment used a random Keypair while the program expects a declared ID | Read target/deploy/<program>-keypair.json and pass that keypair to deploy_upgradeable_program |
InsufficientFunds | The payer does not have enough lamports for loader buffer and program accounts | Airdrop more lamports before deployment |
MissingRequiredSignature when changing authority | The current authority did not sign the transaction | Pass the current authority as from_keypair, or keep payer and current authority as the same keypair |
| Program deployment is slower than expected | Loader deployment writes bytes through real loader instructions | Use svm.add_program(...) when you do not need loader state or upgrade authority behavior |
Is this page helpful?