use crate::account::AccountError;
use crate::program::Program;
use anchor_lang::AccountDeserialize;
use litesvm::LiteSVM;
use litesvm_utils::TransactionResult;
use solana_hash::Hash;
use solana_keypair::Keypair;
use solana_program::pubkey::Pubkey;
use solana_signature::Signature;
use solana_signer::Signer;
use solana_transaction::Transaction;
pub struct AnchorContext {
pub svm: LiteSVM,
pub program_id: Pubkey,
payer: Keypair,
program: Program,
}
impl AnchorContext {
pub fn new(mut svm: LiteSVM, program_id: Pubkey) -> Self {
let payer = Keypair::new();
svm.airdrop(&payer.pubkey(), 10_000_000_000).unwrap();
let program = Program::new(program_id);
Self {
svm,
program_id,
payer,
program,
}
}
pub(crate) fn new_with_payer(svm: LiteSVM, program_id: Pubkey, payer: Keypair) -> Self {
let program = Program::new(program_id);
Self {
svm,
program_id,
payer,
program,
}
}
pub fn program(&self) -> Program {
self.program
}
pub fn payer(&self) -> &Keypair {
&self.payer
}
pub fn execute_instruction(
&mut self,
instruction: solana_program::instruction::Instruction,
signers: &[&Keypair],
) -> Result<TransactionResult, Box<dyn std::error::Error>> {
let payer_pubkey = if !signers.is_empty() {
signers[0].pubkey()
} else {
self.payer.pubkey()
};
let tx = Transaction::new_signed_with_payer(
&[instruction.clone()],
Some(&payer_pubkey),
signers,
self.svm.latest_blockhash(),
);
match self.svm.send_transaction(tx) {
Ok(result) => Ok(TransactionResult::new(
result,
Some(format!("instruction to {}", instruction.program_id)),
)),
Err(failed) => Ok(TransactionResult::new_failed(
format!("{:?}", failed.err),
failed.meta,
Some(format!("instruction to {}", instruction.program_id)),
)),
}
}
pub fn execute_instructions(
&mut self,
instructions: Vec<solana_program::instruction::Instruction>,
signers: &[&Keypair],
) -> Result<TransactionResult, Box<dyn std::error::Error>> {
let payer_pubkey = if !signers.is_empty() {
signers[0].pubkey()
} else {
self.payer.pubkey()
};
let tx = Transaction::new_signed_with_payer(
&instructions,
Some(&payer_pubkey),
signers,
self.svm.latest_blockhash(),
);
match self.svm.send_transaction(tx) {
Ok(result) => Ok(TransactionResult::new(
result,
Some("batch transaction".to_string()),
)),
Err(failed) => Ok(TransactionResult::new_failed(
format!("{:?}", failed.err),
failed.meta,
Some("batch transaction".to_string()),
)),
}
}
pub fn send_and_confirm_transaction(
&mut self,
transaction: &Transaction,
) -> Result<Signature, Box<dyn std::error::Error>> {
match self.svm.send_transaction(transaction.clone()) {
Ok(_) => Ok(transaction.signatures[0]),
Err(e) => Err(format!("Transaction failed: {:?}", e).into()),
}
}
pub fn get_account<T>(&self, address: &Pubkey) -> Result<T, AccountError>
where
T: AccountDeserialize,
{
let account_data = self
.svm
.get_account(address)
.ok_or(AccountError::AccountNotFound(*address))?;
let mut data = account_data.data.as_slice();
T::try_deserialize(&mut data).map_err(|e| AccountError::DeserializationError(e.to_string()))
}
pub fn get_account_unchecked<T>(&self, address: &Pubkey) -> Result<T, AccountError>
where
T: AccountDeserialize,
{
let account_data = self
.svm
.get_account(address)
.ok_or(AccountError::AccountNotFound(*address))?;
let mut data = account_data.data.as_slice();
T::try_deserialize_unchecked(&mut data)
.map_err(|e| AccountError::DeserializationError(e.to_string()))
}
pub fn create_funded_account(
&mut self,
lamports: u64,
) -> Result<Keypair, Box<dyn std::error::Error>> {
let account = Keypair::new();
self.svm
.airdrop(&account.pubkey(), lamports)
.map_err(|e| format!("Airdrop failed: {:?}", e))?;
Ok(account)
}
pub fn airdrop(
&mut self,
pubkey: &Pubkey,
lamports: u64,
) -> Result<(), Box<dyn std::error::Error>> {
self.svm
.airdrop(pubkey, lamports)
.map_err(|e| format!("Airdrop failed: {:?}", e))?;
Ok(())
}
pub fn latest_blockhash(&self) -> Hash {
self.svm.latest_blockhash()
}
pub fn account_exists(&self, pubkey: &Pubkey) -> bool {
self.svm.get_account(pubkey).is_some()
}
}