referrerpolicy=no-referrer-when-downgrade

pallet_revive/
primitives.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! A crate that hosts a common definitions that are relevant for the pallet-revive.
19
20use crate::{BalanceOf, Config, H160, U256};
21use alloc::{string::String, vec::Vec};
22use codec::{Decode, Encode, MaxEncodedLen};
23use frame_support::weights::Weight;
24use pallet_revive_uapi::ReturnFlags;
25use scale_info::TypeInfo;
26use sp_arithmetic::traits::Bounded;
27use sp_core::Get;
28use sp_runtime::{
29	traits::{One, Saturating, Zero},
30	DispatchError, RuntimeDebug,
31};
32
33#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
34pub enum DepositLimit<Balance> {
35	/// Allows bypassing all balance transfer checks.
36	UnsafeOnlyForDryRun,
37
38	/// Specifies a maximum allowable balance for a deposit.
39	Balance(Balance),
40}
41
42impl<T> DepositLimit<T> {
43	pub fn is_unchecked(&self) -> bool {
44		match self {
45			Self::UnsafeOnlyForDryRun => true,
46			_ => false,
47		}
48	}
49}
50
51impl<T> From<T> for DepositLimit<T> {
52	fn from(value: T) -> Self {
53		Self::Balance(value)
54	}
55}
56
57impl<T: Bounded + Copy> DepositLimit<T> {
58	pub fn limit(&self) -> T {
59		match self {
60			Self::UnsafeOnlyForDryRun => T::max_value(),
61			Self::Balance(limit) => *limit,
62		}
63	}
64}
65
66/// Result type of a `bare_call` or `bare_instantiate` call as well as `ContractsApi::call` and
67/// `ContractsApi::instantiate`.
68///
69/// It contains the execution result together with some auxiliary information.
70///
71/// #Note
72///
73/// It has been extended to include `events` at the end of the struct while not bumping the
74/// `ContractsApi` version. Therefore when SCALE decoding a `ContractResult` its trailing data
75/// should be ignored to avoid any potential compatibility issues.
76#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
77pub struct ContractResult<R, Balance> {
78	/// How much weight was consumed during execution.
79	pub gas_consumed: Weight,
80	/// How much weight is required as gas limit in order to execute this call.
81	///
82	/// This value should be used to determine the weight limit for on-chain execution.
83	///
84	/// # Note
85	///
86	/// This can only be different from [`Self::gas_consumed`] when weight pre charging
87	/// is used. Currently, only `seal_call_runtime` makes use of pre charging.
88	/// Additionally, any `seal_call` or `seal_instantiate` makes use of pre-charging
89	/// when a non-zero `gas_limit` argument is supplied.
90	pub gas_required: Weight,
91	/// How much balance was paid by the origin into the contract's deposit account in order to
92	/// pay for storage.
93	///
94	/// The storage deposit is never actually charged from the origin in case of [`Self::result`]
95	/// is `Err`. This is because on error all storage changes are rolled back including the
96	/// payment of the deposit.
97	pub storage_deposit: StorageDeposit<Balance>,
98	/// The execution result of the vm binary code.
99	pub result: Result<R, DispatchError>,
100}
101
102/// The result of the execution of a `eth_transact` call.
103#[derive(Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo)]
104pub struct EthTransactInfo<Balance> {
105	/// The amount of gas that was necessary to execute the transaction.
106	pub gas_required: Weight,
107	/// Storage deposit charged.
108	pub storage_deposit: Balance,
109	/// The weight and deposit equivalent in EVM Gas.
110	pub eth_gas: U256,
111	/// The execution return value.
112	pub data: Vec<u8>,
113}
114
115/// Error type of a `eth_transact` call.
116#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
117pub enum EthTransactError {
118	Data(Vec<u8>),
119	Message(String),
120}
121
122#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
123/// Error encountered while creating a BalanceWithDust from a U256 balance.
124pub enum BalanceConversionError {
125	/// Error encountered while creating the main balance value.
126	Value,
127	/// Error encountered while creating the dust value.
128	Dust,
129}
130
131/// A Balance amount along with some "dust" to represent the lowest decimals that can't be expressed
132/// in the native currency
133#[derive(Default, Clone, Copy, Eq, PartialEq, Debug)]
134pub struct BalanceWithDust<Balance> {
135	/// The value expressed in the native currency
136	value: Balance,
137	/// The dust, representing up to 1 unit of the native currency.
138	/// The dust is bounded between 0 and `crate::Config::NativeToEthRatio`
139	dust: u32,
140}
141
142impl<Balance> From<Balance> for BalanceWithDust<Balance> {
143	fn from(value: Balance) -> Self {
144		Self { value, dust: 0 }
145	}
146}
147
148impl<Balance> BalanceWithDust<Balance> {
149	/// Deconstructs the `BalanceWithDust` into its components.
150	pub fn deconstruct(self) -> (Balance, u32) {
151		(self.value, self.dust)
152	}
153
154	/// Creates a new `BalanceWithDust` with the given value and dust.
155	pub fn new_unchecked<T: Config>(value: Balance, dust: u32) -> Self {
156		debug_assert!(dust < T::NativeToEthRatio::get());
157		Self { value, dust }
158	}
159
160	/// Creates a new `BalanceWithDust` from the given EVM value.
161	pub fn from_value<T: Config>(
162		value: U256,
163	) -> Result<BalanceWithDust<BalanceOf<T>>, BalanceConversionError>
164	where
165		BalanceOf<T>: TryFrom<U256>,
166	{
167		if value.is_zero() {
168			return Ok(Default::default())
169		}
170
171		let (quotient, remainder) = value.div_mod(T::NativeToEthRatio::get().into());
172		let value = quotient.try_into().map_err(|_| BalanceConversionError::Value)?;
173		let dust = remainder.try_into().map_err(|_| BalanceConversionError::Dust)?;
174
175		Ok(BalanceWithDust { value, dust })
176	}
177}
178
179impl<Balance: Zero + One + Saturating> BalanceWithDust<Balance> {
180	/// Returns true if both the value and dust are zero.
181	pub fn is_zero(&self) -> bool {
182		self.value.is_zero() && self.dust == 0
183	}
184
185	/// Returns the Balance rounded to the nearest whole unit if the dust is non-zero.
186	pub fn into_rounded_balance(self) -> Balance {
187		if self.dust == 0 {
188			self.value
189		} else {
190			self.value.saturating_add(Balance::one())
191		}
192	}
193}
194
195/// Result type of a `bare_code_upload` call.
196pub type CodeUploadResult<Balance> = Result<CodeUploadReturnValue<Balance>, DispatchError>;
197
198/// Result type of a `get_storage` call.
199pub type GetStorageResult = Result<Option<Vec<u8>>, ContractAccessError>;
200
201/// The possible errors that can happen querying the storage of a contract.
202#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)]
203pub enum ContractAccessError {
204	/// The given address doesn't point to a contract.
205	DoesntExist,
206	/// Storage key cannot be decoded from the provided input data.
207	KeyDecodingFailed,
208}
209
210/// Output of a contract call or instantiation which ran to completion.
211#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Default)]
212pub struct ExecReturnValue {
213	/// Flags passed along by `seal_return`. Empty when `seal_return` was never called.
214	pub flags: ReturnFlags,
215	/// Buffer passed along by `seal_return`. Empty when `seal_return` was never called.
216	pub data: Vec<u8>,
217}
218
219impl ExecReturnValue {
220	/// The contract did revert all storage changes.
221	pub fn did_revert(&self) -> bool {
222		self.flags.contains(ReturnFlags::REVERT)
223	}
224}
225
226/// The result of a successful contract instantiation.
227#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)]
228pub struct InstantiateReturnValue {
229	/// The output of the called constructor.
230	pub result: ExecReturnValue,
231	/// The address of the new contract.
232	pub addr: H160,
233}
234
235/// The result of successfully uploading a contract.
236#[derive(Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)]
237pub struct CodeUploadReturnValue<Balance> {
238	/// The key under which the new code is stored.
239	pub code_hash: sp_core::H256,
240	/// The deposit that was reserved at the caller. Is zero when the code already existed.
241	pub deposit: Balance,
242}
243
244/// Reference to an existing code hash or a new vm module.
245#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
246pub enum Code {
247	/// A vm module as raw bytes.
248	Upload(Vec<u8>),
249	/// The code hash of an on-chain vm binary blob.
250	Existing(sp_core::H256),
251}
252
253/// The amount of balance that was either charged or refunded in order to pay for storage.
254#[derive(
255	Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo,
256)]
257pub enum StorageDeposit<Balance> {
258	/// The transaction reduced storage consumption.
259	///
260	/// This means that the specified amount of balance was transferred from the involved
261	/// deposit accounts to the origin.
262	Refund(Balance),
263	/// The transaction increased storage consumption.
264	///
265	/// This means that the specified amount of balance was transferred from the origin
266	/// to the involved deposit accounts.
267	Charge(Balance),
268}
269
270impl<Balance: Zero> Default for StorageDeposit<Balance> {
271	fn default() -> Self {
272		Self::Charge(Zero::zero())
273	}
274}
275
276impl<Balance: Zero + Copy> StorageDeposit<Balance> {
277	/// Returns how much balance is charged or `0` in case of a refund.
278	pub fn charge_or_zero(&self) -> Balance {
279		match self {
280			Self::Charge(amount) => *amount,
281			Self::Refund(_) => Zero::zero(),
282		}
283	}
284
285	pub fn is_zero(&self) -> bool {
286		match self {
287			Self::Charge(amount) => amount.is_zero(),
288			Self::Refund(amount) => amount.is_zero(),
289		}
290	}
291}
292
293impl<Balance> StorageDeposit<Balance>
294where
295	Balance: Saturating + Ord + Copy,
296{
297	/// This is essentially a saturating signed add.
298	pub fn saturating_add(&self, rhs: &Self) -> Self {
299		use StorageDeposit::*;
300		match (self, rhs) {
301			(Charge(lhs), Charge(rhs)) => Charge(lhs.saturating_add(*rhs)),
302			(Refund(lhs), Refund(rhs)) => Refund(lhs.saturating_add(*rhs)),
303			(Charge(lhs), Refund(rhs)) =>
304				if lhs >= rhs {
305					Charge(lhs.saturating_sub(*rhs))
306				} else {
307					Refund(rhs.saturating_sub(*lhs))
308				},
309			(Refund(lhs), Charge(rhs)) =>
310				if lhs > rhs {
311					Refund(lhs.saturating_sub(*rhs))
312				} else {
313					Charge(rhs.saturating_sub(*lhs))
314				},
315		}
316	}
317
318	/// This is essentially a saturating signed sub.
319	pub fn saturating_sub(&self, rhs: &Self) -> Self {
320		use StorageDeposit::*;
321		match (self, rhs) {
322			(Charge(lhs), Refund(rhs)) => Charge(lhs.saturating_add(*rhs)),
323			(Refund(lhs), Charge(rhs)) => Refund(lhs.saturating_add(*rhs)),
324			(Charge(lhs), Charge(rhs)) =>
325				if lhs >= rhs {
326					Charge(lhs.saturating_sub(*rhs))
327				} else {
328					Refund(rhs.saturating_sub(*lhs))
329				},
330			(Refund(lhs), Refund(rhs)) =>
331				if lhs > rhs {
332					Refund(lhs.saturating_sub(*rhs))
333				} else {
334					Charge(rhs.saturating_sub(*lhs))
335				},
336		}
337	}
338
339	/// If the amount of deposit (this type) is constrained by a `limit` this calculates how
340	/// much balance (if any) is still available from this limit.
341	///
342	/// # Note
343	///
344	/// In case of a refund the return value can be larger than `limit`.
345	pub fn available(&self, limit: &Balance) -> Balance {
346		use StorageDeposit::*;
347		match self {
348			Charge(amount) => limit.saturating_sub(*amount),
349			Refund(amount) => limit.saturating_add(*amount),
350		}
351	}
352}
353
354/// Indicates whether the account nonce should be incremented after instantiating a new contract.
355///
356/// In Substrate, where transactions can be batched, the account's nonce should be incremented after
357/// each instantiation, ensuring that each instantiation uses a unique nonce.
358///
359/// For transactions sent from Ethereum wallets, which cannot be batched, the nonce should only be
360/// incremented once. In these cases, Use `BumpNonce::No` to suppress an extra nonce increment.
361///
362/// Note:
363/// The origin's nonce is already incremented pre-dispatch by the `CheckNonce` transaction
364/// extension.
365pub enum BumpNonce {
366	/// Do not increment the nonce after contract instantiation
367	No,
368	/// Increment the nonce after contract instantiation
369	Yes,
370}
371
372/// Indicates whether the code was removed after the last refcount was decremented.
373#[must_use = "You must handle whether the code was removed or not."]
374pub enum CodeRemoved {
375	/// The code was not removed. (refcount > 0)
376	No,
377	/// The code was removed. (refcount == 0)
378	Yes,
379}