diff --git a/prdoc/pr_9617.prdoc b/prdoc/pr_9617.prdoc new file mode 100644 index 0000000000000..ee41b9a1bcfda --- /dev/null +++ b/prdoc/pr_9617.prdoc @@ -0,0 +1,9 @@ +title: '[pallet-revive] Add setting evm balance' +doc: +- audience: Runtime User + description: |- + Adds a balance setter in EVM for pallet-revive. + +crates: +- name: pallet-revive + bump: minor diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 7266d22625ba6..9ed4e02dee25d 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -580,7 +580,9 @@ pub mod pallet { #[pallet::genesis_build] impl BuildGenesisConfig for GenesisConfig where - BalanceOf: Into + TryFrom, + BalanceOf: Into + TryFrom + Bounded, + MomentOf: Into, + T::Hash: frame_support::traits::IsType, { fn build(&self) { use crate::{exec::Key, vm::ContractBlob}; @@ -602,17 +604,8 @@ pub mod pallet { let owner = Pallet::::account_id(); for genesis::Account { address, balance, nonce, contract_data } in &self.accounts { - let Ok(balance_with_dust) = - BalanceWithDust::>::from_value::(*balance).inspect_err(|err| { - log::error!(target: LOG_TARGET, "Failed to convert balance for {address:?}: {err:?}"); - }) - else { - continue; - }; let account_id = T::AddressMapper::to_account_id(address); - let (value, dust) = balance_with_dust.deconstruct(); - let _ = T::Currency::set_balance(&account_id, value); frame_system::Account::::mutate(&account_id, |info| { info.nonce = (*nonce).into(); }); @@ -621,7 +614,7 @@ pub mod pallet { None => { AccountInfoOf::::insert( address, - AccountInfo { account_type: AccountType::EOA, dust }, + AccountInfo { account_type: AccountType::EOA, dust: 0 }, ); }, Some(genesis::ContractData { code, storage }) => { @@ -650,7 +643,7 @@ pub mod pallet { AccountInfoOf::::insert( address, - AccountInfo { account_type: info.clone().into(), dust }, + AccountInfo { account_type: info.clone().into(), dust: 0 }, ); >::insert(blob.code_hash(), code); @@ -662,6 +655,10 @@ pub mod pallet { } }, } + + let _ = Pallet::::set_evm_balance(address, *balance).inspect_err(|err| { + log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}"); + }); } } } @@ -1539,11 +1536,32 @@ where } /// Get the balance with EVM decimals of the given `address`. + /// + /// Returns the spendable balance excluding the existential deposit. pub fn evm_balance(address: &H160) -> U256 { let balance = AccountInfo::::balance((*address).into()); Self::convert_native_to_evm(balance) } + /// Set the EVM balance of an account. + /// + /// The account's total balance becomes the EVM value plus the existential deposit, + /// consistent with `evm_balance` which returns the spendable balance excluding the existential + /// deposit. + pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error> { + let ed = T::Currency::minimum_balance(); + let balance_with_dust = BalanceWithDust::>::from_value::(evm_value) + .map_err(|_| >::BalanceConversionFailed)?; + let (value, dust) = balance_with_dust.deconstruct(); + let account_id = T::AddressMapper::to_account_id(&address); + T::Currency::set_balance(&account_id, ed.saturating_add(value)); + AccountInfoOf::::mutate(address, |account| { + account.as_mut().map(|a| a.dust = dust); + }); + + Ok(()) + } + /// Get the nonce for the given `address`. pub fn evm_nonce(address: &H160) -> u32 where diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index a1ed36240577e..9dda6f55cadb9 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -460,7 +460,7 @@ impl Default for Origin { fn ext_builder_with_genesis_config_works() { let pvm_contract = Account { address: BOB_ADDR, - balance: U256::from(100), + balance: U256::from(100_000_100), nonce: 42, contract_data: Some(ContractData { code: compile_module("dummy").unwrap().0, @@ -470,7 +470,7 @@ fn ext_builder_with_genesis_config_works() { let evm_contract = Account { address: CHARLIE_ADDR, - balance: U256::from(100), + balance: U256::from(1_000_00_100), nonce: 43, contract_data: Some(ContractData { code: vec![revm::bytecode::opcode::RETURN], diff --git a/substrate/frame/revive/src/tests/pvm.rs b/substrate/frame/revive/src/tests/pvm.rs index c21dfe3ad02a1..d25060322699a 100644 --- a/substrate/frame/revive/src/tests/pvm.rs +++ b/substrate/frame/revive/src/tests/pvm.rs @@ -193,6 +193,22 @@ fn eth_call_transfer_with_dust_works() { }); } +#[test] +fn set_evm_balance_works() { + let (binary, _) = compile_module("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(binary)).build_and_unwrap_contract(); + let native_with_dust = BalanceWithDust::new_unchecked::(100, 10); + let evm_value = Pallet::::convert_native_to_evm(native_with_dust); + + assert_ok!(Pallet::::set_evm_balance(&addr, evm_value)); + + assert_eq!(Pallet::::evm_balance(&addr), evm_value); + }); +} + #[test] fn contract_call_transfer_with_dust_works() { let (binary_caller, _code_hash_caller) = compile_module("call_with_value").unwrap();