Skip to main content

sof_solana_compat/
submit_ext.rs

1use async_trait::async_trait;
2use bincode::serialize;
3use solana_signer::signers::Signers;
4use solana_transaction::versioned::VersionedTransaction;
5use thiserror::Error;
6
7use crate::{BuilderError, TxBuilder};
8
9/// Error returned by the Solana-coupled submission helpers.
10#[derive(Debug, Error)]
11pub enum SolanaCompatSubmitError {
12    /// Transaction building or signing failed before submission.
13    #[error("failed to build/sign transaction: {source}")]
14    Build {
15        /// Underlying builder/signing error.
16        source: BuilderError,
17    },
18    /// Core byte-oriented submit path failed.
19    #[error(transparent)]
20    Submit {
21        /// Underlying core submit error.
22        source: sof_tx::SubmitError,
23    },
24    /// Could not encode the signed transaction into bytes.
25    #[error("failed to encode signed transaction bytes: {message}")]
26    Encode {
27        /// Encoder error details.
28        message: String,
29    },
30}
31
32/// Solana-coupled convenience methods layered on top of `sof-tx` core byte submission.
33#[async_trait]
34pub trait TxSubmitClientSolanaExt {
35    /// Builds, signs, and submits a transaction in one API call.
36    async fn submit_unsigned<T>(
37        &mut self,
38        builder: TxBuilder,
39        signers: &T,
40        mode: sof_tx::SubmitMode,
41    ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>
42    where
43        T: Signers + Sync + ?Sized;
44
45    /// Builds, signs, and submits a transaction with explicit toxic-flow context.
46    async fn submit_unsigned_with_context<T>(
47        &mut self,
48        builder: TxBuilder,
49        signers: &T,
50        mode: sof_tx::SubmitMode,
51        context: sof_tx::TxSubmitContext,
52    ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>
53    where
54        T: Signers + Sync + ?Sized;
55
56    /// Submits one signed `VersionedTransaction`.
57    async fn submit_transaction(
58        &mut self,
59        tx: VersionedTransaction,
60        mode: sof_tx::SubmitMode,
61    ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>;
62
63    /// Submits one signed `VersionedTransaction` with explicit toxic-flow context.
64    async fn submit_transaction_with_context(
65        &mut self,
66        tx: VersionedTransaction,
67        mode: sof_tx::SubmitMode,
68        context: sof_tx::TxSubmitContext,
69    ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>;
70}
71
72#[async_trait]
73impl TxSubmitClientSolanaExt for sof_tx::TxSubmitClient {
74    async fn submit_unsigned<T>(
75        &mut self,
76        builder: TxBuilder,
77        signers: &T,
78        mode: sof_tx::SubmitMode,
79    ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>
80    where
81        T: Signers + Sync + ?Sized,
82    {
83        self.submit_unsigned_with_context(
84            builder,
85            signers,
86            mode,
87            sof_tx::TxSubmitContext::default(),
88        )
89        .await
90    }
91
92    async fn submit_unsigned_with_context<T>(
93        &mut self,
94        builder: TxBuilder,
95        signers: &T,
96        mode: sof_tx::SubmitMode,
97        context: sof_tx::TxSubmitContext,
98    ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>
99    where
100        T: Signers + Sync + ?Sized,
101    {
102        let blockhash = self
103            .refresh_latest_blockhash_bytes()
104            .await
105            .map_err(|source| SolanaCompatSubmitError::Submit {
106                source: sof_tx::SubmitError::Rpc { source },
107            })?
108            .ok_or(SolanaCompatSubmitError::Submit {
109                source: sof_tx::SubmitError::MissingRecentBlockhash,
110            })?;
111        let tx = builder
112            .build_and_sign(blockhash, signers)
113            .map_err(|source| SolanaCompatSubmitError::Build { source })?;
114        self.submit_transaction_with_context(tx, mode, context)
115            .await
116    }
117
118    async fn submit_transaction(
119        &mut self,
120        tx: VersionedTransaction,
121        mode: sof_tx::SubmitMode,
122    ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError> {
123        self.submit_transaction_with_context(tx, mode, sof_tx::TxSubmitContext::default())
124            .await
125    }
126
127    async fn submit_transaction_with_context(
128        &mut self,
129        tx: VersionedTransaction,
130        mode: sof_tx::SubmitMode,
131        context: sof_tx::TxSubmitContext,
132    ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError> {
133        let tx_bytes = serialize(&tx).map_err(|error| SolanaCompatSubmitError::Encode {
134            message: error.to_string(),
135        })?;
136        self.submit_signed_with_context(
137            sof_tx::SignedTx::VersionedTransactionBytes(tx_bytes),
138            mode,
139            context,
140        )
141        .await
142        .map_err(|source| SolanaCompatSubmitError::Submit { source })
143    }
144}