1#![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;
81use 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};
103use 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 #[pallet::no_default]
133 type AccountProvider: AccountProvider;
134
135 type FeeCalculator: FeeCalculator;
137
138 type GasWeightMapping: GasWeightMapping;
140
141 type WeightPerGas: Get<Weight>;
143
144 #[pallet::no_default]
146 type BlockHashMapping: BlockHashMapping;
147
148 #[pallet::no_default_bounds]
150 type CallOrigin: EnsureAddressOrigin<Self::RuntimeOrigin>;
151
152 #[pallet::no_default_bounds]
154 type CreateOriginFilter: EnsureCreateOrigin<Self>;
155
156 #[pallet::no_default_bounds]
158 type CreateInnerOriginFilter: EnsureCreateOrigin<Self>;
159
160 #[pallet::no_default_bounds]
162 type WithdrawOrigin: EnsureAddressOrigin<Self::RuntimeOrigin, Success = AccountIdOf<Self>>;
163
164 #[pallet::no_default_bounds]
166 type AddressMapping: AddressMapping<AccountIdOf<Self>>;
167
168 #[pallet::no_default]
170 type Currency: Currency<AccountIdOf<Self>> + Inspect<AccountIdOf<Self>>;
171
172 type PrecompilesType: PrecompileSet;
174 type PrecompilesValue: Get<Self::PrecompilesType>;
175
176 type ChainId: Get<u64>;
178 type BlockGasLimit: Get<U256>;
180
181 #[pallet::no_default]
183 type Runner: Runner<Self>;
184
185 #[pallet::no_default_bounds]
189 type OnChargeTransaction: OnChargeEVMTransaction<Self>;
190
191 #[pallet::no_default_bounds]
193 type OnCreate: OnCreate<Self>;
194
195 type FindAuthor: FindAuthor<H160>;
197
198 type GasLimitPovSizeRatio: Get<u64>;
200
201 type GasLimitStorageGrowthRatio: Get<u64>;
203
204 #[pallet::no_default]
206 type Timestamp: Time;
207
208 type WeightInfo: WeightInfo;
210
211 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 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 #[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 #[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 #[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 #[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 Log { log: Log },
576 Created { address: H160 },
578 CreatedFailed { address: H160 },
580 Executed { address: H160 },
582 ExecutedFailed { address: H160 },
584 }
585
586 #[pallet::error]
587 pub enum Error<T> {
588 BalanceLow,
590 FeeOverflow,
592 PaymentOverflow,
594 WithdrawFailed,
596 GasPriceTooLow,
598 InvalidNonce,
600 GasLimitTooLow,
602 GasLimitTooHigh,
604 InvalidChainId,
606 InvalidSignature,
608 Reentrancy,
610 TransactionMustComeFromEOA,
612 Undefined,
614 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 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
692pub type AccountIdOf<T> = <<T as Config>::AccountProvider as AccountProvider>::AccountId;
694
695pub type BalanceOf<T> = <<T as Config>::Currency as Currency<AccountIdOf<T>>>::Balance;
697
698type 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 type Success;
730
731 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 fn try_address_origin(
741 address: &H160,
742 origin: OuterOrigin,
743 ) -> Result<Self::Success, OuterOrigin>;
744}
745
746pub 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
764pub 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
781pub 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
792pub 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
812pub 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
854pub trait AddressMapping<A> {
856 fn into_account_id(address: H160) -> A;
857}
858
859pub 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
868pub 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
882pub trait BlockHashMapping {
884 fn block_hash(number: u32) -> H256;
885}
886
887pub 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
896pub 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 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 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 pub fn remove_account_if_empty(address: &H160) {
959 if Self::is_account_empty(address) {
960 Self::remove_account(address);
961 }
962 }
963
964 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 pub fn remove_account_code(address: &H160) {
976 <AccountCodes<T>>::remove(address);
977 <AccountCodesMetadata<T>>::remove(address);
978 }
979
980 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 let meta = CodeMetadata::from_code(&code);
1004 <AccountCodesMetadata<T>>::insert(address, meta);
1005
1006 <AccountCodes<T>>::insert(address, code);
1007 Ok(())
1008 }
1009
1010 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() {
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 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 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
1062pub trait OnChargeEVMTransaction<T: Config> {
1065 type LiquidityInfo: Default;
1066
1067 fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, Error<T>>;
1070
1071 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 fn pay_priority_fee(tip: Self::LiquidityInfo);
1085}
1086
1087pub 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 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 let refund_amount = paid
1133 .peek()
1134 .saturating_sub(corrected_fee.unique_saturated_into());
1135 let refund_imbalance = C::deposit_into_existing(&account_id, refund_amount)
1139 .unwrap_or_else(|_| C::PositiveImbalance::zero());
1140
1141 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 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 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 OU::on_unbalanced(base_fee);
1168 return Some(tip);
1169 }
1170 None
1171 }
1172
1173 fn pay_priority_fee(tip: Self::LiquidityInfo) {
1174 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}
1181pub 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 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 let refund_amount = paid
1226 .peek()
1227 .saturating_sub(corrected_fee.unique_saturated_into());
1228 let refund_imbalance = F::deposit(&account_id, refund_amount, Precision::BestEffort)
1230 .unwrap_or_else(|_| Debt::<AccountIdOf<T>, F>::zero());
1231
1232 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 OU::on_unbalanced(base_fee);
1241 return Some(tip);
1242 }
1243 None
1244 }
1245
1246 fn pay_priority_fee(tip: Self::LiquidityInfo) {
1247 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
1255impl<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 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
1305pub 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}