mod error;
mod program_cache;
mod svm;
mod sysvars;
pub mod token;
pub use solana_clock::Clock;
pub use solana_instruction::{AccountMeta, Instruction};
pub use solana_instruction_error::InstructionError;
pub use solana_pubkey::Pubkey;
pub use solana_rent::Rent;
pub use solana_sdk_ids;
pub use solana_sdk_ids::system_program;
pub use crate::error::ProgramError;
pub use crate::program_cache::loader_keys;
pub use crate::svm::{ExecutionResult, ExecutionTrace, ExecutedInstruction, QuasarSvm, QuasarSvmConfig};
pub use crate::sysvars::Sysvars;
pub use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Account {
pub address: Pubkey,
pub lamports: u64,
pub data: Vec<u8>,
pub owner: Pubkey,
pub executable: bool,
}
impl Account {
pub fn from_pair(address: Pubkey, account: solana_account::Account) -> Self {
Self {
address,
lamports: account.lamports,
data: account.data,
owner: account.owner,
executable: account.executable,
}
}
pub fn to_pair(&self) -> (Pubkey, solana_account::Account) {
(
self.address,
solana_account::Account {
lamports: self.lamports,
data: self.data.clone(),
owner: self.owner,
executable: self.executable,
rent_epoch: 0,
},
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccountDiff {
pub address: Pubkey,
pub pre: Account,
pub post: Account,
}
pub const SPL_TOKEN_PROGRAM_ID: Pubkey =
solana_pubkey::pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
pub const SPL_TOKEN_2022_PROGRAM_ID: Pubkey =
solana_pubkey::pubkey!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
pub const SPL_ASSOCIATED_TOKEN_PROGRAM_ID: Pubkey =
solana_pubkey::pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
impl QuasarSvm {
pub fn with_program(self, program_id: &Pubkey, elf: &[u8]) -> Self {
self.add_program(program_id, &loader_keys::LOADER_V3, elf);
self
}
pub fn with_program_loader(self, program_id: &Pubkey, loader: &Pubkey, elf: &[u8]) -> Self {
self.add_program(program_id, loader, elf);
self
}
pub fn with_token_program(self) -> Self {
let elf = include_bytes!("../programs/spl_token.so");
self.with_program_loader(&SPL_TOKEN_PROGRAM_ID, &loader_keys::LOADER_V2, elf)
}
pub fn with_token_2022_program(self) -> Self {
let elf = include_bytes!("../programs/spl_token_2022.so");
self.with_program(&SPL_TOKEN_2022_PROGRAM_ID, elf)
}
pub fn with_associated_token_program(self) -> Self {
let elf = include_bytes!("../programs/spl_associated_token.so");
self.with_program_loader(
&SPL_ASSOCIATED_TOKEN_PROGRAM_ID,
&loader_keys::LOADER_V2,
elf,
)
}
pub fn with_account(mut self, account: Account) -> Self {
self.set_account(account);
self
}
pub fn with_slot(mut self, slot: u64) -> Self {
self.sysvars.warp_to_slot(slot);
self
}
pub fn with_compute_budget(mut self, max_units: u64) -> Self {
self.compute_budget.compute_unit_limit = max_units;
self
}
pub fn with_airdrop(mut self, pubkey: &Pubkey, lamports: u64) -> Self {
self.airdrop(pubkey, lamports);
self
}
pub fn with_create_account(mut self, pubkey: &Pubkey, space: usize, owner: &Pubkey) -> Self {
self.create_account(pubkey, space, owner);
self
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExecutionStatus {
Success,
Err(ProgramError),
}
impl ExecutionResult {
pub fn status(&self) -> ExecutionStatus {
match &self.raw_result {
Ok(()) => ExecutionStatus::Success,
Err(e) => ExecutionStatus::Err(ProgramError::from(e.clone())),
}
}
pub fn is_ok(&self) -> bool {
self.raw_result.is_ok()
}
pub fn is_err(&self) -> bool {
self.raw_result.is_err()
}
pub fn unwrap(&self) {
if let Err(ref e) = self.raw_result {
panic!("{}", self.format_error(e));
}
}
pub fn expect(&self, msg: &str) {
if let Err(ref e) = self.raw_result {
panic!("{msg}: {}", self.format_error(e));
}
}
pub fn account(&self, address: &Pubkey) -> Option<&Account> {
self.accounts.iter().find(|a| a.address == *address)
}
pub fn print_logs(&self) {
for log in &self.logs {
println!(" {log}");
}
}
pub fn assert_success(&self) {
if let Err(ref e) = self.raw_result {
panic!("expected success, got: {}", self.format_error(e));
}
}
pub fn assert_error(&self, expected: ProgramError) {
match &self.raw_result {
Ok(()) => panic!("expected error {:?}, but execution succeeded", expected),
Err(e) => {
let actual = ProgramError::from(e.clone());
assert_eq!(
actual, expected,
"expected error {:?}, got {:?}",
expected, actual
);
}
}
}
fn format_error(&self, e: &InstructionError) -> String {
let err = ProgramError::from(e.clone());
if self.logs.is_empty() {
format!("{err}")
} else {
format!(
"{err}\n\nProgram logs:\n{}",
self.logs
.iter()
.map(|l| format!(" {l}"))
.collect::<Vec<_>>()
.join("\n")
)
}
}
}