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