soly 0.1.1

Simple Transaction Builder and Sender for Solana
Documentation
use {
    crate::{Error, Result, TransactionRpcProvider},
    base64::prelude::*,
    solana_hash::Hash,
    solana_message::AddressLookupTableAccount,
    solana_pubkey::Pubkey,
    solana_rpc_client::nonblocking::rpc_client::RpcClient,
    solana_rpc_client_api::response::RpcPrioritizationFee,
    solana_signature::Signature,
    tracing::{debug, trace},
};

#[async_trait::async_trait]
impl TransactionRpcProvider for std::sync::Arc<RpcClient> {
    async fn get_recent_prioritization_fees(
        &self,
        accounts: &[Pubkey],
    ) -> Result<Vec<RpcPrioritizationFee>> {
        debug!(accounts =? accounts.len(), "get_recent_prioritization_fees");
        self.as_ref()
            .get_recent_prioritization_fees(accounts)
            .await
            .map_err(|e| Error::SolanaRpcError(format!("failed to get prioritization fees: {e}")))
    }

    async fn get_lookup_table_accounts(
        &self,
        pubkeys: &[Pubkey],
    ) -> Result<Vec<AddressLookupTableAccount>> {
        debug!(accounts =? pubkeys.len(), "calling get_lookup_table_accounts");
        crate::lookup::fetch_lookup_tables(pubkeys, &self).await
    }

    async fn get_latest_blockhash(&self) -> Result<Hash> {
        debug!("calling get_latest_blockhash");
        self.as_ref()
            .get_latest_blockhash()
            .await
            .map_err(|e| Error::SolanaRpcError(format!("failed to get latest blockhash: {e}")))
    }

    async fn simulate_transaction(
        &self,
        tx: &solana_transaction::versioned::VersionedTransaction,
        config: solana_rpc_client_api::config::RpcSimulateTransactionConfig,
    ) -> Result<solana_rpc_client_api::response::RpcSimulateTransactionResult> {
        let result = self
            .as_ref()
            .simulate_transaction_with_config(tx, config)
            .await
            .map_err(|e| Error::SolanaRpcError(format!("failed to simulate transaction: {e}")))?;
        if let Some(e) = result.value.err {
            let logs = result.value.logs.unwrap_or(Vec::new());
            let transaction_base64 = BASE64_STANDARD.encode(bincode::serialize(&tx)?);
            let msg = format!("{e}\nbase64: {transaction_base64}\n{}", logs.join("\n"));
            return Err(Error::SolanaSimulateFailure(msg));
        }
        Ok(result.value)
    }

    async fn send_and_confirm_transaction(
        &self,
        tx: &solana_transaction::versioned::VersionedTransaction,
        config: Option<solana_rpc_client_api::config::RpcSendTransactionConfig>,
    ) -> Result<Signature> {
        if tracing::enabled!(tracing::Level::TRACE) {
            let transaction_base64 = BASE64_STANDARD.encode(bincode::serialize(&tx)?);
            trace!(send_tx =? transaction_base64);
        }
        match config {
            None => self
                .as_ref()
                .send_and_confirm_transaction(tx)
                .await
                .map_err(|e| Error::SolanaRpcError(format!("failed to send transaction: {e}"))),
            Some(config) => {
                let result = self
                    .as_ref()
                    .send_transaction_with_config(tx, config)
                    .await
                    .map_err(|e| {
                        Error::SolanaRpcError(format!("failed to send transaction: {e}"))
                    })?;

                match self.as_ref().confirm_transaction(&result).await {
                    Err(e) => Err(Error::SolanaRpcError(format!(
                        "failed to confirm transaction: {result} Error:{e}"
                    ))),
                    Ok(t) => {
                        if t {
                            Ok(result)
                        } else {
                            Err(Error::SolanaRpcError(format!(
                                "Transaction is not confirmed: {result}"
                            )))
                        }
                    }
                }
            }
        }
    }
}