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#[derive(Debug, Error)]
11pub enum SolanaCompatSubmitError {
12 #[error("failed to build/sign transaction: {source}")]
14 Build {
15 source: BuilderError,
17 },
18 #[error(transparent)]
20 Submit {
21 source: sof_tx::SubmitError,
23 },
24 #[error("failed to encode signed transaction bytes: {message}")]
26 Encode {
27 message: String,
29 },
30}
31
32#[async_trait]
34pub trait TxSubmitClientSolanaExt {
35 async fn submit_unsigned_via<T>(
37 &mut self,
38 builder: TxBuilder,
39 signers: &T,
40 plan: sof_tx::SubmitPlan,
41 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>
42 where
43 T: Signers + Sync + ?Sized;
44
45 async fn submit_unsigned<T>(
47 &mut self,
48 builder: TxBuilder,
49 signers: &T,
50 mode: sof_tx::SubmitMode,
51 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>
52 where
53 T: Signers + Sync + ?Sized;
54
55 async fn submit_unsigned_with_context_via<T>(
57 &mut self,
58 builder: TxBuilder,
59 signers: &T,
60 plan: sof_tx::SubmitPlan,
61 context: sof_tx::TxSubmitContext,
62 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>
63 where
64 T: Signers + Sync + ?Sized;
65
66 async fn submit_unsigned_with_context<T>(
68 &mut self,
69 builder: TxBuilder,
70 signers: &T,
71 mode: sof_tx::SubmitMode,
72 context: sof_tx::TxSubmitContext,
73 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>
74 where
75 T: Signers + Sync + ?Sized;
76
77 async fn submit_transaction_via(
79 &mut self,
80 tx: VersionedTransaction,
81 plan: sof_tx::SubmitPlan,
82 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>;
83
84 async fn submit_transaction(
86 &mut self,
87 tx: VersionedTransaction,
88 mode: sof_tx::SubmitMode,
89 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>;
90
91 async fn submit_transaction_with_context_via(
93 &mut self,
94 tx: VersionedTransaction,
95 plan: sof_tx::SubmitPlan,
96 context: sof_tx::TxSubmitContext,
97 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>;
98
99 async fn submit_transaction_with_context(
101 &mut self,
102 tx: VersionedTransaction,
103 mode: sof_tx::SubmitMode,
104 context: sof_tx::TxSubmitContext,
105 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>;
106}
107
108#[async_trait]
109impl TxSubmitClientSolanaExt for sof_tx::TxSubmitClient {
110 async fn submit_unsigned_via<T>(
111 &mut self,
112 builder: TxBuilder,
113 signers: &T,
114 plan: sof_tx::SubmitPlan,
115 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>
116 where
117 T: Signers + Sync + ?Sized,
118 {
119 self.submit_unsigned_with_context_via(
120 builder,
121 signers,
122 plan,
123 sof_tx::TxSubmitContext::default(),
124 )
125 .await
126 }
127
128 async fn submit_unsigned<T>(
129 &mut self,
130 builder: TxBuilder,
131 signers: &T,
132 mode: sof_tx::SubmitMode,
133 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>
134 where
135 T: Signers + Sync + ?Sized,
136 {
137 self.submit_unsigned_with_context_via(
138 builder,
139 signers,
140 sof_tx::SubmitPlan::from(mode),
141 sof_tx::TxSubmitContext::default(),
142 )
143 .await
144 }
145
146 async fn submit_unsigned_with_context_via<T>(
147 &mut self,
148 builder: TxBuilder,
149 signers: &T,
150 plan: sof_tx::SubmitPlan,
151 context: sof_tx::TxSubmitContext,
152 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>
153 where
154 T: Signers + Sync + ?Sized,
155 {
156 let blockhash = self
157 .refresh_latest_blockhash_bytes()
158 .await
159 .map_err(|source| SolanaCompatSubmitError::Submit {
160 source: sof_tx::SubmitError::Rpc { source },
161 })?
162 .ok_or(SolanaCompatSubmitError::Submit {
163 source: sof_tx::SubmitError::MissingRecentBlockhash,
164 })?;
165 let tx = builder
166 .build_and_sign(blockhash, signers)
167 .map_err(|source| SolanaCompatSubmitError::Build { source })?;
168 self.submit_transaction_with_context_via(tx, plan, context)
169 .await
170 }
171
172 async fn submit_unsigned_with_context<T>(
173 &mut self,
174 builder: TxBuilder,
175 signers: &T,
176 mode: sof_tx::SubmitMode,
177 context: sof_tx::TxSubmitContext,
178 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError>
179 where
180 T: Signers + Sync + ?Sized,
181 {
182 self.submit_unsigned_with_context_via(
183 builder,
184 signers,
185 sof_tx::SubmitPlan::from(mode),
186 context,
187 )
188 .await
189 }
190
191 async fn submit_transaction_via(
192 &mut self,
193 tx: VersionedTransaction,
194 plan: sof_tx::SubmitPlan,
195 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError> {
196 self.submit_transaction_with_context_via(tx, plan, sof_tx::TxSubmitContext::default())
197 .await
198 }
199
200 async fn submit_transaction(
201 &mut self,
202 tx: VersionedTransaction,
203 mode: sof_tx::SubmitMode,
204 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError> {
205 self.submit_transaction_with_context_via(
206 tx,
207 sof_tx::SubmitPlan::from(mode),
208 sof_tx::TxSubmitContext::default(),
209 )
210 .await
211 }
212
213 async fn submit_transaction_with_context_via(
214 &mut self,
215 tx: VersionedTransaction,
216 plan: sof_tx::SubmitPlan,
217 context: sof_tx::TxSubmitContext,
218 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError> {
219 let tx_bytes = serialize(&tx).map_err(|error| SolanaCompatSubmitError::Encode {
220 message: error.to_string(),
221 })?;
222 self.submit_signed_with_context_via(
223 sof_tx::SignedTx::VersionedTransactionBytes(tx_bytes),
224 plan,
225 context,
226 )
227 .await
228 .map_err(|source| SolanaCompatSubmitError::Submit { source })
229 }
230
231 async fn submit_transaction_with_context(
232 &mut self,
233 tx: VersionedTransaction,
234 mode: sof_tx::SubmitMode,
235 context: sof_tx::TxSubmitContext,
236 ) -> Result<sof_tx::SubmitResult, SolanaCompatSubmitError> {
237 self.submit_transaction_with_context_via(tx, sof_tx::SubmitPlan::from(mode), context)
238 .await
239 }
240}