diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index 0f8bd408c7..89282e0579 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -79,6 +79,7 @@ use scale_info::TypeInfo; // Substrate use frame_support::{ dispatch::{DispatchResultWithPostInfo, Pays, PostDispatchInfo}, + ensure, storage::KeyPrefixIterator, traits::{ fungible::{Balanced, Credit, Debt}, @@ -103,9 +104,8 @@ use fp_account::AccountId20; use fp_evm::GenesisAccount; pub use fp_evm::{ Account, AccountProvider, CallInfo, CreateInfo, ExecutionInfoV2 as ExecutionInfo, - FeeCalculator, IsPrecompileResult, LinearCostPrecompile, Log, Precompile, PrecompileFailure, - PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet, - TransactionValidationError, Vicinity, + FeeCalculator, IsPrecompileResult, Log, Precompile, PrecompileFailure, PrecompileHandle, + PrecompileOutput, PrecompileResult, PrecompileSet, TransactionValidationError, Vicinity, }; pub use self::{ @@ -413,6 +413,7 @@ pub mod pallet { access_list: Vec<(H160, Vec)>, ) -> DispatchResultWithPostInfo { T::CallOrigin::ensure_address_origin(&source, origin)?; + Self::ensure_balance_for_contract_creation(&source)?; let whitelist = >::get(); let whitelist_disabled = >::get(); @@ -453,6 +454,15 @@ pub mod pallet { value: create_address, .. } => { + let mini_balance = <::Currency as Currency< + <::AccountProvider as AccountProvider>::AccountId, + >>::minimum_balance(); + T::Currency::transfer( + &T::AddressMapping::into_account_id(source), + &T::AddressMapping::into_account_id(create_address), + mini_balance, + ExistenceRequirement::AllowDeath, + )?; Pallet::::deposit_event(Event::::Created { address: create_address, }); @@ -504,6 +514,7 @@ pub mod pallet { access_list: Vec<(H160, Vec)>, ) -> DispatchResultWithPostInfo { T::CallOrigin::ensure_address_origin(&source, origin)?; + Self::ensure_balance_for_contract_creation(&source)?; let whitelist = >::get(); let whitelist_disabled = >::get(); @@ -545,6 +556,15 @@ pub mod pallet { value: create_address, .. } => { + let mini_balance = <::Currency as Currency< + <::AccountProvider as AccountProvider>::AccountId, + >>::minimum_balance(); + T::Currency::transfer( + &T::AddressMapping::into_account_id(source), + &T::AddressMapping::into_account_id(create_address), + mini_balance, + ExistenceRequirement::AllowDeath, + )?; Pallet::::deposit_event(Event::::Created { address: create_address, }); @@ -645,6 +665,10 @@ pub mod pallet { NotAllowed, /// Address not allowed to deploy contracts either via CREATE or CALL(CREATE). CreateOriginNotAllowed, + /// Not enough balance to pay existential deposit + BalanceLowForExistentialDeposit, + /// Token transfer to new contract failed + TransferToNewContractFailed, } impl From for Error { @@ -1095,6 +1119,45 @@ impl Pallet { T::FindAuthor::find_author(pre_runtime_digests).unwrap_or_default() } + + /// Ensure balance to pre fund contract creation. + pub fn ensure_balance_for_contract_creation(source: &H160) -> Result<(), Error> { + let account_id = T::AddressMapping::into_account_id(*source); + let balance = + T::Currency::reducible_balance(&account_id, Preservation::Preserve, Fortitude::Polite); + + let balance = UniqueSaturatedInto::::unique_saturated_into(balance); + + let mini_balance = <::Currency as Currency< + <::AccountProvider as AccountProvider>::AccountId, + >>::minimum_balance(); + + let mini_balance = UniqueSaturatedInto::::unique_saturated_into(mini_balance); + + ensure!( + balance >= mini_balance, + Error::::BalanceLowForExistentialDeposit + ); + Ok(()) + } + + /// transfer existential deposit to new contract + pub fn transfer_minimal_to_new_contract( + source: &H160, + create_address: &H160, + ) -> Result<(), Error> { + let mini_balance = <::Currency as Currency< + <::AccountProvider as AccountProvider>::AccountId, + >>::minimum_balance(); + T::Currency::transfer( + &T::AddressMapping::into_account_id(*source), + &T::AddressMapping::into_account_id(*create_address), + mini_balance, + ExistenceRequirement::AllowDeath, + ) + .map_err(|_| Error::::TransferToNewContractFailed)?; + Ok(()) + } } /// Handle withdrawing, refunding and depositing of transaction fees. diff --git a/frame/evm/src/runner/stack.rs b/frame/evm/src/runner/stack.rs index dd821eb73b..79964bfb3d 100644 --- a/frame/evm/src/runner/stack.rs +++ b/frame/evm/src/runner/stack.rs @@ -578,6 +578,10 @@ where proof_size_base_cost: Option, config: &evm::Config, ) -> Result> { + Pallet::::ensure_balance_for_contract_creation(&source).map_err(|_| RunnerError { + error: Error::::BalanceLow, + weight: Weight::zero(), + })?; let measured_proof_size_before = get_proof_size().unwrap_or_default(); let (_, weight) = T::FeeCalculator::min_gas_price(); @@ -609,7 +613,7 @@ where )?; } let precompiles = T::PrecompilesValue::get(); - Self::execute( + let result = Self::execute( source, value, gas_limit, @@ -628,7 +632,21 @@ where executor.transact_create(source, value, init, gas_limit, access_list); (reason, address) }, - ) + ); + if let Ok(CreateInfo { + exit_reason: ExitReason::Succeed(_), + value: create_address, + .. + }) = &result + { + Pallet::::transfer_minimal_to_new_contract(&source, create_address).map_err( + |_| RunnerError { + error: Error::::BalanceLow, + weight: Weight::default(), + }, + )?; + } + result } fn create2( @@ -649,6 +667,10 @@ where proof_size_base_cost: Option, config: &evm::Config, ) -> Result> { + Pallet::::ensure_balance_for_contract_creation(&source).map_err(|_| RunnerError { + error: Error::::BalanceLow, + weight: Weight::zero(), + })?; let measured_proof_size_before = get_proof_size().unwrap_or_default(); let (_, weight) = T::FeeCalculator::min_gas_price(); @@ -662,7 +684,6 @@ where weight: Weight::zero(), }); } - Self::validate( source, None, @@ -681,7 +702,7 @@ where } let precompiles = T::PrecompilesValue::get(); let code_hash = H256::from(sp_io::hashing::keccak_256(&init)); - Self::execute( + let result = Self::execute( source, value, gas_limit, @@ -704,7 +725,21 @@ where executor.transact_create2(source, value, init, salt, gas_limit, access_list); (reason, address) }, - ) + ); + if let Ok(CreateInfo { + exit_reason: ExitReason::Succeed(_), + value: create_address, + .. + }) = &result + { + Pallet::::transfer_minimal_to_new_contract(&source, create_address).map_err( + |_| RunnerError { + error: Error::::BalanceLow, + weight: Weight::default(), + }, + )?; + } + result } }