1use crate::{Client, Error, UserError};
4use avail_rust_core::{
5 EncodeSelector, Extrinsic, ExtrinsicSignature, H256, HasHeader, HashNumber, MultiAddress, RpcError,
6 TransactionEventDecodable, avail,
7 grandpa::GrandpaJustification,
8 rpc::{self, ExtrinsicFilter, SignerPayload},
9 types::HashStringNumber,
10};
11use codec::Decode;
12
13pub struct BlockApi {
15 client: Client,
16 block_id: HashStringNumber,
17 retry_on_error: Option<bool>,
18}
19
20impl BlockApi {
21 pub fn new(client: Client, block_id: impl Into<HashStringNumber>) -> Self {
26 BlockApi { client, block_id: block_id.into(), retry_on_error: None }
27 }
28
29 pub fn tx(&self) -> BlockWithTx {
33 BlockWithTx::new(self.client.clone(), self.block_id.clone())
34 }
35
36 pub fn ext(&self) -> BlockWithExt {
38 BlockWithExt::new(self.client.clone(), self.block_id.clone())
39 }
40
41 pub fn raw_ext(&self) -> BlockWithRawExt {
43 BlockWithRawExt::new(self.client.clone(), self.block_id.clone())
44 }
45
46 pub fn events(&self) -> BlockEvents {
48 BlockEvents::new(self.client.clone(), self.block_id.clone())
49 }
50
51 pub fn set_retry_on_error(&mut self, value: Option<bool>) {
57 self.retry_on_error = value;
58 }
59
60 pub async fn justification(&self) -> Result<Option<GrandpaJustification>, Error> {
67 let block_id: HashNumber = self.block_id.clone().try_into().map_err(UserError::Decoding)?;
68 let at = match block_id {
69 HashNumber::Hash(h) => self
70 .client
71 .chain()
72 .retry_on(self.retry_on_error, None)
73 .block_height(h)
74 .await?
75 .ok_or(Error::Other("Failed to find block from the provided hash".into()))?,
76 HashNumber::Number(n) => n,
77 };
78
79 self.client
80 .chain()
81 .retry_on(self.retry_on_error, None)
82 .grandpa_block_justification(at)
83 .await
84 .map_err(|e| e.into())
85 }
86
87 pub fn should_retry_on_error(&self) -> bool {
89 self.retry_on_error
90 .unwrap_or_else(|| self.client.is_global_retries_enabled())
91 }
92}
93
94pub struct BlockWithRawExt {
96 client: Client,
97 block_id: HashStringNumber,
98 retry_on_error: Option<bool>,
99}
100
101impl BlockWithRawExt {
102 pub fn new(client: Client, block_id: impl Into<HashStringNumber>) -> Self {
106 Self { client, block_id: block_id.into(), retry_on_error: None }
107 }
108
109 pub async fn get(
116 &self,
117 extrinsic_id: impl Into<HashStringNumber>,
118 encode_as: EncodeSelector,
119 ) -> Result<Option<BlockRawExtrinsic>, Error> {
120 async fn inner(
121 s: &BlockWithRawExt,
122 extrinsic_id: HashStringNumber,
123 encode_as: EncodeSelector,
124 ) -> Result<Option<BlockRawExtrinsic>, Error> {
125 let filter = match extrinsic_id {
126 HashStringNumber::Hash(x) => ExtrinsicFilter::from(x),
127 HashStringNumber::String(x) => ExtrinsicFilter::try_from(x).map_err(UserError::Decoding)?,
128 HashStringNumber::Number(x) => ExtrinsicFilter::from(x),
129 };
130 let opts = BlockExtOptionsExpanded {
131 filter: Some(filter),
132 encode_as: Some(encode_as),
133 ..Default::default()
134 };
135
136 s.first(opts).await
137 }
138
139 inner(self, extrinsic_id.into(), encode_as).await
140 }
141
142 pub async fn first(&self, mut opts: BlockExtOptionsExpanded) -> Result<Option<BlockRawExtrinsic>, Error> {
149 if opts.encode_as.is_none() {
150 opts.encode_as = Some(EncodeSelector::Extrinsic)
151 }
152
153 let block_id: HashNumber = self.block_id.clone().try_into().map_err(UserError::Decoding)?;
154 let mut result = self
155 .client
156 .chain()
157 .retry_on(self.retry_on_error, None)
158 .system_fetch_extrinsics(block_id, opts.into())
159 .await?;
160
161 let Some(first) = result.first_mut() else {
162 return Ok(None);
163 };
164
165 let metadata =
166 BlockExtrinsicMetadata::new(first.ext_hash, first.ext_index, first.pallet_id, first.variant_id, block_id);
167 let ext = BlockRawExtrinsic::new(first.data.take(), metadata, first.signer_payload.take());
168
169 Ok(Some(ext))
170 }
171
172 pub async fn last(&self, mut opts: BlockExtOptionsExpanded) -> Result<Option<BlockRawExtrinsic>, Error> {
176 if opts.encode_as.is_none() {
177 opts.encode_as = Some(EncodeSelector::Extrinsic)
178 }
179
180 let block_id: HashNumber = self.block_id.clone().try_into().map_err(UserError::Decoding)?;
181 let mut result = self
182 .client
183 .chain()
184 .retry_on(self.retry_on_error, None)
185 .system_fetch_extrinsics(block_id, opts.into())
186 .await?;
187
188 let Some(last) = result.last_mut() else {
189 return Ok(None);
190 };
191
192 let metadata =
193 BlockExtrinsicMetadata::new(last.ext_hash, last.ext_index, last.pallet_id, last.variant_id, block_id);
194 let ext = BlockRawExtrinsic::new(last.data.take(), metadata, last.signer_payload.take());
195
196 Ok(Some(ext))
197 }
198
199 pub async fn all(&self, mut opts: BlockExtOptionsExpanded) -> Result<Vec<BlockRawExtrinsic>, Error> {
203 if opts.encode_as.is_none() {
204 opts.encode_as = Some(EncodeSelector::Extrinsic)
205 }
206
207 let block_id: HashNumber = self.block_id.clone().try_into().map_err(UserError::Decoding)?;
208 let result = self
209 .client
210 .chain()
211 .retry_on(self.retry_on_error, None)
212 .system_fetch_extrinsics(block_id, opts.into())
213 .await?;
214
215 let result = result
216 .into_iter()
217 .map(|x| {
218 let metadata =
219 BlockExtrinsicMetadata::new(x.ext_hash, x.ext_index, x.pallet_id, x.variant_id, block_id);
220 BlockRawExtrinsic::new(x.data, metadata, x.signer_payload)
221 })
222 .collect();
223
224 Ok(result)
225 }
226
227 pub async fn count(&self, mut opts: BlockExtOptionsExpanded) -> Result<usize, Error> {
231 opts.encode_as = Some(EncodeSelector::None);
232
233 let result = self.all(opts).await?;
234 Ok(result.len())
235 }
236
237 pub async fn exists(&self, mut opts: BlockExtOptionsExpanded) -> Result<bool, Error> {
239 opts.encode_as = Some(EncodeSelector::None);
240
241 let result = self.first(opts).await?;
242 Ok(result.is_some())
243 }
244
245 pub fn set_retry_on_error(&mut self, value: Option<bool>) {
248 self.retry_on_error = value;
249 }
250
251 pub fn should_retry_on_error(&self) -> bool {
253 self.retry_on_error
254 .unwrap_or_else(|| self.client.is_global_retries_enabled())
255 }
256}
257
258pub struct BlockWithExt {
260 rxt: BlockWithRawExt,
261}
262
263impl BlockWithExt {
264 pub fn new(client: Client, block_id: impl Into<HashStringNumber>) -> Self {
268 Self { rxt: BlockWithRawExt::new(client, block_id) }
269 }
270
271 pub async fn get<T: HasHeader + Decode>(
278 &self,
279 extrinsic_id: impl Into<HashStringNumber>,
280 ) -> Result<Option<BlockExtrinsic<T>>, Error> {
281 async fn inner<T: HasHeader + Decode>(
282 s: &BlockWithExt,
283 extrinsic_id: HashStringNumber,
284 ) -> Result<Option<BlockExtrinsic<T>>, Error> {
285 let filter = match extrinsic_id {
286 HashStringNumber::Hash(x) => ExtrinsicFilter::from(x),
287 HashStringNumber::String(x) => ExtrinsicFilter::try_from(x).map_err(UserError::Decoding)?,
288 HashStringNumber::Number(x) => ExtrinsicFilter::from(x),
289 };
290 let filter = Some(filter);
291 s.first::<T>(BlockExtOptionsSimple { filter, ..Default::default() })
292 .await
293 }
294
295 inner::<T>(self, extrinsic_id.into()).await
296 }
297
298 pub async fn first<T: HasHeader + Decode>(
305 &self,
306 opts: BlockExtOptionsSimple,
307 ) -> Result<Option<BlockExtrinsic<T>>, Error> {
308 let mut opts: BlockExtOptionsExpanded = opts.into();
309 if opts.filter.is_none() {
310 opts.filter = Some(T::HEADER_INDEX.into())
311 }
312
313 let first = self.rxt.first(opts).await?;
314 let Some(first) = first else {
315 return Ok(None);
316 };
317
318 let Some(data) = first.data else {
319 return Err(RpcError::ExpectedData("Fetched raw extrinsic had no data.".into()).into());
320 };
321
322 let ext = Extrinsic::<T>::try_from(data.as_str()).map_err(UserError::Decoding)?;
323 let ext = BlockExtrinsic::new(ext.signature, ext.call, first.metadata);
324
325 Ok(Some(ext))
326 }
327
328 pub async fn last<T: HasHeader + Decode>(
332 &self,
333 opts: BlockExtOptionsSimple,
334 ) -> Result<Option<BlockExtrinsic<T>>, Error> {
335 let mut opts: BlockExtOptionsExpanded = opts.into();
336 if opts.filter.is_none() {
337 opts.filter = Some(T::HEADER_INDEX.into())
338 }
339
340 let last = self.rxt.last(opts).await?;
341 let Some(last) = last else {
342 return Ok(None);
343 };
344
345 let Some(data) = last.data else {
346 return Err(RpcError::ExpectedData("Fetched raw extrinsic had no data.".into()).into());
347 };
348
349 let ext = Extrinsic::<T>::try_from(data.as_str()).map_err(UserError::Decoding)?;
350 let ext = BlockExtrinsic::new(ext.signature, ext.call, last.metadata);
351 Ok(Some(ext))
352 }
353
354 pub async fn all<T: HasHeader + Decode>(
358 &self,
359 opts: BlockExtOptionsSimple,
360 ) -> Result<Vec<BlockExtrinsic<T>>, Error> {
361 let mut opts: BlockExtOptionsExpanded = opts.into();
362 if opts.filter.is_none() {
363 opts.filter = Some(T::HEADER_INDEX.into())
364 }
365
366 let all = self.rxt.all(opts).await?;
367 let mut result = Vec::with_capacity(all.len());
368 for raw_ext in all {
369 let Some(data) = raw_ext.data else {
370 return Err(RpcError::ExpectedData("Fetched raw extrinsic had no data.".into()).into());
371 };
372 let ext = Extrinsic::<T>::try_from(data.as_str()).map_err(UserError::Decoding)?;
373 let ext = BlockExtrinsic::new(ext.signature, ext.call, raw_ext.metadata);
374 result.push(ext);
375 }
376
377 Ok(result)
378 }
379
380 pub async fn count<T: HasHeader>(&self, opts: BlockExtOptionsSimple) -> Result<usize, Error> {
384 let mut opts: BlockExtOptionsExpanded = opts.into();
385 opts.encode_as = Some(EncodeSelector::None);
386 if opts.filter.is_none() {
387 opts.filter = Some(T::HEADER_INDEX.into())
388 }
389
390 return self.rxt.count(opts).await;
391 }
392
393 pub async fn exists<T: HasHeader>(&self, opts: BlockExtOptionsSimple) -> Result<bool, Error> {
397 let mut opts: BlockExtOptionsExpanded = opts.into();
398 opts.encode_as = Some(EncodeSelector::None);
399 if opts.filter.is_none() {
400 opts.filter = Some(T::HEADER_INDEX.into())
401 }
402
403 return self.rxt.exists(opts).await;
404 }
405
406 pub fn set_retry_on_error(&mut self, value: Option<bool>) {
409 self.rxt.set_retry_on_error(value);
410 }
411
412 pub fn should_retry_on_error(&self) -> bool {
414 self.rxt.should_retry_on_error()
415 }
416}
417
418pub struct BlockWithTx {
420 ext: BlockWithExt,
421}
422
423impl BlockWithTx {
424 pub fn new(client: Client, block_id: impl Into<HashStringNumber>) -> Self {
428 Self { ext: BlockWithExt::new(client, block_id) }
429 }
430
431 pub async fn get<T: HasHeader + Decode>(
438 &self,
439 extrinsic_id: impl Into<HashStringNumber>,
440 ) -> Result<Option<BlockTransaction<T>>, Error> {
441 let ext = self.ext.get(extrinsic_id).await?;
442 let Some(ext) = ext else {
443 return Ok(None);
444 };
445
446 let Some(signature) = ext.signature else {
447 return Err(
448 UserError::Other("Extrinsic is unsigned; cannot decode it as a signed transaction.".into()).into()
449 );
450 };
451
452 let ext = BlockTransaction::new(signature, ext.call, ext.metadata);
453 Ok(Some(ext))
454 }
455
456 pub async fn first<T: HasHeader + Decode>(
460 &self,
461 opts: BlockExtOptionsSimple,
462 ) -> Result<Option<BlockTransaction<T>>, Error> {
463 let ext = self.ext.first(opts).await?;
464 let Some(ext) = ext else {
465 return Ok(None);
466 };
467
468 let Some(signature) = ext.signature else {
469 return Err(
470 UserError::Other("Extrinsic is unsigned; cannot decode it as a signed transaction.".into()).into()
471 );
472 };
473
474 let ext = BlockTransaction::new(signature, ext.call, ext.metadata);
475 Ok(Some(ext))
476 }
477
478 pub async fn last<T: HasHeader + Decode>(
482 &self,
483 opts: BlockExtOptionsSimple,
484 ) -> Result<Option<BlockTransaction<T>>, Error> {
485 let ext = self.ext.last(opts).await?;
486 let Some(ext) = ext else {
487 return Ok(None);
488 };
489
490 let Some(signature) = ext.signature else {
491 return Err(
492 UserError::Other("Extrinsic is unsigned; cannot decode it as a signed transaction.".into()).into()
493 );
494 };
495
496 let ext = BlockTransaction::new(signature, ext.call, ext.metadata);
497 Ok(Some(ext))
498 }
499
500 pub async fn all<T: HasHeader + Decode>(
504 &self,
505 opts: BlockExtOptionsSimple,
506 ) -> Result<Vec<BlockTransaction<T>>, Error> {
507 let all = self.ext.all::<T>(opts).await?;
508 let mut result = Vec::with_capacity(all.len());
509 for ext in all {
510 let Some(signature) = ext.signature else {
511 return Err(UserError::Other(
512 "Extrinsic is unsigned; cannot decode it as a signed transaction.".into(),
513 )
514 .into());
515 };
516 result.push(BlockTransaction::new(signature, ext.call, ext.metadata));
517 }
518
519 Ok(result)
520 }
521
522 pub async fn count<T: HasHeader>(&self, opts: BlockExtOptionsSimple) -> Result<usize, Error> {
524 self.ext.count::<T>(opts).await
525 }
526
527 pub async fn exists<T: HasHeader>(&self, opts: BlockExtOptionsSimple) -> Result<bool, Error> {
529 self.ext.exists::<T>(opts).await
530 }
531
532 pub fn set_retry_on_error(&mut self, value: Option<bool>) {
535 self.ext.set_retry_on_error(value);
536 }
537
538 pub fn should_retry_on_error(&self) -> bool {
540 self.ext.should_retry_on_error()
541 }
542}
543
544pub struct BlockEvents {
546 client: Client,
547 block_id: HashStringNumber,
548 retry_on_error: Option<bool>,
549}
550
551impl BlockEvents {
552 pub fn new(client: Client, block_id: impl Into<HashStringNumber>) -> Self {
556 BlockEvents { client, block_id: block_id.into(), retry_on_error: None }
557 }
558
559 pub async fn ext(&self, tx_index: u32) -> Result<Option<ExtrinsicEvents>, Error> {
566 let mut events = self
567 .block(BlockEventsOptions {
568 filter: Some(tx_index.into()),
569 enable_encoding: Some(true),
570 enable_decoding: Some(false),
571 })
572 .await?;
573
574 let Some(first) = events.first_mut() else {
575 return Ok(None);
576 };
577
578 let mut result: Vec<ExtrinsicEvent> = Vec::with_capacity(first.events.len());
579 for phase_event in &mut first.events {
580 let Some(data) = phase_event.encoded_data.take() else {
581 return Err(
582 RpcError::ExpectedData("The node did not return encoded data for this event.".into()).into()
583 );
584 };
585
586 let ext_event = ExtrinsicEvent {
587 index: phase_event.index,
588 pallet_id: phase_event.pallet_id,
589 variant_id: phase_event.variant_id,
590 data,
591 };
592 result.push(ext_event);
593 }
594
595 Ok(Some(ExtrinsicEvents::new(result)))
596 }
597
598 pub async fn block(&self, mut opts: BlockEventsOptions) -> Result<Vec<rpc::BlockPhaseEvent>, Error> {
602 if opts.enable_encoding.is_none() {
603 opts.enable_encoding = Some(true);
604 }
605
606 self.client
607 .chain()
608 .retry_on(self.retry_on_error, None)
609 .system_fetch_events(self.block_id.clone(), opts.into())
610 .await
611 }
612
613 pub fn set_retry_on_error(&mut self, value: Option<bool>) {
616 self.retry_on_error = value;
617 }
618
619 pub fn should_retry_on_error(&self) -> bool {
621 self.retry_on_error
622 .unwrap_or_else(|| self.client.is_global_retries_enabled())
623 }
624}
625
626#[derive(Debug, Default, Clone)]
627pub struct BlockEventsOptions {
628 filter: Option<rpc::EventFilter>,
629 enable_encoding: Option<bool>,
630 enable_decoding: Option<bool>,
631}
632
633impl From<BlockEventsOptions> for rpc::EventOpts {
634 fn from(value: BlockEventsOptions) -> Self {
635 rpc::EventOpts {
636 filter: value.filter,
637 enable_encoding: value.enable_encoding,
638 enable_decoding: value.enable_decoding,
639 }
640 }
641}
642
643#[derive(Debug, Default, Clone)]
644pub struct BlockExtOptionsSimple {
645 pub filter: Option<ExtrinsicFilter>,
646 pub ss58_address: Option<String>,
647 pub app_id: Option<u32>,
648 pub nonce: Option<u32>,
649}
650
651#[derive(Debug, Default, Clone)]
652pub struct BlockExtOptionsExpanded {
653 pub filter: Option<ExtrinsicFilter>,
654 pub ss58_address: Option<String>,
655 pub app_id: Option<u32>,
656 pub nonce: Option<u32>,
657 pub encode_as: Option<EncodeSelector>,
658}
659
660impl From<BlockExtOptionsExpanded> for rpc::ExtrinsicOpts {
661 fn from(value: BlockExtOptionsExpanded) -> Self {
662 rpc::ExtrinsicOpts {
663 transaction_filter: value.filter.unwrap_or_default(),
664 ss58_address: value.ss58_address,
665 app_id: value.app_id,
666 nonce: value.nonce,
667 encode_as: value.encode_as.unwrap_or_default(),
668 }
669 }
670}
671
672impl From<BlockExtOptionsSimple> for BlockExtOptionsExpanded {
673 fn from(value: BlockExtOptionsSimple) -> Self {
674 Self {
675 filter: value.filter,
676 ss58_address: value.ss58_address,
677 app_id: value.app_id,
678 nonce: value.nonce,
679 encode_as: Some(EncodeSelector::Extrinsic),
680 }
681 }
682}
683
684#[derive(Debug, Clone)]
685pub struct BlockExtrinsicMetadata {
686 pub ext_hash: H256,
687 pub ext_index: u32,
688 pub pallet_id: u8,
689 pub variant_id: u8,
690 pub block_id: HashNumber,
691}
692
693impl BlockExtrinsicMetadata {
694 pub fn new(ext_hash: H256, ext_index: u32, pallet_id: u8, variant_id: u8, block_id: HashNumber) -> Self {
696 Self { ext_hash, ext_index, pallet_id, variant_id, block_id }
697 }
698}
699
700#[derive(Debug, Clone)]
701pub struct BlockRawExtrinsic {
702 pub data: Option<String>,
703 pub metadata: BlockExtrinsicMetadata,
704 pub signer_payload: Option<SignerPayload>,
705}
706
707impl BlockRawExtrinsic {
708 pub fn new(data: Option<String>, metadata: BlockExtrinsicMetadata, signer_payload: Option<SignerPayload>) -> Self {
710 Self { data, metadata, signer_payload }
711 }
712
713 pub async fn events(&self, client: Client) -> Result<ExtrinsicEvents, Error> {
719 let events = BlockEvents::new(client, self.metadata.block_id)
720 .ext(self.ext_index())
721 .await?;
722 let Some(events) = events else {
723 return Err(RpcError::ExpectedData("No events found for the requested extrinsic.".into()).into());
724 };
725
726 Ok(events)
727 }
728
729 pub fn ext_index(&self) -> u32 {
731 self.metadata.ext_index
732 }
733
734 pub fn ext_hash(&self) -> H256 {
736 self.metadata.ext_hash
737 }
738
739 pub fn app_id(&self) -> Option<u32> {
741 Some(self.signer_payload.as_ref()?.app_id)
742 }
743
744 pub fn nonce(&self) -> Option<u32> {
746 Some(self.signer_payload.as_ref()?.nonce)
747 }
748
749 pub fn ss58_address(&self) -> Option<String> {
751 self.signer_payload.as_ref()?.ss58_address.clone()
752 }
753}
754
755#[derive(Debug, Clone)]
757pub struct BlockExtrinsic<T: HasHeader + Decode> {
758 pub signature: Option<ExtrinsicSignature>,
759 pub call: T,
760 pub metadata: BlockExtrinsicMetadata,
761}
762
763impl<T: HasHeader + Decode> BlockExtrinsic<T> {
764 pub fn new(signature: Option<ExtrinsicSignature>, call: T, metadata: BlockExtrinsicMetadata) -> Self {
766 Self { signature, call, metadata }
767 }
768
769 pub async fn events(&self, client: Client) -> Result<ExtrinsicEvents, Error> {
771 let events = BlockEvents::new(client, self.metadata.block_id)
772 .ext(self.ext_index())
773 .await?;
774 let Some(events) = events else {
775 return Err(RpcError::ExpectedData("No events found for extrinsic".into()).into());
776 };
777
778 Ok(events)
779 }
780
781 pub fn ext_index(&self) -> u32 {
783 self.metadata.ext_index
784 }
785
786 pub fn ext_hash(&self) -> H256 {
788 self.metadata.ext_hash
789 }
790
791 pub fn app_id(&self) -> Option<u32> {
793 Some(self.signature.as_ref()?.tx_extra.app_id)
794 }
795
796 pub fn nonce(&self) -> Option<u32> {
798 Some(self.signature.as_ref()?.tx_extra.nonce)
799 }
800
801 pub fn tip(&self) -> Option<u128> {
803 Some(self.signature.as_ref()?.tx_extra.tip)
804 }
805
806 pub fn ss58_address(&self) -> Option<String> {
808 match &self.signature.as_ref()?.address {
809 MultiAddress::Id(x) => Some(std::format!("{}", x)),
810 _ => None,
811 }
812 }
813}
814
815impl<T: HasHeader + Decode> TryFrom<BlockRawExtrinsic> for BlockExtrinsic<T> {
816 type Error = String;
817
818 fn try_from(value: BlockRawExtrinsic) -> Result<Self, Self::Error> {
819 let Some(data) = &value.data else {
820 return Err("Encoded extrinsic payload is missing from the RPC response.")?;
821 };
822
823 let extrinsic = Extrinsic::<T>::try_from(data.as_str())?;
824 Ok(Self::new(extrinsic.signature, extrinsic.call, value.metadata))
825 }
826}
827
828#[derive(Debug, Clone)]
830pub struct BlockTransaction<T: HasHeader + Decode> {
831 pub signature: ExtrinsicSignature,
832 pub call: T,
833 pub metadata: BlockExtrinsicMetadata,
834}
835
836impl<T: HasHeader + Decode> BlockTransaction<T> {
837 pub fn new(signature: ExtrinsicSignature, call: T, metadata: BlockExtrinsicMetadata) -> Self {
839 Self { signature, call, metadata }
840 }
841
842 pub async fn events(&self, client: Client) -> Result<ExtrinsicEvents, Error> {
844 let events = BlockEvents::new(client, self.metadata.block_id)
845 .ext(self.ext_index())
846 .await?;
847 let Some(events) = events else {
848 return Err(RpcError::ExpectedData("No events found for the requested extrinsic.".into()).into());
849 };
850
851 Ok(events)
852 }
853
854 pub fn ext_index(&self) -> u32 {
856 self.metadata.ext_index
857 }
858
859 pub fn ext_hash(&self) -> H256 {
861 self.metadata.ext_hash
862 }
863
864 pub fn app_id(&self) -> u32 {
866 self.signature.tx_extra.app_id
867 }
868
869 pub fn nonce(&self) -> u32 {
871 self.signature.tx_extra.nonce
872 }
873
874 pub fn tip(&self) -> u128 {
876 self.signature.tx_extra.tip
877 }
878
879 pub fn ss58_address(&self) -> Option<String> {
881 match &self.signature.address {
882 MultiAddress::Id(x) => Some(std::format!("{}", x)),
883 _ => None,
884 }
885 }
886}
887
888impl<T: HasHeader + Decode> TryFrom<BlockExtrinsic<T>> for BlockTransaction<T> {
889 type Error = String;
890
891 fn try_from(value: BlockExtrinsic<T>) -> Result<Self, Self::Error> {
892 let Some(signature) = value.signature else {
893 return Err("Extrinsic is unsigned; expected a signature.")?;
894 };
895
896 Ok(Self::new(signature, value.call, value.metadata))
897 }
898}
899
900impl<T: HasHeader + Decode> TryFrom<BlockRawExtrinsic> for BlockTransaction<T> {
901 type Error = String;
902
903 fn try_from(value: BlockRawExtrinsic) -> Result<Self, Self::Error> {
904 let ext = BlockExtrinsic::try_from(value)?;
905 Self::try_from(ext)
906 }
907}
908
909#[derive(Debug, Clone)]
910pub struct ExtrinsicEvent {
911 pub index: u32,
912 pub pallet_id: u8,
913 pub variant_id: u8,
914 pub data: String,
915}
916
917#[derive(Debug, Clone)]
918pub struct ExtrinsicEvents {
919 pub events: Vec<ExtrinsicEvent>,
920}
921
922impl ExtrinsicEvents {
923 pub fn new(events: Vec<ExtrinsicEvent>) -> Self {
925 Self { events }
926 }
927
928 pub fn first<T: HasHeader + codec::Decode>(&self) -> Option<T> {
930 let event = self
931 .events
932 .iter()
933 .find(|x| x.pallet_id == T::HEADER_INDEX.0 && x.variant_id == T::HEADER_INDEX.1);
934 let event = event?;
935
936 T::from_event(&event.data).ok()
937 }
938
939 pub fn all<T: HasHeader + codec::Decode>(&self) -> Result<Vec<T>, String> {
941 let mut result = Vec::new();
942 for event in &self.events {
943 if event.pallet_id != T::HEADER_INDEX.0 || event.variant_id != T::HEADER_INDEX.1 {
944 continue;
945 }
946
947 let decoded = T::from_event(event.data.as_str())?;
948 result.push(decoded);
949 }
950
951 Ok(result)
952 }
953
954 pub fn is_extrinsic_success_present(&self) -> bool {
956 self.is_present::<avail::system::events::ExtrinsicSuccess>()
957 }
958
959 pub fn is_extrinsic_failed_present(&self) -> bool {
961 self.is_present::<avail::system::events::ExtrinsicFailed>()
962 }
963
964 pub fn proxy_executed_successfully(&self) -> Option<bool> {
966 let executed = self.first::<avail::proxy::events::ProxyExecuted>()?;
967 Some(executed.result.is_ok())
968 }
969
970 pub fn multisig_executed_successfully(&self) -> Option<bool> {
972 let executed = self.first::<avail::multisig::events::MultisigExecuted>()?;
973 Some(executed.result.is_ok())
974 }
975
976 pub fn is_present<T: HasHeader>(&self) -> bool {
978 self.count::<T>() > 0
979 }
980
981 pub fn is_present_parts(&self, pallet_id: u8, variant_id: u8) -> bool {
983 self.count_parts(pallet_id, variant_id) > 0
984 }
985
986 pub fn count<T: HasHeader>(&self) -> u32 {
988 self.count_parts(T::HEADER_INDEX.0, T::HEADER_INDEX.1)
989 }
990
991 pub fn count_parts(&self, pallet_id: u8, variant_id: u8) -> u32 {
993 let mut count = 0;
994 self.events.iter().for_each(|x| {
995 if x.pallet_id == pallet_id && x.variant_id == variant_id {
996 count += 1
997 }
998 });
999
1000 count
1001 }
1002}