polymesh_api_client/
transaction.rs

1use jsonrpsee::core::client::Subscription;
2
3#[cfg(not(feature = "std"))]
4use alloc::format;
5use codec::{Decode, Encode};
6use sp_std::prelude::*;
7
8use async_trait::async_trait;
9
10#[cfg(feature = "serde")]
11use serde::{de::DeserializeOwned, ser::Serialize};
12
13use crate::*;
14
15#[cfg(feature = "serde")]
16pub trait RuntimeTraits:
17  Clone + Encode + Decode + Serialize + DeserializeOwned + core::fmt::Debug
18{
19}
20
21#[cfg(not(feature = "serde"))]
22pub trait RuntimeTraits: Clone + Encode + Decode + core::fmt::Debug {}
23
24#[cfg(feature = "serde")]
25impl<T> RuntimeTraits for T where
26  T: Clone + Encode + Decode + Serialize + DeserializeOwned + core::fmt::Debug
27{
28}
29
30#[cfg(not(feature = "serde"))]
31impl<T> RuntimeTraits for T where T: Clone + Encode + Decode + core::fmt::Debug {}
32
33pub trait RuntimeEnumTraits: RuntimeTraits + EnumInfo {}
34
35impl<T> RuntimeEnumTraits for T where T: RuntimeTraits + EnumInfo {}
36
37pub trait EnumInfo: Into<&'static str> {
38  fn as_name(&self) -> &'static str;
39  fn as_docs(&self) -> &'static [&'static str];
40  fn as_short_doc(&self) -> &'static str {
41    self.as_docs()[0]
42  }
43}
44
45#[derive(Clone, Debug)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47pub enum ExtrinsicResult<Api: ChainApi + ?Sized> {
48  Success(Api::DispatchInfo),
49  Failed(Api::DispatchInfo, Api::DispatchError),
50}
51
52impl<Api: ChainApi> ExtrinsicResult<Api> {
53  pub fn is_success(&self) -> bool {
54    match self {
55      Self::Success(_) => true,
56      Self::Failed(_, _) => false,
57    }
58  }
59
60  pub fn is_failed(&self) -> bool {
61    match self {
62      Self::Success(_) => false,
63      Self::Failed(_, _) => true,
64    }
65  }
66
67  pub fn ok(&self) -> Result<()> {
68    match self {
69      Self::Success(_) => Ok(()),
70      Self::Failed(_, err) => Err(Error::ExtrinsicError(format!("{}", err.as_short_doc()))),
71    }
72  }
73}
74
75#[async_trait]
76pub trait ChainApi: Clone {
77  type RuntimeCall: RuntimeEnumTraits;
78  type RuntimeEvent: RuntimeEnumTraits;
79  type DispatchInfo: RuntimeTraits;
80  type DispatchError: RuntimeEnumTraits;
81
82  async fn get_nonce(&self, account: AccountId) -> Result<u32>;
83
84  async fn block_events(
85    &self,
86    block: Option<BlockHash>,
87  ) -> Result<Vec<EventRecord<Self::RuntimeEvent>>>;
88
89  fn event_to_extrinsic_result(
90    event: &EventRecord<Self::RuntimeEvent>,
91  ) -> Option<ExtrinsicResult<Self>>;
92
93  fn events_to_extrinsic_result(
94    events: &[EventRecord<Self::RuntimeEvent>],
95  ) -> Option<ExtrinsicResult<Self>> {
96    // Search backwards, since the event we want is normally the last.
97    events
98      .iter()
99      .rev()
100      .find_map(Self::event_to_extrinsic_result)
101  }
102
103  fn client(&self) -> &Client;
104
105  /// Submit a signed/unsigned transaction, but don't wait for it to execute.
106  async fn submit_and_watch(&self, xt: ExtrinsicV4) -> Result<TransactionResults<Self>> {
107    let (tx_hex, tx_hash) = xt.as_hex_and_hash();
108    let status = self.client().submit_and_watch(tx_hex).await?;
109    Ok(TransactionResults::new(self, status, tx_hash))
110  }
111}
112
113pub struct TransactionResults<Api: ChainApi> {
114  api: Api,
115  sub: Option<Subscription<TransactionStatus>>,
116  tx_hash: TxHash,
117  status: Option<TransactionStatus>,
118  block: Option<BlockHash>,
119  events: Option<EventRecords<Api::RuntimeEvent>>,
120  extrinsic_result: Option<ExtrinsicResult<Api>>,
121  finalized: bool,
122}
123
124impl<Api: ChainApi> TransactionResults<Api> {
125  pub fn new(api: &Api, sub: Subscription<TransactionStatus>, tx_hash: TxHash) -> Self {
126    Self {
127      api: api.clone(),
128      sub: Some(sub),
129      tx_hash,
130      status: None,
131      block: None,
132      events: None,
133      extrinsic_result: None,
134      finalized: false,
135    }
136  }
137
138  async fn next_status(&mut self) -> Result<bool> {
139    if let Some(sub) = &mut self.sub {
140      match sub.next().await {
141        None => {
142          // End of stream, no more updates possible.
143          self.sub = None;
144          Ok(false)
145        }
146        Some(Ok(status)) => {
147          use TransactionStatus::*;
148          // Got an update.
149          match status {
150            InBlock(block) => {
151              self.block = Some(block);
152            }
153            Finalized(block) => {
154              self.finalized = true;
155              self.block = Some(block);
156            }
157            Future | Ready | Broadcast(_) => (),
158            Retracted(_) => {
159              // The transaction is back in the pool.  Might be included in a future block.
160              self.block = None;
161            }
162            _ => {
163              // Call failed to be included in a block or finalized.
164              self.block = None;
165              self.sub = None;
166            }
167          }
168          self.status = Some(status);
169          Ok(true)
170        }
171        Some(Err(err)) => {
172          // Error waiting for an update.  Most likely the connection was closed.
173          self.sub = None;
174          Err(err)?
175        }
176      }
177    } else {
178      Ok(false)
179    }
180  }
181
182  pub async fn events(&mut self) -> Result<Option<&EventRecords<Api::RuntimeEvent>>> {
183    self.load_events().await?;
184    Ok(self.events.as_ref())
185  }
186
187  pub async fn extrinsic_result(&mut self) -> Result<Option<&ExtrinsicResult<Api>>> {
188    self.load_events().await?;
189    Ok(self.extrinsic_result.as_ref())
190  }
191
192  pub async fn ok(&mut self) -> Result<()> {
193    match self.extrinsic_result().await? {
194      Some(res) => res.ok(),
195      None => Err(Error::ExtrinsicError(
196        "Failed to get extrinsic results".into(),
197      )),
198    }
199  }
200
201  async fn load_events(&mut self) -> Result<bool> {
202    // Do nothing if we already have the events.
203    if self.events.is_some() {
204      return Ok(true);
205    }
206
207    // Make sure the transaction is in a block.
208    let block_hash = if let Some(block) = self.block {
209      block
210    } else {
211      match self.wait_in_block().await? {
212        None => {
213          // Still not in a block.
214          return Ok(false);
215        }
216        Some(block) => block,
217      }
218    };
219
220    // Find the extrinsic index of our transaction.
221    let client = self.api.client();
222    let idx = client
223      .find_extrinsic_block_index(block_hash, self.tx_hash)
224      .await?;
225
226    if let Some(idx) = idx {
227      // Get block events.
228      let block_events = self.api.block_events(Some(block_hash)).await?;
229      let events = EventRecords::from_vec(block_events, Some(Phase::ApplyExtrinsic(idx as u32)));
230      self.extrinsic_result = Api::events_to_extrinsic_result(events.0.as_slice());
231      self.events = Some(events);
232      Ok(true)
233    } else {
234      Ok(false)
235    }
236  }
237
238  pub fn status(&self) -> Option<&TransactionStatus> {
239    self.status.as_ref()
240  }
241
242  pub fn hash(&self) -> TxHash {
243    self.tx_hash
244  }
245
246  pub fn api(&self) -> &Api {
247    &self.api
248  }
249
250  /// Get block header for this transaction.
251  pub async fn get_block_header(&self) -> Result<Option<Header>> {
252    match self.block {
253      None => Ok(None),
254      block => self.api.client().get_block_header(block).await,
255    }
256  }
257
258  /// Wait for the transaction to be included in a block.
259  pub async fn wait_in_block(&mut self) -> Result<Option<BlockHash>> {
260    // Wait for call to be included in a block.
261    while self.block.is_none() {
262      if !self.next_status().await? {
263        // No more updates available.
264        return Ok(None);
265      }
266    }
267    return Ok(self.block);
268  }
269
270  /// Wait for the transaction to be finalized.
271  pub async fn wait_finalized(&mut self) -> Result<Option<BlockHash>> {
272    // Wait for call to be included in a block.
273    while !self.finalized {
274      if !self.next_status().await? {
275        // No more updates available.
276        return Ok(None);
277      }
278    }
279    return Ok(self.block);
280  }
281}
282
283pub struct Call<Api: ChainApi> {
284  pub api: Api,
285  call: Api::RuntimeCall,
286}
287
288impl<Api: ChainApi> Call<Api> {
289  pub fn new(api: &Api, call: Api::RuntimeCall) -> Self {
290    Self {
291      api: api.clone(),
292      call,
293    }
294  }
295
296  pub fn runtime_call(&self) -> &Api::RuntimeCall {
297    &self.call
298  }
299
300  pub fn into_runtime_call(self) -> Api::RuntimeCall {
301    self.call
302  }
303
304  pub fn encoded(&self) -> Encoded {
305    let call = &self.call;
306    call.into()
307  }
308
309  /// Submit the transaction unsigned.
310  pub async fn submit_unsigned_and_watch(&self) -> Result<TransactionResults<Api>> {
311    Ok(
312      self
313        .submit_raw_xt_and_watch(ExtrinsicV4::unsigned(self.encoded()))
314        .await?,
315    )
316  }
317
318  /// Prepare a transaction for offline signing.
319  pub async fn prepare(
320    &self,
321    account: AccountId,
322    lifetime: Option<u64>,
323  ) -> Result<PreparedTransaction> {
324    let client = self.api.client();
325    // Query account nonce.
326    let nonce = self.api.get_nonce(account).await?;
327
328    let encoded_call = self.encoded();
329    let (additional, era) = client.get_additional_signed(lifetime).await?;
330    let extra = Extra::new(era, nonce);
331    Ok(PreparedTransaction::new(
332      account,
333      additional,
334      extra,
335      encoded_call,
336    ))
337  }
338
339  /// Sign, submit and execute the transaction.
340  pub async fn execute(&self, signer: &mut impl Signer) -> Result<TransactionResults<Api>> {
341    // Sign and submit transaction.
342    let mut res = self.submit_and_watch(signer).await?;
343    // Wait for transaction to be included in a block.
344    res.ok().await?;
345    // Transaction successful.
346    Ok(res)
347  }
348
349  /// Sign and submit the transaction, but don't wait for it to execute.
350  ///
351  /// The return values can be used to wait for transaction to execute and get the results.
352  pub async fn submit_and_watch(
353    &self,
354    signer: &mut impl Signer,
355  ) -> Result<TransactionResults<Api>> {
356    // First try using a locked signer.
357    if let Some(mut signer) = signer.lock().await {
358      return self.submit_and_watch_inner(&mut signer).await;
359    }
360    self.submit_and_watch_inner(signer).await
361  }
362
363  async fn submit_and_watch_inner(
364    &self,
365    signer: &mut impl Signer,
366  ) -> Result<TransactionResults<Api>> {
367    let client = self.api.client();
368    let account = signer.account();
369    // Query account nonce.
370    let nonce = match signer.nonce().await {
371      Some(0) | None => self.api.get_nonce(account.clone()).await?,
372      Some(nonce) => nonce,
373    };
374
375    let encoded_call = self.encoded();
376    let (additional, era) = client.get_additional_signed(None).await?;
377    let extra = Extra::new(era, nonce);
378    let payload = SignedPayload::new(&encoded_call, &extra, additional);
379
380    let payload = payload.encode();
381    let sig = signer.sign(&payload[..]).await?;
382
383    let xt = ExtrinsicV4::signed(account, sig, extra, encoded_call);
384
385    let res = self.submit_raw_xt_and_watch(xt).await?;
386
387    // Update nonce if the call was submitted.
388    signer.set_nonce(nonce + 1).await;
389
390    Ok(res)
391  }
392
393  /// Submit a signed/unsigned transaction, but don't wait for it to execute.
394  ///
395  /// You most likely want to uses either [`Self::execute`] or [`Self::submit_and_watch`]
396  /// not this method.
397  pub async fn submit_raw_xt_and_watch(&self, xt: ExtrinsicV4) -> Result<TransactionResults<Api>> {
398    let (tx_hex, tx_hash) = xt.as_hex_and_hash();
399    let status = self.api.client().submit_and_watch(tx_hex).await?;
400    Ok(TransactionResults::new(&self.api, status, tx_hash))
401  }
402}
403
404impl<Api: ChainApi> Encode for Call<Api> {
405  fn size_hint(&self) -> usize {
406    self.call.size_hint()
407  }
408  fn encode_to<T: ::codec::Output + ?Sized>(&self, dest: &mut T) {
409    self.call.encode_to(dest)
410  }
411}
412
413impl<Api: ChainApi> core::fmt::Debug for Call<Api> {
414  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
415    self.call.fmt(f)
416  }
417}