From a89bd4f2729cace1ac7f46d10b323044cd3de305 Mon Sep 17 00:00:00 2001 From: dancoombs Date: Mon, 14 Oct 2024 14:41:00 -0500 Subject: [PATCH] feat(provider): modify da gas provider interface for better caching/sync --- Cargo.toml | 2 +- crates/provider/Cargo.toml | 2 +- crates/provider/src/alloy/da/arbitrum.rs | 38 ++++- crates/provider/src/alloy/da/local/bedrock.rs | 148 ++++++++--------- crates/provider/src/alloy/da/local/nitro.rs | 151 ++++++++++++------ crates/provider/src/alloy/da/mod.rs | 109 +++++++------ crates/provider/src/alloy/da/optimism.rs | 36 ++++- crates/provider/src/alloy/entry_point/v0_6.rs | 60 +++++-- crates/provider/src/alloy/entry_point/v0_7.rs | 61 +++++-- crates/provider/src/traits/da.rs | 66 ++++++++ crates/provider/src/traits/entry_point.rs | 37 ++++- crates/provider/src/traits/mod.rs | 3 + crates/provider/src/traits/test_utils.rs | 17 +- crates/sim/src/estimation/v0_6.rs | 6 +- crates/sim/src/gas/gas.rs | 2 + crates/types/Cargo.toml | 2 +- crates/types/src/chain.rs | 19 +-- crates/types/src/da.rs | 96 +++++++++++ crates/types/src/lib.rs | 2 + 19 files changed, 609 insertions(+), 248 deletions(-) create mode 100644 crates/provider/src/traits/da.rs create mode 100644 crates/types/src/da.rs diff --git a/Cargo.toml b/Cargo.toml index 054142d94..07a3fa2c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ "crates/sim/", "crates/task/", "crates/types/", - "crates/utils/", + "crates/utils/" ] default-members = ["bin/rundler"] resolver = "2" diff --git a/crates/provider/Cargo.toml b/crates/provider/Cargo.toml index a8359442d..2e73669d6 100644 --- a/crates/provider/Cargo.toml +++ b/crates/provider/Cargo.toml @@ -32,9 +32,9 @@ anyhow.workspace = true async-trait.workspace = true auto_impl.workspace = true const-hex.workspace = true -thiserror.workspace = true futures-util.workspace = true reqwest.workspace = true +thiserror.workspace = true tokio.workspace = true tower.workspace = true tracing.workspace = true diff --git a/crates/provider/src/alloy/da/arbitrum.rs b/crates/provider/src/alloy/da/arbitrum.rs index 0e5f1b113..8a43ef1e8 100644 --- a/crates/provider/src/alloy/da/arbitrum.rs +++ b/crates/provider/src/alloy/da/arbitrum.rs @@ -11,10 +11,11 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use alloy_primitives::{Address, Bytes, B256}; +use alloy_primitives::{Address, Bytes}; use alloy_provider::Provider as AlloyProvider; use alloy_sol_types::sol; use alloy_transport::Transport; +use rundler_types::da::{DAGasBlockData, DAGasUOData}; use NodeInterface::NodeInterfaceInstance; use super::DAGasOracle; @@ -63,18 +64,43 @@ where { async fn estimate_da_gas( &self, - _hash: B256, + uo_data: Bytes, to: Address, - data: Bytes, block: BlockHashOrNumber, _gas_price: u128, - ) -> ProviderResult { + ) -> ProviderResult<(u128, DAGasUOData, DAGasBlockData)> { let ret = self .node_interface - .gasEstimateL1Component(to, true, data) + .gasEstimateL1Component(to, true, uo_data) .block(block.into()) .call() .await?; - Ok(ret.gasEstimateForL1 as u128) + Ok(( + ret.gasEstimateForL1 as u128, + DAGasUOData::Empty, + DAGasBlockData::Empty, + )) + } + + async fn block_data(&self, _block: BlockHashOrNumber) -> ProviderResult { + Ok(DAGasBlockData::Empty) + } + + async fn uo_data( + &self, + _uo_data: Bytes, + _to: Address, + _block: BlockHashOrNumber, + ) -> ProviderResult { + Ok(DAGasUOData::Empty) + } + + fn calc_da_gas_sync( + &self, + _uo_data: &DAGasUOData, + _block_data: &DAGasBlockData, + _gas_price: u128, + ) -> u128 { + panic!("ArbitrumNitroDAGasOracle does not support calc_da_gas_sync") } } diff --git a/crates/provider/src/alloy/da/local/bedrock.rs b/crates/provider/src/alloy/da/local/bedrock.rs index 7e148f75d..9e9f92b44 100644 --- a/crates/provider/src/alloy/da/local/bedrock.rs +++ b/crates/provider/src/alloy/da/local/bedrock.rs @@ -11,14 +11,13 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use std::sync::Mutex as StdMutex; - -use alloy_primitives::{Address, Bytes, B256}; +use alloy_primitives::{Address, Bytes}; use alloy_provider::Provider as AlloyProvider; use alloy_rpc_types_eth::state::{AccountOverride, StateOverride}; use alloy_transport::Transport; use anyhow::Context; use reth_tasks::pool::BlockingTaskPool; +use rundler_types::da::{BedrockDAGasBlockData, BedrockDAGasUOData, DAGasBlockData, DAGasUOData}; use rundler_utils::cache::LruMap; use tokio::sync::Mutex as TokioMutex; @@ -47,19 +46,10 @@ const MIN_TRANSACTION_SIZE: i128 = 100_000_000; pub(crate) struct LocalBedrockDAGasOracle { oracle: GasPriceOracleInstance, multicaller: MulticallInstance, - block_da_data_cache: TokioMutex>, - uo_cache: StdMutex>, + block_data_cache: TokioMutex>, blocking_task_pool: BlockingTaskPool, } -#[derive(Debug, Clone, Copy)] -struct BlockDAData { - base_fee_scalar: u64, - l1_base_fee: u64, - blob_base_fee_scalar: u64, - blob_base_fee: u64, -} - impl LocalBedrockDAGasOracle where AP: AlloyProvider + Clone, @@ -71,8 +61,7 @@ where Self { oracle, multicaller, - block_da_data_cache: TokioMutex::new(LruMap::new(100)), - uo_cache: StdMutex::new(LruMap::new(10000)), + block_data_cache: TokioMutex::new(LruMap::new(100)), blocking_task_pool: BlockingTaskPool::build() .expect("failed to build blocking task pool"), } @@ -87,27 +76,59 @@ where { async fn estimate_da_gas( &self, - hash: B256, - _to: Address, data: Bytes, + to: Address, block: BlockHashOrNumber, gas_price: u128, - ) -> ProviderResult { - let block_da_data = { - let mut cache = self.block_da_data_cache.lock().await; - - match cache.get(&block) { - Some(block_da_data) => *block_da_data, - None => { - let block_da_data = self.get_block_da_data(block).await?; - cache.insert(block, block_da_data); - block_da_data - } + ) -> ProviderResult<(u128, DAGasUOData, DAGasBlockData)> { + let block_data = self.block_data(block).await?; + let uo_data = self.uo_data(data, to, block).await?; + let da_gas = self.calc_da_gas_sync(&uo_data, &block_data, gas_price); + Ok((da_gas, uo_data, block_data)) + } + + async fn block_data(&self, block: BlockHashOrNumber) -> ProviderResult { + let mut cache = self.block_data_cache.lock().await; + match cache.get(&block) { + Some(block_data) => Ok(DAGasBlockData::Bedrock(block_data.clone())), + None => { + let block_data = self.get_block_data(block).await?; + cache.insert(block, block_data.clone()); + Ok(DAGasBlockData::Bedrock(block_data)) } + } + } + + async fn uo_data( + &self, + uo_data: Bytes, + _to: Address, + _block: BlockHashOrNumber, + ) -> ProviderResult { + let uo_data = self.get_uo_data(uo_data).await?; + Ok(DAGasUOData::Bedrock(uo_data)) + } + + fn calc_da_gas_sync( + &self, + uo_data: &DAGasUOData, + block_data: &DAGasBlockData, + gas_price: u128, + ) -> u128 { + let block_da_data = match block_data { + DAGasBlockData::Bedrock(block_da_data) => block_da_data, + _ => panic!("LocalBedrockDAGasOracle only supports Bedrock block data"), + }; + let uo_data = match uo_data { + DAGasUOData::Bedrock(uo_data) => uo_data, + _ => panic!("LocalBedrockDAGasOracle only supports Bedrock user operation data"), }; - let l1_fee = self.fjord_l1_fee(hash, data, &block_da_data).await?; - Ok(l1_fee.checked_div(gas_price).unwrap_or(u128::MAX)) + let fee_scaled = (block_da_data.base_fee_scalar * 16 * block_da_data.l1_base_fee + + block_da_data.blob_base_fee_scalar * block_da_data.blob_base_fee) + as u128; + let l1_fee = (uo_data.uo_units as u128 * fee_scaled) / DECIMAL_SCALAR; + l1_fee.checked_div(gas_price).unwrap_or(u128::MAX) } } @@ -125,7 +146,10 @@ where .unwrap_or(false) } - async fn get_block_da_data(&self, block: BlockHashOrNumber) -> ProviderResult { + async fn get_block_data( + &self, + block: BlockHashOrNumber, + ) -> ProviderResult { assert!(self.is_fjord().await); let calls = vec![ @@ -187,7 +211,7 @@ where .try_into() .context("blob_base_fee too large for u64")?; - Ok(BlockDAData { + Ok(BedrockDAGasBlockData { base_fee_scalar, l1_base_fee, blob_base_fee_scalar, @@ -195,49 +219,27 @@ where }) } - async fn fjord_l1_fee( - &self, - hash: B256, - data: Bytes, - block_da_data: &BlockDAData, - ) -> ProviderResult { - let maybe_uo_units = self.uo_cache.lock().unwrap().get(&hash).cloned(); - let uo_units = match maybe_uo_units { - Some(uo_units) => uo_units, - None => { - // Blocking call compressing potentially a lot of data. - // Generally takes more than 100µs so should be spawned on blocking threadpool. - // https://ryhl.io/blog/async-what-is-blocking/ - // https://docs.rs/tokio/latest/tokio/index.html#cpu-bound-tasks-and-blocking-code - let compressed_len = self - .blocking_task_pool - .spawn(move || { - let mut buf = vec![0; data.len() * 2]; - rundler_bindings_fastlz::compress(&data, &mut buf).len() as u64; - }) - .await - .map_err(|e| anyhow::anyhow!("failed to compress data: {:?}", e))?; - - let uo_units = compressed_len + 68; - self.uo_cache.lock().unwrap().insert(hash, uo_units); - uo_units - } - }; + async fn get_uo_data(&self, data: Bytes) -> ProviderResult { + // Blocking call compressing potentially a lot of data. + // Generally takes more than 100µs so should be spawned on blocking threadpool. + // https://ryhl.io/blog/async-what-is-blocking/ + // https://docs.rs/tokio/latest/tokio/index.html#cpu-bound-tasks-and-blocking-code + let compressed_len = self + .blocking_task_pool + .spawn(move || { + let mut buf = vec![0; data.len() * 2]; + rundler_bindings_fastlz::compress(&data, &mut buf).len() as u64 + }) + .await + .map_err(|e| anyhow::anyhow!("failed to compress data: {:?}", e))?; - Ok(Self::fjord_l1_cost(uo_units, block_da_data)) - } + let compressed_with_buffer = compressed_len + 68; - fn fjord_l1_cost(fast_lz_size: u64, block_da_data: &BlockDAData) -> u128 { - let estimated_size = Self::fjord_linear_regression(fast_lz_size) as u128; - let fee_scaled = (block_da_data.base_fee_scalar * 16 * block_da_data.l1_base_fee - + block_da_data.blob_base_fee_scalar * block_da_data.blob_base_fee) - as u128; - (estimated_size * fee_scaled) / DECIMAL_SCALAR - } + let estimated_size = COST_INTERCEPT + COST_FASTLZ_COEF * compressed_with_buffer as i128; + let uo_units = estimated_size.clamp(MIN_TRANSACTION_SIZE, u64::MAX as i128); - fn fjord_linear_regression(fast_lz_size: u64) -> u64 { - let estimated_size = COST_INTERCEPT + COST_FASTLZ_COEF * fast_lz_size as i128; - let ret = estimated_size.clamp(MIN_TRANSACTION_SIZE, u64::MAX as i128); - ret as u64 + Ok(BedrockDAGasUOData { + uo_units: uo_units as u64, + }) } } diff --git a/crates/provider/src/alloy/da/local/nitro.rs b/crates/provider/src/alloy/da/local/nitro.rs index 31e831975..678f58f76 100644 --- a/crates/provider/src/alloy/da/local/nitro.rs +++ b/crates/provider/src/alloy/da/local/nitro.rs @@ -11,12 +11,11 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use std::sync::Mutex as StdMutex; - -use alloy_primitives::{Address, Bytes, B256}; +use alloy_primitives::{Address, Bytes}; use alloy_provider::Provider as AlloyProvider; use alloy_transport::Transport; use anyhow::Context; +use rundler_types::da::{DAGasBlockData, DAGasUOData, NitroDAGasBlockData, NitroDAGasUOData}; use rundler_utils::cache::LruMap; use tokio::sync::Mutex as TokioMutex; @@ -32,15 +31,7 @@ use crate::{ pub(crate) struct CachedNitroDAGasOracle { node_interface: NodeInterfaceInstance, // Use a tokio::Mutex here to ensure only one network call per block, other threads can async wait for the result - block_da_data_cache: TokioMutex>, - // Use a std::sync::Mutex here as both reads and writes to the LRU cache require interior mutability - uo_cache: StdMutex>, -} - -#[derive(Debug, Clone, Copy)] -struct BlockDAData { - l1_base_fee: u128, - base_fee: u128, + block_data_cache: TokioMutex>, } impl CachedNitroDAGasOracle @@ -51,8 +42,7 @@ where pub(crate) fn new(oracle_address: Address, provider: AP) -> Self { Self { node_interface: NodeInterfaceInstance::new(oracle_address, provider), - block_da_data_cache: TokioMutex::new(LruMap::new(100)), - uo_cache: StdMutex::new(LruMap::new(10000)), + block_data_cache: TokioMutex::new(LruMap::new(100)), } } } @@ -67,45 +57,82 @@ where { async fn estimate_da_gas( &self, - hash: B256, - to: Address, data: Bytes, + to: Address, block: BlockHashOrNumber, _gas_price: u128, - ) -> ProviderResult { - let mut cache = self.block_da_data_cache.lock().await; + ) -> ProviderResult<(u128, DAGasUOData, DAGasBlockData)> { + let mut cache = self.block_data_cache.lock().await; match cache.get(&block) { Some(block_da_data) => { // Found the block, drop the block cache - let block_da_data = *block_da_data; + let block_da_data = block_da_data.clone(); drop(cache); - // Check if we have uo units cached - let maybe_uo_units = self.uo_cache.lock().unwrap().get(&hash).cloned(); - - if let Some(uo_units) = maybe_uo_units { - // Double cache hit, calculate the da fee from the cached uo units - Ok(calculate_da_fee(uo_units, &block_da_data)) - } else { - // UO cache miss, make remote call to get the da fee - let (_, uo_units, gas_estimate_for_l1) = - self.get_da_data(to, data, block).await?; - self.uo_cache.lock().unwrap().insert(hash, uo_units); - Ok(gas_estimate_for_l1) - } + let uo_data = self.get_uo_data(to, data, block).await?; + let uo_data = DAGasUOData::Nitro(uo_data); + let block_data = DAGasBlockData::Nitro(block_da_data); + let l1_gas_estimate = self.calc_da_gas_sync(&uo_data, &block_data, _gas_price); + + Ok((l1_gas_estimate, uo_data, block_data)) } None => { // Block cache miss, make remote call to get the da fee - let (block_da_data, uo_units, gas_estimate_for_l1) = - self.get_da_data(to, data, block).await?; - cache.insert(block, block_da_data); + let (gas_estimate_for_l1, block_da_data) = + self.call_oracle_contract(to, data, block).await?; + cache.insert(block, block_da_data.clone()); drop(cache); - self.uo_cache.lock().unwrap().insert(hash, uo_units); - Ok(gas_estimate_for_l1) + let uo_units = calculate_uo_units(gas_estimate_for_l1, &block_da_data); + + Ok(( + gas_estimate_for_l1, + DAGasUOData::Nitro(NitroDAGasUOData { uo_units }), + DAGasBlockData::Nitro(block_da_data), + )) + } + } + } + + async fn block_data(&self, block: BlockHashOrNumber) -> ProviderResult { + let mut cache = self.block_data_cache.lock().await; + match cache.get(&block) { + Some(block_data) => Ok(DAGasBlockData::Nitro(block_data.clone())), + None => { + let block_data = self.get_block_data(block).await?; + cache.insert(block, block_data.clone()); + Ok(DAGasBlockData::Nitro(block_data)) } } } + + async fn uo_data( + &self, + uo_data: Bytes, + to: Address, + block: BlockHashOrNumber, + ) -> ProviderResult { + let uo_data = self.get_uo_data(to, uo_data, block).await?; + Ok(DAGasUOData::Nitro(uo_data)) + } + + fn calc_da_gas_sync( + &self, + uo_data: &DAGasUOData, + block_data: &DAGasBlockData, + _gas_price: u128, + ) -> u128 { + let uo_units = match uo_data { + DAGasUOData::Nitro(uo_data) => uo_data.uo_units, + _ => panic!("NitroDAGasOracle only supports Nitro DAGasUOData"), + }; + let block_data = match block_data { + DAGasBlockData::Nitro(block_data) => block_data, + _ => panic!("NitroDAGasOracle only supports Nitro DAGasBlockData"), + }; + + calculate_da_fee(uo_units, block_data) + } } impl CachedNitroDAGasOracle @@ -113,12 +140,36 @@ where AP: AlloyProvider, T: Transport + Clone, { - async fn get_da_data( + async fn get_block_data( + &self, + block: BlockHashOrNumber, + ) -> ProviderResult { + // phantom data, not important for the calculation + let data = Bytes::new(); + let to = Address::ZERO; + + let (_, block_data) = self.call_oracle_contract(to, data, block).await?; + + Ok(block_data) + } + + async fn get_uo_data( + &self, + to: Address, + data: Bytes, + block: BlockHashOrNumber, + ) -> ProviderResult { + let (l1_gas_estimate, block_da_data) = self.call_oracle_contract(to, data, block).await?; + let uo_units = calculate_uo_units(l1_gas_estimate, &block_da_data); + Ok(NitroDAGasUOData { uo_units }) + } + + async fn call_oracle_contract( &self, to: Address, data: Bytes, block: BlockHashOrNumber, - ) -> ProviderResult<(BlockDAData, u128, u128)> { + ) -> ProviderResult<(u128, NitroDAGasBlockData)> { let ret = self .node_interface .gasEstimateL1Component(to, true, data) @@ -135,15 +186,13 @@ where .try_into() .context("Arbitrum NodeInterface returned baseFee too big for u128")?; - let block_da_data = BlockDAData { - l1_base_fee, - base_fee, - }; - - // Calculate UO units from the gas estimate for L1 - let uo_units = calculate_uo_units(ret.gasEstimateForL1 as u128, &block_da_data); - - Ok((block_da_data, uo_units, ret.gasEstimateForL1 as u128)) + Ok(( + ret.gasEstimateForL1 as u128, + NitroDAGasBlockData { + l1_base_fee, + base_fee, + }, + )) } } @@ -154,8 +203,8 @@ where // // Errors should round down -// Calculate the da fee from the cahed scaled UO units -fn calculate_da_fee(uo_units: u128, block_da_data: &BlockDAData) -> u128 { +// Calculate the da fee from the cached scaled UO units +fn calculate_da_fee(uo_units: u128, block_da_data: &NitroDAGasBlockData) -> u128 { // Multiply by l1_base_fee let a = uo_units.saturating_mul(block_da_data.l1_base_fee); // Add 10% @@ -171,7 +220,7 @@ fn calculate_da_fee(uo_units: u128, block_da_data: &BlockDAData) -> u128 { } // Calculate scaled UO units from the da fee -fn calculate_uo_units(da_fee: u128, block_da_data: &BlockDAData) -> u128 { +fn calculate_uo_units(da_fee: u128, block_da_data: &NitroDAGasBlockData) -> u128 { // Undo base fee division, scale up to reduce rounding error let a = da_fee .saturating_mul(CACHE_UNITS_SCALAR) diff --git a/crates/provider/src/alloy/da/mod.rs b/crates/provider/src/alloy/da/mod.rs index a6436cc37..b9d86762a 100644 --- a/crates/provider/src/alloy/da/mod.rs +++ b/crates/provider/src/alloy/da/mod.rs @@ -11,12 +11,15 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use alloy_primitives::{Address, Bytes, B256}; +use alloy_primitives::{Address, Bytes}; use alloy_provider::Provider as AlloyProvider; use alloy_transport::Transport; -use rundler_types::chain::{ChainSpec, DAGasOracleContractType}; +use rundler_types::{ + chain::ChainSpec, + da::{DAGasBlockData, DAGasOracleContractType, DAGasUOData}, +}; -use crate::{BlockHashOrNumber, ProviderResult}; +use crate::{BlockHashOrNumber, DAGasOracle, ProviderResult}; mod arbitrum; use arbitrum::ArbitrumNitroDAGasOracle; @@ -25,33 +28,40 @@ use optimism::OptimismBedrockDAGasOracle; mod local; use local::{CachedNitroDAGasOracle, LocalBedrockDAGasOracle}; -/// Trait for a DA gas oracle -#[async_trait::async_trait] -#[auto_impl::auto_impl(&, &mut, Rc, Arc, Box)] -pub(crate) trait DAGasOracle: Send + Sync { - async fn estimate_da_gas( - &self, - hash: B256, - to: Address, - data: Bytes, - block: BlockHashOrNumber, - gas_price: u128, - ) -> ProviderResult; -} - struct ZeroDAGasOracle; #[async_trait::async_trait] impl DAGasOracle for ZeroDAGasOracle { async fn estimate_da_gas( &self, - _hash: B256, - _to: Address, _data: Bytes, + _to: Address, + _block: BlockHashOrNumber, + _gas_price: u128, + ) -> ProviderResult<(u128, DAGasUOData, DAGasBlockData)> { + Ok((0, DAGasUOData::Empty, DAGasBlockData::Empty)) + } + + async fn block_data(&self, _block: BlockHashOrNumber) -> ProviderResult { + Ok(DAGasBlockData::Empty) + } + + async fn uo_data( + &self, + _uo_data: Bytes, + _to: Address, _block: BlockHashOrNumber, + ) -> ProviderResult { + Ok(DAGasUOData::Empty) + } + + fn calc_da_gas_sync( + &self, + _uo_data: &DAGasUOData, + _block_data: &DAGasBlockData, _gas_price: u128, - ) -> ProviderResult { - Ok(0) + ) -> u128 { + panic!("ZeroDAGasOracle does not support calc_da_gas_sync") } } @@ -103,7 +113,6 @@ mod tests { async fn compare_opt_latest() { let provider = opt_provider(); let block = provider.get_block_number().await.unwrap(); - println!("block: {}", block); compare_opt_and_local_bedrock(provider, block.into()).await; } @@ -124,9 +133,9 @@ mod tests { async fn compare_arb_latest() { let provider = arb_provider(); let block = provider.get_block_number().await.unwrap(); - let (uo, hash) = test_uo_data_1(); + let uo = test_uo_data_1(); - compare_arb_and_cached_on_data(provider, block.into(), hash, uo).await; + compare_arb_and_cached_on_data(provider, block.into(), uo).await; } #[tokio::test] @@ -134,16 +143,16 @@ mod tests { async fn compare_arb_fixed() { let provider = arb_provider(); let block: BlockHashOrNumber = 262113260.into(); - let (uo, hash) = test_uo_data_1(); + let uo = test_uo_data_1(); - compare_arb_and_cached_on_data(provider, block, hash, uo).await; + compare_arb_and_cached_on_data(provider, block, uo).await; } #[tokio::test] #[ignore] async fn compare_arb_multiple() { - let (uo1, hash1) = test_uo_data_1(); - let (uo2, hash2) = test_uo_data_2(); + let uo1 = test_uo_data_1(); + let uo2 = test_uo_data_2(); let provider = arb_provider(); let oracle_addr = address!("00000000000000000000000000000000000000C8"); @@ -151,16 +160,16 @@ mod tests { let cached_oracle = CachedNitroDAGasOracle::new(oracle_addr, provider); let block: BlockHashOrNumber = 262113260.into(); - compare_oracles_on_data(&contract_oracle, &cached_oracle, block, hash1, uo1.clone()).await; - compare_oracles_on_data(&contract_oracle, &cached_oracle, block, hash2, uo2.clone()).await; + compare_oracles_on_data(&contract_oracle, &cached_oracle, block, uo1.clone()).await; + compare_oracles_on_data(&contract_oracle, &cached_oracle, block, uo2.clone()).await; let block: BlockHashOrNumber = 262113261.into(); - compare_oracles_on_data(&contract_oracle, &cached_oracle, block, hash1, uo1.clone()).await; - compare_oracles_on_data(&contract_oracle, &cached_oracle, block, hash2, uo2.clone()).await; + compare_oracles_on_data(&contract_oracle, &cached_oracle, block, uo1.clone()).await; + compare_oracles_on_data(&contract_oracle, &cached_oracle, block, uo2.clone()).await; let block: BlockHashOrNumber = 262113262.into(); - compare_oracles_on_data(&contract_oracle, &cached_oracle, block, hash1, uo1).await; - compare_oracles_on_data(&contract_oracle, &cached_oracle, block, hash2, uo2).await; + compare_oracles_on_data(&contract_oracle, &cached_oracle, block, uo1).await; + compare_oracles_on_data(&contract_oracle, &cached_oracle, block, uo2).await; } async fn compare_oracles( @@ -168,32 +177,29 @@ mod tests { oracle_b: &impl DAGasOracle, block: BlockHashOrNumber, ) { - let (uo, hash) = test_uo_data_1(); - compare_oracles_on_data(oracle_a, oracle_b, block, hash, uo).await; + let uo = test_uo_data_1(); + compare_oracles_on_data(oracle_a, oracle_b, block, uo).await; } async fn compare_oracles_on_data( oracle_a: &impl DAGasOracle, oracle_b: &impl DAGasOracle, block: BlockHashOrNumber, - hash: B256, data: Bytes, ) { let gas_price = 1; let to = Address::random(); - let gas_a = oracle_a - .estimate_da_gas(hash, to, data.clone(), block, gas_price) + let (gas_a, _, _) = oracle_a + .estimate_da_gas(data.clone(), to, block, gas_price) .await .unwrap(); - let gas_b = oracle_b - .estimate_da_gas(hash, to, data, block, gas_price) + let (gas_b, _, _) = oracle_b + .estimate_da_gas(data, to, block, gas_price) .await .unwrap(); // Allow for some variance with oracle b being within 0.1% smaller than oracle a - println!("gas_a: {}", gas_a); - println!("gas_b: {}", gas_b); let ratio = gas_b as f64 / gas_a as f64; assert!((0.999..=1.000).contains(&ratio)); } @@ -213,7 +219,6 @@ mod tests { async fn compare_arb_and_cached_on_data( provider: impl AlloyProvider + Clone, block: BlockHashOrNumber, - hash: B256, data: Bytes, ) { let oracle_addr = address!("00000000000000000000000000000000000000C8"); @@ -221,7 +226,7 @@ mod tests { let contract_oracle = ArbitrumNitroDAGasOracle::new(oracle_addr, provider.clone()); let cached_oracle = CachedNitroDAGasOracle::new(oracle_addr, provider); - compare_oracles_on_data(&contract_oracle, &cached_oracle, block, hash, data).await; + compare_oracles_on_data(&contract_oracle, &cached_oracle, block, data).await; } fn opt_provider() -> impl AlloyProvider + Clone { @@ -244,7 +249,7 @@ mod tests { .boxed() } - fn test_uo_data_1() -> (Bytes, B256) { + fn test_uo_data_1() -> Bytes { let puo = PackedUserOperation { sender: address!("f497A8026717FbbA3944c3dd2533c0716b7685e2"), nonce: uint!(0x23_U256), @@ -256,13 +261,10 @@ mod tests { paymasterAndData: Bytes::default(), signature: bytes!("0b83faeeac250d4c4a2459c1d6e1f8427f96af246d7fb3027b10bb05d934912f23a9491c16ab97ab32ac88179f279e871387c23547aa2e27b83fc358058e71fa1c"), }; - ( - puo.abi_encode().into(), - b256!("0000000000000000000000000000000000000000000000000000000000000001"), - ) + puo.abi_encode().into() } - fn test_uo_data_2() -> (Bytes, B256) { + fn test_uo_data_2() -> Bytes { let puo = PackedUserOperation { sender: address!("f497A8026717FbbA3944c3dd2533c0716b7685e2"), nonce: uint!(0x24_U256), // changed this @@ -274,10 +276,7 @@ mod tests { paymasterAndData: Bytes::default(), signature: bytes!("00"), // removed this, should result in different costs }; - ( - puo.abi_encode().into(), - b256!("0000000000000000000000000000000000000000000000000000000000000002"), - ) + puo.abi_encode().into() } fn get_api_key() -> String { diff --git a/crates/provider/src/alloy/da/optimism.rs b/crates/provider/src/alloy/da/optimism.rs index 1be6ac53f..adce5de1d 100644 --- a/crates/provider/src/alloy/da/optimism.rs +++ b/crates/provider/src/alloy/da/optimism.rs @@ -11,11 +11,12 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use alloy_primitives::{Address, Bytes, B256}; +use alloy_primitives::{Address, Bytes}; use alloy_provider::Provider as AlloyProvider; use alloy_sol_types::sol; use alloy_transport::Transport; use anyhow::Context; +use rundler_types::da::{DAGasBlockData, DAGasUOData}; use GasPriceOracle::GasPriceOracleInstance; use super::DAGasOracle; @@ -59,12 +60,11 @@ where { async fn estimate_da_gas( &self, - _hash: B256, - _to: Address, data: Bytes, + _to: Address, block: BlockHashOrNumber, gas_price: u128, - ) -> ProviderResult { + ) -> ProviderResult<(u128, DAGasUOData, DAGasBlockData)> { let l1_fee: u128 = self .oracle .getL1Fee(data) @@ -75,6 +75,32 @@ where .try_into() .context("failed to convert DA fee to u128")?; - Ok(l1_fee.checked_div(gas_price).unwrap_or(u128::MAX)) + Ok(( + l1_fee.checked_div(gas_price).unwrap_or(u128::MAX), + DAGasUOData::Empty, + DAGasBlockData::Empty, + )) + } + + async fn block_data(&self, _block: BlockHashOrNumber) -> ProviderResult { + Ok(DAGasBlockData::Empty) + } + + async fn uo_data( + &self, + _uo_data: Bytes, + _to: Address, + _block: BlockHashOrNumber, + ) -> ProviderResult { + Ok(DAGasUOData::Empty) + } + + fn calc_da_gas_sync( + &self, + _uo_data: &DAGasUOData, + _block_data: &DAGasBlockData, + _gas_price: u128, + ) -> u128 { + panic!("OptimismBedrockDAGasOracle does not support calc_da_gas_sync") } } diff --git a/crates/provider/src/alloy/entry_point/v0_6.rs b/crates/provider/src/alloy/entry_point/v0_6.rs index 0e32e0432..ccf95b6b6 100644 --- a/crates/provider/src/alloy/entry_point/v0_6.rs +++ b/crates/provider/src/alloy/entry_point/v0_6.rs @@ -31,15 +31,16 @@ use rundler_contracts::v0_6::{ UserOperation as ContractUserOperation, UserOpsPerAggregator as UserOpsPerAggregatorV0_6, }; use rundler_types::{ - chain::ChainSpec, v0_6::UserOperation, GasFees, UserOperation as _, UserOpsPerAggregator, - ValidationOutput, ValidationRevert, + chain::ChainSpec, + da::{DAGasBlockData, DAGasUOData}, + v0_6::UserOperation, + GasFees, UserOperation as _, UserOpsPerAggregator, ValidationOutput, ValidationRevert, }; use crate::{ - alloy::da::{self, DAGasOracle}, - AggregatorOut, AggregatorSimOut, BlockHashOrNumber, BundleHandler, DAGasProvider, DepositInfo, - EntryPoint, EntryPointProvider as EntryPointProviderTrait, EvmCall, ExecutionResult, - HandleOpsOut, ProviderResult, SignatureAggregator, SimulationProvider, + alloy::da, AggregatorOut, AggregatorSimOut, BlockHashOrNumber, BundleHandler, DAGasOracle, + DAGasProvider, DepositInfo, EntryPoint, EntryPointProvider as EntryPointProviderTrait, EvmCall, + ExecutionResult, HandleOpsOut, ProviderResult, SignatureAggregator, SimulationProvider, }; /// Entry point provider for v0.6 @@ -317,8 +318,7 @@ where user_op: UserOperation, block: BlockHashOrNumber, gas_price: u128, - ) -> ProviderResult { - let hash = user_op.hash(*self.i_entry_point.address(), self.chain_spec.id); + ) -> ProviderResult<(u128, DAGasUOData, DAGasBlockData)> { let data = self .i_entry_point .handleOps(vec![user_op.into()], Address::random()) @@ -331,15 +331,45 @@ where super::max_bundle_transaction_data(*self.i_entry_point.address(), data, gas_price); self.da_gas_oracle - .estimate_da_gas( - hash, - *self.i_entry_point.address(), - bundle_data, - block, - gas_price, - ) + .estimate_da_gas(bundle_data, *self.i_entry_point.address(), block, gas_price) .await } + + async fn block_data(&self, block: BlockHashOrNumber) -> ProviderResult { + self.da_gas_oracle.block_data(block).await + } + + async fn uo_data( + &self, + uo: UserOperation, + block: BlockHashOrNumber, + ) -> ProviderResult { + let gas_price = uo.max_fee_per_gas; + let data = self + .i_entry_point + .handleOps(vec![uo.into()], Address::random()) + .into_transaction_request() + .input + .into_input() + .unwrap(); + + let bundle_data = + super::max_bundle_transaction_data(*self.i_entry_point.address(), data, gas_price); + + self.da_gas_oracle + .uo_data(bundle_data, *self.i_entry_point.address(), block) + .await + } + + fn calc_da_gas_sync( + &self, + uo_data: &DAGasUOData, + block_data: &DAGasBlockData, + gas_price: u128, + ) -> u128 { + self.da_gas_oracle + .calc_da_gas_sync(uo_data, block_data, gas_price) + } } #[async_trait::async_trait] diff --git a/crates/provider/src/alloy/entry_point/v0_7.rs b/crates/provider/src/alloy/entry_point/v0_7.rs index 349461201..6bd450d15 100644 --- a/crates/provider/src/alloy/entry_point/v0_7.rs +++ b/crates/provider/src/alloy/entry_point/v0_7.rs @@ -38,15 +38,17 @@ use rundler_contracts::v0_7::{ ENTRY_POINT_SIMULATIONS_V0_7_DEPLOYED_BYTECODE, }; use rundler_types::{ - chain::ChainSpec, v0_7::UserOperation, GasFees, UserOperation as _, UserOpsPerAggregator, - ValidationOutput, ValidationRevert, + chain::ChainSpec, + da::{DAGasBlockData, DAGasUOData}, + v0_7::UserOperation, + GasFees, UserOperation as _, UserOpsPerAggregator, ValidationOutput, ValidationRevert, }; use crate::{ - alloy::da::{self, DAGasOracle}, - AggregatorOut, AggregatorSimOut, BlockHashOrNumber, BundleHandler, DAGasProvider, DepositInfo, - EntryPoint, EntryPointProvider as EntryPointProviderTrait, EvmCall, ExecutionResult, - HandleOpsOut, ProviderResult, SignatureAggregator, SimulationProvider, + alloy::da::{self}, + AggregatorOut, AggregatorSimOut, BlockHashOrNumber, BundleHandler, DAGasOracle, DAGasProvider, + DepositInfo, EntryPoint, EntryPointProvider as EntryPointProviderTrait, EvmCall, + ExecutionResult, HandleOpsOut, ProviderResult, SignatureAggregator, SimulationProvider, }; /// Entry point provider for v0.7 @@ -306,8 +308,7 @@ where user_op: UserOperation, block: BlockHashOrNumber, gas_price: u128, - ) -> ProviderResult { - let hash = user_op.hash(*self.i_entry_point.address(), self.chain_spec.id); + ) -> ProviderResult<(u128, DAGasUOData, DAGasBlockData)> { let data = self .i_entry_point .handleOps(vec![user_op.pack()], Address::random()) @@ -320,15 +321,45 @@ where super::max_bundle_transaction_data(*self.i_entry_point.address(), data, gas_price); self.da_gas_oracle - .estimate_da_gas( - hash, - *self.i_entry_point.address(), - bundle_data, - block, - gas_price, - ) + .estimate_da_gas(bundle_data, *self.i_entry_point.address(), block, gas_price) .await } + + async fn block_data(&self, block: BlockHashOrNumber) -> ProviderResult { + self.da_gas_oracle.block_data(block).await + } + + async fn uo_data( + &self, + uo: UserOperation, + block: BlockHashOrNumber, + ) -> ProviderResult { + let gas_price = uo.max_fee_per_gas; + let data = self + .i_entry_point + .handleOps(vec![uo.pack()], Address::random()) + .into_transaction_request() + .input + .into_input() + .unwrap(); + + let bundle_data = + super::max_bundle_transaction_data(*self.i_entry_point.address(), data, gas_price); + + self.da_gas_oracle + .uo_data(bundle_data, *self.i_entry_point.address(), block) + .await + } + + fn calc_da_gas_sync( + &self, + uo_data: &DAGasUOData, + block_data: &DAGasBlockData, + gas_price: u128, + ) -> u128 { + self.da_gas_oracle + .calc_da_gas_sync(uo_data, block_data, gas_price) + } } #[async_trait::async_trait] diff --git a/crates/provider/src/traits/da.rs b/crates/provider/src/traits/da.rs new file mode 100644 index 000000000..75de2a7fc --- /dev/null +++ b/crates/provider/src/traits/da.rs @@ -0,0 +1,66 @@ +// This file is part of Rundler. +// +// Rundler is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version. +// +// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with Rundler. +// If not, see https://www.gnu.org/licenses/. + +use alloy_primitives::{Address, Bytes}; +use rundler_types::da::{DAGasBlockData, DAGasUOData}; + +use crate::{BlockHashOrNumber, ProviderResult}; + +// Trait for a DA gas oracle +#[async_trait::async_trait] +#[auto_impl::auto_impl(&, &mut, Rc, Arc, Box)] +pub(crate) trait DAGasOracle: Send + Sync { + /// Estimate the DA gas for a given user operation's bytes + /// + /// Returns the estimated gas, as well as both the UO DA data and the block DA data. + /// These fields can be safely ignored if the caller is only interested in the gas estimate and + /// is not implementing any caching logic. + async fn estimate_da_gas( + &self, + uo_bytes: Bytes, + to: Address, + block: BlockHashOrNumber, + gas_price: u128, + ) -> ProviderResult<(u128, DAGasUOData, DAGasBlockData)>; + + /// Retrieve the DA gas data for a given block. This value can change block to block and + /// thus must be retrieved fresh from the DA for every block. + async fn block_data(&self, block: BlockHashOrNumber) -> ProviderResult; + + /// Retrieve the DA gas data for a given user operation's bytes + /// + /// This should not change block to block, but may change after underlying hard forks, + /// thus a block number is required. + /// + /// It is safe to calculate this once and re-use if the same UO is used for multiple calls within + /// a small time period (no hard forks impacting DA calculations) + async fn uo_data( + &self, + uo_data: Bytes, + to: Address, + block: BlockHashOrNumber, + ) -> ProviderResult; + + /// Synchronously calculate the DA gas for a given user operation data and block data. + /// These values must have been previously retrieved from a DA oracle of the same implementation + /// else this function will PANIC. + /// + /// This function is intended to allow synchronous DA gas calculation from a cached UO data and a + /// recently retrieved block data. + fn calc_da_gas_sync( + &self, + uo_data: &DAGasUOData, + block_data: &DAGasBlockData, + gas_price: u128, + ) -> u128; +} diff --git a/crates/provider/src/traits/entry_point.rs b/crates/provider/src/traits/entry_point.rs index 0538359c0..5c683739e 100644 --- a/crates/provider/src/traits/entry_point.rs +++ b/crates/provider/src/traits/entry_point.rs @@ -13,6 +13,7 @@ use alloy_primitives::{Address, Bytes, U256}; use rundler_types::{ + da::{DAGasBlockData, DAGasUOData}, GasFees, Timestamp, UserOperation, UserOpsPerAggregator, ValidationOutput, ValidationRevert, }; @@ -164,13 +165,43 @@ pub trait DAGasProvider: Send + Sync { /// Calculate the DA portion of the gas for a user operation /// - /// Returns zero for operations that do not require DA gas + /// Returns the da gas plus any calculated DAGasUOData and DAGasBlockData. + /// These values can be cached and re-used if the same UO is used for multiple calls within + /// a small time period (no hard forks impacting DA calculations). + /// + /// Returns zero for operations that do not require DA gas. async fn calc_da_gas( &self, - op: Self::UO, + uo: Self::UO, block: BlockHashOrNumber, gas_price: u128, - ) -> ProviderResult; + ) -> ProviderResult<(u128, DAGasUOData, DAGasBlockData)>; + + /// Retrieve the DA gas data for a given block. This value can change block to block and + /// thus must be retrieved fresh from the DA for every block. + async fn block_data(&self, block: BlockHashOrNumber) -> ProviderResult; + + /// Retrieve the DA gas data for a given user operation's bytes + /// + /// This should not change block to block, but may change after underlying hard forks, + /// thus a block number is required. + /// + /// It is safe to calculate this once and re-use if the same UO is used for multiple calls within + /// a small time period (no hard forks impacting DA calculations) + async fn uo_data(&self, uo: Self::UO, block: BlockHashOrNumber) -> ProviderResult; + + /// Synchronously calculate the DA gas for a given user operation data and block data. + /// These values must have been previously retrieved from a DA provider of the same implementation + /// else this function will PANIC. + /// + /// This function is intended to allow synchronous DA gas calculation from a cached UO data and a + /// recently retrieved block data. + fn calc_da_gas_sync( + &self, + uo_data: &DAGasUOData, + block_data: &DAGasBlockData, + gas_price: u128, + ) -> u128; } /// Trait for simulating user operations on an entry point contract diff --git a/crates/provider/src/traits/mod.rs b/crates/provider/src/traits/mod.rs index ff12b2acd..2f38d1876 100644 --- a/crates/provider/src/traits/mod.rs +++ b/crates/provider/src/traits/mod.rs @@ -13,6 +13,9 @@ //! Traits for the provider module. +mod da; +pub(crate) use da::DAGasOracle; + mod error; pub use error::*; diff --git a/crates/provider/src/traits/test_utils.rs b/crates/provider/src/traits/test_utils.rs index 17dc55774..442953cf5 100644 --- a/crates/provider/src/traits/test_utils.rs +++ b/crates/provider/src/traits/test_utils.rs @@ -22,6 +22,7 @@ use alloy_rpc_types_trace::geth::{ }; use rundler_contracts::utils::GetGasUsed::GasUsedResult; use rundler_types::{ + da::{DAGasBlockData, DAGasUOData}, v0_6, v0_7, GasFees, UserOpsPerAggregator, ValidationOutput, ValidationRevert, }; @@ -176,7 +177,13 @@ mockall::mock! { op: v0_6::UserOperation, block: BlockHashOrNumber, gas_price: u128, - ) -> ProviderResult; + ) -> ProviderResult<(u128, DAGasUOData, DAGasBlockData)>; + + async fn block_data(&self, block: BlockHashOrNumber) -> ProviderResult; + + async fn uo_data(&self, uo: v0_6::UserOperation, block: BlockHashOrNumber) -> ProviderResult; + + fn calc_da_gas_sync(&self, uo_data: &DAGasUOData, block_data: &DAGasBlockData, gas_price: u128) -> u128; } #[async_trait::async_trait] @@ -264,7 +271,13 @@ mockall::mock! { op: v0_7::UserOperation, block: BlockHashOrNumber, gas_price: u128, - ) -> ProviderResult; + ) -> ProviderResult<(u128, DAGasUOData, DAGasBlockData)>; + + async fn block_data(&self, block: BlockHashOrNumber) -> ProviderResult; + + async fn uo_data(&self, uo: v0_7::UserOperation, block: BlockHashOrNumber) -> ProviderResult; + + fn calc_da_gas_sync(&self, uo_data: &DAGasUOData, block_data: &DAGasBlockData, gas_price: u128) -> u128; } #[async_trait::async_trait] diff --git a/crates/sim/src/estimation/v0_6.rs b/crates/sim/src/estimation/v0_6.rs index 8827dbd72..f08940f53 100644 --- a/crates/sim/src/estimation/v0_6.rs +++ b/crates/sim/src/estimation/v0_6.rs @@ -436,7 +436,7 @@ mod tests { EvmCall, ExecutionResult, GasUsedResult, MockEntryPointV0_6, MockEvmProvider, }; use rundler_types::{ - chain::DAGasOracleContractType, + da::DAGasOracleContractType, v0_6::{UserOperation, UserOperationOptionalGas, UserOperationRequiredFields}, GasFees, UserOperation as UserOperationTrait, ValidationRevert, }; @@ -631,7 +631,7 @@ mod tests { let (mut entry, provider) = create_base_config(); entry .expect_calc_da_gas() - .returning(|_a, _b, _c| Ok(TEST_FEE)); + .returning(|_a, _b, _c| Ok((TEST_FEE, Default::default(), Default::default()))); let settings = Settings { max_verification_gas: 10000000000, @@ -707,7 +707,7 @@ mod tests { entry .expect_calc_da_gas() - .returning(|_a, _b, _c| Ok(TEST_FEE)); + .returning(|_a, _b, _c| Ok((TEST_FEE, Default::default(), Default::default()))); let settings = Settings { max_verification_gas: 10000000000, diff --git a/crates/sim/src/gas/gas.rs b/crates/sim/src/gas/gas.rs index dbffd795b..512f29687 100644 --- a/crates/sim/src/gas/gas.rs +++ b/crates/sim/src/gas/gas.rs @@ -51,6 +51,7 @@ pub async fn estimate_pre_verification_gas< entry_point .calc_da_gas(random_op.clone(), block, gas_price) .await? + .0 } else { 0 }; @@ -85,6 +86,7 @@ pub async fn calc_required_pre_verification_gas< entry_point .calc_da_gas(op.clone(), block, gas_price) .await? + .0 } else { 0 }; diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index ab085a541..c86172216 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -17,8 +17,8 @@ alloy-sol-types.workspace = true anyhow.workspace = true async-trait.workspace = true chrono = "0.4.38" -constcat = "0.5.0" const-hex.workspace = true +constcat = "0.5.0" futures-util.workspace = true metrics.workspace = true metrics-derive.workspace = true diff --git a/crates/types/src/chain.rs b/crates/types/src/chain.rs index 3671cd5a7..080f2e92a 100644 --- a/crates/types/src/chain.rs +++ b/crates/types/src/chain.rs @@ -18,6 +18,8 @@ use std::str::FromStr; use alloy_primitives::Address; use serde::{Deserialize, Serialize}; +use crate::da::DAGasOracleContractType; + const ENTRY_POINT_ADDRESS_V6_0: &str = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; const ENTRY_POINT_ADDRESS_V7_0: &str = "0x0000000071727De22E5E9d8BAf0edAc6f37da032"; @@ -118,23 +120,6 @@ pub struct ChainSpec { pub chain_history_size: u64, } -/// Type of gas oracle contract for pricing calldata in preVerificationGas -#[derive(Clone, Copy, Debug, Deserialize, Default, Serialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum DAGasOracleContractType { - /// No gas oracle contract - #[default] - None, - /// Arbitrum Nitro type gas oracle contract - ArbitrumNitro, - /// Optimism Bedrock type gas oracle contract - OptimismBedrock, - /// Local Bedrock type gas oracle contract - LocalBedrock, - /// Cached Nitro type gas oracle contract - CachedNitro, -} - /// Type of oracle for estimating priority fees #[derive(Clone, Debug, Deserialize, Default, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] diff --git a/crates/types/src/da.rs b/crates/types/src/da.rs new file mode 100644 index 000000000..f09fe7532 --- /dev/null +++ b/crates/types/src/da.rs @@ -0,0 +1,96 @@ +// This file is part of Rundler. +// +// Rundler is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version. +// +// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with Rundler. +// If not, see https://www.gnu.org/licenses/. + +//! Types associated with DA gas calculations + +use serde::{Deserialize, Serialize}; + +/// Type of gas oracle contract for pricing calldata in preVerificationGas +#[derive(Clone, Copy, Debug, Deserialize, Default, Serialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum DAGasOracleContractType { + /// No gas oracle contract + #[default] + None, + /// Arbitrum Nitro type gas oracle contract + ArbitrumNitro, + /// Optimism Bedrock type gas oracle contract + OptimismBedrock, + /// Local Bedrock type gas oracle contract + LocalBedrock, + /// Cached Nitro type gas oracle contract + CachedNitro, +} + +/// Data associated with a user operation for Nitro DA gas calculations +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NitroDAGasUOData { + /// The calculated user operation units as they apply to DA gas. Only have meaning when used + /// with the NitroDAGasBlockData that was used to calculate them and combined with a NitroDAGasBlockData. + pub uo_units: u128, +} + +/// Data associated with a user operation for Bedrock DA gas calculations +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BedrockDAGasUOData { + /// The calculated user operation units as they apply to DA gas. Only have meaning when used + /// with the BedrockDAGasBlockData that was used to calculate them and combined with a + /// BedrockDAGasBlockData. + pub uo_units: u64, +} + +/// Data associated with a user operation for DA gas calculations +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub enum DAGasUOData { + /// Empty, no data + #[default] + Empty, + /// Nitro DA + Nitro(NitroDAGasUOData), + /// Bedrock DA + Bedrock(BedrockDAGasUOData), +} + +/// Data associated with a block for DA gas calculations +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub enum DAGasBlockData { + /// Empty, no data + #[default] + Empty, + /// Nitro DA + Nitro(NitroDAGasBlockData), + /// Bedrock DA + Bedrock(BedrockDAGasBlockData), +} + +/// Data associated with a block for Nitro DA gas calculations +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NitroDAGasBlockData { + /// L1 base fee retrieved from the nitro node interface precompile. + pub l1_base_fee: u128, + /// Base fee retrieved from the nitro node interface precompile. + pub base_fee: u128, +} + +/// Data associated with a block for Bedrock DA gas calculations +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BedrockDAGasBlockData { + /// Base fee scalar retrieved from the bedrock gas oracle. + pub base_fee_scalar: u64, + /// L1 base fee retrieved from the bedrock gas oracle. + pub l1_base_fee: u64, + /// Blob base fee scalar retrieved from the bedrock gas oracle. + pub blob_base_fee_scalar: u64, + /// Blob base fee retrieved from the bedrock gas oracle. + pub blob_base_fee: u64, +} diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 840844e01..dccb061b0 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -24,6 +24,8 @@ pub mod builder; pub mod chain; +pub mod da; + mod entity; pub use entity::{Entity, EntityInfo, EntityInfos, EntityType, EntityUpdate, EntityUpdateType};