substrate_api_client/api/rpc_api/
author.rs1use crate::{
17 api::{rpc_api::events::FetchEvents, Error, Result},
18 rpc::{HandleSubscription, Request, Subscribe},
19 Api, ExtrinsicReport, TransactionStatus, XtStatus,
20};
21use ac_compose_macros::rpc_params;
22use ac_primitives::{config::Config, UncheckedExtrinsic};
23#[cfg(all(not(feature = "sync-api"), not(feature = "std")))]
24use alloc::boxed::Box;
25use codec::{Decode, Encode};
26use log::*;
27use serde::de::DeserializeOwned;
28use sp_core::Bytes;
29use sp_runtime::traits::Hash as HashTrait;
30
31pub type TransactionSubscriptionFor<Client, Hash> =
32 <Client as Subscribe>::Subscription<TransactionStatus<Hash, Hash>>;
33
34#[maybe_async::maybe_async(?Send)]
36pub trait SubmitExtrinsic {
37 type Hash;
38
39 async fn submit_extrinsic<Address, Call, Signature, TransactionExtension>(
42 &self,
43 extrinsic: UncheckedExtrinsic<Address, Call, Signature, TransactionExtension>,
44 ) -> Result<Self::Hash>
45 where
46 Address: Encode,
47 Call: Encode,
48 Signature: Encode,
49 TransactionExtension: Encode;
50
51 async fn submit_opaque_extrinsic(&self, encoded_extrinsic: &Bytes) -> Result<Self::Hash>;
54}
55
56#[maybe_async::maybe_async(?Send)]
57impl<T, Client> SubmitExtrinsic for Api<T, Client>
58where
59 T: Config,
60 Client: Request,
61{
62 type Hash = T::Hash;
63
64 async fn submit_extrinsic<Address, Call, Signature, TransactionExtension>(
65 &self,
66 extrinsic: UncheckedExtrinsic<Address, Call, Signature, TransactionExtension>,
67 ) -> Result<Self::Hash>
68 where
69 Address: Encode,
70 Call: Encode,
71 Signature: Encode,
72 TransactionExtension: Encode,
73 {
74 self.submit_opaque_extrinsic(&extrinsic.encode().into()).await
75 }
76
77 async fn submit_opaque_extrinsic(&self, encoded_extrinsic: &Bytes) -> Result<Self::Hash> {
78 let hex_encoded_xt = rpc_params![encoded_extrinsic];
79 debug!("sending extrinsic: {hex_encoded_xt:?}");
80 let xt_hash = self.client().request("author_submitExtrinsic", hex_encoded_xt).await?;
81 Ok(xt_hash)
82 }
83}
84
85#[maybe_async::maybe_async(?Send)]
86pub trait SubmitAndWatch {
87 type Client: Subscribe;
88 type Hash: DeserializeOwned + Decode + Encode;
89
90 async fn submit_and_watch_extrinsic<Address, Call, Signature, TransactionExtension>(
95 &self,
96 extrinsic: UncheckedExtrinsic<Address, Call, Signature, TransactionExtension>,
97 ) -> Result<TransactionSubscriptionFor<Self::Client, Self::Hash>>
98 where
99 Address: Encode,
100 Call: Encode,
101 Signature: Encode,
102 TransactionExtension: Encode;
103
104 async fn submit_and_watch_opaque_extrinsic(
109 &self,
110 encoded_extrinsic: &Bytes,
111 ) -> Result<TransactionSubscriptionFor<Self::Client, Self::Hash>>;
112
113 async fn submit_and_watch_extrinsic_until<Address, Call, Signature, TransactionExtension>(
134 &self,
135 extrinsic: UncheckedExtrinsic<Address, Call, Signature, TransactionExtension>,
136 watch_until: XtStatus,
137 ) -> Result<ExtrinsicReport<Self::Hash>>
138 where
139 Address: Encode,
140 Call: Encode,
141 Signature: Encode,
142 TransactionExtension: Encode;
143
144 async fn submit_and_watch_opaque_extrinsic_until(
165 &self,
166 encoded_extrinsic: &Bytes,
167 watch_until: XtStatus,
168 ) -> Result<ExtrinsicReport<Self::Hash>>;
169
170 async fn submit_and_watch_extrinsic_until_without_events<
182 Address,
183 Call,
184 Signature,
185 TransactionExtension,
186 >(
187 &self,
188 extrinsic: UncheckedExtrinsic<Address, Call, Signature, TransactionExtension>,
189 watch_until: XtStatus,
190 ) -> Result<ExtrinsicReport<Self::Hash>>
191 where
192 Address: Encode,
193 Call: Encode,
194 Signature: Encode,
195 TransactionExtension: Encode;
196
197 async fn submit_and_watch_opaque_extrinsic_until_without_events(
209 &self,
210 encoded_extrinsic: &Bytes,
211 watch_until: XtStatus,
212 ) -> Result<ExtrinsicReport<Self::Hash>>;
213
214 async fn populate_events(&self, report: &mut ExtrinsicReport<Self::Hash>) -> Result<()>;
219}
220
221#[maybe_async::maybe_async(?Send)]
222impl<T, Client> SubmitAndWatch for Api<T, Client>
223where
224 T: Config,
225 Client: Subscribe + Request,
226{
227 type Client = Client;
228 type Hash = T::Hash;
229
230 async fn submit_and_watch_extrinsic<Address, Call, Signature, TransactionExtension>(
231 &self,
232 extrinsic: UncheckedExtrinsic<Address, Call, Signature, TransactionExtension>,
233 ) -> Result<TransactionSubscriptionFor<Self::Client, Self::Hash>>
234 where
235 Address: Encode,
236 Call: Encode,
237 Signature: Encode,
238 TransactionExtension: Encode,
239 {
240 self.submit_and_watch_opaque_extrinsic(&extrinsic.encode().into()).await
241 }
242
243 async fn submit_and_watch_opaque_extrinsic(
244 &self,
245 encoded_extrinsic: &Bytes,
246 ) -> Result<TransactionSubscriptionFor<Self::Client, Self::Hash>> {
247 self.client()
248 .subscribe(
249 "author_submitAndWatchExtrinsic",
250 rpc_params![encoded_extrinsic],
251 "author_unsubmitAndWatchExtrinsic",
252 )
253 .await
254 .map_err(|e| e.into())
255 }
256
257 async fn submit_and_watch_extrinsic_until<Address, Call, Signature, TransactionExtension>(
258 &self,
259 extrinsic: UncheckedExtrinsic<Address, Call, Signature, TransactionExtension>,
260 watch_until: XtStatus,
261 ) -> Result<ExtrinsicReport<Self::Hash>>
262 where
263 Address: Encode,
264 Call: Encode,
265 Signature: Encode,
266 TransactionExtension: Encode,
267 {
268 self.submit_and_watch_opaque_extrinsic_until(&extrinsic.encode().into(), watch_until)
269 .await
270 }
271
272 async fn submit_and_watch_opaque_extrinsic_until(
273 &self,
274 encoded_extrinsic: &Bytes,
275 watch_until: XtStatus,
276 ) -> Result<ExtrinsicReport<Self::Hash>> {
277 let mut report = self
278 .submit_and_watch_opaque_extrinsic_until_without_events(encoded_extrinsic, watch_until)
279 .await?;
280
281 if watch_until < XtStatus::InBlock {
282 return Ok(report)
283 }
284 self.populate_events(&mut report).await?;
285 report.check_events_for_dispatch_error(self.metadata())?;
286 Ok(report)
287 }
288
289 async fn populate_events(&self, report: &mut ExtrinsicReport<Self::Hash>) -> Result<()> {
290 if report.events.is_some() {
291 return Err(Error::EventsAlreadyPresent)
292 }
293 let block_hash = report.block_hash.ok_or(Error::BlockHashNotFound)?;
294 let extrinsic_events =
295 self.fetch_events_for_extrinsic(block_hash, report.extrinsic_hash).await?;
296 report.add_events(extrinsic_events);
297 Ok(())
298 }
299
300 async fn submit_and_watch_extrinsic_until_without_events<
301 Address,
302 Call,
303 Signature,
304 TransactionExtension,
305 >(
306 &self,
307 extrinsic: UncheckedExtrinsic<Address, Call, Signature, TransactionExtension>,
308 watch_until: XtStatus,
309 ) -> Result<ExtrinsicReport<Self::Hash>>
310 where
311 Address: Encode,
312 Call: Encode,
313 Signature: Encode,
314 TransactionExtension: Encode,
315 {
316 self.submit_and_watch_opaque_extrinsic_until_without_events(
317 &extrinsic.encode().into(),
318 watch_until,
319 )
320 .await
321 }
322
323 async fn submit_and_watch_opaque_extrinsic_until_without_events(
324 &self,
325 encoded_extrinsic: &Bytes,
326 watch_until: XtStatus,
327 ) -> Result<ExtrinsicReport<Self::Hash>> {
328 let tx_hash = T::Hasher::hash(encoded_extrinsic);
329 let mut subscription: TransactionSubscriptionFor<Self::Client, Self::Hash> =
330 self.submit_and_watch_opaque_extrinsic(encoded_extrinsic).await?;
331
332 while let Some(transaction_status) = subscription.next().await {
333 let transaction_status = transaction_status?;
334 match transaction_status.is_expected() {
335 Ok(_) =>
336 if transaction_status.reached_status(watch_until) {
337 subscription.unsubscribe().await?;
338 let block_hash = transaction_status.get_maybe_block_hash();
339 return Ok(ExtrinsicReport::new(
340 tx_hash,
341 block_hash.copied(),
342 transaction_status,
343 None,
344 ))
345 },
346 Err(e) => {
347 subscription.unsubscribe().await?;
348 return Err(e)
349 },
350 }
351 }
352 Err(Error::NoStream)
353 }
354}