ink_e2e/
contract_results.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
15use std::{
16    fmt,
17    fmt::Debug,
18    marker::PhantomData,
19};
20
21use frame_support::pallet_prelude::{
22    Decode,
23    Encode,
24};
25use ink::codegen::ContractCallBuilder;
26use ink_env::{
27    Environment,
28    call::{
29        FromAddr,
30        utils::DecodeMessageResult,
31    },
32};
33use ink_primitives::{
34    Address,
35    ConstructorResult,
36    H256,
37    MessageResult,
38};
39use ink_revive_types::{
40    CodeUploadResult,
41    ExecReturnValue,
42    InstantiateReturnValue,
43    StorageDeposit,
44    evm::CallTrace,
45};
46use sp_runtime::{
47    DispatchError,
48    Weight,
49};
50
51/// Alias for the contract instantiate result.
52pub type ContractInstantiateResultFor<E> =
53    ContractResult<InstantiateReturnValue, <E as Environment>::Balance>;
54
55// todo use the obj one from `pallet-revive` instead
56/// Result type of a `bare_call`, `bare_instantiate`, `ReviveApi::call`, and
57/// `ReviveApi::instantiate`.
58///
59/// It contains the execution result together with some auxiliary information.
60///
61/// # Note
62///
63/// It has been extended to include `events` at the end of the struct while not bumping
64/// the `ReviveApi` version. Therefore when SCALE decoding a `ContractResult` its
65/// trailing data should be ignored to avoid any potential compatibility issues.
66#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode)]
67pub struct ContractResult<R, Balance> {
68    /// How much weight was consumed during execution.
69    pub gas_consumed: Weight,
70    /// How much weight is required as gas limit in order to execute this call.
71    ///
72    /// This value should be used to determine the weight limit for on-chain execution.
73    ///
74    /// # Note
75    ///
76    /// This can only different from [`Self::gas_consumed`] when weight pre-charging
77    /// is used. Currently, only `seal_call_runtime` makes use of pre-charging.
78    /// Additionally, any `seal_call` or `seal_instantiate` makes use of pre-charging
79    /// when a non-zero `gas_limit` argument is supplied.
80    pub gas_required: Weight,
81    /// How much balance was paid by the origin into the contract's deposit account in
82    /// order to pay for storage.
83    ///
84    /// The storage deposit is never actually charged from the origin in case of
85    /// [`Self::result`] is `Err`. This is because on error all storage changes are
86    /// rolled back including the payment of the deposit.
87    pub storage_deposit: StorageDeposit<Balance>,
88    /// The execution result of the code.
89    pub result: Result<R, DispatchError>,
90}
91
92/// Alias for the contract exec result.
93pub type ContractExecResultFor<E> =
94    ContractResult<ExecReturnValue, <E as Environment>::Balance>;
95
96/// Result of a contract instantiation using bare call.
97pub struct BareInstantiationResult<E: Environment, EventLog> {
98    // The address at which the contract was instantiated.
99    pub addr: Address,
100    // The account id at which the contract was instantiated.
101    pub account_id: E::AccountId,
102    /// Events that happened with the contract instantiation.
103    pub events: EventLog,
104    /// Trace of the instantiated contract.
105    pub trace: Option<CallTrace>,
106    /// Code hash of the instantiated contract.
107    pub code_hash: H256,
108}
109
110/// We implement a custom `Debug` here, as to avoid requiring the trait bound
111/// `Debug` for `E`.
112impl<E: Environment, EventLog> Debug for BareInstantiationResult<E, EventLog>
113where
114    EventLog: Debug,
115{
116    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
117        f.debug_struct("BareInstantiationResult")
118            .field("addr", &self.addr)
119            .field("account_id", &self.account_id.encode())
120            .field("events", &self.events)
121            .field("trace", &self.trace)
122            .field("code_hash", &self.code_hash)
123            .finish()
124    }
125}
126
127/// Result of a contract instantiation.
128pub struct InstantiationResult<E: Environment, EventLog, Abi> {
129    /// The address at which the contract was instantiated.
130    pub addr: Address,
131    /// The account id at which the contract was instantiated.
132    pub account_id: E::AccountId,
133    /// The result of the dry run, contains debug messages
134    /// if there were any.
135    pub dry_run: InstantiateDryRunResult<E, Abi>,
136    /// Events that happened with the contract instantiation.
137    pub events: EventLog,
138    /// todo
139    pub trace: Option<CallTrace>,
140    /// todo
141    pub code_hash: H256,
142}
143
144impl<E: Environment, EventLog, Abi> InstantiationResult<E, EventLog, Abi> {
145    /// Returns a call builder for the contract which was instantiated.
146    ///
147    /// # Note
148    ///
149    /// This uses the ABI used for the contract instantiation call.
150    pub fn call_builder<Contract>(&self) -> <Contract as ContractCallBuilder>::Type<Abi>
151    where
152        Contract: ContractCallBuilder,
153        <Contract as ContractCallBuilder>::Type<Abi>: FromAddr,
154    {
155        <<Contract as ContractCallBuilder>::Type<Abi> as FromAddr>::from_addr(self.addr)
156    }
157
158    /// Returns a call builder for the specified ABI for the contract which was
159    /// instantiated.
160    ///
161    /// # Note
162    ///
163    /// This is useful for contracts that support multiple ABIs.
164    pub fn call_builder_abi<Contract, CallAbi>(
165        &self,
166    ) -> <Contract as ContractCallBuilder>::Type<CallAbi>
167    where
168        Contract: ContractCallBuilder,
169        <Contract as ContractCallBuilder>::Type<CallAbi>: FromAddr,
170    {
171        <<Contract as ContractCallBuilder>::Type<CallAbi> as FromAddr>::from_addr(
172            self.addr,
173        )
174    }
175}
176
177/// We implement a custom `Debug` here, as to avoid requiring the trait bound `Debug` for
178/// `E`.
179impl<E: Environment, EventLog, Abi> Debug for InstantiationResult<E, EventLog, Abi>
180where
181    E::AccountId: Debug,
182    E::Balance: Debug,
183    E::EventRecord: Debug,
184    EventLog: Debug,
185{
186    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
187        // todo add missing fields
188        f.debug_struct("InstantiationResult")
189            .field("addr", &self.addr)
190            .field("dry_run", &self.dry_run)
191            .field("events", &self.events)
192            .finish()
193    }
194}
195
196/// Result of a contract upload.
197pub struct UploadResult<E: Environment, EventLog> {
198    /// The hash with which the contract can be instantiated.
199    pub code_hash: H256,
200    /// The result of the dry run, contains debug messages if there were any.
201    pub dry_run: CodeUploadResult<E::Balance>,
202    /// Events that happened with the contract instantiation.
203    pub events: EventLog,
204}
205
206/// We implement a custom `Debug` here, to avoid requiring the trait bound `Debug` for
207/// `E`.
208impl<E: Environment, EventLog> Debug for UploadResult<E, EventLog>
209where
210    E::Balance: Debug,
211    H256: Debug,
212    EventLog: Debug,
213{
214    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
215        f.debug_struct("UploadResult")
216            .field("code_hash", &self.code_hash)
217            .field("dry_run", &self.dry_run)
218            .field("events", &self.events)
219            .finish()
220    }
221}
222
223/// Result of a contract call.
224pub struct CallResult<E: Environment, V, EventLog, Abi> {
225    /// The result of the dry run, contains debug messages if there were any.
226    pub dry_run: CallDryRunResult<E, V, Abi>,
227    /// Events that happened with the contract instantiation.
228    pub events: EventLog,
229    /// todo
230    pub trace: Option<CallTrace>,
231}
232
233impl<E: Environment, V: DecodeMessageResult<Abi>, EventLog, Abi>
234    CallResult<E, V, EventLog, Abi>
235{
236    /// Returns the [`MessageResult`] from the execution of the dry-run message
237    /// call.
238    ///
239    /// # Panics
240    /// - if the dry-run message call failed to execute.
241    /// - if message result cannot be decoded into the expected return value type.
242    pub fn message_result(&self) -> MessageResult<V> {
243        self.dry_run.message_result()
244    }
245
246    /// Returns the decoded return value of the message from the dry-run.
247    ///
248    /// Panics if the value could not be decoded. The raw bytes can be accessed
249    /// via [`CallResult::return_data`].
250    pub fn return_value(self) -> V {
251        self.dry_run.return_value()
252    }
253}
254
255impl<E: Environment, V, EventLog, Abi> CallResult<E, V, EventLog, Abi> {
256    /// Returns the return value of the message dry-run as raw bytes.
257    ///
258    /// Panics if the dry-run message call failed to execute.
259    pub fn return_data(&self) -> &[u8] {
260        &self.dry_run.exec_return_value().data
261    }
262
263    /// Returns the error from nested contract calls (e.g., precompile errors)
264    /// if available in the trace, otherwise returns the raw error data.
265    pub fn extract_error(&self) -> Option<String> {
266        if !self.dry_run.did_revert() {
267            return None;
268        }
269
270        // Check trace for error information
271        if let Some(trace) = &self.trace {
272            // // Check nested calls first (more specific errors)
273            for call in &trace.calls {
274                if let Some(error) = &call.error {
275                    return Some(error.clone());
276                }
277            }
278
279            // Then check top-level error
280            if let Some(error) = &trace.error {
281                return Some(error.clone());
282            }
283        }
284        // Fallback to raw data
285        Some(format!("{:?}", self.return_data()))
286    }
287}
288
289// TODO(#xxx) Improve the `Debug` implementation.
290impl<E: Environment, V, EventLog, Abi> Debug for CallResult<E, V, EventLog, Abi>
291where
292    E: Debug,
293    E::Balance: Debug,
294    E::EventRecord: Debug,
295    V: Debug,
296    EventLog: Debug,
297{
298    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
299        f.debug_struct("CallResult")
300            .field("dry_run", &self.dry_run)
301            .field("events", &self.events)
302            .field("trace", &self.trace)
303            .finish()
304    }
305}
306
307/// Result of the dry run of a contract call.
308pub struct CallDryRunResult<E: Environment, V, Abi> {
309    /// The result of the dry run, contains debug messages if there were any.
310    pub exec_result: ContractExecResultFor<E>,
311    /// The execution trace (if any).
312    pub trace: Option<CallTrace>,
313    /// Phantom data for return type and its ABI encoding.
314    pub _marker: PhantomData<(V, Abi)>,
315}
316
317/// We implement a custom `Debug` here, as to avoid requiring the trait bound `Debug` for
318/// `E`.
319impl<E: Environment, V, Abi> Debug for CallDryRunResult<E, V, Abi>
320where
321    E::Balance: Debug,
322    E::EventRecord: Debug,
323{
324    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
325        f.debug_struct("CallDryRunResult")
326            .field("exec_result", &self.exec_result)
327            .field("trace", &self.trace)
328            .finish()
329    }
330}
331
332impl<E: Environment, V, Abi> CallDryRunResult<E, V, Abi> {
333    /// Returns true if the dry-run execution resulted in an error.
334    pub fn is_err(&self) -> bool {
335        self.exec_result.result.is_err() || self.did_revert()
336    }
337
338    /// Returns the [`ExecReturnValue`] resulting from the dry-run message call.
339    ///
340    /// Panics if the dry-run message call failed to execute.
341    pub fn exec_return_value(&self) -> &ExecReturnValue {
342        self.exec_result
343            .result
344            .as_ref()
345            .unwrap_or_else(|call_err| panic!("Call dry-run failed: {call_err:?}"))
346    }
347
348    /// Returns true if the message call reverted.
349    pub fn did_revert(&self) -> bool {
350        let res = self.exec_result.result.clone().expect("no result found");
351        res.did_revert()
352    }
353
354    /// Returns the return value as raw bytes of the message from the dry-run.
355    ///
356    /// Panics if the dry-run message call failed to execute.
357    pub fn return_data(&self) -> &[u8] {
358        &self.exec_return_value().data
359    }
360}
361
362impl<E: Environment, V: DecodeMessageResult<Abi>, Abi> CallDryRunResult<E, V, Abi> {
363    /// Returns the [`MessageResult`] from the execution of the dry-run message call.
364    ///
365    /// # Panics
366    /// - if the dry-run message call failed to execute.
367    /// - if message result cannot be decoded into the expected return value type.
368    pub fn message_result(&self) -> MessageResult<V> {
369        let data = &self.exec_return_value().data;
370        DecodeMessageResult::decode_output(data.as_ref(), self.did_revert()).unwrap_or_else(|env_err| {
371            panic!(
372                "Decoding dry run result to ink! message return type failed: {env_err:?} {:?}\n\n\
373                Attempt to stringify returned data: {:?}",
374                self.exec_return_value(),
375                String::from_utf8_lossy(&self.exec_return_value().data[..])
376            )
377        })
378    }
379
380    /// Returns the decoded return value of the message from the dry-run.
381    ///
382    /// Panics if the value could not be decoded. The raw bytes can be accessed via
383    /// [`CallResult::return_data`].
384    pub fn return_value(&self) -> V {
385        self.message_result()
386            .unwrap_or_else(|lang_err| {
387                panic!(
388                    "Encountered a `LangError` while decoding dry run result to ink! message: {lang_err:?}"
389                )
390            })
391    }
392}
393
394/// Result of the dry run of a contract call.
395#[derive(Clone)]
396pub struct InstantiateDryRunResult<E: Environment, Abi> {
397    /// The result of the dry run, contains debug messages if there were any.
398    pub contract_result: ContractInstantiateResultFor<E>,
399    /// Phantom data for return type and its ABI encoding.
400    pub _marker: PhantomData<Abi>,
401}
402
403impl<E: Environment, Abi> From<ContractInstantiateResultFor<E>>
404    for InstantiateDryRunResult<E, Abi>
405{
406    fn from(contract_result: ContractInstantiateResultFor<E>) -> Self {
407        Self {
408            contract_result,
409            _marker: PhantomData,
410        }
411    }
412}
413
414impl<E: Environment, Abi> InstantiateDryRunResult<E, Abi> {
415    /// Returns true if the dry-run execution resulted in an error.
416    pub fn is_err(&self) -> bool {
417        self.contract_result.result.is_err() || self.did_revert()
418    }
419
420    /// Returns the [`InstantiateReturnValue`] resulting from the dry-run message call.
421    ///
422    /// Panics if the dry-run message call failed to execute.
423    pub fn instantiate_return_value(&self) -> &InstantiateReturnValue {
424        self.contract_result
425            .result
426            .as_ref()
427            .unwrap_or_else(|call_err| panic!("Instantiate dry-run failed: {call_err:?}"))
428    }
429
430    /// Returns the encoded return value from the constructor.
431    ///
432    /// # Panics
433    /// - if the dry-run message instantiate failed to execute.
434    /// - if message result cannot be decoded into the expected return value type.
435    pub fn constructor_result<V: DecodeMessageResult<Abi>>(
436        &self,
437    ) -> ConstructorResult<V> {
438        let data = &self.instantiate_return_value().result.data;
439        DecodeMessageResult::decode_output(data.as_ref(), self.did_revert()).unwrap_or_else(|env_err| {
440            panic!("Decoding dry run result to constructor return type failed: {env_err:?}")
441        })
442    }
443
444    /// Returns the return value of the instantiation dry-run as raw bytes.
445    ///
446    /// Panics if the dry-run message call failed to execute.
447    pub fn return_data(&self) -> &[u8] {
448        &self.instantiate_return_value().result.data
449    }
450
451    /// Returns true if the instantiation dry-run reverted.
452    pub fn did_revert(&self) -> bool {
453        let res = self.instantiate_return_value().clone().result;
454        res.did_revert()
455    }
456}
457
458impl<E, Abi> Debug for InstantiateDryRunResult<E, Abi>
459where
460    E: Environment,
461    E::AccountId: Debug,
462    E::Balance: Debug,
463    E::EventRecord: Debug,
464{
465    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
466        f.debug_struct("InstantiateDryRunResult")
467            .field("contract_result", &self.contract_result)
468            .finish()
469    }
470}