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}