1mod error;
2mod program_cache;
3mod svm;
4mod sysvars;
5pub mod token;
6
7pub use solana_clock::Clock;
8pub use solana_instruction::{AccountMeta, Instruction};
9pub use solana_instruction_error::InstructionError;
10pub use solana_pubkey::Pubkey;
11pub use solana_rent::Rent;
12pub use solana_sdk_ids;
13
14pub use solana_sdk_ids::system_program;
16
17pub use crate::error::ProgramError;
18pub use crate::program_cache::loader_keys;
19pub use crate::svm::{ExecutionResult, ExecutionTrace, ExecutedInstruction, QuasarSvm, QuasarSvmConfig};
20pub use crate::sysvars::Sysvars;
21pub use std::collections::HashMap;
22
23#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct Account {
29 pub address: Pubkey,
30 pub lamports: u64,
31 pub data: Vec<u8>,
32 pub owner: Pubkey,
33 pub executable: bool,
34}
35
36impl Account {
37 pub fn from_pair(address: Pubkey, account: solana_account::Account) -> Self {
38 Self {
39 address,
40 lamports: account.lamports,
41 data: account.data,
42 owner: account.owner,
43 executable: account.executable,
44 }
45 }
46
47 pub fn to_pair(&self) -> (Pubkey, solana_account::Account) {
48 (
49 self.address,
50 solana_account::Account {
51 lamports: self.lamports,
52 data: self.data.clone(),
53 owner: self.owner,
54 executable: self.executable,
55 rent_epoch: 0,
56 },
57 )
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
66pub struct AccountDiff {
67 pub address: Pubkey,
68 pub pre: Account,
69 pub post: Account,
70}
71
72pub const SPL_TOKEN_PROGRAM_ID: Pubkey =
77 solana_pubkey::pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
78
79pub const SPL_TOKEN_2022_PROGRAM_ID: Pubkey =
80 solana_pubkey::pubkey!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
81
82pub const SPL_ASSOCIATED_TOKEN_PROGRAM_ID: Pubkey =
83 solana_pubkey::pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
84
85impl QuasarSvm {
90 pub fn with_program(self, program_id: &Pubkey, elf: &[u8]) -> Self {
92 self.add_program(program_id, &loader_keys::LOADER_V3, elf);
93 self
94 }
95
96 pub fn with_program_loader(self, program_id: &Pubkey, loader: &Pubkey, elf: &[u8]) -> Self {
98 self.add_program(program_id, loader, elf);
99 self
100 }
101
102 pub fn with_token_program(self) -> Self {
104 let elf = include_bytes!("../programs/spl_token.so");
105 self.with_program_loader(&SPL_TOKEN_PROGRAM_ID, &loader_keys::LOADER_V2, elf)
106 }
107
108 pub fn with_token_2022_program(self) -> Self {
110 let elf = include_bytes!("../programs/spl_token_2022.so");
111 self.with_program(&SPL_TOKEN_2022_PROGRAM_ID, elf)
112 }
113
114 pub fn with_associated_token_program(self) -> Self {
116 let elf = include_bytes!("../programs/spl_associated_token.so");
117 self.with_program_loader(
118 &SPL_ASSOCIATED_TOKEN_PROGRAM_ID,
119 &loader_keys::LOADER_V2,
120 elf,
121 )
122 }
123
124 pub fn with_account(mut self, account: Account) -> Self {
126 self.set_account(account);
127 self
128 }
129
130 pub fn with_slot(mut self, slot: u64) -> Self {
132 self.sysvars.warp_to_slot(slot);
133 self
134 }
135
136 pub fn with_compute_budget(mut self, max_units: u64) -> Self {
138 self.compute_budget.compute_unit_limit = max_units;
139 self
140 }
141
142 pub fn with_airdrop(mut self, pubkey: &Pubkey, lamports: u64) -> Self {
144 self.airdrop(pubkey, lamports);
145 self
146 }
147
148 pub fn with_create_account(mut self, pubkey: &Pubkey, space: usize, owner: &Pubkey) -> Self {
150 self.create_account(pubkey, space, owner);
151 self
152 }
153}
154
155#[derive(Debug, Clone, PartialEq, Eq)]
160pub enum ExecutionStatus {
161 Success,
162 Err(ProgramError),
163}
164
165impl ExecutionResult {
170 pub fn status(&self) -> ExecutionStatus {
172 match &self.raw_result {
173 Ok(()) => ExecutionStatus::Success,
174 Err(e) => ExecutionStatus::Err(ProgramError::from(e.clone())),
175 }
176 }
177
178 pub fn is_ok(&self) -> bool {
179 self.raw_result.is_ok()
180 }
181
182 pub fn is_err(&self) -> bool {
183 self.raw_result.is_err()
184 }
185
186 pub fn unwrap(&self) {
188 if let Err(ref e) = self.raw_result {
189 panic!("{}", self.format_error(e));
190 }
191 }
192
193 pub fn expect(&self, msg: &str) {
195 if let Err(ref e) = self.raw_result {
196 panic!("{msg}: {}", self.format_error(e));
197 }
198 }
199
200 pub fn account(&self, address: &Pubkey) -> Option<&Account> {
202 self.accounts.iter().find(|a| a.address == *address)
203 }
204
205 pub fn print_logs(&self) {
207 for log in &self.logs {
208 println!(" {log}");
209 }
210 }
211
212 pub fn assert_success(&self) {
214 if let Err(ref e) = self.raw_result {
215 panic!("expected success, got: {}", self.format_error(e));
216 }
217 }
218
219 pub fn assert_error(&self, expected: ProgramError) {
221 match &self.raw_result {
222 Ok(()) => panic!("expected error {:?}, but execution succeeded", expected),
223 Err(e) => {
224 let actual = ProgramError::from(e.clone());
225 assert_eq!(
226 actual, expected,
227 "expected error {:?}, got {:?}",
228 expected, actual
229 );
230 }
231 }
232 }
233
234 fn format_error(&self, e: &InstructionError) -> String {
235 let err = ProgramError::from(e.clone());
236 if self.logs.is_empty() {
237 format!("{err}")
238 } else {
239 format!(
240 "{err}\n\nProgram logs:\n{}",
241 self.logs
242 .iter()
243 .map(|l| format!(" {l}"))
244 .collect::<Vec<_>>()
245 .join("\n")
246 )
247 }
248 }
249}