diff --git a/src/tasks/submit/flashbots/provider.rs b/src/tasks/submit/flashbots/provider.rs index 2b70062c..8f8c4bd2 100644 --- a/src/tasks/submit/flashbots/provider.rs +++ b/src/tasks/submit/flashbots/provider.rs @@ -1,16 +1,20 @@ //! A generic Flashbots bundle API wrapper. +use std::borrow::Cow; + use crate::config::BuilderConfig; use alloy::{ primitives::{BlockNumber, keccak256}, - rpc::types::mev::{EthBundleHash, MevSendBundle, SimBundleResponse}, + rpc::{ + json_rpc::{Id, Response, ResponsePayload, RpcRecv, RpcSend}, + types::mev::{EthBundleHash, MevSendBundle, SimBundleResponse}, + }, signers::Signer, }; -use eyre::Context as _; use init4_bin_base::utils::signer::LocalOrAws; use reqwest::header::CONTENT_TYPE; use serde_json::json; -/// A wrapper over a `Provider` that adds Flashbots MEV bundle helpers. +/// An RPC connection to relevant Flashbots endpoints. #[derive(Debug)] pub struct Flashbots { /// The base URL for the Flashbots API. @@ -34,20 +38,13 @@ impl Flashbots { } /// Sends a bundle via `mev_sendBundle`. - pub async fn send_bundle(&self, bundle: MevSendBundle) -> eyre::Result { - let params = serde_json::to_value(bundle)?; - let v = self.raw_call("mev_sendBundle", params).await?; - let hash: EthBundleHash = - serde_json::from_value(v.get("result").cloned().unwrap_or(serde_json::Value::Null))?; - Ok(hash) + pub async fn send_bundle(&self, bundle: &MevSendBundle) -> eyre::Result { + self.raw_call("mev_sendBundle", &[bundle]).await } /// Simulate a bundle via `mev_simBundle`. - pub async fn simulate_bundle(&self, bundle: MevSendBundle) -> eyre::Result<()> { - let params = serde_json::to_value(bundle)?; - let v = self.raw_call("mev_simBundle", params).await?; - let resp: SimBundleResponse = - serde_json::from_value(v.get("result").cloned().unwrap_or(serde_json::Value::Null))?; + pub async fn simulate_bundle(&self, bundle: &MevSendBundle) -> eyre::Result<()> { + let resp: SimBundleResponse = self.raw_call("mev_simBundle", &[bundle]).await?; dbg!("successfully simulated bundle", &resp); Ok(()) } @@ -55,27 +52,29 @@ impl Flashbots { /// Fetches the bundle status by hash pub async fn bundle_status( &self, - _hash: EthBundleHash, + hash: EthBundleHash, block_number: BlockNumber, ) -> eyre::Result<()> { - let params = json!({ "bundleHash": _hash, "blockNumber": block_number }); - let _ = self.raw_call("flashbots_getBundleStatsV2", params).await?; + let params = json!({ "bundleHash": hash, "blockNumber": block_number }); + let _resp: serde_json::Value = + self.raw_call("flashbots_getBundleStatsV2", &[params]).await?; + Ok(()) } /// Makes a raw JSON-RPC call with the Flashbots signature header to the method with the given params. - async fn raw_call( + async fn raw_call( &self, method: &str, - params: serde_json::Value, - ) -> eyre::Result { - let params = match params { - serde_json::Value::Array(_) => params, - other => serde_json::Value::Array(vec![other]), - }; - - let body = json!({"jsonrpc":"2.0","id":1,"method":method,"params":params}); - let body_bz = serde_json::to_vec(&body)?; + params: &Params, + ) -> eyre::Result { + let req = alloy::rpc::json_rpc::Request::new( + Cow::Owned(method.to_string()), + Id::Number(1), + params, + ); + let body_bz = serde_json::to_vec(&req)?; + drop(req); let value = self.compute_signature(&body_bz).await?; @@ -88,18 +87,19 @@ impl Flashbots { .send() .await?; - let text = resp.text().await?; - let v: serde_json::Value = - serde_json::from_str(&text).wrap_err("failed to parse flashbots JSON")?; - if let Some(err) = v.get("error") { - eyre::bail!("flashbots error: {err}"); + let resp: Response = resp.json().await?; + + match resp.payload { + ResponsePayload::Success(payload) => Ok(payload), + ResponsePayload::Failure(err) => { + eyre::bail!("flashbots error: {err}"); + } } - Ok(v) } /// Builds an EIP-191 signature for the given body bytes. async fn compute_signature(&self, body_bz: &[u8]) -> Result { - let payload = format!("0x{:x}", keccak256(body_bz)); + let payload = keccak256(body_bz).to_string(); let signature = self.signer.sign_message(payload.as_ref()).await?; dbg!(signature.to_string()); let address = self.signer.address(); diff --git a/src/tasks/submit/flashbots/task.rs b/src/tasks/submit/flashbots/task.rs index 002e2107..74aef094 100644 --- a/src/tasks/submit/flashbots/task.rs +++ b/src/tasks/submit/flashbots/task.rs @@ -236,15 +236,13 @@ impl FlashbotsTask { }; // simulate the bundle against Flashbots then send the bundle - if let Err(err) = - flashbots.simulate_bundle(bundle.clone()).instrument(span.clone()).await - { + if let Err(err) = flashbots.simulate_bundle(&bundle).instrument(span.clone()).await { span_scoped!(span, debug!(%err, "bundle simulation failed")); continue; } let _ = flashbots - .send_bundle(bundle) + .send_bundle(&bundle) .instrument(span.clone()) .await .inspect(|bundle_hash| { diff --git a/tests/flashbots_provider_test.rs b/tests/flashbots_provider_test.rs index fe76d528..41258c2c 100644 --- a/tests/flashbots_provider_test.rs +++ b/tests/flashbots_provider_test.rs @@ -40,7 +40,7 @@ async fn test_simulate_valid_bundle_sepolia() { dbg!("submitting bundle with 1 tx", &bundle_body); let bundle = MevSendBundle::new(latest_block, Some(0), ProtocolVersion::V0_1, bundle_body); - flashbots.simulate_bundle(bundle).await.expect("failed to simulate bundle"); + flashbots.simulate_bundle(&bundle).await.expect("failed to simulate bundle"); } async fn get_test_provider() -> Flashbots {