kabu_broadcast_flashbots/client/
middleware.rs

1use alloy_json_rpc::RpcError;
2use alloy_network::Ethereum;
3use alloy_provider::Provider;
4use alloy_signer_local::PrivateKeySigner;
5use alloy_transport::TransportErrorKind;
6use eyre::Result;
7use thiserror::Error;
8use url::Url;
9
10use crate::client::SendBundleResponseType;
11use crate::{
12    client::bundle::{BundleRequest, SimulatedBundle},
13    client::relay::{Relay, RelayError},
14};
15
16/// Errors for the Flashbots middleware.
17#[derive(Debug, Error)]
18pub enum FlashbotsMiddlewareError {
19    /// Some parameters were missing.
20    ///
21    /// For bundle simulation, check that the following are set:
22    /// - `simulation_block`
23    /// - `simulation_timestamp`
24    /// - `block`
25    ///
26    /// For bundle submission, check that the following are set:
27    /// - `block`
28    ///
29    /// Additionally, `min_timestamp` and `max_timestamp` must
30    /// both be set or unset.
31    #[error("Some parameters were missing")]
32    MissingParameters,
33    /// The relay responded with an error.
34    #[error(transparent)]
35    RelayError(#[from] RelayError),
36    /// An error occured in one of the middlewares.
37    #[error(transparent)]
38    MiddlewareError(#[from] RpcError<TransportErrorKind>),
39}
40
41#[derive(Clone)]
42pub struct FlashbotsMiddleware<P> {
43    provider: P,
44    relay: Relay,
45    simulation_relay: Option<Relay>,
46}
47
48impl<P> FlashbotsMiddleware<P>
49where
50    P: Provider<Ethereum> + Send + Sync + Clone + 'static,
51{
52    /// Initialize a new Flashbots middleware.
53    ///
54    /// The signer is used to sign requests to the relay.
55    pub fn new(relay_url: impl Into<Url>, provider: P) -> Self {
56        Self { provider, relay: Relay::new(relay_url, Some(PrivateKeySigner::random())), simulation_relay: None }
57    }
58
59    pub fn new_no_signer(relay_url: impl Into<Url>, provider: P) -> Self {
60        Self { provider, relay: Relay::new(relay_url, None), simulation_relay: None }
61    }
62
63    /// Get the relay client used by the middleware.
64    pub fn relay(&self) -> &Relay {
65        &self.relay
66    }
67
68    /// Get the relay client used by the middleware to simulate
69    /// bundles if set.
70    pub fn simulation_relay(&self) -> Option<&Relay> {
71        self.simulation_relay.as_ref()
72    }
73
74    /// Set a separate relay to use for simulating bundles.
75    ///
76    /// This can either be a full Flashbots relay or a node that implements
77    /// the `eth_callBundle` remote procedure call.
78    pub fn set_simulation_relay(&mut self, relay_url: impl Into<Url>) {
79        self.simulation_relay = Some(Relay::new(relay_url, None));
80    }
81
82    /// Simulate a bundle.
83    ///
84    /// See [`eth_callBundle`][fb_callBundle] for more information.
85    ///
86    /// [fb_callBundle]: https://docs.flashbots.net/flashbots-auction/searchers/advanced/rpc-endpoint#eth_callbundle
87    pub async fn simulate_bundle(&self, bundle: &BundleRequest) -> Result<SimulatedBundle, FlashbotsMiddlewareError> {
88        bundle
89            .target_block()
90            .and(bundle.simulation_block())
91            .and(bundle.simulation_timestamp())
92            .ok_or(FlashbotsMiddlewareError::MissingParameters)?;
93
94        self.simulation_relay
95            .as_ref()
96            .unwrap_or(&self.relay)
97            .request("eth_callBundle", [bundle])
98            .await
99            .map_err(FlashbotsMiddlewareError::RelayError)
100    }
101
102    pub async fn simulate_local_bundle(&self, bundle: &BundleRequest) -> Result<SimulatedBundle, FlashbotsMiddlewareError> {
103        match self.provider.client().request("eth_callBundle", [bundle]).await {
104            Ok(result) => Ok(result),
105            Err(e) => Err(FlashbotsMiddlewareError::MiddlewareError(e)),
106        }
107    }
108
109    /// Send a bundle to the relayer.
110    ///
111    /// See [`eth_sendBundle`][fb_sendBundle] for more information.
112    ///
113    /// [fb_sendBundle]: https://docs.flashbots.net/flashbots-auction/searchers/advanced/rpc-endpoint#eth_sendbundle
114    pub async fn send_bundle(&self, bundle: &BundleRequest) -> Result<(), FlashbotsMiddlewareError> {
115        // The target block must be set
116        bundle.target_block().ok_or(FlashbotsMiddlewareError::MissingParameters)?;
117
118        // `min_timestamp` and `max_timestamp` must both either be unset or set.
119        if bundle.min_timestamp().xor(bundle.max_timestamp()).is_some() {
120            return Err(FlashbotsMiddlewareError::MissingParameters);
121        }
122
123        let _response: SendBundleResponseType =
124            self.relay.request("eth_sendBundle", [bundle]).await.map_err(FlashbotsMiddlewareError::RelayError)?;
125
126        Ok(())
127    }
128}