From ba33d9e8aca66f001a6d2477a9b976652accc1c6 Mon Sep 17 00:00:00 2001 From: sprites0 <199826320+sprites0@users.noreply.github.com> Date: Sat, 5 Jul 2025 03:10:26 +0000 Subject: [PATCH] feat: Add call forwarder https://github.com/hl-archive-node/nanoreth/commit/4b793c496b3e6753424fb9d070802674dcd86360 --- src/call_forwarder.rs | 97 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/main.rs | 8 ++++ src/node/cli.rs | 6 +++ 4 files changed, 112 insertions(+) create mode 100644 src/call_forwarder.rs diff --git a/src/call_forwarder.rs b/src/call_forwarder.rs new file mode 100644 index 000000000..c6f7118de --- /dev/null +++ b/src/call_forwarder.rs @@ -0,0 +1,97 @@ +use alloy_eips::BlockId; +use alloy_primitives::{Bytes, U256}; +use alloy_rpc_types_eth::{state::StateOverride, transaction::TransactionRequest, BlockOverrides}; +use jsonrpsee::{ + http_client::{HttpClient, HttpClientBuilder}, + proc_macros::rpc, + rpc_params, + types::{error::INTERNAL_ERROR_CODE, ErrorObject}, +}; +use jsonrpsee_core::{async_trait, client::ClientT, ClientError, RpcResult}; + +#[rpc(server, namespace = "eth")] +pub(crate) trait CallForwarderApi { + /// Executes a new message call immediately without creating a transaction on the block chain. + #[method(name = "call")] + async fn call( + &self, + request: TransactionRequest, + block_number: Option, + state_overrides: Option, + block_overrides: Option>, + ) -> RpcResult; + + /// Generates and returns an estimate of how much gas is necessary to allow the transaction to + /// complete. + #[method(name = "estimateGas")] + async fn estimate_gas( + &self, + request: TransactionRequest, + block_number: Option, + state_override: Option, + ) -> RpcResult; +} + +pub struct CallForwarderExt { + client: HttpClient, +} + +impl CallForwarderExt { + pub fn new(upstream_rpc_url: String) -> Self { + let client = + HttpClientBuilder::default().build(upstream_rpc_url).expect("Failed to build client"); + + Self { client } + } +} + +#[async_trait] +impl CallForwarderApiServer for CallForwarderExt { + async fn call( + &self, + request: TransactionRequest, + block_number: Option, + state_overrides: Option, + block_overrides: Option>, + ) -> RpcResult { + let result = self + .client + .clone() + .request( + "eth_call", + rpc_params![request, block_number, state_overrides, block_overrides], + ) + .await + .map_err(|e| match e { + ClientError::Call(e) => e, + _ => ErrorObject::owned( + INTERNAL_ERROR_CODE, + format!("Failed to call: {e:?}"), + Some(()), + ), + })?; + Ok(result) + } + + async fn estimate_gas( + &self, + request: TransactionRequest, + block_number: Option, + state_override: Option, + ) -> RpcResult { + let result = self + .client + .clone() + .request("eth_estimateGas", rpc_params![request, block_number, state_override]) + .await + .map_err(|e| match e { + ClientError::Call(e) => e, + _ => ErrorObject::owned( + INTERNAL_ERROR_CODE, + format!("Failed to estimate gas: {e:?}"), + Some(()), + ), + })?; + Ok(result) + } +} diff --git a/src/lib.rs b/src/lib.rs index df6fa7c63..1bc2958b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,5 +6,6 @@ pub mod hl_node_compliance; pub mod node; pub mod pseudo_peer; pub mod tx_forwarder; +pub mod call_forwarder; pub use node::primitives::{HlBlock, HlBlockBody, HlPrimitives}; diff --git a/src/main.rs b/src/main.rs index 5b9cb54ad..8dbfd516e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use clap::Parser; use reth::builder::NodeHandle; use reth_hl::{ + call_forwarder::{self, CallForwarderApiServer}, chainspec::parser::HlChainSpecParser, hl_node_compliance::install_hl_node_compliance, node::{ @@ -42,6 +43,13 @@ fn main() -> eyre::Result<()> { )?; info!("Transaction will be forwarded to {}", upstream_rpc_url); + if ext.forward_call { + ctx.modules.replace_configured( + call_forwarder::CallForwarderExt::new(upstream_rpc_url.clone()).into_rpc(), + )?; + info!("Call/gas estimation will be forwarded to {}", upstream_rpc_url); + } + if ext.hl_node_compliant { install_hl_node_compliance(ctx)?; info!("hl-node compliant mode enabled"); diff --git a/src/node/cli.rs b/src/node/cli.rs index aeeda5610..b7843ccf1 100644 --- a/src/node/cli.rs +++ b/src/node/cli.rs @@ -46,6 +46,12 @@ pub struct HlNodeArgs { /// 3. filters out logs and transactions from subscription. #[arg(long, env = "HL_NODE_COMPLIANT")] pub hl_node_compliant: bool, + + /// Forward eth_call and eth_estimateGas to the upstream RPC. + /// + /// This is useful when read precompile is needed for gas estimation. + #[arg(long, env = "FORWARD_CALL")] + pub forward_call: bool, } /// The main reth_hl cli interface.