diff --git a/Cargo.toml b/Cargo.toml index f5f6ded..941c076 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,6 @@ aws-config = { version = "1.1.7", optional = true } aws-sdk-kms = { version = "1.15.0", optional = true } reqwest = { version = "0.12.15", optional = true } rustls = { version = "0.23.31", optional = true } -serde_json = { version = "1.0.145", optional = true } [dev-dependencies] ajj = "0.3.1" @@ -72,7 +71,6 @@ tokio = { version = "1.43.0", features = ["macros"] } [features] default = ["alloy", "rustls"] alloy = ["dep:alloy"] -flashbots = ["alloy", "aws", "alloy?/json-rpc", "dep:eyre", "dep:reqwest", "dep:serde_json"] aws = ["alloy", "alloy?/signer-aws", "dep:async-trait", "dep:aws-config", "dep:aws-sdk-kms"] perms = ["dep:oauth2", "dep:tokio", "dep:reqwest", "dep:signet-tx-cache", "dep:eyre", "dep:axum", "dep:tower"] rustls = ["dep:rustls", "rustls/aws-lc-rs"] diff --git a/src/lib.rs b/src/lib.rs index 6ef6a7d..57d7052 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,10 +22,6 @@ pub mod utils { /// slot. pub mod calc; - /// A simple interface to flashbots RPC endpoints. - #[cfg(feature = "flashbots")] - pub mod flashbots; - /// [`FromEnv`], [`FromEnvVar`] traits and related utilities. /// /// [`FromEnv`]: from_env::FromEnv diff --git a/src/utils/flashbots.rs b/src/utils/flashbots.rs deleted file mode 100644 index 9d8cc11..0000000 --- a/src/utils/flashbots.rs +++ /dev/null @@ -1,147 +0,0 @@ -//! A generic Flashbots bundle API wrapper. -use crate::utils::signer::LocalOrAws; -use alloy::{ - primitives::{keccak256, BlockNumber}, - rpc::{ - json_rpc::{Id, Response, ResponsePayload, RpcRecv, RpcSend}, - types::mev::{EthBundleHash, MevSendBundle, SimBundleResponse}, - }, - signers::Signer, -}; -use init4_from_env_derive::FromEnv; -use reqwest::header::CONTENT_TYPE; -use serde_json::json; -use std::borrow::Cow; - -/// Configuration for the Flashbots provider. -#[derive(Debug, Clone, FromEnv)] -#[from_env(crate)] -pub struct FlashbotsConfig { - /// Flashbots endpoint for privately submitting rollup blocks. - #[from_env( - var = "FLASHBOTS_ENDPOINT", - desc = "Flashbots endpoint for privately submitting rollup blocks", - optional - )] - pub flashbots_endpoint: Option, -} - -impl FlashbotsConfig { - /// Make a [`Flashbots`] instance from this config, using the specified signer. - pub fn build(&self, signer: LocalOrAws) -> Option { - self.flashbots_endpoint - .as_ref() - .map(|url| Flashbots::new(url.clone(), signer)) - } -} - -/// A basic provider for common Flashbots Relay endpoints. -#[derive(Debug)] -pub struct Flashbots { - /// The base URL for the Flashbots API. - pub relay_url: url::Url, - - /// Signer is loaded once at startup. - signer: LocalOrAws, - - /// The reqwest client to use for requests. - client: reqwest::Client, -} - -impl Flashbots { - /// Instantiate a new provider from the URL and signer. - pub fn new(relay_url: url::Url, signer: LocalOrAws) -> Self { - Self { - relay_url, - client: Default::default(), - signer, - } - } - - /// Instantiate a new provider from the URL and signer, with a specific - /// Reqwest client. - pub const fn new_with_client( - relay_url: url::Url, - signer: LocalOrAws, - client: reqwest::Client, - ) -> Self { - Self { - relay_url, - client, - signer, - } - } - - /// Sends a bundle via `mev_sendBundle`. - 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 resp: SimBundleResponse = self.raw_call("mev_simBundle", &[bundle]).await?; - dbg!("successfully simulated bundle", &resp); - Ok(()) - } - - /// Fetch the bundle status by hash. - pub async fn bundle_status( - &self, - hash: EthBundleHash, - block_number: BlockNumber, - ) -> eyre::Result<()> { - let params = json!({ "bundleHash": hash, "blockNumber": block_number }); - let _resp: serde_json::Value = self - .raw_call("flashbots_getBundleStatsV2", &[params]) - .await?; - - Ok(()) - } - - /// Make a raw JSON-RPC call with the Flashbots signature header to the - /// method with the given params. - async fn raw_call( - &self, - method: &str, - 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?; - - let resp = self - .client - .post(self.relay_url.as_str()) - .header(CONTENT_TYPE, "application/json") - .header("X-Flashbots-Signature", value) - .body(body_bz) - .send() - .await?; - - let resp: Response = resp.json().await?; - - match resp.payload { - ResponsePayload::Success(payload) => Ok(payload), - ResponsePayload::Failure(err) => { - eyre::bail!("flashbots error: {err}"); - } - } - } - - /// Builds an EIP-191 signature for the given body bytes. This signature is - /// used to authenticate to the relay API via a header - async fn compute_signature(&self, body_bz: &[u8]) -> Result { - 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(); - let value = format!("{address}:{signature}"); - Ok(value) - } -} diff --git a/tests/flashbots.rs b/tests/flashbots.rs deleted file mode 100644 index e5bb202..0000000 --- a/tests/flashbots.rs +++ /dev/null @@ -1,96 +0,0 @@ -#![cfg(feature = "flashbots")] - -use alloy::{ - eips::Encodable2718, - network::EthereumWallet, - primitives::{B256, U256}, - providers::{ - fillers::{ - BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, - WalletFiller, - }, - Identity, Provider, ProviderBuilder, SendableTx, - }, - rpc::types::{ - mev::{BundleItem, MevSendBundle, ProtocolVersion}, - TransactionRequest, - }, - signers::{local::PrivateKeySigner, Signer}, -}; -use init4_bin_base::utils::{flashbots::Flashbots, signer::LocalOrAws}; -use std::sync::LazyLock; -use url::Url; - -static FLASHBOTS_URL: LazyLock = LazyLock::new(|| { - Url::parse("https://relay-sepolia.flashbots.net:443").expect("valid flashbots url") -}); -static BUILDER_KEY: LazyLock = LazyLock::new(|| { - LocalOrAws::Local(PrivateKeySigner::from_bytes(&B256::repeat_byte(0x02)).unwrap()) -}); -static TEST_PROVIDER: LazyLock = LazyLock::new(get_test_provider); - -fn get_test_provider() -> Flashbots { - Flashbots::new(FLASHBOTS_URL.clone(), BUILDER_KEY.clone()) -} - -#[allow(clippy::type_complexity)] -fn get_sepolia() -> FillProvider< - JoinFill< - JoinFill< - Identity, - JoinFill>>, - >, - WalletFiller, - >, - alloy::providers::RootProvider, -> { - ProviderBuilder::new() - .wallet(BUILDER_KEY.clone()) - .connect_http( - "https://ethereum-sepolia-rpc.publicnode.com" - .parse() - .unwrap(), - ) -} - -#[tokio::test] -#[ignore = "integration test"] -async fn test_simulate_valid_bundle_sepolia() { - let flashbots = &*TEST_PROVIDER; - let sepolia = get_sepolia(); - - let req = TransactionRequest::default() - .to(BUILDER_KEY.address()) - .value(U256::from(1u64)) - .gas_limit(51_000) - .from(BUILDER_KEY.address()); - let SendableTx::Envelope(tx) = sepolia.fill(req).await.unwrap() else { - panic!("expected filled tx"); - }; - let tx_bytes = tx.encoded_2718().into(); - - let latest_block = sepolia - .get_block_by_number(alloy::eips::BlockNumberOrTag::Latest) - .await - .unwrap() - .unwrap() - .number(); - - let bundle_body = vec![BundleItem::Tx { - tx: tx_bytes, - can_revert: true, - }]; - let bundle = MevSendBundle::new(latest_block, Some(0), ProtocolVersion::V0_1, bundle_body); - - let err = flashbots - .simulate_bundle(&bundle) - .await - .unwrap_err() - .to_string(); - // If we have hit this point, we have succesfully authed to the flashbots - // api via header - assert!( - err.contains("insufficient funds for gas"), - "unexpected error: {err}" - ); -}