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 ink_revive_types::evm::CallTrace;
77use jsonrpsee::core::async_trait;
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                    "Attempt to stringify returned data: {:?}",
804                    String::from_utf8_lossy(&trace.clone().unwrap().output[..])
805                ));
806                log_error(&format!(
807                    "extrinsic for `raw_call` failed: {dispatch_error} {trace:?}"
808                ));
809                return Err(Error::CallExtrinsic(dispatch_error, trace))
810            }
811        }
812
813        Ok((tx_events, trace))
814    }
815
816    /// Dry-runs a bare call.
817    ///
818    /// Important: For an uncomplicated UX of the E2E testing environment we
819    /// decided to automatically map the account in `pallet-revive`, if not
820    /// yet mapped. This is a side effect, as a transaction is then issued
821    /// on-chain and the user incurs costs!
822    async fn bare_call_dry_run<
823        Args: Sync + AbiEncodeWith<Abi> + Clone,
824        RetType: Send + DecodeMessageResult<Abi>,
825        Abi: Sync + Clone,
826    >(
827        &mut self,
828        caller: &Keypair,
829        message: &CallBuilderFinal<E, Args, RetType, Abi>,
830        value: E::Balance,
831        storage_deposit_limit: DepositLimit<E::Balance>,
832    ) -> Result<CallDryRunResult<E, RetType, Abi>, Self::Error>
833    where
834        CallBuilderFinal<E, Args, RetType, Abi>: Clone,
835    {
836        // There's a side effect here!
837        let _ = self.map_account(caller).await;
838
839        let dest = *message.clone().params().callee();
840        let exec_input = message.clone().params().exec_input().encode();
841
842        let (exec_result, trace) = self
843            .api
844            .call_dry_run(dest, exec_input, value, storage_deposit_limit, caller)
845            .await;
846        log_info(&format!("call dry run result: {:?}", &exec_result.result));
847
848        if exec_result.result.is_ok() && exec_result.clone().result.unwrap().did_revert()
849        {
850            log_error(&format!(
851                "Attempt to stringify returned data: {:?}",
852                String::from_utf8_lossy(&exec_result.clone().result.unwrap().data[..])
853            ));
854        }
855
856        let exec_result = self
857            .contract_result_to_result(exec_result)
858            .map_err(Error::CallDryRun)?;
859
860        Ok(CallDryRunResult {
861            exec_result,
862            trace,
863            _marker: Default::default(),
864        })
865    }
866
867    async fn raw_call_dry_run<
868        RetType: Send + DecodeMessageResult<Abi>,
869        Abi: Sync + Clone,
870    >(
871        &mut self,
872        dest: H160,
873        input_data: Vec<u8>,
874        value: E::Balance,
875        storage_deposit_limit: DepositLimit<E::Balance>,
876        signer: &Keypair,
877    ) -> Result<CallDryRunResult<E, RetType, Abi>, Self::Error> {
878        let (exec_result, trace) = self
879            .api
880            .call_dry_run(dest, input_data, value, storage_deposit_limit, signer)
881            .await;
882        Ok(CallDryRunResult {
883            exec_result,
884            trace,
885            _marker: Default::default(),
886        })
887    }
888
889    /// Checks if `caller` was already mapped in `pallet-revive`. If not, it will do so
890    /// and return the events associated with that transaction.
891    async fn map_account(
892        &mut self,
893        caller: &Keypair,
894    ) -> Result<Option<Self::EventLog>, Self::Error> {
895        let addr = self.derive_keypair_address(caller);
896        if self.fetch_original_account(&addr).await?.is_some() {
897            return Ok(None);
898        }
899        let (tx_events, trace) = self.api.map_account(caller).await;
900
901        for evt in tx_events.iter() {
902            let evt = evt.unwrap_or_else(|err| {
903                panic!("unable to unwrap event: {err:?}");
904            });
905
906            if is_extrinsic_failed_event(&evt) {
907                let metadata = self.api.client.metadata();
908                let dispatch_error =
909                    DispatchError::decode_from(evt.field_bytes(), metadata)
910                        .map_err(|e| Error::Decoding(e.to_string()))?;
911                log_error(&format!(
912                    "extrinsic for `map_account` failed: {dispatch_error} {trace:?}"
913                ));
914                return Err(Error::CallExtrinsic(dispatch_error, trace))
915            }
916        }
917
918        Ok(Some(tx_events))
919    }
920
921    async fn to_account_id(&mut self, addr: &H160) -> Result<E::AccountId, Self::Error> {
922        let contract_info_address =
923            dynamic("Revive", "OriginalAccount", vec![Value::from_bytes(addr)]);
924        let raw_value = self
925            .api
926            .client
927            .storage()
928            .at_latest()
929            .await
930            .map_err(|err| Error::Other(format!("failed to fetch latest: {err:?}")))?
931            .fetch(&contract_info_address)
932            .await
933            .map_err(|err| {
934                Error::Other(format!("failed to fetch account info: {err:?}"))
935            })?;
936        match raw_value {
937            None => {
938                // This typically happens when calling this function with a contract, for
939                // which there is no `AccountId`.
940                let fallback = to_fallback_account_id(addr);
941                tracing::debug!(
942                    "No address suffix was found in the node for H160 address {:?}, using fallback {:?}",
943                    addr,
944                    fallback
945                );
946                let account_id = E::AccountId::decode(&mut &fallback[..]).unwrap();
947                Ok(account_id)
948            }
949            Some(raw_value) => {
950                let raw_account_id = raw_value.as_type::<[u8; 32]>().expect("oh");
951                let account: E::AccountId = Decode::decode(&mut &raw_account_id[..])
952                    .map_err(|err| {
953                        panic!("AccountId from `[u8; 32]` deserialization error: {}", err)
954                    })?;
955                Ok(account)
956            }
957        }
958    }
959}
960
961impl<C, E> ContractsBackend<E> for Client<C, E>
962where
963    C: subxt::Config + Send + Sync,
964    C::AccountId: Clone
965        + Debug
966        + Send
967        + Sync
968        + core::fmt::Display
969        + scale::Codec
970        + From<sr25519::PublicKey>
971        + serde::de::DeserializeOwned,
972    C::Address: From<sr25519::PublicKey>,
973    C::Signature: From<sr25519::Signature>,
974    C::Address: Send + Sync,
975    E: Environment,
976    E::AccountId: Debug + Send + Sync,
977    E::Balance:
978        Clone + Debug + Send + Sync + From<u128> + scale::HasCompact + serde::Serialize,
979    H256: Debug + Send + scale::Encode,
980{
981    type Error = Error;
982    type EventLog = ExtrinsicEvents<C>;
983}
984
985impl<C, E> E2EBackend<E> for Client<C, E>
986where
987    C: subxt::Config + Send + Sync,
988    C::AccountId: Clone
989        + Debug
990        + Send
991        + Sync
992        + core::fmt::Display
993        + scale::Codec
994        + From<sr25519::PublicKey>
995        + From<[u8; 32]>
996        + serde::de::DeserializeOwned,
997    C::Address: From<sr25519::PublicKey>,
998    C::Signature: From<sr25519::Signature>,
999    C::Address: Send + Sync,
1000    <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
1001        From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
1002    E: Environment,
1003    E::AccountId: Debug + Send + Sync,
1004    E::EventRecord: Debug,
1005    E::Balance:
1006        Clone + Debug + Send + Sync + From<u128> + scale::HasCompact + serde::Serialize,
1007    H256: Debug + Send + Sync + scale::Encode,
1008{
1009}
1010
1011/// Try to extract the given field from a dynamic [`Value`].
1012///
1013/// Returns `Err` if:
1014///   - The value is not a [`Value::Composite`] with [`Composite::Named`] fields
1015///   - The value does not contain a field with the given name.
1016fn get_composite_field_value<'a, T>(
1017    value: &'a Value<T>,
1018    field_name: &str,
1019) -> Result<&'a Value<T>, Error> {
1020    if let ValueDef::Composite(Composite::Named(fields)) = &value.value {
1021        let (_, field) = fields
1022            .iter()
1023            .find(|(name, _)| name == field_name)
1024            .ok_or_else(|| {
1025                Error::Balance(format!("No field named '{field_name}' found"))
1026            })?;
1027        Ok(field)
1028    } else {
1029        Err(Error::Balance(
1030            "Expected a composite type with named fields".into(),
1031        ))
1032    }
1033}
1034
1035/// Returns true if the give event is System::Extrinsic failed.
1036fn is_extrinsic_failed_event<C: subxt::Config>(event: &EventDetails<C>) -> bool {
1037    event.pallet_name() == "System" && event.variant_name() == "ExtrinsicFailed"
1038}
1039
1040impl<E: Environment, V, C: subxt::Config, Abi> CallResult<E, V, ExtrinsicEvents<C>, Abi> {
1041    /// Returns true if the specified event was triggered by the call.
1042    pub fn contains_event(&self, pallet_name: &str, variant_name: &str) -> bool {
1043        self.events.iter().any(|event| {
1044            let event = event.expect("unable to extract event");
1045            tracing::debug!(
1046                "found event with pallet: {:?}, variant: {:?}",
1047                event.pallet_name(),
1048                event.variant_name()
1049            );
1050            event.pallet_name() == pallet_name && event.variant_name() == variant_name
1051        })
1052    }
1053
1054    /// Returns all the `ContractEmitted` events emitted by the contract.
1055    #[allow(clippy::result_large_err)] // todo
1056    pub fn contract_emitted_events(
1057        &self,
1058    ) -> Result<Vec<EventWithTopics<events::ContractEmitted>>, subxt::Error>
1059    where
1060        HashFor<C>: Into<sp_core::H256>,
1061    {
1062        let mut events_with_topics = Vec::new();
1063        for event in self.events.iter() {
1064            let event = event?;
1065            if let Some(decoded_event) = event.as_event::<events::ContractEmitted>()? {
1066                let topics = decoded_event.topics.clone();
1067                let event_with_topics = EventWithTopics {
1068                    event: decoded_event,
1069                    topics,
1070                };
1071                events_with_topics.push(event_with_topics);
1072            }
1073        }
1074        Ok(events_with_topics)
1075    }
1076}