diff --git a/Cargo.lock b/Cargo.lock index 786631554..5f7b1a779 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7929,6 +7929,7 @@ dependencies = [ "alloy-primitives", "jsonrpsee-types", "op-alloy-network", + "op-alloy-rpc-types", "parking_lot 0.12.3", "reqwest", "reth-chainspec", @@ -7937,6 +7938,7 @@ dependencies = [ "reth-network-api", "reth-node-api", "reth-node-builder", + "reth-optimism-chainspec", "reth-primitives", "reth-provider", "reth-rpc", diff --git a/crates/ethereum-forks/src/hardforks/optimism.rs b/crates/ethereum-forks/src/hardforks/optimism.rs index d0dcd431d..5fb68708c 100644 --- a/crates/ethereum-forks/src/hardforks/optimism.rs +++ b/crates/ethereum-forks/src/hardforks/optimism.rs @@ -8,11 +8,15 @@ pub trait OptimismHardforks: EthereumHardforks { self.fork(OptimismHardfork::Bedrock).active_at_block(block_number) } - /// Convenience method to check if [`Ecotone`](OptimismHardfork::Ecotone) is active at a given - /// timestamp. + /// Returns `true` if [`Ecotone`](OptimismHardfork::Ecotone) is active at given block timestamp. fn is_ecotone_active_at_timestamp(&self, timestamp: u64) -> bool { self.fork(OptimismHardfork::Ecotone).active_at_timestamp(timestamp) } + + /// Returns `true` if [`Ecotone`](OptimismHardfork::Ecotone) is active at given block timestamp. + fn is_fjord_active_at_timestamp(&self, timestamp: u64) -> bool { + self.fork(OptimismHardfork::Ecotone).active_at_timestamp(timestamp) + } } impl OptimismHardforks for ChainHardforks {} diff --git a/crates/optimism/rpc/Cargo.toml b/crates/optimism/rpc/Cargo.toml index 8b1f64330..f82483590 100644 --- a/crates/optimism/rpc/Cargo.toml +++ b/crates/optimism/rpc/Cargo.toml @@ -33,6 +33,7 @@ reth-chainspec.workspace = true alloy-primitives.workspace = true op-alloy-network.workspace = true revm.workspace = true +op-alloy-rpc-types.workspace = true # async parking_lot.workspace = true @@ -47,6 +48,9 @@ serde_json.workspace = true thiserror.workspace = true tracing.workspace = true +[dev-dependencies] +reth-optimism-chainspec.workspace = true + [features] optimism = [ "reth-evm-optimism/optimism", diff --git a/crates/optimism/rpc/src/error.rs b/crates/optimism/rpc/src/error.rs index 29a348ab7..42a4d68e4 100644 --- a/crates/optimism/rpc/src/error.rs +++ b/crates/optimism/rpc/src/error.rs @@ -1,5 +1,6 @@ //! RPC errors specific to OP. +use reth_evm_optimism::OptimismBlockExecutionError; use reth_primitives::revm_primitives::{InvalidTransaction, OptimismInvalidTransaction}; use reth_rpc_eth_api::AsEthApiError; use reth_rpc_eth_types::EthApiError; @@ -12,6 +13,9 @@ pub enum OpEthApiError { /// L1 ethereum error. #[error(transparent)] Eth(#[from] EthApiError), + /// EVM error originating from invalid optimism data. + #[error(transparent)] + Evm(#[from] OptimismBlockExecutionError), /// Thrown when calculating L1 gas fee. #[error("failed to calculate l1 gas fee")] L1BlockFeeError, @@ -35,11 +39,10 @@ impl AsEthApiError for OpEthApiError { impl From for jsonrpsee_types::error::ErrorObject<'static> { fn from(err: OpEthApiError) -> Self { match err { - OpEthApiError::Eth(err) => err.into(), - OpEthApiError::L1BlockFeeError | OpEthApiError::L1BlockGasError => { - internal_rpc_err(err.to_string()) - } - OpEthApiError::InvalidTransaction(err) => err.into(), + OpEthApiError::Eth(_) | OpEthApiError::InvalidTransaction(_) => err.into(), + OpEthApiError::Evm(_) | + OpEthApiError::L1BlockFeeError | + OpEthApiError::L1BlockGasError => internal_rpc_err(err.to_string()), } } } diff --git a/crates/optimism/rpc/src/eth/block.rs b/crates/optimism/rpc/src/eth/block.rs index c7a7b45ab..6f60297de 100644 --- a/crates/optimism/rpc/src/eth/block.rs +++ b/crates/optimism/rpc/src/eth/block.rs @@ -1,6 +1,7 @@ -//! Loads and formats OP block RPC response. +//! Loads and formats OP block RPC response. -use reth_node_api::FullNodeComponents; +use reth_chainspec::ChainSpec; +use reth_node_api::{FullNodeComponents, NodeTypes}; use reth_primitives::TransactionMeta; use reth_provider::{BlockReaderIdExt, HeaderProvider}; use reth_rpc_eth_api::{ @@ -13,13 +14,12 @@ use reth_rpc_eth_api::{ use reth_rpc_eth_types::{EthStateCache, ReceiptBuilder}; use reth_rpc_types::{AnyTransactionReceipt, BlockId}; -use crate::{op_receipt_fields, OpEthApi, OpEthApiError}; +use crate::{OpEthApi, OpEthApiError}; impl EthBlocks for OpEthApi where - Self: LoadBlock + EthApiSpec + LoadTransaction, - Self::Error: From, - N: FullNodeComponents, + Self: EthApiSpec + LoadBlock + LoadTransaction, + N: FullNodeComponents>, { #[inline] fn provider(&self) -> impl HeaderProvider { @@ -41,14 +41,15 @@ where let timestamp = block.timestamp; let block = block.unseal(); - let l1_block_info = reth_evm_optimism::extract_l1_info(&block).ok(); + let l1_block_info = + reth_evm_optimism::extract_l1_info(&block).map_err(OpEthApiError::from)?; let receipts = block .body .into_iter() .zip(receipts.iter()) .enumerate() - .map(|(idx, (ref tx, receipt))| { + .map(|(idx, (ref tx, receipt))| -> Result<_, _> { let meta = TransactionMeta { tx_hash: tx.hash, index: idx as u64, @@ -59,14 +60,13 @@ where timestamp, }; - let optimism_tx_meta = - self.build_op_tx_meta(tx, l1_block_info.clone(), timestamp)?; + let op_tx_meta = + self.build_op_receipt_meta(tx, l1_block_info.clone(), receipt)?; - ReceiptBuilder::new(tx, meta, receipt, &receipts) - .map(|builder| { - op_receipt_fields(builder, tx, receipt, optimism_tx_meta).build() - }) - .map_err(Self::Error::from_eth_err) + Ok(ReceiptBuilder::new(tx, meta, receipt, &receipts) + .map_err(Self::Error::from_eth_err)? + .add_other_fields(op_tx_meta.into()) + .build()) }) .collect::, Self::Error>>(); return receipts.map(Some) diff --git a/crates/optimism/rpc/src/eth/mod.rs b/crates/optimism/rpc/src/eth/mod.rs index 56056c747..13078e456 100644 --- a/crates/optimism/rpc/src/eth/mod.rs +++ b/crates/optimism/rpc/src/eth/mod.rs @@ -10,7 +10,6 @@ pub mod rpc; use std::{fmt, sync::Arc}; -use crate::eth::rpc::SequencerClient; use alloy_primitives::U256; use op_alloy_network::AnyNetwork; use reth_chainspec::ChainSpec; @@ -19,8 +18,8 @@ use reth_network_api::NetworkInfo; use reth_node_api::{BuilderProvider, FullNodeComponents, FullNodeTypes, NodeTypes}; use reth_node_builder::EthApiBuilderCtx; use reth_provider::{ - BlockIdReader, BlockNumReader, BlockReaderIdExt, ChainSpecProvider, HeaderProvider, - StageCheckpointReader, StateProviderFactory, + BlockIdReader, BlockNumReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, + HeaderProvider, StageCheckpointReader, StateProviderFactory, }; use reth_rpc::eth::{core::EthApiInner, DevSigner}; use reth_rpc_eth_api::{ @@ -37,7 +36,7 @@ use reth_tasks::{ }; use reth_transaction_pool::TransactionPool; -use crate::OpEthApiError; +use crate::{eth::rpc::SequencerClient, OpEthApiError}; /// Adapter for [`EthApiInner`], which holds all the data required to serve core `eth_` API. pub type EthApiNodeBackend = EthApiInner< @@ -63,7 +62,12 @@ pub struct OpEthApi { sequencer_client: Arc>>, } -impl OpEthApi { +impl OpEthApi +where + N: FullNodeComponents< + Provider: BlockReaderIdExt + ChainSpecProvider + CanonStateSubscriptions + Clone + 'static, + >, +{ /// Creates a new instance for given context. #[allow(clippy::type_complexity)] pub fn with_spawner(ctx: &EthApiBuilderCtx) -> Self { diff --git a/crates/optimism/rpc/src/eth/pending_block.rs b/crates/optimism/rpc/src/eth/pending_block.rs index 54f29059d..19f17cff3 100644 --- a/crates/optimism/rpc/src/eth/pending_block.rs +++ b/crates/optimism/rpc/src/eth/pending_block.rs @@ -1,4 +1,4 @@ -//! Loads OP pending block for a RPC response. +//! Loads OP pending block for a RPC response. use reth_chainspec::ChainSpec; use reth_evm::ConfigureEvm; diff --git a/crates/optimism/rpc/src/eth/receipt.rs b/crates/optimism/rpc/src/eth/receipt.rs index 881b91c81..c2c689cc2 100644 --- a/crates/optimism/rpc/src/eth/receipt.rs +++ b/crates/optimism/rpc/src/eth/receipt.rs @@ -1,21 +1,24 @@ -//! Loads and formats OP receipt RPC response. +//! Loads and formats OP receipt RPC response. -use reth_node_api::FullNodeComponents; +use op_alloy_rpc_types::{receipt::L1BlockInfo, OptimismTransactionReceiptFields}; +use reth_chainspec::{ChainSpec, OptimismHardforks}; +use reth_evm_optimism::RethL1BlockInfo; +use reth_node_api::{FullNodeComponents, NodeTypes}; use reth_primitives::{Receipt, TransactionMeta, TransactionSigned}; +use reth_provider::ChainSpecProvider; use reth_rpc_eth_api::{ helpers::{EthApiSpec, LoadReceipt, LoadTransaction}, FromEthApiError, }; use reth_rpc_eth_types::{EthApiError, EthStateCache, ReceiptBuilder}; -use reth_rpc_types::{optimism::OptimismTransactionReceiptFields, AnyTransactionReceipt}; +use reth_rpc_types::AnyTransactionReceipt; -use crate::{OpEthApi, OpEthApiError, OptimismTxMeta}; +use crate::{OpEthApi, OpEthApiError}; impl LoadReceipt for OpEthApi where - Self: EthApiSpec + LoadTransaction, - Self::Error: From, - N: FullNodeComponents, + Self: EthApiSpec + LoadTransaction, + N: FullNodeComponents>, { #[inline] fn cache(&self) -> &EthStateCache { @@ -35,57 +38,263 @@ where .ok_or(Self::Error::from_eth_err(EthApiError::UnknownBlockNumber))?; let block = block.unseal(); - let l1_block_info = reth_evm_optimism::extract_l1_info(&block).ok(); - let optimism_tx_meta = self.build_op_tx_meta(&tx, l1_block_info, block.timestamp)?; + let l1_block_info = + reth_evm_optimism::extract_l1_info(&block).map_err(OpEthApiError::from)?; - let resp_builder = ReceiptBuilder::new(&tx, meta, &receipt, &receipts) - .map_err(Self::Error::from_eth_err)?; - let resp_builder = op_receipt_fields(resp_builder, &tx, &receipt, optimism_tx_meta); + let op_receipt_meta = self + .build_op_receipt_meta(&tx, l1_block_info, &receipt) + .map_err(OpEthApiError::from)?; - Ok(resp_builder.build()) + let receipt_resp = ReceiptBuilder::new(&tx, meta, &receipt, &receipts) + .map_err(Self::Error::from_eth_err)? + .add_other_fields(op_receipt_meta.into()) + .build(); + + Ok(receipt_resp) } } -/// Applies OP specific fields to a receipt builder. -pub fn op_receipt_fields( - resp_builder: ReceiptBuilder, - tx: &TransactionSigned, - receipt: &Receipt, - optimism_tx_meta: OptimismTxMeta, -) -> ReceiptBuilder { - let mut op_fields = OptimismTransactionReceiptFields::default(); +impl OpEthApi +where + N: FullNodeComponents>, +{ + /// Builds a receipt w.r.t. chain spec. + pub fn build_op_receipt_meta( + &self, + tx: &TransactionSigned, + l1_block_info: revm::L1BlockInfo, + receipt: &Receipt, + ) -> Result { + Ok(OpReceiptFieldsBuilder::default() + .l1_block_info(&self.inner.provider().chain_spec(), tx, l1_block_info)? + .deposit_nonce(receipt.deposit_nonce) + .deposit_version(receipt.deposit_receipt_version) + .build()) + } +} - if tx.is_deposit() { - op_fields.deposit_nonce = receipt.deposit_nonce; - op_fields.deposit_receipt_version = receipt.deposit_receipt_version; - } else if let Some(l1_block_info) = optimism_tx_meta.l1_block_info { - // always present - op_fields.l1_block_info.l1_fee = optimism_tx_meta.l1_fee; - op_fields.l1_block_info.l1_gas_price = Some(l1_block_info.l1_base_fee.saturating_to()); - op_fields.l1_block_info.l1_gas_used = optimism_tx_meta.l1_data_gas.map(|dg| { - dg.saturating_add( - l1_block_info.l1_fee_overhead.unwrap_or_default().saturating_to::(), - ) - }); +/// L1 fee and data gas for a non-deposit transaction, or deposit nonce and receipt version for a +/// deposit transaction. +#[derive(Debug, Default, Clone)] +pub struct OpReceiptFieldsBuilder { + /// Block timestamp. + pub l1_block_timestamp: u64, + /// The L1 fee for transaction. + pub l1_fee: Option, + /// L1 gas used by transaction. + pub l1_data_gas: Option, + /// L1 fee scalar. + pub l1_fee_scalar: Option, + /* ---------------------------------------- Bedrock ---------------------------------------- */ + /// The base fee of the L1 origin block. + pub l1_base_fee: Option, + /* --------------------------------------- Regolith ---------------------------------------- */ + /// Deposit nonce, if this is a deposit transaction. + pub deposit_nonce: Option, + /* ---------------------------------------- Canyon ----------------------------------------- */ + /// Deposit receipt version, if this is a deposit transaction. + pub deposit_receipt_version: Option, + /* ---------------------------------------- Ecotone ---------------------------------------- */ + /// The current L1 fee scalar. + pub l1_base_fee_scalar: Option, + /// The current L1 blob base fee. + pub l1_blob_base_fee: Option, + /// The current L1 blob base fee scalar. + pub l1_blob_base_fee_scalar: Option, +} - // we know if we're __pre__ Ecotone by checking the l1 fee overhead value which is - // None if ecotone is active - if l1_block_info.l1_fee_overhead.is_some() { - // only pre Ecotone - op_fields.l1_block_info.l1_fee_scalar = - Some(f64::from(l1_block_info.l1_base_fee_scalar) / 1_000_000.0); - } else { - // base fee scalar is enabled post Ecotone - op_fields.l1_block_info.l1_base_fee_scalar = - Some(l1_block_info.l1_base_fee_scalar.saturating_to()); +impl OpReceiptFieldsBuilder { + /// Returns a new builder. + pub fn new(block_timestamp: u64) -> Self { + Self { l1_block_timestamp: block_timestamp, ..Default::default() } + } + + /// Applies [`L1BlockInfo`](revm::L1BlockInfo). + pub fn l1_block_info( + mut self, + chain_spec: &ChainSpec, + tx: &TransactionSigned, + l1_block_info: revm::L1BlockInfo, + ) -> Result { + let raw_tx = tx.envelope_encoded(); + let timestamp = self.l1_block_timestamp; + + self.l1_fee = Some( + l1_block_info + .l1_tx_data_fee(chain_spec, timestamp, &raw_tx, tx.is_deposit()) + .map_err(|_| OpEthApiError::L1BlockFeeError)? + .saturating_to(), + ); + + self.l1_data_gas = Some( + l1_block_info + .l1_data_gas(chain_spec, timestamp, &raw_tx) + .map_err(|_| OpEthApiError::L1BlockGasError)? + .saturating_add(l1_block_info.l1_fee_overhead.unwrap_or_default()) + .saturating_to(), + ); + + self.l1_fee_scalar = (!chain_spec.hardforks.is_ecotone_active_at_timestamp(timestamp)) + .then_some(f64::from(l1_block_info.l1_base_fee_scalar) / 1_000_000.0); + + self.l1_base_fee = Some(l1_block_info.l1_base_fee.saturating_to()); + self.l1_base_fee_scalar = Some(l1_block_info.l1_base_fee_scalar.saturating_to()); + self.l1_blob_base_fee = l1_block_info.l1_blob_base_fee.map(|fee| fee.saturating_to()); + self.l1_blob_base_fee_scalar = + l1_block_info.l1_blob_base_fee_scalar.map(|scalar| scalar.saturating_to()); + + Ok(self) + } + + /// Applies deposit transaction metadata: deposit nonce. + pub const fn deposit_nonce(mut self, nonce: Option) -> Self { + self.deposit_nonce = nonce; + self + } + + /// Applies deposit transaction metadata: deposit receipt version. + pub const fn deposit_version(mut self, version: Option) -> Self { + self.deposit_receipt_version = version; + self + } + + /// Builds the [`OptimismTransactionReceiptFields`] object. + pub const fn build(self) -> OptimismTransactionReceiptFields { + let Self { + l1_block_timestamp: _, // used to compute other fields + l1_fee, + l1_data_gas: l1_gas_used, + l1_fee_scalar, + l1_base_fee: l1_gas_price, + deposit_nonce, + deposit_receipt_version, + l1_base_fee_scalar, + l1_blob_base_fee, + l1_blob_base_fee_scalar, + } = self; + + OptimismTransactionReceiptFields { + l1_block_info: L1BlockInfo { + l1_gas_price, + l1_gas_used, + l1_fee, + l1_fee_scalar, + l1_base_fee_scalar, + l1_blob_base_fee, + l1_blob_base_fee_scalar, + }, + deposit_nonce, + deposit_receipt_version, } - - // 4844 post Ecotone - op_fields.l1_block_info.l1_blob_base_fee = - l1_block_info.l1_blob_base_fee.map(|v| v.saturating_to()); - op_fields.l1_block_info.l1_blob_base_fee_scalar = - l1_block_info.l1_blob_base_fee_scalar.map(|v| v.saturating_to()); } - - resp_builder.add_other_fields(op_fields.into()) +} + +#[cfg(test)] +mod test { + use alloy_primitives::hex; + use reth_optimism_chainspec::OP_MAINNET; + use reth_primitives::Block; + + use super::*; + + /// OP Mainnet transaction at index 0 in block 124665056. + /// + /// + const TX_SET_L1_BLOCK_OP_MAINNET_BLOCK_124665056: [u8; 251] = hex!("7ef8f8a0683079df94aa5b9cf86687d739a60a9b4f0835e520ec4d664e2e415dca17a6df94deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8a4440a5e200000146b000f79c500000000000000040000000066d052e700000000013ad8a3000000000000000000000000000000000000000000000000000000003ef1278700000000000000000000000000000000000000000000000000000000000000012fdf87b89884a61e74b322bbcf60386f543bfae7827725efaaf0ab1de2294a590000000000000000000000006887246668a3b87f54deb3b94ba47a6f63f32985"); + + /// OP Mainnet transaction at index 1 in block 124665056. + /// + /// + const TX_1_OP_MAINNET_BLOCK_124665056: [u8; 1176] = hex!("02f904940a8303fba78401d6d2798401db2b6d830493e0943e6f4f7866654c18f536170780344aa8772950b680b904246a761202000000000000000000000000087000a300de7200382b55d40045000000e5d60e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000022482ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c0000000000000000000000000000000000000000000000049b9ca9a6943400000000000000000000000000000000000000000000000000000000000000000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024b6b55f250000000000000000000000000000000000000000000000049b9ca9a694340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000415ec214a3950bea839a7e6fbb0ba1540ac2076acd50820e2d5ef83d0902cdffb24a47aff7de5190290769c4f0a9c6fabf63012986a0d590b1b571547a8c7050ea1b00000000000000000000000000000000000000000000000000000000000000c080a06db770e6e25a617fe9652f0958bd9bd6e49281a53036906386ed39ec48eadf63a07f47cf51a4a40b4494cf26efc686709a9b03939e20ee27e59682f5faa536667e"); + + /// Timestamp of OP mainnet block 124665056. + /// + /// + const BLOCK_124665056_TIMESTAMP: u64 = 1724928889; + + /// L1 block info for transaction at index 1 in block 124665056. + /// + /// + const TX_META_TX_1_OP_MAINNET_BLOCK_124665056: OptimismTransactionReceiptFields = + OptimismTransactionReceiptFields { + l1_block_info: L1BlockInfo { + l1_gas_price: Some(1055991687), // since bedrock l1 base fee + l1_gas_used: Some(4471), + l1_fee: Some(24681034813), + l1_fee_scalar: None, + l1_base_fee_scalar: Some(5227), + l1_blob_base_fee: Some(1), + l1_blob_base_fee_scalar: Some(1014213), + }, + deposit_nonce: None, + deposit_receipt_version: None, + }; + + #[test] + fn op_receipt_fields_from_block_and_tx() { + // rig + let tx_0 = TransactionSigned::decode_enveloped( + &mut TX_SET_L1_BLOCK_OP_MAINNET_BLOCK_124665056.as_slice(), + ) + .unwrap(); + + let tx_1 = + TransactionSigned::decode_enveloped(&mut TX_1_OP_MAINNET_BLOCK_124665056.as_slice()) + .unwrap(); + + let block = Block { body: [tx_0, tx_1.clone()].to_vec(), ..Default::default() }; + + let l1_block_info = + reth_evm_optimism::extract_l1_info(&block).expect("should extract l1 info"); + + // test + assert!(OP_MAINNET.hardforks.is_fjord_active_at_timestamp(BLOCK_124665056_TIMESTAMP)); + + let receipt_meta = OpReceiptFieldsBuilder::new(BLOCK_124665056_TIMESTAMP) + .l1_block_info(&OP_MAINNET, &tx_1, l1_block_info) + .expect("should parse revm l1 info") + .build(); + + let L1BlockInfo { + l1_gas_price, + l1_gas_used, + l1_fee, + l1_fee_scalar, + l1_base_fee_scalar, + l1_blob_base_fee, + l1_blob_base_fee_scalar, + } = receipt_meta.l1_block_info; + + assert_eq!( + l1_gas_price, TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_gas_price, + "incorrect l1 base fee (former gas price)" + ); + assert_eq!( + l1_gas_used, TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_gas_used, + "incorrect l1 gas used" + ); + assert_eq!( + l1_fee, TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_fee, + "incorrect l1 fee" + ); + assert_eq!( + l1_fee_scalar, TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_fee_scalar, + "incorrect l1 fee scalar" + ); + assert_eq!( + l1_base_fee_scalar, + TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_base_fee_scalar, + "incorrect l1 base fee scalar" + ); + assert_eq!( + l1_blob_base_fee, + TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_blob_base_fee, + "incorrect l1 blob base fee" + ); + assert_eq!( + l1_blob_base_fee_scalar, + TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_blob_base_fee_scalar, + "incorrect l1 blob base fee scalar" + ); + } } diff --git a/crates/optimism/rpc/src/eth/transaction.rs b/crates/optimism/rpc/src/eth/transaction.rs index 40da80c96..a332ab3f1 100644 --- a/crates/optimism/rpc/src/eth/transaction.rs +++ b/crates/optimism/rpc/src/eth/transaction.rs @@ -1,20 +1,16 @@ //! Loads and formats OP transaction RPC response. use alloy_primitives::{Bytes, B256}; - -use reth_evm_optimism::RethL1BlockInfo; use reth_node_api::FullNodeComponents; -use reth_primitives::TransactionSigned; use reth_provider::{BlockReaderIdExt, TransactionsProvider}; use reth_rpc_eth_api::{ - helpers::{EthApiSpec, EthSigner, EthTransactions, LoadTransaction, SpawnBlocking}, - EthApiTypes, FromEthApiError, + helpers::{EthSigner, EthTransactions, LoadTransaction, SpawnBlocking}, + FromEthApiError, }; use reth_rpc_eth_types::{utils::recover_raw_transaction, EthStateCache}; use reth_transaction_pool::{PoolTransaction, TransactionOrigin, TransactionPool}; -use revm::L1BlockInfo; -use crate::{eth::rpc::SequencerClient, OpEthApi, OpEthApiError}; +use crate::{eth::rpc::SequencerClient, OpEthApi}; impl EthTransactions for OpEthApi where @@ -76,67 +72,6 @@ where } } -/// L1 fee and data gas for a transaction, along with the L1 block info. -#[derive(Debug, Default, Clone)] -pub struct OptimismTxMeta { - /// The L1 block info. - pub l1_block_info: Option, - /// The L1 fee for the block. - pub l1_fee: Option, - /// The L1 data gas for the block. - pub l1_data_gas: Option, -} - -impl OptimismTxMeta { - /// Creates a new [`OptimismTxMeta`]. - pub const fn new( - l1_block_info: Option, - l1_fee: Option, - l1_data_gas: Option, - ) -> Self { - Self { l1_block_info, l1_fee, l1_data_gas } - } -} - -impl OpEthApi -where - Self: EthApiSpec + LoadTransaction, - ::Error: From, - N: FullNodeComponents, -{ - /// Builds [`OptimismTxMeta`] object using the provided [`TransactionSigned`], L1 block - /// info and block timestamp. The [`L1BlockInfo`] is used to calculate the l1 fee and l1 data - /// gas for the transaction. If the [`L1BlockInfo`] is not provided, the meta info will be - /// empty. - pub fn build_op_tx_meta( - &self, - tx: &TransactionSigned, - l1_block_info: Option, - block_timestamp: u64, - ) -> Result::Error> { - let Some(l1_block_info) = l1_block_info else { return Ok(OptimismTxMeta::default()) }; - - let (l1_fee, l1_data_gas) = if tx.is_deposit() { - (None, None) - } else { - let envelope_buf = tx.envelope_encoded(); - - let inner_l1_fee = l1_block_info - .l1_tx_data_fee(&self.chain_spec(), block_timestamp, &envelope_buf, tx.is_deposit()) - .map_err(|_| OpEthApiError::L1BlockFeeError)?; - let inner_l1_data_gas = l1_block_info - .l1_data_gas(&self.chain_spec(), block_timestamp, &envelope_buf) - .map_err(|_| OpEthApiError::L1BlockGasError)?; - ( - Some(inner_l1_fee.saturating_to::()), - Some(inner_l1_data_gas.saturating_to::()), - ) - }; - - Ok(OptimismTxMeta::new(Some(l1_block_info), l1_fee, l1_data_gas)) - } -} - impl OpEthApi where N: FullNodeComponents, diff --git a/crates/optimism/rpc/src/lib.rs b/crates/optimism/rpc/src/lib.rs index e70194a89..677bc939d 100644 --- a/crates/optimism/rpc/src/lib.rs +++ b/crates/optimism/rpc/src/lib.rs @@ -14,4 +14,4 @@ pub mod error; pub mod eth; pub use error::OpEthApiError; -pub use eth::{receipt::op_receipt_fields, transaction::OptimismTxMeta, OpEthApi}; +pub use eth::OpEthApi; diff --git a/crates/primitives-traits/src/constants/mod.rs b/crates/primitives-traits/src/constants/mod.rs index 34e286a3e..8da0bd331 100644 --- a/crates/primitives-traits/src/constants/mod.rs +++ b/crates/primitives-traits/src/constants/mod.rs @@ -131,6 +131,9 @@ pub const EMPTY_ROOT_HASH: B256 = /// From address from Optimism system txs: `0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001` pub const OP_SYSTEM_TX_FROM_ADDR: Address = address!("deaddeaddeaddeaddeaddeaddeaddeaddead0001"); +/// To address from Optimism system txs: `0x4200000000000000000000000000000000000015` +pub const OP_SYSTEM_TX_TO_ADDR: Address = address!("4200000000000000000000000000000000000015"); + /// Transactions root of empty receipts set. pub const EMPTY_RECEIPTS: B256 = EMPTY_ROOT_HASH;