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