1use crate::{
2 Client, Error, UserError,
3 block::{
4 BlockExtrinsicMetadata,
5 events::{BlockEvents, BlockEventsQuery},
6 extrinsic::BlockExtrinsic,
7 extrinsic_options::Options,
8 shared::BlockContext,
9 signed::BlockSignedExtrinsic,
10 },
11};
12use avail_rust_core::{
13 EncodeSelector, EncodedExtrinsic, ExtrinsicSignature, H256, HasHeader, HashNumber, RpcError,
14 rpc::{self, ExtrinsicFilter, ExtrinsicInfo},
15 types::HashStringNumber,
16};
17use codec::Decode;
18
19pub struct BlockEncodedExtrinsicsQuery {
21 ctx: BlockContext,
22}
23
24impl BlockEncodedExtrinsicsQuery {
25 pub fn new(client: Client, block_id: HashStringNumber) -> Self {
34 Self { ctx: BlockContext::new(client, block_id) }
35 }
36
37 pub async fn get(&self, extrinsic_id: impl Into<HashStringNumber>) -> Result<Option<BlockEncodedExtrinsic>, Error> {
50 async fn inner(
51 s: &BlockEncodedExtrinsicsQuery,
52 extrinsic_id: HashStringNumber,
53 ) -> Result<Option<BlockEncodedExtrinsic>, Error> {
54 let filter = match extrinsic_id {
55 HashStringNumber::Hash(x) => ExtrinsicFilter::from(x),
56 HashStringNumber::String(x) => ExtrinsicFilter::try_from(x).map_err(UserError::Decoding)?,
57 HashStringNumber::Number(x) => ExtrinsicFilter::from(x),
58 };
59 let opts = Options::new().filter(filter);
60
61 s.first(opts).await
62 }
63
64 inner(self, extrinsic_id.into()).await
65 }
66
67 pub async fn first(&self, opts: Options) -> Result<Option<BlockEncodedExtrinsic>, Error> {
80 let block_id = self.ctx.hash_number()?;
81 let chain = self.ctx.chain();
82 let opts = opts.to_rpc_opts(EncodeSelector::Extrinsic);
83 let mut result = chain.system_fetch_extrinsics(block_id, opts).await?;
84
85 let Some(info) = result.first_mut() else {
86 return Ok(None);
87 };
88
89 let ext = BlockEncodedExtrinsic::from_extrinsic_info(info, block_id)?;
90 Ok(Some(ext))
91 }
92
93 pub async fn last(&self, opts: Options) -> Result<Option<BlockEncodedExtrinsic>, Error> {
106 let block_id = self.ctx.hash_number()?;
107 let chain = self.ctx.chain();
108 let opts = opts.to_rpc_opts(EncodeSelector::Extrinsic);
109 let mut result = chain.system_fetch_extrinsics(block_id, opts).await?;
110
111 let Some(info) = result.last_mut() else {
112 return Ok(None);
113 };
114
115 let ext = BlockEncodedExtrinsic::from_extrinsic_info(info, block_id)?;
116 Ok(Some(ext))
117 }
118
119 pub async fn all(&self, opts: Options) -> Result<Vec<BlockEncodedExtrinsic>, Error> {
131 let block_id = self.ctx.hash_number()?;
132 let chain = self.ctx.chain();
133 let opts = opts.to_rpc_opts(EncodeSelector::Extrinsic);
134 let extrinsics = chain.system_fetch_extrinsics(block_id, opts).await?;
135
136 let mut result = Vec::with_capacity(extrinsics.len());
137 for info in extrinsics {
138 let ext = BlockEncodedExtrinsic::from_extrinsic_info(&info, block_id)?;
139 result.push(ext);
140 }
141
142 Ok(result)
143 }
144
145 pub async fn count(&self, opts: Options) -> Result<usize, Error> {
157 let opts: rpc::ExtrinsicOpts = opts.to_rpc_opts(EncodeSelector::None);
158
159 let block_id = self.ctx.block_id.clone();
160 let chain = self.ctx.chain();
161 let result = chain.system_fetch_extrinsics(block_id, opts).await?;
162
163 Ok(result.len())
164 }
165
166 pub async fn exists(&self, opts: Options) -> Result<bool, Error> {
179 self.count(opts).await.map(|x| x > 0)
180 }
181
182 pub fn set_retry_on_error(&mut self, value: Option<bool>) {
193 self.ctx.set_retry_on_error(value);
194 }
195
196 pub fn should_retry_on_error(&self) -> bool {
202 self.ctx.should_retry_on_error()
203 }
204}
205
206#[derive(Debug, Clone)]
208pub struct BlockEncodedExtrinsic {
209 pub signature: Option<ExtrinsicSignature>,
211 pub call: Vec<u8>,
213 pub metadata: BlockExtrinsicMetadata,
215}
216
217impl BlockEncodedExtrinsic {
218 pub fn new(signature: Option<ExtrinsicSignature>, call: Vec<u8>, metadata: BlockExtrinsicMetadata) -> Self {
228 Self { signature, call, metadata }
229 }
230
231 pub async fn events(&self, client: Client) -> Result<BlockEvents, Error> {
243 let events = BlockEventsQuery::new(client, self.metadata.block_id)
244 .extrinsic(self.ext_index())
245 .await?;
246
247 if events.is_empty() {
248 return Err(RpcError::ExpectedData("No events found for the requested extrinsic.".into()).into());
249 };
250
251 Ok(events)
252 }
253
254 pub fn ext_index(&self) -> u32 {
259 self.metadata.ext_index
260 }
261
262 pub fn ext_hash(&self) -> H256 {
267 self.metadata.ext_hash
268 }
269
270 pub fn app_id(&self) -> Option<u32> {
276 Some(self.signature.as_ref()?.extra.app_id)
277 }
278
279 pub fn nonce(&self) -> Option<u32> {
285 Some(self.signature.as_ref()?.extra.nonce)
286 }
287
288 pub fn tip(&self) -> Option<u128> {
294 Some(self.signature.as_ref()?.extra.tip)
295 }
296
297 pub fn ss58_address(&self) -> Option<String> {
303 match &self.signature.as_ref()?.address {
304 avail_rust_core::MultiAddress::Id(account_id32) => Some(std::format!("{}", account_id32)),
305 _ => None,
306 }
307 }
308
309 pub fn as_extrinsic<T: HasHeader + Decode>(self) -> Result<BlockExtrinsic<T>, Error> {
315 BlockExtrinsic::<T>::try_from(self).map_err(Error::Other)
316 }
317
318 pub fn as_signed<T: HasHeader + Decode>(self) -> Result<BlockSignedExtrinsic<T>, Error> {
324 BlockSignedExtrinsic::<T>::try_from(self).map_err(Error::Other)
325 }
326
327 pub fn is<T: HasHeader>(&self) -> bool {
333 self.metadata.pallet_id == T::HEADER_INDEX.0 && self.metadata.variant_id == T::HEADER_INDEX.1
334 }
335
336 pub fn header(&self) -> (u8, u8) {
341 (self.metadata.pallet_id, self.metadata.variant_id)
342 }
343
344 pub fn from_extrinsic_info(info: &ExtrinsicInfo, block_id: HashNumber) -> Result<Self, Error> {
353 let metadata = BlockExtrinsicMetadata::from_extrinsic_info(info, block_id);
354 let Some(data) = info.data.as_ref() else {
355 return Err(Error::RpcError(RpcError::ExpectedData("Expected data for encoded extrinsic.".into())));
356 };
357
358 let extrinsic = EncodedExtrinsic::try_from(data).map_err(Error::Other)?;
359 Ok(BlockEncodedExtrinsic::new(extrinsic.signature, extrinsic.call, metadata))
360 }
361}
362
363#[cfg(test)]
364pub mod tests {
365 use super::*;
366 use crate::TURING_ENDPOINT;
367 use avail_rust_core::{ExtrinsicDecodable, avail};
368
369 fn match_timestamp(ext: &BlockEncodedExtrinsic) {
370 assert_eq!(
371 std::format!("{:?}", ext.ext_hash()),
372 "0xdbfa60611f72a714100338db1c7b11c66636a76f116b214d879de069afe67a74"
373 );
374 assert_eq!(ext.ext_index(), 0);
375 assert_eq!(ext.nonce(), None);
376 assert_eq!(ext.header(), (3, 0));
377 assert!(ext.signature.is_none());
378 assert!(ext.app_id().is_none());
379 let set = avail::timestamp::tx::Set::from_call(&ext.call).unwrap();
380 assert_eq!(set.now, 1761567760000);
381 }
382
383 fn match_failed_send_message(ext: &BlockEncodedExtrinsic) {
384 assert_eq!(
385 std::format!("{:?}", ext.ext_hash()),
386 "0x92cdb77314063a01930b093516d19a453399710cc8ae635ff5ab6cf76b26f218"
387 );
388 assert_eq!(ext.header(), (39, 11));
389 assert_eq!(ext.ext_index(), 3);
390 assert_eq!(ext.nonce(), None);
391 assert!(ext.signature.is_none());
392 assert!(ext.app_id().is_none());
393 let f = avail::vector::tx::FailedSendMessageTxs::from_call(&ext.call).unwrap();
394 assert_eq!(f.failed_txs.len(), 0);
395 }
396
397 fn match_submit_data_1(ext: &BlockEncodedExtrinsic) {
398 assert_eq!(
399 std::format!("{:?}", ext.ext_hash()),
400 "0x8b84294cba5f2b88e2887ac999ebac3806af7be9cca2a521fc889421f240f3ef"
401 );
402 assert_eq!(ext.ext_index(), 1);
403 assert_eq!(ext.header(), (29, 1));
404 assert_eq!(ext.nonce(), Some(30));
405 assert!(ext.signature.is_some());
406 assert_eq!(ext.app_id(), Some(1));
407 assert_eq!(ext.ss58_address(), Some("5Ev2jfLbYH6ENZ8ThTmqBX58zoinvHyqvRMvtoiUnLLcv1NJ".to_string()));
408 let sd = avail::data_availability::tx::SubmitData::from_call(&ext.call).unwrap();
409 assert_eq!(String::from_utf8(sd.data).unwrap(), "AABBCC");
410 }
411
412 fn match_submit_data_2(ext: &BlockEncodedExtrinsic) {
413 assert_eq!(
414 std::format!("{:?}", ext.ext_hash()),
415 "0x19fab0492322016c644af12f1547c587ef51edd10311db85cb3aa2680f6ae4ba"
416 );
417 assert_eq!(ext.ext_index(), 2);
418 assert_eq!(ext.header(), (29, 1));
419 assert_eq!(ext.nonce(), Some(4));
420 assert!(ext.signature.is_some());
421 assert_eq!(ext.app_id(), Some(2));
422 assert_eq!(ext.ss58_address(), Some("5DPDXCcqk1YNVZ3M9s9iwJnr9XAVfTxf8hNa4LS51fjHKAzk".to_string()));
423 let sd = avail::data_availability::tx::SubmitData::from_call(&ext.call).unwrap();
424 assert_eq!(String::from_utf8(sd.data).unwrap(), "CCBBAA");
425 }
426
427 #[tokio::test]
428 async fn query_get_test() {
429 let client = Client::new(TURING_ENDPOINT).await.unwrap();
430 let query = client.block(2491314).encoded();
431
432 for i in 0..4usize {
433 let ext = query.get(i as u32).await.unwrap().unwrap();
434
435 match i {
437 0 => match_timestamp(&ext),
438 1 => match_submit_data_1(&ext),
439 2 => match_submit_data_2(&ext),
440 3 => match_failed_send_message(&ext),
441 _ => panic!(),
442 };
443 }
444
445 assert!(query.get(4).await.unwrap().is_none());
447 }
448
449 #[tokio::test]
450 async fn query_first_test() {
451 let client = Client::new(TURING_ENDPOINT).await.unwrap();
452 let query = client.block(2491314).encoded();
453
454 let opts = Options::new().app_id(1);
456 let ext = query.first(opts).await.unwrap().unwrap();
457 match_submit_data_1(&ext);
458
459 let opts = Options::new().app_id(2);
461 let ext = query.first(opts).await.unwrap().unwrap();
462 match_submit_data_2(&ext);
463
464 let opts = Options::new().nonce(30);
466 let ext = query.first(opts).await.unwrap().unwrap();
467 match_submit_data_1(&ext);
468
469 let opts = Options::new().nonce(4);
471 let ext = query.first(opts).await.unwrap().unwrap();
472 match_submit_data_2(&ext);
473
474 let opts = Options::new().filter(avail::data_availability::tx::SubmitData::HEADER_INDEX);
476 let ext = query.first(opts).await.unwrap().unwrap();
477 match_submit_data_1(&ext);
478
479 let opts = Options::new().filter(avail::data_availability::tx::SubmitData::HEADER_INDEX.0);
481 let ext = query.first(opts).await.unwrap().unwrap();
482 match_submit_data_1(&ext);
483
484 let ext = query.first(Default::default()).await.unwrap().unwrap();
486 match_timestamp(&ext);
487
488 let opts = Options::new().filter(100u32);
490 assert!(query.first(opts).await.unwrap().is_none());
491 }
492
493 #[tokio::test]
494 async fn query_last_test() {
495 let client = Client::new(TURING_ENDPOINT).await.unwrap();
496 let query = client.block(2491314).encoded();
497
498 let opts = Options::new().app_id(1);
500 let ext = query.last(opts).await.unwrap().unwrap();
501 match_submit_data_1(&ext);
502
503 let opts = Options::new().app_id(2);
505 let ext = query.last(opts).await.unwrap().unwrap();
506 match_submit_data_2(&ext);
507
508 let opts = Options::new().nonce(30);
510 let ext = query.last(opts).await.unwrap().unwrap();
511 match_submit_data_1(&ext);
512
513 let opts = Options::new().nonce(4);
515 let ext = query.last(opts).await.unwrap().unwrap();
516 match_submit_data_2(&ext);
517
518 let opts = Options::new().filter(avail::data_availability::tx::SubmitData::HEADER_INDEX);
520 let ext = query.last(opts).await.unwrap().unwrap();
521 match_submit_data_2(&ext);
522
523 let opts = Options::new().filter(avail::data_availability::tx::SubmitData::HEADER_INDEX.0);
525 let ext = query.last(opts).await.unwrap().unwrap();
526 match_submit_data_2(&ext);
527
528 let ext = query.last(Default::default()).await.unwrap().unwrap();
530 match_failed_send_message(&ext);
531
532 let opts = Options::new().filter(100u32);
534 assert!(query.last(opts).await.unwrap().is_none());
535 }
536
537 #[tokio::test]
538 async fn query_all_test() {
539 let client = Client::new(TURING_ENDPOINT).await.unwrap();
540 let query = client.block(2491314).encoded();
541
542 let opts = Options::new().app_id(1);
544 let ext = query.all(opts).await.unwrap();
545 match_submit_data_1(&ext[0]);
546
547 let opts = Options::new().app_id(2);
549 let ext = query.all(opts).await.unwrap();
550 match_submit_data_2(&ext[0]);
551 assert_eq!(ext.len(), 1);
552
553 let opts = Options::new().nonce(30);
555 let ext = query.all(opts).await.unwrap();
556 match_submit_data_1(&ext[0]);
557 assert_eq!(ext.len(), 1);
558
559 let opts = Options::new().nonce(4);
561 let ext = query.all(opts).await.unwrap();
562 match_submit_data_2(&ext[0]);
563 assert_eq!(ext.len(), 1);
564
565 let opts = Options::new().filter(avail::data_availability::tx::SubmitData::HEADER_INDEX);
567 let ext = query.all(opts).await.unwrap();
568 match_submit_data_1(&ext[0]);
569 match_submit_data_2(&ext[1]);
570 assert_eq!(ext.len(), 2);
571
572 let opts = Options::new().filter(avail::data_availability::tx::SubmitData::HEADER_INDEX.0);
574 let ext = query.all(opts).await.unwrap();
575 match_submit_data_1(&ext[0]);
576 match_submit_data_2(&ext[1]);
577 assert_eq!(ext.len(), 2);
578
579 let ext = query.all(Default::default()).await.unwrap();
581 match_timestamp(&ext[0]);
582 match_submit_data_1(&ext[1]);
583 match_submit_data_2(&ext[2]);
584 match_failed_send_message(&ext[3]);
585 assert_eq!(ext.len(), 4);
586
587 let opts = Options::new().filter(100u32);
589 let ext = query.all(opts).await.unwrap();
590 assert_eq!(ext.len(), 0)
591 }
592
593 #[tokio::test]
594 async fn query_count_test() {
595 let client = Client::new(TURING_ENDPOINT).await.unwrap();
596 let query = client.block(2491314).encoded();
597
598 let opts = Options::new().app_id(1);
600 assert_eq!(query.count(opts).await.unwrap(), 1);
601
602 let opts = Options::new().app_id(2);
604 assert_eq!(query.count(opts).await.unwrap(), 1);
605
606 let opts = Options::new().nonce(30);
608 assert_eq!(query.count(opts).await.unwrap(), 1);
609
610 let opts = Options::new().nonce(4);
612 assert_eq!(query.count(opts).await.unwrap(), 1);
613
614 let opts = Options::new().filter(avail::data_availability::tx::SubmitData::HEADER_INDEX);
616 assert_eq!(query.count(opts).await.unwrap(), 2);
617
618 let opts = Options::new().filter(avail::data_availability::tx::SubmitData::HEADER_INDEX.0);
620 assert_eq!(query.count(opts).await.unwrap(), 2);
621
622 assert_eq!(query.count(Default::default()).await.unwrap(), 4);
624
625 let opts = Options::new().filter(100u32);
627 assert_eq!(query.count(opts).await.unwrap(), 0);
628 }
629
630 #[tokio::test]
631 async fn query_exists_test() {
632 let client = Client::new(TURING_ENDPOINT).await.unwrap();
633 let query = client.block(2491314).encoded();
634
635 let opts = Options::new().app_id(1);
637 assert_eq!(query.exists(opts).await.unwrap(), true);
638
639 let opts = Options::new().app_id(2);
641 assert_eq!(query.exists(opts).await.unwrap(), true);
642
643 let opts = Options::new().nonce(30);
645 assert_eq!(query.exists(opts).await.unwrap(), true);
646
647 let opts = Options::new().nonce(4);
649 assert_eq!(query.exists(opts).await.unwrap(), true);
650
651 let opts = Options::new().filter(avail::data_availability::tx::SubmitData::HEADER_INDEX);
653 assert_eq!(query.exists(opts).await.unwrap(), true);
654
655 let opts = Options::new().filter(avail::data_availability::tx::SubmitData::HEADER_INDEX.0);
657 assert_eq!(query.exists(opts).await.unwrap(), true);
658
659 assert_eq!(query.exists(Default::default()).await.unwrap(), true);
661
662 let opts = Options::new().filter(100u32);
664 assert_eq!(query.exists(opts).await.unwrap(), false);
665 }
666}