1use crate::{
2 Client, Error, ExtrinsicDecodable, UserError,
3 block::{
4 BlockExtrinsicMetadata,
5 encoded::{BlockEncodedExtrinsic, BlockEncodedExtrinsicsQuery},
6 events::{BlockEvents, BlockEventsQuery},
7 extrinsic_options::Options,
8 signed::BlockSignedExtrinsic,
9 },
10};
11use avail_rust_core::{
12 ExtrinsicSignature, H256, HasHeader, MultiAddress, RpcError, rpc::ExtrinsicFilter, types::HashStringNumber,
13};
14use codec::Decode;
15
16pub struct BlockExtrinsicsQuery {
18 xt: BlockEncodedExtrinsicsQuery,
19}
20
21impl BlockExtrinsicsQuery {
22 pub fn new(client: Client, block_id: HashStringNumber) -> Self {
31 Self { xt: BlockEncodedExtrinsicsQuery::new(client, block_id) }
32 }
33
34 pub async fn get<T: HasHeader + Decode>(
47 &self,
48 extrinsic_id: impl Into<HashStringNumber>,
49 ) -> Result<Option<BlockExtrinsic<T>>, Error> {
50 async fn inner<T: HasHeader + Decode>(
51 s: &BlockExtrinsicsQuery,
52 extrinsic_id: HashStringNumber,
53 ) -> Result<Option<BlockExtrinsic<T>>, 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 s.first::<T>(Options::new().filter(filter)).await
60 }
61
62 inner::<T>(self, extrinsic_id.into()).await
63 }
64
65 pub async fn first<T: HasHeader + Decode>(&self, mut opts: Options) -> Result<Option<BlockExtrinsic<T>>, Error> {
78 opts.filter = opts.filter.or(Some(T::HEADER_INDEX.into()));
79
80 let encoded = self.xt.first(opts).await?;
81 let Some(encoded) = encoded else {
82 return Ok(None);
83 };
84
85 Ok(Some(encoded.as_extrinsic::<T>()?))
86 }
87
88 pub async fn last<T: HasHeader + Decode>(&self, mut opts: Options) -> Result<Option<BlockExtrinsic<T>>, Error> {
101 opts.filter = opts.filter.or(Some(T::HEADER_INDEX.into()));
102
103 let encoded = self.xt.last(opts).await?;
104 let Some(encoded) = encoded else {
105 return Ok(None);
106 };
107
108 Ok(Some(encoded.as_extrinsic::<T>()?))
109 }
110
111 pub async fn all<T: HasHeader + Decode>(&self, mut opts: Options) -> Result<Vec<BlockExtrinsic<T>>, Error> {
123 opts.filter = opts.filter.or(Some(T::HEADER_INDEX.into()));
124
125 let all = self.xt.all(opts).await?;
126 let mut result = Vec::with_capacity(all.len());
127 for encoded in all {
128 result.push(encoded.as_extrinsic::<T>()?);
129 }
130
131 Ok(result)
132 }
133
134 pub async fn count<T: HasHeader>(&self, mut opts: Options) -> Result<usize, Error> {
146 opts.filter = Some(T::HEADER_INDEX.into());
147 return self.xt.count(opts).await;
148 }
149
150 pub async fn exists<T: HasHeader>(&self, mut opts: Options) -> Result<bool, Error> {
163 opts.filter = Some(T::HEADER_INDEX.into());
164 return self.xt.exists(opts).await;
165 }
166
167 pub fn set_retry_on_error(&mut self, value: Option<bool>) {
178 self.xt.set_retry_on_error(value);
179 }
180
181 pub fn should_retry_on_error(&self) -> bool {
187 self.xt.should_retry_on_error()
188 }
189}
190
191#[derive(Debug, Clone)]
193pub struct BlockExtrinsic<T: HasHeader + Decode> {
194 pub signature: Option<ExtrinsicSignature>,
196 pub call: T,
198 pub metadata: BlockExtrinsicMetadata,
200}
201
202impl<T: HasHeader + Decode> BlockExtrinsic<T> {
203 pub fn new(signature: Option<ExtrinsicSignature>, call: T, metadata: BlockExtrinsicMetadata) -> Self {
213 Self { signature, call, metadata }
214 }
215
216 pub async fn events(&self, client: Client) -> Result<BlockEvents, Error> {
228 let events = BlockEventsQuery::new(client, self.metadata.block_id)
229 .extrinsic(self.ext_index())
230 .await?;
231
232 if events.is_empty() {
233 return Err(RpcError::ExpectedData("No events found for extrinsic".into()).into());
234 };
235
236 Ok(events)
237 }
238
239 pub fn ext_index(&self) -> u32 {
244 self.metadata.ext_index
245 }
246
247 pub fn ext_hash(&self) -> H256 {
252 self.metadata.ext_hash
253 }
254
255 pub fn app_id(&self) -> Option<u32> {
261 Some(self.signature.as_ref()?.extra.app_id)
262 }
263
264 pub fn nonce(&self) -> Option<u32> {
270 Some(self.signature.as_ref()?.extra.nonce)
271 }
272
273 pub fn tip(&self) -> Option<u128> {
279 Some(self.signature.as_ref()?.extra.tip)
280 }
281
282 pub fn ss58_address(&self) -> Option<String> {
288 match &self.signature.as_ref()?.address {
289 MultiAddress::Id(x) => Some(std::format!("{}", x)),
290 _ => None,
291 }
292 }
293
294 pub fn as_signed(self) -> Result<BlockSignedExtrinsic<T>, Error> {
300 BlockSignedExtrinsic::<T>::try_from(self).map_err(Error::Other)
301 }
302}
303
304impl<T: HasHeader + Decode> TryFrom<BlockEncodedExtrinsic> for BlockExtrinsic<T> {
305 type Error = String;
306
307 fn try_from(value: BlockEncodedExtrinsic) -> Result<Self, Self::Error> {
316 let call = T::from_call(value.call)?;
317 Ok(Self::new(value.signature, call, value.metadata))
318 }
319}
320
321impl<T: HasHeader + Decode> TryFrom<&BlockEncodedExtrinsic> for BlockExtrinsic<T> {
322 type Error = String;
323
324 fn try_from(value: &BlockEncodedExtrinsic) -> Result<Self, Self::Error> {
333 let call = T::from_call(&value.call)?;
334 Ok(Self::new(value.signature.clone(), call, value.metadata.clone()))
335 }
336}
337
338#[cfg(test)]
339pub mod tests {
340 use super::*;
341 use crate::TURING_ENDPOINT;
342 use avail_rust_core::avail::{
343 balances::tx::TransferAllowDeath, data_availability::tx::SubmitData, timestamp::tx::Set,
344 vector::tx::FailedSendMessageTxs,
345 };
346
347 fn match_timestamp(ext: &BlockExtrinsic<Set>) {
348 assert_eq!(
349 std::format!("{:?}", ext.ext_hash()),
350 "0xdbfa60611f72a714100338db1c7b11c66636a76f116b214d879de069afe67a74"
351 );
352 assert_eq!(ext.ext_index(), 0);
353 assert_eq!(ext.nonce(), None);
354 assert!(ext.signature.is_none());
355 assert!(ext.app_id().is_none());
356 assert_eq!(ext.call.now, 1761567760000);
357 }
358
359 fn match_failed_send_message(ext: &BlockExtrinsic<FailedSendMessageTxs>) {
360 assert_eq!(
361 std::format!("{:?}", ext.ext_hash()),
362 "0x92cdb77314063a01930b093516d19a453399710cc8ae635ff5ab6cf76b26f218"
363 );
364 assert_eq!(ext.ext_index(), 3);
365 assert_eq!(ext.nonce(), None);
366 assert!(ext.signature.is_none());
367 assert!(ext.app_id().is_none());
368 assert_eq!(ext.call.failed_txs.len(), 0);
369 }
370
371 fn match_submit_data_1(ext: &BlockExtrinsic<SubmitData>) {
372 assert_eq!(
373 std::format!("{:?}", ext.ext_hash()),
374 "0x8b84294cba5f2b88e2887ac999ebac3806af7be9cca2a521fc889421f240f3ef"
375 );
376 assert_eq!(ext.ext_index(), 1);
377 assert_eq!(ext.nonce(), Some(30));
378 assert!(ext.signature.is_some());
379 assert_eq!(ext.app_id(), Some(1));
380 assert_eq!(ext.ss58_address(), Some("5Ev2jfLbYH6ENZ8ThTmqBX58zoinvHyqvRMvtoiUnLLcv1NJ".to_string()));
381 assert_eq!(String::from_utf8(ext.call.data.clone()).unwrap(), "AABBCC");
382 }
383
384 fn match_submit_data_2(ext: &BlockExtrinsic<SubmitData>) {
385 assert_eq!(
386 std::format!("{:?}", ext.ext_hash()),
387 "0x19fab0492322016c644af12f1547c587ef51edd10311db85cb3aa2680f6ae4ba"
388 );
389 assert_eq!(ext.ext_index(), 2);
390 assert_eq!(ext.nonce(), Some(4));
391 assert!(ext.signature.is_some());
392 assert_eq!(ext.app_id(), Some(2));
393 assert_eq!(ext.ss58_address(), Some("5DPDXCcqk1YNVZ3M9s9iwJnr9XAVfTxf8hNa4LS51fjHKAzk".to_string()));
394 assert_eq!(String::from_utf8(ext.call.data.clone()).unwrap(), "CCBBAA");
395 }
396
397 #[tokio::test]
398 async fn query_get_test() {
399 let client = Client::new(TURING_ENDPOINT).await.unwrap();
400 let query = client.block(2491314).extrinsics();
401
402 let ext = query.get::<Set>(0).await.unwrap().unwrap();
403 match_timestamp(&ext);
404
405 let ext = query.get::<SubmitData>(1).await.unwrap().unwrap();
406 match_submit_data_1(&ext);
407
408 let ext = query.get::<SubmitData>(2).await.unwrap().unwrap();
409 match_submit_data_2(&ext);
410
411 let ext = query.get::<FailedSendMessageTxs>(3).await.unwrap().unwrap();
412 match_failed_send_message(&ext);
413
414 assert!(query.get::<FailedSendMessageTxs>(2).await.is_err());
416
417 assert!(query.get::<FailedSendMessageTxs>(4).await.unwrap().is_none());
419 }
420
421 #[tokio::test]
422 async fn query_first_test() {
423 let client = Client::new(TURING_ENDPOINT).await.unwrap();
424 let query = client.block(2491314).extrinsics();
425
426 let opts = Options::new().app_id(1);
428 let ext = query.first(opts).await.unwrap().unwrap();
429 match_submit_data_1(&ext);
430
431 let opts = Options::new().app_id(2);
433 let ext = query.first(opts).await.unwrap().unwrap();
434 match_submit_data_2(&ext);
435
436 let opts = Options::new().nonce(30);
438 let ext = query.first(opts).await.unwrap().unwrap();
439 match_submit_data_1(&ext);
440
441 let opts = Options::new().nonce(4);
443 let ext = query.first(opts).await.unwrap().unwrap();
444 match_submit_data_2(&ext);
445
446 let opts = Options::new().filter(SubmitData::HEADER_INDEX);
448 let ext = query.first(opts).await.unwrap().unwrap();
449 match_submit_data_1(&ext);
450
451 let opts = Options::new().filter(SubmitData::HEADER_INDEX.0);
453 let ext = query.first(opts).await.unwrap().unwrap();
454 match_submit_data_1(&ext);
455
456 let ext = query.first::<Set>(Default::default()).await.unwrap().unwrap();
458 match_timestamp(&ext);
459
460 let opts = Options::new().filter(1u32);
462 assert!(query.first::<Set>(opts).await.is_err());
463
464 let opts = Options::new().filter(100u32);
466 assert!(query.first::<Set>(opts).await.unwrap().is_none());
467 }
468
469 #[tokio::test]
470 async fn query_last_test() {
471 let client = Client::new(TURING_ENDPOINT).await.unwrap();
472 let query = client.block(2491314).extrinsics();
473
474 let opts = Options::new().app_id(1);
476 let ext = query.last(opts).await.unwrap().unwrap();
477 match_submit_data_1(&ext);
478
479 let opts = Options::new().app_id(2);
481 let ext = query.last(opts).await.unwrap().unwrap();
482 match_submit_data_2(&ext);
483
484 let opts = Options::new().nonce(30);
486 let ext = query.last(opts).await.unwrap().unwrap();
487 match_submit_data_1(&ext);
488
489 let opts = Options::new().nonce(4);
491 let ext = query.last(opts).await.unwrap().unwrap();
492 match_submit_data_2(&ext);
493
494 let opts = Options::new().filter(SubmitData::HEADER_INDEX);
496 let ext = query.last(opts).await.unwrap().unwrap();
497 match_submit_data_2(&ext);
498
499 let opts = Options::new().filter(SubmitData::HEADER_INDEX.0);
501 let ext = query.last(opts).await.unwrap().unwrap();
502 match_submit_data_2(&ext);
503
504 let ext = query.last(Default::default()).await.unwrap().unwrap();
506 match_failed_send_message(&ext);
507
508 let opts = Options::new().filter(1u32);
510 assert!(query.last::<Set>(opts).await.is_err());
511
512 let opts = Options::new().filter(100u32);
514 assert!(query.last::<Set>(opts).await.unwrap().is_none());
515 }
516
517 #[tokio::test]
518 async fn query_all_test() {
519 let client = Client::new(TURING_ENDPOINT).await.unwrap();
520 let query = client.block(2491314).extrinsics();
521
522 let opts = Options::new().app_id(1);
524 let ext = query.all(opts).await.unwrap();
525 match_submit_data_1(&ext[0]);
526
527 let opts = Options::new().app_id(2);
529 let ext = query.all(opts).await.unwrap();
530 match_submit_data_2(&ext[0]);
531 assert_eq!(ext.len(), 1);
532
533 let opts = Options::new().nonce(30);
535 let ext = query.all(opts).await.unwrap();
536 match_submit_data_1(&ext[0]);
537 assert_eq!(ext.len(), 1);
538
539 let opts = Options::new().nonce(4);
541 let ext = query.all(opts).await.unwrap();
542 match_submit_data_2(&ext[0]);
543 assert_eq!(ext.len(), 1);
544
545 let opts = Options::new().filter(SubmitData::HEADER_INDEX);
547 let ext = query.all(opts).await.unwrap();
548 match_submit_data_1(&ext[0]);
549 match_submit_data_2(&ext[1]);
550 assert_eq!(ext.len(), 2);
551
552 let opts = Options::new().filter(SubmitData::HEADER_INDEX.0);
554 let ext = query.all(opts).await.unwrap();
555 match_submit_data_1(&ext[0]);
556 match_submit_data_2(&ext[1]);
557 assert_eq!(ext.len(), 2);
558
559 let ext = query.all::<SubmitData>(Default::default()).await.unwrap();
561 match_submit_data_1(&ext[0]);
562 match_submit_data_2(&ext[1]);
563 assert_eq!(ext.len(), 2);
564
565 let ext = query.all::<Set>(Default::default()).await.unwrap();
566 match_timestamp(&ext[0]);
567 assert_eq!(ext.len(), 1);
568
569 let ext = query.all::<FailedSendMessageTxs>(Default::default()).await.unwrap();
570 match_failed_send_message(&ext[0]);
571 assert_eq!(ext.len(), 1);
572
573 let opts = Options::new().filter(1u32);
575 assert!(query.all::<Set>(opts).await.is_err());
576
577 let opts = Options::new().filter(100u32);
579 let ext = query.all::<Set>(opts).await.unwrap();
580 assert_eq!(ext.len(), 0)
581 }
582
583 #[tokio::test]
584 async fn query_count_test() {
585 let client = Client::new(TURING_ENDPOINT).await.unwrap();
586 let query = client.block(2491314).extrinsics();
587
588 let opts = Options::new().app_id(1);
590 assert_eq!(query.count::<SubmitData>(opts).await.unwrap(), 1);
591
592 let opts = Options::new().app_id(2);
594 assert_eq!(query.count::<SubmitData>(opts).await.unwrap(), 1);
595
596 let opts = Options::new().nonce(30);
598 assert_eq!(query.count::<SubmitData>(opts).await.unwrap(), 1);
599
600 let opts = Options::new().nonce(4);
602 assert_eq!(query.count::<SubmitData>(opts).await.unwrap(), 1);
603
604 assert_eq!(query.count::<SubmitData>(Default::default()).await.unwrap(), 2);
606
607 assert_eq!(query.count::<Set>(Default::default()).await.unwrap(), 1);
609
610 assert_eq!(query.count::<TransferAllowDeath>(Default::default()).await.unwrap(), 0);
612 }
613
614 #[tokio::test]
615 async fn query_exists_test() {
616 let client = Client::new(TURING_ENDPOINT).await.unwrap();
617 let query = client.block(2491314).extrinsics();
618
619 let opts = Options::new().app_id(1);
621 assert_eq!(query.exists::<SubmitData>(opts).await.unwrap(), true);
622
623 let opts = Options::new().app_id(2);
625 assert_eq!(query.exists::<SubmitData>(opts).await.unwrap(), true);
626
627 let opts = Options::new().nonce(30);
629 assert_eq!(query.exists::<SubmitData>(opts).await.unwrap(), true);
630
631 let opts = Options::new().nonce(4);
633 assert_eq!(query.exists::<SubmitData>(opts).await.unwrap(), true);
634
635 assert_eq!(query.exists::<SubmitData>(Default::default()).await.unwrap(), true);
637
638 assert_eq!(query.exists::<Set>(Default::default()).await.unwrap(), true);
640
641 assert_eq!(query.exists::<TransferAllowDeath>(Default::default()).await.unwrap(), false);
643 }
644}