1use crate::{
2 BlockState, Client, Error, UserError, avail, conversions,
3 submission::SubmittedTransaction,
4 subxt_signer::sr25519::Keypair,
5 transaction_options::Options,
6 utils::{with_retry_on_error, with_retry_on_error_and_none},
7};
8use avail::{
9 balances::types::AccountData,
10 system::{storage as SystemStorage, types::AccountInfo},
11};
12#[cfg(feature = "next")]
13use avail_rust_core::rpc::{
14 blob::{Blob, BlobInfo},
15 kate::DataProof,
16};
17use avail_rust_core::{
18 AccountId, AccountIdLike, AvailHeader, BlockInfo, H256, HashNumber, StorageMap, StorageValue, consensus,
19 ext::subxt_rpcs::client::RpcParams,
20 grandpa::GrandpaJustification,
21 header::DigestItem,
22 rpc::{
23 self, BlockPhaseEvent, Error as RpcError, ExtrinsicInfo, LegacyBlock,
24 kate::{BlockLength, Cell, GCellBlock, GDataProof, GMultiProof, GRow, ProofResponse},
25 runtime_api,
26 },
27 types::{
28 HashString,
29 metadata::{ChainInfo, HashStringNumber},
30 substrate::{FeeDetails, PerDispatchClassWeight, RuntimeDispatchInfo},
31 },
32};
33use codec::Decode;
34
35pub struct Chain {
37 pub(crate) client: Client,
38 retry_on_error: Option<bool>,
39 retry_on_none: Option<bool>,
40}
41impl Chain {
42 pub fn new(client: Client) -> Self {
47 Self { client, retry_on_error: None, retry_on_none: None }
48 }
49
50 pub fn retry_on(mut self, error: Option<bool>, none: Option<bool>) -> Self {
59 self.retry_on_error = error;
60 self.retry_on_none = none;
61 self
62 }
63
64 pub async fn block_hash(&self, block_height: Option<u32>) -> Result<Option<H256>, RpcError> {
74 let retry = self.should_retry_on_error();
75 let retry_on_none = self.retry_on_none.unwrap_or(false);
76
77 let f = || async move { rpc::chain::get_block_hash(&self.client.rpc_client, block_height).await };
78 with_retry_on_error_and_none(f, retry, retry_on_none).await
79 }
80
81 pub async fn block_header(&self, at: Option<impl Into<HashStringNumber>>) -> Result<Option<AvailHeader>, Error> {
91 let retry_on_error = self.should_retry_on_error();
92 let retry_on_none = self.retry_on_none.unwrap_or(false);
93
94 let at = if let Some(at) = at {
95 Some(conversions::hash_string_number::to_hash(self, at).await?)
96 } else {
97 None
98 };
99
100 let f = || async move { rpc::chain::get_header(&self.client.rpc_client, at).await };
101 Ok(with_retry_on_error_and_none(f, retry_on_error, retry_on_none).await?)
102 }
103
104 pub async fn legacy_block(&self, at: Option<H256>) -> Result<Option<LegacyBlock>, RpcError> {
114 let retry = self.should_retry_on_error();
115 let retry_on_none = self.retry_on_none.unwrap_or(false);
116
117 let f = || async move { rpc::chain::get_block(&self.client.rpc_client, at).await };
118 with_retry_on_error_and_none(f, retry, retry_on_none).await
119 }
120
121 pub async fn block_nonce(
133 &self,
134 account_id: impl Into<AccountIdLike>,
135 at: impl Into<HashStringNumber>,
136 ) -> Result<u32, Error> {
137 self.account_info(account_id, at).await.map(|x| x.nonce)
138 }
139
140 pub async fn account_nonce(&self, account_id: impl Into<AccountIdLike>) -> Result<u32, Error> {
151 let account_id = conversions::account_id_like::to_account_id(account_id)?;
152
153 let retry_on_error = self.should_retry_on_error();
154 let a = &account_id;
155 let f =
156 || async move { rpc::system::account_next_index(&self.client.rpc_client, &std::format!("{}", a)).await };
157
158 Ok(with_retry_on_error(f, retry_on_error).await?)
159 }
160
161 pub async fn account_balance(
172 &self,
173 account_id: impl Into<AccountIdLike>,
174 at: impl Into<HashStringNumber>,
175 ) -> Result<AccountData, Error> {
176 self.account_info(account_id, at).await.map(|x| x.data)
177 }
178
179 pub async fn account_info(
192 &self,
193 account_id: impl Into<AccountIdLike>,
194 at: impl Into<HashStringNumber>,
195 ) -> Result<AccountInfo, Error> {
196 let account_id = conversions::account_id_like::to_account_id(account_id)?;
197 let at = conversions::hash_string_number::to_hash(self, at).await?;
198
199 let retry_on_error = self.should_retry_on_error();
200
201 let a = &account_id;
202 let f = || async move {
203 SystemStorage::Account::fetch(&self.client.rpc_client, a, Some(at))
204 .await
205 .map(|x| x.unwrap_or_default())
206 };
207
208 Ok(with_retry_on_error(f, retry_on_error).await?)
209 }
210
211 pub async fn block_state(&self, block_id: impl Into<HashStringNumber>) -> Result<BlockState, Error> {
220 let block_id = conversions::hash_string_number::to_hash_number(block_id)?;
221 let chain_info = self.chain_info().await?;
222 let n = match block_id {
223 HashNumber::Hash(h) => {
224 if h == chain_info.finalized_hash {
225 return Ok(BlockState::Finalized);
226 }
227
228 if h == chain_info.best_hash {
229 return Ok(BlockState::Included);
230 }
231
232 let Some(n) = self.block_height(h).await? else {
233 return Ok(BlockState::DoesNotExist);
234 };
235
236 let Some(block_hash) = self.block_hash(Some(n)).await? else {
237 return Ok(BlockState::DoesNotExist);
238 };
239
240 if block_hash != h {
241 return Ok(BlockState::Discarded);
242 }
243
244 n
245 },
246 HashNumber::Number(n) => n,
247 };
248
249 if n > chain_info.best_height {
250 return Ok(BlockState::DoesNotExist);
251 }
252
253 if n > chain_info.finalized_height {
254 return Ok(BlockState::Included);
255 }
256
257 Ok(BlockState::Finalized)
258 }
259
260 pub async fn block_height(&self, at: impl Into<HashString>) -> Result<Option<u32>, Error> {
267 let at = conversions::hash_string::to_hash(at)?;
268 let retry_on_error = self.should_retry_on_error();
269 let retry_on_none = self.retry_on_none.unwrap_or(false);
270
271 let f = || async move { rpc::system::get_block_number(&self.client.rpc_client, at).await };
272 Ok(with_retry_on_error_and_none(f, retry_on_error, retry_on_none).await?)
273 }
274
275 pub async fn block_info(&self, use_best_block: bool) -> Result<BlockInfo, RpcError> {
277 let retry = self.should_retry_on_error();
278 let f = || async move { rpc::system::latest_block_info(&self.client.rpc_client, use_best_block).await };
279 with_retry_on_error(f, retry).await
280 }
281
282 pub async fn block_info_from(&self, block_id: impl Into<HashStringNumber>) -> Result<BlockInfo, Error> {
290 let block_id = conversions::hash_string_number::to_hash_number(block_id)?;
291 let (height, hash) = match block_id {
292 HashNumber::Hash(hash) => {
293 let height = self.block_height(hash).await?;
294 let Some(height) = height else {
295 return Err(Error::User(UserError::Other(std::format!(
296 "No block height was found for hash: {}",
297 hash
298 ))));
299 };
300 (height, hash)
301 },
302 HashNumber::Number(height) => {
303 let hash = self.block_hash(Some(height)).await?;
304 let Some(hash) = hash else {
305 return Err(Error::User(UserError::Other(std::format!(
306 "No block hash was found for height: {}",
307 height
308 ))));
309 };
310 (height, hash)
311 },
312 };
313
314 Ok(BlockInfo::from((hash, height)))
315 }
316
317 pub async fn block_author(&self, block_id: impl Into<HashStringNumber>) -> Result<AccountId, Error> {
325 let hash = conversions::hash_string_number::to_hash(self, block_id).await?;
326
327 let header = self.block_header(Some(hash)).await?;
328 let Some(header) = header else {
329 return Err(Error::Other("No block header was found".into()));
330 };
331
332 for item in &header.digest.logs {
333 let (id, value) = match &item {
334 DigestItem::PreRuntime(id, value) => (id, value),
335 _ => continue,
336 };
337
338 if !id.eq(&consensus::babe::BABE_ENGINE_ID) {
339 continue;
340 }
341
342 let mut v = value.as_slice();
343 let pre_digest = consensus::babe::PreDigest::decode(&mut v).map_err(|e| Error::Other(e.to_string()))?;
344
345 let validators = avail::session::storage::Validators::fetch(&self.client.rpc_client, Some(hash)).await?;
346 let Some(validators) = validators else {
347 return Err(Error::Other(std::format!(
348 "No validators in storage was found for block hash: {:?}",
349 hash
350 )));
351 };
352
353 if let Some(account_id) = validators.get(pre_digest.authority_index() as usize) {
354 return Ok(account_id.clone());
355 }
356 }
357
358 Err(Error::Other(std::format!("Failed to find block author for block hash: {}", hash)))
359 }
360
361 pub async fn block_event_count(&self, block_id: impl Into<HashStringNumber>) -> Result<usize, Error> {
369 let hash = conversions::hash_string_number::to_hash(self, block_id).await?;
370 let retry_on_error = self.should_retry_on_error();
371
372 let f = || async move { avail::system::storage::EventCount::fetch(&self.client.rpc_client, Some(hash)).await };
373 let count = with_retry_on_error_and_none(f, retry_on_error, false).await?;
374 let Some(count) = count else {
375 return Err(Error::Other(std::format!("Failed to find block event count at block hash: {:?}", hash)));
376 };
377
378 Ok(count as usize)
379 }
380
381 pub async fn block_weight(&self, block_id: impl Into<HashStringNumber>) -> Result<PerDispatchClassWeight, Error> {
389 let hash = conversions::hash_string_number::to_hash(self, block_id).await?;
390 let retry_on_error = self.should_retry_on_error();
391
392 let f = || async move { avail::system::storage::BlockWeight::fetch(&self.client.rpc_client, Some(hash)).await };
393 let weight = with_retry_on_error_and_none(f, retry_on_error, false).await?;
394 let Some(weight) = weight else {
395 return Err(Error::Other(std::format!("Failed to find block weight at block hash: {:?}", hash)));
396 };
397
398 Ok(weight)
399 }
400
401 pub async fn chain_info(&self) -> Result<ChainInfo, RpcError> {
403 let retry = self.should_retry_on_error();
404
405 let f = || async move { rpc::system::latest_chain_info(&self.client.rpc_client).await };
406 with_retry_on_error(f, retry).await
407 }
408
409 pub async fn build_payload<'a>(
419 &self,
420 account_id: &AccountId,
421 call: &'a avail_rust_core::ExtrinsicCall,
422 options: Options,
423 ) -> Result<avail_rust_core::ExtrinsicPayload<'a>, Error> {
424 let refined_options = options.build(&self.client, account_id, self.retry_on_error).await?;
425
426 let extra = avail_rust_core::ExtrinsicExtra::from(&refined_options);
427 let additional = avail_rust_core::ExtrinsicAdditional {
428 spec_version: self.client.online_client().spec_version(),
429 tx_version: self.client.online_client().transaction_version(),
430 genesis_hash: self.client.online_client().genesis_hash(),
431 fork_hash: refined_options.mortality.block_hash,
432 };
433
434 Ok(avail_rust_core::ExtrinsicPayload::new_borrowed(call, extra, additional))
435 }
436
437 pub async fn build_extrinsic_from_call<'a>(
442 &self,
443 signer: &Keypair,
444 call: &'a avail_rust_core::ExtrinsicCall,
445 options: Options,
446 ) -> Result<avail_rust_core::GenericExtrinsic<'a>, Error> {
447 let account_id = signer.public_key().to_account_id();
448
449 let payload = self.build_payload(&account_id, call, options).await?;
450 let signature = payload.sign(signer);
451
452 Ok(avail_rust_core::GenericExtrinsic::new(account_id, signature, payload))
453 }
454
455 pub async fn submit(&self, ext: &avail_rust_core::GenericExtrinsic<'_>) -> Result<H256, RpcError> {
460 let retry = self.should_retry_on_error();
461 let encoded = ext.encode();
462
463 #[cfg(feature = "tracing")]
464 if let Some(signed) = &ext.signature {
465 if let avail_rust_core::MultiAddress::Id(account_id) = &signed.address {
466 tracing::info!(target: "tx", "Submitting Transaction. Address: {}, Nonce: {}, App Id: {}", account_id, signed.extra.nonce, signed.extra.app_id);
467 }
468 }
469
470 let enc_slice = encoded.as_slice();
471 let f = || async move { rpc::author::submit_extrinsic(&self.client.rpc_client, enc_slice).await };
472 let tx_hash = with_retry_on_error(f, retry).await?;
473
474 #[cfg(feature = "tracing")]
475 if let Some(signed) = &ext.signature {
476 if let avail_rust_core::MultiAddress::Id(account_id) = &signed.address {
477 tracing::info!(target: "tx", "Transaction Submitted. Address: {}, Nonce: {}, App Id: {}, Tx Hash: {:?},", account_id, signed.extra.nonce, signed.extra.app_id, tx_hash);
478 }
479 }
480
481 Ok(tx_hash)
482 }
483
484 pub async fn submit_raw(&self, ext: &[u8]) -> Result<H256, RpcError> {
489 let retry = self.should_retry_on_error();
490
491 let f = || async move { rpc::author::submit_extrinsic(&self.client.rpc_client, ext).await };
492 let tx_hash = with_retry_on_error(f, retry).await?;
493 Ok(tx_hash)
494 }
495
496 pub async fn sign_and_submit_payload(
498 &self,
499 signer: &Keypair,
500 tx_payload: avail_rust_core::ExtrinsicPayload<'_>,
501 ) -> Result<H256, RpcError> {
502 use avail_rust_core::GenericExtrinsic;
503
504 let account_id = signer.public_key().to_account_id();
505 let signature = tx_payload.sign(signer);
506 let tx = GenericExtrinsic::new(account_id, signature, tx_payload);
507 let tx_hash = self.submit(&tx).await?;
508
509 Ok(tx_hash)
510 }
511
512 pub async fn sign_and_submit_call(
519 &self,
520 signer: &Keypair,
521 tx_call: &avail_rust_core::ExtrinsicCall,
522 options: Options,
523 ) -> Result<SubmittedTransaction, Error> {
524 let account_id = signer.public_key().to_account_id();
525 let refined_options = options.build(&self.client, &account_id, self.retry_on_error).await?;
526
527 let extra = avail_rust_core::ExtrinsicExtra::from(&refined_options);
528 let tx_additional = avail_rust_core::ExtrinsicAdditional {
529 spec_version: self.client.online_client().spec_version(),
530 tx_version: self.client.online_client().transaction_version(),
531 genesis_hash: self.client.online_client().genesis_hash(),
532 fork_hash: refined_options.mortality.block_hash,
533 };
534
535 let tx_payload = avail_rust_core::ExtrinsicPayload::new_borrowed(tx_call, extra, tx_additional.clone());
536 let tx_hash = self.sign_and_submit_payload(signer, tx_payload).await?;
537
538 let value = SubmittedTransaction::new(self.client.clone(), tx_hash, account_id, refined_options, tx_additional);
539 Ok(value)
540 }
541
542 pub async fn state_call(&self, method: &str, data: &[u8], at: Option<H256>) -> Result<String, RpcError> {
544 let retry = self.should_retry_on_error();
545
546 let f = || async move { rpc::state::call(&self.client.rpc_client, method, data, at).await };
547 with_retry_on_error(f, retry).await
548 }
549
550 pub async fn state_get_metadata(&self, at: Option<H256>) -> Result<Vec<u8>, RpcError> {
552 let retry = self.should_retry_on_error();
553
554 let f = || async move { rpc::state::get_metadata(&self.client.rpc_client, at).await };
555 with_retry_on_error(f, retry).await
556 }
557
558 pub async fn state_get_storage(&self, key: &str, at: Option<H256>) -> Result<Option<Vec<u8>>, RpcError> {
560 let retry = self.should_retry_on_error();
561
562 let f = || async move { rpc::state::get_storage(&self.client.rpc_client, key, at).await };
563 with_retry_on_error(f, retry).await
564 }
565
566 pub async fn state_get_keys_paged(
568 &self,
569 prefix: Option<&str>,
570 count: u32,
571 start_key: Option<&str>,
572 at: Option<H256>,
573 ) -> Result<Vec<String>, RpcError> {
574 let retry = self.should_retry_on_error();
575
576 let f =
577 || async move { rpc::state::get_keys_paged(&self.client.rpc_client, prefix, count, start_key, at).await };
578
579 with_retry_on_error(f, retry).await
580 }
581
582 pub async fn rpc_raw_call<T: serde::de::DeserializeOwned>(
584 &self,
585 method: &str,
586 params: RpcParams,
587 ) -> Result<T, RpcError> {
588 let retry = self.should_retry_on_error();
589
590 let p = ¶ms;
591 let f = || async move { rpc::raw_call(&self.client.rpc_client, method, p.clone()).await };
592 with_retry_on_error(f, retry).await
593 }
594
595 pub async fn runtime_api_raw_call<T: codec::Decode>(
597 &self,
598 method: &str,
599 data: &[u8],
600 at: Option<H256>,
601 ) -> Result<T, RpcError> {
602 let retry = self.should_retry_on_error();
603
604 let f = || async move { runtime_api::raw_call(&self.client.rpc_client, method, data, at).await };
605 with_retry_on_error(f, retry).await
606 }
607
608 pub async fn grandpa_block_justification(&self, at: u32) -> Result<Option<GrandpaJustification>, RpcError> {
615 let retry = self.should_retry_on_error();
616
617 let f = || async move { rpc::grandpa::block_justification(&self.client.rpc_client, at).await };
618 let result = with_retry_on_error(f, retry).await?;
619
620 let Some(result) = result else {
621 return Ok(None);
622 };
623
624 let justification = const_hex::decode(result.trim_start_matches("0x"))
625 .map_err(|x| RpcError::MalformedResponse(x.to_string()))?;
626
627 let justification = GrandpaJustification::decode(&mut justification.as_slice());
628 let justification = justification.map_err(|e| RpcError::MalformedResponse(e.to_string()))?;
629 Ok(Some(justification))
630 }
631
632 pub async fn transaction_payment_query_info(
641 &self,
642 extrinsic: Vec<u8>,
643 at: Option<H256>,
644 ) -> Result<RuntimeDispatchInfo, RpcError> {
645 let retry = self.should_retry_on_error();
646
647 let ext = &extrinsic;
648 let f = || async move {
649 runtime_api::api_transaction_payment_query_info(&self.client.rpc_client, ext.clone(), at).await
650 };
651 with_retry_on_error(f, retry).await
652 }
653
654 pub async fn transaction_payment_query_fee_details(
663 &self,
664 extrinsic: Vec<u8>,
665 at: Option<H256>,
666 ) -> Result<FeeDetails, RpcError> {
667 let retry = self.should_retry_on_error();
668
669 let ext = &extrinsic;
670 let f = || async move {
671 runtime_api::api_transaction_payment_query_fee_details(&self.client.rpc_client, ext.clone(), at).await
672 };
673 with_retry_on_error(f, retry).await
674 }
675
676 pub async fn transaction_payment_query_call_info(
685 &self,
686 call: Vec<u8>,
687 at: Option<H256>,
688 ) -> Result<RuntimeDispatchInfo, RpcError> {
689 let retry = self.should_retry_on_error();
690
691 let c = &call;
692 let f = || async move {
693 runtime_api::api_transaction_payment_query_call_info(&self.client.rpc_client, c.clone(), at).await
694 };
695 with_retry_on_error(f, retry).await
696 }
697
698 pub async fn transaction_payment_query_call_fee_details(
707 &self,
708 call: Vec<u8>,
709 at: Option<H256>,
710 ) -> Result<FeeDetails, RpcError> {
711 let retry = self.should_retry_on_error();
712
713 let c = &call;
714 let f = || async move {
715 runtime_api::api_transaction_payment_query_call_fee_details(&self.client.rpc_client, c.clone(), at).await
716 };
717 with_retry_on_error(f, retry).await
718 }
719
720 pub async fn kate_block_length(&self, at: Option<H256>) -> Result<BlockLength, RpcError> {
725 let retry = self.should_retry_on_error();
726
727 let f = || async move { rpc::kate::block_length(&self.client.rpc_client, at).await };
728 with_retry_on_error(f, retry).await
729 }
730
731 pub async fn kate_query_data_proof(
736 &self,
737 transaction_index: u32,
738 at: Option<H256>,
739 ) -> Result<ProofResponse, RpcError> {
740 let retry = self.should_retry_on_error();
741
742 let f = || async move { rpc::kate::query_data_proof(&self.client.rpc_client, transaction_index, at).await };
743 with_retry_on_error(f, retry).await
744 }
745
746 pub async fn kate_query_proof(&self, cells: Vec<Cell>, at: Option<H256>) -> Result<Vec<GDataProof>, RpcError> {
751 let retry = self.should_retry_on_error();
752
753 let cells_ref = &cells;
754 let f = || async move { rpc::kate::query_proof(&self.client.rpc_client, cells_ref.clone(), at).await };
755 with_retry_on_error(f, retry).await
756 }
757
758 pub async fn kate_query_rows(&self, rows: Vec<u32>, at: Option<H256>) -> Result<Vec<GRow>, RpcError> {
763 let retry = self.should_retry_on_error();
764
765 let rows_ref = &rows;
766 let f = || async move { rpc::kate::query_rows(&self.client.rpc_client, rows_ref.clone(), at).await };
767 with_retry_on_error(f, retry).await
768 }
769
770 pub async fn kate_query_multi_proof(
775 &self,
776 cells: Vec<Cell>,
777 at: Option<H256>,
778 ) -> Result<Vec<(GMultiProof, GCellBlock)>, RpcError> {
779 let retry = self.should_retry_on_error();
780
781 let cells_ref = &cells;
782 let f = || async move { rpc::kate::query_multi_proof(&self.client.rpc_client, cells_ref.clone(), at).await };
783 with_retry_on_error(f, retry).await
784 }
785
786 #[cfg(feature = "next")]
787 pub async fn blob_submit_blob(&self, metadata_signed_transaction: &[u8], blob: &[u8]) -> Result<(), Error> {
796 let retry = self.should_retry_on_error();
797
798 let f =
799 || async move { rpc::blob::submit_blob(&self.client.rpc_client, metadata_signed_transaction, blob).await };
800
801 Ok(with_retry_on_error(f, retry).await?)
802 }
803
804 #[cfg(feature = "next")]
805 pub async fn blob_get_blob(&self, blob_hash: H256, block_hash: Option<H256>) -> Result<Blob, Error> {
806 let retry = self.should_retry_on_error();
807
808 let f = || async move { rpc::blob::get_blob_v2(&self.client.rpc_client, blob_hash, block_hash).await };
809
810 Ok(with_retry_on_error(f, retry).await?)
811 }
812
813 #[cfg(feature = "next")]
815 pub async fn blob_get_blob_info(&self, blob_hash: H256) -> Result<BlobInfo, Error> {
816 let retry = self.should_retry_on_error();
817
818 let f = || async move { rpc::blob::get_blob_info(&self.client.rpc_client, blob_hash).await };
819
820 Ok(with_retry_on_error(f, retry).await?)
821 }
822
823 #[cfg(feature = "next")]
826 pub async fn blob_inclusion_proof(&self, blob_hash: H256, at: Option<H256>) -> Result<DataProof, Error> {
827 let retry = self.should_retry_on_error();
828
829 let f = || async move { rpc::blob::inclusion_proof(&self.client.rpc_client, blob_hash, at).await };
830
831 Ok(with_retry_on_error(f, retry).await?)
832 }
833
834 pub async fn system_fetch_extrinsics(
839 &self,
840 block_id: impl Into<HashStringNumber>,
841 opts: rpc::ExtrinsicOpts,
842 ) -> Result<Vec<ExtrinsicInfo>, Error> {
843 let block_id = conversions::hash_string_number::to_hash_number(block_id)?;
844 let retry = self.should_retry_on_error();
845
846 let opts2 = &opts;
847 let f = || async move { rpc::system::fetch_extrinsics_v1(&self.client.rpc_client, block_id, opts2).await };
848 with_retry_on_error(f, retry).await.map_err(|e| e.into())
849 }
850
851 pub async fn system_fetch_events(
856 &self,
857 at: impl Into<HashStringNumber>,
858 opts: rpc::EventOpts,
859 ) -> Result<Vec<BlockPhaseEvent>, Error> {
860 let at = conversions::hash_string_number::to_hash(self, at).await?;
861 let retry = self.should_retry_on_error();
862
863 let opts2 = &opts;
864 let f = || async move { rpc::system::fetch_events_v1(&self.client.rpc_client, at, opts2).await };
865 with_retry_on_error(f, retry).await.map_err(|e| e.into())
866 }
867
868 pub fn should_retry_on_error(&self) -> bool {
870 self.retry_on_error
871 .unwrap_or_else(|| self.client.is_global_retries_enabled())
872 }
873}