diff --git a/pallets/xvm/src/lib.rs b/pallets/xvm/src/lib.rs index cd42cd2eba..9e4653fdfa 100644 --- a/pallets/xvm/src/lib.rs +++ b/pallets/xvm/src/lib.rs @@ -37,10 +37,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::{ - ensure, - traits::{Currency, Get}, -}; +use frame_support::{ensure, traits::Currency}; use pallet_contracts::{CollectEvents, DebugInfo, Determinism}; use pallet_evm::GasWeightMapping; use parity_scale_codec::Decode; @@ -87,9 +84,6 @@ pub mod pallet { /// `CheckedEthereumTransact` implementation. type EthereumTransact: CheckedEthereumTransact; - /// Existential deposit of currency for payable calls. - type ExistentialDeposit: Get; - /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } @@ -256,10 +250,21 @@ where T::Lookup::lookup(decoded).map_err(|_| error) }?; - // TODO: maybe max(source_balance - existential_deposit, value)? might be overkill - // Adjust value to account for existential deposit, as `pallet-contracts` - // respects it on transfer. - let adjusted_value = value.saturating_sub(T::ExistentialDeposit::get()); + // Adjust `value` if needed, to make sure `source` balance won't be below ED after calling + // a payable contract. This is needed as `pallet-contracts` always respects `source` ED. + // Without the adjustment, the first call from any `source` to a payable contract will + // always fail. + // + // Only the first call to a payable contract results less `value` by ED amount, the following + // ones will not be affected. + let source_balance = T::Currency::free_balance(&source); + let existential_deposit = T::Currency::minimum_balance(); + let killing_source = source_balance.saturating_sub(value) < existential_deposit; + let adjusted_value = if killing_source { + value.saturating_sub(existential_deposit) + } else { + value + }; // With overheads, less weight is available. let weight_limit = context diff --git a/pallets/xvm/src/mock.rs b/pallets/xvm/src/mock.rs index bde6a54aa2..9783b71297 100644 --- a/pallets/xvm/src/mock.rs +++ b/pallets/xvm/src/mock.rs @@ -181,7 +181,6 @@ impl pallet_xvm::Config for TestRuntime { type GasWeightMapping = MockGasWeightMapping; type AccountMapping = HashedAccountMapping; type EthereumTransact = MockEthereumTransact; - type ExistentialDeposit = ConstU128<2>; type WeightInfo = weights::SubstrateWeight; } diff --git a/runtime/local/src/lib.rs b/runtime/local/src/lib.rs index 564907d2bb..765dc47b84 100644 --- a/runtime/local/src/lib.rs +++ b/runtime/local/src/lib.rs @@ -475,7 +475,6 @@ impl pallet_xvm::Config for Runtime { type GasWeightMapping = pallet_evm::FixedGasWeightMapping; type AccountMapping = HashedAccountMapping; type EthereumTransact = EthereumChecked; - type ExistentialDeposit = ExistentialDeposit; type WeightInfo = pallet_xvm::weights::SubstrateWeight; } diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index d33627a336..bc94244c62 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -790,7 +790,6 @@ impl pallet_xvm::Config for Runtime { type GasWeightMapping = pallet_evm::FixedGasWeightMapping; type AccountMapping = HashedAccountMapping; type EthereumTransact = EthereumChecked; - type ExistentialDeposit = ExistentialDeposit; type WeightInfo = pallet_xvm::weights::SubstrateWeight; } diff --git a/tests/integration/src/xvm.rs b/tests/integration/src/xvm.rs index ad1e3116d8..cd824dc70c 100644 --- a/tests/integration/src/xvm.rs +++ b/tests/integration/src/xvm.rs @@ -157,7 +157,7 @@ fn evm_payable_call_via_xvm_works() { new_test_ext().execute_with(|| { let evm_payable_addr = deploy_evm_contract(EVM_PAYABLE); - let value = 1_000_000_000; + let value = UNIT; assert_ok!(Xvm::call( Context { source_vm_id: VmId::Wasm, @@ -199,7 +199,8 @@ fn wasm_payable_call_via_xvm_works() { new_test_ext().execute_with(|| { let contract_addr = deploy_wasm_contract("payable"); - let value = 1_000_000_000; + let prev_balance = Balances::free_balance(&contract_addr); + let value = UNIT; assert_ok!(Xvm::call( Context { source_vm_id: VmId::Evm, @@ -212,7 +213,10 @@ fn wasm_payable_call_via_xvm_works() { hex::decode("0000002a").unwrap(), value )); - assert_eq!(Balances::free_balance(contract_addr.clone()), value,); + assert_eq!( + Balances::free_balance(contract_addr.clone()), + value + prev_balance + ); }); } @@ -239,7 +243,7 @@ fn calling_wasm_payable_from_evm_works() { vec![], )); // `pallet-contracts` respects the existential deposit of caller. The actual amount - // it got is `value - ExistentialDeposit`. Adding to its existing balance and we got `value`. + // it got is `value - ExistentialDeposit`. Adding to its existing balance results `value`. assert_eq!( Balances::free_balance(&wasm_payable_addr), value, @@ -248,6 +252,34 @@ fn calling_wasm_payable_from_evm_works() { Balances::free_balance(&account_id_from(call_wasm_payable_addr)), ExistentialDeposit::get(), ); + + let value = 1_000_000_000; + assert_ok!(EVM::call( + RuntimeOrigin::root(), + alith(), + call_wasm_payable_addr.clone(), + // to: 0x00a8f69d59df362b69a8d4acdb9001eb3e1b8d067b8fdaa70081aed945bde5c48c + // input: 0x0000002a (deposit) + // value: 1000000000 + hex::decode("4012b914000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000000002100a8f69d59df362b69a8d4acdb9001eb3e1b8d067b8fdaa70081aed945bde5c48c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000002a00000000000000000000000000000000000000000000000000000000").unwrap(), + U256::from(value), + 1_000_000, + U256::from(DefaultBaseFeePerGas::get()), + None, + None, + vec![], + )); + // For the second call with the same value, the wasm payable contract will receive + // the full amount, as the EVM contract already has enough balance for existential + // deposit. + assert_eq!( + Balances::free_balance(&wasm_payable_addr), + 2 * value, + ); + assert_eq!( + Balances::free_balance(&account_id_from(call_wasm_payable_addr)), + ExistentialDeposit::get(), + ); }); }