pallet_evm/
lib.rs

1// This file is part of Frontier.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # EVM Pallet
19//!
20//! The EVM pallet allows unmodified EVM code to be executed in a Substrate-based blockchain.
21//! - [`evm::Config`]
22//!
23//! ## EVM Engine
24//!
25//! The EVM pallet uses [`SputnikVM`](https://github.com/rust-blockchain/evm) as the underlying EVM engine.
26//! The engine is overhauled so that it's [`modular`](https://github.com/corepaper/evm).
27//!
28//! ## Execution Lifecycle
29//!
30//! There are a separate set of accounts managed by the EVM pallet. Substrate based accounts can call the EVM Pallet
31//! to deposit or withdraw balance from the Substrate base-currency into a different balance managed and used by
32//! the EVM pallet. Once a user has populated their balance, they can create and call smart contracts using this pallet.
33//!
34//! There's one-to-one mapping from Substrate accounts and EVM external accounts that is defined by a conversion function.
35//!
36//! ## EVM Pallet vs Ethereum Network
37//!
38//! The EVM pallet should be able to produce nearly identical results compared to the Ethereum mainnet,
39//! including gas cost and balance changes.
40//!
41//! Observable differences include:
42//!
43//! - The available length of block hashes may not be 256 depending on the configuration of the System pallet
44//!   in the Substrate runtime.
45//! - Difficulty and coinbase, which do not make sense in this pallet and is currently hard coded to zero.
46//!
47//! We currently do not aim to make unobservable behaviors, such as state root, to be the same. We also don't aim to follow
48//! the exact same transaction / receipt format. However, given one Ethereum transaction and one Substrate account's
49//! private key, one should be able to convert any Ethereum transaction into a transaction compatible with this pallet.
50//!
51//! The gas configurations are configurable. Right now, a pre-defined London hard fork configuration option is provided.
52
53// Ensure we're `no_std` when compiling for Wasm.
54#![cfg_attr(not(feature = "std"), no_std)]
55#![warn(unused_crate_dependencies)]
56#![allow(clippy::too_many_arguments)]
57#![allow(clippy::useless_conversion)]
58
59extern crate alloc;
60
61#[cfg(feature = "runtime-benchmarks")]
62pub mod benchmarking;
63
64#[cfg(test)]
65mod mock;
66pub mod runner;
67#[cfg(test)]
68mod tests;
69pub mod weights;
70
71use alloc::{borrow::Cow, collections::btree_map::BTreeMap, vec::Vec};
72use core::cmp::min;
73use ethereum::AuthorizationList;
74pub use evm::{
75	Config as EvmConfig, Context, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed,
76};
77use hash_db::Hasher;
78use impl_trait_for_tuples::impl_for_tuples;
79use scale_codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
80use scale_info::TypeInfo;
81// Substrate
82use frame_support::{
83	dispatch::{DispatchResultWithPostInfo, Pays, PostDispatchInfo},
84	storage::KeyPrefixIterator,
85	traits::{
86		fungible::{Balanced, Credit, Debt},
87		tokens::{
88			currency::Currency,
89			fungible::Inspect,
90			imbalance::{Imbalance, OnUnbalanced, SignedImbalance},
91			ExistenceRequirement, Fortitude, Precision, Preservation, WithdrawReasons,
92		},
93		FindAuthor, Get, Time,
94	},
95	weights::Weight,
96};
97use frame_system::RawOrigin;
98use sp_core::{H160, H256, U256};
99use sp_runtime::{
100	traits::{BadOrigin, NumberFor, Saturating, UniqueSaturatedInto, Zero},
101	AccountId32, DispatchErrorWithPostInfo,
102};
103// Frontier
104use fp_account::AccountId20;
105use fp_evm::GenesisAccount;
106pub use fp_evm::{
107	Account, AccountProvider, CallInfo, CreateInfo, ExecutionInfoV2 as ExecutionInfo,
108	FeeCalculator, IsPrecompileResult, LinearCostPrecompile, Log, Precompile, PrecompileFailure,
109	PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet,
110	TransactionValidationError, Vicinity,
111};
112
113pub use self::{
114	pallet::*,
115	runner::{Runner, RunnerError},
116	weights::WeightInfo,
117};
118
119#[frame_support::pallet]
120pub mod pallet {
121	use super::*;
122	use frame_support::pallet_prelude::*;
123	use frame_system::pallet_prelude::*;
124
125	#[pallet::pallet]
126	#[pallet::without_storage_info]
127	pub struct Pallet<T>(PhantomData<T>);
128
129	#[pallet::config(with_default)]
130	pub trait Config: frame_system::Config {
131		/// Account info provider.
132		#[pallet::no_default]
133		type AccountProvider: AccountProvider;
134
135		/// Calculator for current gas price.
136		type FeeCalculator: FeeCalculator;
137
138		/// Maps Ethereum gas to Substrate weight.
139		type GasWeightMapping: GasWeightMapping;
140
141		/// Weight corresponding to a gas unit.
142		type WeightPerGas: Get<Weight>;
143
144		/// Block number to block hash.
145		#[pallet::no_default]
146		type BlockHashMapping: BlockHashMapping;
147
148		/// Allow the origin to call on behalf of given address.
149		#[pallet::no_default_bounds]
150		type CallOrigin: EnsureAddressOrigin<Self::RuntimeOrigin>;
151
152		/// Allow the source address to deploy contracts directly via CREATE calls.
153		#[pallet::no_default_bounds]
154		type CreateOriginFilter: EnsureCreateOrigin<Self>;
155
156		/// Allow the source address to deploy contracts via CALL(CREATE) calls.
157		#[pallet::no_default_bounds]
158		type CreateInnerOriginFilter: EnsureCreateOrigin<Self>;
159
160		/// Allow the origin to withdraw on behalf of given address.
161		#[pallet::no_default_bounds]
162		type WithdrawOrigin: EnsureAddressOrigin<Self::RuntimeOrigin, Success = AccountIdOf<Self>>;
163
164		/// Mapping from address to account id.
165		#[pallet::no_default_bounds]
166		type AddressMapping: AddressMapping<AccountIdOf<Self>>;
167
168		/// Currency type for withdraw and balance storage.
169		#[pallet::no_default]
170		type Currency: Currency<AccountIdOf<Self>> + Inspect<AccountIdOf<Self>>;
171
172		/// Precompiles associated with this EVM engine.
173		type PrecompilesType: PrecompileSet;
174		type PrecompilesValue: Get<Self::PrecompilesType>;
175
176		/// Chain ID of EVM.
177		type ChainId: Get<u64>;
178		/// The block gas limit. Can be a simple constant, or an adjustment algorithm in another pallet.
179		type BlockGasLimit: Get<U256>;
180
181		/// EVM execution runner.
182		#[pallet::no_default]
183		type Runner: Runner<Self>;
184
185		/// To handle fee deduction for EVM transactions. An example is this pallet being used by `pallet_ethereum`
186		/// where the chain implementing `pallet_ethereum` should be able to configure what happens to the fees
187		/// Similar to `OnChargeTransaction` of `pallet_transaction_payment`
188		#[pallet::no_default_bounds]
189		type OnChargeTransaction: OnChargeEVMTransaction<Self>;
190
191		/// Called on create calls, used to record owner
192		#[pallet::no_default_bounds]
193		type OnCreate: OnCreate<Self>;
194
195		/// Find author for the current block.
196		type FindAuthor: FindAuthor<H160>;
197
198		/// Gas limit Pov size ratio.
199		type GasLimitPovSizeRatio: Get<u64>;
200
201		/// Gas limit storage growth ratio.
202		type GasLimitStorageGrowthRatio: Get<u64>;
203
204		/// Get the timestamp for the current block.
205		#[pallet::no_default]
206		type Timestamp: Time;
207
208		/// Weight information for extrinsics in this pallet.
209		type WeightInfo: WeightInfo;
210
211		/// EVM config used in the module.
212		fn config() -> &'static EvmConfig {
213			&PECTRA_CONFIG
214		}
215	}
216
217	pub mod config_preludes {
218		use super::*;
219		use core::str::FromStr;
220		use frame_support::{derive_impl, parameter_types, ConsensusEngineId};
221		use sp_runtime::traits::BlakeTwo256;
222
223		pub struct TestDefaultConfig;
224
225		#[derive_impl(
226			frame_system::config_preludes::SolochainDefaultConfig,
227			no_aggregated_types
228		)]
229		impl frame_system::DefaultConfig for TestDefaultConfig {}
230
231		const BLOCK_GAS_LIMIT: u64 = 150_000_000;
232		const MAX_POV_SIZE: u64 = 5 * 1024 * 1024;
233		/// The maximum storage growth per block in bytes.
234		const MAX_STORAGE_GROWTH: u64 = 400 * 1024;
235
236		parameter_types! {
237			pub BlockGasLimit: U256 = U256::from(BLOCK_GAS_LIMIT);
238			pub const ChainId: u64 = 42;
239			pub const GasLimitPovSizeRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_POV_SIZE);
240			pub const GasLimitStorageGrowthRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_STORAGE_GROWTH);
241			pub WeightPerGas: Weight = Weight::from_parts(20_000, 0);
242		}
243
244		#[register_default_impl(TestDefaultConfig)]
245		impl DefaultConfig for TestDefaultConfig {
246			type CallOrigin = EnsureAddressRoot<Self::AccountId>;
247			type WithdrawOrigin = EnsureAddressNever<Self::AccountId>;
248			type AddressMapping = HashedAddressMapping<BlakeTwo256>;
249			type FeeCalculator = FixedGasPrice;
250			type GasWeightMapping = FixedGasWeightMapping<Self>;
251			type WeightPerGas = WeightPerGas;
252			type PrecompilesType = ();
253			type PrecompilesValue = ();
254			type ChainId = ChainId;
255			type BlockGasLimit = BlockGasLimit;
256			type OnChargeTransaction = ();
257			type OnCreate = ();
258			type FindAuthor = FindAuthorTruncated;
259			type GasLimitPovSizeRatio = GasLimitPovSizeRatio;
260			type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio;
261			type CreateOriginFilter = ();
262			type CreateInnerOriginFilter = ();
263			type WeightInfo = ();
264		}
265
266		impl FixedGasWeightMappingAssociatedTypes for TestDefaultConfig {
267			type WeightPerGas = <Self as DefaultConfig>::WeightPerGas;
268			type BlockWeights = <Self as frame_system::DefaultConfig>::BlockWeights;
269			type GasLimitPovSizeRatio = <Self as DefaultConfig>::GasLimitPovSizeRatio;
270		}
271
272		pub struct FixedGasPrice;
273		impl FeeCalculator for FixedGasPrice {
274			fn min_gas_price() -> (U256, Weight) {
275				(1.into(), Weight::zero())
276			}
277		}
278
279		pub struct FindAuthorTruncated;
280		impl FindAuthor<H160> for FindAuthorTruncated {
281			fn find_author<'a, I>(_digests: I) -> Option<H160>
282			where
283				I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
284			{
285				Some(H160::from_str("1234500000000000000000000000000000000000").unwrap())
286			}
287		}
288	}
289
290	#[pallet::call]
291	impl<T: Config> Pallet<T> {
292		/// Withdraw balance from EVM into currency/balances pallet.
293		#[pallet::call_index(0)]
294		#[pallet::weight(<T as pallet::Config>::WeightInfo::withdraw())]
295		pub fn withdraw(
296			origin: OriginFor<T>,
297			address: H160,
298			value: BalanceOf<T>,
299		) -> DispatchResult {
300			let destination = T::WithdrawOrigin::ensure_address_origin(&address, origin)?;
301			let address_account_id = T::AddressMapping::into_account_id(address);
302
303			T::Currency::transfer(
304				&address_account_id,
305				&destination,
306				value,
307				ExistenceRequirement::AllowDeath,
308			)?;
309
310			Ok(())
311		}
312
313		/// Issue an EVM call operation. This is similar to a message call transaction in Ethereum.
314		#[pallet::call_index(1)]
315		#[pallet::weight({
316			let without_base_extrinsic_weight = true;
317			T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight)
318		})]
319		pub fn call(
320			origin: OriginFor<T>,
321			source: H160,
322			target: H160,
323			input: Vec<u8>,
324			value: U256,
325			gas_limit: u64,
326			max_fee_per_gas: U256,
327			max_priority_fee_per_gas: Option<U256>,
328			nonce: Option<U256>,
329			access_list: Vec<(H160, Vec<H256>)>,
330			authorization_list: AuthorizationList,
331		) -> DispatchResultWithPostInfo {
332			T::CallOrigin::ensure_address_origin(&source, origin)?;
333
334			let is_transactional = true;
335			let validate = true;
336			let info = match T::Runner::call(
337				source,
338				target,
339				input,
340				value,
341				gas_limit,
342				Some(max_fee_per_gas),
343				max_priority_fee_per_gas,
344				nonce,
345				access_list,
346				authorization_list,
347				is_transactional,
348				validate,
349				None,
350				None,
351				T::config(),
352			) {
353				Ok(info) => info,
354				Err(e) => {
355					return Err(DispatchErrorWithPostInfo {
356						post_info: PostDispatchInfo {
357							actual_weight: Some(e.weight),
358							pays_fee: Pays::Yes,
359						},
360						error: e.error.into(),
361					})
362				}
363			};
364
365			match info.exit_reason {
366				ExitReason::Succeed(_) => {
367					Pallet::<T>::deposit_event(Event::<T>::Executed { address: target });
368				}
369				_ => {
370					Pallet::<T>::deposit_event(Event::<T>::ExecutedFailed { address: target });
371				}
372			};
373
374			Ok(PostDispatchInfo {
375				actual_weight: {
376					let mut gas_to_weight = T::GasWeightMapping::gas_to_weight(
377						info.used_gas.standard.unique_saturated_into(),
378						true,
379					);
380					if let Some(weight_info) = info.weight_info {
381						if let Some(proof_size_usage) = weight_info.proof_size_usage {
382							*gas_to_weight.proof_size_mut() = proof_size_usage;
383						}
384					}
385					Some(gas_to_weight)
386				},
387				pays_fee: Pays::No,
388			})
389		}
390
391		/// Issue an EVM create operation. This is similar to a contract creation transaction in
392		/// Ethereum.
393		#[pallet::call_index(2)]
394		#[pallet::weight({
395			let without_base_extrinsic_weight = true;
396			T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight)
397		})]
398		pub fn create(
399			origin: OriginFor<T>,
400			source: H160,
401			init: Vec<u8>,
402			value: U256,
403			gas_limit: u64,
404			max_fee_per_gas: U256,
405			max_priority_fee_per_gas: Option<U256>,
406			nonce: Option<U256>,
407			access_list: Vec<(H160, Vec<H256>)>,
408			authorization_list: AuthorizationList,
409		) -> DispatchResultWithPostInfo {
410			T::CallOrigin::ensure_address_origin(&source, origin)?;
411
412			let is_transactional = true;
413			let validate = true;
414			let info = match T::Runner::create(
415				source,
416				init,
417				value,
418				gas_limit,
419				Some(max_fee_per_gas),
420				max_priority_fee_per_gas,
421				nonce,
422				access_list,
423				authorization_list,
424				is_transactional,
425				validate,
426				None,
427				None,
428				T::config(),
429			) {
430				Ok(info) => info,
431				Err(e) => {
432					return Err(DispatchErrorWithPostInfo {
433						post_info: PostDispatchInfo {
434							actual_weight: Some(e.weight),
435							pays_fee: Pays::Yes,
436						},
437						error: e.error.into(),
438					})
439				}
440			};
441
442			match info {
443				CreateInfo {
444					exit_reason: ExitReason::Succeed(_),
445					value: create_address,
446					..
447				} => {
448					Pallet::<T>::deposit_event(Event::<T>::Created {
449						address: create_address,
450					});
451				}
452				CreateInfo {
453					exit_reason: _,
454					value: create_address,
455					..
456				} => {
457					Pallet::<T>::deposit_event(Event::<T>::CreatedFailed {
458						address: create_address,
459					});
460				}
461			}
462
463			Ok(PostDispatchInfo {
464				actual_weight: {
465					let mut gas_to_weight = T::GasWeightMapping::gas_to_weight(
466						info.used_gas.standard.unique_saturated_into(),
467						true,
468					);
469					if let Some(weight_info) = info.weight_info {
470						if let Some(proof_size_usage) = weight_info.proof_size_usage {
471							*gas_to_weight.proof_size_mut() = proof_size_usage;
472						}
473					}
474					Some(gas_to_weight)
475				},
476				pays_fee: Pays::No,
477			})
478		}
479
480		/// Issue an EVM create2 operation.
481		#[pallet::call_index(3)]
482		#[pallet::weight({
483			let without_base_extrinsic_weight = true;
484			T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight)
485		})]
486		pub fn create2(
487			origin: OriginFor<T>,
488			source: H160,
489			init: Vec<u8>,
490			salt: H256,
491			value: U256,
492			gas_limit: u64,
493			max_fee_per_gas: U256,
494			max_priority_fee_per_gas: Option<U256>,
495			nonce: Option<U256>,
496			access_list: Vec<(H160, Vec<H256>)>,
497			authorization_list: AuthorizationList,
498		) -> DispatchResultWithPostInfo {
499			T::CallOrigin::ensure_address_origin(&source, origin)?;
500
501			let is_transactional = true;
502			let validate = true;
503			let info = match T::Runner::create2(
504				source,
505				init,
506				salt,
507				value,
508				gas_limit,
509				Some(max_fee_per_gas),
510				max_priority_fee_per_gas,
511				nonce,
512				access_list,
513				authorization_list,
514				is_transactional,
515				validate,
516				None,
517				None,
518				T::config(),
519			) {
520				Ok(info) => info,
521				Err(e) => {
522					return Err(DispatchErrorWithPostInfo {
523						post_info: PostDispatchInfo {
524							actual_weight: Some(e.weight),
525							pays_fee: Pays::Yes,
526						},
527						error: e.error.into(),
528					})
529				}
530			};
531
532			match info {
533				CreateInfo {
534					exit_reason: ExitReason::Succeed(_),
535					value: create_address,
536					..
537				} => {
538					Pallet::<T>::deposit_event(Event::<T>::Created {
539						address: create_address,
540					});
541				}
542				CreateInfo {
543					exit_reason: _,
544					value: create_address,
545					..
546				} => {
547					Pallet::<T>::deposit_event(Event::<T>::CreatedFailed {
548						address: create_address,
549					});
550				}
551			}
552
553			Ok(PostDispatchInfo {
554				actual_weight: {
555					let mut gas_to_weight = T::GasWeightMapping::gas_to_weight(
556						info.used_gas.standard.unique_saturated_into(),
557						true,
558					);
559					if let Some(weight_info) = info.weight_info {
560						if let Some(proof_size_usage) = weight_info.proof_size_usage {
561							*gas_to_weight.proof_size_mut() = proof_size_usage;
562						}
563					}
564					Some(gas_to_weight)
565				},
566				pays_fee: Pays::No,
567			})
568		}
569	}
570
571	#[pallet::event]
572	#[pallet::generate_deposit(pub(super) fn deposit_event)]
573	pub enum Event<T: Config> {
574		/// Ethereum events from contracts.
575		Log { log: Log },
576		/// A contract has been created at given address.
577		Created { address: H160 },
578		/// A contract was attempted to be created, but the execution failed.
579		CreatedFailed { address: H160 },
580		/// A contract has been executed successfully with states applied.
581		Executed { address: H160 },
582		/// A contract has been executed with errors. States are reverted with only gas fees applied.
583		ExecutedFailed { address: H160 },
584	}
585
586	#[pallet::error]
587	pub enum Error<T> {
588		/// Not enough balance to perform action
589		BalanceLow,
590		/// Calculating total fee overflowed
591		FeeOverflow,
592		/// Calculating total payment overflowed
593		PaymentOverflow,
594		/// Withdraw fee failed
595		WithdrawFailed,
596		/// Gas price is too low.
597		GasPriceTooLow,
598		/// Nonce is invalid
599		InvalidNonce,
600		/// Gas limit is too low.
601		GasLimitTooLow,
602		/// Gas limit is too high.
603		GasLimitTooHigh,
604		/// The chain id is invalid.
605		InvalidChainId,
606		/// the signature is invalid.
607		InvalidSignature,
608		/// EVM reentrancy
609		Reentrancy,
610		/// EIP-3607,
611		TransactionMustComeFromEOA,
612		/// Undefined error.
613		Undefined,
614		/// Address not allowed to deploy contracts either via CREATE or CALL(CREATE).
615		CreateOriginNotAllowed,
616	}
617
618	impl<T> From<TransactionValidationError> for Error<T> {
619		fn from(validation_error: TransactionValidationError) -> Self {
620			match validation_error {
621				TransactionValidationError::GasLimitTooLow => Error::<T>::GasLimitTooLow,
622				TransactionValidationError::GasLimitTooHigh => Error::<T>::GasLimitTooHigh,
623				TransactionValidationError::BalanceTooLow => Error::<T>::BalanceLow,
624				TransactionValidationError::TxNonceTooLow => Error::<T>::InvalidNonce,
625				TransactionValidationError::TxNonceTooHigh => Error::<T>::InvalidNonce,
626				TransactionValidationError::GasPriceTooLow => Error::<T>::GasPriceTooLow,
627				TransactionValidationError::PriorityFeeTooHigh => Error::<T>::GasPriceTooLow,
628				TransactionValidationError::InvalidFeeInput => Error::<T>::GasPriceTooLow,
629				TransactionValidationError::InvalidChainId => Error::<T>::InvalidChainId,
630				TransactionValidationError::InvalidSignature => Error::<T>::InvalidSignature,
631				TransactionValidationError::EmptyAuthorizationList => Error::<T>::Undefined,
632				TransactionValidationError::AuthorizationListTooLarge => Error::<T>::Undefined,
633				TransactionValidationError::UnknownError => Error::<T>::Undefined,
634			}
635		}
636	}
637
638	#[pallet::genesis_config]
639	#[derive(frame_support::DefaultNoBound)]
640	pub struct GenesisConfig<T> {
641		pub accounts: BTreeMap<H160, GenesisAccount>,
642		#[serde(skip)]
643		pub _marker: PhantomData<T>,
644	}
645
646	#[pallet::genesis_build]
647	impl<T: Config> BuildGenesisConfig for GenesisConfig<T>
648	where
649		U256: UniqueSaturatedInto<BalanceOf<T>>,
650	{
651		fn build(&self) {
652			const MAX_ACCOUNT_NONCE: usize = 100;
653
654			for (address, account) in &self.accounts {
655				let account_id = T::AddressMapping::into_account_id(*address);
656
657				// ASSUME: in one single EVM transaction, the nonce will not increase more than
658				// `u128::max_value()`.
659				for _ in 0..min(
660					MAX_ACCOUNT_NONCE,
661					UniqueSaturatedInto::<usize>::unique_saturated_into(account.nonce),
662				) {
663					T::AccountProvider::inc_account_nonce(&account_id);
664				}
665
666				let _ = T::Currency::deposit_creating(
667					&account_id,
668					account.balance.unique_saturated_into(),
669				);
670
671				let _ = Pallet::<T>::create_account(*address, account.code.clone(), None);
672
673				for (index, value) in &account.storage {
674					<AccountStorages<T>>::insert(address, index, value);
675				}
676			}
677		}
678	}
679
680	#[pallet::storage]
681	pub type AccountCodes<T: Config> = StorageMap<_, Blake2_128Concat, H160, Vec<u8>, ValueQuery>;
682
683	#[pallet::storage]
684	pub type AccountCodesMetadata<T: Config> =
685		StorageMap<_, Blake2_128Concat, H160, CodeMetadata, OptionQuery>;
686
687	#[pallet::storage]
688	pub type AccountStorages<T: Config> =
689		StorageDoubleMap<_, Blake2_128Concat, H160, Blake2_128Concat, H256, H256, ValueQuery>;
690}
691
692/// Utility alias for easy access to the [`AccountProvider::AccountId`] type from a given config.
693pub type AccountIdOf<T> = <<T as Config>::AccountProvider as AccountProvider>::AccountId;
694
695/// Type alias for currency balance.
696pub type BalanceOf<T> = <<T as Config>::Currency as Currency<AccountIdOf<T>>>::Balance;
697
698/// Type alias for negative imbalance during fees
699type NegativeImbalanceOf<C, T> = <C as Currency<AccountIdOf<T>>>::NegativeImbalance;
700
701#[derive(
702	Debug,
703	Clone,
704	Copy,
705	Eq,
706	PartialEq,
707	Encode,
708	Decode,
709	DecodeWithMemTracking,
710	TypeInfo,
711	MaxEncodedLen
712)]
713pub struct CodeMetadata {
714	pub size: u64,
715	pub hash: H256,
716}
717
718impl CodeMetadata {
719	pub fn from_code(code: &[u8]) -> Self {
720		let size = code.len() as u64;
721		let hash = H256::from(sp_io::hashing::keccak_256(code));
722
723		Self { size, hash }
724	}
725}
726
727pub trait EnsureAddressOrigin<OuterOrigin> {
728	/// Success return type.
729	type Success;
730
731	/// Perform the origin check.
732	fn ensure_address_origin(
733		address: &H160,
734		origin: OuterOrigin,
735	) -> Result<Self::Success, BadOrigin> {
736		Self::try_address_origin(address, origin).map_err(|_| BadOrigin)
737	}
738
739	/// Try with origin.
740	fn try_address_origin(
741		address: &H160,
742		origin: OuterOrigin,
743	) -> Result<Self::Success, OuterOrigin>;
744}
745
746/// Ensure that the EVM address is the same as the Substrate address. This only works if the account
747/// ID is `H160`.
748pub struct EnsureAddressSame;
749
750impl<OuterOrigin> EnsureAddressOrigin<OuterOrigin> for EnsureAddressSame
751where
752	OuterOrigin: Into<Result<RawOrigin<H160>, OuterOrigin>> + From<RawOrigin<H160>>,
753{
754	type Success = H160;
755
756	fn try_address_origin(address: &H160, origin: OuterOrigin) -> Result<H160, OuterOrigin> {
757		origin.into().and_then(|o| match o {
758			RawOrigin::Signed(who) if &who == address => Ok(who),
759			r => Err(OuterOrigin::from(r)),
760		})
761	}
762}
763
764/// Ensure that the origin is root.
765pub struct EnsureAddressRoot<AccountId>(core::marker::PhantomData<AccountId>);
766
767impl<OuterOrigin, AccountId> EnsureAddressOrigin<OuterOrigin> for EnsureAddressRoot<AccountId>
768where
769	OuterOrigin: Into<Result<RawOrigin<AccountId>, OuterOrigin>> + From<RawOrigin<AccountId>>,
770{
771	type Success = ();
772
773	fn try_address_origin(_address: &H160, origin: OuterOrigin) -> Result<(), OuterOrigin> {
774		origin.into().and_then(|o| match o {
775			RawOrigin::Root => Ok(()),
776			r => Err(OuterOrigin::from(r)),
777		})
778	}
779}
780
781/// Ensure that the origin never happens.
782pub struct EnsureAddressNever<AccountId>(core::marker::PhantomData<AccountId>);
783
784impl<OuterOrigin, AccountId> EnsureAddressOrigin<OuterOrigin> for EnsureAddressNever<AccountId> {
785	type Success = AccountId;
786
787	fn try_address_origin(_address: &H160, origin: OuterOrigin) -> Result<AccountId, OuterOrigin> {
788		Err(origin)
789	}
790}
791
792/// Ensure that the address is truncated hash of the origin. Only works if the account id is
793/// `AccountId32`.
794pub struct EnsureAddressTruncated;
795
796impl<OuterOrigin> EnsureAddressOrigin<OuterOrigin> for EnsureAddressTruncated
797where
798	OuterOrigin: Into<Result<RawOrigin<AccountId32>, OuterOrigin>> + From<RawOrigin<AccountId32>>,
799{
800	type Success = AccountId32;
801
802	fn try_address_origin(address: &H160, origin: OuterOrigin) -> Result<AccountId32, OuterOrigin> {
803		origin.into().and_then(|o| match o {
804			RawOrigin::Signed(who) if AsRef::<[u8; 32]>::as_ref(&who)[0..20] == address[0..20] => {
805				Ok(who)
806			}
807			r => Err(OuterOrigin::from(r)),
808		})
809	}
810}
811
812/// Ensure that the address is AccountId20.
813pub struct EnsureAccountId20;
814
815impl<OuterOrigin> EnsureAddressOrigin<OuterOrigin> for EnsureAccountId20
816where
817	OuterOrigin: Into<Result<RawOrigin<AccountId20>, OuterOrigin>> + From<RawOrigin<AccountId20>>,
818{
819	type Success = AccountId20;
820
821	fn try_address_origin(address: &H160, origin: OuterOrigin) -> Result<AccountId20, OuterOrigin> {
822		let acc: AccountId20 = AccountId20::from(*address);
823		origin.into().and_then(|o| match o {
824			RawOrigin::Signed(who) if who == acc => Ok(who),
825			r => Err(OuterOrigin::from(r)),
826		})
827	}
828}
829
830pub trait EnsureCreateOrigin<T> {
831	fn check_create_origin(address: &H160) -> Result<(), Error<T>>;
832}
833
834pub struct EnsureAllowedCreateAddress<AddressGetter>(core::marker::PhantomData<AddressGetter>);
835
836impl<AddressGetter, T: Config> EnsureCreateOrigin<T> for EnsureAllowedCreateAddress<AddressGetter>
837where
838	AddressGetter: Get<Vec<H160>>,
839{
840	fn check_create_origin(address: &H160) -> Result<(), Error<T>> {
841		if !AddressGetter::get().contains(address) {
842			return Err(Error::<T>::CreateOriginNotAllowed);
843		}
844		Ok(())
845	}
846}
847
848impl<T> EnsureCreateOrigin<T> for () {
849	fn check_create_origin(_address: &H160) -> Result<(), Error<T>> {
850		Ok(())
851	}
852}
853
854/// Trait to be implemented for evm address mapping.
855pub trait AddressMapping<A> {
856	fn into_account_id(address: H160) -> A;
857}
858
859/// Identity address mapping.
860pub struct IdentityAddressMapping;
861
862impl<T: From<H160>> AddressMapping<T> for IdentityAddressMapping {
863	fn into_account_id(address: H160) -> T {
864		address.into()
865	}
866}
867
868/// Hashed address mapping.
869pub struct HashedAddressMapping<H>(core::marker::PhantomData<H>);
870
871impl<H: Hasher<Out = H256>> AddressMapping<AccountId32> for HashedAddressMapping<H> {
872	fn into_account_id(address: H160) -> AccountId32 {
873		let mut data = [0u8; 24];
874		data[0..4].copy_from_slice(b"evm:");
875		data[4..24].copy_from_slice(&address[..]);
876		let hash = H::hash(&data);
877
878		AccountId32::from(Into::<[u8; 32]>::into(hash))
879	}
880}
881
882/// A trait for getting a block hash by number.
883pub trait BlockHashMapping {
884	fn block_hash(number: u32) -> H256;
885}
886
887/// Returns the Substrate block hash by number.
888pub struct SubstrateBlockHashMapping<T>(core::marker::PhantomData<T>);
889impl<T: Config> BlockHashMapping for SubstrateBlockHashMapping<T> {
890	fn block_hash(number: u32) -> H256 {
891		let number = <NumberFor<T::Block>>::from(number);
892		H256::from_slice(frame_system::Pallet::<T>::block_hash(number).as_ref())
893	}
894}
895
896/// A mapping function that converts Ethereum gas to Substrate weight
897pub trait GasWeightMapping {
898	fn gas_to_weight(gas: u64, without_base_weight: bool) -> Weight;
899	fn weight_to_gas(weight: Weight) -> u64;
900}
901
902pub trait FixedGasWeightMappingAssociatedTypes {
903	type WeightPerGas: Get<Weight>;
904	type BlockWeights: Get<frame_system::limits::BlockWeights>;
905	type GasLimitPovSizeRatio: Get<u64>;
906}
907
908impl<T: Config> FixedGasWeightMappingAssociatedTypes for T {
909	type WeightPerGas = T::WeightPerGas;
910	type BlockWeights = T::BlockWeights;
911	type GasLimitPovSizeRatio = T::GasLimitPovSizeRatio;
912}
913
914pub struct FixedGasWeightMapping<T>(core::marker::PhantomData<T>);
915impl<T> GasWeightMapping for FixedGasWeightMapping<T>
916where
917	T: FixedGasWeightMappingAssociatedTypes,
918{
919	fn gas_to_weight(gas: u64, without_base_weight: bool) -> Weight {
920		let mut weight = T::WeightPerGas::get().saturating_mul(gas);
921		if without_base_weight {
922			weight = weight.saturating_sub(
923				T::BlockWeights::get()
924					.get(frame_support::dispatch::DispatchClass::Normal)
925					.base_extrinsic,
926			);
927		}
928		// Apply a gas to proof size ratio based on BlockGasLimit
929		let ratio = T::GasLimitPovSizeRatio::get();
930		if ratio > 0 {
931			let proof_size = gas.saturating_div(ratio);
932			*weight.proof_size_mut() = proof_size;
933		}
934
935		weight
936	}
937	fn weight_to_gas(weight: Weight) -> u64 {
938		weight.div(T::WeightPerGas::get().ref_time()).ref_time()
939	}
940}
941
942static PECTRA_CONFIG: EvmConfig = EvmConfig::pectra();
943
944impl<T: Config> Pallet<T> {
945	/// Check whether an account is empty.
946	pub fn is_account_empty(address: &H160) -> bool {
947		let (account, _) = Self::account_basic(address);
948		let code_len = <AccountCodes<T>>::decode_len(address).unwrap_or(0);
949
950		account.nonce == U256::zero() && account.balance == U256::zero() && code_len == 0
951	}
952
953	pub fn iter_account_storages(address: &H160) -> KeyPrefixIterator<H256> {
954		<AccountStorages<T>>::iter_key_prefix(address)
955	}
956
957	/// Remove an account if its empty.
958	pub fn remove_account_if_empty(address: &H160) {
959		if Self::is_account_empty(address) {
960			Self::remove_account(address);
961		}
962	}
963
964	/// Remove an account.
965	pub fn remove_account(address: &H160) {
966		let account_id = T::AddressMapping::into_account_id(*address);
967		T::AccountProvider::remove_account(&account_id);
968
969		<AccountCodes<T>>::remove(address);
970		<AccountCodesMetadata<T>>::remove(address);
971		let _ = <AccountStorages<T>>::clear_prefix(address, u32::MAX, None);
972	}
973
974	/// Remove an account's code if present.
975	pub fn remove_account_code(address: &H160) {
976		<AccountCodes<T>>::remove(address);
977		<AccountCodesMetadata<T>>::remove(address);
978	}
979
980	/// Create an account.
981	pub fn create_account(
982		address: H160,
983		code: Vec<u8>,
984		caller: Option<H160>,
985	) -> Result<(), ExitError> {
986		if let Some(caller_address) = caller {
987			T::CreateInnerOriginFilter::check_create_origin(&caller_address).map_err(|e| {
988				let error: &'static str = e.into();
989				ExitError::Other(Cow::Borrowed(error))
990			})?;
991		}
992
993		if code.is_empty() {
994			return Ok(());
995		}
996
997		if !<AccountCodes<T>>::contains_key(address) {
998			let account_id = T::AddressMapping::into_account_id(address);
999			T::AccountProvider::create_account(&account_id);
1000		}
1001
1002		// Update metadata.
1003		let meta = CodeMetadata::from_code(&code);
1004		<AccountCodesMetadata<T>>::insert(address, meta);
1005
1006		<AccountCodes<T>>::insert(address, code);
1007		Ok(())
1008	}
1009
1010	/// Get the account metadata (hash and size) from storage if it exists,
1011	/// or compute it from code and store it if it doesn't exist.
1012	pub fn account_code_metadata(address: H160) -> CodeMetadata {
1013		if let Some(meta) = <AccountCodesMetadata<T>>::get(address) {
1014			return meta;
1015		}
1016
1017		let code = <AccountCodes<T>>::get(address);
1018
1019		// If code is empty we return precomputed hash for empty code.
1020		// We don't store it as this address could get code deployed in the future.
1021		if code.is_empty() {
1022			const EMPTY_CODE_HASH: [u8; 32] = hex_literal::hex!(
1023				"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
1024			);
1025			return CodeMetadata {
1026				size: 0,
1027				hash: EMPTY_CODE_HASH.into(),
1028			};
1029		}
1030
1031		let meta = CodeMetadata::from_code(&code);
1032
1033		<AccountCodesMetadata<T>>::insert(address, meta);
1034		meta
1035	}
1036
1037	/// Get the account basic in EVM format.
1038	pub fn account_basic(address: &H160) -> (Account, frame_support::weights::Weight) {
1039		let account_id = T::AddressMapping::into_account_id(*address);
1040		let nonce = T::AccountProvider::account_nonce(&account_id);
1041		let balance =
1042			T::Currency::reducible_balance(&account_id, Preservation::Preserve, Fortitude::Polite);
1043
1044		(
1045			Account {
1046				nonce: U256::from(UniqueSaturatedInto::<u128>::unique_saturated_into(nonce)),
1047				balance: U256::from(UniqueSaturatedInto::<u128>::unique_saturated_into(balance)),
1048			},
1049			T::DbWeight::get().reads(2),
1050		)
1051	}
1052
1053	/// Get the author using the FindAuthor trait.
1054	pub fn find_author() -> H160 {
1055		let digest = <frame_system::Pallet<T>>::digest();
1056		let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
1057
1058		T::FindAuthor::find_author(pre_runtime_digests).unwrap_or_default()
1059	}
1060}
1061
1062/// Handle withdrawing, refunding and depositing of transaction fees.
1063/// Similar to `OnChargeTransaction` of `pallet_transaction_payment`
1064pub trait OnChargeEVMTransaction<T: Config> {
1065	type LiquidityInfo: Default;
1066
1067	/// Before the transaction is executed the payment of the transaction fees
1068	/// need to be secured.
1069	fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, Error<T>>;
1070
1071	/// After the transaction was executed the actual fee can be calculated.
1072	/// This function should refund any overpaid fees and optionally deposit
1073	/// the corrected amount, and handles the base fee rationing using the provided
1074	/// `OnUnbalanced` implementation.
1075	/// Returns the `NegativeImbalance` - if any - produced by the priority fee.
1076	fn correct_and_deposit_fee(
1077		who: &H160,
1078		corrected_fee: U256,
1079		base_fee: U256,
1080		already_withdrawn: Self::LiquidityInfo,
1081	) -> Self::LiquidityInfo;
1082
1083	/// Introduced in EIP1559 to handle the priority tip.
1084	fn pay_priority_fee(tip: Self::LiquidityInfo);
1085}
1086
1087/// Implements the transaction payment for a pallet implementing the `Currency`
1088/// trait (eg. the pallet_balances) using an unbalance handler (implementing
1089/// `OnUnbalanced`).
1090/// Similar to `CurrencyAdapter` of `pallet_transaction_payment`
1091pub struct EVMCurrencyAdapter<C, OU>(core::marker::PhantomData<(C, OU)>);
1092
1093impl<T, C, OU> OnChargeEVMTransaction<T> for EVMCurrencyAdapter<C, OU>
1094where
1095	T: Config,
1096	C: Currency<AccountIdOf<T>>,
1097	C::PositiveImbalance:
1098		Imbalance<<C as Currency<AccountIdOf<T>>>::Balance, Opposite = C::NegativeImbalance>,
1099	C::NegativeImbalance:
1100		Imbalance<<C as Currency<AccountIdOf<T>>>::Balance, Opposite = C::PositiveImbalance>,
1101	OU: OnUnbalanced<NegativeImbalanceOf<C, T>>,
1102	U256: UniqueSaturatedInto<<C as Currency<AccountIdOf<T>>>::Balance>,
1103{
1104	// Kept type as Option to satisfy bound of Default
1105	type LiquidityInfo = Option<NegativeImbalanceOf<C, T>>;
1106
1107	fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, Error<T>> {
1108		if fee.is_zero() {
1109			return Ok(None);
1110		}
1111		let account_id = T::AddressMapping::into_account_id(*who);
1112		let imbalance = C::withdraw(
1113			&account_id,
1114			fee.unique_saturated_into(),
1115			WithdrawReasons::FEE,
1116			ExistenceRequirement::AllowDeath,
1117		)
1118		.map_err(|_| Error::<T>::BalanceLow)?;
1119		Ok(Some(imbalance))
1120	}
1121
1122	fn correct_and_deposit_fee(
1123		who: &H160,
1124		corrected_fee: U256,
1125		base_fee: U256,
1126		already_withdrawn: Self::LiquidityInfo,
1127	) -> Self::LiquidityInfo {
1128		if let Some(paid) = already_withdrawn {
1129			let account_id = T::AddressMapping::into_account_id(*who);
1130
1131			// Calculate how much refund we should return
1132			let refund_amount = paid
1133				.peek()
1134				.saturating_sub(corrected_fee.unique_saturated_into());
1135			// refund to the account that paid the fees. If this fails, the
1136			// account might have dropped below the existential balance. In
1137			// that case we don't refund anything.
1138			let refund_imbalance = C::deposit_into_existing(&account_id, refund_amount)
1139				.unwrap_or_else(|_| C::PositiveImbalance::zero());
1140
1141			// Make sure this works with 0 ExistentialDeposit
1142			// https://github.com/paritytech/substrate/issues/10117
1143			// If we tried to refund something, the account still empty and the ED is set to 0,
1144			// we call `make_free_balance_be` with the refunded amount.
1145			let refund_imbalance = if C::minimum_balance().is_zero()
1146				&& refund_amount > C::Balance::zero()
1147				&& C::total_balance(&account_id).is_zero()
1148			{
1149				// Known bug: Substrate tried to refund to a zeroed AccountData, but
1150				// interpreted the account to not exist.
1151				match C::make_free_balance_be(&account_id, refund_amount) {
1152					SignedImbalance::Positive(p) => p,
1153					_ => C::PositiveImbalance::zero(),
1154				}
1155			} else {
1156				refund_imbalance
1157			};
1158
1159			// merge the imbalance caused by paying the fees and refunding parts of it again.
1160			let adjusted_paid = paid
1161				.offset(refund_imbalance)
1162				.same()
1163				.unwrap_or_else(|_| C::NegativeImbalance::zero());
1164
1165			let (base_fee, tip) = adjusted_paid.split(base_fee.unique_saturated_into());
1166			// Handle base fee. Can be either burned, rationed, etc ...
1167			OU::on_unbalanced(base_fee);
1168			return Some(tip);
1169		}
1170		None
1171	}
1172
1173	fn pay_priority_fee(tip: Self::LiquidityInfo) {
1174		// Default Ethereum behaviour: issue the tip to the block author.
1175		if let Some(tip) = tip {
1176			let account_id = T::AddressMapping::into_account_id(<Pallet<T>>::find_author());
1177			let _ = C::deposit_into_existing(&account_id, tip.peek());
1178		}
1179	}
1180}
1181/// Implements transaction payment for a pallet implementing the [`fungible`]
1182/// trait (eg. pallet_balances) using an unbalance handler (implementing
1183/// [`OnUnbalanced`]).
1184///
1185/// Equivalent of `EVMCurrencyAdapter` but for fungible traits. Similar to `FungibleAdapter` of
1186/// `pallet_transaction_payment`
1187pub struct EVMFungibleAdapter<F, OU>(core::marker::PhantomData<(F, OU)>);
1188
1189impl<T, F, OU> OnChargeEVMTransaction<T> for EVMFungibleAdapter<F, OU>
1190where
1191	T: Config,
1192	F: Balanced<AccountIdOf<T>>,
1193	OU: OnUnbalanced<Credit<AccountIdOf<T>, F>>,
1194	U256: UniqueSaturatedInto<<F as Inspect<AccountIdOf<T>>>::Balance>,
1195{
1196	// Kept type as Option to satisfy bound of Default
1197	type LiquidityInfo = Option<Credit<AccountIdOf<T>, F>>;
1198
1199	fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, Error<T>> {
1200		if fee.is_zero() {
1201			return Ok(None);
1202		}
1203		let account_id = T::AddressMapping::into_account_id(*who);
1204		let imbalance = F::withdraw(
1205			&account_id,
1206			fee.unique_saturated_into(),
1207			Precision::Exact,
1208			Preservation::Preserve,
1209			Fortitude::Polite,
1210		)
1211		.map_err(|_| Error::<T>::BalanceLow)?;
1212		Ok(Some(imbalance))
1213	}
1214
1215	fn correct_and_deposit_fee(
1216		who: &H160,
1217		corrected_fee: U256,
1218		base_fee: U256,
1219		already_withdrawn: Self::LiquidityInfo,
1220	) -> Self::LiquidityInfo {
1221		if let Some(paid) = already_withdrawn {
1222			let account_id = T::AddressMapping::into_account_id(*who);
1223
1224			// Calculate how much refund we should return
1225			let refund_amount = paid
1226				.peek()
1227				.saturating_sub(corrected_fee.unique_saturated_into());
1228			// refund to the account that paid the fees.
1229			let refund_imbalance = F::deposit(&account_id, refund_amount, Precision::BestEffort)
1230				.unwrap_or_else(|_| Debt::<AccountIdOf<T>, F>::zero());
1231
1232			// merge the imbalance caused by paying the fees and refunding parts of it again.
1233			let adjusted_paid = paid
1234				.offset(refund_imbalance)
1235				.same()
1236				.unwrap_or_else(|_| Credit::<AccountIdOf<T>, F>::zero());
1237
1238			let (base_fee, tip) = adjusted_paid.split(base_fee.unique_saturated_into());
1239			// Handle base fee. Can be either burned, rationed, etc ...
1240			OU::on_unbalanced(base_fee);
1241			return Some(tip);
1242		}
1243		None
1244	}
1245
1246	fn pay_priority_fee(tip: Self::LiquidityInfo) {
1247		// Default Ethereum behaviour: issue the tip to the block author.
1248		if let Some(tip) = tip {
1249			let account_id = T::AddressMapping::into_account_id(<Pallet<T>>::find_author());
1250			let _ = F::deposit(&account_id, tip.peek(), Precision::BestEffort);
1251		}
1252	}
1253}
1254
1255/// Implementation for () does not specify what to do with imbalance
1256impl<T> OnChargeEVMTransaction<T> for ()
1257where
1258	T: Config,
1259	T::Currency: Balanced<AccountIdOf<T>>,
1260	U256: UniqueSaturatedInto<<<T as Config>::Currency as Inspect<AccountIdOf<T>>>::Balance>,
1261{
1262	// Kept type as Option to satisfy bound of Default
1263	type LiquidityInfo = Option<Credit<AccountIdOf<T>, T::Currency>>;
1264
1265	fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, Error<T>> {
1266		EVMFungibleAdapter::<T::Currency, ()>::withdraw_fee(who, fee)
1267	}
1268
1269	fn correct_and_deposit_fee(
1270		who: &H160,
1271		corrected_fee: U256,
1272		base_fee: U256,
1273		already_withdrawn: Self::LiquidityInfo,
1274	) -> Self::LiquidityInfo {
1275		<EVMFungibleAdapter<T::Currency, ()> as OnChargeEVMTransaction<T>>::correct_and_deposit_fee(
1276			who,
1277			corrected_fee,
1278			base_fee,
1279			already_withdrawn,
1280		)
1281	}
1282
1283	fn pay_priority_fee(tip: Self::LiquidityInfo) {
1284		<EVMFungibleAdapter<T::Currency, ()> as OnChargeEVMTransaction<T>>::pay_priority_fee(tip);
1285	}
1286}
1287
1288pub trait OnCreate<T> {
1289	fn on_create(owner: H160, contract: H160);
1290}
1291
1292impl<T> OnCreate<T> for () {
1293	fn on_create(_owner: H160, _contract: H160) {}
1294}
1295
1296#[impl_for_tuples(1, 12)]
1297impl<T> OnCreate<T> for Tuple {
1298	fn on_create(owner: H160, contract: H160) {
1299		for_tuples!(#(
1300			Tuple::on_create(owner, contract);
1301		)*)
1302	}
1303}
1304
1305/// EVM account provider based on the [`frame_system`] accounts.
1306///
1307/// Uses standard Substrate accounts system to hold EVM accounts.
1308pub struct FrameSystemAccountProvider<T>(core::marker::PhantomData<T>);
1309
1310impl<T: frame_system::Config> AccountProvider for FrameSystemAccountProvider<T> {
1311	type AccountId = T::AccountId;
1312	type Nonce = T::Nonce;
1313
1314	fn account_nonce(who: &Self::AccountId) -> Self::Nonce {
1315		frame_system::Pallet::<T>::account_nonce(who)
1316	}
1317
1318	fn inc_account_nonce(who: &Self::AccountId) {
1319		frame_system::Pallet::<T>::inc_account_nonce(who)
1320	}
1321
1322	fn create_account(who: &Self::AccountId) {
1323		let _ = frame_system::Pallet::<T>::inc_sufficients(who);
1324	}
1325
1326	fn remove_account(who: &Self::AccountId) {
1327		let _ = frame_system::Pallet::<T>::dec_sufficients(who);
1328	}
1329}