diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index b2eafe609c..9016c0c91a 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -672,15 +672,18 @@ pub trait OnChargeEVMTransaction { /// After the transaction was executed the actual fee can be calculated. /// This function should refund any overpaid fees and optionally deposit - /// the corrected amount. + /// the corrected amount, and handles the base fee rationing using the provided + /// `OnUnbalanced` implementation. + /// Returns the `NegativeImbalance` - if any - produced by the priority fee. fn correct_and_deposit_fee( who: &H160, corrected_fee: U256, + base_fee: U256, already_withdrawn: Self::LiquidityInfo, - ); + ) -> Self::LiquidityInfo; - /// Introduced in EIP1559 to handle the priority tip payment to the block Author. - fn pay_priority_fee(tip: U256); + /// Introduced in EIP1559 to handle the priority tip. + fn pay_priority_fee(tip: Self::LiquidityInfo); } /// Implements the transaction payment for a pallet implementing the `Currency` @@ -724,8 +727,9 @@ where fn correct_and_deposit_fee( who: &H160, corrected_fee: U256, + base_fee: U256, already_withdrawn: Self::LiquidityInfo, - ) { + ) -> Self::LiquidityInfo { if let Some(paid) = already_withdrawn { let account_id = T::AddressMapping::into_account_id(*who); @@ -762,13 +766,21 @@ where .offset(refund_imbalance) .same() .unwrap_or_else(|_| C::NegativeImbalance::zero()); - OU::on_unbalanced(adjusted_paid); + + let (base_fee, tip) = adjusted_paid.split(base_fee.low_u128().unique_saturated_into()); + // Handle base fee. Can be either burned, rationed, etc ... + OU::on_unbalanced(base_fee); + return Some(tip); } + None } - fn pay_priority_fee(tip: U256) { - let account_id = T::AddressMapping::into_account_id(>::find_author()); - let _ = C::deposit_into_existing(&account_id, tip.low_u128().unique_saturated_into()); + fn pay_priority_fee(tip: Self::LiquidityInfo) { + // Default Ethereum behaviour: issue the tip to the block author. + if let Some(tip) = tip { + let account_id = T::AddressMapping::into_account_id(>::find_author()); + let _ = C::deposit_into_existing(&account_id, tip.peek()); + } } } @@ -793,12 +805,13 @@ impl OnChargeEVMTransaction for () fn correct_and_deposit_fee( who: &H160, corrected_fee: U256, + base_fee: U256, already_withdrawn: Self::LiquidityInfo, - ) { - ::Currency, ()> as OnChargeEVMTransaction>::correct_and_deposit_fee(who, corrected_fee, already_withdrawn) + ) -> Self::LiquidityInfo { + ::Currency, ()> as OnChargeEVMTransaction>::correct_and_deposit_fee(who, corrected_fee, base_fee, already_withdrawn) } - fn pay_priority_fee(tip: U256) { + fn pay_priority_fee(tip: Self::LiquidityInfo) { ::Currency, ()> as OnChargeEVMTransaction>::pay_priority_fee(tip); } } diff --git a/frame/evm/src/runner/stack.rs b/frame/evm/src/runner/stack.rs index 8257e1c47b..a74a59f7c2 100644 --- a/frame/evm/src/runner/stack.rs +++ b/frame/evm/src/runner/stack.rs @@ -154,20 +154,18 @@ impl Runner { // Post execution. let used_gas = U256::from(executor.used_gas()); - let (actual_fee, actual_priority_fee) = - if let Some(max_priority_fee) = max_priority_fee_per_gas { - let actual_priority_fee = max_fee_per_gas - .saturating_sub(base_fee) - .min(max_priority_fee) - .saturating_mul(used_gas); - let actual_fee = executor - .fee(base_fee) - .checked_add(actual_priority_fee) - .unwrap_or_else(U256::max_value); - (actual_fee, Some(actual_priority_fee)) - } else { - (executor.fee(base_fee), None) - }; + let actual_fee = if let Some(max_priority_fee) = max_priority_fee_per_gas { + let actual_priority_fee = max_fee_per_gas + .saturating_sub(base_fee) + .min(max_priority_fee) + .saturating_mul(used_gas); + executor + .fee(base_fee) + .checked_add(actual_priority_fee) + .unwrap_or_else(U256::max_value) + } else { + executor.fee(base_fee) + }; log::debug!( target: "evm", "Execution {:?} [source: {:?}, value: {}, gas_limit: {}, actual_fee: {}, is_transactional: {}]", @@ -199,10 +197,16 @@ impl Runner { // Refunded 200 - 40 = 160. // Tip 5 * 6 = 30. // Burned 200 - (160 + 30) = 10. Which is equivalent to gas_used * base_fee. - T::OnChargeTransaction::correct_and_deposit_fee(&source, actual_fee, fee); - if let Some(actual_priority_fee) = actual_priority_fee { - T::OnChargeTransaction::pay_priority_fee(actual_priority_fee); - } + let actual_priority_fee = T::OnChargeTransaction::correct_and_deposit_fee( + &source, + // Actual fee after evm execution, including tip. + actual_fee, + // Base fee. + executor.fee(base_fee), + // Fee initially withdrawn. + fee, + ); + T::OnChargeTransaction::pay_priority_fee(actual_priority_fee); let state = executor.into_state(); diff --git a/frame/evm/src/tests.rs b/frame/evm/src/tests.rs index f928f02833..b1f23882aa 100644 --- a/frame/evm/src/tests.rs +++ b/frame/evm/src/tests.rs @@ -127,7 +127,7 @@ fn fee_deduction() { assert_eq!(Balances::free_balance(&substrate_addr), 90); // Refund fees as 5 units - <::OnChargeTransaction as OnChargeEVMTransaction>::correct_and_deposit_fee(&evm_addr, U256::from(5), imbalance); + <::OnChargeTransaction as OnChargeEVMTransaction>::correct_and_deposit_fee(&evm_addr, U256::from(5), U256::from(5), imbalance); assert_eq!(Balances::free_balance(&substrate_addr), 95); }); } @@ -249,6 +249,30 @@ fn author_should_get_tip() { }); } +#[test] +fn issuance_after_tip() { + new_test_ext().execute_with(|| { + let before_tip = ::Currency::total_issuance(); + let result = EVM::call( + Origin::root(), + H160::default(), + H160::from_str("1000000000000000000000000000000000000001").unwrap(), + Vec::new(), + U256::from(1), + 1000000, + U256::from(2_000_000_000), + Some(U256::from(1)), + None, + Vec::new(), + ); + result.expect("EVM can be called"); + let after_tip = ::Currency::total_issuance(); + // Only base fee is burned + let (base_fee, _) = ::FeeCalculator::min_gas_price(); + assert_eq!(after_tip, (before_tip - (base_fee.low_u64() * 21_000))); + }); +} + #[test] fn author_same_balance_without_tip() { new_test_ext().execute_with(|| {