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