diff --git a/Cargo.lock b/Cargo.lock index f572969a7..8ce84afac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2936,6 +2936,7 @@ dependencies = [ "alloy-rpc-types", "alloy-rpc-types-trace", "anyhow", + "async-trait", "borsh", "citrea-evm", "citrea-primitives", diff --git a/bin/citrea/src/eth.rs b/bin/citrea/src/eth.rs index 4ff7c0c8c..9926578b4 100644 --- a/bin/citrea/src/eth.rs +++ b/bin/citrea/src/eth.rs @@ -28,7 +28,7 @@ pub(crate) fn register_ethereum( } }; - let ethereum_rpc = ethereum_rpc::get_ethereum_rpc::( + let ethereum_rpc = ethereum_rpc::create_rpc_module::( da_service, eth_rpc_config, storage, diff --git a/crates/ethereum-rpc/Cargo.toml b/crates/ethereum-rpc/Cargo.toml index 2c533e25a..e365ec927 100644 --- a/crates/ethereum-rpc/Cargo.toml +++ b/crates/ethereum-rpc/Cargo.toml @@ -27,6 +27,7 @@ serde = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } +async-trait = { workspace = true } # Reth deps alloy-network = { workspace = true } diff --git a/crates/ethereum-rpc/src/ethereum.rs b/crates/ethereum-rpc/src/ethereum.rs index 207df4a9a..4e37ade26 100644 --- a/crates/ethereum-rpc/src/ethereum.rs +++ b/crates/ethereum-rpc/src/ethereum.rs @@ -42,6 +42,8 @@ pub struct Ethereum { pub(crate) web3_client_version: String, pub(crate) trace_cache: Mutex, ByLength>>, pub(crate) subscription_manager: Option, + pub(crate) is_sequencer: bool, + pub(crate) enable_subscription: bool, } impl Ethereum { @@ -55,6 +57,8 @@ impl Ethereum { ledger_db: LedgerDB, sequencer_client: Option, soft_confirmation_rx: Option>, + is_sequencer: bool, + enable_subscription: bool, ) -> Self { let evm = Evm::::default(); let gas_price_oracle = @@ -82,6 +86,8 @@ impl Ethereum { web3_client_version: current_version, trace_cache, subscription_manager, + is_sequencer, + enable_subscription, } } diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index 51785339f..fb4647272 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -6,7 +6,7 @@ mod trace; use std::sync::Arc; use alloy_network::AnyNetwork; -use alloy_primitives::{keccak256, Bytes, B256, U256}; +use alloy_primitives::{keccak256, Address, Bytes, B256, U256}; use alloy_rpc_types::{FeeHistory, Index}; use alloy_rpc_types_trace::geth::{GethDebugTracingOptions, GethTrace}; #[cfg(feature = "local")] @@ -16,17 +16,19 @@ use citrea_sequencer::SequencerRpcClient; pub use ethereum::{EthRpcConfig, Ethereum}; pub use gas_price::fee_history::FeeHistoryCacheConfig; pub use gas_price::gas_oracle::GasPriceOracleConfig; -use jsonrpsee::http_client::HttpClientBuilder; +use jsonrpsee::core::SubscriptionResult; +use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::ErrorObjectOwned; -use jsonrpsee::RpcModule; +use jsonrpsee::{core::RpcResult, http_client::HttpClientBuilder}; +use jsonrpsee::{PendingSubscriptionSink, RpcModule}; use reth_primitives::BlockNumberOrTag; use reth_rpc_eth_api::RpcTransaction; use reth_rpc_eth_types::EthApiError; -use serde_json::json; +use serde_json::{json, Value}; use sov_db::ledger_db::{LedgerDB, SharedLedgerOps}; use sov_ledger_rpc::LedgerRpcClient; use sov_modules_api::da::BlockHeaderTrait; -use sov_modules_api::utils::to_jsonrpsee_error_object; +use sov_modules_api::utils::{jsonrpsee_error_not_found, to_jsonrpsee_error_object}; use sov_modules_api::WorkingSet; use sov_rollup_interface::services::da::DaService; use tokio::join; @@ -51,503 +53,659 @@ pub struct SyncStatus { pub l2_status: LayerStatus, } -pub fn get_ethereum_rpc( - da_service: Arc, - eth_rpc_config: EthRpcConfig, - storage: C::Storage, - ledger_db: LedgerDB, - sequencer_client_url: Option, - soft_confirmation_rx: Option>, -) -> RpcModule> { - // Unpack config - let EthRpcConfig { - #[cfg(feature = "local")] - eth_signer, - gas_price_oracle_config, - fee_history_cache_config, - } = eth_rpc_config; - - // If the node does not have a sequencer client, then it is the sequencer. - let is_sequencer = sequencer_client_url.is_none(); - let enable_subscriptions = soft_confirmation_rx.is_some(); - - // If the running node is a full node rpc context should also have sequencer client so that it can send txs to sequencer - let mut rpc = RpcModule::new(Ethereum::new( - da_service, - gas_price_oracle_config, - fee_history_cache_config, - #[cfg(feature = "local")] - eth_signer, - storage, - ledger_db, - sequencer_client_url.map(|url| HttpClientBuilder::default().build(url).unwrap()), - soft_confirmation_rx, - )); - - register_rpc_methods(&mut rpc, is_sequencer, enable_subscriptions) - .expect("Failed to register ethereum RPC methods"); - rpc -} - -fn register_rpc_methods( - rpc: &mut RpcModule>, - // Checks wether the running node is a sequencer or not, if it is not a sequencer it should also have methods like eth_sendRawTransaction here. - is_sequencer: bool, - enable_subscriptions: bool, -) -> Result<(), jsonrpsee::core::RegisterMethodError> { - rpc.register_async_method("web3_clientVersion", |_, ethereum, _| async move { - Ok::<_, ErrorObjectOwned>(ethereum.web3_client_version.clone()) - })?; - - rpc.register_blocking_method("web3_sha3", move |params, _, _| { - let data: Bytes = params.one()?; - - let hash = B256::from_slice(keccak256(&data).as_slice()); - - Ok::<_, ErrorObjectOwned>(hash) - })?; +// pub fn get_ethereum_rpc( +// da_service: Arc, +// eth_rpc_config: EthRpcConfig, +// storage: C::Storage, +// ledger_db: LedgerDB, +// sequencer_client_url: Option, +// soft_confirmation_rx: Option>, +// ) -> RpcModule> { +// // Unpack config +// let EthRpcConfig { +// #[cfg(feature = "local")] +// eth_signer, +// gas_price_oracle_config, +// fee_history_cache_config, +// } = eth_rpc_config; + +// // If the node does not have a sequencer client, then it is the sequencer. +// let is_sequencer = sequencer_client_url.is_none(); +// let enable_subscriptions = soft_confirmation_rx.is_some(); + +// // If the running node is a full node rpc context should also have sequencer client so that it can send txs to sequencer +// let mut rpc = RpcModule::new(Ethereum::new( +// da_service, +// gas_price_oracle_config, +// fee_history_cache_config, +// #[cfg(feature = "local")] +// eth_signer, +// storage, +// ledger_db, +// sequencer_client_url.map(|url| HttpClientBuilder::default().build(url).unwrap()), +// soft_confirmation_rx, +// )); + +// register_rpc_methods(&mut rpc, is_sequencer, enable_subscriptions) +// .expect("Failed to register ethereum RPC methods"); +// rpc +// } - rpc.register_blocking_method("eth_gasPrice", move |_, ethereum, _| { - let price = { - let mut working_set = WorkingSet::new(ethereum.storage.clone()); +// Kept as Is +// TODO assess removal +// #[cfg(feature = "local")] +// rpc.register_async_method("eth_sendTransaction", |parameters, ethereum| async move { +// let mut transaction_request: TransactionRequest = parameters.one().unwrap(); + +// let evm = Evm::::default(); + +// // get from, return error if none +// let from = transaction_request +// .from +// .ok_or(to_jsonrpsee_error_object("No from address", ETH_RPC_ERROR))?; + +// // return error if not in signers +// if !ethereum.eth_signer.signers().contains(&from) { +// return Err(to_jsonrpsee_error_object( +// "From address not in signers", +// ETH_RPC_ERROR, +// )); +// } + +// let raw_evm_tx = { +// let mut working_set = WorkingSet::new(ethereum.storage.clone()); + +// // set nonce if none +// if transaction_request.nonce.is_none() { +// let nonce = evm +// .get_transaction_count(from, None, &mut working_set) +// .unwrap_or_default(); + +// transaction_request.nonce = Some(nonce); +// } + +// // get current chain id +// let chain_id = evm +// .chain_id(&mut working_set) +// .expect("Failed to get chain id") +// .map(|id| id.to::()) +// .unwrap_or(1); + +// // get call request to estimate gas and gas prices +// let (call_request, _gas_price, _max_fee_per_gas) = +// get_call_request_and_params(from, chain_id, &transaction_request); + +// // estimate gas limit +// let gas_limit = U256::from( +// evm.eth_estimate_gas(call_request, None, &mut working_set)? +// .to::(), +// ); + +// let TransactionRequest { +// to, +// gas_price, +// max_fee_per_gas, +// max_priority_fee_per_gas, +// gas, +// value, +// input: data, +// nonce, +// mut access_list, +// max_fee_per_blob_gas, +// blob_versioned_hashes, +// sidecar, +// .. +// } = transaction_request; + +// // todo: remove this inlining after https://github.com/alloy-rs/alloy/pull/183#issuecomment-1928161285 +// let transaction = match ( +// gas_price, +// max_fee_per_gas, +// access_list.take(), +// max_fee_per_blob_gas, +// blob_versioned_hashes, +// sidecar, +// ) { +// // legacy transaction +// // gas price required +// (Some(_), None, None, None, None, None) => { +// Some(TypedTransactionRequest::Legacy(LegacyTransactionRequest { +// nonce: nonce.unwrap_or_default(), +// gas_price: gas_price.unwrap_or_default(), +// gas_limit: gas.unwrap_or_default(), +// value: value.unwrap_or_default(), +// input: data.into_input().unwrap_or_default(), +// kind: match to { +// Some(to) => RpcTransactionKind::Call(to), +// None => RpcTransactionKind::Create, +// }, +// chain_id: None, +// })) +// } +// // EIP2930 +// // if only accesslist is set, and no eip1599 fees +// (_, None, Some(access_list), None, None, None) => Some( +// TypedTransactionRequest::EIP2930(EIP2930TransactionRequest { +// nonce: nonce.unwrap_or_default(), +// gas_price: gas_price.unwrap_or_default(), +// gas_limit: gas.unwrap_or_default(), +// value: value.unwrap_or_default(), +// input: data.into_input().unwrap_or_default(), +// kind: match to { +// Some(to) => RpcTransactionKind::Call(to), +// None => RpcTransactionKind::Create, +// }, +// chain_id: 0, +// access_list, +// }), +// ), +// // EIP1559 +// // if 4844 fields missing +// // gas_price, max_fee_per_gas, access_list, max_fee_per_blob_gas, blob_versioned_hashes, +// // sidecar, +// (None, _, _, None, None, None) => { +// // Empty fields fall back to the canonical transaction schema. +// Some(TypedTransactionRequest::EIP1559( +// EIP1559TransactionRequest { +// nonce: nonce.unwrap_or_default(), +// max_fee_per_gas: max_fee_per_gas.unwrap_or_default(), +// max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default(), +// gas_limit: gas.unwrap_or_default(), +// value: value.unwrap_or_default(), +// input: data.into_input().unwrap_or_default(), +// kind: match to { +// Some(to) => RpcTransactionKind::Call(to), +// None => RpcTransactionKind::Create, +// }, +// chain_id: 0, +// access_list: access_list.unwrap_or_default(), +// }, +// )) +// } +// // EIP4884 +// // all blob fields required +// ( +// None, +// _, +// _, +// Some(max_fee_per_blob_gas), +// Some(blob_versioned_hashes), +// Some(sidecar), +// ) => { +// // As per the EIP, we follow the same semantics as EIP-1559. +// Some(TypedTransactionRequest::EIP4844( +// EIP4844TransactionRequest { +// chain_id: 0, +// nonce: nonce.unwrap_or_default(), +// max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default(), +// max_fee_per_gas: max_fee_per_gas.unwrap_or_default(), +// gas_limit: gas.unwrap_or_default(), +// value: value.unwrap_or_default(), +// input: data.into_input().unwrap_or_default(), +// kind: match to { +// Some(to) => RpcTransactionKind::Call(to), +// None => RpcTransactionKind::Create, +// }, +// access_list: access_list.unwrap_or_default(), + +// // eip-4844 specific. +// max_fee_per_blob_gas, +// blob_versioned_hashes, +// sidecar, +// }, +// )) +// } + +// _ => None, +// }; + +// // get typed transaction request +// let transaction_request = match transaction { +// Some(TypedTransactionRequest::Legacy(mut m)) => { +// m.chain_id = Some(chain_id); +// m.gas_limit = gas_limit; +// m.gas_price = gas_price.unwrap(); + +// TypedTransactionRequest::Legacy(m) +// } +// Some(TypedTransactionRequest::EIP2930(mut m)) => { +// m.chain_id = chain_id; +// m.gas_limit = gas_limit; +// m.gas_price = gas_price.unwrap(); + +// TypedTransactionRequest::EIP2930(m) +// } +// Some(TypedTransactionRequest::EIP1559(mut m)) => { +// m.chain_id = chain_id; +// m.gas_limit = gas_limit; +// m.max_fee_per_gas = max_fee_per_gas.unwrap(); + +// TypedTransactionRequest::EIP1559(m) +// } +// Some(TypedTransactionRequest::EIP4844(mut m)) => { +// m.chain_id = chain_id; +// m.gas_limit = gas_limit; +// m.max_fee_per_gas = max_fee_per_gas.unwrap(); + +// TypedTransactionRequest::EIP4844(m) +// } +// None => return Err(EthApiError::ConflictingFeeFieldsInRequest.into()), +// }; + +// // get raw transaction +// let transaction = to_primitive_transaction(transaction_request) +// .ok_or(SignError::InvalidTransactionRequest)?; + +// // sign transaction +// let signed_tx = ethereum +// .eth_signer +// .sign_transaction(transaction, from) +// .map_err(|e| to_jsonrpsee_error_object(e, ETH_RPC_ERROR))?; + +// RlpEvmTransaction { +// rlp: signed_tx.envelope_encoded().to_vec(), +// } +// }; +// let (tx_hash, raw_message) = ethereum +// .make_raw_tx(raw_evm_tx) +// .map_err(|e| to_jsonrpsee_error_object(e, ETH_RPC_ERROR))?; - let (base_fee, suggested_tip) = ethereum.max_fee_per_gas(&mut working_set); +// ethereum.add_messages(vec![raw_message]); - suggested_tip + base_fee - }; +// Ok::<_, ErrorObjectOwned>(tx_hash) +// })?; - Ok::(price) - })?; +// fn get_call_request_and_params( +// from: Address, +// chain_id: u64, +// request: &TransactionRequest, +// ) -> (TransactionRequest, U256, U256) { +// // TODO: we need an oracle to fetch the gas price of the current chain +// // https://github.com/Sovereign-Labs/sovereign-sdk/issues/883 +// let gas_price = request.gas_price.unwrap_or_default(); +// let max_fee_per_gas = request.max_fee_per_gas.unwrap_or_default(); - rpc.register_blocking_method("eth_maxFeePerGas", move |_, ethereum, _| { - let max_fee_per_gas = { - let mut working_set = WorkingSet::new(ethereum.storage.clone()); +// // TODO: Generate call request better according to the transaction type +// // https://github.com/Sovereign-Labs/sovereign-sdk/issues/946 +// let call_request = TransactionRequest { +// from: Some(from), +// to: request.to, +// gas: request.gas, +// gas_price: Some(U256::from(gas_price)), +// max_fee_per_gas: Some(U256::from(max_fee_per_gas)), +// value: request.value, +// input: request.input.clone(), +// nonce: request.nonce, +// chain_id: Some(U64::from(chain_id)), +// access_list: request.access_list.clone(), +// max_priority_fee_per_gas: Some(U256::from(max_fee_per_gas)), +// transaction_type: None, +// blob_versioned_hashes: None, +// max_fee_per_blob_gas: None, +// sidecar: None, +// other: OtherFields::default(), +// }; - let (base_fee, suggested_tip) = ethereum.max_fee_per_gas(&mut working_set); +// (call_request, gas_price, max_fee_per_gas) +// } - suggested_tip + base_fee - }; +#[rpc(server, client)] +pub trait EthereumRpc { + /// Returns the client version. + #[method(name = "web3_clientVersion")] + fn web3_client_version(&self) -> RpcResult; + + /// Returns Keccak-256 hash of the given data. + #[method(name = "web3_sha3")] + #[blocking] + fn web3_sha3(&self, data: Bytes) -> RpcResult; + + /// Returns the current gas price. + #[method(name = "eth_gasPrice")] + #[blocking] + fn eth_gas_price(&self) -> RpcResult; + + /// Returns the maximum fee per gas. + #[method(name = "eth_maxFeePerGas")] + #[blocking] + fn eth_max_fee_per_gas(&self) -> RpcResult; + + /// Returns the maximum priority fee per gas. + #[method(name = "eth_maxPriorityFeePerGas")] + #[blocking] + fn eth_max_priority_fee_per_gas(&self) -> RpcResult; + + /// Returns fee history. + #[method(name = "eth_feeHistory")] + #[blocking] + fn eth_fee_history( + &self, + block_count: Index, + newest_block: BlockNumberOrTag, + reward_percentiles: Option>, + ) -> RpcResult; + + /// Returns accounts. + #[method(name = "eth_accounts")] + fn eth_accounts(&self) -> RpcResult>; + + /// Returns traces for a block by hash. + #[method(name = "debug_traceBlockByHash")] + #[blocking] + fn debug_trace_block_by_hash( + &self, + block_hash: B256, + opts: Option, + ) -> RpcResult>; + + /// Returns traces for a block by number. + #[method(name = "debug_traceBlockByNumber")] + #[blocking] + fn debug_trace_block_by_number( + &self, + block_number: BlockNumberOrTag, + opts: Option, + ) -> RpcResult>; + + /// Returns trace for a transaction. + #[method(name = "debug_traceTransaction")] + #[blocking] + fn debug_trace_transaction( + &self, + tx_hash: B256, + opts: Option, + ) -> RpcResult; + + /// Returns the transaction pool content. + #[method(name = "txpool_content")] + fn txpool_content(&self) -> RpcResult; + + /// Gets uncle by block hash and index. + #[method(name = "eth_getUncleByBlockHashAndIndex")] + fn get_uncle_by_block_hash_and_index( + &self, + block_hash: String, + uncle_index: String, + ) -> RpcResult; + + /// Sends raw transaction (full node only). + #[method(name = "eth_sendRawTransaction")] + async fn eth_send_raw_transaction(&self, data: Bytes) -> RpcResult; + + /// Gets transaction by hash (full node only). + #[method(name = "eth_getTransactionByHash")] + async fn eth_get_transaction_by_hash( + &self, + hash: B256, + mempool_only: Option, + ) -> RpcResult>>; + + /// Gets sync status (full node only). + #[method(name = "citrea_syncStatus")] + async fn citrea_sync_status(&self) -> RpcResult; + + /// Subscribe to debug events. + #[subscription(name = "debug_subscribe" => "debug_subscription", unsubscribe = "debug_unsubscribe", item = GethTrace)] + async fn subscribe_debug( + &self, + topic: String, + start_block: BlockNumberOrTag, + end_block: BlockNumberOrTag, + opts: Option, + ) -> SubscriptionResult; + + /// Subscribe to Ethereum events. + #[subscription(name = "eth_subscribe" => "eth_subscription", unsubscribe = "eth_unsubscribe", item = Value)] + async fn subscribe_eth(&self, topic: String, filter: Option) -> SubscriptionResult; +} - Ok::(max_fee_per_gas) - })?; +const ETH_RPC_ERROR: &str = "ETH_RPC_ERROR"; - rpc.register_blocking_method("eth_maxPriorityFeePerGas", move |_, ethereum, _| { - let max_priority_fee = { - let mut working_set = WorkingSet::new(ethereum.storage.clone()); +fn to_eth_rpc_error(err: impl ToString) -> ErrorObjectOwned { + to_jsonrpsee_error_object(ETH_RPC_ERROR, err) +} - let (_base_fee, suggested_tip) = ethereum.max_fee_per_gas(&mut working_set); +pub struct EthereumRpcServerImpl +where + C: sov_modules_api::Context, + Da: DaService, +{ + ethereum: Arc>, +} - suggested_tip - }; +impl EthereumRpcServerImpl +where + C: sov_modules_api::Context, + Da: DaService, +{ + pub fn new(ethereum: Arc>) -> Self { + Self { ethereum } + } +} - Ok::(max_priority_fee) - })?; +#[async_trait::async_trait] +impl EthereumRpcServer for EthereumRpcServerImpl +where + C: sov_modules_api::Context, + Da: DaService, +{ + fn web3_client_version(&self) -> RpcResult { + Ok(self.ethereum.web3_client_version.clone()) + } - rpc.register_blocking_method("eth_feeHistory", move |params, ethereum, _| { - let mut params = params.sequence(); + fn web3_sha3(&self, data: Bytes) -> RpcResult { + Ok(B256::from_slice(keccak256(&data).as_slice())) + } - let block_count: Index = params.next()?; - let newest_block: BlockNumberOrTag = params.next()?; - let reward_percentiles: Option> = params.optional_next()?; + fn eth_gas_price(&self) -> RpcResult { + let mut working_set = WorkingSet::new(self.ethereum.storage.clone()); + let (base_fee, suggested_tip) = self.ethereum.max_fee_per_gas(&mut working_set); + Ok(suggested_tip + base_fee) + } - // convert block count to u64 from hex - let block_count = usize::from(block_count) as u64; + fn eth_max_fee_per_gas(&self) -> RpcResult { + let mut working_set = WorkingSet::new(self.ethereum.storage.clone()); + let (base_fee, suggested_tip) = self.ethereum.max_fee_per_gas(&mut working_set); + Ok(suggested_tip + base_fee) + } - let fee_history = { - let mut working_set = WorkingSet::new(ethereum.storage.clone()); + fn eth_max_priority_fee_per_gas(&self) -> RpcResult { + let mut working_set = WorkingSet::new(self.ethereum.storage.clone()); + let (_base_fee, suggested_tip) = self.ethereum.max_fee_per_gas(&mut working_set); + Ok(suggested_tip) + } - ethereum.gas_price_oracle.fee_history( + fn eth_fee_history( + &self, + block_count: Index, + newest_block: BlockNumberOrTag, + reward_percentiles: Option>, + ) -> RpcResult { + let block_count = block_count.0 as u64; + let mut working_set = WorkingSet::new(self.ethereum.storage.clone()); + + self.ethereum + .gas_price_oracle + .fee_history( block_count, newest_block, reward_percentiles, &mut working_set, - )? - }; + ) + .map_err(to_eth_rpc_error) + } - Ok::(fee_history) - })?; - - #[cfg(feature = "local")] - rpc.register_async_method("eth_accounts", |_, ethereum, _| async move { - Ok::<_, ErrorObjectOwned>(ethereum.eth_signer.signers()) - })?; - - // #[cfg(feature = "local")] - // rpc.register_async_method("eth_sendTransaction", |parameters, ethereum| async move { - // let mut transaction_request: TransactionRequest = parameters.one().unwrap(); - - // let evm = Evm::::default(); - - // // get from, return error if none - // let from = transaction_request - // .from - // .ok_or(to_jsonrpsee_error_object("No from address", ETH_RPC_ERROR))?; - - // // return error if not in signers - // if !ethereum.eth_signer.signers().contains(&from) { - // return Err(to_jsonrpsee_error_object( - // "From address not in signers", - // ETH_RPC_ERROR, - // )); - // } - - // let raw_evm_tx = { - // let mut working_set = WorkingSet::new(ethereum.storage.clone()); - - // // set nonce if none - // if transaction_request.nonce.is_none() { - // let nonce = evm - // .get_transaction_count(from, None, &mut working_set) - // .unwrap_or_default(); - - // transaction_request.nonce = Some(nonce); - // } - - // // get current chain id - // let chain_id = evm - // .chain_id(&mut working_set) - // .expect("Failed to get chain id") - // .map(|id| id.to::()) - // .unwrap_or(1); - - // // get call request to estimate gas and gas prices - // let (call_request, _gas_price, _max_fee_per_gas) = - // get_call_request_and_params(from, chain_id, &transaction_request); - - // // estimate gas limit - // let gas_limit = U256::from( - // evm.eth_estimate_gas(call_request, None, &mut working_set)? - // .to::(), - // ); - - // let TransactionRequest { - // to, - // gas_price, - // max_fee_per_gas, - // max_priority_fee_per_gas, - // gas, - // value, - // input: data, - // nonce, - // mut access_list, - // max_fee_per_blob_gas, - // blob_versioned_hashes, - // sidecar, - // .. - // } = transaction_request; - - // // todo: remove this inlining after https://github.com/alloy-rs/alloy/pull/183#issuecomment-1928161285 - // let transaction = match ( - // gas_price, - // max_fee_per_gas, - // access_list.take(), - // max_fee_per_blob_gas, - // blob_versioned_hashes, - // sidecar, - // ) { - // // legacy transaction - // // gas price required - // (Some(_), None, None, None, None, None) => { - // Some(TypedTransactionRequest::Legacy(LegacyTransactionRequest { - // nonce: nonce.unwrap_or_default(), - // gas_price: gas_price.unwrap_or_default(), - // gas_limit: gas.unwrap_or_default(), - // value: value.unwrap_or_default(), - // input: data.into_input().unwrap_or_default(), - // kind: match to { - // Some(to) => RpcTransactionKind::Call(to), - // None => RpcTransactionKind::Create, - // }, - // chain_id: None, - // })) - // } - // // EIP2930 - // // if only accesslist is set, and no eip1599 fees - // (_, None, Some(access_list), None, None, None) => Some( - // TypedTransactionRequest::EIP2930(EIP2930TransactionRequest { - // nonce: nonce.unwrap_or_default(), - // gas_price: gas_price.unwrap_or_default(), - // gas_limit: gas.unwrap_or_default(), - // value: value.unwrap_or_default(), - // input: data.into_input().unwrap_or_default(), - // kind: match to { - // Some(to) => RpcTransactionKind::Call(to), - // None => RpcTransactionKind::Create, - // }, - // chain_id: 0, - // access_list, - // }), - // ), - // // EIP1559 - // // if 4844 fields missing - // // gas_price, max_fee_per_gas, access_list, max_fee_per_blob_gas, blob_versioned_hashes, - // // sidecar, - // (None, _, _, None, None, None) => { - // // Empty fields fall back to the canonical transaction schema. - // Some(TypedTransactionRequest::EIP1559( - // EIP1559TransactionRequest { - // nonce: nonce.unwrap_or_default(), - // max_fee_per_gas: max_fee_per_gas.unwrap_or_default(), - // max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default(), - // gas_limit: gas.unwrap_or_default(), - // value: value.unwrap_or_default(), - // input: data.into_input().unwrap_or_default(), - // kind: match to { - // Some(to) => RpcTransactionKind::Call(to), - // None => RpcTransactionKind::Create, - // }, - // chain_id: 0, - // access_list: access_list.unwrap_or_default(), - // }, - // )) - // } - // // EIP4884 - // // all blob fields required - // ( - // None, - // _, - // _, - // Some(max_fee_per_blob_gas), - // Some(blob_versioned_hashes), - // Some(sidecar), - // ) => { - // // As per the EIP, we follow the same semantics as EIP-1559. - // Some(TypedTransactionRequest::EIP4844( - // EIP4844TransactionRequest { - // chain_id: 0, - // nonce: nonce.unwrap_or_default(), - // max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default(), - // max_fee_per_gas: max_fee_per_gas.unwrap_or_default(), - // gas_limit: gas.unwrap_or_default(), - // value: value.unwrap_or_default(), - // input: data.into_input().unwrap_or_default(), - // kind: match to { - // Some(to) => RpcTransactionKind::Call(to), - // None => RpcTransactionKind::Create, - // }, - // access_list: access_list.unwrap_or_default(), - - // // eip-4844 specific. - // max_fee_per_blob_gas, - // blob_versioned_hashes, - // sidecar, - // }, - // )) - // } - - // _ => None, - // }; - - // // get typed transaction request - // let transaction_request = match transaction { - // Some(TypedTransactionRequest::Legacy(mut m)) => { - // m.chain_id = Some(chain_id); - // m.gas_limit = gas_limit; - // m.gas_price = gas_price.unwrap(); - - // TypedTransactionRequest::Legacy(m) - // } - // Some(TypedTransactionRequest::EIP2930(mut m)) => { - // m.chain_id = chain_id; - // m.gas_limit = gas_limit; - // m.gas_price = gas_price.unwrap(); - - // TypedTransactionRequest::EIP2930(m) - // } - // Some(TypedTransactionRequest::EIP1559(mut m)) => { - // m.chain_id = chain_id; - // m.gas_limit = gas_limit; - // m.max_fee_per_gas = max_fee_per_gas.unwrap(); - - // TypedTransactionRequest::EIP1559(m) - // } - // Some(TypedTransactionRequest::EIP4844(mut m)) => { - // m.chain_id = chain_id; - // m.gas_limit = gas_limit; - // m.max_fee_per_gas = max_fee_per_gas.unwrap(); - - // TypedTransactionRequest::EIP4844(m) - // } - // None => return Err(EthApiError::ConflictingFeeFieldsInRequest.into()), - // }; - - // // get raw transaction - // let transaction = to_primitive_transaction(transaction_request) - // .ok_or(SignError::InvalidTransactionRequest)?; - - // // sign transaction - // let signed_tx = ethereum - // .eth_signer - // .sign_transaction(transaction, from) - // .map_err(|e| to_jsonrpsee_error_object(e, ETH_RPC_ERROR))?; - - // RlpEvmTransaction { - // rlp: signed_tx.envelope_encoded().to_vec(), - // } - // }; - // let (tx_hash, raw_message) = ethereum - // .make_raw_tx(raw_evm_tx) - // .map_err(|e| to_jsonrpsee_error_object(e, ETH_RPC_ERROR))?; - - // ethereum.add_messages(vec![raw_message]); - - // Ok::<_, ErrorObjectOwned>(tx_hash) - // })?; - - rpc.register_blocking_method::, ErrorObjectOwned>, _>( - "debug_traceBlockByHash", - move |parameters, ethereum, _| { - let mut params = parameters.sequence(); - - let block_hash: B256 = params.next()?; - let evm = Evm::::default(); - let mut working_set = WorkingSet::new(ethereum.storage.clone()); - let opts: Option = params.optional_next()?; - - let block_number = - match evm.get_block_number_by_block_hash(block_hash, &mut working_set) { - Some(block_number) => block_number, - None => { - return Err(EthApiError::HeaderNotFound(block_hash.into()).into()); - } - }; + fn eth_accounts(&self) -> RpcResult> { + Ok(self.ethereum.eth_signer.signers()) + } - debug_trace_by_block_number(block_number, None, ðereum, &evm, &mut working_set, opts) - }, - )?; + fn debug_trace_block_by_hash( + &self, + block_hash: B256, + opts: Option, + ) -> RpcResult> { + let evm = Evm::::default(); + let mut working_set = WorkingSet::new(self.ethereum.storage.clone()); + + let block_number = match evm.get_block_number_by_block_hash(block_hash, &mut working_set) { + Some(block_number) => block_number, + None => { + return Err(EthApiError::HeaderNotFound(block_hash.into()).into()); + } + }; - rpc.register_blocking_method::, ErrorObjectOwned>, _>( - "debug_traceBlockByNumber", - move |parameters, ethereum, _| { - let mut params = parameters.sequence(); + debug_trace_by_block_number( + block_number, + None, + &self.ethereum, + &evm, + &mut working_set, + opts, + ) + .map_err(to_eth_rpc_error) + } - let block_number: BlockNumberOrTag = params.next()?; - let opts: Option = params.optional_next()?; + fn debug_trace_block_by_number( + &self, + block_number: BlockNumberOrTag, + opts: Option, + ) -> RpcResult> { + let mut working_set = WorkingSet::new(self.ethereum.storage.clone()); + let evm = Evm::::default(); + let latest_block_number: u64 = evm.block_number(&mut working_set)?.saturating_to(); + + let block_number = match block_number { + BlockNumberOrTag::Number(block_number) => block_number, + BlockNumberOrTag::Latest => latest_block_number, + _ => return Err(EthApiError::Unsupported( + "Earliest, pending, safe and finalized are not supported for debug_traceBlockByNumber", + ).into()), + }; - let mut working_set = WorkingSet::new(ethereum.storage.clone()); - let evm = Evm::::default(); - let latest_block_number: u64 = evm.block_number(&mut working_set)?.saturating_to(); + debug_trace_by_block_number( + block_number, + None, + &self.ethereum, + &evm, + &mut working_set, + opts, + ) + .map_err(to_eth_rpc_error) + } - let block_number = match block_number { - BlockNumberOrTag::Number(block_number) => block_number, - BlockNumberOrTag::Latest => latest_block_number, - _ => return Err(EthApiError::Unsupported( - "Earliest, pending, safe and finalized are not supported for debug_traceBlockByNumber", - ) - .into()), - }; - - debug_trace_by_block_number(block_number, None, ðereum, &evm, &mut working_set, opts) - }, - )?; - - rpc.register_blocking_method::, _>( - "debug_traceTransaction", - move |parameters, ethereum, _| { - // the main rpc handler for debug_traceTransaction - // Checks the cache in ethereum struct if the trace exists - // if found; returns the trace - // else; calls the debug_trace_transaction_block function in evm - // that function traces the entire block, returns all the traces to here - // then we put them into cache and return the trace of the requested transaction - let mut params = parameters.sequence(); - - let tx_hash: B256 = params.next()?; - - let evm = Evm::::default(); - let mut working_set = WorkingSet::new(ethereum.storage.clone()); - - let tx = evm - .get_transaction_by_hash(tx_hash, &mut working_set) - .unwrap() - .ok_or_else(|| EthApiError::UnknownBlockOrTxIndex)?; - let trace_idx: u64 = tx - .transaction_index - .expect("Tx index must be set for tx inside block"); - - let block_number: u64 = tx - .block_number - .expect("Block number must be set for tx inside block"); - - let opts: Option = params.optional_next()?; - - let traces = debug_trace_by_block_number( - block_number, - Some(trace_idx as usize), - ðereum, - &evm, - &mut working_set, - opts, - )?; - Ok(traces[0].clone()) - }, - )?; + // the main rpc handler for debug_traceTransaction + // Checks the cache in ethereum struct if the trace exists + // if found; returns the trace + // else; calls the debug_trace_transaction_block function in evm + // that function traces the entire block, returns all the traces to here + // then we put them into cache and return the trace of the requested transaction + fn debug_trace_transaction( + &self, + tx_hash: B256, + opts: Option, + ) -> RpcResult { + let evm = Evm::::default(); + let mut working_set = WorkingSet::new(self.ethereum.storage.clone()); + + let tx = evm + .get_transaction_by_hash(tx_hash, &mut working_set) + .unwrap() + .ok_or_else(|| EthApiError::UnknownBlockOrTxIndex)?; + + let trace_idx: u64 = tx + .transaction_index + .expect("Tx index must be set for tx inside block"); + + let block_number: u64 = tx + .block_number + .expect("Block number must be set for tx inside block"); + + let traces = debug_trace_by_block_number( + block_number, + Some(trace_idx as usize), + &self.ethereum, + &evm, + &mut working_set, + opts, + ) + .map_err(to_eth_rpc_error)?; + + Ok(traces[0].clone()) + } - rpc.register_async_method("txpool_content", |_, _, _| async move { + fn txpool_content(&self) -> RpcResult { // This is a simple mock for serde. - let json = json!({ + Ok(json!({ "pending": {}, "queued": {} - }); - - Ok::<_, ErrorObjectOwned>(json) - })?; - - rpc.register_async_method( - "eth_getUncleByBlockHashAndIndex", - |parameters, _, _| async move { - let mut params = parameters.sequence(); - - let _block_hash: String = params.next()?; - let _uncle_index_position: String = params.next()?; - - let res = json!(null); - - Ok::<_, ErrorObjectOwned>(res) - }, - )?; - - if !is_sequencer { - rpc.register_async_method::, _, _>( - "eth_sendRawTransaction", - |parameters, ethereum, _| async move { - // send this directly to the sequencer - let data: Bytes = parameters.one()?; - // sequencer client should send it - let tx_hash = ethereum - .sequencer_client - .as_ref() - .unwrap() - .eth_send_raw_transaction(data) - .await; + })) + } + + fn get_uncle_by_block_hash_and_index( + &self, + _block_hash: String, + _uncle_index: String, + ) -> RpcResult { + Ok(json!(null)) + } + + async fn eth_send_raw_transaction(&self, data: Bytes) -> RpcResult { + if self.ethereum.is_sequencer { + return Err(jsonrpsee_error_not_found()); + } + + let tx_hash = SequencerRpcClient::eth_send_raw_transaction( + self.ethereum.sequencer_client.as_ref().unwrap(), + data, + ) + .await; + + match tx_hash { + Ok(tx_hash) => Ok(tx_hash), + Err(e) => match e { + jsonrpsee::core::client::Error::Call(e_owned) => Err(e_owned), + _ => Err(to_jsonrpsee_error_object("SEQUENCER_CLIENT_ERROR", e)), + }, + } + } - match tx_hash { - Ok(tx_hash) => Ok(tx_hash), + async fn eth_get_transaction_by_hash( + &self, + hash: B256, + mempool_only: Option, + ) -> RpcResult>> { + if self.ethereum.is_sequencer { + return Err(jsonrpsee_error_not_found()); + } + + match mempool_only { + Some(true) => { + match SequencerRpcClient::eth_get_transaction_by_hash( + self.ethereum.sequencer_client.as_ref().unwrap(), + hash, + Some(true), + ) + .await + { + Ok(tx) => Ok(tx), Err(e) => match e { jsonrpsee::core::client::Error::Call(e_owned) => Err(e_owned), _ => Err(to_jsonrpsee_error_object("SEQUENCER_CLIENT_ERROR", e)), }, } - }, - )?; - - rpc.register_async_method::>, ErrorObjectOwned>, _, _>( - "eth_getTransactionByHash", - |parameters, ethereum, _| async move { - let mut params = parameters.sequence(); - let hash: B256 = params.next()?; - let mempool_only: Result, ErrorObjectOwned> = params.optional_next(); - - // check if mempool_only parameter was given what was its value - match mempool_only { - // only ask sequencer - Ok(Some(true)) => { - match ethereum - .sequencer_client - .as_ref() - .unwrap() - .eth_get_transaction_by_hash(hash, Some(true)) - .await + } + _ => { + let evm = Evm::::default(); + let mut working_set = WorkingSet::new(self.ethereum.storage.clone()); + match evm.get_transaction_by_hash(hash, &mut working_set) { + Ok(Some(tx)) => Ok(Some(tx)), + Ok(None) => { + match SequencerRpcClient::eth_get_transaction_by_hash( + self.ethereum.sequencer_client.as_ref().unwrap(), + hash, + Some(true), + ) + .await { Ok(tx) => Ok(tx), Err(e) => match e { @@ -556,227 +714,183 @@ fn register_rpc_methods( }, } } - _ => { - // if mempool_only is not true ask evm first then sequencer - let evm = Evm::::default(); - let mut working_set = WorkingSet::new(ethereum.storage.clone()); - match evm.get_transaction_by_hash(hash, &mut working_set) { - Ok(Some(tx)) => Ok(Some(tx)), - Ok(None) => { - // if not found in evm then ask to sequencer mempool - match ethereum - .sequencer_client - .as_ref() - .unwrap() - .eth_get_transaction_by_hash(hash, Some(true)) - .await - { - Ok(tx) => Ok(tx), - Err(e) => match e { - jsonrpsee::core::client::Error::Call(e_owned) => { - Err(e_owned) - } - _ => Err(to_jsonrpsee_error_object( - "SEQUENCER_CLIENT_ERROR", - e, - )), - }, - } - } - Err(e) => { - // return error - Err(e) - } - } - } + Err(e) => Err(e), } + } + } + } + + async fn citrea_sync_status(&self) -> RpcResult { + if self.ethereum.is_sequencer { + return Err(jsonrpsee_error_not_found()); + } + + let (sequencer_response, da_response) = join!( + self.ethereum + .sequencer_client + .as_ref() + .unwrap() + .get_head_soft_confirmation_height(), + self.ethereum.da_service.get_last_finalized_block_header() + ); + + let l2_head_block_number = match sequencer_response { + Ok(block_number) => block_number, + Err(e) => match e { + jsonrpsee::core::client::Error::Call(e_owned) => return Err(e_owned), + _ => return Err(to_jsonrpsee_error_object("SEQUENCER_CLIENT_ERROR", e)), }, - )?; - - rpc.register_async_method::, _, _>( - "citrea_syncStatus", - |_, ethereum, _| async move { - // sequencer client should send latest l2 height - // da service should send latest finalized l1 block header - let (sequencer_response, da_response) = join!( - ethereum - .sequencer_client - .as_ref() - .unwrap() - .get_head_soft_confirmation_height(), - ethereum.da_service.get_last_finalized_block_header() - ); - // handle sequencer response - let l2_head_block_number = match sequencer_response { - Ok(block_number) => block_number, - Err(e) => match e { - jsonrpsee::core::client::Error::Call(e_owned) => return Err(e_owned), - _ => return Err(to_jsonrpsee_error_object("SEQUENCER_CLIENT_ERROR", e)), - }, - }; + }; - // get l2 synced block number + let head_soft_confirmation = self.ethereum.ledger_db.get_head_soft_confirmation(); + let l2_synced_block_number = match head_soft_confirmation { + Ok(Some((height, _))) => height.0, + Ok(None) => 0u64, + Err(e) => return Err(to_jsonrpsee_error_object("LEDGER_DB_ERROR", e)), + }; - let head_soft_confirmation = ethereum.ledger_db.get_head_soft_confirmation(); + let l1_head_block_number = match da_response { + Ok(header) => header.height(), + Err(e) => return Err(to_jsonrpsee_error_object("DA_SERVICE_ERROR", e)), + }; - let l2_synced_block_number = match head_soft_confirmation { - Ok(Some((height, _))) => height.0, - Ok(None) => 0u64, - Err(e) => return Err(to_jsonrpsee_error_object("LEDGER_DB_ERROR", e)), - }; + let l1_synced_block_number = match self.ethereum.ledger_db.get_last_scanned_l1_height() { + Ok(Some(slot_number)) => slot_number.0, + Ok(None) => 0u64, + Err(e) => return Err(to_jsonrpsee_error_object("LEDGER_DB_ERROR", e)), + }; - // handle da service response - let l1_head_block_number = match da_response { - Ok(header) => header.height(), - Err(e) => return Err(to_jsonrpsee_error_object("DA_SERVICE_ERROR", e)), - }; + let l1_status = if l1_synced_block_number < l1_head_block_number { + LayerStatus::Syncing(SyncValues { + synced_block_number: l1_synced_block_number, + head_block_number: l1_head_block_number, + }) + } else { + LayerStatus::Synced(l1_head_block_number) + }; - // get l1 synced block number - let l1_synced_block_number = match ethereum.ledger_db.get_last_scanned_l1_height() { - Ok(Some(slot_number)) => slot_number.0, - Ok(None) => 0u64, - Err(e) => return Err(to_jsonrpsee_error_object("LEDGER_DB_ERROR", e)), - }; + let l2_status = if l2_synced_block_number < l2_head_block_number { + LayerStatus::Syncing(SyncValues { + synced_block_number: l2_synced_block_number, + head_block_number: l2_head_block_number, + }) + } else { + LayerStatus::Synced(l2_head_block_number) + }; - let l1_status = if l1_synced_block_number < l1_head_block_number { - LayerStatus::Syncing(SyncValues { - synced_block_number: l1_synced_block_number, - head_block_number: l1_head_block_number, - }) - } else { - LayerStatus::Synced(l1_head_block_number) - }; - let l2_status = if l2_synced_block_number < l2_head_block_number { - LayerStatus::Syncing(SyncValues { - synced_block_number: l2_synced_block_number, - head_block_number: l2_head_block_number, - }) - } else { - LayerStatus::Synced(l2_head_block_number) - }; - Ok::(SyncStatus { - l1_status, - l2_status, - }) - }, - )?; + Ok(SyncStatus { + l1_status, + l2_status, + }) } - if enable_subscriptions { - rpc.register_subscription( - "debug_subscribe", - "debug_subscription", - "debug_unsubscribe", - |parameters, pending, ethereum, _| async move { - let mut params = parameters.sequence(); - - let topic: String = match params.next() { - Ok(v) => v, - Err(err) => { - pending.reject(err).await; - return Ok(()); - } - }; - match topic.as_str() { - "traceChain" => handle_debug_trace_chain(params, pending, ethereum).await, - _ => { - pending - .reject(EthApiError::Unsupported("Unsupported subscription topic")) - .await; - return Ok(()); - } - }; + async fn subscribe_debug( + &self, + pending: PendingSubscriptionSink, + topic: String, + start_block: BlockNumberOrTag, + end_block: BlockNumberOrTag, + opts: Option, + ) -> SubscriptionResult { + if !self.ethereum.enable_subscription { + pending.reject(jsonrpsee_error_not_found()).await; + return Ok(()); + } + + if &topic == "traceChain" { + handle_debug_trace_chain(start_block, end_block, opts, pending, self.ethereum.clone()) + .await; + } else { + pending + .reject(to_eth_rpc_error("Unsupported subscription topic")) + .await; + } + Ok(()) + } - Ok(()) - }, - )?; - - rpc.register_subscription( - "eth_subscribe", - "eth_subscription", - "eth_unsubscribe", - |parameters, pending, ethereum, _| async move { - let mut params = parameters.sequence(); - - let topic: String = match params.next() { - Ok(v) => v, - Err(err) => { - pending.reject(err).await; - return Ok(()); - } - }; - match topic.as_str() { - "newHeads" => { - let subscription = pending.accept().await.unwrap(); - ethereum - .subscription_manager - .as_ref() - .unwrap() - .register_new_heads_subscription(subscription) - .await; - } - "logs" => { - let filter: Filter = match params.next() { - Ok(v) => v, - Err(err) => { - pending.reject(err).await; - return Ok(()); - } - }; - let subscription = pending.accept().await.unwrap(); - ethereum - .subscription_manager - .as_ref() - .unwrap() - .register_new_logs_subscription(filter, subscription) - .await; - } - _ => { - pending - .reject(EthApiError::Unsupported("Unsupported subscription topic")) - .await; - return Ok(()); - } + async fn subscribe_eth( + &self, + pending: PendingSubscriptionSink, + topic: String, + filter: Option, + ) -> SubscriptionResult { + if !self.ethereum.enable_subscription { + pending.reject(jsonrpsee_error_not_found()).await; + return Ok(()); + } + + match topic.as_str() { + "newHeads" => { + let subscription = pending.accept().await?; + self.ethereum + .subscription_manager + .as_ref() + .unwrap() + .register_new_heads_subscription(subscription) + .await; + } + "logs" => { + let Some(filter) = filter else { + pending.reject(to_eth_rpc_error("Missing filter")).await; + return Ok(()); }; - Ok(()) - }, - )?; + let subscription = pending.accept().await?; + self.ethereum + .subscription_manager + .as_ref() + .unwrap() + .register_new_logs_subscription(filter, subscription) + .await; + } + _ => { + pending + .reject(EthApiError::Unsupported("Unsupported subscription topic")) + .await; + } + } + Ok(()) } - - Ok(()) } -// fn get_call_request_and_params( -// from: Address, -// chain_id: u64, -// request: &TransactionRequest, -// ) -> (TransactionRequest, U256, U256) { -// // TODO: we need an oracle to fetch the gas price of the current chain -// // https://github.com/Sovereign-Labs/sovereign-sdk/issues/883 -// let gas_price = request.gas_price.unwrap_or_default(); -// let max_fee_per_gas = request.max_fee_per_gas.unwrap_or_default(); +pub fn create_rpc_module( + da_service: Arc, + eth_rpc_config: EthRpcConfig, + storage: C::Storage, + ledger_db: LedgerDB, + sequencer_client_url: Option, + soft_confirmation_rx: Option>, +) -> RpcModule> +where + C: sov_modules_api::Context, + Da: DaService, +{ + // Unpack config + let EthRpcConfig { + #[cfg(feature = "local")] + eth_signer, + gas_price_oracle_config, + fee_history_cache_config, + } = eth_rpc_config; -// // TODO: Generate call request better according to the transaction type -// // https://github.com/Sovereign-Labs/sovereign-sdk/issues/946 -// let call_request = TransactionRequest { -// from: Some(from), -// to: request.to, -// gas: request.gas, -// gas_price: Some(U256::from(gas_price)), -// max_fee_per_gas: Some(U256::from(max_fee_per_gas)), -// value: request.value, -// input: request.input.clone(), -// nonce: request.nonce, -// chain_id: Some(U64::from(chain_id)), -// access_list: request.access_list.clone(), -// max_priority_fee_per_gas: Some(U256::from(max_fee_per_gas)), -// transaction_type: None, -// blob_versioned_hashes: None, -// max_fee_per_blob_gas: None, -// sidecar: None, -// other: OtherFields::default(), -// }; + // If the node does not have a sequencer client, then it is the sequencer. + let is_sequencer = sequencer_client_url.is_none(); + let enable_subscriptions = soft_confirmation_rx.is_some(); -// (call_request, gas_price, max_fee_per_gas) -// } + // If the running node is a full node rpc context should also have sequencer client so that it can send txs to sequencer + let ethereum = Arc::new(Ethereum::new( + da_service, + gas_price_oracle_config, + fee_history_cache_config, + #[cfg(feature = "local")] + eth_signer, + storage, + ledger_db, + sequencer_client_url.map(|url| HttpClientBuilder::default().build(url).unwrap()), + soft_confirmation_rx, + is_sequencer, + enable_subscriptions, + )); + let server = EthereumRpcServerImpl::new(ethereum); + EthereumRpcServer::into_rpc(server) +} diff --git a/crates/ethereum-rpc/src/trace.rs b/crates/ethereum-rpc/src/trace.rs index 1e3100c9f..43316b237 100644 --- a/crates/ethereum-rpc/src/trace.rs +++ b/crates/ethereum-rpc/src/trace.rs @@ -7,7 +7,7 @@ use alloy_rpc_types_trace::geth::{ }; #[cfg(feature = "local")] use citrea_evm::Evm; -use jsonrpsee::types::{ErrorObjectOwned, ParamsSequence}; +use jsonrpsee::types::ErrorObjectOwned; use jsonrpsee::{PendingSubscriptionSink, SubscriptionMessage}; use reth_primitives::BlockNumberOrTag; use reth_rpc_eth_types::error::EthApiError; @@ -18,25 +18,12 @@ use tracing::error; use crate::ethereum::Ethereum; pub async fn handle_debug_trace_chain( - mut params: ParamsSequence<'_>, + start_block: BlockNumberOrTag, + end_block: BlockNumberOrTag, + opts: Option, pending: PendingSubscriptionSink, ethereum: Arc>, ) { - let start_block: BlockNumberOrTag = match params.next() { - Ok(v) => v, - Err(err) => { - pending.reject(err).await; - return; - } - }; - let end_block: BlockNumberOrTag = match params.next() { - Ok(v) => v, - Err(err) => { - pending.reject(err).await; - return; - } - }; - // start block is exclusive, hence latest is not supported let BlockNumberOrTag::Number(start_block) = start_block else { pending.reject(EthApiError::Unsupported( @@ -77,14 +64,6 @@ pub async fn handle_debug_trace_chain = match params.optional_next() { - Ok(v) => v, - Err(err) => { - pending.reject(err).await; - return; - } - }; - let subscription = pending.accept().await.unwrap(); // This task will be fetching and sending to the subscription sink the list of traces diff --git a/crates/sovereign-sdk/module-system/sov-modules-api/src/utils.rs b/crates/sovereign-sdk/module-system/sov-modules-api/src/utils.rs index ef54e3fbc..8ba3876a5 100644 --- a/crates/sovereign-sdk/module-system/sov-modules-api/src/utils.rs +++ b/crates/sovereign-sdk/module-system/sov-modules-api/src/utils.rs @@ -14,3 +14,11 @@ pub fn to_jsonrpsee_error_object(message: &str, err: impl ToString) -> ErrorObje Some(err.to_string()), ) } + +pub fn jsonrpsee_error_not_found() -> ErrorObjectOwned { + ErrorObjectOwned::owned( + jsonrpsee::types::error::METHOD_NOT_FOUND_CODE, + jsonrpsee::types::error::METHOD_NOT_FOUND_MSG, + None::, + ) +}