ink_e2e/
subxt_client.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#[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
103/// Represents an initialized contract message builder.
104pub 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
111/// The `Client` takes care of communicating with the node.
112///
113/// This node's RPC interface will be used for instantiating the contract
114/// and interacting with it .
115pub struct Client<C, E>
116where
117    C: subxt::Config,
118    E: Environment,
119{
120    // TODO (@peterwht): make private once call builder supports RLP
121    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    /// Creates a new [`Client`] instance using a `subxt` client.
142    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    // TODO (@peterwht): private after call builder supports RLP
155    /// Executes an `instantiate_with_code` call and captures the resulting events.
156    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        // todo remove assert once salt() returns no more option
167        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            // The `account_id` must exist at this point. If the instantiation fails
207            // the dry-run must already return that.
208            addr,
209            events,
210            trace,
211            code_hash: H256(crate::client_utils::code_hash(&code[..])),
212        })
213    }
214
215    /// Executes an `upload` call and captures the resulting events.
216    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        // todo
223        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        // todo still up to date?
267        // The `pallet-revive` behavior is that if the code was already stored on the
268        // chain we won't get an event with the hash, but the extrinsic will still
269        // succeed. We then don't error (`cargo-contract` would), but instead
270        // return the hash from the dry-run.
271        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    /// todo check if comment still holds
289    /// Transforms a [`ContractResult`] from a dry run into a [`Result`] type, containing
290    /// details of the [`DispatchError`] if the dry run failed.
291    #[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    /// Converts a `sp_runtime::DispatchError` into a `DispatchError` which contains error
308    /// details.
309    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    /// Returns the URL of the running node.
319    pub fn url(&self) -> &str {
320        &self.url
321    }
322
323    /// Derives the Ethereum address from a keypair.
324    // copied from `pallet-revive`
325    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    /// Returns the original mapped `AccountId32` for a `H160`.
332    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                // Something that encodes to an AccountId32 is what we need for the map
439                // key here:
440                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        // todo beware side effect! this is wrong, we have to batch up the `map_account`
568        // into the RPC dry run instead
569        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        /*
592        if let Ok(res) = result.result.clone() {
593            if res.result.did_revert() {
594                return Err(Self::Error::InstantiateDryRunReverted(DryRunRevert {
595                    error: res.result.data,
596                }));
597            }
598        }
599         */
600
601        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    // todo is not really a `bare_call`
692    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        // todo beware side effect! this is wrong, we have to batch up the `map_account`
707        // into the RPC dry run instead
708        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), /* todo this param is not necessary,
717                                                  * because the last argument is the
718                                                  * caller and this value can be
719                                                  * created in the function */
720                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        // todo: Ok(tx_events)
763        Ok(())
764    }
765
766    // todo not used anywhere
767    // code is also not dry
768    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
839/// Try to extract the given field from a dynamic [`Value`].
840///
841/// Returns `Err` if:
842///   - The value is not a [`Value::Composite`] with [`Composite::Named`] fields
843///   - The value does not contain a field with the given name.
844fn 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
863/// Returns true if the give event is System::Extrinsic failed.
864fn 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    /// Returns true if the specified event was triggered by the call.
870    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    /// Returns all the `ContractEmitted` events emitted by the contract.
883    #[allow(clippy::result_large_err)] // todo
884    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}