From bb4bf298ec2eb7e804e6d233012c60b3dc760f91 Mon Sep 17 00:00:00 2001 From: Mablr <59505383+mablr@users.noreply.github.com> Date: Sat, 14 Jun 2025 17:29:01 +0200 Subject: [PATCH] feat(gas_oracle): implement median-based priority fee suggestion for Optimism (#16794) --- crates/optimism/rpc/src/eth/mod.rs | 3 +- crates/rpc/rpc-eth-types/src/gas_oracle.rs | 74 +++++++++++++++++++++- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/crates/optimism/rpc/src/eth/mod.rs b/crates/optimism/rpc/src/eth/mod.rs index df709baedc..7d37873a4d 100644 --- a/crates/optimism/rpc/src/eth/mod.rs +++ b/crates/optimism/rpc/src/eth/mod.rs @@ -244,9 +244,8 @@ where } async fn suggested_priority_fee(&self) -> Result { - let base_tip = self.inner.eth_api.gas_oracle().suggest_tip_cap().await?; let min_tip = U256::from(self.inner.min_suggested_priority_fee); - Ok(base_tip.max(min_tip)) + self.inner.eth_api.gas_oracle().op_suggest_tip_cap(min_tip).await.map_err(Into::into) } } diff --git a/crates/rpc/rpc-eth-types/src/gas_oracle.rs b/crates/rpc/rpc-eth-types/src/gas_oracle.rs index 27b23b54e4..795363f3df 100644 --- a/crates/rpc/rpc-eth-types/src/gas_oracle.rs +++ b/crates/rpc/rpc-eth-types/src/gas_oracle.rs @@ -2,7 +2,7 @@ //! previous blocks. use super::{EthApiError, EthResult, EthStateCache, RpcInvalidTransactionError}; -use alloy_consensus::{constants::GWEI_TO_WEI, BlockHeader, Transaction}; +use alloy_consensus::{constants::GWEI_TO_WEI, BlockHeader, Transaction, TxReceipt}; use alloy_eips::BlockNumberOrTag; use alloy_primitives::{B256, U256}; use alloy_rpc_types_eth::BlockId; @@ -273,6 +273,78 @@ where Ok(Some((parent_hash, prices))) } + /// Suggests a max priority fee value using a simplified and more predictable algorithm + /// appropriate for chains like Optimism with a single known block builder. + /// + /// It returns either: + /// - The minimum suggested priority fee when blocks have capacity + /// - 10% above the median effective priority fee from the last block when at capacity + /// + /// A block is considered at capacity if its total gas used plus the maximum single transaction + /// gas would exceed the block's gas limit. + pub async fn op_suggest_tip_cap(&self, min_suggested_priority_fee: U256) -> EthResult { + let header = self + .provider + .sealed_header_by_number_or_tag(BlockNumberOrTag::Latest)? + .ok_or(EthApiError::HeaderNotFound(BlockId::latest()))?; + + let mut inner = self.inner.lock().await; + + // if we have stored a last price, then we check whether or not it was for the same head + if inner.last_price.block_hash == header.hash() { + return Ok(inner.last_price.price); + } + + let mut suggestion = min_suggested_priority_fee; + + // find the maximum gas used by any of the transactions in the block to use as the + // capacity margin for the block, if no receipts are found return the + // suggested_min_priority_fee + let Some(max_tx_gas_used) = self + .cache + .get_receipts(header.hash()) + .await? + .ok_or(EthApiError::ReceiptsNotFound(BlockId::latest()))? + // get the gas used by each transaction in the block, by subtracting the + // cumulative gas used of the previous transaction from the cumulative gas used of the + // current transaction. This is because there is no gas_used() method on the Receipt + // trait. + .windows(2) + .map(|window| { + let prev = window[0].cumulative_gas_used(); + let curr = window[1].cumulative_gas_used(); + curr - prev + }) + .max() + else { + return Ok(suggestion); + }; + + // if the block is at capacity, the suggestion must be increased + if header.gas_used() + max_tx_gas_used > header.gas_limit() { + let Some(median_tip) = self.get_block_median_tip(header.hash()).await? else { + return Ok(suggestion); + }; + + let new_suggestion = median_tip + median_tip / U256::from(10); + + if new_suggestion > suggestion { + suggestion = new_suggestion; + } + } + + // constrain to the max price + if let Some(max_price) = self.oracle_config.max_price { + if suggestion > max_price { + suggestion = max_price; + } + } + + inner.last_price = GasPriceOracleResult { block_hash: header.hash(), price: suggestion }; + + Ok(suggestion) + } + /// Get the median tip value for the given block. This is useful for determining /// tips when a block is at capacity. ///