diff --git a/frame/ethereum/src/tests/eip1559.rs b/frame/ethereum/src/tests/eip1559.rs index 94fad5d8d0..687cb89252 100644 --- a/frame/ethereum/src/tests/eip1559.rs +++ b/frame/ethereum/src/tests/eip1559.rs @@ -18,8 +18,9 @@ //! Consensus extension module tests for BABE consensus. use super::*; +use fp_ethereum::ValidatedTransaction; use frame_support::{traits::Get, weights::DispatchClass}; -use pallet_evm::GasWeightMapping; +use pallet_evm::{AddressMapping, GasWeightMapping}; fn eip1559_erc20_creation_unsigned_transaction() -> EIP1559UnsignedTransaction { EIP1559UnsignedTransaction { @@ -453,3 +454,35 @@ fn self_contained_transaction_with_extra_gas_should_adjust_weight_with_post_disp ); }); } + +#[test] +fn validated_transaction_apply_zero_gas_price_works() { + let (pairs, mut ext) = new_test_ext_with_initial_balance(2, 1_000); + let alice = &pairs[0]; + let bob = &pairs[1]; + let substrate_alice = + ::AddressMapping::into_account_id(alice.address); + let substrate_bob = ::AddressMapping::into_account_id(bob.address); + + ext.execute_with(|| { + let transaction = EIP1559UnsignedTransaction { + nonce: U256::zero(), + max_priority_fee_per_gas: U256::zero(), + max_fee_per_gas: U256::zero(), + gas_limit: U256::from(21_000), + action: ethereum::TransactionAction::Call(bob.address), + value: U256::from(100), + input: Default::default(), + } + .sign(&alice.private_key, None); + + assert_ok!(crate::ValidatedTransaction::::apply( + alice.address, + transaction + )); + // Alice didn't pay fees, transfer 100 to Bob. + assert_eq!(Balances::free_balance(&substrate_alice), 900); + // Bob received 100 from Alice. + assert_eq!(Balances::free_balance(&substrate_bob), 1_100); + }); +} diff --git a/frame/ethereum/src/tests/eip2930.rs b/frame/ethereum/src/tests/eip2930.rs index fbdac95590..7f5cd050ad 100644 --- a/frame/ethereum/src/tests/eip2930.rs +++ b/frame/ethereum/src/tests/eip2930.rs @@ -18,6 +18,8 @@ //! Consensus extension module tests for BABE consensus. use super::*; +use fp_ethereum::ValidatedTransaction; +use pallet_evm::AddressMapping; fn eip2930_erc20_creation_unsigned_transaction() -> EIP2930UnsignedTransaction { EIP2930UnsignedTransaction { @@ -374,3 +376,34 @@ fn self_contained_transaction_with_extra_gas_should_adjust_weight_with_post_disp ); }); } + +#[test] +fn validated_transaction_apply_zero_gas_price_works() { + let (pairs, mut ext) = new_test_ext_with_initial_balance(2, 1_000); + let alice = &pairs[0]; + let bob = &pairs[1]; + let substrate_alice = + ::AddressMapping::into_account_id(alice.address); + let substrate_bob = ::AddressMapping::into_account_id(bob.address); + + ext.execute_with(|| { + let transaction = EIP2930UnsignedTransaction { + nonce: U256::zero(), + gas_price: U256::zero(), + gas_limit: U256::from(21_000), + action: ethereum::TransactionAction::Call(bob.address), + value: U256::from(100), + input: Default::default(), + } + .sign(&alice.private_key, None); + + assert_ok!(crate::ValidatedTransaction::::apply( + alice.address, + transaction + )); + // Alice didn't pay fees, transfer 100 to Bob. + assert_eq!(Balances::free_balance(&substrate_alice), 900); + // Bob received 100 from Alice. + assert_eq!(Balances::free_balance(&substrate_bob), 1_100); + }); +} diff --git a/frame/ethereum/src/tests/legacy.rs b/frame/ethereum/src/tests/legacy.rs index 84acb27d94..c749125a5d 100644 --- a/frame/ethereum/src/tests/legacy.rs +++ b/frame/ethereum/src/tests/legacy.rs @@ -18,6 +18,8 @@ //! Consensus extension module tests for BABE consensus. use super::*; +use fp_ethereum::ValidatedTransaction; +use pallet_evm::AddressMapping; fn legacy_erc20_creation_unsigned_transaction() -> LegacyUnsignedTransaction { LegacyUnsignedTransaction { @@ -374,3 +376,34 @@ fn self_contained_transaction_with_extra_gas_should_adjust_weight_with_post_disp ); }); } + +#[test] +fn validated_transaction_apply_zero_gas_price_works() { + let (pairs, mut ext) = new_test_ext_with_initial_balance(2, 1_000); + let alice = &pairs[0]; + let bob = &pairs[1]; + let substrate_alice = + ::AddressMapping::into_account_id(alice.address); + let substrate_bob = ::AddressMapping::into_account_id(bob.address); + + ext.execute_with(|| { + let transaction = LegacyUnsignedTransaction { + nonce: U256::zero(), + gas_price: U256::zero(), + gas_limit: U256::from(21_000), + action: ethereum::TransactionAction::Call(bob.address), + value: U256::from(100), + input: Default::default(), + } + .sign(&alice.private_key); + + assert_ok!(crate::ValidatedTransaction::::apply( + alice.address, + transaction + )); + // Alice didn't pay fees, transfer 100 to Bob. + assert_eq!(Balances::free_balance(&substrate_alice), 900); + // Bob received 100 from Alice. + assert_eq!(Balances::free_balance(&substrate_bob), 1_100); + }); +} diff --git a/frame/evm/src/runner/stack.rs b/frame/evm/src/runner/stack.rs index 2d1c737ba0..49b475d057 100644 --- a/frame/evm/src/runner/stack.rs +++ b/frame/evm/src/runner/stack.rs @@ -132,6 +132,9 @@ where { let (total_fee_per_gas, _actual_priority_fee_per_gas) = match (max_fee_per_gas, max_priority_fee_per_gas, is_transactional) { + // Zero max_fee_per_gas for validated transactional calls exist in XCM -> EVM + // because fees are already withdrawn in the xcm-executor. + (Some(max_fee), _, true) if max_fee.is_zero() => (U256::zero(), U256::zero()), // With no tip, we pay exactly the base_fee (Some(_), None, _) => (base_fee, U256::zero()), // With tip, we include as much of the tip on top of base_fee that we can, never