1#[cfg(feature = "std")]
16use std::fmt::Debug;
17use std::path::PathBuf;
18
19use ink::H160;
20use ink_env::{
21 call::{
22 utils::{
23 DecodeMessageResult,
24 ReturnType,
25 Set,
26 },
27 Call,
28 ExecutionInput,
29 },
30 Environment,
31};
32use ink_primitives::{
33 abi::AbiEncodeWith,
34 types::AccountIdMapper,
35 DepositLimit,
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 builders::{
63 constructor_exec_input,
64 CreateBuilderPartial,
65 },
66 deposit_limit_to_balance,
67 events::{
68 CodeStoredEvent,
69 EventWithTopics,
70 },
71 log_error,
72 log_info,
73 sr25519,
74 InstantiateDryRunResult,
75 Keypair,
76 ReviveApi,
77 H256,
78};
79use crate::{
80 backend::{
81 BuilderClient,
82 ChainBackend,
83 },
84 client_utils::{
85 salt,
86 ContractsRegistry,
87 },
88 contract_results::{
89 BareInstantiationResult,
90 CallDryRunResult,
91 CallResult,
92 ContractResult,
93 UploadResult,
94 },
95 error::DryRunError,
96 events,
97 ContractsBackend,
98 E2EBackend,
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
386 E: Environment,
387 E::AccountId: Debug + Send + Sync,
388 E::Balance: Clone
389 + Debug
390 + Send
391 + Sync
392 + TryFrom<u128>
393 + scale::HasCompact
394 + serde::Serialize,
395 E::EventRecord: Debug,
396{
397 type AccountId = E::AccountId;
398 type Balance = E::Balance;
399 type Error = Error;
400 type EventLog = ExtrinsicEvents<C>;
401
402 async fn create_and_fund_account(
403 &mut self,
404 origin: &Keypair,
405 amount: Self::Balance,
406 ) -> Keypair {
407 let (_, phrase, _) =
408 <sp_core::sr25519::Pair as sp_core::Pair>::generate_with_phrase(None);
409 let phrase =
410 subxt_signer::bip39::Mnemonic::parse(phrase).expect("valid phrase expected");
411 let keypair = Keypair::from_phrase(&phrase, None).expect("valid phrase expected");
412 let account_id = <Keypair as Signer<C>>::account_id(&keypair);
413 let origin_account_id = origin.public_key().to_account_id();
414
415 self.api
416 .try_transfer_balance(origin, account_id.clone(), amount)
417 .await
418 .unwrap_or_else(|err| {
419 panic!(
420 "transfer from {origin_account_id} to {account_id} failed with {err:?}"
421 )
422 });
423
424 log_info(&format!(
425 "transfer from {origin_account_id} to {account_id} succeeded",
426 ));
427
428 keypair
429 }
430
431 async fn free_balance(
432 &mut self,
433 account: Self::AccountId,
434 ) -> Result<Self::Balance, Self::Error> {
435 let account_addr = subxt::dynamic::storage(
436 "System",
437 "Account",
438 vec![
439 Value::from_bytes(&account),
442 ],
443 );
444
445 let best_block = self.api.best_block().await;
446
447 let account = self
448 .api
449 .client
450 .storage()
451 .at(best_block)
452 .fetch_or_default(&account_addr)
453 .await
454 .unwrap_or_else(|err| {
455 panic!("unable to fetch balance: {err:?}");
456 })
457 .to_value()
458 .unwrap_or_else(|err| {
459 panic!("unable to decode account info: {err:?}");
460 });
461
462 let account_data = get_composite_field_value(&account, "data")?;
463 let balance = get_composite_field_value(account_data, "free")?;
464 let balance = balance.as_u128().ok_or_else(|| {
465 Error::Balance(format!("{balance:?} should convert to u128"))
466 })?;
467 let balance = E::Balance::try_from(balance).map_err(|_| {
468 Error::Balance(format!("{balance:?} failed to convert from u128"))
469 })?;
470
471 log_info(&format!("balance of contract {account:?} is {balance:?}"));
472 Ok(balance)
473 }
474
475 async fn runtime_call<'a>(
476 &mut self,
477 origin: &Keypair,
478 pallet_name: &'a str,
479 call_name: &'a str,
480 call_data: Vec<Value>,
481 ) -> Result<Self::EventLog, Self::Error> {
482 let tx_events = self
483 .api
484 .runtime_call(origin, pallet_name, call_name, call_data)
485 .await;
486
487 for evt in tx_events.iter() {
488 let evt = evt.unwrap_or_else(|err| {
489 panic!("unable to unwrap event: {err:?}");
490 });
491
492 if is_extrinsic_failed_event(&evt) {
493 let metadata = self.api.client.metadata();
494 let dispatch_error =
495 subxt::error::DispatchError::decode_from(evt.field_bytes(), metadata)
496 .map_err(|e| Error::Decoding(e.to_string()))?;
497
498 log_error(&format!("extrinsic for call failed: {dispatch_error}"));
499 return Err(Error::CallExtrinsic(dispatch_error))
500 }
501 }
502
503 Ok(tx_events)
504 }
505}
506
507#[async_trait]
508impl<C, E> BuilderClient<E> for Client<C, E>
509where
510 C: subxt::Config + Send + Sync,
511 C::AccountId: Clone
512 + Debug
513 + Send
514 + Sync
515 + core::fmt::Display
516 + scale::Codec
517 + From<sr25519::PublicKey>
518 + serde::de::DeserializeOwned,
519 C::Address: From<sr25519::PublicKey>,
520 C::Signature: From<sr25519::Signature>,
521 C::Address: Send + Sync,
522 <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
523 From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
524
525 E: Environment,
526 E::AccountId: Debug + Send + Sync,
527 E::EventRecord: Debug,
528 E::Balance:
529 Clone + Debug + Send + Sync + From<u128> + scale::HasCompact + serde::Serialize,
530 H256: Debug + Send + Sync + scale::Encode,
531{
532 async fn bare_instantiate<
533 Contract: Clone,
534 Args: Send + Sync + AbiEncodeWith<Abi> + Clone,
535 R,
536 Abi: Send + Sync + Clone,
537 >(
538 &mut self,
539 contract_name: &str,
540 caller: &Keypair,
541 constructor: &mut CreateBuilderPartial<E, Contract, Args, R, Abi>,
542 value: E::Balance,
543 gas_limit: Weight,
544 storage_deposit_limit: DepositLimit<E::Balance>,
545 ) -> Result<BareInstantiationResult<Self::EventLog>, Self::Error> {
546 let code = self.contracts.load_code(contract_name);
547 let data = constructor_exec_input(constructor.clone());
548 let storage_deposit_limit = deposit_limit_to_balance::<E>(storage_deposit_limit);
549 let ret = self
550 .exec_instantiate(caller, code, data, value, gas_limit, storage_deposit_limit)
551 .await?;
552 log_info(&format!("instantiated contract at {:?}", ret.addr));
553 Ok(ret)
554 }
555
556 async fn bare_instantiate_dry_run<
557 Contract: Clone,
558 Args: Send + Sync + AbiEncodeWith<Abi> + Clone,
559 R,
560 Abi: Send + Sync + Clone,
561 >(
562 &mut self,
563 contract_name: &str,
564 caller: &Keypair,
565 constructor: &mut CreateBuilderPartial<E, Contract, Args, R, Abi>,
566 value: E::Balance,
567 storage_deposit_limit: DepositLimit<E::Balance>,
568 ) -> Result<InstantiateDryRunResult<E, Abi>, Self::Error> {
569 let _ = self.map_account(caller).await;
572
573 let code = self.contracts.load_code(contract_name);
574 let data = constructor_exec_input(constructor.clone());
575
576 let result = self
577 .api
578 .instantiate_with_code_dry_run(
579 value,
580 storage_deposit_limit,
581 code,
582 data,
583 salt(),
584 caller,
585 )
586 .await;
587
588 log_info(&format!("instantiate dry run: {:?}", &result.result));
589 let result = self
590 .contract_result_to_result(result)
591 .map_err(Error::InstantiateDryRun)?;
592
593 Ok(result.into())
604 }
605
606 async fn bare_upload(
607 &mut self,
608 contract_name: &str,
609 caller: &Keypair,
610 storage_deposit_limit: E::Balance,
611 ) -> Result<UploadResult<E, Self::EventLog>, Self::Error> {
612 let code = self.contracts.load_code(contract_name);
613 let ret = self
614 .exec_upload(caller, code, storage_deposit_limit)
615 .await?;
616 log_info(&format!("contract stored with hash {:?}", ret.code_hash));
617 Ok(ret)
618 }
619
620 async fn bare_remove_code(
621 &mut self,
622 caller: &Keypair,
623 code_hash: H256,
624 ) -> Result<Self::EventLog, Self::Error> {
625 let tx_events = self.api.remove_code(caller, code_hash).await;
626
627 for evt in tx_events.iter() {
628 let evt = evt.unwrap_or_else(|err| {
629 panic!("unable to unwrap event: {err:?}");
630 });
631
632 if is_extrinsic_failed_event(&evt) {
633 let metadata = self.api.client.metadata();
634 let dispatch_error =
635 DispatchError::decode_from(evt.field_bytes(), metadata)
636 .map_err(|e| Error::Decoding(e.to_string()))?;
637 return Err(Error::RemoveCodeExtrinsic(dispatch_error))
638 }
639 }
640
641 Ok(tx_events)
642 }
643
644 async fn bare_call<
645 Args: Sync + AbiEncodeWith<Abi> + Clone,
646 RetType: Send + DecodeMessageResult<Abi>,
647 Abi: Sync + Clone,
648 >(
649 &mut self,
650 caller: &Keypair,
651 message: &CallBuilderFinal<E, Args, RetType, Abi>,
652 value: E::Balance,
653 gas_limit: Weight,
654 storage_deposit_limit: DepositLimit<E::Balance>,
655 ) -> Result<(Self::EventLog, Option<CallTrace>), Self::Error>
656 where
657 CallBuilderFinal<E, Args, RetType, Abi>: Clone,
658 {
659 let addr = *message.clone().params().callee();
660 let exec_input = message.clone().params().exec_input().encode();
661 log_info(&format!("call: {exec_input:02X?}"));
662
663 let (tx_events, trace) = self
664 .api
665 .call(
666 addr,
667 value,
668 gas_limit.into(),
669 deposit_limit_to_balance::<E>(storage_deposit_limit),
670 exec_input,
671 caller,
672 )
673 .await;
674
675 for evt in tx_events.iter() {
676 let evt = evt.unwrap_or_else(|err| {
677 panic!("unable to unwrap event: {err:?}");
678 });
679
680 if is_extrinsic_failed_event(&evt) {
681 let metadata = self.api.client.metadata();
682 let dispatch_error =
683 DispatchError::decode_from(evt.field_bytes(), metadata)
684 .map_err(|e| Error::Decoding(e.to_string()))?;
685 log_error(&format!("extrinsic for call failed: {dispatch_error}"));
686 return Err(Error::CallExtrinsic(dispatch_error))
687 }
688 }
689
690 Ok((tx_events, trace))
691 }
692
693 async fn bare_call_dry_run<
695 Args: Sync + AbiEncodeWith<Abi> + Clone,
696 RetType: Send + DecodeMessageResult<Abi>,
697 Abi: Sync + Clone,
698 >(
699 &mut self,
700 caller: &Keypair,
701 message: &CallBuilderFinal<E, Args, RetType, Abi>,
702 value: E::Balance,
703 storage_deposit_limit: DepositLimit<E::Balance>,
704 ) -> Result<CallDryRunResult<E, RetType, Abi>, Self::Error>
705 where
706 CallBuilderFinal<E, Args, RetType, Abi>: Clone,
707 {
708 let _ = self.map_account(caller).await;
711
712 let dest = *message.clone().params().callee();
713 let exec_input = message.clone().params().exec_input().encode();
714
715 let (exec_result, trace) = self
716 .api
717 .call_dry_run(
718 Signer::<C>::account_id(caller), dest,
723 exec_input,
724 value,
725 deposit_limit_to_balance::<E>(storage_deposit_limit),
726 caller,
727 )
728 .await;
729 log_info(&format!("call dry run result: {:?}", &exec_result.result));
730
731 let exec_result = self
732 .contract_result_to_result(exec_result)
733 .map_err(Error::CallDryRun)?;
734
735 Ok(CallDryRunResult {
736 exec_result,
737 trace,
738 _marker: Default::default(),
739 })
740 }
741
742 async fn map_account(&mut self, caller: &Keypair) -> Result<(), Self::Error> {
743 let addr = self.derive_keypair_address(caller);
744 if self.fetch_original_account(&addr).await?.is_some() {
745 return Ok(());
746 }
747 let tx_events = self.api.map_account(caller).await;
748
749 for evt in tx_events.iter() {
750 let evt = evt.unwrap_or_else(|err| {
751 panic!("unable to unwrap event: {err:?}");
752 });
753
754 if is_extrinsic_failed_event(&evt) {
755 let metadata = self.api.client.metadata();
756 let dispatch_error =
757 DispatchError::decode_from(evt.field_bytes(), metadata)
758 .map_err(|e| Error::Decoding(e.to_string()))?;
759 log_error(&format!("extrinsic for call failed: {dispatch_error}"));
760 return Err(Error::CallExtrinsic(dispatch_error))
761 }
762 }
763
764 Ok(())
766 }
767
768 async fn map_account_dry_run(&mut self, caller: &Keypair) -> Result<(), Self::Error> {
771 let tx_events = self.api.map_account(caller).await;
772
773 for evt in tx_events.iter() {
774 let evt = evt.unwrap_or_else(|err| {
775 panic!("unable to unwrap event: {err:?}");
776 });
777
778 if is_extrinsic_failed_event(&evt) {
779 let metadata = self.api.client.metadata();
780 let dispatch_error =
781 DispatchError::decode_from(evt.field_bytes(), metadata)
782 .map_err(|e| Error::Decoding(e.to_string()))?;
783 log_error(&format!("extrinsic for call failed: {dispatch_error}"));
784 return Err(Error::CallExtrinsic(dispatch_error))
785 }
786 }
787
788 Ok(())
789 }
790}
791
792impl<C, E> ContractsBackend<E> for Client<C, E>
793where
794 C: subxt::Config + Send + Sync,
795 C::AccountId: Clone
796 + Debug
797 + Send
798 + Sync
799 + core::fmt::Display
800 + scale::Codec
801 + From<sr25519::PublicKey>
802 + serde::de::DeserializeOwned,
803 C::Address: From<sr25519::PublicKey>,
804 C::Signature: From<sr25519::Signature>,
805 C::Address: Send + Sync,
806
807 E: Environment,
808 E::AccountId: Debug + Send + Sync,
809 E::Balance:
810 Clone + Debug + Send + Sync + From<u128> + scale::HasCompact + serde::Serialize,
811 H256: Debug + Send + scale::Encode,
812{
813 type Error = Error;
814 type EventLog = ExtrinsicEvents<C>;
815}
816
817impl<C, E> E2EBackend<E> for Client<C, E>
818where
819 C: subxt::Config + Send + Sync,
820 C::AccountId: Clone
821 + Debug
822 + Send
823 + Sync
824 + core::fmt::Display
825 + scale::Codec
826 + From<sr25519::PublicKey>
827 + serde::de::DeserializeOwned,
828 C::Address: From<sr25519::PublicKey>,
829 C::Signature: From<sr25519::Signature>,
830 C::Address: Send + Sync,
831 <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
832 From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
833
834 E: Environment,
835 E::AccountId: Debug + Send + Sync,
836 E::EventRecord: Debug,
837 E::Balance:
838 Clone + Debug + Send + Sync + From<u128> + scale::HasCompact + serde::Serialize,
839 H256: Debug + Send + Sync + scale::Encode,
840{
841}
842
843fn get_composite_field_value<'a, T>(
849 value: &'a Value<T>,
850 field_name: &str,
851) -> Result<&'a Value<T>, Error> {
852 if let ValueDef::Composite(Composite::Named(fields)) = &value.value {
853 let (_, field) = fields
854 .iter()
855 .find(|(name, _)| name == field_name)
856 .ok_or_else(|| {
857 Error::Balance(format!("No field named '{field_name}' found"))
858 })?;
859 Ok(field)
860 } else {
861 Err(Error::Balance(
862 "Expected a composite type with named fields".into(),
863 ))
864 }
865}
866
867fn is_extrinsic_failed_event<C: subxt::Config>(event: &EventDetails<C>) -> bool {
869 event.pallet_name() == "System" && event.variant_name() == "ExtrinsicFailed"
870}
871
872impl<E: Environment, V, C: subxt::Config, Abi> CallResult<E, V, ExtrinsicEvents<C>, Abi> {
873 pub fn contains_event(&self, pallet_name: &str, variant_name: &str) -> bool {
875 self.events.iter().any(|event| {
876 let event = event.unwrap();
877 eprintln!(
878 "pallet: {:?}, variant: {:?}",
879 event.pallet_name(),
880 event.variant_name()
881 );
882 event.pallet_name() == pallet_name && event.variant_name() == variant_name
883 })
884 }
885
886 #[allow(clippy::result_large_err)] pub fn contract_emitted_events(
889 &self,
890 ) -> Result<Vec<EventWithTopics<events::ContractEmitted>>, subxt::Error>
891 where
892 HashFor<C>: Into<sp_core::H256>,
893 {
894 let mut events_with_topics = Vec::new();
895 for event in self.events.iter() {
896 let event = event?;
897 if let Some(decoded_event) = event.as_event::<events::ContractEmitted>()? {
898 let topics = decoded_event.topics.clone();
899 let event_with_topics = EventWithTopics {
900 event: decoded_event,
901 topics,
902 };
903 events_with_topics.push(event_with_topics);
904 }
905 }
906 Ok(events_with_topics)
907 }
908}