Quick Start
Learn how to use the litesvm-loader crate for testing upgradeable program deployment
Installation
Make sure you have all the needed dependencies:
cargo add --dev litesvm litesvm-loader solana-keypair solana-signerIf 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-transactionWhat 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
Noneto 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 program
vec![], // 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 |