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    call::{
28        utils::DecodeMessageResult,
29        FromAddr,
30    },
31    Environment,
32};
33use ink_primitives::{
34    Address,
35    ConstructorResult,
36    MessageResult,
37    H256,
38};
39use pallet_revive::{
40    evm::CallTrace,
41    CodeUploadResult,
42    ExecReturnValue,
43    InstantiateReturnValue,
44    StorageDeposit,
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<EventLog> {
98    /// The address at which the contract was instantiated.
99    pub addr: Address,
100    /// Events that happened with the contract instantiation.
101    pub events: EventLog,
102    /// todo
103    pub trace: Option<CallTrace>,
104    /// todo
105    pub code_hash: H256,
106}
107
108impl<EventLog> BareInstantiationResult<EventLog> {
109    /// Returns the address at which the contract was instantiated.
110    /// todo why this strange name? shouldn't it be `fn addr()`?
111    pub fn call(&self) -> Address {
112        self.addr
113    }
114}
115
116/// We implement a custom `Debug` here, as to avoid requiring the trait bound `Debug` for
117/// `E`.
118impl<EventLog> Debug for BareInstantiationResult<EventLog>
119where
120    EventLog: Debug,
121{
122    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
123        // todo add missing fields
124        f.debug_struct("BareInstantiationResult")
125            .field("addr", &self.addr)
126            .field("events", &self.events)
127            .field("trace", &self.trace)
128            .finish()
129    }
130}
131
132/// Result of a contract instantiation.
133pub struct InstantiationResult<E: Environment, EventLog, Abi> {
134    /// The account id at which the contract was instantiated.
135    pub addr: Address,
136    /// The result of the dry run, contains debug messages
137    /// if there were any.
138    pub dry_run: InstantiateDryRunResult<E, Abi>,
139    /// Events that happened with the contract instantiation.
140    pub events: EventLog,
141    /// todo
142    pub trace: Option<CallTrace>,
143    /// todo
144    pub code_hash: H256,
145}
146
147impl<E: Environment, EventLog, Abi> InstantiationResult<E, EventLog, Abi> {
148    /// Returns a call builder for the contract which was instantiated.
149    ///
150    /// # Note
151    ///
152    /// This uses the ABI used for the contract instantiation call.
153    pub fn call_builder<Contract>(&self) -> <Contract as ContractCallBuilder>::Type<Abi>
154    where
155        Contract: ContractCallBuilder,
156        <Contract as ContractCallBuilder>::Type<Abi>: FromAddr,
157    {
158        <<Contract as ContractCallBuilder>::Type<Abi> as FromAddr>::from_addr(self.addr)
159    }
160
161    /// Returns a call builder for the specified ABI for the contract which was
162    /// instantiated.
163    ///
164    /// # Note
165    ///
166    /// This is useful for contracts that support multiple ABIs.
167    pub fn call_builder_abi<Contract, CallAbi>(
168        &self,
169    ) -> <Contract as ContractCallBuilder>::Type<CallAbi>
170    where
171        Contract: ContractCallBuilder,
172        <Contract as ContractCallBuilder>::Type<CallAbi>: FromAddr,
173    {
174        <<Contract as ContractCallBuilder>::Type<CallAbi> as FromAddr>::from_addr(
175            self.addr,
176        )
177    }
178}
179
180/// We implement a custom `Debug` here, as to avoid requiring the trait bound `Debug` for
181/// `E`.
182impl<E: Environment, EventLog, Abi> Debug for InstantiationResult<E, EventLog, Abi>
183where
184    E::AccountId: Debug,
185    E::Balance: Debug,
186    E::EventRecord: Debug,
187    EventLog: Debug,
188{
189    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
190        // todo add missing fields
191        f.debug_struct("InstantiationResult")
192            .field("addr", &self.addr)
193            .field("dry_run", &self.dry_run)
194            .field("events", &self.events)
195            .finish()
196    }
197}
198
199/// Result of a contract upload.
200pub struct UploadResult<E: Environment, EventLog> {
201    /// The hash with which the contract can be instantiated.
202    pub code_hash: H256,
203    /// The result of the dry run, contains debug messages if there were any.
204    pub dry_run: CodeUploadResult<E::Balance>,
205    /// Events that happened with the contract instantiation.
206    pub events: EventLog,
207}
208
209/// We implement a custom `Debug` here, to avoid requiring the trait bound `Debug` for
210/// `E`.
211impl<E: Environment, EventLog> Debug for UploadResult<E, EventLog>
212where
213    E::Balance: Debug,
214    H256: Debug,
215    EventLog: Debug,
216{
217    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
218        f.debug_struct("UploadResult")
219            .field("code_hash", &self.code_hash)
220            .field("dry_run", &self.dry_run)
221            .field("events", &self.events)
222            .finish()
223    }
224}
225
226/// Result of a contract call.
227pub struct CallResult<E: Environment, V, EventLog, Abi> {
228    /// The result of the dry run, contains debug messages if there were any.
229    pub dry_run: CallDryRunResult<E, V, Abi>,
230    /// Events that happened with the contract instantiation.
231    pub events: EventLog,
232    /// todo
233    pub trace: Option<CallTrace>,
234}
235
236impl<E: Environment, V: DecodeMessageResult<Abi>, EventLog, Abi>
237    CallResult<E, V, EventLog, Abi>
238{
239    /// Returns the [`MessageResult`] from the execution of the dry-run message
240    /// call.
241    ///
242    /// # Panics
243    /// - if the dry-run message call failed to execute.
244    /// - if message result cannot be decoded into the expected return value type.
245    pub fn message_result(&self) -> MessageResult<V> {
246        self.dry_run.message_result()
247    }
248
249    /// Returns the decoded return value of the message from the dry-run.
250    ///
251    /// Panics if the value could not be decoded. The raw bytes can be accessed
252    /// via [`CallResult::return_data`].
253    pub fn return_value(self) -> V {
254        self.dry_run.return_value()
255    }
256}
257
258impl<E: Environment, V, EventLog, Abi> CallResult<E, V, EventLog, Abi> {
259    /// Returns the return value of the message dry-run as raw bytes.
260    ///
261    /// Panics if the dry-run message call failed to execute.
262    pub fn return_data(&self) -> &[u8] {
263        &self.dry_run.exec_return_value().data
264    }
265}
266
267// TODO(#xxx) Improve the `Debug` implementation.
268impl<E: Environment, V, EventLog, Abi> Debug for CallResult<E, V, EventLog, Abi>
269where
270    E: Debug,
271    E::Balance: Debug,
272    E::EventRecord: Debug,
273    V: Debug,
274    EventLog: Debug,
275{
276    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
277        f.debug_struct("CallResult")
278            .field("dry_run", &self.dry_run)
279            .field("events", &self.events)
280            .field("trace", &self.trace)
281            .finish()
282    }
283}
284
285/// Result of the dry run of a contract call.
286pub struct CallDryRunResult<E: Environment, V, Abi> {
287    /// The result of the dry run, contains debug messages if there were any.
288    pub exec_result: ContractExecResultFor<E>,
289    /// The execution trace (if any).
290    pub trace: Option<CallTrace>,
291    /// Phantom data for return type and its ABI encoding.
292    pub _marker: PhantomData<(V, Abi)>,
293}
294
295/// We implement a custom `Debug` here, as to avoid requiring the trait bound `Debug` for
296/// `E`.
297impl<E: Environment, V, Abi> Debug for CallDryRunResult<E, V, Abi>
298where
299    E::Balance: Debug,
300    E::EventRecord: Debug,
301{
302    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
303        f.debug_struct("CallDryRunResult")
304            .field("exec_result", &self.exec_result)
305            .finish()
306    }
307}
308
309impl<E: Environment, V, Abi> CallDryRunResult<E, V, Abi> {
310    /// Returns true if the dry-run execution resulted in an error.
311    pub fn is_err(&self) -> bool {
312        self.exec_result.result.is_err() || self.did_revert()
313    }
314
315    /// Returns the [`ExecReturnValue`] resulting from the dry-run message call.
316    ///
317    /// Panics if the dry-run message call failed to execute.
318    pub fn exec_return_value(&self) -> &ExecReturnValue {
319        self.exec_result
320            .result
321            .as_ref()
322            .unwrap_or_else(|call_err| panic!("Call dry-run failed: {call_err:?}"))
323    }
324
325    /// Returns true if the message call reverted.
326    pub fn did_revert(&self) -> bool {
327        let res = self.exec_result.result.clone().expect("no result found");
328        res.did_revert()
329    }
330
331    /// Returns the return value as raw bytes of the message from the dry-run.
332    ///
333    /// Panics if the dry-run message call failed to execute.
334    pub fn return_data(&self) -> &[u8] {
335        &self.exec_return_value().data
336    }
337}
338
339impl<E: Environment, V: DecodeMessageResult<Abi>, Abi> CallDryRunResult<E, V, Abi> {
340    /// Returns the [`MessageResult`] from the execution of the dry-run message call.
341    ///
342    /// # Panics
343    /// - if the dry-run message call failed to execute.
344    /// - if message result cannot be decoded into the expected return value type.
345    pub fn message_result(&self) -> MessageResult<V> {
346        let data = &self.exec_return_value().data;
347        DecodeMessageResult::decode_output(data.as_ref(), self.did_revert()).unwrap_or_else(|env_err| {
348            panic!(
349                "Decoding dry run result to ink! message return type failed: {env_err:?} {:?}",
350                self.exec_return_value()
351            )
352        })
353    }
354
355    /// Returns the decoded return value of the message from the dry-run.
356    ///
357    /// Panics if the value could not be decoded. The raw bytes can be accessed via
358    /// [`CallResult::return_data`].
359    pub fn return_value(&self) -> V {
360        self.message_result()
361            .unwrap_or_else(|lang_err| {
362                panic!(
363                    "Encountered a `LangError` while decoding dry run result to ink! message: {lang_err:?}"
364                )
365            })
366    }
367}
368
369/// Result of the dry run of a contract call.
370pub struct InstantiateDryRunResult<E: Environment, Abi> {
371    /// The result of the dry run, contains debug messages if there were any.
372    pub contract_result: ContractInstantiateResultFor<E>,
373    /// Phantom data for return type and its ABI encoding.
374    pub _marker: PhantomData<Abi>,
375}
376
377impl<E: Environment, Abi> From<ContractInstantiateResultFor<E>>
378    for InstantiateDryRunResult<E, Abi>
379{
380    fn from(contract_result: ContractInstantiateResultFor<E>) -> Self {
381        Self {
382            contract_result,
383            _marker: PhantomData,
384        }
385    }
386}
387
388impl<E: Environment, Abi> InstantiateDryRunResult<E, Abi> {
389    /// Returns true if the dry-run execution resulted in an error.
390    pub fn is_err(&self) -> bool {
391        self.contract_result.result.is_err()
392    }
393
394    /// Returns the [`InstantiateReturnValue`] resulting from the dry-run message call.
395    ///
396    /// Panics if the dry-run message call failed to execute.
397    pub fn instantiate_return_value(&self) -> &InstantiateReturnValue {
398        self.contract_result
399            .result
400            .as_ref()
401            .unwrap_or_else(|call_err| panic!("Instantiate dry-run failed: {call_err:?}"))
402    }
403
404    /// Returns the encoded return value from the constructor.
405    ///
406    /// # Panics
407    /// - if the dry-run message instantiate failed to execute.
408    /// - if message result cannot be decoded into the expected return value type.
409    pub fn constructor_result<V: DecodeMessageResult<Abi>>(
410        &self,
411    ) -> ConstructorResult<V> {
412        let data = &self.instantiate_return_value().result.data;
413        DecodeMessageResult::decode_output(data.as_ref(), self.did_revert()).unwrap_or_else(|env_err| {
414            panic!("Decoding dry run result to constructor return type failed: {env_err:?}")
415        })
416    }
417
418    /// Returns the return value of the instantiation dry-run as raw bytes.
419    ///
420    /// Panics if the dry-run message call failed to execute.
421    pub fn return_data(&self) -> &[u8] {
422        &self.instantiate_return_value().result.data
423    }
424
425    /// Returns true if the instantiation dry-run reverted.
426    pub fn did_revert(&self) -> bool {
427        let res = self.instantiate_return_value().clone().result;
428        res.did_revert()
429    }
430}
431
432impl<E, Abi> Debug for InstantiateDryRunResult<E, Abi>
433where
434    E: Environment,
435    E::AccountId: Debug,
436    E::Balance: Debug,
437    E::EventRecord: Debug,
438{
439    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
440        f.debug_struct("InstantiateDryRunResult")
441            .field("contract_result", &self.contract_result)
442            .finish()
443    }
444}