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    deposit_limit_to_balance,
29    events::{
30        CodeStoredEvent,
31        EventWithTopics,
32    },
33    log_error,
34    log_info,
35    sr25519,
36};
37use crate::{
38    ContractsBackend,
39    E2EBackend,
40    backend::{
41        BuilderClient,
42        ChainBackend,
43    },
44    client_utils::{
45        ContractsRegistry,
46        salt,
47    },
48    contract_results::{
49        BareInstantiationResult,
50        CallDryRunResult,
51        CallResult,
52        ContractResult,
53        UploadResult,
54    },
55    error::DryRunError,
56    events,
57    events::ContractInstantiatedEvent,
58};
59use ink::H160;
60use ink_env::{
61    Environment,
62    call::{
63        Call,
64        ExecutionInput,
65        utils::{
66            DecodeMessageResult,
67            ReturnType,
68            Set,
69        },
70    },
71};
72use ink_primitives::{
73    DepositLimit,
74    abi::AbiEncodeWith,
75};
76use jsonrpsee::core::async_trait;
77use pallet_revive::evm::CallTrace;
78use scale::{
79    Decode,
80    Encode,
81};
82use sp_weights::Weight;
83use subxt::{
84    blocks::ExtrinsicEvents,
85    config::{
86        DefaultExtrinsicParams,
87        ExtrinsicParams,
88        HashFor,
89    },
90    error::DispatchError,
91    events::EventDetails,
92    ext::scale_value::{
93        Composite,
94        Value,
95        ValueDef,
96    },
97    storage::dynamic,
98    tx::Signer,
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    api: ReviveApi<C, E>,
121    contracts: ContractsRegistry,
122    url: String,
123}
124
125impl<C, E> Client<C, E>
126where
127    C: subxt::Config,
128    C::AccountId:
129        From<sr25519::PublicKey> + scale::Codec + serde::de::DeserializeOwned + Debug,
130    C::Address: From<sr25519::PublicKey>,
131    C::Signature: From<sr25519::Signature>,
132    <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
133        From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
134    E: Environment,
135    E::AccountId: Debug,
136    E::EventRecord: Debug,
137    E::Balance: Debug + scale::HasCompact + serde::Serialize,
138    H256: Debug + scale::Encode,
139{
140    /// Creates a new [`Client`] instance using a `subxt` client.
141    pub async fn new<P: Into<PathBuf>>(
142        client: subxt::backend::rpc::RpcClient,
143        contracts: impl IntoIterator<Item = P>,
144        url: String,
145    ) -> Result<Self, subxt::Error> {
146        Ok(Self {
147            api: ReviveApi::new(client).await?,
148            contracts: ContractsRegistry::new(contracts),
149            url,
150        })
151    }
152
153    /// Executes an `upload` call and captures the resulting events.
154    async fn exec_upload(
155        &mut self,
156        signer: &Keypair,
157        code: Vec<u8>,
158        storage_deposit_limit: Option<E::Balance>,
159    ) -> Result<UploadResult<E, ExtrinsicEvents<C>>, Error> {
160        let dry_run = self
161            .api
162            .upload_dry_run(signer, code.clone(), storage_deposit_limit)
163            .await;
164        log_info(&format!("upload dry run: {dry_run:?}"));
165        if let Err(err) = dry_run {
166            let dispatch_err = self.runtime_dispatch_error_to_subxt_dispatch_error(&err);
167            return Err(Error::UploadDryRun(dispatch_err))
168        }
169
170        let storage_deposit_limit = match storage_deposit_limit {
171            None => {
172                dry_run
173                    .as_ref()
174                    .expect("would have returned already")
175                    .deposit
176            }
177            Some(limit) => limit,
178        };
179
180        let (tx_events, trace) =
181            self.api.upload(signer, code, storage_deposit_limit).await;
182
183        let mut hash = None;
184        for evt in tx_events.iter() {
185            let evt = evt.unwrap_or_else(|err| {
186                panic!("unable to unwrap event: {err:?}");
187            });
188
189            if let Some(uploaded) =
190                evt.as_event::<CodeStoredEvent>().unwrap_or_else(|err| {
191                    panic!("event conversion to `Uploaded` failed: {err:?}");
192                })
193            {
194                log_info(&format!(
195                    "contract was uploaded with hash {:?}",
196                    uploaded.code_hash
197                ));
198                hash = Some(uploaded.code_hash);
199                break
200            } else if is_extrinsic_failed_event(&evt) {
201                let metadata = self.api.client.metadata();
202                let dispatch_error =
203                    DispatchError::decode_from(evt.field_bytes(), metadata)
204                        .map_err(|e| Error::Decoding(e.to_string()))?;
205
206                log_error(&format!(
207                    "extrinsic for upload failed: {dispatch_error} {trace:?}"
208                ));
209                return Err(Error::UploadExtrinsic(dispatch_error, trace))
210            }
211        }
212
213        // todo still up to date?
214        // The `pallet-revive` behavior is that if the code was already stored on the
215        // chain we won't get an event with the hash, but the extrinsic will still
216        // succeed. We then don't error (`cargo-contract` would), but instead
217        // return the hash from the dry-run.
218        let code_hash = match hash {
219            Some(hash) => hash,
220            None => {
221                dry_run
222                    .as_ref()
223                    .unwrap_or_else(|err| panic!("must have worked: {err:?}"))
224                    .code_hash
225            }
226        };
227
228        Ok(UploadResult {
229            dry_run,
230            code_hash,
231            events: tx_events,
232        })
233    }
234
235    /// todo check if comment still holds
236    /// Transforms a [`ContractResult`] from a dry run into a [`Result`] type, containing
237    /// details of the [`DispatchError`] if the dry run failed.
238    #[allow(clippy::type_complexity)]
239    fn contract_result_to_result<V>(
240        &self,
241        contract_result: ContractResult<V, E::Balance>,
242    ) -> Result<ContractResult<V, E::Balance>, DryRunError<DispatchError>> {
243        if let Err(error) = contract_result.result {
244            let subxt_dispatch_err =
245                self.runtime_dispatch_error_to_subxt_dispatch_error(&error);
246            Err(DryRunError::<DispatchError> {
247                error: subxt_dispatch_err,
248            })
249        } else {
250            Ok(contract_result)
251        }
252    }
253
254    /// Converts a `sp_runtime::DispatchError` into a `DispatchError` which contains error
255    /// details.
256    fn runtime_dispatch_error_to_subxt_dispatch_error(
257        &self,
258        dispatch_error: &sp_runtime::DispatchError,
259    ) -> DispatchError {
260        let dispatch_err_encoded = Encode::encode(&dispatch_error);
261        DispatchError::decode_from(dispatch_err_encoded, self.api.client.metadata())
262            .expect("failed to decode valid dispatch error")
263    }
264
265    /// Returns the URL of the running node.
266    pub fn url(&self) -> &str {
267        &self.url
268    }
269
270    /// Derives the Ethereum address from a keypair.
271    // copied from `pallet-revive`
272    fn derive_keypair_address(&self, signer: &Keypair) -> H160 {
273        let account_id = <Keypair as subxt::tx::Signer<C>>::account_id(signer);
274        let account_bytes = account_id.encode();
275        crate::AccountIdMapper::to_address(account_bytes.as_ref())
276    }
277
278    /// Returns the original mapped `AccountId32` for a `H160`.
279    ///
280    /// Returns `None` if no mapping is found in the `pallet-revive` runtime
281    /// storage; this is the case for e.g. contracts.
282    async fn fetch_original_account(
283        &self,
284        addr: &H160,
285    ) -> Result<Option<E::AccountId>, Error> {
286        let original_account_entry = subxt::dynamic::storage(
287            "Revive",
288            "OriginalAccount",
289            vec![Value::from_bytes(addr)],
290        );
291        let best_block = self.api.best_block().await;
292        let raw_value = self
293            .api
294            .client
295            .storage()
296            .at(best_block)
297            .fetch(&original_account_entry)
298            .await
299            .map_err(|err| {
300                Error::Other(format!("Unable to fetch original account: {err:?}"))
301            })?;
302        Ok(match raw_value {
303            Some(value) => {
304                let raw_account_id = value.as_type::<[u8; 32]>().map_err(|err| {
305                    Error::Decoding(format!("unable to deserialize AccountId: {err}"))
306                })?;
307                let account: E::AccountId = Decode::decode(&mut &raw_account_id[..])
308                    .map_err(|err| {
309                        Error::Decoding(format!("unable to decode AccountId: {err}"))
310                    })?;
311                Some(account)
312            }
313            None => None,
314        })
315    }
316
317    /// Returns the `AccountId` for a `H160`.
318    ///
319    /// Queries runtime, returns fallback account if no result.
320    pub async fn to_account_id(&self, addr: &H160) -> Result<E::AccountId, Error> {
321        match self.fetch_original_account(addr).await? {
322            Some(v) => Ok(v),
323            None => {
324                let fallback = to_fallback_account_id(addr);
325                let account_id = E::AccountId::decode(&mut &fallback[..]).unwrap();
326                Ok(account_id)
327            }
328        }
329    }
330}
331
332/// Returns the fallback accountfor an `H160`.
333fn to_fallback_account_id(address: &H160) -> [u8; 32] {
334    let mut account_id = [0xEE; 32];
335    account_id[..20].copy_from_slice(address.as_bytes());
336    account_id
337}
338
339#[async_trait]
340impl<C, E> ChainBackend for Client<C, E>
341where
342    C: subxt::Config + Send + Sync,
343    C::AccountId: Clone
344        + Debug
345        + Send
346        + Sync
347        + core::fmt::Display
348        + scale::Codec
349        + From<sr25519::PublicKey>
350        + serde::de::DeserializeOwned,
351    C::Address: From<sr25519::PublicKey>,
352    C::Signature: From<sr25519::Signature>,
353    C::Address: Send + Sync,
354    <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
355        From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
356    E: Environment,
357    E::AccountId: Debug + Send + Sync,
358    E::Balance: Clone
359        + Debug
360        + Send
361        + Sync
362        + TryFrom<u128>
363        + scale::HasCompact
364        + serde::Serialize,
365    E::EventRecord: Debug,
366{
367    type AccountId = E::AccountId;
368    type Balance = E::Balance;
369    type Error = Error;
370    type EventLog = ExtrinsicEvents<C>;
371
372    async fn create_and_fund_account(
373        &mut self,
374        origin: &Keypair,
375        amount: Self::Balance,
376    ) -> Keypair {
377        let (_, phrase, _) =
378            <sp_core::sr25519::Pair as sp_core::Pair>::generate_with_phrase(None);
379        let phrase =
380            subxt_signer::bip39::Mnemonic::parse(phrase).expect("valid phrase expected");
381        let keypair = Keypair::from_phrase(&phrase, None).expect("valid phrase expected");
382        let account_id = <Keypair as Signer<C>>::account_id(&keypair);
383        let origin_account_id = origin.public_key().to_account_id();
384
385        self.api
386            .transfer_allow_death(origin, account_id.clone(), amount)
387            .await
388            .unwrap_or_else(|err| {
389                panic!(
390                    "transfer from {origin_account_id} to {account_id} failed with {err:?}"
391                )
392            });
393
394        log_info(&format!(
395            "transfer from {origin_account_id} to {account_id} succeeded",
396        ));
397
398        keypair
399    }
400
401    async fn free_balance(
402        &mut self,
403        account: Self::AccountId,
404    ) -> Result<Self::Balance, Self::Error> {
405        let account_addr = subxt::dynamic::storage(
406            "System",
407            "Account",
408            vec![
409                // Something that encodes to an AccountId32 is what we need for the map
410                // key here:
411                Value::from_bytes(&account),
412            ],
413        );
414
415        let best_block = self.api.best_block().await;
416
417        let account = self
418            .api
419            .client
420            .storage()
421            .at(best_block)
422            .fetch_or_default(&account_addr)
423            .await
424            .unwrap_or_else(|err| {
425                panic!("unable to fetch balance: {err:?}");
426            })
427            .to_value()
428            .unwrap_or_else(|err| {
429                panic!("unable to decode account info: {err:?}");
430            });
431
432        let account_data = get_composite_field_value(&account, "data")?;
433        let balance = get_composite_field_value(account_data, "free")?;
434        let balance = balance.as_u128().ok_or_else(|| {
435            Error::Balance(format!("{balance:?} should convert to u128"))
436        })?;
437        let balance = E::Balance::try_from(balance).map_err(|_| {
438            Error::Balance(format!("{balance:?} failed to convert from u128"))
439        })?;
440
441        log_info(&format!("balance of contract {account:?} is {balance:?}"));
442        Ok(balance)
443    }
444
445    async fn runtime_call<'a>(
446        &mut self,
447        origin: &Keypair,
448        pallet_name: &'a str,
449        call_name: &'a str,
450        call_data: Vec<Value>,
451    ) -> Result<Self::EventLog, Self::Error> {
452        let (tx_events, trace) = self
453            .api
454            .runtime_call(origin, pallet_name, call_name, call_data)
455            .await;
456
457        for evt in tx_events.iter() {
458            let evt = evt.unwrap_or_else(|err| {
459                panic!("unable to unwrap event: {err:?}");
460            });
461
462            if is_extrinsic_failed_event(&evt) {
463                let metadata = self.api.client.metadata();
464                let dispatch_error =
465                    subxt::error::DispatchError::decode_from(evt.field_bytes(), metadata)
466                        .map_err(|e| Error::Decoding(e.to_string()))?;
467                log_error(&format!(
468                    "extrinsic for `runtime_call` failed: {dispatch_error} {trace:?}"
469                ));
470                return Err(Error::CallExtrinsic(dispatch_error, trace))
471            }
472        }
473
474        Ok(tx_events)
475    }
476
477    async fn transfer_allow_death(
478        &mut self,
479        origin: &Keypair,
480        dest: Self::AccountId,
481        value: Self::Balance,
482    ) -> Result<(), Self::Error> {
483        let dest = dest.encode();
484        let dest: C::AccountId = Decode::decode(&mut &dest[..]).unwrap();
485        self.api
486            .transfer_allow_death(origin, dest, value)
487            .await
488            .map_err(|err| Error::Balance(format!("{err:?}")))
489    }
490}
491
492#[async_trait]
493impl<C, E> BuilderClient<E> for Client<C, E>
494where
495    C: subxt::Config + Send + Sync,
496    C::AccountId: Clone
497        + Debug
498        + Send
499        + Sync
500        + core::fmt::Display
501        + scale::Codec
502        + From<sr25519::PublicKey>
503        + From<[u8; 32]>
504        + serde::de::DeserializeOwned,
505    C::Address: From<sr25519::PublicKey>,
506    C::Signature: From<sr25519::Signature>,
507    C::Address: Send + Sync,
508    <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
509        From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
510    E: Environment,
511    E::AccountId: Debug + Send + Sync,
512    E::EventRecord: Debug,
513    E::Balance:
514        Clone + Debug + Send + Sync + From<u128> + scale::HasCompact + serde::Serialize,
515    H256: Debug + Send + Sync + scale::Encode,
516{
517    fn load_code(&self, contract_name: &str) -> Vec<u8> {
518        self.contracts.load_code(contract_name)
519    }
520
521    async fn bare_instantiate<
522        Contract: Clone,
523        Args: Send + Sync + AbiEncodeWith<Abi> + Clone,
524        R,
525        Abi: Send + Sync + Clone,
526    >(
527        &mut self,
528        code: Vec<u8>,
529        caller: &Keypair,
530        constructor: &mut CreateBuilderPartial<E, Contract, Args, R, Abi>,
531        value: E::Balance,
532        gas_limit: Weight,
533        storage_deposit_limit: DepositLimit<E::Balance>,
534    ) -> Result<BareInstantiationResult<E, Self::EventLog>, Self::Error> {
535        let data = constructor_exec_input(constructor.clone());
536        let ret = self
537            .raw_instantiate(code, caller, data, value, gas_limit, storage_deposit_limit)
538            .await?;
539        Ok(ret)
540    }
541
542    async fn raw_instantiate(
543        &mut self,
544        code: Vec<u8>,
545        caller: &Keypair,
546        constructor: Vec<u8>,
547        value: E::Balance,
548        gas_limit: Weight,
549        storage_deposit_limit: DepositLimit<E::Balance>,
550    ) -> Result<BareInstantiationResult<E, Self::EventLog>, Self::Error> {
551        let storage_deposit_limit = deposit_limit_to_balance::<E>(storage_deposit_limit);
552        let (events, trace) = self
553            .api
554            .instantiate_with_code(
555                value,
556                gas_limit.into(),
557                storage_deposit_limit,
558                code.clone(),
559                constructor.clone(),
560                salt(),
561                caller,
562            )
563            .await;
564
565        let mut addr = None;
566        for evt in events.iter() {
567            let evt = evt.unwrap_or_else(|err| {
568                panic!("unable to unwrap event: {err:?}");
569            });
570            if let Some(instantiated) = evt
571                .as_event::<ContractInstantiatedEvent>()
572                .unwrap_or_else(|err| {
573                    panic!("event conversion to `Instantiated` failed: {err:?}");
574                })
575            {
576                log_info(&format!(
577                    "contract was instantiated at {:?}",
578                    instantiated.contract
579                ));
580                addr = Some(instantiated.contract);
581
582                // We can't `break` here, we need to assign the account id from the
583                // last `ContractInstantiatedEvent`, in case the contract instantiates
584                // multiple accounts as part of its constructor!
585            } else if is_extrinsic_failed_event(&evt) {
586                let metadata = self.api.client.metadata();
587                let dispatch_error =
588                    subxt::error::DispatchError::decode_from(evt.field_bytes(), metadata)
589                        .map_err(|e| Error::Decoding(e.to_string()))?;
590                log_error(&format!(
591                    "extrinsic for instantiate failed: {dispatch_error} {trace:?}"
592                ));
593                return Err(Error::InstantiateExtrinsic(dispatch_error, trace))
594            }
595        }
596        let addr = addr.expect("cannot extract contract address from events");
597        let mut account_id = [0xEE; 32];
598        account_id[..20].copy_from_slice(addr.as_bytes());
599
600        Ok(BareInstantiationResult {
601            // The `account_id` must exist at this point. If the instantiation fails
602            // the dry-run must already return that.
603            addr,
604            account_id: E::AccountId::decode(&mut &account_id[..]).unwrap(),
605            events,
606            trace,
607            code_hash: H256(crate::client_utils::code_hash(&code[..])),
608        })
609    }
610
611    async fn exec_instantiate(
612        &mut self,
613        signer: &Keypair,
614        contract_name: &str,
615        data: Vec<u8>,
616        value: E::Balance,
617        gas_limit: Weight,
618        storage_deposit_limit: E::Balance,
619    ) -> Result<BareInstantiationResult<E, Self::EventLog>, Self::Error> {
620        let code = self.contracts.load_code(contract_name);
621        self.raw_instantiate(
622            code,
623            signer,
624            data,
625            value,
626            gas_limit,
627            DepositLimit::Balance(storage_deposit_limit),
628        )
629        .await
630    }
631
632    /// Important: For an uncomplicated UX of the E2E testing environment we
633    /// decided to automatically map the account in `pallet-revive`, if not
634    /// yet mapped. This is a side effect, as a transaction is then issued
635    /// on-chain and the user incurs costs!
636    async fn bare_instantiate_dry_run<
637        Contract: Clone,
638        Args: Send + Sync + AbiEncodeWith<Abi> + Clone,
639        R,
640        Abi: Send + Sync + Clone,
641    >(
642        &mut self,
643        contract_name: &str,
644        caller: &Keypair,
645        constructor: &mut CreateBuilderPartial<E, Contract, Args, R, Abi>,
646        value: E::Balance,
647        storage_deposit_limit: DepositLimit<E::Balance>,
648    ) -> Result<InstantiateDryRunResult<E, Abi>, Self::Error> {
649        let code = self.contracts.load_code(contract_name);
650        let data = constructor_exec_input(constructor.clone());
651        self.raw_instantiate_dry_run(code, caller, data, value, storage_deposit_limit)
652            .await
653    }
654
655    /// Important: For an uncomplicated UX of the E2E testing environment we
656    /// decided to automatically map the account in `pallet-revive`, if not
657    /// yet mapped. This is a side effect, as a transaction is then issued
658    /// on-chain and the user incurs costs!
659    async fn raw_instantiate_dry_run<Abi: Sync + Clone>(
660        &mut self,
661        code: Vec<u8>,
662        caller: &Keypair,
663        data: Vec<u8>,
664        value: E::Balance,
665        storage_deposit_limit: DepositLimit<E::Balance>,
666    ) -> Result<InstantiateDryRunResult<E, Abi>, Self::Error> {
667        // There's a side effect here!
668        let _ = self.map_account(caller).await;
669
670        let result = self
671            .api
672            .instantiate_with_code_dry_run(
673                value,
674                storage_deposit_limit,
675                code,
676                data,
677                salt(),
678                caller,
679            )
680            .await;
681
682        log_info(&format!("instantiate dry run: {:?}", &result.result));
683        let result = self
684            .contract_result_to_result(result)
685            .map_err(Error::InstantiateDryRun)?;
686
687        /*
688        // todo
689        if let Ok(res) = result.result.clone() {
690            if res.result.did_revert() {
691                return Err(Self::Error::InstantiateDryRunReverted(DryRunRevert {
692                    error: res.result.data,
693                }));
694            }
695        }
696         */
697
698        Ok(result.into())
699    }
700
701    async fn bare_upload(
702        &mut self,
703        contract_name: &str,
704        caller: &Keypair,
705        storage_deposit_limit: Option<E::Balance>,
706    ) -> Result<UploadResult<E, Self::EventLog>, Self::Error> {
707        let code = self.contracts.load_code(contract_name);
708        let ret = self
709            .exec_upload(caller, code, storage_deposit_limit)
710            .await?;
711        log_info(&format!("contract stored with hash {:?}", ret.code_hash));
712        Ok(ret)
713    }
714
715    async fn bare_remove_code(
716        &mut self,
717        caller: &Keypair,
718        code_hash: H256,
719    ) -> Result<Self::EventLog, Self::Error> {
720        let (tx_events, trace) = self.api.remove_code(caller, code_hash).await;
721
722        for evt in tx_events.iter() {
723            let evt = evt.unwrap_or_else(|err| {
724                panic!("unable to unwrap event: {err:?}");
725            });
726
727            if is_extrinsic_failed_event(&evt) {
728                let metadata = self.api.client.metadata();
729                let dispatch_error =
730                    DispatchError::decode_from(evt.field_bytes(), metadata)
731                        .map_err(|e| Error::Decoding(e.to_string()))?;
732                log_error(&format!(
733                    "extrinsic for remove code failed: {dispatch_error} {trace:?}"
734                ));
735                return Err(Error::RemoveCodeExtrinsic(dispatch_error, trace))
736            }
737        }
738
739        Ok(tx_events)
740    }
741
742    async fn bare_call<
743        Args: Sync + AbiEncodeWith<Abi> + Clone,
744        RetType: Send + DecodeMessageResult<Abi>,
745        Abi: Sync + Clone,
746    >(
747        &mut self,
748        caller: &Keypair,
749        message: &CallBuilderFinal<E, Args, RetType, Abi>,
750        value: E::Balance,
751        gas_limit: Weight,
752        storage_deposit_limit: DepositLimit<E::Balance>,
753    ) -> Result<(Self::EventLog, Option<CallTrace>), Self::Error>
754    where
755        CallBuilderFinal<E, Args, RetType, Abi>: Clone,
756    {
757        let addr = *message.clone().params().callee();
758        let exec_input = message.clone().params().exec_input().encode();
759        log_info(&format!("call: {exec_input:02X?}"));
760        self.raw_call(
761            addr,
762            exec_input,
763            value,
764            gas_limit,
765            storage_deposit_limit,
766            caller,
767        )
768        .await
769    }
770
771    async fn raw_call(
772        &mut self,
773        dest: H160,
774        input_data: Vec<u8>,
775        value: E::Balance,
776        gas_limit: Weight,
777        storage_deposit_limit: DepositLimit<E::Balance>,
778        signer: &Keypair,
779    ) -> Result<(Self::EventLog, Option<CallTrace>), Self::Error> {
780        let (tx_events, trace) = self
781            .api
782            .call(
783                dest,
784                value,
785                gas_limit.into(),
786                deposit_limit_to_balance::<E>(storage_deposit_limit),
787                input_data,
788                signer,
789            )
790            .await;
791
792        for evt in tx_events.iter() {
793            let evt = evt.unwrap_or_else(|err| {
794                panic!("unable to unwrap event: {err:?}");
795            });
796
797            if is_extrinsic_failed_event(&evt) {
798                let metadata = self.api.client.metadata();
799                let dispatch_error =
800                    DispatchError::decode_from(evt.field_bytes(), metadata)
801                        .map_err(|e| Error::Decoding(e.to_string()))?;
802                log_error(&format!(
803                    "extrinsic for `raw_call` failed: {dispatch_error} {trace:?}"
804                ));
805                return Err(Error::CallExtrinsic(dispatch_error, trace))
806            }
807        }
808
809        Ok((tx_events, trace))
810    }
811
812    /// Dry-runs a bare call.
813    ///
814    /// Important: For an uncomplicated UX of the E2E testing environment we
815    /// decided to automatically map the account in `pallet-revive`, if not
816    /// yet mapped. This is a side effect, as a transaction is then issued
817    /// on-chain and the user incurs costs!
818    async fn bare_call_dry_run<
819        Args: Sync + AbiEncodeWith<Abi> + Clone,
820        RetType: Send + DecodeMessageResult<Abi>,
821        Abi: Sync + Clone,
822    >(
823        &mut self,
824        caller: &Keypair,
825        message: &CallBuilderFinal<E, Args, RetType, Abi>,
826        value: E::Balance,
827        storage_deposit_limit: DepositLimit<E::Balance>,
828    ) -> Result<CallDryRunResult<E, RetType, Abi>, Self::Error>
829    where
830        CallBuilderFinal<E, Args, RetType, Abi>: Clone,
831    {
832        // There's a side effect here!
833        let _ = self.map_account(caller).await;
834
835        let dest = *message.clone().params().callee();
836        let exec_input = message.clone().params().exec_input().encode();
837
838        let (exec_result, trace) = self
839            .api
840            .call_dry_run(dest, exec_input, value, storage_deposit_limit, caller)
841            .await;
842        log_info(&format!("call dry run result: {:?}", &exec_result.result));
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: DepositLimit<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}