#![allow(missing_docs)] #[global_allocator] static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator(); mod block_ingest; mod call_forwarder; mod serialized; mod share_blocks; mod spot_meta; mod tx_forwarder; use std::{collections::BTreeMap, sync::Arc}; use block_ingest::BlockIngest; use call_forwarder::CallForwarderApiServer; use clap::{Args, Parser}; use reth::cli::Cli; use reth_ethereum_cli::chainspec::EthereumChainSpecParser; use reth_hyperliquid_types::PrecompilesCache; use reth_node_ethereum::EthereumNode; use share_blocks::ShareBlocksArgs; use tokio::sync::Mutex; use tracing::info; use tx_forwarder::EthForwarderApiServer; #[derive(Args, Debug, Clone)] struct HyperliquidExtArgs { /// Upstream RPC URL to forward incoming transactions. #[arg(long, default_value = "https://rpc.hyperliquid.xyz/evm")] pub upstream_rpc_url: String, /// Forward eth_call and eth_estimateGas to the upstream RPC. #[arg(long)] pub forward_call: bool, /// Enable hl-node compliant mode. /// /// This option /// 1. filters out system transactions from block transaction list. /// 2. filters out logs that are not from the block's transactions. /// 3. filters out logs and transactions from subscription. #[arg(long, default_value = "false")] pub hl_node_compliant: bool, /// Enable hlfs to backfill archive blocks #[command(flatten)] pub hlfs: ShareBlocksArgs, } fn main() { reth_cli_util::sigsegv_handler::install(); // Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided. if std::env::var_os("RUST_BACKTRACE").is_none() { std::env::set_var("RUST_BACKTRACE", "1"); } let precompiles_cache = PrecompilesCache::new(parking_lot::Mutex::new(BTreeMap::new())); let local_blocks_cache = Arc::new(Mutex::new(BTreeMap::new())); if let Err(err) = Cli::::parse().run( |builder, ext_args| async move { if ext_args.hl_node_compliant { info!(target: "reth::cli", "hl-node compliant mode enabled"); std::env::set_var("HL_NODE_COMPLIANT", "true"); } let ingest_dir = builder.config().ingest_dir.clone().expect("ingest dir not set"); let local_ingest_dir = builder.config().local_ingest_dir.clone(); info!(target: "reth::cli", "Launching node"); let handle = builder .node(EthereumNode::default()) .add_precompiles_cache(precompiles_cache.clone()) .extend_rpc_modules(move |ctx| { let upstream_rpc_url = ext_args.upstream_rpc_url; ctx.modules.replace_configured( tx_forwarder::EthForwarderExt::new(upstream_rpc_url.clone()).into_rpc(), )?; if ext_args.forward_call { ctx.modules.replace_configured( call_forwarder::CallForwarderExt::new(upstream_rpc_url.clone()) .into_rpc(), )?; } info!("Transaction forwarder extension enabled"); Ok(()) }) .launch() .await?; // start HLFS (serve + peer-backed backfill) using the node's network let hlfs = if ext_args.hlfs.share_blocks { let net = handle.node.network.clone(); Some( crate::share_blocks::ShareBlocks::start_with_network(&ext_args.hlfs, net) .await?, ) } else { None }; let ingest = BlockIngest { ingest_dir, local_ingest_dir, local_blocks_cache, precompiles_cache, hlfs, }; ingest.run(handle.node).await.unwrap(); handle.node_exit_future.await }, ) { eprintln!("Error: {err:?}"); std::process::exit(1); } }