pezpallet_revive/
lib.rs

1// This file is part of Bizinikiwi.
2
3// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
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#![doc = include_str!("../README.md")]
19#![allow(rustdoc::private_intra_doc_links)]
20#![cfg_attr(not(feature = "std"), no_std)]
21#![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "1024")]
22
23extern crate alloc;
24
25mod address;
26mod benchmarking;
27mod call_builder;
28mod debug;
29mod exec;
30mod gas;
31mod impl_fungibles;
32mod limits;
33mod primitives;
34mod storage;
35#[cfg(test)]
36mod tests;
37mod transient_storage;
38mod vm;
39mod weightinfo_extension;
40
41pub mod evm;
42pub mod migrations;
43pub mod mock;
44pub mod precompiles;
45pub mod test_utils;
46pub mod tracing;
47pub mod weights;
48
49use crate::{
50	evm::{
51		block_hash::EthereumBlockBuilderIR,
52		block_storage, create_call,
53		fees::{Combinator, InfoT as FeeInfo},
54		runtime::SetWeightLimit,
55		CallTracer, GenericTransaction, PrestateTracer, Trace, Tracer, TracerType, TYPE_EIP1559,
56	},
57	exec::{AccountIdOf, ExecError, Stack as ExecStack},
58	gas::GasMeter,
59	storage::{meter::Meter as StorageMeter, AccountType, DeletionQueueManager},
60	tracing::if_tracing,
61	vm::{pvm::extract_code_and_data, CodeInfo, RuntimeCosts},
62	weightinfo_extension::OnFinalizeBlockParts,
63};
64use alloc::{boxed::Box, format, vec};
65use codec::{Codec, Decode, Encode};
66use environmental::*;
67use pezframe_support::{
68	dispatch::{
69		DispatchErrorWithPostInfo, DispatchResult, DispatchResultWithPostInfo, GetDispatchInfo,
70		Pays, PostDispatchInfo, RawOrigin,
71	},
72	ensure,
73	pezpallet_prelude::DispatchClass,
74	traits::{
75		fungible::{Balanced, Inspect, Mutate, MutateHold},
76		tokens::Balance,
77		ConstU32, ConstU64, EnsureOrigin, Get, IsSubType, IsType, OriginTrait,
78	},
79	weights::WeightMeter,
80	BoundedVec, RuntimeDebugNoBound,
81};
82use pezframe_system::{
83	ensure_signed,
84	pezpallet_prelude::{BlockNumberFor, OriginFor},
85	Pezpallet as System,
86};
87use pezsp_runtime::{
88	traits::{BadOrigin, Bounded, Convert, Dispatchable, Saturating, UniqueSaturatedInto, Zero},
89	AccountId32, DispatchError, FixedPointNumber, FixedU128,
90};
91use scale_info::TypeInfo;
92
93pub use crate::{
94	address::{
95		create1, create2, is_eth_derived, AccountId32Mapper, AddressMapper, TestAccountMapper,
96	},
97	debug::DebugSettings,
98	evm::{
99		block_hash::ReceiptGasInfo, Address as EthAddress, Block as EthBlock, DryRunConfig,
100		ReceiptInfo,
101	},
102	exec::{DelegateInfo, Executable, Key, MomentOf, Origin as ExecOrigin},
103	pezpallet::{genesis, *},
104	storage::{AccountInfo, ContractInfo},
105	vm::{BytecodeType, ContractBlob},
106};
107pub use codec;
108pub use pezframe_support::{self, dispatch::DispatchInfo, traits::Time, weights::Weight};
109pub use pezframe_system::{self, limits::BlockWeights};
110pub use pezsp_core::{keccak_256, H160, H256, U256};
111pub use pezsp_runtime;
112pub use primitives::*;
113pub use weights::WeightInfo;
114
115#[cfg(doc)]
116pub use crate::vm::pvm::SyscallDoc;
117
118pub type BalanceOf<T> = <T as Config>::Balance;
119type TrieId = BoundedVec<u8, ConstU32<128>>;
120type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
121type CallOf<T> = <T as Config>::RuntimeCall;
122
123/// Used as a sentinel value when reading and writing contract memory.
124///
125/// It is usually used to signal `None` to a contract when only a primitive is allowed
126/// and we don't want to go through encoding a full Rust type. Using `u32::Max` is a safe
127/// sentinel because contracts are never allowed to use such a large amount of resources
128/// that this value makes sense for a memory location or length.
129const SENTINEL: u32 = u32::MAX;
130
131/// The target that is used for the log output emitted by this crate.
132///
133/// Hence you can use this target to selectively increase the log level for this crate.
134///
135/// Example: `RUST_LOG=runtime::revive=debug my_code --dev`
136const LOG_TARGET: &str = "runtime::revive";
137
138#[pezframe_support::pezpallet]
139pub mod pezpallet {
140	use super::*;
141	use pezframe_support::{pezpallet_prelude::*, traits::FindAuthor};
142	use pezframe_system::pezpallet_prelude::*;
143	use pezsp_core::U256;
144	use pezsp_runtime::Perbill;
145
146	/// The in-code storage version.
147	pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
148
149	#[pezpallet::pezpallet]
150	#[pezpallet::storage_version(STORAGE_VERSION)]
151	pub struct Pezpallet<T>(_);
152
153	#[pezpallet::config(with_default)]
154	pub trait Config: pezframe_system::Config {
155		/// The time implementation used to supply timestamps to contracts through `seal_now`.
156		type Time: Time<Moment: Into<U256>>;
157
158		/// The balance type of [`Self::Currency`].
159		///
160		/// Just added here to add additional trait bounds.
161		#[pezpallet::no_default]
162		type Balance: Balance + TryFrom<U256> + Into<U256> + Bounded + UniqueSaturatedInto<u64>;
163
164		/// The fungible in which fees are paid and contract balances are held.
165		#[pezpallet::no_default]
166		type Currency: Inspect<Self::AccountId, Balance = Self::Balance>
167			+ Mutate<Self::AccountId>
168			+ MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>
169			+ Balanced<Self::AccountId>;
170
171		/// The overarching event type.
172		#[pezpallet::no_default_bounds]
173		#[allow(deprecated)]
174		type RuntimeEvent: From<Event<Self>>
175			+ IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
176
177		/// The overarching call type.
178		#[pezpallet::no_default_bounds]
179		type RuntimeCall: Parameter
180			+ Dispatchable<
181				RuntimeOrigin = OriginFor<Self>,
182				Info = DispatchInfo,
183				PostInfo = PostDispatchInfo,
184			> + IsType<<Self as pezframe_system::Config>::RuntimeCall>
185			+ From<Call<Self>>
186			+ IsSubType<Call<Self>>
187			+ GetDispatchInfo;
188
189		/// The overarching origin type.
190		#[pezpallet::no_default_bounds]
191		type RuntimeOrigin: IsType<OriginFor<Self>>
192			+ From<Origin<Self>>
193			+ Into<Result<Origin<Self>, OriginFor<Self>>>;
194
195		/// Overarching hold reason.
196		#[pezpallet::no_default_bounds]
197		type RuntimeHoldReason: From<HoldReason>;
198
199		/// Describes the weights of the dispatchables of this module and is also used to
200		/// construct a default cost schedule.
201		type WeightInfo: WeightInfo;
202
203		/// Type that allows the runtime authors to add new host functions for a contract to call.
204		///
205		/// Pass in a tuple of types that implement [`precompiles::Precompile`].
206		#[pezpallet::no_default_bounds]
207		#[allow(private_bounds)]
208		type Precompiles: precompiles::Precompiles<Self>;
209
210		/// Find the author of the current block.
211		type FindAuthor: FindAuthor<Self::AccountId>;
212
213		/// The amount of balance a caller has to pay for each byte of storage.
214		///
215		/// # Note
216		///
217		/// It is safe to change this value on a live chain as all refunds are pro rata.
218		#[pezpallet::constant]
219		#[pezpallet::no_default_bounds]
220		type DepositPerByte: Get<BalanceOf<Self>>;
221
222		/// The amount of balance a caller has to pay for each storage item.
223		///
224		/// # Note
225		///
226		/// It is safe to change this value on a live chain as all refunds are pro rata.
227		#[pezpallet::constant]
228		#[pezpallet::no_default_bounds]
229		type DepositPerItem: Get<BalanceOf<Self>>;
230
231		/// The amount of balance a caller has to pay for each child trie storage item.
232		///
233		/// Those are the items created by a contract. In Solidity each value is a single
234		/// storage item. This is why we need to set a lower value here than for the main
235		/// trie items. Otherwise the storage deposit is too high.
236		///
237		/// # Note
238		///
239		/// It is safe to change this value on a live chain as all refunds are pro rata.
240		#[pezpallet::constant]
241		#[pezpallet::no_default_bounds]
242		type DepositPerChildTrieItem: Get<BalanceOf<Self>>;
243
244		/// The percentage of the storage deposit that should be held for using a code hash.
245		/// Instantiating a contract, protects the code from being removed. In order to prevent
246		/// abuse these actions are protected with a percentage of the code deposit.
247		#[pezpallet::constant]
248		type CodeHashLockupDepositPercent: Get<Perbill>;
249
250		/// Use either valid type is [`address::AccountId32Mapper`] or [`address::H160Mapper`].
251		#[pezpallet::no_default]
252		type AddressMapper: AddressMapper<Self>;
253
254		/// Make contract callable functions marked as `#[unstable]` available.
255		///
256		/// Contracts that use `#[unstable]` functions won't be able to be uploaded unless
257		/// this is set to `true`. This is only meant for testnets and dev nodes in order to
258		/// experiment with new features.
259		///
260		/// # Warning
261		///
262		/// Do **not** set to `true` on productions chains.
263		#[pezpallet::constant]
264		type UnsafeUnstableInterface: Get<bool>;
265
266		/// Allow EVM bytecode to be uploaded and instantiated.
267		#[pezpallet::constant]
268		type AllowEVMBytecode: Get<bool>;
269
270		/// Origin allowed to upload code.
271		///
272		/// By default, it is safe to set this to `EnsureSigned`, allowing anyone to upload contract
273		/// code.
274		#[pezpallet::no_default_bounds]
275		type UploadOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
276
277		/// Origin allowed to instantiate code.
278		///
279		/// # Note
280		///
281		/// This is not enforced when a contract instantiates another contract. The
282		/// [`Self::UploadOrigin`] should make sure that no code is deployed that does unwanted
283		/// instantiations.
284		///
285		/// By default, it is safe to set this to `EnsureSigned`, allowing anyone to instantiate
286		/// contract code.
287		#[pezpallet::no_default_bounds]
288		type InstantiateOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
289
290		/// The amount of memory in bytes that teyrchain nodes a lot to the runtime.
291		///
292		/// This is used in [`Pezpallet::integrity_test`] to make sure that the runtime has enough
293		/// memory to support this pezpallet if set to the correct value.
294		type RuntimeMemory: Get<u32>;
295
296		/// The amount of memory in bytes that relay chain validators a lot to the PoV.
297		///
298		/// This is used in [`Pezpallet::integrity_test`] to make sure that the runtime has enough
299		/// memory to support this pezpallet if set to the correct value.
300		///
301		/// This value is usually higher than [`Self::RuntimeMemory`] to account for the fact
302		/// that validators have to hold all storage items in PvF memory.
303		type PVFMemory: Get<u32>;
304
305		/// The [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID.
306		///
307		/// This is a unique identifier assigned to each blockchain network,
308		/// preventing replay attacks.
309		#[pezpallet::constant]
310		type ChainId: Get<u64>;
311
312		/// The ratio between the decimal representation of the native token and the ETH token.
313		#[pezpallet::constant]
314		type NativeToEthRatio: Get<u32>;
315
316		/// Set to [`crate::evm::fees::Info`] for a production runtime.
317		///
318		/// For mock runtimes that do not need to interact with any eth compat functionality
319		/// the default value of `()` will suffice.
320		#[pezpallet::no_default_bounds]
321		type FeeInfo: FeeInfo<Self>;
322
323		/// The fraction the maximum extrinsic weight `eth_transact` extrinsics are capped to.
324		///
325		/// This is not a security measure but a requirement due to how we map gas to `(Weight,
326		/// StorageDeposit)`. The mapping might derive a `Weight` that is too large to fit into an
327		/// extrinsic. In this case we cap it to the limit specified here.
328		///
329		/// `eth_transact` transactions that use more weight than specified will fail with an out of
330		/// gas error during execution. Larger fractions will allow more transactions to run.
331		/// Smaller values waste less block space: Choose as small as possible and as large as
332		/// necessary.
333		///
334		///  Default: `0.5`.
335		#[pezpallet::constant]
336		type MaxEthExtrinsicWeight: Get<FixedU128>;
337
338		/// Allows debug-mode configuration, such as enabling unlimited contract size.
339		#[pezpallet::constant]
340		type DebugEnabled: Get<bool>;
341	}
342
343	/// Container for different types that implement [`DefaultConfig`]` of this pezpallet.
344	pub mod config_preludes {
345		use super::*;
346		use pezframe_support::{
347			derive_impl,
348			traits::{ConstBool, ConstU32},
349		};
350		use pezframe_system::EnsureSigned;
351		use pezsp_core::parameter_types;
352
353		type Balance = u64;
354
355		pub const DOLLARS: Balance = 1_000_000_000_000;
356		pub const CENTS: Balance = DOLLARS / 100;
357		pub const MILLICENTS: Balance = CENTS / 1_000;
358
359		pub const fn deposit(items: u32, bytes: u32) -> Balance {
360			items as Balance * 20 * CENTS + (bytes as Balance) * MILLICENTS
361		}
362
363		parameter_types! {
364			pub const DepositPerItem: Balance = deposit(1, 0);
365			pub const DepositPerChildTrieItem: Balance = deposit(1, 0) / 100;
366			pub const DepositPerByte: Balance = deposit(0, 1);
367			pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
368			pub const MaxEthExtrinsicWeight: FixedU128 = FixedU128::from_rational(9, 10);
369		}
370
371		/// A type providing default configurations for this pezpallet in testing environment.
372		pub struct TestDefaultConfig;
373
374		impl Time for TestDefaultConfig {
375			type Moment = u64;
376			fn now() -> Self::Moment {
377				0u64
378			}
379		}
380
381		impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
382			fn convert(w: Weight) -> T {
383				w.ref_time().into()
384			}
385		}
386
387		#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
388		impl pezframe_system::DefaultConfig for TestDefaultConfig {}
389
390		#[pezframe_support::register_default_impl(TestDefaultConfig)]
391		impl DefaultConfig for TestDefaultConfig {
392			#[inject_runtime_type]
393			type RuntimeEvent = ();
394
395			#[inject_runtime_type]
396			type RuntimeHoldReason = ();
397
398			#[inject_runtime_type]
399			type RuntimeCall = ();
400
401			#[inject_runtime_type]
402			type RuntimeOrigin = ();
403
404			type Precompiles = ();
405			type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
406			type DepositPerByte = DepositPerByte;
407			type DepositPerItem = DepositPerItem;
408			type DepositPerChildTrieItem = DepositPerChildTrieItem;
409			type Time = Self;
410			type UnsafeUnstableInterface = ConstBool<true>;
411			type AllowEVMBytecode = ConstBool<true>;
412			type UploadOrigin = EnsureSigned<Self::AccountId>;
413			type InstantiateOrigin = EnsureSigned<Self::AccountId>;
414			type WeightInfo = ();
415			type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
416			type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
417			type ChainId = ConstU64<42>;
418			type NativeToEthRatio = ConstU32<1_000_000>;
419			type FindAuthor = ();
420			type FeeInfo = ();
421			type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
422			type DebugEnabled = ConstBool<false>;
423		}
424	}
425
426	#[pezpallet::event]
427	pub enum Event<T: Config> {
428		/// A custom event emitted by the contract.
429		ContractEmitted {
430			/// The contract that emitted the event.
431			contract: H160,
432			/// Data supplied by the contract. Metadata generated during contract compilation
433			/// is needed to decode it.
434			data: Vec<u8>,
435			/// A list of topics used to index the event.
436			/// Number of topics is capped by [`limits::NUM_EVENT_TOPICS`].
437			topics: Vec<H256>,
438		},
439
440		/// Contract deployed by deployer at the specified address.
441		Instantiated { deployer: H160, contract: H160 },
442
443		/// Emitted when an Ethereum transaction reverts.
444		///
445		/// Ethereum transactions always complete successfully at the extrinsic level,
446		/// as even reverted calls must store their `ReceiptInfo`.
447		/// To distinguish reverted calls from successful ones, this event is emitted
448		/// for failed Ethereum transactions.
449		EthExtrinsicRevert { dispatch_error: DispatchError },
450	}
451
452	#[pezpallet::error]
453	#[repr(u8)]
454	pub enum Error<T> {
455		/// Invalid schedule supplied, e.g. with zero weight of a basic operation.
456		InvalidSchedule = 0x01,
457		/// Invalid combination of flags supplied to `seal_call` or `seal_delegate_call`.
458		InvalidCallFlags = 0x02,
459		/// The executed contract exhausted its gas limit.
460		OutOfGas = 0x03,
461		/// Performing the requested transfer failed. Probably because there isn't enough
462		/// free balance in the sender's account.
463		TransferFailed = 0x04,
464		/// Performing a call was denied because the calling depth reached the limit
465		/// of what is specified in the schedule.
466		MaxCallDepthReached = 0x05,
467		/// No contract was found at the specified address.
468		ContractNotFound = 0x06,
469		/// No code could be found at the supplied code hash.
470		CodeNotFound = 0x07,
471		/// No code info could be found at the supplied code hash.
472		CodeInfoNotFound = 0x08,
473		/// A buffer outside of sandbox memory was passed to a contract API function.
474		OutOfBounds = 0x09,
475		/// Input passed to a contract API function failed to decode as expected type.
476		DecodingFailed = 0x0A,
477		/// Contract trapped during execution.
478		ContractTrapped = 0x0B,
479		/// Event body or storage item exceeds [`limits::STORAGE_BYTES`].
480		ValueTooLarge = 0x0C,
481		/// Termination of a contract is not allowed while the contract is already
482		/// on the call stack. Can be triggered by `seal_terminate`.
483		TerminatedWhileReentrant = 0x0D,
484		/// `seal_call` forwarded this contracts input. It therefore is no longer available.
485		InputForwarded = 0x0E,
486		/// The amount of topics passed to `seal_deposit_events` exceeds the limit.
487		TooManyTopics = 0x0F,
488		/// A contract with the same AccountId already exists.
489		DuplicateContract = 0x12,
490		/// A contract self destructed in its constructor.
491		///
492		/// This can be triggered by a call to `seal_terminate`.
493		TerminatedInConstructor = 0x13,
494		/// A call tried to invoke a contract that is flagged as non-reentrant.
495		ReentranceDenied = 0x14,
496		/// A contract called into the runtime which then called back into this pezpallet.
497		ReenteredPallet = 0x15,
498		/// A contract attempted to invoke a state modifying API while being in read-only mode.
499		StateChangeDenied = 0x16,
500		/// Origin doesn't have enough balance to pay the required storage deposits.
501		StorageDepositNotEnoughFunds = 0x17,
502		/// More storage was created than allowed by the storage deposit limit.
503		StorageDepositLimitExhausted = 0x18,
504		/// Code removal was denied because the code is still in use by at least one contract.
505		CodeInUse = 0x19,
506		/// The contract ran to completion but decided to revert its storage changes.
507		/// Please note that this error is only returned from extrinsics. When called directly
508		/// or via RPC an `Ok` will be returned. In this case the caller needs to inspect the flags
509		/// to determine whether a reversion has taken place.
510		ContractReverted = 0x1A,
511		/// The contract failed to compile or is missing the correct entry points.
512		///
513		/// A more detailed error can be found on the node console if debug messages are enabled
514		/// by supplying `-lruntime::revive=debug`.
515		CodeRejected = 0x1B,
516		/// The code blob supplied is larger than [`limits::code::BLOB_BYTES`].
517		BlobTooLarge = 0x1C,
518		/// The contract declares too much memory (ro + rw + stack).
519		StaticMemoryTooLarge = 0x1D,
520		/// The program contains a basic block that is larger than allowed.
521		BasicBlockTooLarge = 0x1E,
522		/// The program contains an invalid instruction.
523		InvalidInstruction = 0x1F,
524		/// The contract has reached its maximum number of delegate dependencies.
525		MaxDelegateDependenciesReached = 0x20,
526		/// The dependency was not found in the contract's delegate dependencies.
527		DelegateDependencyNotFound = 0x21,
528		/// The contract already depends on the given delegate dependency.
529		DelegateDependencyAlreadyExists = 0x22,
530		/// Can not add a delegate dependency to the code hash of the contract itself.
531		CannotAddSelfAsDelegateDependency = 0x23,
532		/// Can not add more data to transient storage.
533		OutOfTransientStorage = 0x24,
534		/// The contract tried to call a syscall which does not exist (at its current api level).
535		InvalidSyscall = 0x25,
536		/// Invalid storage flags were passed to one of the storage syscalls.
537		InvalidStorageFlags = 0x26,
538		/// PolkaVM failed during code execution. Probably due to a malformed program.
539		ExecutionFailed = 0x27,
540		/// Failed to convert a U256 to a Balance.
541		BalanceConversionFailed = 0x28,
542		/// Immutable data can only be set during deploys and only be read during calls.
543		/// Additionally, it is only valid to set the data once and it must not be empty.
544		InvalidImmutableAccess = 0x2A,
545		/// An `AccountID32` account tried to interact with the pezpallet without having a mapping.
546		///
547		/// Call [`Pezpallet::map_account`] in order to create a mapping for the account.
548		AccountUnmapped = 0x2B,
549		/// Tried to map an account that is already mapped.
550		AccountAlreadyMapped = 0x2C,
551		/// The transaction used to dry-run a contract is invalid.
552		InvalidGenericTransaction = 0x2D,
553		/// The refcount of a code either over or underflowed.
554		RefcountOverOrUnderflow = 0x2E,
555		/// Unsupported precompile address.
556		UnsupportedPrecompileAddress = 0x2F,
557		/// The calldata exceeds [`limits::CALLDATA_BYTES`].
558		CallDataTooLarge = 0x30,
559		/// The return data exceeds [`limits::CALLDATA_BYTES`].
560		ReturnDataTooLarge = 0x31,
561		/// Invalid jump destination. Dynamic jumps points to invalid not jumpdest opcode.
562		InvalidJump = 0x32,
563		/// Attempting to pop a value from an empty stack.
564		StackUnderflow = 0x33,
565		/// Attempting to push a value onto a full stack.
566		StackOverflow = 0x34,
567		/// Too much deposit was drawn from the shared txfee and deposit credit.
568		///
569		/// This happens if the passed `gas` inside the ethereum transaction is too low.
570		TxFeeOverdraw = 0x35,
571		/// When calling an EVM constructor `data` has to be empty.
572		///
573		/// EVM constructors do not accept data. Their input data is part of the code blob itself.
574		EvmConstructorNonEmptyData = 0x36,
575		/// Tried to construct an EVM contract via code hash.
576		///
577		/// EVM contracts can only be instantiated via code upload as no initcode is
578		/// stored on-chain.
579		EvmConstructedFromHash = 0x37,
580		/// The contract does not have enough balance to refund the storage deposit.
581		///
582		/// This is a bug and should never happen. It means the accounting got out of sync.
583		StorageRefundNotEnoughFunds = 0x38,
584		/// This means there are locks on the contracts storage deposit that prevents refunding it.
585		///
586		/// This would be the case if the contract used its storage deposits for governance
587		/// or other pallets that allow creating locks over held balance.
588		StorageRefundLocked = 0x39,
589		/// Called a pre-compile that is not allowed to be delegate called.
590		///
591		/// Some pre-compile functions will trap the caller context if being delegate
592		/// called or if their caller was being delegate called.
593		PrecompileDelegateDenied = 0x40,
594		/// Benchmarking only error.
595		#[cfg(feature = "runtime-benchmarks")]
596		BenchmarkingError = 0xFF,
597	}
598
599	/// A reason for the pezpallet revive placing a hold on funds.
600	#[pezpallet::composite_enum]
601	pub enum HoldReason {
602		/// The Pezpallet has reserved it for storing code on-chain.
603		CodeUploadDepositReserve,
604		/// The Pezpallet has reserved it for storage deposit.
605		StorageDepositReserve,
606		/// Deposit for creating an address mapping in [`OriginalAccount`].
607		AddressMapping,
608	}
609
610	#[derive(
611		PartialEq,
612		Eq,
613		Clone,
614		MaxEncodedLen,
615		Encode,
616		Decode,
617		DecodeWithMemTracking,
618		TypeInfo,
619		RuntimeDebug,
620	)]
621	#[pezpallet::origin]
622	pub enum Origin<T: Config> {
623		EthTransaction(T::AccountId),
624	}
625
626	/// A mapping from a contract's code hash to its code.
627	/// The code's size is bounded by [`crate::limits::BLOB_BYTES`] for PVM and
628	/// [`revm::primitives::eip170::MAX_CODE_SIZE`] for EVM bytecode.
629	#[pezpallet::storage]
630	#[pezpallet::unbounded]
631	pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, Vec<u8>>;
632
633	/// A mapping from a contract's code hash to its code info.
634	#[pezpallet::storage]
635	pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
636
637	/// The data associated to a contract or externally owned account.
638	#[pezpallet::storage]
639	pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
640
641	/// The immutable data associated with a given account.
642	#[pezpallet::storage]
643	pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
644
645	/// Evicted contracts that await child trie deletion.
646	///
647	/// Child trie deletion is a heavy operation depending on the amount of storage items
648	/// stored in said trie. Therefore this operation is performed lazily in `on_idle`.
649	#[pezpallet::storage]
650	pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
651
652	/// A pair of monotonic counters used to track the latest contract marked for deletion
653	/// and the latest deleted contract in queue.
654	#[pezpallet::storage]
655	pub(crate) type DeletionQueueCounter<T: Config> =
656		StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
657
658	/// Map a Ethereum address to its original `AccountId32`.
659	///
660	/// When deriving a `H160` from an `AccountId32` we use a hash function. In order to
661	/// reconstruct the original account we need to store the reverse mapping here.
662	/// Register your `AccountId32` using [`Pezpallet::map_account`] in order to
663	/// use it with this pezpallet.
664	#[pezpallet::storage]
665	pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
666
667	/// The current Ethereum block that is stored in the `on_finalize` method.
668	///
669	/// # Note
670	///
671	/// This could be further optimized into the future to store only the minimum
672	/// information needed to reconstruct the Ethereum block at the RPC level.
673	///
674	/// Since the block is convenient to have around, and the extra details are capped
675	/// by a few hashes and the vector of transaction hashes, we store the block here.
676	#[pezpallet::storage]
677	#[pezpallet::unbounded]
678	pub(crate) type EthereumBlock<T> = StorageValue<_, EthBlock, ValueQuery>;
679
680	/// Mapping for block number and hashes.
681	///
682	/// The maximum number of elements stored is capped by the block hash count `BLOCK_HASH_COUNT`.
683	#[pezpallet::storage]
684	pub(crate) type BlockHash<T: Config> =
685		StorageMap<_, Identity, BlockNumberFor<T>, H256, ValueQuery>;
686
687	/// The details needed to reconstruct the receipt info offchain.
688	///
689	/// This contains valuable information about the gas used by the transaction.
690	///
691	/// NOTE: The item is unbound and should therefore never be read on chain.
692	/// It could otherwise inflate the PoV size of a block.
693	#[pezpallet::storage]
694	#[pezpallet::unbounded]
695	pub(crate) type ReceiptInfoData<T: Config> = StorageValue<_, Vec<ReceiptGasInfo>, ValueQuery>;
696
697	/// Incremental ethereum block builder.
698	#[pezpallet::storage]
699	#[pezpallet::unbounded]
700	pub(crate) type EthBlockBuilderIR<T: Config> =
701		StorageValue<_, EthereumBlockBuilderIR<T>, ValueQuery>;
702
703	/// The first transaction and receipt of the ethereum block.
704	///
705	/// These values are moved out of the `EthBlockBuilderIR` to avoid serializing and
706	/// deserializing them on every transaction. Instead, they are loaded when needed.
707	#[pezpallet::storage]
708	#[pezpallet::unbounded]
709	pub(crate) type EthBlockBuilderFirstValues<T: Config> =
710		StorageValue<_, Option<(Vec<u8>, Vec<u8>)>, ValueQuery>;
711
712	/// Debugging settings that can be configured when DebugEnabled config is true.
713	#[pezpallet::storage]
714	pub(crate) type DebugSettingsOf<T: Config> = StorageValue<_, DebugSettings, ValueQuery>;
715
716	pub mod genesis {
717		use super::*;
718		use crate::evm::Bytes32;
719
720		/// Genesis configuration for contract-specific data.
721		#[derive(Clone, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
722		pub struct ContractData {
723			/// Contract code.
724			pub code: Vec<u8>,
725			/// Initial storage entries as 32-byte key/value pairs.
726			pub storage: alloc::collections::BTreeMap<Bytes32, Bytes32>,
727		}
728
729		/// Genesis configuration for a contract account.
730		#[derive(PartialEq, Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
731		pub struct Account<T: Config> {
732			/// Contract address.
733			pub address: H160,
734			/// Contract balance.
735			#[serde(default)]
736			pub balance: U256,
737			/// Account nonce
738			#[serde(default)]
739			pub nonce: T::Nonce,
740			/// Contract-specific data (code and storage). None for EOAs.
741			#[serde(flatten, skip_serializing_if = "Option::is_none")]
742			pub contract_data: Option<ContractData>,
743		}
744	}
745
746	#[pezpallet::genesis_config]
747	#[derive(Debug, PartialEq, pezframe_support::DefaultNoBound)]
748	pub struct GenesisConfig<T: Config> {
749		/// List of native Bizinikiwi accounts (typically `AccountId32`) to be mapped at genesis
750		/// block, enabling them to interact with smart contracts.
751		#[serde(default, skip_serializing_if = "Vec::is_empty")]
752		pub mapped_accounts: Vec<T::AccountId>,
753
754		/// Account entries (both EOAs and contracts)
755		#[serde(default, skip_serializing_if = "Vec::is_empty")]
756		pub accounts: Vec<genesis::Account<T>>,
757
758		/// Optional debugging settings applied at genesis.
759		#[serde(default, skip_serializing_if = "Option::is_none")]
760		pub debug_settings: Option<DebugSettings>,
761	}
762
763	#[pezpallet::genesis_build]
764	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
765		fn build(&self) {
766			use crate::{exec::Key, vm::ContractBlob};
767			use pezframe_support::traits::fungible::Mutate;
768
769			if !System::<T>::account_exists(&Pezpallet::<T>::account_id()) {
770				let _ = T::Currency::mint_into(
771					&Pezpallet::<T>::account_id(),
772					T::Currency::minimum_balance(),
773				);
774			}
775
776			for id in &self.mapped_accounts {
777				if let Err(err) = T::AddressMapper::map_no_deposit(id) {
778					log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
779				}
780			}
781
782			let owner = Pezpallet::<T>::account_id();
783
784			for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
785				let account_id = T::AddressMapper::to_account_id(address);
786
787				if !System::<T>::account_exists(&account_id) {
788					let _ = T::Currency::mint_into(&account_id, T::Currency::minimum_balance());
789				}
790
791				pezframe_system::Account::<T>::mutate(&account_id, |info| {
792					info.nonce = (*nonce).into();
793				});
794
795				match contract_data {
796					None => {
797						AccountInfoOf::<T>::insert(
798							address,
799							AccountInfo { account_type: AccountType::EOA, dust: 0 },
800						);
801					},
802					Some(genesis::ContractData { code, storage }) => {
803						let blob = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
804							ContractBlob::<T>::from_pvm_code(   code.clone(), owner.clone()).inspect_err(|err| {
805								log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
806							})
807						} else {
808							ContractBlob::<T>::from_evm_runtime_code(code.clone(), account_id).inspect_err(|err| {
809								log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
810							})
811						};
812
813						let Ok(blob) = blob else {
814							continue;
815						};
816
817						let code_hash = *blob.code_hash();
818						let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
819							.inspect_err(|err| {
820								log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
821							})
822						else {
823							continue;
824						};
825
826						AccountInfoOf::<T>::insert(
827							address,
828							AccountInfo { account_type: info.clone().into(), dust: 0 },
829						);
830
831						<PristineCode<T>>::insert(blob.code_hash(), code);
832						<CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
833						for (k, v) in storage {
834							let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
835								log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
836							});
837						}
838					},
839				}
840
841				let _ = Pezpallet::<T>::set_evm_balance(address, *balance).inspect_err(|err| {
842					log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}");
843				});
844			}
845
846			// Build genesis block
847			block_storage::on_finalize_build_eth_block::<T>(
848				// Make sure to use the block number from storage instead of the hardcoded 0.
849				// This enables testing tools like anvil to customise the genesis block number.
850				pezframe_system::Pezpallet::<T>::block_number(),
851			);
852
853			// Set debug settings.
854			if let Some(settings) = self.debug_settings.as_ref() {
855				settings.write_to_storage::<T>()
856			}
857		}
858	}
859
860	#[pezpallet::hooks]
861	impl<T: Config> Hooks<BlockNumberFor<T>> for Pezpallet<T> {
862		fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
863			let mut meter = WeightMeter::with_limit(limit);
864			ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
865			meter.consumed()
866		}
867
868		fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
869			// Kill related ethereum block storage items.
870			block_storage::on_initialize::<T>();
871
872			// Warm up the pezpallet account.
873			System::<T>::account_exists(&Pezpallet::<T>::account_id());
874			// Account for the fixed part of the costs incurred in `on_finalize`.
875			<T as Config>::WeightInfo::on_finalize_block_fixed()
876		}
877
878		fn on_finalize(block_number: BlockNumberFor<T>) {
879			// Build the ethereum block and place it in storage.
880			block_storage::on_finalize_build_eth_block::<T>(block_number);
881		}
882
883		fn integrity_test() {
884			assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
885
886			T::FeeInfo::integrity_test();
887
888			// The memory available in the block building runtime
889			let max_runtime_mem: u64 = T::RuntimeMemory::get().into();
890
891			// We only allow 50% of the runtime memory to be utilized by the contracts call
892			// stack, keeping the rest for other facilities, such as PoV, etc.
893			const TOTAL_MEMORY_DEVIDER: u64 = 2;
894
895			// Validators are configured to be able to use more memory than block builders. This is
896			// because in addition to `max_runtime_mem` they need to hold additional data in
897			// memory: PoV in multiple copies (1x encoded + 2x decoded) and all storage which
898			// includes emitted events. The assumption is that storage/events size
899			// can be a maximum of half of the validator runtime memory - max_runtime_mem.
900			let max_block_weight = T::BlockWeights::get()
901				.get(DispatchClass::Normal)
902				.max_total
903				.unwrap_or_else(|| T::BlockWeights::get().max_block);
904			let max_key_size: u64 =
905				Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
906					.expect("Key of maximal size shall be created")
907					.hash()
908					.len()
909					.try_into()
910					.unwrap();
911
912			let max_immutable_key_size: u64 = T::AccountId::max_encoded_len().try_into().unwrap();
913			let max_immutable_size: u64 = max_block_weight
914				.checked_div_per_component(&<RuntimeCosts as gas::Token<T>>::weight(
915					&RuntimeCosts::SetImmutableData(limits::IMMUTABLE_BYTES),
916				))
917				.unwrap()
918				.saturating_mul(
919					u64::from(limits::IMMUTABLE_BYTES)
920						.saturating_add(max_immutable_key_size)
921						.into(),
922				);
923
924			let max_pvf_mem: u64 = T::PVFMemory::get().into();
925			let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
926
927			// We can use storage to store events using the available block ref_time with the
928			// `deposit_event` host function. The overhead of stored events, which is around 100B,
929			// is not taken into account to simplify calculations, as it does not change much.
930			let max_events_size = max_block_weight
931				.checked_div_per_component(
932					&(<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::DepositEvent {
933						num_topic: 0,
934						len: limits::EVENT_BYTES,
935					})
936					.saturating_add(<RuntimeCosts as gas::Token<T>>::weight(
937						&RuntimeCosts::HostFn,
938					))),
939				)
940				.unwrap()
941				.saturating_mul(limits::EVENT_BYTES.into());
942
943			assert!(
944				max_events_size < storage_size_limit,
945				"Maximal events size {} exceeds the events limit {}",
946				max_events_size,
947				storage_size_limit
948			);
949
950			// The incremental block builder uses 3 x maximum entry size for receipts and
951			// for transactions. Transactions are bounded to `MAX_TRANSACTION_PAYLOAD_SIZE`.
952			//
953			// To determine the maximum size of the receipts, we know the following:
954			// - (I) first receipt is stored into pezpallet storage and not given to the hasher
955			//   until finalization.
956			// - (II) the hasher will not consume more memory than the receipts we are giving it.
957			// - (III) the hasher is capped by 3 x maximum entry for 3 or more transactions.
958			//
959			// # Case 1. One transaction with maximum receipts
960			//
961			// The worst case scenario for having one single transaction is for the transaction
962			// to emit the maximum receipt size (ie `max_events_size`). In this case,
963			// the maximum storage (and memory) consumed is bounded by `max_events_size` (II). The
964			// receipt is stored in pezpallet storage, and loaded from storage in the
965			// `on_finalize` hook (I).
966			//
967			// # Case 2. Two transactions
968			//
969			// The sum of the receipt size of both transactions cannot exceed `max_events_size`,
970			// otherwise one transaction will be reverted. From (II), the bytes utilized
971			// by the builder are capped to `max_events_size`.
972			//
973			// # Case 3. Three or more transactions
974			//
975			// Similar to the above case, the sum of all receipt size is bounded to
976			// `max_events_size`. Therefore, the bytes are capped to `max_events_size`.
977			//
978			// On average, a transaction could emit `max_events_size / num_tx`. The would
979			// consume `max_events_size / num_tx * 3` bytes, which is lower than
980			// `max_events_size` for more than 3 transactions.
981			//
982			// In practice, the builder will consume even lower amounts considering
983			// it is unlikely for a transaction to utilize all the weight of the block for events.
984			let max_eth_block_builder_bytes =
985				block_storage::block_builder_bytes_usage(max_events_size.try_into().unwrap());
986
987			log::debug!(
988				target: LOG_TARGET,
989				"Integrity check: max_eth_block_builder_bytes={} KB using max_events_size={} KB",
990				max_eth_block_builder_bytes / 1024,
991				max_events_size / 1024,
992			);
993
994			// Check that the configured memory limits fit into runtime memory.
995			//
996			// Dynamic allocations are not available, yet. Hence they are not taken into
997			// consideration here.
998			let memory_left = i128::from(max_runtime_mem)
999				.saturating_div(TOTAL_MEMORY_DEVIDER.into())
1000				.saturating_sub(limits::MEMORY_REQUIRED.into())
1001				.saturating_sub(max_eth_block_builder_bytes.into());
1002
1003			log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
1004
1005			assert!(
1006				memory_left >= 0,
1007				"Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
1008				memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
1009			);
1010
1011			// We can use storage to store items using the available block ref_time with the
1012			// `set_storage` host function.
1013			let max_storage_size = max_block_weight
1014				.checked_div_per_component(
1015					&<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetStorage {
1016						new_bytes: limits::STORAGE_BYTES,
1017						old_bytes: 0,
1018					})
1019					.saturating_mul(u64::from(limits::STORAGE_BYTES).saturating_add(max_key_size)),
1020				)
1021				.unwrap()
1022				.saturating_add(max_immutable_size.into())
1023				.saturating_add(max_eth_block_builder_bytes.into());
1024
1025			assert!(
1026				max_storage_size < storage_size_limit,
1027				"Maximal storage size {} exceeds the storage limit {}",
1028				max_storage_size,
1029				storage_size_limit
1030			);
1031		}
1032	}
1033
1034	#[pezpallet::call]
1035	impl<T: Config> Pezpallet<T> {
1036		/// A raw EVM transaction, typically dispatched by an Ethereum JSON-RPC server.
1037		///
1038		/// # Parameters
1039		///
1040		/// * `payload`: The encoded [`crate::evm::TransactionSigned`].
1041		///
1042		/// # Note
1043		///
1044		/// This call cannot be dispatched directly; attempting to do so will result in a failed
1045		/// transaction. It serves as a wrapper for an Ethereum transaction. When submitted, the
1046		/// runtime converts it into a [`pezsp_runtime::generic::CheckedExtrinsic`] by recovering
1047		/// the signer and validating the transaction.
1048		#[allow(unused_variables)]
1049		#[pezpallet::call_index(0)]
1050		#[pezpallet::weight(Weight::MAX)]
1051		pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
1052			Err(pezframe_system::Error::CallFiltered::<T>.into())
1053		}
1054
1055		/// Makes a call to an account, optionally transferring some balance.
1056		///
1057		/// # Parameters
1058		///
1059		/// * `dest`: Address of the contract to call.
1060		/// * `value`: The balance to transfer from the `origin` to `dest`.
1061		/// * `gas_limit`: The gas limit enforced when executing the constructor.
1062		/// * `storage_deposit_limit`: The maximum amount of balance that can be charged from the
1063		///   caller to pay for the storage consumed.
1064		/// * `data`: The input data to pass to the contract.
1065		///
1066		/// * If the account is a smart-contract account, the associated code will be
1067		/// executed and any value will be transferred.
1068		/// * If the account is a regular account, any value will be transferred.
1069		/// * If no account exists and the call value is not less than `existential_deposit`,
1070		/// a regular account will be created and any value will be transferred.
1071		#[pezpallet::call_index(1)]
1072		#[pezpallet::weight(<T as Config>::WeightInfo::call().saturating_add(*gas_limit))]
1073		pub fn call(
1074			origin: OriginFor<T>,
1075			dest: H160,
1076			#[pezpallet::compact] value: BalanceOf<T>,
1077			gas_limit: Weight,
1078			#[pezpallet::compact] storage_deposit_limit: BalanceOf<T>,
1079			data: Vec<u8>,
1080		) -> DispatchResultWithPostInfo {
1081			Self::ensure_non_contract_if_signed(&origin)?;
1082			let mut output = Self::bare_call(
1083				origin,
1084				dest,
1085				Pezpallet::<T>::convert_native_to_evm(value),
1086				gas_limit,
1087				storage_deposit_limit,
1088				data,
1089				ExecConfig::new_bizinikiwi_tx(),
1090			);
1091
1092			if let Ok(return_value) = &output.result {
1093				if return_value.did_revert() {
1094					output.result = Err(<Error<T>>::ContractReverted.into());
1095				}
1096			}
1097			dispatch_result(output.result, output.gas_consumed, <T as Config>::WeightInfo::call())
1098		}
1099
1100		/// Instantiates a contract from a previously deployed vm binary.
1101		///
1102		/// This function is identical to [`Self::instantiate_with_code`] but without the
1103		/// code deployment step. Instead, the `code_hash` of an on-chain deployed vm binary
1104		/// must be supplied.
1105		#[pezpallet::call_index(2)]
1106		#[pezpallet::weight(
1107			<T as Config>::WeightInfo::instantiate(data.len() as u32).saturating_add(*gas_limit)
1108		)]
1109		pub fn instantiate(
1110			origin: OriginFor<T>,
1111			#[pezpallet::compact] value: BalanceOf<T>,
1112			gas_limit: Weight,
1113			#[pezpallet::compact] storage_deposit_limit: BalanceOf<T>,
1114			code_hash: pezsp_core::H256,
1115			data: Vec<u8>,
1116			salt: Option<[u8; 32]>,
1117		) -> DispatchResultWithPostInfo {
1118			Self::ensure_non_contract_if_signed(&origin)?;
1119			let data_len = data.len() as u32;
1120			let mut output = Self::bare_instantiate(
1121				origin,
1122				Pezpallet::<T>::convert_native_to_evm(value),
1123				gas_limit,
1124				storage_deposit_limit,
1125				Code::Existing(code_hash),
1126				data,
1127				salt,
1128				ExecConfig::new_bizinikiwi_tx(),
1129			);
1130			if let Ok(retval) = &output.result {
1131				if retval.result.did_revert() {
1132					output.result = Err(<Error<T>>::ContractReverted.into());
1133				}
1134			}
1135			dispatch_result(
1136				output.result.map(|result| result.result),
1137				output.gas_consumed,
1138				<T as Config>::WeightInfo::instantiate(data_len),
1139			)
1140		}
1141
1142		/// Instantiates a new contract from the supplied `code` optionally transferring
1143		/// some balance.
1144		///
1145		/// This dispatchable has the same effect as calling [`Self::upload_code`] +
1146		/// [`Self::instantiate`]. Bundling them together provides efficiency gains. Please
1147		/// also check the documentation of [`Self::upload_code`].
1148		///
1149		/// # Parameters
1150		///
1151		/// * `value`: The balance to transfer from the `origin` to the newly created contract.
1152		/// * `gas_limit`: The gas limit enforced when executing the constructor.
1153		/// * `storage_deposit_limit`: The maximum amount of balance that can be charged/reserved
1154		///   from the caller to pay for the storage consumed.
1155		/// * `code`: The contract code to deploy in raw bytes.
1156		/// * `data`: The input data to pass to the contract constructor.
1157		/// * `salt`: Used for the address derivation. If `Some` is supplied then `CREATE2`
1158		/// 	semantics are used. If `None` then `CRATE1` is used.
1159		///
1160		///
1161		/// Instantiation is executed as follows:
1162		///
1163		/// - The supplied `code` is deployed, and a `code_hash` is created for that code.
1164		/// - If the `code_hash` already exists on the chain the underlying `code` will be shared.
1165		/// - The destination address is computed based on the sender, code_hash and the salt.
1166		/// - The smart-contract account is created at the computed address.
1167		/// - The `value` is transferred to the new account.
1168		/// - The `deploy` function is executed in the context of the newly-created account.
1169		#[pezpallet::call_index(3)]
1170		#[pezpallet::weight(
1171			<T as Config>::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
1172			.saturating_add(*gas_limit)
1173		)]
1174		pub fn instantiate_with_code(
1175			origin: OriginFor<T>,
1176			#[pezpallet::compact] value: BalanceOf<T>,
1177			gas_limit: Weight,
1178			#[pezpallet::compact] storage_deposit_limit: BalanceOf<T>,
1179			code: Vec<u8>,
1180			data: Vec<u8>,
1181			salt: Option<[u8; 32]>,
1182		) -> DispatchResultWithPostInfo {
1183			Self::ensure_non_contract_if_signed(&origin)?;
1184			let code_len = code.len() as u32;
1185			let data_len = data.len() as u32;
1186			let mut output = Self::bare_instantiate(
1187				origin,
1188				Pezpallet::<T>::convert_native_to_evm(value),
1189				gas_limit,
1190				storage_deposit_limit,
1191				Code::Upload(code),
1192				data,
1193				salt,
1194				ExecConfig::new_bizinikiwi_tx(),
1195			);
1196			if let Ok(retval) = &output.result {
1197				if retval.result.did_revert() {
1198					output.result = Err(<Error<T>>::ContractReverted.into());
1199				}
1200			}
1201			dispatch_result(
1202				output.result.map(|result| result.result),
1203				output.gas_consumed,
1204				<T as Config>::WeightInfo::instantiate_with_code(code_len, data_len),
1205			)
1206		}
1207
1208		/// Same as [`Self::instantiate_with_code`], but intended to be dispatched **only**
1209		/// by an EVM transaction through the EVM compatibility layer.
1210		///
1211		/// # Parameters
1212		///
1213		/// * `value`: The balance to transfer from the `origin` to the newly created contract.
1214		/// * `gas_limit`: The gas limit enforced when executing the constructor.
1215		/// * `storage_deposit_limit`: The maximum amount of balance that can be charged/reserved
1216		///   from the caller to pay for the storage consumed.
1217		/// * `code`: The contract code to deploy in raw bytes.
1218		/// * `data`: The input data to pass to the contract constructor.
1219		/// * `salt`: Used for the address derivation. If `Some` is supplied then `CREATE2`
1220		/// 	semantics are used. If `None` then `CRATE1` is used.
1221		/// * `transaction_encoded`: The RLP encoding of the signed Ethereum transaction,
1222		///   represented as [crate::evm::TransactionSigned], provided by the Ethereum wallet. This
1223		///   is used for building the Ethereum transaction root.
1224		///
1225		/// Calling this dispatchable ensures that the origin's nonce is bumped only once,
1226		/// via the `CheckNonce` transaction extension. In contrast, [`Self::instantiate_with_code`]
1227		/// also bumps the nonce after contract instantiation, since it may be invoked multiple
1228		/// times within a batch call transaction.
1229		#[pezpallet::call_index(10)]
1230		#[pezpallet::weight(
1231			<T as Config>::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pezpallet::<T>::has_dust(*value).into())
1232			.saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1233			.saturating_add(*gas_limit)
1234		)]
1235		pub fn eth_instantiate_with_code(
1236			origin: OriginFor<T>,
1237			value: U256,
1238			gas_limit: Weight,
1239			code: Vec<u8>,
1240			data: Vec<u8>,
1241			transaction_encoded: Vec<u8>,
1242			effective_gas_price: U256,
1243			encoded_len: u32,
1244		) -> DispatchResultWithPostInfo {
1245			let signer = Self::ensure_eth_signed(origin)?;
1246			let origin = OriginFor::<T>::signed(signer.clone());
1247			Self::ensure_non_contract_if_signed(&origin)?;
1248			let mut call = Call::<T>::eth_instantiate_with_code {
1249				value,
1250				gas_limit,
1251				code: code.clone(),
1252				data: data.clone(),
1253				transaction_encoded: transaction_encoded.clone(),
1254				effective_gas_price,
1255				encoded_len,
1256			}
1257			.into();
1258			let info = T::FeeInfo::dispatch_info(&call);
1259			let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1260			drop(call);
1261
1262			block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1263				let output = Self::bare_instantiate(
1264					origin,
1265					value,
1266					gas_limit,
1267					BalanceOf::<T>::max_value(),
1268					Code::Upload(code),
1269					data,
1270					None,
1271					ExecConfig::new_eth_tx(
1272						effective_gas_price,
1273						encoded_len,
1274						base_info.total_weight(),
1275					),
1276				);
1277
1278				block_storage::EthereumCallResult::new::<T>(
1279					signer,
1280					output.map_result(|r| r.result),
1281					base_info.call_weight,
1282					encoded_len,
1283					&info,
1284					effective_gas_price,
1285				)
1286			})
1287		}
1288
1289		/// Same as [`Self::call`], but intended to be dispatched **only**
1290		/// by an EVM transaction through the EVM compatibility layer.
1291		#[pezpallet::call_index(11)]
1292		#[pezpallet::weight(
1293		    T::WeightInfo::eth_call(Pezpallet::<T>::has_dust(*value).into())
1294				.saturating_add(*gas_limit)
1295				.saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1296		)]
1297		pub fn eth_call(
1298			origin: OriginFor<T>,
1299			dest: H160,
1300			value: U256,
1301			gas_limit: Weight,
1302			data: Vec<u8>,
1303			transaction_encoded: Vec<u8>,
1304			effective_gas_price: U256,
1305			encoded_len: u32,
1306		) -> DispatchResultWithPostInfo {
1307			let signer = Self::ensure_eth_signed(origin)?;
1308			let origin = OriginFor::<T>::signed(signer.clone());
1309
1310			Self::ensure_non_contract_if_signed(&origin)?;
1311			let mut call = Call::<T>::eth_call {
1312				dest,
1313				value,
1314				gas_limit,
1315				data: data.clone(),
1316				transaction_encoded: transaction_encoded.clone(),
1317				effective_gas_price,
1318				encoded_len,
1319			}
1320			.into();
1321			let info = T::FeeInfo::dispatch_info(&call);
1322			let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1323			drop(call);
1324
1325			block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1326				let output = Self::bare_call(
1327					origin,
1328					dest,
1329					value,
1330					gas_limit,
1331					BalanceOf::<T>::max_value(),
1332					data,
1333					ExecConfig::new_eth_tx(
1334						effective_gas_price,
1335						encoded_len,
1336						base_info.total_weight(),
1337					),
1338				);
1339
1340				block_storage::EthereumCallResult::new::<T>(
1341					signer,
1342					output,
1343					base_info.call_weight,
1344					encoded_len,
1345					&info,
1346					effective_gas_price,
1347				)
1348			})
1349		}
1350
1351		/// Executes a Bizinikiwi runtime call from an Ethereum transaction.
1352		///
1353		/// This dispatchable is intended to be called **only** through the EVM compatibility
1354		/// layer. The provided call will be dispatched using `RawOrigin::Signed`.
1355		///
1356		/// # Parameters
1357		///
1358		/// * `origin`: Must be an [`Origin::EthTransaction`] origin.
1359		/// * `call`: The Bizinikiwi runtime call to execute.
1360		/// * `transaction_encoded`: The RLP encoding of the Ethereum transaction,
1361		#[pezpallet::call_index(12)]
1362		#[pezpallet::weight(T::WeightInfo::eth_bizinikiwi_call(transaction_encoded.len() as u32).saturating_add(call.get_dispatch_info().call_weight))]
1363		pub fn eth_bizinikiwi_call(
1364			origin: OriginFor<T>,
1365			call: Box<<T as Config>::RuntimeCall>,
1366			transaction_encoded: Vec<u8>,
1367		) -> DispatchResultWithPostInfo {
1368			// Note that the inner dispatch uses `RawOrigin::Signed`, which cannot
1369			// re-enter `eth_bizinikiwi_call` (which requires `Origin::EthTransaction`).
1370			let signer = Self::ensure_eth_signed(origin)?;
1371			let weight_overhead =
1372				T::WeightInfo::eth_bizinikiwi_call(transaction_encoded.len() as u32);
1373
1374			block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1375				let call_weight = call.get_dispatch_info().call_weight;
1376				let mut call_result = call.dispatch(RawOrigin::Signed(signer).into());
1377
1378				// Add extrinsic_overhead to the actual weight in PostDispatchInfo
1379				match &mut call_result {
1380					Ok(post_info) | Err(DispatchErrorWithPostInfo { post_info, .. }) => {
1381						post_info.actual_weight = Some(
1382							post_info
1383								.actual_weight
1384								.unwrap_or_else(|| call_weight)
1385								.saturating_add(weight_overhead),
1386						);
1387					},
1388				}
1389
1390				// Return zero EVM gas (Bizinikiwi dispatch, not EVM contract call).
1391				// Actual weight is in `post_info.actual_weight`.
1392				block_storage::EthereumCallResult {
1393					receipt_gas_info: ReceiptGasInfo::default(),
1394					result: call_result,
1395				}
1396			})
1397		}
1398
1399		/// Upload new `code` without instantiating a contract from it.
1400		///
1401		/// If the code does not already exist a deposit is reserved from the caller
1402		/// The size of the reserve depends on the size of the supplied `code`.
1403		///
1404		/// # Note
1405		///
1406		/// Anyone can instantiate a contract from any uploaded code and thus prevent its removal.
1407		/// To avoid this situation a constructor could employ access control so that it can
1408		/// only be instantiated by permissioned entities. The same is true when uploading
1409		/// through [`Self::instantiate_with_code`].
1410		///
1411		/// If the refcount of the code reaches zero after terminating the last contract that
1412		/// references this code, the code will be removed automatically.
1413		#[pezpallet::call_index(4)]
1414		#[pezpallet::weight(<T as Config>::WeightInfo::upload_code(code.len() as u32))]
1415		pub fn upload_code(
1416			origin: OriginFor<T>,
1417			code: Vec<u8>,
1418			#[pezpallet::compact] storage_deposit_limit: BalanceOf<T>,
1419		) -> DispatchResult {
1420			Self::ensure_non_contract_if_signed(&origin)?;
1421			Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1422		}
1423
1424		/// Remove the code stored under `code_hash` and refund the deposit to its owner.
1425		///
1426		/// A code can only be removed by its original uploader (its owner) and only if it is
1427		/// not used by any contract.
1428		#[pezpallet::call_index(5)]
1429		#[pezpallet::weight(<T as Config>::WeightInfo::remove_code())]
1430		pub fn remove_code(
1431			origin: OriginFor<T>,
1432			code_hash: pezsp_core::H256,
1433		) -> DispatchResultWithPostInfo {
1434			let origin = ensure_signed(origin)?;
1435			<ContractBlob<T>>::remove(&origin, code_hash)?;
1436			// we waive the fee because removing unused code is beneficial
1437			Ok(Pays::No.into())
1438		}
1439
1440		/// Privileged function that changes the code of an existing contract.
1441		///
1442		/// This takes care of updating refcounts and all other necessary operations. Returns
1443		/// an error if either the `code_hash` or `dest` do not exist.
1444		///
1445		/// # Note
1446		///
1447		/// This does **not** change the address of the contract in question. This means
1448		/// that the contract address is no longer derived from its code hash after calling
1449		/// this dispatchable.
1450		#[pezpallet::call_index(6)]
1451		#[pezpallet::weight(<T as Config>::WeightInfo::set_code())]
1452		pub fn set_code(
1453			origin: OriginFor<T>,
1454			dest: H160,
1455			code_hash: pezsp_core::H256,
1456		) -> DispatchResult {
1457			ensure_root(origin)?;
1458			<AccountInfoOf<T>>::try_mutate(&dest, |account| {
1459				let Some(account) = account else {
1460					return Err(<Error<T>>::ContractNotFound.into());
1461				};
1462
1463				let AccountType::Contract(ref mut contract) = account.account_type else {
1464					return Err(<Error<T>>::ContractNotFound.into());
1465				};
1466
1467				<CodeInfo<T>>::increment_refcount(code_hash)?;
1468				let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1469				contract.code_hash = code_hash;
1470
1471				Ok(())
1472			})
1473		}
1474
1475		/// Register the callers account id so that it can be used in contract interactions.
1476		///
1477		/// This will error if the origin is already mapped or is a eth native `Address20`. It will
1478		/// take a deposit that can be released by calling [`Self::unmap_account`].
1479		#[pezpallet::call_index(7)]
1480		#[pezpallet::weight(<T as Config>::WeightInfo::map_account())]
1481		pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1482			Self::ensure_non_contract_if_signed(&origin)?;
1483			let origin = ensure_signed(origin)?;
1484			T::AddressMapper::map(&origin)
1485		}
1486
1487		/// Unregister the callers account id in order to free the deposit.
1488		///
1489		/// There is no reason to ever call this function other than freeing up the deposit.
1490		/// This is only useful when the account should no longer be used.
1491		#[pezpallet::call_index(8)]
1492		#[pezpallet::weight(<T as Config>::WeightInfo::unmap_account())]
1493		pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1494			let origin = ensure_signed(origin)?;
1495			T::AddressMapper::unmap(&origin)
1496		}
1497
1498		/// Dispatch an `call` with the origin set to the callers fallback address.
1499		///
1500		/// Every `AccountId32` can control its corresponding fallback account. The fallback account
1501		/// is the `AccountId20` with the last 12 bytes set to `0xEE`. This is essentially a
1502		/// recovery function in case an `AccountId20` was used without creating a mapping first.
1503		#[pezpallet::call_index(9)]
1504		#[pezpallet::weight({
1505			let dispatch_info = call.get_dispatch_info();
1506			(
1507				<T as Config>::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1508				dispatch_info.class
1509			)
1510		})]
1511		pub fn dispatch_as_fallback_account(
1512			origin: OriginFor<T>,
1513			call: Box<<T as Config>::RuntimeCall>,
1514		) -> DispatchResultWithPostInfo {
1515			Self::ensure_non_contract_if_signed(&origin)?;
1516			let origin = ensure_signed(origin)?;
1517			let unmapped_account =
1518				T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
1519			call.dispatch(RawOrigin::Signed(unmapped_account).into())
1520		}
1521	}
1522}
1523
1524/// Create a dispatch result reflecting the amount of consumed gas.
1525fn dispatch_result<R>(
1526	result: Result<R, DispatchError>,
1527	gas_consumed: Weight,
1528	base_weight: Weight,
1529) -> DispatchResultWithPostInfo {
1530	let post_info = PostDispatchInfo {
1531		actual_weight: Some(gas_consumed.saturating_add(base_weight)),
1532		pays_fee: Default::default(),
1533	};
1534
1535	result
1536		.map(|_| post_info)
1537		.map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1538}
1539
1540impl<T: Config> Pezpallet<T> {
1541	/// A generalized version of [`Self::call`].
1542	///
1543	/// Identical to [`Self::call`] but tailored towards being called by other code within the
1544	/// runtime as opposed to from an extrinsic. It returns more information and allows the
1545	/// enablement of features that are not suitable for an extrinsic (debugging, event
1546	/// collection).
1547	pub fn bare_call(
1548		origin: OriginFor<T>,
1549		dest: H160,
1550		evm_value: U256,
1551		gas_limit: Weight,
1552		storage_deposit_limit: BalanceOf<T>,
1553		data: Vec<u8>,
1554		exec_config: ExecConfig<T>,
1555	) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1556		let mut gas_meter = GasMeter::new(gas_limit);
1557		let mut storage_deposit = Default::default();
1558
1559		let try_call = || {
1560			let origin = ExecOrigin::from_runtime_origin(origin)?;
1561			let mut storage_meter = StorageMeter::new(storage_deposit_limit);
1562			let result = ExecStack::<T, ContractBlob<T>>::run_call(
1563				origin.clone(),
1564				dest,
1565				&mut gas_meter,
1566				&mut storage_meter,
1567				evm_value,
1568				data,
1569				&exec_config,
1570			)?;
1571			storage_deposit =
1572				storage_meter.try_into_deposit(&origin, &exec_config).inspect_err(|err| {
1573					log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1574				})?;
1575			Ok(result)
1576		};
1577		let result = Self::run_guarded(try_call);
1578		ContractResult {
1579			result: result.map_err(|r| r.error),
1580			gas_consumed: gas_meter.gas_consumed(),
1581			gas_required: gas_meter.gas_required(),
1582			storage_deposit,
1583		}
1584	}
1585
1586	/// Prepare a dry run for the given account.
1587	///
1588	///
1589	/// This function is public because it is called by the runtime API implementation
1590	/// (see `impl_runtime_apis_plus_revive`).
1591	pub fn prepare_dry_run(account: &T::AccountId) {
1592		// Bump the  nonce to simulate what would happen
1593		// `pre-dispatch` if the transaction was executed.
1594		pezframe_system::Pezpallet::<T>::inc_account_nonce(account);
1595	}
1596
1597	/// A generalized version of [`Self::instantiate`] or [`Self::instantiate_with_code`].
1598	///
1599	/// Identical to [`Self::instantiate`] or [`Self::instantiate_with_code`] but tailored towards
1600	/// being called by other code within the runtime as opposed to from an extrinsic. It returns
1601	/// more information to the caller useful to estimate the cost of the operation.
1602	pub fn bare_instantiate(
1603		origin: OriginFor<T>,
1604		evm_value: U256,
1605		gas_limit: Weight,
1606		mut storage_deposit_limit: BalanceOf<T>,
1607		code: Code,
1608		data: Vec<u8>,
1609		salt: Option<[u8; 32]>,
1610		exec_config: ExecConfig<T>,
1611	) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1612		let mut gas_meter = GasMeter::new(gas_limit);
1613		let mut storage_deposit = Default::default();
1614		let try_instantiate = || {
1615			let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1616
1617			if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1618			let (executable, upload_deposit) = match code {
1619				Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1620					let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1621					let (executable, upload_deposit) = Self::try_upload_code(
1622						upload_account,
1623						code,
1624						BytecodeType::Pvm,
1625						storage_deposit_limit,
1626						&exec_config,
1627					)?;
1628					storage_deposit_limit.saturating_reduce(upload_deposit);
1629					(executable, upload_deposit)
1630				},
1631				Code::Upload(code) => {
1632					if T::AllowEVMBytecode::get() {
1633						ensure!(data.is_empty(), <Error<T>>::EvmConstructorNonEmptyData);
1634						let origin = T::UploadOrigin::ensure_origin(origin)?;
1635						let executable = ContractBlob::from_evm_init_code(code, origin)?;
1636						(executable, Default::default())
1637					} else {
1638						return Err(<Error<T>>::CodeRejected.into());
1639					}
1640				},
1641				Code::Existing(code_hash) => {
1642					let executable = ContractBlob::from_storage(code_hash, &mut gas_meter)?;
1643					ensure!(executable.code_info().is_pvm(), <Error<T>>::EvmConstructedFromHash);
1644					(executable, Default::default())
1645				},
1646			};
1647			let instantiate_origin = ExecOrigin::from_account_id(instantiate_account.clone());
1648			let mut storage_meter = StorageMeter::new(storage_deposit_limit);
1649			let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1650				instantiate_account,
1651				executable,
1652				&mut gas_meter,
1653				&mut storage_meter,
1654				evm_value,
1655				data,
1656				salt.as_ref(),
1657				&exec_config,
1658			);
1659			storage_deposit = storage_meter
1660				.try_into_deposit(&instantiate_origin, &exec_config)?
1661				.saturating_add(&StorageDeposit::Charge(upload_deposit));
1662			result
1663		};
1664		let output = Self::run_guarded(try_instantiate);
1665		ContractResult {
1666			result: output
1667				.map(|(addr, result)| InstantiateReturnValue { result, addr })
1668				.map_err(|e| e.error),
1669			gas_consumed: gas_meter.gas_consumed(),
1670			gas_required: gas_meter.gas_required(),
1671			storage_deposit,
1672		}
1673	}
1674
1675	/// Dry-run Ethereum calls.
1676	///
1677	/// # Parameters
1678	///
1679	/// - `tx`: The Ethereum transaction to simulate.
1680	pub fn dry_run_eth_transact(
1681		mut tx: GenericTransaction,
1682		dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
1683	) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
1684	where
1685		T::Nonce: Into<U256>,
1686		CallOf<T>: SetWeightLimit,
1687	{
1688		log::debug!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?}");
1689
1690		let origin = T::AddressMapper::to_account_id(&tx.from.unwrap_or_default());
1691		Self::prepare_dry_run(&origin);
1692
1693		let base_fee = Self::evm_base_fee();
1694		let effective_gas_price = tx.effective_gas_price(base_fee).unwrap_or(base_fee);
1695
1696		if effective_gas_price < base_fee {
1697			Err(EthTransactError::Message(format!(
1698				"Effective gas price {effective_gas_price:?} lower than base fee {base_fee:?}"
1699			)))?;
1700		}
1701
1702		if tx.nonce.is_none() {
1703			tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
1704		}
1705		if tx.chain_id.is_none() {
1706			tx.chain_id = Some(T::ChainId::get().into());
1707		}
1708		if tx.gas_price.is_none() {
1709			tx.gas_price = Some(effective_gas_price);
1710		}
1711		if tx.max_priority_fee_per_gas.is_none() {
1712			tx.max_priority_fee_per_gas = Some(effective_gas_price);
1713		}
1714		if tx.max_fee_per_gas.is_none() {
1715			tx.max_fee_per_gas = Some(effective_gas_price);
1716		}
1717
1718		let gas = tx.gas;
1719		if tx.gas.is_none() {
1720			tx.gas = Some(Self::evm_block_gas_limit());
1721		}
1722		if tx.r#type.is_none() {
1723			tx.r#type = Some(TYPE_EIP1559.into());
1724		}
1725
1726		// Store values before moving the tx
1727		let value = tx.value.unwrap_or_default();
1728		let input = tx.input.clone().to_vec();
1729		let from = tx.from;
1730		let to = tx.to;
1731
1732		// we need to parse the weight from the transaction so that it is run
1733		// using the exact weight limit passed by the eth wallet
1734		let mut call_info = create_call::<T>(tx, None, false)
1735			.map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
1736
1737		// the dry-run might leave out certain fields
1738		// in those cases we skip the check that the caller has enough balance
1739		// to pay for the fees
1740		let exec_config = {
1741			let base_info = T::FeeInfo::base_dispatch_info(&mut call_info.call);
1742			ExecConfig::new_eth_tx(
1743				effective_gas_price,
1744				call_info.encoded_len,
1745				base_info.total_weight(),
1746			)
1747			.with_dry_run(dry_run_config)
1748		};
1749
1750		// emulate transaction behavior
1751		let fees = call_info.tx_fee.saturating_add(call_info.storage_deposit);
1752		if let Some(from) = &from {
1753			let fees = if gas.is_some() { fees } else { Zero::zero() };
1754			let balance = Self::evm_balance(from);
1755			if balance < Pezpallet::<T>::convert_native_to_evm(fees).saturating_add(value) {
1756				return Err(EthTransactError::Message(format!(
1757					"insufficient funds for gas * price + value ({fees:?}): address {from:?} have {balance:?} (supplied gas {gas:?})",
1758				)));
1759			}
1760		}
1761
1762		// the deposit is done when the transaction is transformed from an `eth_transact`
1763		// we emulate this behavior for the dry-run here
1764		T::FeeInfo::deposit_txfee(T::Currency::issue(fees));
1765
1766		let extract_error = |err| {
1767			if err == Error::<T>::StorageDepositNotEnoughFunds.into() {
1768				Err(EthTransactError::Message(format!("Not enough gas supplied: {err:?}")))
1769			} else {
1770				Err(EthTransactError::Message(format!("failed to run contract: {err:?}")))
1771			}
1772		};
1773
1774		// Dry run the call
1775		let mut dry_run = match to {
1776			// A contract call.
1777			Some(dest) => {
1778				if dest == RUNTIME_PALLETS_ADDR {
1779					let Ok(dispatch_call) = <CallOf<T>>::decode(&mut &input[..]) else {
1780						return Err(EthTransactError::Message(format!(
1781							"Failed to decode pezpallet-call {input:?}"
1782						)));
1783					};
1784
1785					if let Err(result) =
1786						dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
1787					{
1788						return Err(EthTransactError::Message(format!(
1789							"Failed to dispatch call: {:?}",
1790							result.error,
1791						)));
1792					};
1793
1794					Default::default()
1795				} else {
1796					// Dry run the call.
1797					let result = crate::Pezpallet::<T>::bare_call(
1798						OriginFor::<T>::signed(origin),
1799						dest,
1800						value,
1801						call_info.weight_limit,
1802						BalanceOf::<T>::max_value(),
1803						input.clone(),
1804						exec_config,
1805					);
1806
1807					let data = match result.result {
1808						Ok(return_value) => {
1809							if return_value.did_revert() {
1810								return Err(EthTransactError::Data(return_value.data));
1811							}
1812							return_value.data
1813						},
1814						Err(err) => {
1815							log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
1816							return extract_error(err);
1817						},
1818					};
1819
1820					EthTransactInfo {
1821						gas_required: result.gas_required,
1822						storage_deposit: result.storage_deposit.charge_or_zero(),
1823						data,
1824						eth_gas: Default::default(),
1825					}
1826				}
1827			},
1828			// A contract deployment
1829			None => {
1830				// Extract code and data from the input.
1831				let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
1832					extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
1833				} else {
1834					(input, vec![])
1835				};
1836
1837				// Dry run the call.
1838				let result = crate::Pezpallet::<T>::bare_instantiate(
1839					OriginFor::<T>::signed(origin),
1840					value,
1841					call_info.weight_limit,
1842					BalanceOf::<T>::max_value(),
1843					Code::Upload(code.clone()),
1844					data.clone(),
1845					None,
1846					exec_config,
1847				);
1848
1849				let returned_data = match result.result {
1850					Ok(return_value) => {
1851						if return_value.result.did_revert() {
1852							return Err(EthTransactError::Data(return_value.result.data));
1853						}
1854						return_value.result.data
1855					},
1856					Err(err) => {
1857						log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
1858						return extract_error(err);
1859					},
1860				};
1861
1862				EthTransactInfo {
1863					gas_required: result.gas_required,
1864					storage_deposit: result.storage_deposit.charge_or_zero(),
1865					data: returned_data,
1866					eth_gas: Default::default(),
1867				}
1868			},
1869		};
1870
1871		// replace the weight passed in the transaction with the dry_run result
1872		call_info.call.set_weight_limit(dry_run.gas_required);
1873
1874		// we notify the wallet that the tx would not fit
1875		let total_weight = T::FeeInfo::dispatch_info(&call_info.call).total_weight();
1876		let max_weight = Self::evm_max_extrinsic_weight();
1877		if total_weight.any_gt(max_weight) {
1878			Err(EthTransactError::Message(format!(
1879				"\
1880				The transaction consumes more than the allowed weight. \
1881				needed={total_weight} \
1882				allowed={max_weight} \
1883				overweight_by={}\
1884				",
1885				total_weight.saturating_sub(max_weight),
1886			)))?;
1887		}
1888
1889		// not enough gas supplied to pay for both the tx fees and the storage deposit
1890		let transaction_fee = T::FeeInfo::tx_fee(call_info.encoded_len, &call_info.call);
1891		let available_fee = T::FeeInfo::remaining_txfee();
1892		if transaction_fee > available_fee {
1893			Err(EthTransactError::Message(format!(
1894				"Not enough gas supplied: Off by: {:?}",
1895				call_info.tx_fee.saturating_sub(available_fee),
1896			)))?;
1897		}
1898
1899		// We add `1` to account for the potential rounding error of the multiplication.
1900		// Returning a larger value here just increases the the pre-dispatch weight.
1901		let eth_gas: U256 = T::FeeInfo::next_fee_multiplier_reciprocal()
1902			.saturating_mul_int(transaction_fee.saturating_add(dry_run.storage_deposit))
1903			.saturating_add(1_u32.into())
1904			.into();
1905
1906		log::debug!(target: LOG_TARGET, "\
1907			dry_run_eth_transact: \
1908			weight_limit={} \
1909			total_weight={total_weight} \
1910			max_weight={max_weight} \
1911			weight_left={} \
1912			eth_gas={eth_gas}) \
1913			encoded_len={} \
1914			tx_fee={transaction_fee:?} \
1915			storage_deposit={:?}\
1916			",
1917			dry_run.gas_required,
1918			max_weight.saturating_sub(total_weight),
1919			call_info.encoded_len,
1920			dry_run.storage_deposit,
1921
1922		);
1923		dry_run.eth_gas = eth_gas;
1924		Ok(dry_run)
1925	}
1926
1927	/// Get the balance with EVM decimals of the given `address`.
1928	///
1929	/// Returns the spendable balance excluding the existential deposit.
1930	pub fn evm_balance(address: &H160) -> U256 {
1931		let balance = AccountInfo::<T>::balance_of((*address).into());
1932		Self::convert_native_to_evm(balance)
1933	}
1934
1935	/// Get the current Ethereum block from storage.
1936	pub fn eth_block() -> EthBlock {
1937		EthereumBlock::<T>::get()
1938	}
1939
1940	/// Convert the Ethereum block number into the Ethereum block hash.
1941	///
1942	/// # Note
1943	///
1944	/// The Ethereum block number is identical to the Bizinikiwi block number.
1945	/// If the provided block number is outside of the pruning None is returned.
1946	pub fn eth_block_hash_from_number(number: U256) -> Option<H256> {
1947		let number = BlockNumberFor::<T>::try_from(number).ok()?;
1948		let hash = <BlockHash<T>>::get(number);
1949		if hash == H256::zero() {
1950			None
1951		} else {
1952			Some(hash)
1953		}
1954	}
1955
1956	/// The details needed to reconstruct the receipt information offchain.
1957	pub fn eth_receipt_data() -> Vec<ReceiptGasInfo> {
1958		ReceiptInfoData::<T>::get()
1959	}
1960
1961	/// Set the EVM balance of an account.
1962	///
1963	/// The account's total balance becomes the EVM value plus the existential deposit,
1964	/// consistent with `evm_balance` which returns the spendable balance excluding the existential
1965	/// deposit.
1966	pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error<T>> {
1967		let (balance, dust) = Self::new_balance_with_dust(evm_value)
1968			.map_err(|_| <Error<T>>::BalanceConversionFailed)?;
1969		let account_id = T::AddressMapper::to_account_id(&address);
1970		T::Currency::set_balance(&account_id, balance);
1971		AccountInfoOf::<T>::mutate(&address, |account| {
1972			if let Some(account) = account {
1973				account.dust = dust;
1974			} else {
1975				*account = Some(AccountInfo { dust, ..Default::default() });
1976			}
1977		});
1978
1979		Ok(())
1980	}
1981
1982	/// Construct native balance from EVM balance.
1983	///
1984	/// Adds the existential deposit and returns the native balance plus the dust.
1985	pub fn new_balance_with_dust(
1986		evm_value: U256,
1987	) -> Result<(BalanceOf<T>, u32), BalanceConversionError> {
1988		let ed = T::Currency::minimum_balance();
1989		let balance_with_dust = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(evm_value)?;
1990		let (value, dust) = balance_with_dust.deconstruct();
1991
1992		Ok((ed.saturating_add(value), dust))
1993	}
1994
1995	/// Get the nonce for the given `address`.
1996	pub fn evm_nonce(address: &H160) -> u32
1997	where
1998		T::Nonce: Into<u32>,
1999	{
2000		let account = T::AddressMapper::to_account_id(&address);
2001		System::<T>::account_nonce(account).into()
2002	}
2003
2004	/// Get the block gas limit.
2005	pub fn evm_block_gas_limit() -> U256 {
2006		let max_block_weight = T::BlockWeights::get()
2007			.get(DispatchClass::Normal)
2008			.max_total
2009			.unwrap_or_else(|| T::BlockWeights::get().max_block);
2010
2011		let length_fee = T::FeeInfo::next_fee_multiplier_reciprocal().saturating_mul_int(
2012			T::FeeInfo::length_to_fee(*T::BlockLength::get().max.get(DispatchClass::Normal)),
2013		);
2014
2015		Self::evm_gas_from_weight(max_block_weight).saturating_add(length_fee.into())
2016	}
2017
2018	/// The maximum weight an `eth_transact` is allowed to consume.
2019	pub fn evm_max_extrinsic_weight() -> Weight {
2020		let factor = <T as Config>::MaxEthExtrinsicWeight::get();
2021		let max_weight = <T as pezframe_system::Config>::BlockWeights::get()
2022			.get(DispatchClass::Normal)
2023			.max_extrinsic
2024			.unwrap_or_else(|| <T as pezframe_system::Config>::BlockWeights::get().max_block);
2025		Weight::from_parts(
2026			factor.saturating_mul_int(max_weight.ref_time()),
2027			factor.saturating_mul_int(max_weight.proof_size()),
2028		)
2029	}
2030
2031	/// Get the base gas price.
2032	pub fn evm_base_fee() -> U256 {
2033		let multiplier = T::FeeInfo::next_fee_multiplier();
2034		multiplier.saturating_mul_int::<u128>(T::NativeToEthRatio::get().into()).into()
2035	}
2036
2037	/// Build an EVM tracer from the given tracer type.
2038	pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
2039	where
2040		T::Nonce: Into<u32>,
2041	{
2042		match tracer_type {
2043			TracerType::CallTracer(config) => CallTracer::new(
2044				config.unwrap_or_default(),
2045				Self::evm_gas_from_weight as fn(Weight) -> U256,
2046			)
2047			.into(),
2048			TracerType::PrestateTracer(config) => {
2049				PrestateTracer::new(config.unwrap_or_default()).into()
2050			},
2051		}
2052	}
2053
2054	/// A generalized version of [`Self::upload_code`].
2055	///
2056	/// It is identical to [`Self::upload_code`] and only differs in the information it returns.
2057	pub fn bare_upload_code(
2058		origin: OriginFor<T>,
2059		code: Vec<u8>,
2060		storage_deposit_limit: BalanceOf<T>,
2061	) -> CodeUploadResult<BalanceOf<T>> {
2062		let origin = T::UploadOrigin::ensure_origin(origin)?;
2063
2064		let bytecode_type = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2065			BytecodeType::Pvm
2066		} else {
2067			if !T::AllowEVMBytecode::get() {
2068				return Err(<Error<T>>::CodeRejected.into());
2069			}
2070			BytecodeType::Evm
2071		};
2072
2073		let (module, deposit) = Self::try_upload_code(
2074			origin,
2075			code,
2076			bytecode_type,
2077			storage_deposit_limit,
2078			&ExecConfig::new_bizinikiwi_tx(),
2079		)?;
2080		Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit })
2081	}
2082
2083	/// Query storage of a specified contract under a specified key.
2084	pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
2085		let contract_info =
2086			AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2087
2088		let maybe_value = contract_info.read(&Key::from_fixed(key));
2089		Ok(maybe_value)
2090	}
2091
2092	/// Get the immutable data of a specified contract.
2093	///
2094	/// Returns `None` if the contract does not exist or has no immutable data.
2095	pub fn get_immutables(address: H160) -> Option<ImmutableData> {
2096		let immutable_data = <ImmutableDataOf<T>>::get(address);
2097		immutable_data
2098	}
2099
2100	/// Sets immutable data of a contract
2101	///
2102	/// Returns an error if the contract does not exist.
2103	///
2104	/// # Warning
2105	///
2106	/// Does not collect any storage deposit. Not safe to be called by user controlled code.
2107	pub fn set_immutables(address: H160, data: ImmutableData) -> Result<(), ContractAccessError> {
2108		AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2109		<ImmutableDataOf<T>>::insert(address, data);
2110		Ok(())
2111	}
2112
2113	/// Query storage of a specified contract under a specified variable-sized key.
2114	pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
2115		let contract_info =
2116			AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2117
2118		let maybe_value = contract_info.read(
2119			&Key::try_from_var(key)
2120				.map_err(|_| ContractAccessError::KeyDecodingFailed)?
2121				.into(),
2122		);
2123		Ok(maybe_value)
2124	}
2125
2126	/// Convert a native balance to EVM balance.
2127	pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
2128		let (value, dust) = value.into().deconstruct();
2129		value
2130			.into()
2131			.saturating_mul(T::NativeToEthRatio::get().into())
2132			.saturating_add(dust.into())
2133	}
2134
2135	/// Set storage of a specified contract under a specified key.
2136	///
2137	/// If the `value` is `None`, the storage entry is deleted.
2138	///
2139	/// Returns an error if the contract does not exist or if the write operation fails.
2140	///
2141	/// # Warning
2142	///
2143	/// Does not collect any storage deposit. Not safe to be called by user controlled code.
2144	pub fn set_storage(address: H160, key: [u8; 32], value: Option<Vec<u8>>) -> SetStorageResult {
2145		let contract_info =
2146			AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2147
2148		contract_info
2149			.write(&Key::from_fixed(key), value, None, false)
2150			.map_err(ContractAccessError::StorageWriteFailed)
2151	}
2152
2153	/// Set the storage of a specified contract under a specified variable-sized key.
2154	///
2155	/// If the `value` is `None`, the storage entry is deleted.
2156	///
2157	/// Returns an error if the contract does not exist, if the key decoding fails,
2158	/// or if the write operation fails.
2159	///
2160	/// # Warning
2161	///
2162	/// Does not collect any storage deposit. Not safe to be called by user controlled code.
2163	pub fn set_storage_var_key(
2164		address: H160,
2165		key: Vec<u8>,
2166		value: Option<Vec<u8>>,
2167	) -> SetStorageResult {
2168		let contract_info =
2169			AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2170
2171		contract_info
2172			.write(
2173				&Key::try_from_var(key)
2174					.map_err(|_| ContractAccessError::KeyDecodingFailed)?
2175					.into(),
2176				value,
2177				None,
2178				false,
2179			)
2180			.map_err(ContractAccessError::StorageWriteFailed)
2181	}
2182
2183	/// Pezpallet account, used to hold funds for contracts upload deposit.
2184	pub fn account_id() -> T::AccountId {
2185		use pezframe_support::PalletId;
2186		use pezsp_runtime::traits::AccountIdConversion;
2187		PalletId(*b"py/reviv").into_account_truncating()
2188	}
2189
2190	/// The address of the validator that produced the current block.
2191	pub fn block_author() -> H160 {
2192		use pezframe_support::traits::FindAuthor;
2193
2194		let digest = <pezframe_system::Pezpallet<T>>::digest();
2195		let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
2196
2197		T::FindAuthor::find_author(pre_runtime_digests)
2198			.map(|account_id| T::AddressMapper::to_address(&account_id))
2199			.unwrap_or_default()
2200	}
2201
2202	/// Returns the code at `address`.
2203	///
2204	/// This takes pre-compiles into account.
2205	pub fn code(address: &H160) -> Vec<u8> {
2206		use precompiles::{All, Precompiles};
2207		if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
2208			return code.into();
2209		}
2210		AccountInfo::<T>::load_contract(&address)
2211			.and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
2212			.map(|code| code.into())
2213			.unwrap_or_default()
2214	}
2215
2216	/// Uploads new code and returns the Vm binary contract blob and deposit amount collected.
2217	pub fn try_upload_code(
2218		origin: T::AccountId,
2219		code: Vec<u8>,
2220		code_type: BytecodeType,
2221		storage_deposit_limit: BalanceOf<T>,
2222		exec_config: &ExecConfig<T>,
2223	) -> Result<(ContractBlob<T>, BalanceOf<T>), DispatchError> {
2224		let mut module = match code_type {
2225			BytecodeType::Pvm => ContractBlob::from_pvm_code(code, origin)?,
2226			BytecodeType::Evm => ContractBlob::from_evm_runtime_code(code, origin)?,
2227		};
2228		let deposit = module.store_code(exec_config, None)?;
2229		ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
2230		Ok((module, deposit))
2231	}
2232
2233	/// Run the supplied function `f` if no other instance of this pezpallet is on the stack.
2234	fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
2235		executing_contract::using_once(&mut false, || {
2236			executing_contract::with(|f| {
2237				// Fail if already entered contract execution
2238				if *f {
2239					return Err(())
2240				}
2241				// We are entering contract execution
2242				*f = true;
2243				Ok(())
2244			})
2245				.expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
2246				.map_err(|_| <Error<T>>::ReenteredPallet.into())
2247				.map(|_| f())
2248				.and_then(|r| r)
2249		})
2250	}
2251
2252	/// Convert a weight to a gas value.
2253	pub fn evm_gas_from_weight(weight: Weight) -> U256 {
2254		T::FeeInfo::weight_to_fee(&weight, Combinator::Max).into()
2255	}
2256
2257	/// Transfer a deposit from some account to another.
2258	///
2259	/// `from` is usually the transaction origin and `to` a contract or
2260	/// the pallets own account.
2261	fn charge_deposit(
2262		hold_reason: Option<HoldReason>,
2263		from: &T::AccountId,
2264		to: &T::AccountId,
2265		amount: BalanceOf<T>,
2266		exec_config: &ExecConfig<T>,
2267	) -> DispatchResult {
2268		use pezframe_support::traits::tokens::{Fortitude, Precision, Preservation};
2269
2270		if amount.is_zero() {
2271			return Ok(());
2272		}
2273
2274		match (exec_config.collect_deposit_from_hold.is_some(), hold_reason) {
2275			(true, hold_reason) => {
2276				T::FeeInfo::withdraw_txfee(amount)
2277					.ok_or(())
2278					.and_then(|credit| T::Currency::resolve(to, credit).map_err(|_| ()))
2279					.and_then(|_| {
2280						if let Some(hold_reason) = hold_reason {
2281							T::Currency::hold(&hold_reason.into(), to, amount).map_err(|_| ())?;
2282						}
2283						Ok(())
2284					})
2285					.map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2286			},
2287			(false, Some(hold_reason)) => {
2288				T::Currency::transfer_and_hold(
2289					&hold_reason.into(),
2290					from,
2291					to,
2292					amount,
2293					Precision::Exact,
2294					Preservation::Preserve,
2295					Fortitude::Polite,
2296				)
2297				.map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2298			},
2299			(false, None) => {
2300				T::Currency::transfer(from, to, amount, Preservation::Preserve)
2301					.map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2302			},
2303		}
2304		Ok(())
2305	}
2306
2307	/// Refund a deposit.
2308	///
2309	/// `to` is usually the transaction origin and `from` a contract or
2310	/// the pallets own account.
2311	fn refund_deposit(
2312		hold_reason: HoldReason,
2313		from: &T::AccountId,
2314		to: &T::AccountId,
2315		amount: BalanceOf<T>,
2316		exec_config: Option<&ExecConfig<T>>,
2317	) -> Result<(), DispatchError> {
2318		use pezframe_support::traits::{
2319			fungible::InspectHold,
2320			tokens::{Fortitude, Precision, Preservation, Restriction},
2321		};
2322
2323		if amount.is_zero() {
2324			return Ok(());
2325		}
2326
2327		let hold_reason = hold_reason.into();
2328		let result = if exec_config.map(|c| c.collect_deposit_from_hold.is_some()).unwrap_or(false)
2329		{
2330			T::Currency::release(&hold_reason, from, amount, Precision::Exact)
2331				.and_then(|amount| {
2332					T::Currency::withdraw(
2333						from,
2334						amount,
2335						Precision::Exact,
2336						Preservation::Preserve,
2337						Fortitude::Polite,
2338					)
2339				})
2340				.map(T::FeeInfo::deposit_txfee)
2341		} else {
2342			T::Currency::transfer_on_hold(
2343				&hold_reason,
2344				from,
2345				to,
2346				amount,
2347				Precision::Exact,
2348				Restriction::Free,
2349				Fortitude::Polite,
2350			)
2351			.map(|_| ())
2352		};
2353
2354		result.map_err(|_| {
2355			let available = T::Currency::balance_on_hold(&hold_reason, from);
2356			if available < amount {
2357				// The storage deposit accounting got out of sync with the balance: This would be a
2358				// straight up bug in this pezpallet.
2359				log::error!(
2360					target: LOG_TARGET,
2361					"Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. Not enough deposit: {:?}. This is a bug.",
2362					amount, from, to, available,
2363				);
2364				Error::<T>::StorageRefundNotEnoughFunds.into()
2365			} else {
2366				// There are some locks preventing the refund. This could be the case if the
2367				// contract participates in government. The consequence is that if a contract votes
2368				// with its storage deposit it would no longer be possible to remove storage without first
2369				// reducing the lock.
2370				log::warn!(
2371					target: LOG_TARGET,
2372					"Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. First remove locks (staking, governance) from the contracts account.",
2373					amount, from, to,
2374				);
2375				Error::<T>::StorageRefundLocked.into()
2376			}
2377		})
2378	}
2379
2380	/// Returns true if the evm value carries dust.
2381	fn has_dust(value: U256) -> bool {
2382		value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
2383	}
2384
2385	/// Returns true if the evm value carries balance.
2386	fn has_balance(value: U256) -> bool {
2387		value >= U256::from(<T>::NativeToEthRatio::get())
2388	}
2389
2390	/// Return the existential deposit of [`Config::Currency`].
2391	fn min_balance() -> BalanceOf<T> {
2392		<T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
2393	}
2394
2395	/// Deposit a pezpallet revive event.
2396	///
2397	/// This method will be called by the EVM to deposit events emitted by the contract.
2398	/// Therefore all events must be contract emitted events.
2399	fn deposit_event(event: Event<T>) {
2400		<pezframe_system::Pezpallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
2401	}
2402
2403	// Returns Ok with the account that signed the eth transaction.
2404	fn ensure_eth_signed(origin: OriginFor<T>) -> Result<AccountIdOf<T>, DispatchError> {
2405		match <T as Config>::RuntimeOrigin::from(origin).into() {
2406			Ok(Origin::EthTransaction(signer)) => Ok(signer),
2407			_ => Err(BadOrigin.into()),
2408		}
2409	}
2410
2411	/// Ensure that the origin is neither a pre-compile nor a contract.
2412	///
2413	/// This enforces EIP-3607.
2414	fn ensure_non_contract_if_signed(origin: &OriginFor<T>) -> DispatchResult {
2415		if DebugSettings::bypass_eip_3607::<T>() {
2416			return Ok(());
2417		}
2418		let Some(address) = origin
2419			.as_system_ref()
2420			.and_then(|o| o.as_signed())
2421			.map(<T::AddressMapper as AddressMapper<T>>::to_address)
2422		else {
2423			return Ok(());
2424		};
2425		if exec::is_precompile::<T, ContractBlob<T>>(&address)
2426			|| <AccountInfo<T>>::is_contract(&address)
2427		{
2428			log::debug!(
2429				target: crate::LOG_TARGET,
2430				"EIP-3607: reject tx as pre-compile or account exist at {address:?}",
2431			);
2432			Err(DispatchError::BadOrigin)
2433		} else {
2434			Ok(())
2435		}
2436	}
2437}
2438
2439/// The address used to call the runtime's pallets dispatchables
2440///
2441/// Note:
2442/// computed with PalletId(*b"py/paddr").into_account_truncating();
2443pub const RUNTIME_PALLETS_ADDR: H160 =
2444	H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
2445
2446// Set up a global reference to the boolean flag used for the re-entrancy guard.
2447environmental!(executing_contract: bool);
2448
2449pezsp_api::decl_runtime_apis! {
2450	/// The API used to dry-run contract interactions.
2451	#[api_version(1)]
2452	pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, Moment> where
2453		AccountId: Codec,
2454		Balance: Codec,
2455		Nonce: Codec,
2456		BlockNumber: Codec,
2457		Moment: Codec,
2458	{
2459		/// Returns the current ETH block.
2460		///
2461		/// This is one block behind the bizinikiwi block.
2462		fn eth_block() -> EthBlock;
2463
2464		/// Returns the ETH block hash for the given block number.
2465		fn eth_block_hash(number: U256) -> Option<H256>;
2466
2467		/// The details needed to reconstruct the receipt information offchain.
2468		///
2469		/// # Note
2470		///
2471		/// Each entry corresponds to the appropriate Ethereum transaction in the current block.
2472		fn eth_receipt_data() -> Vec<ReceiptGasInfo>;
2473
2474		/// Returns the block gas limit.
2475		fn block_gas_limit() -> U256;
2476
2477		/// Returns the free balance of the given `[H160]` address, using EVM decimals.
2478		fn balance(address: H160) -> U256;
2479
2480		/// Returns the gas price.
2481		fn gas_price() -> U256;
2482
2483		/// Returns the nonce of the given `[H160]` address.
2484		fn nonce(address: H160) -> Nonce;
2485
2486		/// Perform a call from a specified account to a given contract.
2487		///
2488		/// See [`crate::Pezpallet::bare_call`].
2489		fn call(
2490			origin: AccountId,
2491			dest: H160,
2492			value: Balance,
2493			gas_limit: Option<Weight>,
2494			storage_deposit_limit: Option<Balance>,
2495			input_data: Vec<u8>,
2496		) -> ContractResult<ExecReturnValue, Balance>;
2497
2498		/// Instantiate a new contract.
2499		///
2500		/// See `[crate::Pezpallet::bare_instantiate]`.
2501		fn instantiate(
2502			origin: AccountId,
2503			value: Balance,
2504			gas_limit: Option<Weight>,
2505			storage_deposit_limit: Option<Balance>,
2506			code: Code,
2507			data: Vec<u8>,
2508			salt: Option<[u8; 32]>,
2509		) -> ContractResult<InstantiateReturnValue, Balance>;
2510
2511
2512		/// Perform an Ethereum call.
2513		///
2514		/// Deprecated use `v2` version instead.
2515		/// See [`crate::Pezpallet::dry_run_eth_transact`]
2516		fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2517
2518		/// Perform an Ethereum call.
2519		///
2520		/// See [`crate::Pezpallet::dry_run_eth_transact`]
2521		fn eth_transact_with_config(
2522			tx: GenericTransaction,
2523			config: DryRunConfig<Moment>,
2524		) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2525
2526		/// Upload new code without instantiating a contract from it.
2527		///
2528		/// See [`crate::Pezpallet::bare_upload_code`].
2529		fn upload_code(
2530			origin: AccountId,
2531			code: Vec<u8>,
2532			storage_deposit_limit: Option<Balance>,
2533		) -> CodeUploadResult<Balance>;
2534
2535		/// Query a given storage key in a given contract.
2536		///
2537		/// Returns `Ok(Some(Vec<u8>))` if the storage value exists under the given key in the
2538		/// specified account and `Ok(None)` if it doesn't. If the account specified by the address
2539		/// doesn't exist, or doesn't have a contract then `Err` is returned.
2540		fn get_storage(
2541			address: H160,
2542			key: [u8; 32],
2543		) -> GetStorageResult;
2544
2545		/// Query a given variable-sized storage key in a given contract.
2546		///
2547		/// Returns `Ok(Some(Vec<u8>))` if the storage value exists under the given key in the
2548		/// specified account and `Ok(None)` if it doesn't. If the account specified by the address
2549		/// doesn't exist, or doesn't have a contract then `Err` is returned.
2550		fn get_storage_var_key(
2551			address: H160,
2552			key: Vec<u8>,
2553		) -> GetStorageResult;
2554
2555		/// Traces the execution of an entire block and returns call traces.
2556		///
2557		/// This is intended to be called through `state_call` to replay the block from the
2558		/// parent block.
2559		///
2560		/// See eth-rpc `debug_traceBlockByNumber` for usage.
2561		fn trace_block(
2562			block: Block,
2563			config: TracerType
2564		) -> Vec<(u32, Trace)>;
2565
2566		/// Traces the execution of a specific transaction within a block.
2567		///
2568		/// This is intended to be called through `state_call` to replay the block from the
2569		/// parent hash up to the transaction.
2570		///
2571		/// See eth-rpc `debug_traceTransaction` for usage.
2572		fn trace_tx(
2573			block: Block,
2574			tx_index: u32,
2575			config: TracerType
2576		) -> Option<Trace>;
2577
2578		/// Dry run and return the trace of the given call.
2579		///
2580		/// See eth-rpc `debug_traceCall` for usage.
2581		fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
2582
2583		/// The address of the validator that produced the current block.
2584		fn block_author() -> H160;
2585
2586		/// Get the H160 address associated to this account id
2587		fn address(account_id: AccountId) -> H160;
2588
2589		/// Get the account id associated to this H160 address.
2590		fn account_id(address: H160) -> AccountId;
2591
2592		/// The address used to call the runtime's pallets dispatchables
2593		fn runtime_pallets_address() -> H160;
2594
2595		/// The code at the specified address taking pre-compiles into account.
2596		fn code(address: H160) -> Vec<u8>;
2597
2598		/// Construct the new balance and dust components of this EVM balance.
2599		fn new_balance_with_dust(balance: U256) -> Result<(Balance, u32), BalanceConversionError>;
2600	}
2601}
2602
2603/// This macro wraps bizinikiwi's `impl_runtime_apis!` and implements `pezpallet_revive` runtime
2604/// APIs and other required traits.
2605///
2606/// # Note
2607///
2608/// This also implements [`SetWeightLimit`] for the runtime call.
2609///
2610/// # Parameters
2611/// - `$Runtime`: The runtime type to implement the APIs for.
2612/// - `$Revive`: The name under which revive is declared in `construct_runtime`.
2613/// - `$Executive`: The Executive type of the runtime.
2614/// - `$EthExtra`: Type for additional Ethereum runtime extension.
2615/// - `$($rest:tt)*`: Remaining input to be forwarded to the underlying `impl_runtime_apis!`.
2616#[macro_export]
2617macro_rules! impl_runtime_apis_plus_revive_traits {
2618	($Runtime: ty, $Revive: ident, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
2619
2620		type __ReviveMacroMoment = <<$Runtime as $crate::Config>::Time as $crate::Time>::Moment;
2621
2622		impl $crate::evm::runtime::SetWeightLimit for RuntimeCall {
2623			fn set_weight_limit(&mut self, weight_limit: Weight) -> Weight {
2624				use $crate::pezpallet::Call as ReviveCall;
2625				match self {
2626					Self::$Revive(
2627						ReviveCall::eth_call{ gas_limit, .. } |
2628						ReviveCall::eth_instantiate_with_code{ gas_limit, .. }
2629					) => {
2630						let old = *gas_limit;
2631						*gas_limit = weight_limit;
2632						old
2633					},
2634					_ => Weight::default(),
2635				}
2636			}
2637		}
2638
2639		impl_runtime_apis! {
2640			$($rest)*
2641
2642
2643			impl pezpallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, __ReviveMacroMoment> for $Runtime
2644			{
2645				fn eth_block() -> $crate::EthBlock {
2646					$crate::Pezpallet::<Self>::eth_block()
2647				}
2648
2649				fn eth_block_hash(number: $crate::U256) -> Option<$crate::H256> {
2650					$crate::Pezpallet::<Self>::eth_block_hash_from_number(number)
2651				}
2652
2653				fn eth_receipt_data() -> Vec<$crate::ReceiptGasInfo> {
2654					$crate::Pezpallet::<Self>::eth_receipt_data()
2655				}
2656
2657				fn balance(address: $crate::H160) -> $crate::U256 {
2658					$crate::Pezpallet::<Self>::evm_balance(&address)
2659				}
2660
2661				fn block_author() -> $crate::H160 {
2662					$crate::Pezpallet::<Self>::block_author()
2663				}
2664
2665				fn block_gas_limit() -> $crate::U256 {
2666					$crate::Pezpallet::<Self>::evm_block_gas_limit()
2667				}
2668
2669				fn gas_price() -> $crate::U256 {
2670					$crate::Pezpallet::<Self>::evm_base_fee()
2671				}
2672
2673				fn nonce(address: $crate::H160) -> Nonce {
2674					use $crate::AddressMapper;
2675					let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
2676					$crate::pezframe_system::Pezpallet::<Self>::account_nonce(account)
2677				}
2678
2679				fn address(account_id: AccountId) -> $crate::H160 {
2680					use $crate::AddressMapper;
2681					<Self as $crate::Config>::AddressMapper::to_address(&account_id)
2682				}
2683
2684				fn eth_transact(
2685					tx: $crate::evm::GenericTransaction,
2686				) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2687					use $crate::{
2688						codec::Encode, evm::runtime::EthExtra, pezframe_support::traits::Get,
2689						pezsp_runtime::traits::TransactionExtension,
2690						pezsp_runtime::traits::Block as BlockT
2691					};
2692					$crate::Pezpallet::<Self>::dry_run_eth_transact(tx, Default::default())
2693				}
2694
2695				fn eth_transact_with_config(
2696					tx: $crate::evm::GenericTransaction,
2697					config: $crate::DryRunConfig<__ReviveMacroMoment>,
2698				) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2699					use $crate::{
2700						codec::Encode, evm::runtime::EthExtra, pezframe_support::traits::Get,
2701						pezsp_runtime::traits::TransactionExtension,
2702						pezsp_runtime::traits::Block as BlockT
2703					};
2704					$crate::Pezpallet::<Self>::dry_run_eth_transact(tx, config)
2705				}
2706
2707				fn call(
2708					origin: AccountId,
2709					dest: $crate::H160,
2710					value: Balance,
2711					gas_limit: Option<$crate::Weight>,
2712					storage_deposit_limit: Option<Balance>,
2713					input_data: Vec<u8>,
2714				) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
2715					use $crate::pezframe_support::traits::Get;
2716					let blockweights: $crate::BlockWeights =
2717						<Self as $crate::pezframe_system::Config>::BlockWeights::get();
2718
2719					$crate::Pezpallet::<Self>::prepare_dry_run(&origin);
2720					$crate::Pezpallet::<Self>::bare_call(
2721						<Self as $crate::pezframe_system::Config>::RuntimeOrigin::signed(origin),
2722						dest,
2723						$crate::Pezpallet::<Self>::convert_native_to_evm(value),
2724						gas_limit.unwrap_or(blockweights.max_block),
2725						storage_deposit_limit.unwrap_or(u128::MAX),
2726						input_data,
2727						$crate::ExecConfig::new_bizinikiwi_tx().with_dry_run(Default::default()),
2728					)
2729				}
2730
2731				fn instantiate(
2732					origin: AccountId,
2733					value: Balance,
2734					gas_limit: Option<$crate::Weight>,
2735					storage_deposit_limit: Option<Balance>,
2736					code: $crate::Code,
2737					data: Vec<u8>,
2738					salt: Option<[u8; 32]>,
2739				) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
2740					use $crate::pezframe_support::traits::Get;
2741					let blockweights: $crate::BlockWeights =
2742						<Self as $crate::pezframe_system::Config>::BlockWeights::get();
2743
2744					$crate::Pezpallet::<Self>::prepare_dry_run(&origin);
2745					$crate::Pezpallet::<Self>::bare_instantiate(
2746						<Self as $crate::pezframe_system::Config>::RuntimeOrigin::signed(origin),
2747						$crate::Pezpallet::<Self>::convert_native_to_evm(value),
2748						gas_limit.unwrap_or(blockweights.max_block),
2749						storage_deposit_limit.unwrap_or(u128::MAX),
2750						code,
2751						data,
2752						salt,
2753						$crate::ExecConfig::new_bizinikiwi_tx().with_dry_run(Default::default()),
2754					)
2755				}
2756
2757				fn upload_code(
2758					origin: AccountId,
2759					code: Vec<u8>,
2760					storage_deposit_limit: Option<Balance>,
2761				) -> $crate::CodeUploadResult<Balance> {
2762					let origin =
2763						<Self as $crate::pezframe_system::Config>::RuntimeOrigin::signed(origin);
2764					$crate::Pezpallet::<Self>::bare_upload_code(
2765						origin,
2766						code,
2767						storage_deposit_limit.unwrap_or(u128::MAX),
2768					)
2769				}
2770
2771				fn get_storage_var_key(
2772					address: $crate::H160,
2773					key: Vec<u8>,
2774				) -> $crate::GetStorageResult {
2775					$crate::Pezpallet::<Self>::get_storage_var_key(address, key)
2776				}
2777
2778				fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
2779					$crate::Pezpallet::<Self>::get_storage(address, key)
2780				}
2781
2782				fn trace_block(
2783					block: Block,
2784					tracer_type: $crate::evm::TracerType,
2785				) -> Vec<(u32, $crate::evm::Trace)> {
2786					use $crate::{pezsp_runtime::traits::Block, tracing::trace};
2787					let mut traces = vec![];
2788					let (header, extrinsics) = block.deconstruct();
2789					<$Executive>::initialize_block(&header);
2790					for (index, ext) in extrinsics.into_iter().enumerate() {
2791						let mut tracer = $crate::Pezpallet::<Self>::evm_tracer(tracer_type.clone());
2792						let t = tracer.as_tracing();
2793						let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2794
2795						if let Some(tx_trace) = tracer.collect_trace() {
2796							traces.push((index as u32, tx_trace));
2797						}
2798					}
2799
2800					traces
2801				}
2802
2803				fn trace_tx(
2804					block: Block,
2805					tx_index: u32,
2806					tracer_type: $crate::evm::TracerType,
2807				) -> Option<$crate::evm::Trace> {
2808					use $crate::{pezsp_runtime::traits::Block, tracing::trace};
2809
2810					let mut tracer = $crate::Pezpallet::<Self>::evm_tracer(tracer_type);
2811					let (header, extrinsics) = block.deconstruct();
2812
2813					<$Executive>::initialize_block(&header);
2814					for (index, ext) in extrinsics.into_iter().enumerate() {
2815						if index as u32 == tx_index {
2816							let t = tracer.as_tracing();
2817							let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2818							break;
2819						} else {
2820							let _ = <$Executive>::apply_extrinsic(ext);
2821						}
2822					}
2823
2824					tracer.collect_trace()
2825				}
2826
2827				fn trace_call(
2828					tx: $crate::evm::GenericTransaction,
2829					tracer_type: $crate::evm::TracerType,
2830				) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
2831					use $crate::tracing::trace;
2832					let mut tracer = $crate::Pezpallet::<Self>::evm_tracer(tracer_type.clone());
2833					let t = tracer.as_tracing();
2834
2835					t.watch_address(&tx.from.unwrap_or_default());
2836					t.watch_address(&$crate::Pezpallet::<Self>::block_author());
2837					let result = trace(t, || Self::eth_transact(tx));
2838
2839					if let Some(trace) = tracer.collect_trace() {
2840						Ok(trace)
2841					} else if let Err(err) = result {
2842						Err(err)
2843					} else {
2844						Ok($crate::Pezpallet::<Self>::evm_tracer(tracer_type).empty_trace())
2845					}
2846				}
2847
2848				fn runtime_pallets_address() -> $crate::H160 {
2849					$crate::RUNTIME_PALLETS_ADDR
2850				}
2851
2852				fn code(address: $crate::H160) -> Vec<u8> {
2853					$crate::Pezpallet::<Self>::code(&address)
2854				}
2855
2856				fn account_id(address: $crate::H160) -> AccountId {
2857					use $crate::AddressMapper;
2858					<Self as $crate::Config>::AddressMapper::to_account_id(&address)
2859				}
2860
2861				fn new_balance_with_dust(balance: $crate::U256) -> Result<(Balance, u32), $crate::BalanceConversionError> {
2862					$crate::Pezpallet::<Self>::new_balance_with_dust(balance)
2863				}
2864			}
2865		}
2866	};
2867}