1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
use crate::{ client_error::Result, generic_rpc_client_request::GenericRpcClientRequest, rpc_request::RpcRequest, rpc_response::{Response, RpcResponseContext}, }; use serde_json::{Number, Value}; use solana_sdk::{ fee_calculator::{FeeCalculator, FeeRateGovernor}, instruction::InstructionError, transaction::{self, TransactionError}, }; use solana_transaction_status::TransactionStatus; use std::{collections::HashMap, sync::RwLock}; pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8"; pub const SIGNATURE: &str = "43yNSFC6fYTuPgTNFFhF4axw7AfWxB2BPdurme8yrsWEYwm8299xh8n6TAHjGymiSub1XtyxTNyd9GBfY2hxoBw8"; pub type Mocks = HashMap<RpcRequest, Value>; pub struct MockRpcClientRequest { mocks: RwLock<Mocks>, url: String, } impl MockRpcClientRequest { pub fn new(url: String) -> Self { Self::new_with_mocks(url, Mocks::default()) } pub fn new_with_mocks(url: String, mocks: Mocks) -> Self { Self { url, mocks: RwLock::new(mocks), } } } impl GenericRpcClientRequest for MockRpcClientRequest { fn send( &self, request: &RpcRequest, params: serde_json::Value, _retries: usize, ) -> Result<serde_json::Value> { if let Some(value) = self.mocks.write().unwrap().remove(request) { return Ok(value); } if self.url == "fails" { return Ok(Value::Null); } let val = match request { RpcRequest::ConfirmTransaction => { if let Some(params_array) = params.as_array() { if let Value::String(param_string) = ¶ms_array[0] { Value::Bool(param_string == SIGNATURE) } else { Value::Null } } else { Value::Null } } RpcRequest::GetBalance => serde_json::to_value(Response { context: RpcResponseContext { slot: 1 }, value: Value::Number(Number::from(50)), })?, RpcRequest::GetRecentBlockhash => serde_json::to_value(Response { context: RpcResponseContext { slot: 1 }, value: ( Value::String(PUBKEY.to_string()), serde_json::to_value(FeeCalculator::default()).unwrap(), ), })?, RpcRequest::GetFeeCalculatorForBlockhash => { let value = if self.url == "blockhash_expired" { Value::Null } else { serde_json::to_value(Some(FeeCalculator::default())).unwrap() }; serde_json::to_value(Response { context: RpcResponseContext { slot: 1 }, value, })? } RpcRequest::GetFeeRateGovernor => serde_json::to_value(Response { context: RpcResponseContext { slot: 1 }, value: serde_json::to_value(FeeRateGovernor::default()).unwrap(), })?, RpcRequest::GetSignatureStatus => { let status: transaction::Result<()> = if self.url == "account_in_use" { Err(TransactionError::AccountInUse) } else if self.url == "instruction_error" { Err(TransactionError::InstructionError( 0, InstructionError::UninitializedAccount, )) } else { Ok(()) }; let status = if self.url == "sig_not_found" { None } else { Some(TransactionStatus { status, slot: 1, confirmations: Some(0), }) }; serde_json::to_value(Response { context: RpcResponseContext { slot: 1 }, value: vec![status], })? } RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)), RpcRequest::GetSlot => Value::Number(Number::from(0)), RpcRequest::SendTransaction => Value::String(SIGNATURE.to_string()), RpcRequest::GetMinimumBalanceForRentExemption => Value::Number(Number::from(1234)), _ => Value::Null, }; Ok(val) } }