mod blockhash;
mod counter;
mod lookup;
mod native;
mod simple;
mod trace;
use {
crate::TransactionRpcProvider,
dashmap::DashMap,
moka::future::Cache,
solana_hash::Hash,
solana_message::AddressLookupTableAccount,
solana_pubkey::Pubkey,
solana_rpc_client::nonblocking::rpc_client::RpcClient,
std::{
fmt::{Debug, Display},
sync::Arc,
},
};
#[derive(Clone, bon::Builder)]
pub struct SimpleCacheTransactionProvider<
T: Clone,
L: TransactionRpcProvider,
B: TransactionRpcProvider,
> {
inner: T,
lookup_cache: Arc<LookupTableCacheProvider<L>>,
blockhash_cache: Arc<BlockHashCacheProvider<B>>,
}
pub type SimpleCacheTransactionNativeProvider<L, B> =
SimpleCacheTransactionProvider<Arc<RpcClient>, L, B>;
impl<T: AsRef<RpcClient> + Clone, L: TransactionRpcProvider, B: TransactionRpcProvider>
AsRef<RpcClient> for SimpleCacheTransactionProvider<T, L, B>
{
fn as_ref(&self) -> &RpcClient {
self.inner.as_ref()
}
}
#[derive(bon::Builder)]
pub struct LookupTableCacheProvider<T: TransactionRpcProvider> {
inner: T,
lookup_cache: Cache<Pubkey, AddressLookupTableAccount>,
negative_cache: Cache<Pubkey, ()>,
}
#[derive(bon::Builder)]
pub struct BlockHashCacheProvider<T: TransactionRpcProvider> {
inner: T,
blockhash: Cache<(), Hash>,
}
pub type TraceTransactionArcProvider = TraceTransactionProvider<Arc<RpcClient>>;
#[derive(Clone)]
pub struct TraceTransactionProvider<T: AsRef<RpcClient> + Clone>(pub T);
impl<T: AsRef<RpcClient> + Clone> AsRef<RpcClient> for TraceTransactionProvider<T> {
fn as_ref(&self) -> &RpcClient {
self.0.as_ref()
}
}
impl<T: AsRef<RpcClient> + Clone> From<T> for TraceTransactionProvider<T> {
fn from(client: T) -> Self {
Self(client)
}
}
#[derive(Eq, std::hash::Hash, PartialEq, PartialOrd)]
pub enum RpcMethod {
Blockhash,
Lookup,
Simulate,
Send,
Fees,
}
impl RpcMethod {
fn display_debug(&self) -> &str {
match self {
RpcMethod::Blockhash => "blockhash",
RpcMethod::Lookup => "lookup",
RpcMethod::Simulate => "simulate",
RpcMethod::Send => "send",
RpcMethod::Fees => "fees",
}
}
}
impl Debug for RpcMethod {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.display_debug())
}
}
impl Display for RpcMethod {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.display_debug())
}
}
#[derive(Clone)]
pub struct CounterRpcProvider<T: TransactionRpcProvider + AsRef<RpcClient> + Clone> {
inner: T,
pub(super) counters: Arc<DashMap<RpcMethod, u64>>,
}
impl<T: TransactionRpcProvider + AsRef<RpcClient> + Clone> AsRef<RpcClient>
for CounterRpcProvider<T>
{
fn as_ref(&self) -> &RpcClient {
self.inner.as_ref()
}
}
impl<T: TransactionRpcProvider + AsRef<RpcClient> + Clone> From<T> for CounterRpcProvider<T> {
fn from(inner: T) -> Self {
Self::new(inner)
}
}
impl<T: TransactionRpcProvider + AsRef<RpcClient> + Clone> CounterRpcProvider<T> {
pub fn new(inner: T) -> Self {
let counters = Arc::new(DashMap::new());
counters.insert(RpcMethod::Blockhash, 0);
counters.insert(RpcMethod::Lookup, 0);
counters.insert(RpcMethod::Simulate, 0);
counters.insert(RpcMethod::Send, 0);
counters.insert(RpcMethod::Fees, 0);
Self { inner, counters }
}
}
#[cfg(test)]
#[allow(unused_variables)]
mod noop {
use {
super::*,
crate::Result,
solana_rpc_client_api::response::{RpcPrioritizationFee, RpcSimulateTransactionResult},
solana_signature::Signature,
};
#[derive(Clone)]
#[allow(dead_code)]
pub struct NoopRpc<T: AsRef<RpcClient> + Clone>(T);
pub type NoopRpcNative = NoopRpc<Arc<RpcClient>>;
fn accept_asref<T: AsRef<RpcClient> + Send + Sync>(rpc: &T) {
}
fn accept_provider<T: TransactionRpcProvider>(rpc: &T) {
}
impl Default for NoopRpc<Arc<RpcClient>> {
fn default() -> Self {
Self(Arc::new(RpcClient::new(String::from(
"http://localhost:8899",
))))
}
}
impl<T: AsRef<RpcClient> + Clone> AsRef<RpcClient> for NoopRpc<T> {
fn as_ref(&self) -> &RpcClient {
self.0.as_ref()
}
}
impl<T: AsRef<RpcClient> + Clone> From<T> for NoopRpc<T> {
fn from(client: T) -> Self {
Self(client)
}
}
#[async_trait::async_trait]
impl<T: AsRef<RpcClient> + Send + Sync + Clone> TransactionRpcProvider for NoopRpc<T> {
async fn get_recent_prioritization_fees(
&self,
accounts: &[Pubkey],
) -> Result<Vec<RpcPrioritizationFee>> {
Ok(vec![])
}
async fn get_lookup_table_accounts(
&self,
pubkeys: &[Pubkey],
) -> Result<Vec<AddressLookupTableAccount>> {
Ok(vec![])
}
async fn get_latest_blockhash(&self) -> Result<Hash> {
Ok(Hash::new_unique())
}
async fn simulate_transaction(
&self,
tx: &solana_transaction::versioned::VersionedTransaction,
config: solana_rpc_client_api::config::RpcSimulateTransactionConfig,
) -> Result<solana_rpc_client_api::response::RpcSimulateTransactionResult> {
Ok(RpcSimulateTransactionResult {
err: None,
logs: None,
accounts: None,
units_consumed: None,
loaded_accounts_data_size: None,
return_data: None,
inner_instructions: None,
replacement_blockhash: None,
fee: None,
loaded_addresses: None,
post_balances: None,
pre_balances: None,
pre_token_balances: None,
post_token_balances: None,
})
}
async fn send_and_confirm_transaction(
&self,
tx: &solana_transaction::versioned::VersionedTransaction,
config: Option<solana_rpc_client_api::config::RpcSendTransactionConfig>,
) -> Result<Signature> {
Ok(Signature::default())
}
}
#[test]
fn test_traits() {
let rpc = NoopRpc::default();
accept_provider(&rpc);
accept_asref(&rpc);
}
}