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