ethers_flashbots/
middleware.rs

1use crate::{
2    bundle::{BundleHash, BundleRequest, BundleStats, SimulatedBundle},
3    pending_bundle::PendingBundle,
4    relay::{GetBundleStatsParams, GetUserStatsParams, Relay, RelayError, SendBundleResponse},
5    UserStats,
6};
7use async_trait::async_trait;
8use ethers::{
9    core::{
10        types::{BlockNumber, Bytes, U64},
11        utils::keccak256,
12    },
13    providers::{Middleware, MiddlewareError, PendingTransaction},
14    signers::Signer,
15};
16use futures_util::future;
17use thiserror::Error;
18use url::Url;
19
20/// Errors for the Flashbots middleware.
21#[derive(Error, Debug)]
22pub enum FlashbotsMiddlewareError<M: Middleware, S: Signer> {
23    /// Some parameters were missing.
24    ///
25    /// For bundle simulation, check that the following are set:
26    /// - `simulation_block`
27    /// - `simulation_timestamp`
28    /// - `block`
29    ///
30    /// For bundle submission, check that the following are set:
31    /// - `block`
32    ///
33    /// Additionally, `min_timestamp` and `max_timestamp` must
34    /// both be set or unset.
35    #[error("Some parameters were missing")]
36    MissingParameters,
37    /// The relay responded with an error.
38    #[error(transparent)]
39    RelayError(#[from] RelayError<S>),
40    /// An error occured in one of the middlewares.
41    #[error("{0}")]
42    MiddlewareError(M::Error),
43    /// Empty data for bundle simulation request.
44    #[error("Bundle simulation is not available")]
45    BundleSimError,
46    /// Empty data for bundle stats request.
47    #[error("Bundle stats are not available")]
48    BundleStatsError,
49    /// Empty data for user stats request.
50    #[error("User stats are not available")]
51    UserStatsError,
52}
53
54impl<M: Middleware, S: Signer> MiddlewareError for FlashbotsMiddlewareError<M, S> {
55    type Inner = M::Error;
56
57    fn from_err(src: M::Error) -> FlashbotsMiddlewareError<M, S> {
58        FlashbotsMiddlewareError::MiddlewareError(src)
59    }
60
61    fn as_inner(&self) -> Option<&Self::Inner> {
62        match self {
63            FlashbotsMiddlewareError::MiddlewareError(e) => Some(e),
64            _ => None,
65        }
66    }
67}
68
69/// A middleware used to send bundles to a Flashbots relay.
70///
71/// **NOTE**: This middleware does **NOT** sign your transactions. Use
72/// another method to sign your transactions, and then forward the signed
73/// transactions to the middleware.
74///
75/// You can either send custom bundles (see [`BundleRequest`]) or send
76/// transactions as you normally would (see [`Middleware::send_transaction`]) from
77/// another middleware.
78///
79/// If you use [`Middleware::send_transaction`] then a bundle will be constructed
80/// for you with the following assumptions:
81///
82/// - You do not want to allow the transaction to revert
83/// - You do not care to set a minimum or maximum timestamp for the bundle
84/// - The block you are targetting with your bundle is the next block
85/// - You do not want to simulate the bundle before sending to the relay
86///
87/// # Example
88/// ```
89/// use ethers::prelude::*;
90/// use std::convert::TryFrom;
91/// use ethers_flashbots::FlashbotsMiddleware;
92/// use url::Url;
93///
94/// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
95/// let provider = Provider::<Http>::try_from("http://localhost:8545")
96///     .expect("Could not instantiate HTTP provider");
97///
98/// // Used to sign Flashbots relay requests - this is your searcher identity
99/// let signer: LocalWallet = "380eb0f3d505f087e438eca80bc4df9a7faa24f868e69fc0440261a0fc0567dc"
100///     .parse()?;
101///
102/// // Used to sign transactions
103/// let wallet: LocalWallet = "380eb0f3d505f087e438eca80bc4df9a7faa24f868e69fc0440261a0fc0567dc"
104///     .parse()?;
105///
106/// // Note: The order is important! You want the signer
107/// // middleware to sign your transactions *before* they
108/// // are sent to your Flashbots middleware.
109/// let mut client = SignerMiddleware::new(
110///     FlashbotsMiddleware::new(
111///         provider,
112///         Url::parse("https://relay.flashbots.net")?,
113///         signer
114///     ),
115///     wallet
116/// );
117///
118/// // This transaction will now be send as a Flashbots bundle!
119/// let tx = TransactionRequest::pay("vitalik.eth", 100);
120/// let pending_tx = client.send_transaction(tx, None).await?;
121/// # Ok(())
122/// # }
123/// ```
124#[derive(Debug)]
125pub struct FlashbotsMiddleware<M, S> {
126    inner: M,
127    relay: Relay<S>,
128    simulation_relay: Option<Relay<S>>,
129}
130
131impl<M: Middleware, S: Signer> FlashbotsMiddleware<M, S> {
132    /// Initialize a new Flashbots middleware.
133    ///
134    /// The signer is used to sign requests to the relay.
135    pub fn new(inner: M, relay_url: impl Into<Url>, relay_signer: S) -> Self {
136        Self {
137            inner,
138            relay: Relay::new(relay_url, Some(relay_signer)),
139            simulation_relay: None,
140        }
141    }
142
143    /// Get the relay client used by the middleware.
144    pub fn relay(&self) -> &Relay<S> {
145        &self.relay
146    }
147
148    /// Get the relay client used by the middleware to simulate
149    /// bundles if set.
150    pub fn simulation_relay(&self) -> Option<&Relay<S>> {
151        self.simulation_relay.as_ref()
152    }
153
154    /// Set a separate relay to use for simulating bundles.
155    ///
156    /// This can either be a full Flashbots relay or a node that implements
157    /// the `eth_callBundle` remote procedure call.
158    pub fn set_simulation_relay(&mut self, relay_url: impl Into<Url>) {
159        self.simulation_relay = Some(Relay::new(relay_url, None));
160    }
161
162    /// Simulate a bundle.
163    ///
164    /// See [`eth_callBundle`][fb_callBundle] for more information.
165    ///
166    /// [fb_callBundle]: https://docs.flashbots.net/flashbots-auction/searchers/advanced/rpc-endpoint#eth_callbundle
167    pub async fn simulate_bundle(
168        &self,
169        bundle: &BundleRequest,
170    ) -> Result<SimulatedBundle, FlashbotsMiddlewareError<M, S>> {
171        bundle
172            .block()
173            .and(bundle.simulation_block())
174            .and(bundle.simulation_timestamp())
175            .ok_or(FlashbotsMiddlewareError::MissingParameters)?;
176
177        self.simulation_relay
178            .as_ref()
179            .unwrap_or(&self.relay)
180            .request("eth_callBundle", [bundle])
181            .await
182            .map_err(FlashbotsMiddlewareError::RelayError)?
183            .ok_or(FlashbotsMiddlewareError::BundleSimError)
184    }
185
186    /// Send a bundle to the relayer.
187    ///
188    /// See [`eth_sendBundle`][fb_sendBundle] for more information.
189    ///
190    /// [fb_sendBundle]: https://docs.flashbots.net/flashbots-auction/searchers/advanced/rpc-endpoint#eth_sendbundle
191    pub async fn send_bundle(
192        &self,
193        bundle: &BundleRequest,
194    ) -> Result<PendingBundle<'_, <Self as Middleware>::Provider>, FlashbotsMiddlewareError<M, S>>
195    {
196        // The target block must be set
197        bundle
198            .block()
199            .ok_or(FlashbotsMiddlewareError::MissingParameters)?;
200
201        // `min_timestamp` and `max_timestamp` must both either be unset or set.
202        if bundle.min_timestamp().xor(bundle.max_timestamp()).is_some() {
203            return Err(FlashbotsMiddlewareError::MissingParameters);
204        }
205
206        let response: Option<SendBundleResponse> = self
207            .relay
208            .request("eth_sendBundle", [bundle])
209            .await
210            .map_err(FlashbotsMiddlewareError::RelayError)?;
211
212        match response {
213            Some(r) => Ok(PendingBundle::new(
214                r.bundle_hash,
215                bundle.block().unwrap(),
216                bundle.transaction_hashes(),
217                self.provider(),
218            )),
219            None => Ok(PendingBundle::new(
220                None,
221                bundle.block().unwrap(),
222                bundle.transaction_hashes(),
223                self.provider(),
224            )),
225        }
226    }
227
228    /// Get stats for a particular bundle.
229    pub async fn get_bundle_stats(
230        &self,
231        bundle_hash: BundleHash,
232        block_number: U64,
233    ) -> Result<BundleStats, FlashbotsMiddlewareError<M, S>> {
234        self.relay
235            .request(
236                "flashbots_getBundleStatsV2",
237                [GetBundleStatsParams {
238                    bundle_hash,
239                    block_number,
240                }],
241            )
242            .await
243            .map_err(FlashbotsMiddlewareError::RelayError)?
244            .ok_or(FlashbotsMiddlewareError::BundleStatsError)
245    }
246
247    /// Get stats for your searcher identity.
248    ///
249    /// Your searcher identity is determined by the signer you
250    /// constructed the middleware with.
251    pub async fn get_user_stats(&self) -> Result<UserStats, FlashbotsMiddlewareError<M, S>> {
252        let latest_block = self
253            .inner
254            .get_block_number()
255            .await
256            .map_err(FlashbotsMiddlewareError::MiddlewareError)?;
257
258        self.relay
259            .request(
260                "flashbots_getUserStatsV2",
261                [GetUserStatsParams {
262                    block_number: latest_block,
263                }],
264            )
265            .await
266            .map_err(FlashbotsMiddlewareError::RelayError)?
267            .ok_or(FlashbotsMiddlewareError::UserStatsError)
268    }
269}
270
271#[async_trait]
272impl<M, S> Middleware for FlashbotsMiddleware<M, S>
273where
274    M: Middleware,
275    S: Signer,
276{
277    type Error = FlashbotsMiddlewareError<M, S>;
278    type Provider = M::Provider;
279    type Inner = M;
280
281    fn inner(&self) -> &M {
282        &self.inner
283    }
284
285    async fn send_raw_transaction<'a>(
286        &'a self,
287        tx: Bytes,
288    ) -> Result<PendingTransaction<'a, Self::Provider>, Self::Error> {
289        let tx_hash = keccak256(&tx);
290
291        // Get the latest block
292        let latest_block = self
293            .inner
294            .get_block(BlockNumber::Latest)
295            .await
296            .map_err(FlashbotsMiddlewareError::MiddlewareError)?
297            .expect("The latest block is pending (this should not happen)");
298
299        // Construct the bundle, assuming that the target block is the
300        // next block.
301        let bundle = BundleRequest::new().push_transaction(tx.clone()).set_block(
302            latest_block
303                .number
304                .expect("The latest block is pending (this should not happen)")
305                + 1,
306        );
307
308        self.send_bundle(&bundle).await?;
309
310        Ok(PendingTransaction::new(tx_hash.into(), self.provider())
311            .interval(self.provider().get_interval()))
312    }
313}
314
315/// A middleware used to broadcast bundles to multiple builders.
316///
317/// **NOTE**: This middleware does **NOT** sign your transactions. Use
318/// another method to sign your transactions, and then forward the signed
319/// transactions to the middleware.
320///
321/// You can either send custom bundles (see [`BundleRequest`]) or send
322/// transactions as you normally would (see [`Middleware::send_transaction`]) from
323/// another middleware.
324///
325/// If you use [`Middleware::send_transaction`] then a bundle will be constructed
326/// for you with the following assumptions:
327///
328/// - You do not want to allow the transaction to revert
329/// - You do not care to set a minimum or maximum timestamp for the bundle
330/// - The block you are targetting with your bundle is the next block
331/// - You do not want to simulate the bundle before sending to the builder
332///
333/// # Example
334/// ```
335/// use ethers::prelude::*;
336/// use std::convert::TryFrom;
337/// use ethers_flashbots::BroadcasterMiddleware;
338/// use url::Url;
339///
340/// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
341/// let provider = Provider::<Http>::try_from("http://localhost:8545")
342///     .expect("Could not instantiate HTTP provider");
343///
344/// // Used to sign Flashbots relay requests - this is your searcher identity
345/// let signer: LocalWallet = "380eb0f3d505f087e438eca80bc4df9a7faa24f868e69fc0440261a0fc0567dc"
346///     .parse()?;
347///
348/// // Used to sign transactions
349/// let wallet: LocalWallet = "380eb0f3d505f087e438eca80bc4df9a7faa24f868e69fc0440261a0fc0567dc"
350///     .parse()?;
351///
352/// // Note: The order is important! You want the signer
353/// // middleware to sign your transactions *before* they
354/// // are sent to your Flashbots middleware.
355/// let mut client = SignerMiddleware::new(
356///     BroadcasterMiddleware::new(
357///         provider,
358///         vec![Url::parse("https://rpc.titanbuilder.xyz")?, Url::parse("https://relay.flashbots.net")?],
359///         Url::parse("https://relay.flashbots.net")?,
360///         signer
361///     ),
362///     wallet
363/// );
364///
365/// // This transaction will now be sent as a Flashbots bundle!
366/// let tx = TransactionRequest::pay("vitalik.eth", 100);
367/// let pending_tx = client.send_transaction(tx, None).await?;
368/// # Ok(())
369/// # }
370/// ```
371#[derive(Debug)]
372pub struct BroadcasterMiddleware<M, S> {
373    inner: M,
374    relays: Vec<Relay<S>>,
375    simulation_relay: Relay<S>,
376}
377
378impl<M: Middleware, S: Signer> BroadcasterMiddleware<M, S> {
379    /// Initialize a new Flashbots middleware.
380    ///
381    /// The signer is used to sign requests to the relay.
382    pub fn new(
383        inner: M,
384        relay_urls: Vec<Url>,
385        simulation_relay: impl Into<Url>,
386        relay_signer: S,
387    ) -> Self
388    where
389        S: Clone,
390    {
391        Self {
392            inner,
393            relays: relay_urls
394                .into_iter()
395                .map(|r| Relay::new(r, Some(relay_signer.clone())))
396                .collect(),
397            simulation_relay: Relay::new(simulation_relay, Some(relay_signer)),
398        }
399    }
400
401    /// Get the relay client used by the middleware.
402    pub fn relay(&self) -> &Vec<Relay<S>> {
403        &self.relays
404    }
405
406    /// Get the relay client used by the middleware to simulate
407    /// bundles.
408    pub fn simulation_relay(&self) -> &Relay<S> {
409        &self.simulation_relay
410    }
411
412    /// Simulate a bundle.
413    ///
414    /// See [`eth_callBundle`][fb_callBundle] for more information.
415    ///
416    /// [fb_callBundle]: https://docs.flashbots.net/flashbots-auction/searchers/advanced/rpc-endpoint#eth_callbundle
417    pub async fn simulate_bundle(
418        &self,
419        bundle: &BundleRequest,
420    ) -> Result<SimulatedBundle, FlashbotsMiddlewareError<M, S>> {
421        bundle
422            .block()
423            .and(bundle.simulation_block())
424            .and(bundle.simulation_timestamp())
425            .ok_or(FlashbotsMiddlewareError::MissingParameters)?;
426
427        self.simulation_relay
428            .request("eth_callBundle", [bundle])
429            .await
430            .map_err(FlashbotsMiddlewareError::RelayError)?
431            .ok_or(FlashbotsMiddlewareError::BundleSimError)
432    }
433
434    /// Broadcast a bundle to the builders.
435    ///
436    /// See [`eth_sendBundle`][fb_sendBundle] for more information.
437    ///
438    /// [fb_sendBundle]: https://docs.flashbots.net/flashbots-auction/searchers/advanced/rpc-endpoint#eth_sendbundle
439    pub async fn send_bundle(
440        &self,
441        bundle: &BundleRequest,
442    ) -> Result<
443        Vec<
444            Result<
445                PendingBundle<'_, <Self as Middleware>::Provider>,
446                FlashbotsMiddlewareError<M, S>,
447            >,
448        >,
449        FlashbotsMiddlewareError<M, S>,
450    > {
451        // The target block must be set
452        bundle
453            .block()
454            .ok_or(FlashbotsMiddlewareError::MissingParameters)?;
455
456        let futures = self
457            .relays
458            .iter()
459            .map(|relay| async move {
460                let response = relay.request("eth_sendBundle", [bundle]).await;
461                response
462                    .map(|response: Option<SendBundleResponse>| match response {
463                        Some(r) => PendingBundle::new(
464                            r.bundle_hash,
465                            bundle.block().unwrap(),
466                            bundle.transaction_hashes(),
467                            self.provider(),
468                        ),
469                        None => PendingBundle::new(
470                            None,
471                            bundle.block().unwrap(),
472                            bundle.transaction_hashes(),
473                            self.provider(),
474                        ),
475                    })
476                    .map_err(FlashbotsMiddlewareError::RelayError)
477            })
478            .collect::<Vec<_>>();
479
480        let responses = future::join_all(futures).await;
481
482        Ok(responses)
483    }
484}
485
486#[async_trait]
487impl<M, S> Middleware for BroadcasterMiddleware<M, S>
488where
489    M: Middleware,
490    S: Signer,
491{
492    type Error = FlashbotsMiddlewareError<M, S>;
493    type Provider = M::Provider;
494    type Inner = M;
495
496    fn inner(&self) -> &M {
497        &self.inner
498    }
499
500    async fn send_raw_transaction<'a>(
501        &'a self,
502        tx: Bytes,
503    ) -> Result<PendingTransaction<'a, Self::Provider>, Self::Error> {
504        let tx_hash = keccak256(&tx);
505
506        // Get the latest block
507        let latest_block = self
508            .inner
509            .get_block(BlockNumber::Latest)
510            .await
511            .map_err(FlashbotsMiddlewareError::MiddlewareError)?
512            .expect("The latest block is pending (this should not happen)");
513
514        // Construct the bundle, assuming that the target block is the
515        // next block.
516        let bundle = BundleRequest::new().push_transaction(tx.clone()).set_block(
517            latest_block
518                .number
519                .expect("The latest block is pending (this should not happen)")
520                + 1,
521        );
522
523        self.send_bundle(&bundle).await?;
524
525        Ok(PendingTransaction::new(tx_hash.into(), self.provider())
526            .interval(self.provider().get_interval()))
527    }
528}