waterpump-solana-core 0.1.0

Core Solana utilities: RPC context, transaction builder, retry helpers
Documentation
//! Extension trait for signing, sending, and simulating Solana transactions.

use anyhow::Result;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::{
    hash::Hash,
    signature::{Keypair, Signature},
    transaction::VersionedTransaction,
};

/// Extension methods for [`VersionedTransaction`] that simplify the
/// sign-send-confirm lifecycle.
pub trait TransactionExtension {
    /// Sign (or re-sign) the transaction with a fresh blockhash and signers.
    fn sign_transaction(
        &mut self,
        recent_blockhash: &Hash,
        signers: &[&Keypair],
    ) -> Result<VersionedTransaction>;

    /// Sign and send the transaction, returning the signature.
    #[allow(async_fn_in_trait)]
    async fn send_transaction(
        &mut self,
        rpc_client: &RpcClient,
        signers: &[&Keypair],
    ) -> Result<Signature>;

    /// Sign, send, and wait for confirmation, returning the signature.
    #[allow(async_fn_in_trait)]
    async fn send_and_confirm(
        &mut self,
        rpc_client: &RpcClient,
        signers: &[&Keypair],
    ) -> Result<Signature>;

    /// Sign and simulate the transaction without sending it on-chain.
    #[allow(async_fn_in_trait)]
    async fn simulate(
        &mut self,
        rpc_client: &RpcClient,
        signers: &[&Keypair],
    ) -> Result<solana_client::rpc_response::RpcSimulateTransactionResult>;
}

impl TransactionExtension for VersionedTransaction {
    fn sign_transaction(
        &mut self,
        recent_blockhash: &Hash,
        signers: &[&Keypair],
    ) -> Result<VersionedTransaction> {
        self.message.set_recent_blockhash(*recent_blockhash);
        // Create a new signed transaction from the existing message
        let signed_tx = VersionedTransaction::try_new(self.message.clone(), signers)
            .map_err(|e| anyhow::anyhow!("Failed to sign transaction: {}", e))?;

        // Update self with the signed transaction
        *self = signed_tx.clone();

        Ok(signed_tx)
    }

    async fn send_transaction(
        &mut self,
        rpc_client: &RpcClient,
        signers: &[&Keypair],
    ) -> Result<Signature> {
        let recent_blockhash = rpc_client.get_latest_blockhash().await?;
        // Sign the transaction first
        self.sign_transaction(&recent_blockhash, signers)?;

        // Send the transaction
        let signature = rpc_client
            .send_transaction(self)
            .await
            .map_err(|e| anyhow::anyhow!("Failed to send transaction: {}", e))?;

        Ok(signature)
    }

    async fn send_and_confirm(
        &mut self,
        rpc_client: &RpcClient,
        signers: &[&Keypair],
    ) -> Result<Signature> {
        let recent_blockhash = rpc_client.get_latest_blockhash().await?;
        // Sign the transaction first
        self.sign_transaction(&recent_blockhash, signers)?;

        // Send and confirm the transaction
        let signature = rpc_client
            .send_and_confirm_transaction(self)
            .await
            .map_err(|e| anyhow::anyhow!("Failed to send and confirm transaction: {}", e))?;

        Ok(signature)
    }

    async fn simulate(
        &mut self,
        rpc_client: &RpcClient,
        signers: &[&Keypair],
    ) -> Result<solana_client::rpc_response::RpcSimulateTransactionResult> {
        let recent_blockhash = rpc_client.get_latest_blockhash().await?;
        // Sign the transaction first
        self.sign_transaction(&recent_blockhash, signers)?;

        // Simulate the transaction
        let response = rpc_client
            .simulate_transaction(self)
            .await
            .map_err(|e| anyhow::anyhow!("Failed to simulate transaction: {}", e))?;

        if let Some(err) = response.value.err {
            return Err(anyhow::anyhow!("Failed to simulate transaction: {}", err));
        }

        Ok(response.value)
    }
}