diff --git a/crates/rpc/rpc/src/eth/revm_utils.rs b/crates/rpc/rpc/src/eth/revm_utils.rs index 1472eba08..445c67368 100644 --- a/crates/rpc/rpc/src/eth/revm_utils.rs +++ b/crates/rpc/rpc/src/eth/revm_utils.rs @@ -1,5 +1,7 @@ //! utilities for working with revm +use std::cmp::min; + use crate::eth::error::{EthApiError, EthResult, RpcInvalidTransactionError}; #[cfg(feature = "optimism")] use reth_primitives::revm::env::fill_op_tx_env; @@ -438,9 +440,6 @@ pub(crate) struct CallFees { impl CallFees { /// Ensures the fields of a [TransactionRequest] are not conflicting. /// - /// If no `gasPrice` or `maxFeePerGas` is set, then the `gas_price` in the returned `gas_price` - /// will be `0`. See: - /// /// # EIP-4844 transactions /// /// Blob transactions have an additional fee parameter `maxFeePerBlobGas`. @@ -458,21 +457,38 @@ impl CallFees { max_fee_per_blob_gas: Option, block_blob_fee: Option, ) -> EthResult { - /// Ensures that the transaction's max fee is lower than the priority fee, if any. - fn ensure_valid_fee_cap( - max_fee: U256, + /// Get the effective gas price of a transaction as specfified in EIP-1559 with relevant + /// checks. + fn get_effective_gas_price( + max_fee_per_gas: Option, max_priority_fee_per_gas: Option, - ) -> EthResult<()> { - if let Some(max_priority) = max_priority_fee_per_gas { - if max_priority > max_fee { - // Fail early - return Err( - // `max_priority_fee_per_gas` is greater than the `max_fee_per_gas` - RpcInvalidTransactionError::TipAboveFeeCap.into(), - ) + block_base_fee: U256, + ) -> EthResult { + match max_fee_per_gas { + Some(max_fee) => { + if max_fee < block_base_fee { + // `base_fee_per_gas` is greater than the `max_fee_per_gas` + return Err(RpcInvalidTransactionError::FeeCapTooLow.into()) + } + if max_fee < max_priority_fee_per_gas.unwrap_or(U256::ZERO) { + return Err( + // `max_priority_fee_per_gas` is greater than the `max_fee_per_gas` + RpcInvalidTransactionError::TipAboveFeeCap.into(), + ) + } + Ok(min( + max_fee, + block_base_fee + .checked_add(max_priority_fee_per_gas.unwrap_or(U256::ZERO)) + .ok_or_else(|| { + EthApiError::from(RpcInvalidTransactionError::TipVeryHigh) + })?, + )) } + None => Ok(block_base_fee + .checked_add(max_priority_fee_per_gas.unwrap_or(U256::ZERO)) + .ok_or_else(|| EthApiError::from(RpcInvalidTransactionError::TipVeryHigh))?), } - Ok(()) } let has_blob_hashes = @@ -491,18 +507,26 @@ impl CallFees { } (None, max_fee_per_gas, max_priority_fee_per_gas, None) => { // request for eip-1559 transaction - let max_fee = max_fee_per_gas.unwrap_or(block_base_fee); - ensure_valid_fee_cap(max_fee, max_priority_fee_per_gas)?; - + let effective_gas_price = get_effective_gas_price( + max_fee_per_gas, + max_priority_fee_per_gas, + block_base_fee, + )?; let max_fee_per_blob_gas = has_blob_hashes.then_some(block_blob_fee).flatten(); - Ok(CallFees { gas_price: max_fee, max_priority_fee_per_gas, max_fee_per_blob_gas }) + Ok(CallFees { + gas_price: effective_gas_price, + max_priority_fee_per_gas, + max_fee_per_blob_gas, + }) } (None, max_fee_per_gas, max_priority_fee_per_gas, Some(max_fee_per_blob_gas)) => { // request for eip-4844 transaction - let max_fee = max_fee_per_gas.unwrap_or(block_base_fee); - ensure_valid_fee_cap(max_fee, max_priority_fee_per_gas)?; - + let effective_gas_price = get_effective_gas_price( + max_fee_per_gas, + max_priority_fee_per_gas, + block_base_fee, + )?; // Ensure blob_hashes are present if !has_blob_hashes { // Blob transaction but no blob hashes @@ -510,7 +534,7 @@ impl CallFees { } Ok(CallFees { - gas_price: max_fee, + gas_price: effective_gas_price, max_priority_fee_per_gas, max_fee_per_blob_gas: Some(max_fee_per_blob_gas), }) @@ -629,6 +653,8 @@ where #[cfg(test)] mod tests { + use reth_primitives::constants::GWEI_TO_WEI; + use super::*; #[test] @@ -660,4 +686,76 @@ mod tests { assert!(gas_price.is_zero()); assert_eq!(max_fee_per_blob_gas, Some(U256::from(99))); } + + #[test] + fn test_eip_1559_fees() { + let CallFees { gas_price, .. } = CallFees::ensure_fees( + None, + Some(U256::from(25 * GWEI_TO_WEI)), + Some(U256::from(15 * GWEI_TO_WEI)), + U256::from(15 * GWEI_TO_WEI), + None, + None, + Some(U256::ZERO), + ) + .unwrap(); + assert_eq!(gas_price, U256::from(25 * GWEI_TO_WEI)); + + let CallFees { gas_price, .. } = CallFees::ensure_fees( + None, + Some(U256::from(25 * GWEI_TO_WEI)), + Some(U256::from(5 * GWEI_TO_WEI)), + U256::from(15 * GWEI_TO_WEI), + None, + None, + Some(U256::ZERO), + ) + .unwrap(); + assert_eq!(gas_price, U256::from(20 * GWEI_TO_WEI)); + + let CallFees { gas_price, .. } = CallFees::ensure_fees( + None, + Some(U256::from(30 * GWEI_TO_WEI)), + Some(U256::from(30 * GWEI_TO_WEI)), + U256::from(15 * GWEI_TO_WEI), + None, + None, + Some(U256::ZERO), + ) + .unwrap(); + assert_eq!(gas_price, U256::from(30 * GWEI_TO_WEI)); + + let call_fees = CallFees::ensure_fees( + None, + Some(U256::from(30 * GWEI_TO_WEI)), + Some(U256::from(31 * GWEI_TO_WEI)), + U256::from(15 * GWEI_TO_WEI), + None, + None, + Some(U256::ZERO), + ); + assert!(call_fees.is_err()); + + let call_fees = CallFees::ensure_fees( + None, + Some(U256::from(5 * GWEI_TO_WEI)), + Some(U256::from(GWEI_TO_WEI)), + U256::from(15 * GWEI_TO_WEI), + None, + None, + Some(U256::ZERO), + ); + assert!(call_fees.is_err()); + + let call_fees = CallFees::ensure_fees( + None, + Some(U256::MAX), + Some(U256::MAX), + U256::from(5 * GWEI_TO_WEI), + None, + None, + Some(U256::ZERO), + ); + assert!(call_fees.is_err()); + } }