1use super::{
16 builders::{
17 constructor_exec_input,
18 CreateBuilderPartial,
19 },
20 deposit_limit_to_balance,
21 events::{
22 CodeStoredEvent,
23 EventWithTopics,
24 },
25 log_error,
26 log_info,
27 sr25519,
28 InstantiateDryRunResult,
29 Keypair,
30 ReviveApi,
31 H256,
32};
33use crate::{
34 backend::{
35 BuilderClient,
36 ChainBackend,
37 },
38 client_utils::{
39 salt,
40 ContractsRegistry,
41 },
42 contract_results::{
43 BareInstantiationResult,
44 CallDryRunResult,
45 CallResult,
46 ContractResult,
47 UploadResult,
48 },
49 error::DryRunError,
50 events,
51 ContractsBackend,
52 E2EBackend,
53};
54use ink_env::{
55 call::{
56 utils::{
57 ReturnType,
58 Set,
59 },
60 Call,
61 ExecutionInput,
62 },
63 Environment,
64};
65use ink_primitives::{
66 reflect::{
67 AbiDecodeWith,
68 AbiEncodeWith,
69 },
70 types::AccountIdMapper,
71 DepositLimit,
72};
73use jsonrpsee::core::async_trait;
74use pallet_revive::evm::CallTrace;
75use scale::Encode;
76use sp_weights::Weight;
77#[cfg(feature = "std")]
78use std::fmt::Debug;
79use std::path::PathBuf;
80use subxt::{
81 blocks::ExtrinsicEvents,
82 config::{
83 DefaultExtrinsicParams,
84 ExtrinsicParams,
85 },
86 error::DispatchError,
87 events::EventDetails,
88 ext::scale_value::{
89 Composite,
90 Value,
91 ValueDef,
92 },
93 tx::Signer,
94};
95
96pub type Error = crate::error::Error<DispatchError>;
97
98pub type CallBuilderFinal<E, Args, RetType, Abi> = ink_env::call::CallBuilder<
100 E,
101 Set<Call>,
102 Set<ExecutionInput<Args, Abi>>,
103 Set<ReturnType<RetType>>,
104>;
105
106pub struct Client<C, E>
111where
112 C: subxt::Config,
113 E: Environment,
114{
115 pub api: ReviveApi<C, E>,
117 pub contracts: ContractsRegistry,
118 url: String,
119}
120
121impl<C, E> Client<C, E>
122where
123 C: subxt::Config,
124 C::AccountId:
125 From<sr25519::PublicKey> + scale::Codec + serde::de::DeserializeOwned + Debug,
126 C::Address: From<sr25519::PublicKey>,
127 C::Signature: From<sr25519::Signature>,
128 <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
129 From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
130
131 E: Environment,
132 E::AccountId: Debug,
133 E::EventRecord: Debug,
134 E::Balance: Debug + scale::HasCompact + serde::Serialize,
135 H256: Debug + scale::Encode,
136{
137 pub async fn new<P: Into<PathBuf>>(
139 client: subxt::backend::rpc::RpcClient,
140 contracts: impl IntoIterator<Item = P>,
141 url: String,
142 ) -> Result<Self, subxt::Error> {
143 Ok(Self {
144 api: ReviveApi::new(client).await?,
145 contracts: ContractsRegistry::new(contracts),
146 url,
147 })
148 }
149
150 pub async fn exec_instantiate(
153 &mut self,
154 signer: &Keypair,
155 code: Vec<u8>,
156 data: Vec<u8>,
157 value: E::Balance,
158 gas_limit: Weight,
159 storage_deposit_limit: E::Balance,
160 ) -> Result<BareInstantiationResult<ExtrinsicEvents<C>>, Error> {
161 let salt = salt();
162 assert!(salt.is_some());
164 let (events, trace) = self
165 .api
166 .instantiate_with_code(
167 value,
168 gas_limit.into(),
169 storage_deposit_limit,
170 code.clone(),
171 data.clone(),
172 salt,
173 signer,
174 )
175 .await;
176
177 for evt in events.iter() {
178 let evt = evt.unwrap_or_else(|err| {
179 panic!("unable to unwrap event: {err:?}");
180 });
181 if is_extrinsic_failed_event(&evt) {
182 let metadata = self.api.client.metadata();
183 let dispatch_error =
184 subxt::error::DispatchError::decode_from(evt.field_bytes(), metadata)
185 .map_err(|e| Error::Decoding(e.to_string()))?;
186 log_error(&format!(
187 "extrinsic for instantiate failed: {dispatch_error}"
188 ));
189 return Err(Error::InstantiateExtrinsic(dispatch_error))
190 }
191 }
192
193 let account_id =
195 <subxt_signer::sr25519::Keypair as subxt::tx::Signer<C>>::account_id(signer);
196 let account_bytes = account_id.encode();
197 let deployer = AccountIdMapper::to_address(account_bytes.as_ref());
198 let addr = pallet_revive::create2(
199 &deployer,
200 &code[..],
201 &data[..],
202 &salt.expect("todo make salt() return no option, but value"),
203 );
204
205 Ok(BareInstantiationResult {
206 addr,
209 events,
210 trace,
211 })
212 }
213
214 async fn exec_upload(
216 &mut self,
217 signer: &Keypair,
218 code: Vec<u8>,
219 _storage_deposit_limit: E::Balance,
220 ) -> Result<UploadResult<E, ExtrinsicEvents<C>>, Error> {
221 let storage_deposit_limit: E::Balance = unsafe {
223 core::mem::transmute_copy::<u128, <E as Environment>::Balance>(&u128::MAX)
224 };
225 let dry_run = self
226 .api
227 .upload_dry_run(signer, code.clone(), storage_deposit_limit)
228 .await;
229 log_info(&format!("upload dry run: {dry_run:?}"));
230 if let Err(err) = dry_run {
231 let dispatch_err = self.runtime_dispatch_error_to_subxt_dispatch_error(&err);
232 return Err(Error::UploadDryRun(dispatch_err))
233 }
234
235 let tx_events = self.api.upload(signer, code, storage_deposit_limit).await;
236
237 let mut hash = None;
238 for evt in tx_events.iter() {
239 let evt = evt.unwrap_or_else(|err| {
240 panic!("unable to unwrap event: {err:?}");
241 });
242
243 if let Some(uploaded) =
244 evt.as_event::<CodeStoredEvent>().unwrap_or_else(|err| {
245 panic!("event conversion to `Uploaded` failed: {err:?}");
246 })
247 {
248 log_info(&format!(
249 "contract was uploaded with hash {:?}",
250 uploaded.code_hash
251 ));
252 hash = Some(uploaded.code_hash);
253 break
254 } else if is_extrinsic_failed_event(&evt) {
255 let metadata = self.api.client.metadata();
256 let dispatch_error =
257 DispatchError::decode_from(evt.field_bytes(), metadata)
258 .map_err(|e| Error::Decoding(e.to_string()))?;
259
260 log_error(&format!("extrinsic for upload failed: {dispatch_error}"));
261 return Err(Error::UploadExtrinsic(dispatch_error))
262 }
263 }
264
265 let code_hash = match hash {
271 Some(hash) => hash,
272 None => {
273 dry_run
274 .as_ref()
275 .unwrap_or_else(|err| panic!("must have worked: {err:?}"))
276 .code_hash
277 }
278 };
279
280 Ok(UploadResult {
281 dry_run,
282 code_hash,
283 events: tx_events,
284 })
285 }
286
287 #[allow(clippy::type_complexity)]
291 fn contract_result_to_result<V>(
292 &self,
293 contract_result: ContractResult<V, E::Balance>,
294 ) -> Result<ContractResult<V, E::Balance>, DryRunError<DispatchError>> {
295 if let Err(error) = contract_result.result {
296 let subxt_dispatch_err =
297 self.runtime_dispatch_error_to_subxt_dispatch_error(&error);
298 Err(DryRunError::<DispatchError> {
299 error: subxt_dispatch_err,
300 })
301 } else {
302 Ok(contract_result)
303 }
304 }
305
306 fn runtime_dispatch_error_to_subxt_dispatch_error(
309 &self,
310 dispatch_error: &sp_runtime::DispatchError,
311 ) -> DispatchError {
312 let dispatch_err_encoded = Encode::encode(&dispatch_error);
313 DispatchError::decode_from(dispatch_err_encoded, self.api.client.metadata())
314 .expect("failed to decode valid dispatch error")
315 }
316
317 pub fn url(&self) -> &str {
319 &self.url
320 }
321}
322
323#[async_trait]
324impl<C, E> ChainBackend for Client<C, E>
325where
326 C: subxt::Config + Send + Sync,
327 C::AccountId: Clone
328 + Debug
329 + Send
330 + Sync
331 + core::fmt::Display
332 + scale::Codec
333 + From<sr25519::PublicKey>
334 + serde::de::DeserializeOwned,
335 C::Address: From<sr25519::PublicKey>,
336 C::Signature: From<sr25519::Signature>,
337 C::Address: Send + Sync,
338 <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
339 From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
340
341 E: Environment,
342 E::AccountId: Debug + Send + Sync,
343 E::Balance: Clone
344 + Debug
345 + Send
346 + Sync
347 + TryFrom<u128>
348 + scale::HasCompact
349 + serde::Serialize,
350 E::EventRecord: Debug,
351{
352 type AccountId = E::AccountId;
353 type Balance = E::Balance;
354 type Error = Error;
355 type EventLog = ExtrinsicEvents<C>;
356
357 async fn create_and_fund_account(
358 &mut self,
359 origin: &Keypair,
360 amount: Self::Balance,
361 ) -> Keypair {
362 let (_, phrase, _) =
363 <sp_core::sr25519::Pair as sp_core::Pair>::generate_with_phrase(None);
364 let phrase =
365 subxt_signer::bip39::Mnemonic::parse(phrase).expect("valid phrase expected");
366 let keypair = Keypair::from_phrase(&phrase, None).expect("valid phrase expected");
367 let account_id = <Keypair as Signer<C>>::account_id(&keypair);
368 let origin_account_id = origin.public_key().to_account_id();
369
370 self.api
371 .try_transfer_balance(origin, account_id.clone(), amount)
372 .await
373 .unwrap_or_else(|err| {
374 panic!(
375 "transfer from {} to {} failed with {:?}",
376 origin_account_id, account_id, err
377 )
378 });
379
380 log_info(&format!(
381 "transfer from {} to {} succeeded",
382 origin_account_id, account_id,
383 ));
384
385 keypair
386 }
387
388 async fn free_balance(
389 &mut self,
390 account: Self::AccountId,
391 ) -> Result<Self::Balance, Self::Error> {
392 let account_addr = subxt::dynamic::storage(
393 "System",
394 "Account",
395 vec![
396 Value::from_bytes(&account),
399 ],
400 );
401
402 let best_block = self.api.best_block().await;
403
404 let account = self
405 .api
406 .client
407 .storage()
408 .at(best_block)
409 .fetch_or_default(&account_addr)
410 .await
411 .unwrap_or_else(|err| {
412 panic!("unable to fetch balance: {err:?}");
413 })
414 .to_value()
415 .unwrap_or_else(|err| {
416 panic!("unable to decode account info: {err:?}");
417 });
418
419 let account_data = get_composite_field_value(&account, "data")?;
420 let balance = get_composite_field_value(account_data, "free")?;
421 let balance = balance.as_u128().ok_or_else(|| {
422 Error::Balance(format!("{balance:?} should convert to u128"))
423 })?;
424 let balance = E::Balance::try_from(balance).map_err(|_| {
425 Error::Balance(format!("{balance:?} failed to convert from u128"))
426 })?;
427
428 log_info(&format!("balance of contract {account:?} is {balance:?}"));
429 Ok(balance)
430 }
431
432 async fn runtime_call<'a>(
433 &mut self,
434 origin: &Keypair,
435 pallet_name: &'a str,
436 call_name: &'a str,
437 call_data: Vec<Value>,
438 ) -> Result<Self::EventLog, Self::Error> {
439 let tx_events = self
440 .api
441 .runtime_call(origin, pallet_name, call_name, call_data)
442 .await;
443
444 for evt in tx_events.iter() {
445 let evt = evt.unwrap_or_else(|err| {
446 panic!("unable to unwrap event: {err:?}");
447 });
448
449 if is_extrinsic_failed_event(&evt) {
450 let metadata = self.api.client.metadata();
451 let dispatch_error =
452 subxt::error::DispatchError::decode_from(evt.field_bytes(), metadata)
453 .map_err(|e| Error::Decoding(e.to_string()))?;
454
455 log_error(&format!("extrinsic for call failed: {dispatch_error}"));
456 return Err(Error::CallExtrinsic(dispatch_error))
457 }
458 }
459
460 Ok(tx_events)
461 }
462}
463
464#[async_trait]
465impl<C, E> BuilderClient<E> for Client<C, E>
466where
467 C: subxt::Config + Send + Sync,
468 C::AccountId: Clone
469 + Debug
470 + Send
471 + Sync
472 + core::fmt::Display
473 + scale::Codec
474 + From<sr25519::PublicKey>
475 + serde::de::DeserializeOwned,
476 C::Address: From<sr25519::PublicKey>,
477 C::Signature: From<sr25519::Signature>,
478 C::Address: Send + Sync,
479 <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
480 From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
481
482 E: Environment,
483 E::AccountId: Debug + Send + Sync,
484 E::EventRecord: Debug,
485 E::Balance:
486 Clone + Debug + Send + Sync + From<u128> + scale::HasCompact + serde::Serialize,
487 H256: Debug + Send + Sync + scale::Encode,
488{
489 async fn bare_instantiate<
490 Contract: Clone,
491 Args: Send + Sync + AbiEncodeWith<Abi> + Clone,
492 R,
493 Abi: Send + Sync + Clone,
494 >(
495 &mut self,
496 contract_name: &str,
497 caller: &Keypair,
498 constructor: &mut CreateBuilderPartial<E, Contract, Args, R, Abi>,
499 value: E::Balance,
500 gas_limit: Weight,
501 storage_deposit_limit: DepositLimit<E::Balance>,
502 ) -> Result<BareInstantiationResult<Self::EventLog>, Self::Error> {
503 let code = self.contracts.load_code(contract_name);
504 let data = constructor_exec_input(constructor.clone());
505 let storage_deposit_limit = deposit_limit_to_balance::<E>(storage_deposit_limit);
506 let ret = self
507 .exec_instantiate(caller, code, data, value, gas_limit, storage_deposit_limit)
508 .await?;
509 log_info(&format!("instantiated contract at {:?}", ret.addr));
510 Ok(ret)
511 }
512
513 async fn bare_instantiate_dry_run<
514 Contract: Clone,
515 Args: Send + Sync + AbiEncodeWith<Abi> + Clone,
516 R,
517 Abi: Send + Sync + Clone,
518 >(
519 &mut self,
520 contract_name: &str,
521 caller: &Keypair,
522 constructor: &mut CreateBuilderPartial<E, Contract, Args, R, Abi>,
523 value: E::Balance,
524 storage_deposit_limit: DepositLimit<E::Balance>,
525 ) -> Result<InstantiateDryRunResult<E>, Self::Error> {
526 let _ = self.map_account(caller).await;
529
530 let code = self.contracts.load_code(contract_name);
531 let data = constructor_exec_input(constructor.clone());
532
533 let result = self
534 .api
535 .instantiate_with_code_dry_run(
536 value,
537 storage_deposit_limit,
538 code,
539 data,
540 salt(),
541 caller,
542 )
543 .await;
544
545 log_info(&format!("instantiate dry run: {:?}", &result.result));
546 let result = self
547 .contract_result_to_result(result)
548 .map_err(Error::InstantiateDryRun)?;
549
550 Ok(result.into())
561 }
562
563 async fn bare_upload(
564 &mut self,
565 contract_name: &str,
566 caller: &Keypair,
567 storage_deposit_limit: E::Balance,
568 ) -> Result<UploadResult<E, Self::EventLog>, Self::Error> {
569 let code = self.contracts.load_code(contract_name);
570 let ret = self
571 .exec_upload(caller, code, storage_deposit_limit)
572 .await?;
573 log_info(&format!("contract stored with hash {:?}", ret.code_hash));
574 Ok(ret)
575 }
576
577 async fn bare_remove_code(
578 &mut self,
579 caller: &Keypair,
580 code_hash: H256,
581 ) -> Result<Self::EventLog, Self::Error> {
582 let tx_events = self.api.remove_code(caller, code_hash).await;
583
584 for evt in tx_events.iter() {
585 let evt = evt.unwrap_or_else(|err| {
586 panic!("unable to unwrap event: {err:?}");
587 });
588
589 if is_extrinsic_failed_event(&evt) {
590 let metadata = self.api.client.metadata();
591 let dispatch_error =
592 DispatchError::decode_from(evt.field_bytes(), metadata)
593 .map_err(|e| Error::Decoding(e.to_string()))?;
594 return Err(Error::RemoveCodeExtrinsic(dispatch_error))
595 }
596 }
597
598 Ok(tx_events)
599 }
600
601 async fn bare_call<
602 Args: Sync + AbiEncodeWith<Abi> + Clone,
603 RetType: Send + AbiDecodeWith<Abi>,
604 Abi: Sync + Clone,
605 >(
606 &mut self,
607 caller: &Keypair,
608 message: &CallBuilderFinal<E, Args, RetType, Abi>,
609 value: E::Balance,
610 gas_limit: Weight,
611 storage_deposit_limit: DepositLimit<E::Balance>,
612 ) -> Result<(Self::EventLog, Option<CallTrace>), Self::Error>
613 where
614 CallBuilderFinal<E, Args, RetType, Abi>: Clone,
615 {
616 let addr = *message.clone().params().callee();
617 let exec_input = message.clone().params().exec_input().encode();
618 log_info(&format!("call: {:02X?}", exec_input));
619
620 let (tx_events, trace) = self
621 .api
622 .call(
623 addr,
624 value,
625 gas_limit.into(),
626 deposit_limit_to_balance::<E>(storage_deposit_limit),
627 exec_input,
628 caller,
629 )
630 .await;
631
632 for evt in tx_events.iter() {
633 let evt = evt.unwrap_or_else(|err| {
634 panic!("unable to unwrap event: {err:?}");
635 });
636
637 if is_extrinsic_failed_event(&evt) {
638 let metadata = self.api.client.metadata();
639 let dispatch_error =
640 DispatchError::decode_from(evt.field_bytes(), metadata)
641 .map_err(|e| Error::Decoding(e.to_string()))?;
642 log_error(&format!("extrinsic for call failed: {dispatch_error}"));
643 return Err(Error::CallExtrinsic(dispatch_error))
644 }
645 }
646
647 Ok((tx_events, trace))
648 }
649
650 async fn bare_call_dry_run<
652 Args: Sync + AbiEncodeWith<Abi> + Clone,
653 RetType: Send + AbiDecodeWith<Abi>,
654 Abi: Sync + Clone,
655 >(
656 &mut self,
657 caller: &Keypair,
658 message: &CallBuilderFinal<E, Args, RetType, Abi>,
659 value: E::Balance,
660 storage_deposit_limit: DepositLimit<E::Balance>,
661 ) -> Result<CallDryRunResult<E, RetType>, Self::Error>
662 where
663 CallBuilderFinal<E, Args, RetType, Abi>: Clone,
664 {
665 let _ = self.map_account(caller).await;
668
669 let dest = *message.clone().params().callee();
670 let exec_input = message.clone().params().exec_input().encode();
671
672 let (exec_result, trace) = self
673 .api
674 .call_dry_run(
675 Signer::<C>::account_id(caller), dest,
680 exec_input,
681 value,
682 deposit_limit_to_balance::<E>(storage_deposit_limit),
683 caller,
684 )
685 .await;
686 log_info(&format!("call dry run result: {:?}", &exec_result.result));
687
688 let exec_result = self
689 .contract_result_to_result(exec_result)
690 .map_err(Error::CallDryRun)?;
691
692 Ok(CallDryRunResult {
693 exec_result,
694 trace,
695 _marker: Default::default(),
696 })
697 }
698
699 async fn map_account(&mut self, caller: &Keypair) -> Result<(), Self::Error> {
700 let tx_events = self.api.map_account(caller).await;
701
702 for evt in tx_events.iter() {
703 let evt = evt.unwrap_or_else(|err| {
704 panic!("unable to unwrap event: {err:?}");
705 });
706
707 if is_extrinsic_failed_event(&evt) {
708 let metadata = self.api.client.metadata();
709 let dispatch_error =
710 DispatchError::decode_from(evt.field_bytes(), metadata)
711 .map_err(|e| Error::Decoding(e.to_string()))?;
712 log_error(&format!("extrinsic for call failed: {dispatch_error}"));
713 return Err(Error::CallExtrinsic(dispatch_error))
714 }
715 }
716
717 Ok(())
719 }
720
721 async fn map_account_dry_run(&mut self, caller: &Keypair) -> Result<(), Self::Error> {
724 let tx_events = self.api.map_account(caller).await;
725
726 for evt in tx_events.iter() {
727 let evt = evt.unwrap_or_else(|err| {
728 panic!("unable to unwrap event: {err:?}");
729 });
730
731 if is_extrinsic_failed_event(&evt) {
732 let metadata = self.api.client.metadata();
733 let dispatch_error =
734 DispatchError::decode_from(evt.field_bytes(), metadata)
735 .map_err(|e| Error::Decoding(e.to_string()))?;
736 log_error(&format!("extrinsic for call failed: {dispatch_error}"));
737 return Err(Error::CallExtrinsic(dispatch_error))
738 }
739 }
740
741 Ok(())
742 }
743}
744
745impl<C, E> ContractsBackend<E> for Client<C, E>
746where
747 C: subxt::Config + Send + Sync,
748 C::AccountId: Clone
749 + Debug
750 + Send
751 + Sync
752 + core::fmt::Display
753 + scale::Codec
754 + From<sr25519::PublicKey>
755 + serde::de::DeserializeOwned,
756 C::Address: From<sr25519::PublicKey>,
757 C::Signature: From<sr25519::Signature>,
758 C::Address: Send + Sync,
759
760 E: Environment,
761 E::AccountId: Debug + Send + Sync,
762 E::Balance:
763 Clone + Debug + Send + Sync + From<u128> + scale::HasCompact + serde::Serialize,
764 H256: Debug + Send + scale::Encode,
765{
766 type Error = Error;
767 type EventLog = ExtrinsicEvents<C>;
768}
769
770impl<C, E> E2EBackend<E> for Client<C, E>
771where
772 C: subxt::Config + Send + Sync,
773 C::AccountId: Clone
774 + Debug
775 + Send
776 + Sync
777 + core::fmt::Display
778 + scale::Codec
779 + From<sr25519::PublicKey>
780 + serde::de::DeserializeOwned,
781 C::Address: From<sr25519::PublicKey>,
782 C::Signature: From<sr25519::Signature>,
783 C::Address: Send + Sync,
784 <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
785 From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
786
787 E: Environment,
788 E::AccountId: Debug + Send + Sync,
789 E::EventRecord: Debug,
790 E::Balance:
791 Clone + Debug + Send + Sync + From<u128> + scale::HasCompact + serde::Serialize,
792 H256: Debug + Send + Sync + scale::Encode,
793{
794}
795
796fn get_composite_field_value<'a, T>(
802 value: &'a Value<T>,
803 field_name: &str,
804) -> Result<&'a Value<T>, Error> {
805 if let ValueDef::Composite(Composite::Named(fields)) = &value.value {
806 let (_, field) = fields
807 .iter()
808 .find(|(name, _)| name == field_name)
809 .ok_or_else(|| {
810 Error::Balance(format!("No field named '{field_name}' found"))
811 })?;
812 Ok(field)
813 } else {
814 Err(Error::Balance(
815 "Expected a composite type with named fields".into(),
816 ))
817 }
818}
819
820fn is_extrinsic_failed_event<C: subxt::Config>(event: &EventDetails<C>) -> bool {
822 event.pallet_name() == "System" && event.variant_name() == "ExtrinsicFailed"
823}
824
825impl<E: Environment, V, C: subxt::Config> CallResult<E, V, ExtrinsicEvents<C>> {
826 pub fn contains_event(&self, pallet_name: &str, variant_name: &str) -> bool {
828 self.events.iter().any(|event| {
829 let event = event.unwrap();
830 event.pallet_name() == pallet_name && event.variant_name() == variant_name
831 })
832 }
833
834 #[allow(clippy::result_large_err)] pub fn contract_emitted_events(
837 &self,
838 ) -> Result<Vec<EventWithTopics<events::ContractEmitted>>, subxt::Error>
839 where
840 C::Hash: Into<sp_core::H256>,
841 {
842 let mut events_with_topics = Vec::new();
843 for event in self.events.iter() {
844 let event = event?;
845 if let Some(decoded_event) = event.as_event::<events::ContractEmitted>()? {
846 let topics = decoded_event.topics.clone();
847 let event_with_topics = EventWithTopics {
848 event: decoded_event,
849 topics,
850 };
851 events_with_topics.push(event_with_topics);
852 }
853 }
854 Ok(events_with_topics)
855 }
856}