//! Helpers for optimism specific RPC implementations. use std::sync::{atomic::AtomicUsize, Arc}; use jsonrpsee_types::error::{ErrorObject, INTERNAL_ERROR_CODE}; use reqwest::Client; use reth_rpc_eth_api::RawTransactionForwarder; use reth_rpc_eth_types::error::{EthApiError, EthResult}; use reth_rpc_types::ToRpcError; /// Error type when interacting with the Sequencer #[derive(Debug, thiserror::Error)] pub enum SequencerRpcError { /// Wrapper around an [`reqwest::Error`]. #[error(transparent)] HttpError(#[from] reqwest::Error), /// Thrown when serializing transaction to forward to sequencer #[error("invalid sequencer transaction")] InvalidSequencerTransaction, } impl ToRpcError for SequencerRpcError { fn to_rpc_error(&self) -> ErrorObject<'static> { ErrorObject::owned(INTERNAL_ERROR_CODE, self.to_string(), None::) } } impl From for EthApiError { fn from(err: SequencerRpcError) -> Self { Self::other(err) } } /// A client to interact with a Sequencer #[derive(Debug, Clone)] pub struct SequencerClient { inner: Arc, } impl SequencerClient { /// Creates a new [`SequencerClient`]. pub fn new(sequencer_endpoint: impl Into) -> Self { let client = Client::builder().use_rustls_tls().build().unwrap(); Self::with_client(sequencer_endpoint, client) } /// Creates a new [`SequencerClient`]. pub fn with_client(sequencer_endpoint: impl Into, http_client: Client) -> Self { let inner = SequencerClientInner { sequencer_endpoint: sequencer_endpoint.into(), http_client, id: AtomicUsize::new(0), }; Self { inner: Arc::new(inner) } } /// Returns the network of the client pub fn endpoint(&self) -> &str { &self.inner.sequencer_endpoint } /// Returns the client pub fn http_client(&self) -> &Client { &self.inner.http_client } /// Returns the next id for the request fn next_request_id(&self) -> usize { self.inner.id.fetch_add(1, std::sync::atomic::Ordering::SeqCst) } /// Forwards a transaction to the sequencer endpoint. pub async fn forward_raw_transaction(&self, tx: &[u8]) -> Result<(), SequencerRpcError> { let body = serde_json::to_string(&serde_json::json!({ "jsonrpc": "2.0", "method": "eth_sendRawTransaction", "params": [format!("0x{}", reth_primitives::hex::encode(tx))], "id": self.next_request_id() })) .map_err(|_| { tracing::warn!( target = "rpc::eth", "Failed to serialize transaction for forwarding to sequencer" ); SequencerRpcError::InvalidSequencerTransaction })?; self.http_client() .post(self.endpoint()) .header(reqwest::header::CONTENT_TYPE, "application/json") .body(body) .send() .await .inspect_err(|err| { tracing::warn!( target = "rpc::eth", %err, "Failed to forward transaction to sequencer", ); }) .map_err(SequencerRpcError::HttpError)?; Ok(()) } } #[async_trait::async_trait] impl RawTransactionForwarder for SequencerClient { async fn forward_raw_transaction(&self, tx: &[u8]) -> EthResult<()> { Self::forward_raw_transaction(self, tx).await?; Ok(()) } } #[derive(Debug, Default)] struct SequencerClientInner { /// The endpoint of the sequencer sequencer_endpoint: String, /// The HTTP client http_client: Client, /// Keeps track of unique request ids id: AtomicUsize, }