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, EVM_CONFIG,
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 type TransactionGasLimit: Get<Option<U256>>;
185
186 #[pallet::no_default]
188 type Runner: Runner<Self>;
189
190 #[pallet::no_default_bounds]
194 type OnChargeTransaction: OnChargeEVMTransaction<Self>;
195
196 #[pallet::no_default_bounds]
198 type OnCreate: OnCreate<Self>;
199
200 type FindAuthor: FindAuthor<H160>;
202
203 type GasLimitPovSizeRatio: Get<u64>;
205
206 type GasLimitStorageGrowthRatio: Get<u64>;
208
209 #[pallet::no_default]
211 type Timestamp: Time;
212
213 type WeightInfo: WeightInfo;
215
216 fn config() -> &'static EvmConfig {
218 &EVM_CONFIG
219 }
220 }
221
222 pub mod config_preludes {
223 use super::*;
224 use core::str::FromStr;
225 use frame_support::{derive_impl, parameter_types, ConsensusEngineId};
226 use sp_runtime::traits::BlakeTwo256;
227
228 pub struct TestDefaultConfig;
229
230 #[derive_impl(
231 frame_system::config_preludes::SolochainDefaultConfig,
232 no_aggregated_types
233 )]
234 impl frame_system::DefaultConfig for TestDefaultConfig {}
235
236 const BLOCK_GAS_LIMIT: u64 = 150_000_000;
237 const MAX_POV_SIZE: u64 = 5 * 1024 * 1024;
238 const MAX_STORAGE_GROWTH: u64 = 400 * 1024;
240
241 parameter_types! {
242 pub BlockGasLimit: U256 = U256::from(BLOCK_GAS_LIMIT);
243 pub TransactionGasLimit: Option<U256> = Some(fp_evm::MAX_TRANSACTION_GAS_LIMIT);
244 pub const ChainId: u64 = 42;
245 pub const GasLimitPovSizeRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_POV_SIZE);
246 pub const GasLimitStorageGrowthRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_STORAGE_GROWTH);
247 pub WeightPerGas: Weight = Weight::from_parts(20_000, 0);
248 }
249
250 #[register_default_impl(TestDefaultConfig)]
251 impl DefaultConfig for TestDefaultConfig {
252 type CallOrigin = EnsureAddressRoot<Self::AccountId>;
253 type WithdrawOrigin = EnsureAddressNever<Self::AccountId>;
254 type AddressMapping = HashedAddressMapping<BlakeTwo256>;
255 type FeeCalculator = FixedGasPrice;
256 type GasWeightMapping = FixedGasWeightMapping<Self>;
257 type WeightPerGas = WeightPerGas;
258 type PrecompilesType = ();
259 type PrecompilesValue = ();
260 type ChainId = ChainId;
261 type BlockGasLimit = BlockGasLimit;
262 type TransactionGasLimit = TransactionGasLimit;
263 type OnChargeTransaction = ();
264 type OnCreate = ();
265 type FindAuthor = FindAuthorTruncated;
266 type GasLimitPovSizeRatio = GasLimitPovSizeRatio;
267 type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio;
268 type CreateOriginFilter = ();
269 type CreateInnerOriginFilter = ();
270 type WeightInfo = ();
271 }
272
273 impl FixedGasWeightMappingAssociatedTypes for TestDefaultConfig {
274 type WeightPerGas = <Self as DefaultConfig>::WeightPerGas;
275 type BlockWeights = <Self as frame_system::DefaultConfig>::BlockWeights;
276 type GasLimitPovSizeRatio = <Self as DefaultConfig>::GasLimitPovSizeRatio;
277 }
278
279 pub struct FixedGasPrice;
280 impl FeeCalculator for FixedGasPrice {
281 fn min_gas_price() -> (U256, Weight) {
282 (1.into(), Weight::zero())
283 }
284 }
285
286 pub struct FindAuthorTruncated;
287 impl FindAuthor<H160> for FindAuthorTruncated {
288 fn find_author<'a, I>(_digests: I) -> Option<H160>
289 where
290 I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
291 {
292 Some(H160::from_str("1234500000000000000000000000000000000000").unwrap())
293 }
294 }
295 }
296
297 #[pallet::call]
298 impl<T: Config> Pallet<T> {
299 #[pallet::call_index(0)]
301 #[pallet::weight(<T as pallet::Config>::WeightInfo::withdraw())]
302 pub fn withdraw(
303 origin: OriginFor<T>,
304 address: H160,
305 value: BalanceOf<T>,
306 ) -> DispatchResult {
307 let destination = T::WithdrawOrigin::ensure_address_origin(&address, origin)?;
308 let address_account_id = T::AddressMapping::into_account_id(address);
309
310 T::Currency::transfer(
311 &address_account_id,
312 &destination,
313 value,
314 ExistenceRequirement::AllowDeath,
315 )?;
316
317 Ok(())
318 }
319
320 #[pallet::call_index(1)]
322 #[pallet::weight({
323 let without_base_extrinsic_weight = true;
324 T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight)
325 })]
326 pub fn call(
327 origin: OriginFor<T>,
328 source: H160,
329 target: H160,
330 input: Vec<u8>,
331 value: U256,
332 gas_limit: u64,
333 max_fee_per_gas: U256,
334 max_priority_fee_per_gas: Option<U256>,
335 nonce: Option<U256>,
336 access_list: Vec<(H160, Vec<H256>)>,
337 authorization_list: AuthorizationList,
338 ) -> DispatchResultWithPostInfo {
339 T::CallOrigin::ensure_address_origin(&source, origin)?;
340
341 let is_transactional = true;
342 let validate = true;
343 let info = match T::Runner::call(
344 source,
345 target,
346 input,
347 value,
348 gas_limit,
349 Some(max_fee_per_gas),
350 max_priority_fee_per_gas,
351 nonce,
352 access_list,
353 authorization_list,
354 is_transactional,
355 validate,
356 None,
357 None,
358 None,
359 T::config(),
360 ) {
361 Ok(info) => info,
362 Err(e) => {
363 return Err(DispatchErrorWithPostInfo {
364 post_info: PostDispatchInfo {
365 actual_weight: Some(e.weight),
366 pays_fee: Pays::Yes,
367 },
368 error: e.error.into(),
369 })
370 }
371 };
372
373 match info.exit_reason {
374 ExitReason::Succeed(_) => {
375 Pallet::<T>::deposit_event(Event::<T>::Executed { address: target });
376 }
377 _ => {
378 Pallet::<T>::deposit_event(Event::<T>::ExecutedFailed { address: target });
379 }
380 };
381
382 Ok(PostDispatchInfo {
383 actual_weight: {
384 let mut gas_to_weight = T::GasWeightMapping::gas_to_weight(
385 info.used_gas.standard.unique_saturated_into(),
386 true,
387 );
388 if let Some(weight_info) = info.weight_info {
389 if let Some(proof_size_usage) = weight_info.proof_size_usage {
390 *gas_to_weight.proof_size_mut() = proof_size_usage;
391 }
392 }
393 Some(gas_to_weight)
394 },
395 pays_fee: Pays::No,
396 })
397 }
398
399 #[pallet::call_index(2)]
402 #[pallet::weight({
403 let without_base_extrinsic_weight = true;
404 T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight)
405 })]
406 pub fn create(
407 origin: OriginFor<T>,
408 source: H160,
409 init: Vec<u8>,
410 value: U256,
411 gas_limit: u64,
412 max_fee_per_gas: U256,
413 max_priority_fee_per_gas: Option<U256>,
414 nonce: Option<U256>,
415 access_list: Vec<(H160, Vec<H256>)>,
416 authorization_list: AuthorizationList,
417 ) -> DispatchResultWithPostInfo {
418 T::CallOrigin::ensure_address_origin(&source, origin)?;
419
420 let is_transactional = true;
421 let validate = true;
422 let info = match T::Runner::create(
423 source,
424 init,
425 value,
426 gas_limit,
427 Some(max_fee_per_gas),
428 max_priority_fee_per_gas,
429 nonce,
430 access_list,
431 authorization_list,
432 is_transactional,
433 validate,
434 None,
435 None,
436 T::config(),
437 ) {
438 Ok(info) => info,
439 Err(e) => {
440 return Err(DispatchErrorWithPostInfo {
441 post_info: PostDispatchInfo {
442 actual_weight: Some(e.weight),
443 pays_fee: Pays::Yes,
444 },
445 error: e.error.into(),
446 })
447 }
448 };
449
450 match info {
451 CreateInfo {
452 exit_reason: ExitReason::Succeed(_),
453 value: create_address,
454 ..
455 } => {
456 Pallet::<T>::deposit_event(Event::<T>::Created {
457 address: create_address,
458 });
459 }
460 CreateInfo {
461 exit_reason: _,
462 value: create_address,
463 ..
464 } => {
465 Pallet::<T>::deposit_event(Event::<T>::CreatedFailed {
466 address: create_address,
467 });
468 }
469 }
470
471 Ok(PostDispatchInfo {
472 actual_weight: {
473 let mut gas_to_weight = T::GasWeightMapping::gas_to_weight(
474 info.used_gas.standard.unique_saturated_into(),
475 true,
476 );
477 if let Some(weight_info) = info.weight_info {
478 if let Some(proof_size_usage) = weight_info.proof_size_usage {
479 *gas_to_weight.proof_size_mut() = proof_size_usage;
480 }
481 }
482 Some(gas_to_weight)
483 },
484 pays_fee: Pays::No,
485 })
486 }
487
488 #[pallet::call_index(3)]
490 #[pallet::weight({
491 let without_base_extrinsic_weight = true;
492 T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight)
493 })]
494 pub fn create2(
495 origin: OriginFor<T>,
496 source: H160,
497 init: Vec<u8>,
498 salt: H256,
499 value: U256,
500 gas_limit: u64,
501 max_fee_per_gas: U256,
502 max_priority_fee_per_gas: Option<U256>,
503 nonce: Option<U256>,
504 access_list: Vec<(H160, Vec<H256>)>,
505 authorization_list: AuthorizationList,
506 ) -> DispatchResultWithPostInfo {
507 T::CallOrigin::ensure_address_origin(&source, origin)?;
508
509 let is_transactional = true;
510 let validate = true;
511 let info = match T::Runner::create2(
512 source,
513 init,
514 salt,
515 value,
516 gas_limit,
517 Some(max_fee_per_gas),
518 max_priority_fee_per_gas,
519 nonce,
520 access_list,
521 authorization_list,
522 is_transactional,
523 validate,
524 None,
525 None,
526 T::config(),
527 ) {
528 Ok(info) => info,
529 Err(e) => {
530 return Err(DispatchErrorWithPostInfo {
531 post_info: PostDispatchInfo {
532 actual_weight: Some(e.weight),
533 pays_fee: Pays::Yes,
534 },
535 error: e.error.into(),
536 })
537 }
538 };
539
540 match info {
541 CreateInfo {
542 exit_reason: ExitReason::Succeed(_),
543 value: create_address,
544 ..
545 } => {
546 Pallet::<T>::deposit_event(Event::<T>::Created {
547 address: create_address,
548 });
549 }
550 CreateInfo {
551 exit_reason: _,
552 value: create_address,
553 ..
554 } => {
555 Pallet::<T>::deposit_event(Event::<T>::CreatedFailed {
556 address: create_address,
557 });
558 }
559 }
560
561 Ok(PostDispatchInfo {
562 actual_weight: {
563 let mut gas_to_weight = T::GasWeightMapping::gas_to_weight(
564 info.used_gas.standard.unique_saturated_into(),
565 true,
566 );
567 if let Some(weight_info) = info.weight_info {
568 if let Some(proof_size_usage) = weight_info.proof_size_usage {
569 *gas_to_weight.proof_size_mut() = proof_size_usage;
570 }
571 }
572 Some(gas_to_weight)
573 },
574 pays_fee: Pays::No,
575 })
576 }
577 }
578
579 #[pallet::event]
580 #[pallet::generate_deposit(pub(super) fn deposit_event)]
581 pub enum Event<T: Config> {
582 Log { log: Log },
584 Created { address: H160 },
586 CreatedFailed { address: H160 },
588 Executed { address: H160 },
590 ExecutedFailed { address: H160 },
592 }
593
594 #[pallet::error]
595 pub enum Error<T> {
596 BalanceLow,
598 FeeOverflow,
600 PaymentOverflow,
602 WithdrawFailed,
604 GasPriceTooLow,
606 InvalidNonce,
608 GasLimitTooLow,
610 GasLimitExceedsBlockLimit,
612 InvalidChainId,
614 InvalidSignature,
616 Reentrancy,
618 TransactionMustComeFromEOA,
620 Undefined,
622 CreateOriginNotAllowed,
624 TransactionGasLimitExceedsCap,
626 }
627
628 impl<T> From<TransactionValidationError> for Error<T> {
629 fn from(validation_error: TransactionValidationError) -> Self {
630 match validation_error {
631 TransactionValidationError::GasLimitTooLow => Error::<T>::GasLimitTooLow,
632 TransactionValidationError::GasLimitExceedsBlockLimit => {
633 Error::<T>::GasLimitExceedsBlockLimit
634 }
635 TransactionValidationError::BalanceTooLow => Error::<T>::BalanceLow,
636 TransactionValidationError::TxNonceTooLow => Error::<T>::InvalidNonce,
637 TransactionValidationError::TxNonceTooHigh => Error::<T>::InvalidNonce,
638 TransactionValidationError::GasPriceTooLow => Error::<T>::GasPriceTooLow,
639 TransactionValidationError::PriorityFeeTooHigh => Error::<T>::GasPriceTooLow,
640 TransactionValidationError::InvalidFeeInput => Error::<T>::GasPriceTooLow,
641 TransactionValidationError::InvalidChainId => Error::<T>::InvalidChainId,
642 TransactionValidationError::InvalidSignature => Error::<T>::InvalidSignature,
643 TransactionValidationError::EmptyAuthorizationList => Error::<T>::Undefined,
644 TransactionValidationError::AuthorizationListTooLarge => Error::<T>::Undefined,
645 TransactionValidationError::TransactionGasLimitExceedsCap => {
646 Error::<T>::TransactionGasLimitExceedsCap
647 }
648 TransactionValidationError::UnknownError => Error::<T>::Undefined,
649 }
650 }
651 }
652
653 #[pallet::genesis_config]
654 #[derive(frame_support::DefaultNoBound)]
655 pub struct GenesisConfig<T> {
656 pub accounts: BTreeMap<H160, GenesisAccount>,
657 #[serde(skip)]
658 pub _marker: PhantomData<T>,
659 }
660
661 #[pallet::genesis_build]
662 impl<T: Config> BuildGenesisConfig for GenesisConfig<T>
663 where
664 U256: UniqueSaturatedInto<BalanceOf<T>>,
665 {
666 fn build(&self) {
667 const MAX_ACCOUNT_NONCE: usize = 100;
668
669 for (address, account) in &self.accounts {
670 let account_id = T::AddressMapping::into_account_id(*address);
671
672 for _ in 0..min(
675 MAX_ACCOUNT_NONCE,
676 UniqueSaturatedInto::<usize>::unique_saturated_into(account.nonce),
677 ) {
678 T::AccountProvider::inc_account_nonce(&account_id);
679 }
680
681 let _ = T::Currency::deposit_creating(
682 &account_id,
683 account.balance.unique_saturated_into(),
684 );
685
686 let _ = Pallet::<T>::create_account(*address, account.code.clone(), None);
687
688 for (index, value) in &account.storage {
689 <AccountStorages<T>>::insert(address, index, value);
690 }
691 }
692 }
693 }
694
695 #[pallet::storage]
696 pub type AccountCodes<T: Config> = StorageMap<_, Blake2_128Concat, H160, Vec<u8>, ValueQuery>;
697
698 #[pallet::storage]
699 pub type AccountCodesMetadata<T: Config> =
700 StorageMap<_, Blake2_128Concat, H160, CodeMetadata, OptionQuery>;
701
702 #[pallet::storage]
703 pub type AccountStorages<T: Config> =
704 StorageDoubleMap<_, Blake2_128Concat, H160, Blake2_128Concat, H256, H256, ValueQuery>;
705}
706
707pub type AccountIdOf<T> = <<T as Config>::AccountProvider as AccountProvider>::AccountId;
709
710pub type BalanceOf<T> = <<T as Config>::Currency as Currency<AccountIdOf<T>>>::Balance;
712
713type NegativeImbalanceOf<C, T> = <C as Currency<AccountIdOf<T>>>::NegativeImbalance;
715
716#[derive(
717 Debug,
718 Clone,
719 Copy,
720 Eq,
721 PartialEq,
722 Encode,
723 Decode,
724 DecodeWithMemTracking,
725 TypeInfo,
726 MaxEncodedLen
727)]
728pub struct CodeMetadata {
729 pub size: u64,
730 pub hash: H256,
731}
732
733impl CodeMetadata {
734 pub fn from_code(code: &[u8]) -> Self {
735 let size = code.len() as u64;
736 let hash = H256::from(sp_io::hashing::keccak_256(code));
737
738 Self { size, hash }
739 }
740}
741
742pub trait EnsureAddressOrigin<OuterOrigin> {
743 type Success;
745
746 fn ensure_address_origin(
748 address: &H160,
749 origin: OuterOrigin,
750 ) -> Result<Self::Success, BadOrigin> {
751 Self::try_address_origin(address, origin).map_err(|_| BadOrigin)
752 }
753
754 fn try_address_origin(
756 address: &H160,
757 origin: OuterOrigin,
758 ) -> Result<Self::Success, OuterOrigin>;
759}
760
761pub struct EnsureAddressSame;
764
765impl<OuterOrigin> EnsureAddressOrigin<OuterOrigin> for EnsureAddressSame
766where
767 OuterOrigin: Into<Result<RawOrigin<H160>, OuterOrigin>> + From<RawOrigin<H160>>,
768{
769 type Success = H160;
770
771 fn try_address_origin(address: &H160, origin: OuterOrigin) -> Result<H160, OuterOrigin> {
772 origin.into().and_then(|o| match o {
773 RawOrigin::Signed(who) if &who == address => Ok(who),
774 r => Err(OuterOrigin::from(r)),
775 })
776 }
777}
778
779pub struct EnsureAddressRoot<AccountId>(core::marker::PhantomData<AccountId>);
781
782impl<OuterOrigin, AccountId> EnsureAddressOrigin<OuterOrigin> for EnsureAddressRoot<AccountId>
783where
784 OuterOrigin: Into<Result<RawOrigin<AccountId>, OuterOrigin>> + From<RawOrigin<AccountId>>,
785{
786 type Success = ();
787
788 fn try_address_origin(_address: &H160, origin: OuterOrigin) -> Result<(), OuterOrigin> {
789 origin.into().and_then(|o| match o {
790 RawOrigin::Root => Ok(()),
791 r => Err(OuterOrigin::from(r)),
792 })
793 }
794}
795
796pub struct EnsureAddressNever<AccountId>(core::marker::PhantomData<AccountId>);
798
799impl<OuterOrigin, AccountId> EnsureAddressOrigin<OuterOrigin> for EnsureAddressNever<AccountId> {
800 type Success = AccountId;
801
802 fn try_address_origin(_address: &H160, origin: OuterOrigin) -> Result<AccountId, OuterOrigin> {
803 Err(origin)
804 }
805}
806
807pub struct EnsureAddressTruncated;
810
811impl<OuterOrigin> EnsureAddressOrigin<OuterOrigin> for EnsureAddressTruncated
812where
813 OuterOrigin: Into<Result<RawOrigin<AccountId32>, OuterOrigin>> + From<RawOrigin<AccountId32>>,
814{
815 type Success = AccountId32;
816
817 fn try_address_origin(address: &H160, origin: OuterOrigin) -> Result<AccountId32, OuterOrigin> {
818 origin.into().and_then(|o| match o {
819 RawOrigin::Signed(who) if AsRef::<[u8; 32]>::as_ref(&who)[0..20] == address[0..20] => {
820 Ok(who)
821 }
822 r => Err(OuterOrigin::from(r)),
823 })
824 }
825}
826
827pub struct EnsureAccountId20;
829
830impl<OuterOrigin> EnsureAddressOrigin<OuterOrigin> for EnsureAccountId20
831where
832 OuterOrigin: Into<Result<RawOrigin<AccountId20>, OuterOrigin>> + From<RawOrigin<AccountId20>>,
833{
834 type Success = AccountId20;
835
836 fn try_address_origin(address: &H160, origin: OuterOrigin) -> Result<AccountId20, OuterOrigin> {
837 let acc: AccountId20 = AccountId20::from(*address);
838 origin.into().and_then(|o| match o {
839 RawOrigin::Signed(who) if who == acc => Ok(who),
840 r => Err(OuterOrigin::from(r)),
841 })
842 }
843}
844
845pub trait EnsureCreateOrigin<T> {
846 fn check_create_origin(address: &H160) -> Result<(), Error<T>>;
847}
848
849pub struct EnsureAllowedCreateAddress<AddressGetter>(core::marker::PhantomData<AddressGetter>);
850
851impl<AddressGetter, T: Config> EnsureCreateOrigin<T> for EnsureAllowedCreateAddress<AddressGetter>
852where
853 AddressGetter: Get<Vec<H160>>,
854{
855 fn check_create_origin(address: &H160) -> Result<(), Error<T>> {
856 if !AddressGetter::get().contains(address) {
857 return Err(Error::<T>::CreateOriginNotAllowed);
858 }
859 Ok(())
860 }
861}
862
863impl<T> EnsureCreateOrigin<T> for () {
864 fn check_create_origin(_address: &H160) -> Result<(), Error<T>> {
865 Ok(())
866 }
867}
868
869pub trait AddressMapping<A> {
871 fn into_account_id(address: H160) -> A;
872}
873
874pub struct IdentityAddressMapping;
876
877impl<T: From<H160>> AddressMapping<T> for IdentityAddressMapping {
878 fn into_account_id(address: H160) -> T {
879 address.into()
880 }
881}
882
883pub struct HashedAddressMapping<H>(core::marker::PhantomData<H>);
885
886impl<H: Hasher<Out = H256>> AddressMapping<AccountId32> for HashedAddressMapping<H> {
887 fn into_account_id(address: H160) -> AccountId32 {
888 let mut data = [0u8; 24];
889 data[0..4].copy_from_slice(b"evm:");
890 data[4..24].copy_from_slice(&address[..]);
891 let hash = H::hash(&data);
892
893 AccountId32::from(Into::<[u8; 32]>::into(hash))
894 }
895}
896
897pub trait BlockHashMapping {
899 fn block_hash(number: u32) -> H256;
900}
901
902pub struct SubstrateBlockHashMapping<T>(core::marker::PhantomData<T>);
904impl<T: Config> BlockHashMapping for SubstrateBlockHashMapping<T> {
905 fn block_hash(number: u32) -> H256 {
906 let number = <NumberFor<T::Block>>::from(number);
907 H256::from_slice(frame_system::Pallet::<T>::block_hash(number).as_ref())
908 }
909}
910
911pub trait GasWeightMapping {
913 fn gas_to_weight(gas: u64, without_base_weight: bool) -> Weight;
914 fn weight_to_gas(weight: Weight) -> u64;
915}
916
917pub trait FixedGasWeightMappingAssociatedTypes {
918 type WeightPerGas: Get<Weight>;
919 type BlockWeights: Get<frame_system::limits::BlockWeights>;
920 type GasLimitPovSizeRatio: Get<u64>;
921}
922
923impl<T: Config> FixedGasWeightMappingAssociatedTypes for T {
924 type WeightPerGas = T::WeightPerGas;
925 type BlockWeights = T::BlockWeights;
926 type GasLimitPovSizeRatio = T::GasLimitPovSizeRatio;
927}
928
929pub struct FixedGasWeightMapping<T>(core::marker::PhantomData<T>);
930impl<T> GasWeightMapping for FixedGasWeightMapping<T>
931where
932 T: FixedGasWeightMappingAssociatedTypes,
933{
934 fn gas_to_weight(gas: u64, without_base_weight: bool) -> Weight {
935 let mut weight = T::WeightPerGas::get().saturating_mul(gas);
936 if without_base_weight {
937 weight = weight.saturating_sub(
938 T::BlockWeights::get()
939 .get(frame_support::dispatch::DispatchClass::Normal)
940 .base_extrinsic,
941 );
942 }
943 let ratio = T::GasLimitPovSizeRatio::get();
945 if ratio > 0 {
946 let proof_size = gas.saturating_div(ratio);
947 *weight.proof_size_mut() = proof_size;
948 }
949
950 weight
951 }
952 fn weight_to_gas(weight: Weight) -> u64 {
953 weight.div(T::WeightPerGas::get().ref_time()).ref_time()
954 }
955}
956
957impl<T: Config> Pallet<T> {
958 pub fn is_account_empty(address: &H160) -> bool {
960 let (account, _) = Self::account_basic(address);
961 let code_len = <AccountCodes<T>>::decode_len(address).unwrap_or(0);
962
963 account.nonce == U256::zero() && account.balance == U256::zero() && code_len == 0
964 }
965
966 pub fn iter_account_storages(address: &H160) -> KeyPrefixIterator<H256> {
967 <AccountStorages<T>>::iter_key_prefix(address)
968 }
969
970 pub fn remove_account_if_empty(address: &H160) {
972 if Self::is_account_empty(address) {
973 Self::remove_account(address);
974 }
975 }
976
977 pub fn remove_account(address: &H160) {
979 let account_id = T::AddressMapping::into_account_id(*address);
980 T::AccountProvider::remove_account(&account_id);
981
982 <AccountCodes<T>>::remove(address);
983 <AccountCodesMetadata<T>>::remove(address);
984 let _ = <AccountStorages<T>>::clear_prefix(address, u32::MAX, None);
985 }
986
987 pub fn remove_account_code(address: &H160) {
989 <AccountCodes<T>>::remove(address);
990 <AccountCodesMetadata<T>>::remove(address);
991 }
992
993 pub fn create_account(
995 address: H160,
996 code: Vec<u8>,
997 caller: Option<H160>,
998 ) -> Result<(), ExitError> {
999 if let Some(caller_address) = caller {
1000 T::CreateInnerOriginFilter::check_create_origin(&caller_address).map_err(|e| {
1001 let error: &'static str = e.into();
1002 ExitError::Other(Cow::Borrowed(error))
1003 })?;
1004 }
1005
1006 if code.is_empty() {
1007 return Ok(());
1008 }
1009
1010 if !<AccountCodes<T>>::contains_key(address) {
1011 let account_id = T::AddressMapping::into_account_id(address);
1012 T::AccountProvider::create_account(&account_id);
1013 }
1014
1015 let meta = CodeMetadata::from_code(&code);
1017 <AccountCodesMetadata<T>>::insert(address, meta);
1018
1019 <AccountCodes<T>>::insert(address, code);
1020 Ok(())
1021 }
1022
1023 pub fn account_code_metadata(address: H160) -> CodeMetadata {
1026 if let Some(meta) = <AccountCodesMetadata<T>>::get(address) {
1027 return meta;
1028 }
1029
1030 let code = <AccountCodes<T>>::get(address);
1031
1032 if code.is_empty() {
1035 const EMPTY_CODE_HASH: [u8; 32] = hex_literal::hex!(
1036 "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
1037 );
1038 return CodeMetadata {
1039 size: 0,
1040 hash: EMPTY_CODE_HASH.into(),
1041 };
1042 }
1043
1044 let meta = CodeMetadata::from_code(&code);
1045
1046 <AccountCodesMetadata<T>>::insert(address, meta);
1047 meta
1048 }
1049
1050 pub fn account_basic(address: &H160) -> (Account, frame_support::weights::Weight) {
1052 let account_id = T::AddressMapping::into_account_id(*address);
1053 let nonce = T::AccountProvider::account_nonce(&account_id);
1054 let balance =
1055 T::Currency::reducible_balance(&account_id, Preservation::Preserve, Fortitude::Polite);
1056
1057 (
1058 Account {
1059 nonce: U256::from(UniqueSaturatedInto::<u128>::unique_saturated_into(nonce)),
1060 balance: U256::from(UniqueSaturatedInto::<u128>::unique_saturated_into(balance)),
1061 },
1062 T::DbWeight::get().reads(2),
1063 )
1064 }
1065
1066 pub fn find_author() -> H160 {
1068 let digest = <frame_system::Pallet<T>>::digest();
1069 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
1070
1071 T::FindAuthor::find_author(pre_runtime_digests).unwrap_or_default()
1072 }
1073}
1074
1075pub trait OnChargeEVMTransaction<T: Config> {
1078 type LiquidityInfo: Default;
1079
1080 fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, Error<T>>;
1083
1084 fn correct_and_deposit_fee(
1090 who: &H160,
1091 corrected_fee: U256,
1092 base_fee: U256,
1093 already_withdrawn: Self::LiquidityInfo,
1094 ) -> Self::LiquidityInfo;
1095
1096 fn pay_priority_fee(tip: Self::LiquidityInfo);
1098}
1099
1100pub struct EVMCurrencyAdapter<C, OU>(core::marker::PhantomData<(C, OU)>);
1105
1106impl<T, C, OU> OnChargeEVMTransaction<T> for EVMCurrencyAdapter<C, OU>
1107where
1108 T: Config,
1109 C: Currency<AccountIdOf<T>>,
1110 C::PositiveImbalance:
1111 Imbalance<<C as Currency<AccountIdOf<T>>>::Balance, Opposite = C::NegativeImbalance>,
1112 C::NegativeImbalance:
1113 Imbalance<<C as Currency<AccountIdOf<T>>>::Balance, Opposite = C::PositiveImbalance>,
1114 OU: OnUnbalanced<NegativeImbalanceOf<C, T>>,
1115 U256: UniqueSaturatedInto<<C as Currency<AccountIdOf<T>>>::Balance>,
1116{
1117 type LiquidityInfo = Option<NegativeImbalanceOf<C, T>>;
1119
1120 fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, Error<T>> {
1121 if fee.is_zero() {
1122 return Ok(None);
1123 }
1124 let account_id = T::AddressMapping::into_account_id(*who);
1125 let imbalance = C::withdraw(
1126 &account_id,
1127 fee.unique_saturated_into(),
1128 WithdrawReasons::FEE,
1129 ExistenceRequirement::AllowDeath,
1130 )
1131 .map_err(|_| Error::<T>::BalanceLow)?;
1132 Ok(Some(imbalance))
1133 }
1134
1135 fn correct_and_deposit_fee(
1136 who: &H160,
1137 corrected_fee: U256,
1138 base_fee: U256,
1139 already_withdrawn: Self::LiquidityInfo,
1140 ) -> Self::LiquidityInfo {
1141 if let Some(paid) = already_withdrawn {
1142 let account_id = T::AddressMapping::into_account_id(*who);
1143
1144 let refund_amount = paid
1146 .peek()
1147 .saturating_sub(corrected_fee.unique_saturated_into());
1148 let refund_imbalance = C::deposit_into_existing(&account_id, refund_amount)
1152 .unwrap_or_else(|_| C::PositiveImbalance::zero());
1153
1154 let refund_imbalance = if C::minimum_balance().is_zero()
1159 && refund_amount > C::Balance::zero()
1160 && C::total_balance(&account_id).is_zero()
1161 {
1162 match C::make_free_balance_be(&account_id, refund_amount) {
1165 SignedImbalance::Positive(p) => p,
1166 _ => C::PositiveImbalance::zero(),
1167 }
1168 } else {
1169 refund_imbalance
1170 };
1171
1172 let adjusted_paid = paid
1174 .offset(refund_imbalance)
1175 .same()
1176 .unwrap_or_else(|_| C::NegativeImbalance::zero());
1177
1178 let (base_fee, tip) = adjusted_paid.split(base_fee.unique_saturated_into());
1179 OU::on_unbalanced(base_fee);
1181 return Some(tip);
1182 }
1183 None
1184 }
1185
1186 fn pay_priority_fee(tip: Self::LiquidityInfo) {
1187 if let Some(tip) = tip {
1189 let account_id = T::AddressMapping::into_account_id(<Pallet<T>>::find_author());
1190 let _ = C::deposit_into_existing(&account_id, tip.peek());
1191 }
1192 }
1193}
1194pub struct EVMFungibleAdapter<F, OU>(core::marker::PhantomData<(F, OU)>);
1201
1202impl<T, F, OU> OnChargeEVMTransaction<T> for EVMFungibleAdapter<F, OU>
1203where
1204 T: Config,
1205 F: Balanced<AccountIdOf<T>>,
1206 OU: OnUnbalanced<Credit<AccountIdOf<T>, F>>,
1207 U256: UniqueSaturatedInto<<F as Inspect<AccountIdOf<T>>>::Balance>,
1208{
1209 type LiquidityInfo = Option<Credit<AccountIdOf<T>, F>>;
1211
1212 fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, Error<T>> {
1213 if fee.is_zero() {
1214 return Ok(None);
1215 }
1216 let account_id = T::AddressMapping::into_account_id(*who);
1217 let imbalance = F::withdraw(
1218 &account_id,
1219 fee.unique_saturated_into(),
1220 Precision::Exact,
1221 Preservation::Preserve,
1222 Fortitude::Polite,
1223 )
1224 .map_err(|_| Error::<T>::BalanceLow)?;
1225 Ok(Some(imbalance))
1226 }
1227
1228 fn correct_and_deposit_fee(
1229 who: &H160,
1230 corrected_fee: U256,
1231 base_fee: U256,
1232 already_withdrawn: Self::LiquidityInfo,
1233 ) -> Self::LiquidityInfo {
1234 if let Some(paid) = already_withdrawn {
1235 let account_id = T::AddressMapping::into_account_id(*who);
1236
1237 let refund_amount = paid
1239 .peek()
1240 .saturating_sub(corrected_fee.unique_saturated_into());
1241 let refund_imbalance = F::deposit(&account_id, refund_amount, Precision::BestEffort)
1243 .unwrap_or_else(|_| Debt::<AccountIdOf<T>, F>::zero());
1244
1245 let adjusted_paid = paid
1247 .offset(refund_imbalance)
1248 .same()
1249 .unwrap_or_else(|_| Credit::<AccountIdOf<T>, F>::zero());
1250
1251 let (base_fee, tip) = adjusted_paid.split(base_fee.unique_saturated_into());
1252 OU::on_unbalanced(base_fee);
1254 return Some(tip);
1255 }
1256 None
1257 }
1258
1259 fn pay_priority_fee(tip: Self::LiquidityInfo) {
1260 if let Some(tip) = tip {
1262 let account_id = T::AddressMapping::into_account_id(<Pallet<T>>::find_author());
1263 let _ = F::deposit(&account_id, tip.peek(), Precision::BestEffort);
1264 }
1265 }
1266}
1267
1268impl<T> OnChargeEVMTransaction<T> for ()
1270where
1271 T: Config,
1272 T::Currency: Balanced<AccountIdOf<T>>,
1273 U256: UniqueSaturatedInto<<<T as Config>::Currency as Inspect<AccountIdOf<T>>>::Balance>,
1274{
1275 type LiquidityInfo = Option<Credit<AccountIdOf<T>, T::Currency>>;
1277
1278 fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, Error<T>> {
1279 EVMFungibleAdapter::<T::Currency, ()>::withdraw_fee(who, fee)
1280 }
1281
1282 fn correct_and_deposit_fee(
1283 who: &H160,
1284 corrected_fee: U256,
1285 base_fee: U256,
1286 already_withdrawn: Self::LiquidityInfo,
1287 ) -> Self::LiquidityInfo {
1288 <EVMFungibleAdapter<T::Currency, ()> as OnChargeEVMTransaction<T>>::correct_and_deposit_fee(
1289 who,
1290 corrected_fee,
1291 base_fee,
1292 already_withdrawn,
1293 )
1294 }
1295
1296 fn pay_priority_fee(tip: Self::LiquidityInfo) {
1297 <EVMFungibleAdapter<T::Currency, ()> as OnChargeEVMTransaction<T>>::pay_priority_fee(tip);
1298 }
1299}
1300
1301pub trait OnCreate<T> {
1302 fn on_create(owner: H160, contract: H160);
1303}
1304
1305impl<T> OnCreate<T> for () {
1306 fn on_create(_owner: H160, _contract: H160) {}
1307}
1308
1309#[impl_for_tuples(1, 12)]
1310impl<T> OnCreate<T> for Tuple {
1311 fn on_create(owner: H160, contract: H160) {
1312 for_tuples!(#(
1313 Tuple::on_create(owner, contract);
1314 )*)
1315 }
1316}
1317
1318pub struct FrameSystemAccountProvider<T>(core::marker::PhantomData<T>);
1322
1323impl<T: frame_system::Config> AccountProvider for FrameSystemAccountProvider<T> {
1324 type AccountId = T::AccountId;
1325 type Nonce = T::Nonce;
1326
1327 fn account_nonce(who: &Self::AccountId) -> Self::Nonce {
1328 frame_system::Pallet::<T>::account_nonce(who)
1329 }
1330
1331 fn inc_account_nonce(who: &Self::AccountId) {
1332 frame_system::Pallet::<T>::inc_account_nonce(who)
1333 }
1334
1335 fn create_account(who: &Self::AccountId) {
1336 let _ = frame_system::Pallet::<T>::inc_sufficients(who);
1337 }
1338
1339 fn remove_account(who: &Self::AccountId) {
1340 let _ = frame_system::Pallet::<T>::dec_sufficients(who);
1341 }
1342}