# HPSVM HPSVM is a fast, lightweight Rust library for testing Solana programs using an in-process Solana VM. It is optimized for program developers, offering significantly faster compile and execution times compared to alternatives like `solana-program-test` and `solana-test-validator`. It has no Node.js dependencies and runs entirely in-process, making it ideal for unit and integration tests in pure Rust projects. The library provides an ergonomic builder-pattern API with sane defaults and extensive configurability. Core capabilities include: submitting and simulating versioned transactions, airdropping lamports, reading and writing arbitrary account state, manipulating sysvars (e.g., `Clock`, `Rent`), controlling compute budgets and fee structures, loading custom SBF programs, registering custom syscalls, and optionally enabling low-level register tracing for VM debugging. Companion crates (`hpsvm-token`, `hpsvm-loader`) provide ready-made helpers for common SPL token operations and upgradeable program deployment. --- ## HPSVM::new Creates a fully configured test VM instance with all standard Solana features enabled: all feature flags, builtins, sysvars, feature accounts, default SPL programs, signature verification, and blockhash checking. ```rust use hpsvm::HPSVM; use solana_address::Address; use solana_keypair::Keypair; use solana_message::Message; use solana_signer::Signer; use solana_system_interface::instruction::transfer; use solana_transaction::Transaction; let from_keypair = Keypair::new(); let from = from_keypair.pubkey(); let to = Address::new_unique(); let mut svm = HPSVM::new(); svm.airdrop(&from, 1_000_000_000).unwrap(); svm.airdrop(&to, 1_000_000_000).unwrap(); let instruction = transfer(&from, &to, 64); let tx = Transaction::new( &[&from_keypair], Message::new(&[instruction], Some(&from)), svm.latest_blockhash(), ); let meta = svm.send_transaction(tx).unwrap(); assert_eq!(svm.get_account(&from).unwrap().lamports, 999994936); assert_eq!(svm.get_account(&to).unwrap().lamports, 1000000064); println!("Fee paid: {}", meta.fee); // 5000 lamports println!("CUs consumed: {}", meta.compute_units_consumed); ``` --- ## HPSVM::new_debuggable Creates a VM instance with optional register tracing support enabled. Programs are loaded with register tracing baked in at load time, so this setting cannot be changed after construction. Useful for deep debugging of SBF execution. ```rust #[cfg(feature = "register-tracing")] { use hpsvm::HPSVM; // Enable register tracing via constructor let svm = HPSVM::new_debuggable(true); // Alternatively, set the SBF_TRACE_DIR env var and use HPSVM::new() // std::env::set_var("SBF_TRACE_DIR", "/tmp/traces"); // let svm = HPSVM::new(); // auto-enables tracing } ``` --- ## HPSVM::airdrop Transfers lamports from HPSVM's internal airdrop account to any address. This is the standard way to fund accounts during tests. ```rust use hpsvm::HPSVM; use solana_keypair::Keypair; use solana_signer::Signer; let mut svm = HPSVM::new(); let user = Keypair::new(); // Fund the account with 1 SOL svm.airdrop(&user.pubkey(), 1_000_000_000).unwrap(); assert_eq!(svm.get_balance(&user.pubkey()).unwrap(), 1_000_000_000); ``` --- ## HPSVM::send_transaction Submits a signed transaction for execution. Returns `TransactionMetadata` on success, or `FailedTransactionMetadata` (containing the error and any partial metadata) on failure. Applies fees, updates account state, and records the transaction in history. ```rust use hpsvm::HPSVM; use solana_address::Address; use solana_instruction::{Instruction, account_meta::AccountMeta}; use solana_keypair::Keypair; use solana_message::{Message, VersionedMessage}; use solana_signer::Signer; use solana_transaction::versioned::VersionedTransaction; use solana_transaction_error::TransactionError; let mut svm = HPSVM::new(); let payer = Keypair::new(); let program_id = Address::new_unique(); svm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap(); let ix = Instruction { program_id, accounts: vec![AccountMeta::new(Address::new_unique(), false)], data: vec![1, 2, 3], }; let msg = Message::new_with_blockhash(&[ix], Some(&payer.pubkey()), &svm.latest_blockhash()); let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[&payer]).unwrap(); match svm.send_transaction(tx) { Ok(meta) => { println!("Success! Logs: {:?}", meta.logs); println!("CUs consumed: {}", meta.compute_units_consumed); println!("Signature: {}", meta.signature); println!("Fee: {}", meta.fee); } Err(failed) => { println!("Failed: {:?}", failed.err); println!("Logs: {:?}", failed.meta.logs); } } ``` --- ## HPSVM::simulate_transaction Simulates a transaction without committing any state changes. Returns `SimulatedTransactionInfo` (which includes `TransactionMetadata` and `post_accounts`) on success, allowing inspection of what the transaction would do. ```rust use hpsvm::HPSVM; use solana_address::Address; use solana_keypair::Keypair; use solana_message::Message; use solana_signer::Signer; use solana_system_interface::instruction::transfer; use solana_transaction::Transaction; let mut svm = HPSVM::new(); let from_kp = Keypair::new(); let to = Address::new_unique(); svm.airdrop(&from_kp.pubkey(), 1_000_000_000).unwrap(); let ix = transfer(&from_kp.pubkey(), &to, 500_000); let tx = Transaction::new( &[&from_kp], Message::new(&[ix], Some(&from_kp.pubkey())), svm.latest_blockhash(), ); let sim = svm.simulate_transaction(tx.clone()).unwrap(); println!("Would consume {} CUs", sim.meta.compute_units_consumed); println!("Would pay {} lamports fee", sim.meta.fee); println!("Post accounts: {} modified", sim.post_accounts.len()); // State is NOT modified — account balances are unchanged assert_eq!(svm.get_balance(&from_kp.pubkey()).unwrap(), 1_000_000_000); ``` --- ## HPSVM::add_program Loads a compiled SBF program (`.so` bytes) into the VM using the upgradeable loader. Must be called before sending transactions that invoke the program. ```rust use hpsvm::HPSVM; use solana_address::{Address, address}; use solana_instruction::{Instruction, account_meta::AccountMeta}; use solana_keypair::Keypair; use solana_message::Message; use solana_signer::Signer; use solana_transaction::Transaction; let mut svm = HPSVM::new(); let payer = Keypair::new(); let program_id = address!("GtdambwDgHWrDJdVPBkEHGhCwokqgAoch162teUjJse2"); // Load compiled program bytes let program_bytes = std::fs::read("target/deploy/my_program.so").unwrap(); svm.add_program(program_id, &program_bytes).unwrap(); svm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap(); let ix = Instruction { program_id, accounts: vec![AccountMeta::new(Address::new_unique(), false)], data: vec![0], }; let tx = Transaction::new( &[&payer], Message::new(&[ix], Some(&payer.pubkey())), svm.latest_blockhash(), ); let meta = svm.send_transaction(tx).unwrap(); assert!(meta.compute_units_consumed > 0); ``` --- ## HPSVM::add_program_from_file Convenience wrapper around `add_program` that reads the `.so` file from disk directly by path. ```rust use hpsvm::HPSVM; use solana_address::address; use solana_keypair::Keypair; use solana_signer::Signer; let mut svm = HPSVM::new(); let payer = Keypair::new(); let program_id = address!("HvrRMSshMx3itvsyWDnWg2E3cy5h57iMaR7oVxSZJDSA"); svm.add_program_from_file(program_id, "test_programs/target/deploy/failure.so").unwrap(); svm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap(); // program is now ready to invoke ``` --- ## HPSVM::add_program_with_loader Loads a program with a specific BPF loader ID, which affects compute unit consumption to match mainnet behavior for that loader. ```rust use hpsvm::HPSVM; use solana_address::Address; use solana_keypair::Keypair; use solana_sdk_ids::bpf_loader; use solana_signer::Signer; let mut svm = HPSVM::new(); let program_id = Address::new_unique(); let program_bytes = std::fs::read("target/deploy/legacy_program.so").unwrap(); // Load with BPFLoader2 to match CU behavior for legacy programs svm.add_program_with_loader(program_id, &program_bytes, bpf_loader::id()).unwrap(); ``` --- ## HPSVM::get_account / HPSVM::set_account Read or write the complete state of any account. `set_account` allows writing arbitrary account data regardless of whether the state would normally be achievable on-chain — useful for seeding test state like token balances. ```rust use hpsvm::HPSVM; use solana_account::Account; use solana_address::{Address, address}; use solana_program_option::COption; use solana_program_pack::Pack; use spl_associated_token_account_interface::address::get_associated_token_address; use spl_token_interface::{ ID as TOKEN_PROGRAM_ID, state::{Account as TokenAccount, AccountState}, }; let owner = Address::new_unique(); let usdc_mint = address!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); let ata = get_associated_token_address(&owner, &usdc_mint); let token_acc = TokenAccount { mint: usdc_mint, owner, amount: 1_000_000_000_000, delegate: COption::None, state: AccountState::Initialized, is_native: COption::None, delegated_amount: 0, close_authority: COption::None, }; let mut svm = HPSVM::new(); let mut token_acc_bytes = [0u8; TokenAccount::LEN]; TokenAccount::pack(token_acc, &mut token_acc_bytes).unwrap(); svm.set_account( ata, Account { lamports: 1_000_000_000, data: token_acc_bytes.to_vec(), owner: TOKEN_PROGRAM_ID, executable: false, rent_epoch: 0, }, ).unwrap(); let raw = svm.get_account(&ata).unwrap(); let parsed = TokenAccount::unpack(&raw.data).unwrap(); assert_eq!(parsed.amount, 1_000_000_000_000); ``` --- ## HPSVM::get_balance Returns the lamport balance of an account, or `None` if the account does not exist. ```rust use hpsvm::HPSVM; use solana_keypair::Keypair; use solana_signer::Signer; let mut svm = HPSVM::new(); let kp = Keypair::new(); assert!(svm.get_balance(&kp.pubkey()).is_none()); // account doesn't exist yet svm.airdrop(&kp.pubkey(), 5_000_000).unwrap(); assert_eq!(svm.get_balance(&kp.pubkey()).unwrap(), 5_000_000); ``` --- ## HPSVM::set_sysvar / HPSVM::get_sysvar Write or read any Solana sysvar (e.g., `Clock`, `Rent`, `EpochSchedule`). Writing sysvars enables "time travel" testing, such as simulating a future timestamp or slot. ```rust use hpsvm::HPSVM; use solana_address::Address; use solana_clock::Clock; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_message::{Message, VersionedMessage}; use solana_signer::Signer; use solana_transaction::versioned::VersionedTransaction; let program_id = Address::new_unique(); let mut svm = HPSVM::new(); let payer = Keypair::new(); svm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap(); // Read the current clock let mut clock = svm.get_sysvar::(); println!("Current slot: {}", clock.slot); // Set the clock to a specific unix timestamp (January 1, 2000) clock.unix_timestamp = 946684800; svm.set_sysvar::(&clock); let clock_after = svm.get_sysvar::(); assert_eq!(clock_after.unix_timestamp, 946684800); ``` --- ## HPSVM::warp_to_slot Advances the `Clock` sysvar's `slot` field to a specified slot, useful for testing time-dependent logic such as address lookup tables that cannot be used in the same slot they were created. ```rust use hpsvm::HPSVM; use solana_clock::Clock; use solana_keypair::Keypair; use solana_signer::Signer; let mut svm = HPSVM::new(); let clock_before = svm.get_sysvar::(); assert_eq!(clock_before.slot, 0); svm.warp_to_slot(100); let clock_after = svm.get_sysvar::(); assert_eq!(clock_after.slot, 100); ``` --- ## HPSVM::expire_blockhash Rotates the VM's latest blockhash to a new value, simulating blockhash expiry. Allows re-submitting logically duplicate transactions and testing blockhash validation behavior. ```rust use hpsvm::HPSVM; use solana_address::Address; use solana_keypair::Keypair; use solana_message::Message; use solana_signer::Signer; use solana_system_interface::instruction::transfer; use solana_transaction::Transaction; let mut svm = HPSVM::new(); let kp = Keypair::new(); svm.airdrop(&kp.pubkey(), 1_000_000_000).unwrap(); let to = Address::new_unique(); let ix = transfer(&kp.pubkey(), &to, 100); let old_blockhash = svm.latest_blockhash(); let tx = Transaction::new(&[&kp], Message::new(&[ix], Some(&kp.pubkey())), old_blockhash); svm.send_transaction(tx.clone()).unwrap(); // Expire blockhash so a re-send doesn't fail with AlreadyProcessed svm.expire_blockhash(); assert_ne!(svm.latest_blockhash(), old_blockhash); ``` --- ## HPSVM::get_transaction Retrieves a previously executed transaction from the in-memory history by its signature. Returns `None` if the transaction was never processed or if history is disabled. ```rust use hpsvm::HPSVM; use solana_address::Address; use solana_keypair::Keypair; use solana_message::Message; use solana_signer::Signer; use solana_system_interface::instruction::transfer; use solana_transaction::Transaction; let mut svm = HPSVM::new(); let kp = Keypair::new(); svm.airdrop(&kp.pubkey(), 1_000_000_000).unwrap(); let to = Address::new_unique(); let ix = transfer(&kp.pubkey(), &to, 100); let tx = Transaction::new( &[&kp], Message::new(&[ix], Some(&kp.pubkey())), svm.latest_blockhash(), ); let sig = tx.signatures[0]; svm.send_transaction(tx).unwrap(); let record = svm.get_transaction(&sig).unwrap(); assert!(record.is_ok()); // successful transaction ``` --- ## HPSVM::with_compute_budget Builder method to override the default compute budget for all transactions in the VM. Useful for testing programs at exact CU limits or for simulating budget exhaustion. ```rust use hpsvm::HPSVM; use solana_address::Address; use solana_compute_budget::compute_budget::ComputeBudget; use solana_instruction::error::InstructionError; use solana_keypair::Keypair; use solana_message::Message; use solana_signer::Signer; use solana_system_interface::instruction::transfer; use solana_transaction::Transaction; use solana_transaction_error::TransactionError; let from_kp = Keypair::new(); let to = Address::new_unique(); let mut svm = HPSVM::new(); svm.airdrop(&from_kp.pubkey(), 1_000_000_000).unwrap(); svm.airdrop(&to, 1_000_000_000).unwrap(); // Force a tiny compute budget to make all transactions fail let mut budget = ComputeBudget::new_with_defaults(false, false); budget.compute_unit_limit = 10; // Only 10 CUs svm = svm.with_compute_budget(budget); let ix = transfer(&from_kp.pubkey(), &to, 100); let tx = Transaction::new( &[&from_kp], Message::new(&[ix], Some(&from_kp.pubkey())), svm.latest_blockhash(), ); let err = svm.send_transaction(tx).unwrap_err(); assert_eq!( err.err, TransactionError::InstructionError(0, InstructionError::ComputationalBudgetExceeded) ); ``` --- ## HPSVM::with_sigverify / HPSVM::with_blockhash_check Builder methods to toggle signature verification and blockhash validity checking. Disabling these speeds up tests that don't need cryptographic correctness. ```rust use hpsvm::HPSVM; // Disable both for maximum test throughput (e.g., fuzzing or unit tests) let svm = HPSVM::default() .with_feature_set(agave_feature_set::FeatureSet::all_enabled()) .with_builtins() .with_sysvars() .with_sigverify(false) .with_blockhash_check(false); assert_eq!(svm.get_sigverify(), false); ``` --- ## HPSVM::with_transaction_history Controls the capacity of the transaction history ring buffer. Set to `0` to disable history entirely (allowing duplicate transactions). Defaults to storing recent transactions for replay protection. ```rust use hpsvm::HPSVM; use solana_address::Address; use solana_keypair::Keypair; use solana_message::Message; use solana_signer::Signer; use solana_system_interface::instruction::transfer; use solana_transaction::Transaction; // Disable history to allow re-sending the same transaction let mut svm = HPSVM::new().with_transaction_history(0); let kp = Keypair::new(); svm.airdrop(&kp.pubkey(), 1_000_000_000).unwrap(); let to = Address::new_unique(); let ix = transfer(&kp.pubkey(), &to, 10); let tx = Transaction::new(&[&kp], Message::new(&[ix], Some(&kp.pubkey())), svm.latest_blockhash()); svm.send_transaction(tx.clone()).unwrap(); // This would normally fail with AlreadyProcessed, but history is disabled svm.send_transaction(tx).unwrap(); ``` --- ## HPSVM::with_feature_set Replaces the active Solana feature set, controlling which protocol features are active. All dependent state (builtins, programs, precompiles) is automatically reconfigured. ```rust use agave_feature_set::FeatureSet; use hpsvm::HPSVM; // Build a VM with only specific features active let svm = HPSVM::default() .with_feature_set(FeatureSet::all_enabled()) // enable all features .with_builtins() .with_sysvars() .with_default_programs() .with_sigverify(true) .with_blockhash_check(true); ``` --- ## HPSVM::with_custom_syscall Registers a custom SBF syscall in both program runtime environments (v1 and v2). Must be called after `with_builtins()` and before `with_default_programs()`. Allows testing programs that depend on non-standard syscalls. ```rust use agave_feature_set::FeatureSet; use hpsvm::HPSVM; use solana_native_token::LAMPORTS_PER_SOL; use solana_program_runtime::{ invoke_context::InvokeContext, solana_sbpf::{declare_builtin_function, memory_region::MemoryMapping}, }; declare_builtin_function!( SyscallBurnCus, fn rust( invoke_context: &mut InvokeContext, to_burn: u64, _arg2: u64, _arg3: u64, _arg4: u64, _arg5: u64, _memory_mapping: &mut MemoryMapping, ) -> Result> { invoke_context.consume_checked(to_burn)?; Ok(0) } ); let svm = HPSVM::default() .with_feature_set(FeatureSet::all_enabled()) .with_builtins() .with_custom_syscall("sol_burn_cus", SyscallBurnCus::vm) .with_lamports(1_000_000u64.wrapping_mul(LAMPORTS_PER_SOL)) .with_sysvars() .with_default_programs() .with_sigverify(true) .with_blockhash_check(true); ``` --- ## HPSVM::minimum_balance_for_rent_exemption Returns the minimum lamport balance needed to make an account with the given data length rent-exempt under the current `Rent` sysvar settings. ```rust use hpsvm::HPSVM; let svm = HPSVM::new(); let min_for_empty = svm.minimum_balance_for_rent_exemption(0); let min_for_token_account = svm.minimum_balance_for_rent_exemption(165); // token account size println!("Rent-exempt for empty account: {} lamports", min_for_empty); println!("Rent-exempt for token account: {} lamports", min_for_token_account); ``` --- ## HPSVM::add_builtin Registers a native Rust function as an on-chain builtin program, accessible at a given `program_id`. Used for testing programs that depend on custom native programs or for mocking existing builtins. ```rust use hpsvm::HPSVM; use solana_address::Address; use solana_program_runtime::invoke_context::InvokeContext; fn my_builtin_entrypoint( _invoke_context: &mut InvokeContext, ) { // custom logic here } let mut svm = HPSVM::new(); let program_id = Address::new_unique(); svm.add_builtin(program_id, my_builtin_entrypoint); ``` --- ## HPSVM::set_invocation_inspect_callback (feature: invocation-inspect-callback) Installs a custom callback invoked before and after each transaction's program execution, providing access to the full `InvokeContext` for low-level introspection including register traces. ```rust #[cfg(feature = "register-tracing")] { use std::sync::{Arc, Mutex}; use hpsvm::{HPSVM, InvocationInspectCallback}; use solana_program_runtime::invoke_context::{Executable, InvokeContext, RegisterTrace}; use solana_transaction::sanitized::SanitizedTransaction; use solana_transaction_context::{IndexOfAccount, InstructionContext}; use solana_address::Address; struct TraceCounter { count: Arc> } impl InvocationInspectCallback for TraceCounter { fn before_invocation(&self, _: &HPSVM, _: &SanitizedTransaction, _: &[IndexOfAccount], _: &InvokeContext) {} fn after_invocation(&self, _: &HPSVM, invoke_context: &InvokeContext, register_tracing_enabled: bool) { if register_tracing_enabled { invoke_context.iterate_vm_traces(&|_ctx: InstructionContext, _exe: &Executable, trace: RegisterTrace| { *self.count.lock().unwrap() += trace.len(); }); } } } let counter = Arc::new(Mutex::new(0usize)); let mut svm = HPSVM::new_debuggable(true); svm.set_invocation_inspect_callback(TraceCounter { count: Arc::clone(&counter) }); // ... run transactions ... println!("Total register snapshots: {}", *counter.lock().unwrap()); } ``` --- ## TransactionMetadata The result type returned by `send_transaction` on success. Contains all observable effects of a transaction: logs, inner instructions, compute units consumed, return data, signature, and fee charged. ```rust use hpsvm::HPSVM; use solana_address::Address; use solana_keypair::Keypair; use solana_message::Message; use solana_signer::Signer; use solana_system_interface::instruction::transfer; use solana_transaction::Transaction; let mut svm = HPSVM::new(); let kp = Keypair::new(); let to = Address::new_unique(); svm.airdrop(&kp.pubkey(), 1_000_000_000).unwrap(); let meta = svm.send_transaction(Transaction::new( &[&kp], Message::new(&[transfer(&kp.pubkey(), &to, 100)], Some(&kp.pubkey())), svm.latest_blockhash(), )).unwrap(); println!("Signature: {}", meta.signature); println!("Fee: {} lamports", meta.fee); println!("CUs: {}", meta.compute_units_consumed); println!("Logs:\n{}", meta.pretty_logs()); // formatted for readability println!("Inner ixs: {} groups", meta.inner_instructions.len()); ``` --- ## Durable Nonce Transactions HPSVM supports durable nonce accounts for submitting transactions that remain valid even after a blockhash has expired. ```rust use hpsvm::HPSVM; use solana_keypair::Keypair; use solana_message::Message; use solana_signer::Signer; use solana_system_interface::instruction::{advance_nonce_account, create_nonce_account, transfer}; use solana_transaction::Transaction; use solana_account::{ReadableAccount, state_traits::StateMut}; use solana_nonce::{state::{Data, State as NonceState}, versions::Versions}; let mut svm = HPSVM::new(); let payer_kp = Keypair::new(); let nonce_kp = Keypair::new(); let recipient = solana_address::Address::new_unique(); svm.airdrop(&payer_kp.pubkey(), 1_000_000_000).unwrap(); svm.airdrop(&recipient, 1_000_000_000).unwrap(); // Create nonce account let create_ixns = create_nonce_account(&payer_kp.pubkey(), &nonce_kp.pubkey(), &payer_kp.pubkey(), 1_500_000); let tx = Transaction::new( &[&payer_kp, &nonce_kp], Message::new_with_blockhash(&create_ixns, Some(&payer_kp.pubkey()), &svm.latest_blockhash()), svm.latest_blockhash(), ); svm.send_transaction(tx).unwrap(); // Use nonce as blockhash replacement let nonce_raw = svm.get_account(&nonce_kp.pubkey()).unwrap(); let versions: Versions = StateMut::::state(&nonce_raw).unwrap(); let nonce = match NonceState::from(versions) { NonceState::Initialized(data) => data.blockhash(), _ => panic!("not initialized"), }; // Expire the blockhash — nonce tx still works svm.expire_blockhash(); let tx = Transaction::new( &[&payer_kp], Message::new_with_blockhash( &[advance_nonce_account(&nonce_kp.pubkey(), &payer_kp.pubkey()), transfer(&payer_kp.pubkey(), &recipient, 1)], Some(&payer_kp.pubkey()), &nonce, ), nonce, ); svm.send_transaction(tx).unwrap(); ``` --- ## hpsvm-token: CreateMint The `hpsvm-token` companion crate provides ergonomic builder-pattern helpers for SPL Token instructions. `CreateMint` creates a new token mint account with configurable authority, freeze authority, and decimals. ```rust use hpsvm::HPSVM; use hpsvm_token::{CreateMint, get_spl_account, spl_token::state::Mint}; use solana_keypair::Keypair; use solana_native_token::LAMPORTS_PER_SOL; use solana_signer::Signer; let svm = &mut HPSVM::new(); let payer = Keypair::new(); let authority = Keypair::new(); svm.airdrop(&payer.pubkey(), 10 * LAMPORTS_PER_SOL).unwrap(); let mint_pk = CreateMint::new(svm, &payer) .authority(&authority.pubkey()) .decimals(6) .send() .unwrap(); let mint: Mint = get_spl_account(svm, &mint_pk).unwrap(); assert_eq!(mint.decimals, 6); assert_eq!(mint.supply, 0); assert!(mint.is_initialized); println!("Mint created: {}", mint_pk); ``` --- ## hpsvm-token: MintTo / Transfer / Burn Builder helpers for the most common token operations. Each follows the same pattern: `new(svm, payer, ...)`, optional `.owner()` or `.multisig()`, then `.send()`. ```rust use hpsvm::HPSVM; use hpsvm_token::{ CreateAssociatedTokenAccount, CreateMint, MintTo, Transfer, Burn, get_spl_account, spl_token::state::Account as TokenAccount, }; use solana_keypair::Keypair; use solana_native_token::LAMPORTS_PER_SOL; use solana_signer::Signer; let svm = &mut HPSVM::new(); let payer = Keypair::new(); let owner = Keypair::new(); svm.airdrop(&payer.pubkey(), 10 * LAMPORTS_PER_SOL).unwrap(); let mint = CreateMint::new(svm, &payer).authority(&owner.pubkey()).send().unwrap(); // Create token accounts let source_ata = CreateAssociatedTokenAccount::new(svm, &payer, &mint) .owner(&owner.pubkey()) .send() .unwrap(); let dest_ata = CreateAssociatedTokenAccount::new(svm, &payer, &mint).send().unwrap(); // Mint 1000 tokens MintTo::new(svm, &payer, &mint, &source_ata, 1000) .owner(&owner) .send() .unwrap(); let acct: TokenAccount = get_spl_account(svm, &source_ata).unwrap(); assert_eq!(acct.amount, 1000); // Transfer 400 tokens Transfer::new(svm, &payer, &mint, &dest_ata, 400) .source(&source_ata) .owner(&owner) .send() .unwrap(); // Burn 100 tokens Burn::new(svm, &payer, &mint, &source_ata, 100) .owner(&owner) .send() .unwrap(); let acct: TokenAccount = get_spl_account(svm, &source_ata).unwrap(); assert_eq!(acct.amount, 500); // 1000 minted - 400 transferred - 100 burned ``` --- ## hpsvm-loader: deploy_upgradeable_program / set_upgrade_authority The `hpsvm-loader` companion crate provides helpers for deploying and managing upgradeable programs via the `BPFLoaderUpgradeable` protocol. ```rust use hpsvm::HPSVM; use hpsvm_loader::{deploy_upgradeable_program, set_upgrade_authority}; use solana_keypair::Keypair; use solana_native_token::LAMPORTS_PER_SOL; use solana_signer::Signer; let mut svm = HPSVM::new(); let payer = Keypair::new(); let program_kp = Keypair::new(); let new_authority = Keypair::new(); svm.airdrop(&payer.pubkey(), 100 * LAMPORTS_PER_SOL).unwrap(); let program_bytes = std::fs::read("target/deploy/my_program.so").unwrap(); // Deploy program via full on-chain upgradeable loader flow deploy_upgradeable_program(&mut svm, &payer, &program_kp, &program_bytes).unwrap(); println!("Deployed at: {}", program_kp.pubkey()); // Transfer upgrade authority set_upgrade_authority( &mut svm, &payer, &program_kp.pubkey(), &payer, Some(&new_authority.pubkey()), ).unwrap(); ``` --- ## Priority Fees with ComputeBudgetInstruction HPSVM accurately charges priority fees. Use `ComputeBudgetInstruction` to set compute unit price and limit within a transaction. ```rust use hpsvm::HPSVM; use solana_address::Address; use solana_compute_budget_interface::ComputeBudgetInstruction; use solana_keypair::Keypair; use solana_message::Message; use solana_native_token::LAMPORTS_PER_SOL; use solana_rent::Rent; use solana_signer::Signer; use solana_system_interface::instruction::transfer; use solana_transaction::Transaction; let mut svm = HPSVM::new(); let payer = Keypair::new(); let to = Address::new_unique(); // priority_fee = price(1_000_000 µ-lamports) * limit(10_000 CUs) / 1_000_000 = 10_000 lamports let base_fee = 5000u64; let priority_fee = 10_000u64; let total_fee = base_fee + priority_fee; svm.airdrop(&payer.pubkey(), svm.get_sysvar::().minimum_balance(0) + total_fee + 100).unwrap(); svm.airdrop(&to, LAMPORTS_PER_SOL).unwrap(); let tx = Transaction::new( &[&payer], Message::new( &[ ComputeBudgetInstruction::set_compute_unit_price(1_000_000), ComputeBudgetInstruction::set_compute_unit_limit(10_000), transfer(&payer.pubkey(), &to, 100), ], Some(&payer.pubkey()), ), svm.latest_blockhash(), ); let meta = svm.send_transaction(tx).unwrap(); assert_eq!(meta.fee, total_fee); // 15_000 lamports total ``` --- ## Summary HPSVM is the recommended choice for any pure Rust Solana program testing workflow. Its primary use cases are: writing fast unit and integration tests for on-chain programs, simulating transactions to estimate CUs and fees before sending, seeding arbitrary account state to test program behavior against real-world token accounts (USDC, wSOL, etc.), manipulating time via the `Clock` sysvar to test lockups and vesting schedules, and debugging program execution at the SBF register level with the `register-tracing` feature. The library replaces the need for a running validator node and eliminates the slow startup and RPC overhead of `solana-test-validator`. For teams building production Solana programs, HPSVM integrates seamlessly into standard Rust test harnesses via `cargo test`. Companion crates `hpsvm-token` and `hpsvm-loader` reduce boilerplate for SPL Token operations and upgradeable program deployment. The builder API allows fine-grained control over the VM configuration — feature sets, compute budgets, signature verification, transaction history, log byte limits, and custom syscalls — making it possible to precisely replicate mainnet conditions or deliberately break invariants for negative testing. When true RPC-level fidelity is required (e.g., testing `getProgramAccounts` or validator-specific behavior), `solana-test-validator` remains the appropriate complement.