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