Skip to content

Commit

Permalink
Update value adjustment for wasm payable calls.
Browse files Browse the repository at this point in the history
  • Loading branch information
shaunxw committed Aug 2, 2023
1 parent 06bb378 commit 9a94737
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 18 deletions.
27 changes: 16 additions & 11 deletions pallets/xvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -87,9 +84,6 @@ pub mod pallet {
/// `CheckedEthereumTransact` implementation.
type EthereumTransact: CheckedEthereumTransact;

/// Existential deposit of currency for payable calls.
type ExistentialDeposit: Get<Balance>;

/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
}
Expand Down Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion pallets/xvm/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TestRuntime>;
}

Expand Down
1 change: 0 additions & 1 deletion runtime/local/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,6 @@ impl pallet_xvm::Config for Runtime {
type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
type AccountMapping = HashedAccountMapping;
type EthereumTransact = EthereumChecked;
type ExistentialDeposit = ExistentialDeposit;
type WeightInfo = pallet_xvm::weights::SubstrateWeight<Runtime>;
}

Expand Down
1 change: 0 additions & 1 deletion runtime/shibuya/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -790,7 +790,6 @@ impl pallet_xvm::Config for Runtime {
type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
type AccountMapping = HashedAccountMapping;
type EthereumTransact = EthereumChecked;
type ExistentialDeposit = ExistentialDeposit;
type WeightInfo = pallet_xvm::weights::SubstrateWeight<Runtime>;
}

Expand Down
40 changes: 36 additions & 4 deletions tests/integration/src/xvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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
);
});
}

Expand All @@ -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,
Expand All @@ -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(),
);
});
}

Expand Down

0 comments on commit 9a94737

Please sign in to comment.