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}