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 events
98 .iter()
99 .rev()
100 .find_map(Self::event_to_extrinsic_result)
101 }
102
103 fn client(&self) -> &Client;
104
105 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 self.sub = None;
144 Ok(false)
145 }
146 Some(Ok(status)) => {
147 use TransactionStatus::*;
148 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 self.block = None;
161 }
162 _ => {
163 self.block = None;
165 self.sub = None;
166 }
167 }
168 self.status = Some(status);
169 Ok(true)
170 }
171 Some(Err(err)) => {
172 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 if self.events.is_some() {
204 return Ok(true);
205 }
206
207 let block_hash = if let Some(block) = self.block {
209 block
210 } else {
211 match self.wait_in_block().await? {
212 None => {
213 return Ok(false);
215 }
216 Some(block) => block,
217 }
218 };
219
220 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 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 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 pub async fn wait_in_block(&mut self) -> Result<Option<BlockHash>> {
260 while self.block.is_none() {
262 if !self.next_status().await? {
263 return Ok(None);
265 }
266 }
267 return Ok(self.block);
268 }
269
270 pub async fn wait_finalized(&mut self) -> Result<Option<BlockHash>> {
272 while !self.finalized {
274 if !self.next_status().await? {
275 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 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 pub async fn prepare(
320 &self,
321 account: AccountId,
322 lifetime: Option<u64>,
323 ) -> Result<PreparedTransaction> {
324 let client = self.api.client();
325 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 pub async fn execute(&self, signer: &mut impl Signer) -> Result<TransactionResults<Api>> {
341 let mut res = self.submit_and_watch(signer).await?;
343 res.ok().await?;
345 Ok(res)
347 }
348
349 pub async fn submit_and_watch(
353 &self,
354 signer: &mut impl Signer,
355 ) -> Result<TransactionResults<Api>> {
356 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 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 signer.set_nonce(nonce + 1).await;
389
390 Ok(res)
391 }
392
393 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}