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 super::{
20    H256,
21    InstantiateDryRunResult,
22    Keypair,
23    ReviveApi,
24    builders::{
25        CreateBuilderPartial,
26        constructor_exec_input,
27    },
28    events::{
29        CodeStoredEvent,
30        EventWithTopics,
31    },
32    log_error,
33    log_info,
34    sr25519,
35};
36use crate::{
37    ContractsBackend,
38    E2EBackend,
39    backend::{
40        BuilderClient,
41        ChainBackend,
42    },
43    client_utils::{
44        ContractsRegistry,
45        salt,
46    },
47    contract_results::{
48        BareInstantiationResult,
49        CallDryRunResult,
50        CallResult,
51        ContractResult,
52        UploadResult,
53    },
54    error::DryRunError,
55    events,
56    events::ContractInstantiatedEvent,
57};
58use ink::H160;
59use ink_env::{
60    Environment,
61    call::{
62        Call,
63        ExecutionInput,
64        utils::{
65            DecodeMessageResult,
66            EncodeArgsWith,
67            ReturnType,
68            Set,
69        },
70    },
71};
72use ink_revive_types::evm::CallTrace;
73use jsonrpsee::core::async_trait;
74use scale::{
75    Decode,
76    Encode,
77};
78use sp_weights::Weight;
79use subxt::{
80    blocks::ExtrinsicEvents,
81    config::{
82        DefaultExtrinsicParams,
83        ExtrinsicParams,
84        HashFor,
85    },
86    error::DispatchError,
87    events::EventDetails,
88    ext::scale_value::{
89        Composite,
90        Value,
91        ValueDef,
92    },
93    storage::dynamic,
94    tx::Signer,
95};
96
97pub type Error = crate::error::Error<DispatchError>;
98
99/// Represents an initialized contract message builder.
100pub type CallBuilderFinal<E, Args, RetType, Abi> = ink_env::call::CallBuilder<
101    E,
102    Set<Call>,
103    Set<ExecutionInput<Args, Abi>>,
104    Set<ReturnType<RetType>>,
105>;
106
107/// The `Client` takes care of communicating with the node.
108///
109/// This node's RPC interface will be used for instantiating the contract
110/// and interacting with it .
111pub struct Client<C, E>
112where
113    C: subxt::Config,
114    E: Environment,
115{
116    api: ReviveApi<C, E>,
117    contracts: ContractsRegistry,
118    url: String,
119}
120
121impl<C, E> Client<C, E>
122where
123    C: subxt::Config,
124    C::AccountId:
125        From<sr25519::PublicKey> + scale::Codec + serde::de::DeserializeOwned + Debug,
126    C::Address: From<sr25519::PublicKey>,
127    C::Signature: From<sr25519::Signature>,
128    <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
129        From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
130    E: Environment,
131    E::AccountId: Debug,
132    E::EventRecord: Debug,
133    E::Balance: Debug + scale::HasCompact + serde::Serialize,
134    H256: Debug + scale::Encode,
135{
136    /// Creates a new [`Client`] instance using a `subxt` client.
137    pub async fn new<P: Into<PathBuf>>(
138        client: subxt::backend::rpc::RpcClient,
139        contracts: impl IntoIterator<Item = P>,
140        url: String,
141    ) -> Result<Self, subxt::Error> {
142        Ok(Self {
143            api: ReviveApi::new(client).await?,
144            contracts: ContractsRegistry::new(contracts),
145            url,
146        })
147    }
148
149    /// Executes an `upload` call and captures the resulting events.
150    async fn exec_upload(
151        &mut self,
152        signer: &Keypair,
153        code: Vec<u8>,
154        storage_deposit_limit: Option<E::Balance>,
155    ) -> Result<UploadResult<E, ExtrinsicEvents<C>>, Error> {
156        let dry_run = self
157            .api
158            .upload_dry_run(signer, code.clone(), storage_deposit_limit)
159            .await;
160        log_info(&format!("upload dry run: {dry_run:?}"));
161        if let Err(err) = dry_run {
162            let dispatch_err = self.runtime_dispatch_error_to_subxt_dispatch_error(&err);
163            return Err(Error::UploadDryRun(dispatch_err))
164        }
165
166        let storage_deposit_limit = match storage_deposit_limit {
167            None => {
168                dry_run
169                    .as_ref()
170                    .expect("would have returned already")
171                    .deposit
172            }
173            Some(limit) => limit,
174        };
175
176        let (tx_events, trace) =
177            self.api.upload(signer, code, storage_deposit_limit).await;
178
179        let mut hash = None;
180        for evt in tx_events.iter() {
181            let evt = evt.unwrap_or_else(|err| {
182                panic!("unable to unwrap event: {err:?}");
183            });
184
185            if let Some(uploaded) =
186                evt.as_event::<CodeStoredEvent>().unwrap_or_else(|err| {
187                    panic!("event conversion to `Uploaded` failed: {err:?}");
188                })
189            {
190                log_info(&format!(
191                    "contract was uploaded with hash {:?}",
192                    uploaded.code_hash
193                ));
194                hash = Some(uploaded.code_hash);
195                break
196            } else if is_extrinsic_failed_event(&evt) {
197                let metadata = self.api.client.metadata();
198                let dispatch_error =
199                    DispatchError::decode_from(evt.field_bytes(), metadata)
200                        .map_err(|e| Error::Decoding(e.to_string()))?;
201
202                log_error(&format!(
203                    "extrinsic for upload failed: {dispatch_error} {trace:?}"
204                ));
205                return Err(Error::UploadExtrinsic(dispatch_error, trace))
206            }
207        }
208
209        // todo still up to date?
210        // The `pallet-revive` behavior is that if the code was already stored on the
211        // chain we won't get an event with the hash, but the extrinsic will still
212        // succeed. We then don't error (`cargo-contract` would), but instead
213        // return the hash from the dry-run.
214        let code_hash = match hash {
215            Some(hash) => hash,
216            None => {
217                dry_run
218                    .as_ref()
219                    .unwrap_or_else(|err| panic!("must have worked: {err:?}"))
220                    .code_hash
221            }
222        };
223
224        Ok(UploadResult {
225            dry_run,
226            code_hash,
227            events: tx_events,
228        })
229    }
230
231    /// todo check if comment still holds
232    /// Transforms a [`ContractResult`] from a dry run into a [`Result`] type, containing
233    /// details of the [`DispatchError`] if the dry run failed.
234    #[allow(clippy::type_complexity)]
235    fn contract_result_to_result<V>(
236        &self,
237        contract_result: ContractResult<V, E::Balance>,
238    ) -> Result<ContractResult<V, E::Balance>, DryRunError<DispatchError>> {
239        if let Err(error) = contract_result.result {
240            let subxt_dispatch_err =
241                self.runtime_dispatch_error_to_subxt_dispatch_error(&error);
242            Err(DryRunError::<DispatchError> {
243                error: subxt_dispatch_err,
244            })
245        } else {
246            Ok(contract_result)
247        }
248    }
249
250    /// Converts a `sp_runtime::DispatchError` into a `DispatchError` which contains error
251    /// details.
252    fn runtime_dispatch_error_to_subxt_dispatch_error(
253        &self,
254        dispatch_error: &sp_runtime::DispatchError,
255    ) -> DispatchError {
256        let dispatch_err_encoded = Encode::encode(&dispatch_error);
257        DispatchError::decode_from(dispatch_err_encoded, self.api.client.metadata())
258            .expect("failed to decode valid dispatch error")
259    }
260
261    /// Returns the URL of the running node.
262    pub fn url(&self) -> &str {
263        &self.url
264    }
265
266    /// Derives the Ethereum address from a keypair.
267    // copied from `pallet-revive`
268    fn derive_keypair_address(&self, signer: &Keypair) -> H160 {
269        let account_id = <Keypair as subxt::tx::Signer<C>>::account_id(signer);
270        let account_bytes = Encode::encode(&account_id);
271        crate::AccountIdMapper::to_address(account_bytes.as_ref())
272    }
273
274    /// Returns the original mapped `AccountId32` for a `H160`.
275    ///
276    /// Returns `None` if no mapping is found in the `pallet-revive` runtime
277    /// storage; this is the case for e.g. contracts.
278    async fn fetch_original_account(
279        &self,
280        addr: &H160,
281    ) -> Result<Option<E::AccountId>, Error> {
282        let original_account_entry = subxt::dynamic::storage(
283            "Revive",
284            "OriginalAccount",
285            vec![Value::from_bytes(addr)],
286        );
287        let best_block = self.api.best_block().await;
288        let raw_value = self
289            .api
290            .client
291            .storage()
292            .at(best_block)
293            .fetch(&original_account_entry)
294            .await
295            .map_err(|err| {
296                Error::Other(format!("Unable to fetch original account: {err:?}"))
297            })?;
298        Ok(match raw_value {
299            Some(value) => {
300                let raw_account_id = value.as_type::<[u8; 32]>().map_err(|err| {
301                    Error::Decoding(format!("unable to deserialize AccountId: {err}"))
302                })?;
303                let account: E::AccountId = Decode::decode(&mut &raw_account_id[..])
304                    .map_err(|err| {
305                        Error::Decoding(format!("unable to decode AccountId: {err}"))
306                    })?;
307                Some(account)
308            }
309            None => None,
310        })
311    }
312
313    /// Returns the `AccountId` for a `H160`.
314    ///
315    /// Queries runtime, returns fallback account if no result.
316    pub async fn to_account_id(&self, addr: &H160) -> Result<E::AccountId, Error> {
317        match self.fetch_original_account(addr).await? {
318            Some(v) => Ok(v),
319            None => {
320                let fallback = to_fallback_account_id(addr);
321                let account_id = E::AccountId::decode(&mut &fallback[..]).unwrap();
322                Ok(account_id)
323            }
324        }
325    }
326}
327
328/// Returns the fallback accountfor an `H160`.
329fn to_fallback_account_id(address: &H160) -> [u8; 32] {
330    let mut account_id = [0xEE; 32];
331    account_id[..20].copy_from_slice(address.as_bytes());
332    account_id
333}
334
335#[async_trait]
336impl<C, E> ChainBackend for Client<C, E>
337where
338    C: subxt::Config + Send + Sync,
339    C::AccountId: Clone
340        + Debug
341        + Send
342        + Sync
343        + core::fmt::Display
344        + scale::Codec
345        + From<sr25519::PublicKey>
346        + serde::de::DeserializeOwned,
347    C::Address: From<sr25519::PublicKey>,
348    C::Signature: From<sr25519::Signature>,
349    C::Address: Send + Sync,
350    <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
351        From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
352    E: Environment,
353    E::AccountId: Debug + Send + Sync,
354    E::Balance: Clone
355        + Debug
356        + Send
357        + Sync
358        + TryFrom<u128>
359        + scale::HasCompact
360        + serde::Serialize,
361    E::EventRecord: Debug,
362{
363    type AccountId = E::AccountId;
364    type Balance = E::Balance;
365    type Error = Error;
366    type EventLog = ExtrinsicEvents<C>;
367
368    async fn create_and_fund_account(
369        &mut self,
370        origin: &Keypair,
371        amount: Self::Balance,
372    ) -> Keypair {
373        let (_, phrase, _) =
374            <sp_core::sr25519::Pair as sp_core::Pair>::generate_with_phrase(None);
375        let phrase =
376            subxt_signer::bip39::Mnemonic::parse(phrase).expect("valid phrase expected");
377        let keypair = Keypair::from_phrase(&phrase, None).expect("valid phrase expected");
378        let account_id = <Keypair as Signer<C>>::account_id(&keypair);
379        let origin_account_id = origin.public_key().to_account_id();
380
381        self.api
382            .transfer_allow_death(origin, account_id.clone(), amount)
383            .await
384            .unwrap_or_else(|err| {
385                panic!(
386                    "transfer from {origin_account_id} to {account_id} failed with {err:?}"
387                )
388            });
389
390        log_info(&format!(
391            "transfer from {origin_account_id} to {account_id} succeeded",
392        ));
393
394        keypair
395    }
396
397    async fn free_balance(
398        &mut self,
399        account: Self::AccountId,
400    ) -> Result<Self::Balance, Self::Error> {
401        let account_addr = subxt::dynamic::storage(
402            "System",
403            "Account",
404            vec![
405                // Something that encodes to an AccountId32 is what we need for the map
406                // key here:
407                Value::from_bytes(&account),
408            ],
409        );
410
411        let best_block = self.api.best_block().await;
412
413        let account = self
414            .api
415            .client
416            .storage()
417            .at(best_block)
418            .fetch_or_default(&account_addr)
419            .await
420            .unwrap_or_else(|err| {
421                panic!("unable to fetch balance: {err:?}");
422            })
423            .to_value()
424            .unwrap_or_else(|err| {
425                panic!("unable to decode account info: {err:?}");
426            });
427
428        let account_data = get_composite_field_value(&account, "data")?;
429        let balance = get_composite_field_value(account_data, "free")?;
430        let balance = balance.as_u128().ok_or_else(|| {
431            Error::Balance(format!("{balance:?} should convert to u128"))
432        })?;
433        let balance = E::Balance::try_from(balance).map_err(|_| {
434            Error::Balance(format!("{balance:?} failed to convert from u128"))
435        })?;
436
437        log_info(&format!("balance of contract {account:?} is {balance:?}"));
438        Ok(balance)
439    }
440
441    async fn runtime_call<'a>(
442        &mut self,
443        origin: &Keypair,
444        pallet_name: &'a str,
445        call_name: &'a str,
446        call_data: Vec<Value>,
447    ) -> Result<Self::EventLog, Self::Error> {
448        let (tx_events, trace) = self
449            .api
450            .runtime_call(origin, pallet_name, call_name, call_data)
451            .await;
452
453        for evt in tx_events.iter() {
454            let evt = evt.unwrap_or_else(|err| {
455                panic!("unable to unwrap event: {err:?}");
456            });
457
458            if is_extrinsic_failed_event(&evt) {
459                let metadata = self.api.client.metadata();
460                let dispatch_error =
461                    subxt::error::DispatchError::decode_from(evt.field_bytes(), metadata)
462                        .map_err(|e| Error::Decoding(e.to_string()))?;
463                log_error(&format!(
464                    "extrinsic for `runtime_call` failed: {dispatch_error} {trace:?}"
465                ));
466                return Err(Error::CallExtrinsic(dispatch_error, trace))
467            }
468        }
469
470        Ok(tx_events)
471    }
472
473    async fn transfer_allow_death(
474        &mut self,
475        origin: &Keypair,
476        dest: Self::AccountId,
477        value: Self::Balance,
478    ) -> Result<(), Self::Error> {
479        let dest = Encode::encode(&dest);
480        let dest: C::AccountId = Decode::decode(&mut &dest[..]).unwrap();
481        self.api
482            .transfer_allow_death(origin, dest, value)
483            .await
484            .map_err(|err| Error::Balance(format!("{err:?}")))
485    }
486}
487
488#[async_trait]
489impl<C, E> BuilderClient<E> for Client<C, E>
490where
491    C: subxt::Config + Send + Sync,
492    C::AccountId: Clone
493        + Debug
494        + Send
495        + Sync
496        + core::fmt::Display
497        + scale::Codec
498        + From<sr25519::PublicKey>
499        + From<[u8; 32]>
500        + serde::de::DeserializeOwned,
501    C::Address: From<sr25519::PublicKey>,
502    C::Signature: From<sr25519::Signature>,
503    C::Address: Send + Sync,
504    <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
505        From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
506    E: Environment,
507    E::AccountId: Debug + Send + Sync,
508    E::EventRecord: Debug,
509    E::Balance:
510        Clone + Debug + Send + Sync + From<u128> + scale::HasCompact + serde::Serialize,
511    H256: Debug + Send + Sync + scale::Encode,
512{
513    fn load_code(&self, contract_name: &str) -> Vec<u8> {
514        self.contracts.load_code(contract_name)
515    }
516
517    async fn bare_instantiate<
518        Contract: Clone,
519        Args: Send + Sync + EncodeArgsWith<Abi> + Clone,
520        R,
521        Abi: Send + Sync + Clone,
522    >(
523        &mut self,
524        code: Vec<u8>,
525        caller: &Keypair,
526        constructor: &mut CreateBuilderPartial<E, Contract, Args, R, Abi>,
527        value: E::Balance,
528        gas_limit: Weight,
529        storage_deposit_limit: E::Balance,
530    ) -> Result<BareInstantiationResult<E, Self::EventLog>, Self::Error> {
531        let data = constructor_exec_input(constructor.clone());
532        let ret = self
533            .raw_instantiate(code, caller, data, value, gas_limit, storage_deposit_limit)
534            .await?;
535        Ok(ret)
536    }
537
538    async fn raw_instantiate(
539        &mut self,
540        code: Vec<u8>,
541        caller: &Keypair,
542        constructor: Vec<u8>,
543        value: E::Balance,
544        gas_limit: Weight,
545        storage_deposit_limit: E::Balance,
546    ) -> Result<BareInstantiationResult<E, Self::EventLog>, Self::Error> {
547        let (events, trace) = self
548            .api
549            .instantiate_with_code(
550                value,
551                gas_limit.into(),
552                storage_deposit_limit,
553                code.clone(),
554                constructor.clone(),
555                salt(),
556                caller,
557            )
558            .await;
559
560        let mut addr = None;
561        for evt in events.iter() {
562            let evt = evt.unwrap_or_else(|err| {
563                panic!("unable to unwrap event: {err:?}");
564            });
565            if let Some(instantiated) = evt
566                .as_event::<ContractInstantiatedEvent>()
567                .unwrap_or_else(|err| {
568                    panic!("event conversion to `Instantiated` failed: {err:?}");
569                })
570            {
571                log_info(&format!(
572                    "contract was instantiated at {:?}",
573                    instantiated.contract
574                ));
575                addr = Some(instantiated.contract);
576
577                // We can't `break` here, we need to assign the account id from the
578                // last `ContractInstantiatedEvent`, in case the contract instantiates
579                // multiple accounts as part of its constructor!
580            } else if is_extrinsic_failed_event(&evt) {
581                let metadata = self.api.client.metadata();
582                let dispatch_error =
583                    subxt::error::DispatchError::decode_from(evt.field_bytes(), metadata)
584                        .map_err(|e| Error::Decoding(e.to_string()))?;
585                log_error(&format!(
586                    "extrinsic for instantiate failed: {dispatch_error} {trace:?}"
587                ));
588                return Err(Error::InstantiateExtrinsic(dispatch_error, trace))
589            }
590        }
591        let addr = addr.expect("cannot extract contract address from events");
592        let mut account_id = [0xEE; 32];
593        account_id[..20].copy_from_slice(addr.as_bytes());
594
595        Ok(BareInstantiationResult {
596            // The `account_id` must exist at this point. If the instantiation fails
597            // the dry-run must already return that.
598            addr,
599            account_id: E::AccountId::decode(&mut &account_id[..]).unwrap(),
600            events,
601            trace,
602            code_hash: H256(crate::client_utils::code_hash(&code[..])),
603        })
604    }
605
606    async fn exec_instantiate(
607        &mut self,
608        signer: &Keypair,
609        contract_name: &str,
610        data: Vec<u8>,
611        value: E::Balance,
612        gas_limit: Weight,
613        storage_deposit_limit: E::Balance,
614    ) -> Result<BareInstantiationResult<E, Self::EventLog>, Self::Error> {
615        let code = self.contracts.load_code(contract_name);
616        self.raw_instantiate(code, signer, data, value, gas_limit, storage_deposit_limit)
617            .await
618    }
619
620    /// Important: For an uncomplicated UX of the E2E testing environment we
621    /// decided to automatically map the account in `pallet-revive`, if not
622    /// yet mapped. This is a side effect, as a transaction is then issued
623    /// on-chain and the user incurs costs!
624    async fn bare_instantiate_dry_run<
625        Contract: Clone,
626        Args: Send + Sync + EncodeArgsWith<Abi> + Clone,
627        R,
628        Abi: Send + Sync + Clone,
629    >(
630        &mut self,
631        contract_name: &str,
632        caller: &Keypair,
633        constructor: &mut CreateBuilderPartial<E, Contract, Args, R, Abi>,
634        value: E::Balance,
635        storage_deposit_limit: Option<E::Balance>,
636    ) -> Result<InstantiateDryRunResult<E, Abi>, Self::Error> {
637        let code = self.contracts.load_code(contract_name);
638        let data = constructor_exec_input(constructor.clone());
639        self.raw_instantiate_dry_run(code, caller, data, value, storage_deposit_limit)
640            .await
641    }
642
643    /// Important: For an uncomplicated UX of the E2E testing environment we
644    /// decided to automatically map the account in `pallet-revive`, if not
645    /// yet mapped. This is a side effect, as a transaction is then issued
646    /// on-chain and the user incurs costs!
647    async fn raw_instantiate_dry_run<Abi: Sync + Clone>(
648        &mut self,
649        code: Vec<u8>,
650        caller: &Keypair,
651        data: Vec<u8>,
652        value: E::Balance,
653        storage_deposit_limit: Option<E::Balance>,
654    ) -> Result<InstantiateDryRunResult<E, Abi>, Self::Error> {
655        // There's a side effect here!
656        let _ = self.map_account(caller).await;
657
658        let result = self
659            .api
660            .instantiate_with_code_dry_run(
661                value,
662                storage_deposit_limit,
663                code,
664                data,
665                salt(),
666                caller,
667            )
668            .await;
669
670        log_info(&format!("instantiate dry run: {:?}", &result.result));
671        let result = self
672            .contract_result_to_result(result)
673            .map_err(Error::InstantiateDryRun)?;
674
675        /*
676        // todo
677        if let Ok(res) = result.result.clone() {
678            if res.result.did_revert() {
679                return Err(Self::Error::InstantiateDryRunReverted(DryRunRevert {
680                    error: res.result.data,
681                }));
682            }
683        }
684         */
685
686        Ok(result.into())
687    }
688
689    async fn bare_upload(
690        &mut self,
691        contract_name: &str,
692        caller: &Keypair,
693        storage_deposit_limit: Option<E::Balance>,
694    ) -> Result<UploadResult<E, Self::EventLog>, Self::Error> {
695        let code = self.contracts.load_code(contract_name);
696        let ret = self
697            .exec_upload(caller, code, storage_deposit_limit)
698            .await?;
699        log_info(&format!("contract stored with hash {:?}", ret.code_hash));
700        Ok(ret)
701    }
702
703    async fn bare_remove_code(
704        &mut self,
705        caller: &Keypair,
706        code_hash: H256,
707    ) -> Result<Self::EventLog, Self::Error> {
708        let (tx_events, trace) = self.api.remove_code(caller, code_hash).await;
709
710        for evt in tx_events.iter() {
711            let evt = evt.unwrap_or_else(|err| {
712                panic!("unable to unwrap event: {err:?}");
713            });
714
715            if is_extrinsic_failed_event(&evt) {
716                let metadata = self.api.client.metadata();
717                let dispatch_error =
718                    DispatchError::decode_from(evt.field_bytes(), metadata)
719                        .map_err(|e| Error::Decoding(e.to_string()))?;
720                log_error(&format!(
721                    "extrinsic for remove code failed: {dispatch_error} {trace:?}"
722                ));
723                return Err(Error::RemoveCodeExtrinsic(dispatch_error, trace))
724            }
725        }
726
727        Ok(tx_events)
728    }
729
730    async fn bare_call<
731        Args: Sync + EncodeArgsWith<Abi> + Clone,
732        RetType: Send + DecodeMessageResult<Abi>,
733        Abi: Sync + Clone,
734    >(
735        &mut self,
736        caller: &Keypair,
737        message: &CallBuilderFinal<E, Args, RetType, Abi>,
738        value: E::Balance,
739        gas_limit: Weight,
740        storage_deposit_limit: E::Balance,
741    ) -> Result<(Self::EventLog, Option<CallTrace>), Self::Error>
742    where
743        CallBuilderFinal<E, Args, RetType, Abi>: Clone,
744    {
745        let addr = *message.clone().params().callee();
746        let exec_input = message.clone().params().exec_input().encode();
747        log_info(&format!("call: {exec_input:02X?}"));
748        self.raw_call(
749            addr,
750            exec_input,
751            value,
752            gas_limit,
753            storage_deposit_limit,
754            caller,
755        )
756        .await
757    }
758
759    async fn raw_call(
760        &mut self,
761        dest: H160,
762        input_data: Vec<u8>,
763        value: E::Balance,
764        gas_limit: Weight,
765        storage_deposit_limit: E::Balance,
766        signer: &Keypair,
767    ) -> Result<(Self::EventLog, Option<CallTrace>), Self::Error> {
768        let (tx_events, trace) = self
769            .api
770            .call(
771                dest,
772                value,
773                gas_limit.into(),
774                storage_deposit_limit,
775                input_data,
776                signer,
777            )
778            .await;
779
780        for evt in tx_events.iter() {
781            let evt = evt.unwrap_or_else(|err| {
782                panic!("unable to unwrap event: {err:?}");
783            });
784
785            if is_extrinsic_failed_event(&evt) {
786                let metadata = self.api.client.metadata();
787                let dispatch_error =
788                    DispatchError::decode_from(evt.field_bytes(), metadata)
789                        .map_err(|e| Error::Decoding(e.to_string()))?;
790                log_error(&format!(
791                    "Attempt to stringify returned data: {:?}",
792                    String::from_utf8_lossy(&trace.clone().unwrap().output.0[..])
793                ));
794                log_error(&format!(
795                    "extrinsic for `raw_call` failed: {dispatch_error} {trace:?}"
796                ));
797                return Err(Error::CallExtrinsic(dispatch_error, trace))
798            }
799        }
800
801        Ok((tx_events, trace))
802    }
803
804    /// Dry-runs a bare call.
805    ///
806    /// Important: For an uncomplicated UX of the E2E testing environment we
807    /// decided to automatically map the account in `pallet-revive`, if not
808    /// yet mapped. This is a side effect, as a transaction is then issued
809    /// on-chain and the user incurs costs!
810    async fn bare_call_dry_run<
811        Args: Sync + EncodeArgsWith<Abi> + Clone,
812        RetType: Send + DecodeMessageResult<Abi>,
813        Abi: Sync + Clone,
814    >(
815        &mut self,
816        caller: &Keypair,
817        message: &CallBuilderFinal<E, Args, RetType, Abi>,
818        value: E::Balance,
819        storage_deposit_limit: Option<E::Balance>,
820    ) -> Result<CallDryRunResult<E, RetType, Abi>, Self::Error>
821    where
822        CallBuilderFinal<E, Args, RetType, Abi>: Clone,
823    {
824        // There's a side effect here!
825        let _ = self.map_account(caller).await;
826
827        let dest = *message.clone().params().callee();
828        let exec_input = message.clone().params().exec_input().encode();
829
830        let (exec_result, trace) = self
831            .api
832            .call_dry_run(dest, exec_input, value, storage_deposit_limit, caller)
833            .await;
834        log_info(&format!("call dry run result: {:?}", &exec_result.result));
835
836        if exec_result.result.is_ok() && exec_result.clone().result.unwrap().did_revert()
837        {
838            log_error(&format!(
839                "Attempt to stringify returned data: {:?}",
840                String::from_utf8_lossy(&exec_result.clone().result.unwrap().data[..])
841            ));
842        }
843
844        let exec_result = self
845            .contract_result_to_result(exec_result)
846            .map_err(Error::CallDryRun)?;
847
848        Ok(CallDryRunResult {
849            exec_result,
850            trace,
851            _marker: Default::default(),
852        })
853    }
854
855    async fn raw_call_dry_run<
856        RetType: Send + DecodeMessageResult<Abi>,
857        Abi: Sync + Clone,
858    >(
859        &mut self,
860        dest: H160,
861        input_data: Vec<u8>,
862        value: E::Balance,
863        storage_deposit_limit: Option<E::Balance>,
864        signer: &Keypair,
865    ) -> Result<CallDryRunResult<E, RetType, Abi>, Self::Error> {
866        let (exec_result, trace) = self
867            .api
868            .call_dry_run(dest, input_data, value, storage_deposit_limit, signer)
869            .await;
870        Ok(CallDryRunResult {
871            exec_result,
872            trace,
873            _marker: Default::default(),
874        })
875    }
876
877    /// Checks if `caller` was already mapped in `pallet-revive`. If not, it will do so
878    /// and return the events associated with that transaction.
879    async fn map_account(
880        &mut self,
881        caller: &Keypair,
882    ) -> Result<Option<Self::EventLog>, Self::Error> {
883        let addr = self.derive_keypair_address(caller);
884        if self.fetch_original_account(&addr).await?.is_some() {
885            return Ok(None);
886        }
887        let (tx_events, trace) = self.api.map_account(caller).await;
888
889        for evt in tx_events.iter() {
890            let evt = evt.unwrap_or_else(|err| {
891                panic!("unable to unwrap event: {err:?}");
892            });
893
894            if is_extrinsic_failed_event(&evt) {
895                let metadata = self.api.client.metadata();
896                let dispatch_error =
897                    DispatchError::decode_from(evt.field_bytes(), metadata)
898                        .map_err(|e| Error::Decoding(e.to_string()))?;
899                log_error(&format!(
900                    "extrinsic for `map_account` failed: {dispatch_error} {trace:?}"
901                ));
902                return Err(Error::CallExtrinsic(dispatch_error, trace))
903            }
904        }
905
906        Ok(Some(tx_events))
907    }
908
909    async fn to_account_id(&mut self, addr: &H160) -> Result<E::AccountId, Self::Error> {
910        let contract_info_address =
911            dynamic("Revive", "OriginalAccount", vec![Value::from_bytes(addr)]);
912        let raw_value = self
913            .api
914            .client
915            .storage()
916            .at_latest()
917            .await
918            .map_err(|err| Error::Other(format!("failed to fetch latest: {err:?}")))?
919            .fetch(&contract_info_address)
920            .await
921            .map_err(|err| {
922                Error::Other(format!("failed to fetch account info: {err:?}"))
923            })?;
924        match raw_value {
925            None => {
926                // This typically happens when calling this function with a contract, for
927                // which there is no `AccountId`.
928                let fallback = to_fallback_account_id(addr);
929                tracing::debug!(
930                    "No address suffix was found in the node for H160 address {:?}, using fallback {:?}",
931                    addr,
932                    fallback
933                );
934                let account_id = E::AccountId::decode(&mut &fallback[..]).unwrap();
935                Ok(account_id)
936            }
937            Some(raw_value) => {
938                let raw_account_id = raw_value.as_type::<[u8; 32]>().expect("oh");
939                let account: E::AccountId = Decode::decode(&mut &raw_account_id[..])
940                    .map_err(|err| {
941                        panic!("AccountId from `[u8; 32]` deserialization error: {}", err)
942                    })?;
943                Ok(account)
944            }
945        }
946    }
947}
948
949impl<C, E> ContractsBackend<E> for Client<C, E>
950where
951    C: subxt::Config + Send + Sync,
952    C::AccountId: Clone
953        + Debug
954        + Send
955        + Sync
956        + core::fmt::Display
957        + scale::Codec
958        + From<sr25519::PublicKey>
959        + serde::de::DeserializeOwned,
960    C::Address: From<sr25519::PublicKey>,
961    C::Signature: From<sr25519::Signature>,
962    C::Address: Send + Sync,
963    E: Environment,
964    E::AccountId: Debug + Send + Sync,
965    E::Balance:
966        Clone + Debug + Send + Sync + From<u128> + scale::HasCompact + serde::Serialize,
967    H256: Debug + Send + scale::Encode,
968{
969    type Error = Error;
970    type EventLog = ExtrinsicEvents<C>;
971}
972
973impl<C, E> E2EBackend<E> for Client<C, E>
974where
975    C: subxt::Config + Send + Sync,
976    C::AccountId: Clone
977        + Debug
978        + Send
979        + Sync
980        + core::fmt::Display
981        + scale::Codec
982        + From<sr25519::PublicKey>
983        + From<[u8; 32]>
984        + serde::de::DeserializeOwned,
985    C::Address: From<sr25519::PublicKey>,
986    C::Signature: From<sr25519::Signature>,
987    C::Address: Send + Sync,
988    <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
989        From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
990    E: Environment,
991    E::AccountId: Debug + Send + Sync,
992    E::EventRecord: Debug,
993    E::Balance:
994        Clone + Debug + Send + Sync + From<u128> + scale::HasCompact + serde::Serialize,
995    H256: Debug + Send + Sync + scale::Encode,
996{
997}
998
999/// Try to extract the given field from a dynamic [`Value`].
1000///
1001/// Returns `Err` if:
1002///   - The value is not a [`Value::Composite`] with [`Composite::Named`] fields
1003///   - The value does not contain a field with the given name.
1004fn get_composite_field_value<'a, T>(
1005    value: &'a Value<T>,
1006    field_name: &str,
1007) -> Result<&'a Value<T>, Error> {
1008    if let ValueDef::Composite(Composite::Named(fields)) = &value.value {
1009        let (_, field) = fields
1010            .iter()
1011            .find(|(name, _)| name == field_name)
1012            .ok_or_else(|| {
1013                Error::Balance(format!("No field named '{field_name}' found"))
1014            })?;
1015        Ok(field)
1016    } else {
1017        Err(Error::Balance(
1018            "Expected a composite type with named fields".into(),
1019        ))
1020    }
1021}
1022
1023/// Returns true if the give event is System::Extrinsic failed.
1024fn is_extrinsic_failed_event<C: subxt::Config>(event: &EventDetails<C>) -> bool {
1025    event.pallet_name() == "System" && event.variant_name() == "ExtrinsicFailed"
1026}
1027
1028impl<E: Environment, V, C: subxt::Config, Abi> CallResult<E, V, ExtrinsicEvents<C>, Abi> {
1029    /// Returns true if the specified event was triggered by the call.
1030    pub fn contains_event(&self, pallet_name: &str, variant_name: &str) -> bool {
1031        self.events.iter().any(|event| {
1032            let event = event.expect("unable to extract event");
1033            tracing::debug!(
1034                "found event with pallet: {:?}, variant: {:?}",
1035                event.pallet_name(),
1036                event.variant_name()
1037            );
1038            event.pallet_name() == pallet_name && event.variant_name() == variant_name
1039        })
1040    }
1041
1042    /// Returns all the `ContractEmitted` events emitted by the contract.
1043    #[allow(clippy::result_large_err)] // todo
1044    pub fn contract_emitted_events(
1045        &self,
1046    ) -> Result<Vec<EventWithTopics<events::ContractEmitted>>, subxt::Error>
1047    where
1048        HashFor<C>: Into<sp_core::H256>,
1049    {
1050        let mut events_with_topics = Vec::new();
1051        for event in self.events.iter() {
1052            let event = event?;
1053            if let Some(decoded_event) = event.as_event::<events::ContractEmitted>()? {
1054                let topics = decoded_event.topics.clone();
1055                let event_with_topics = EventWithTopics {
1056                    event: decoded_event,
1057                    topics,
1058                };
1059                events_with_topics.push(event_with_topics);
1060            }
1061        }
1062        Ok(events_with_topics)
1063    }
1064}