kabu_broadcast_flashbots/
flashbots.rs

1use crate::client::{
2    make_signed_body, BundleRequest, BundleTransaction, FlashbotsMiddleware, FlashbotsMiddlewareError, RelayConfig, SendBundleResponseType,
3    SimulatedBundle,
4};
5use alloy_network::Ethereum;
6use alloy_primitives::{TxHash, U64};
7use alloy_provider::Provider;
8use alloy_signer_local::PrivateKeySigner;
9use eyre::{eyre, Result};
10use std::sync::atomic::{AtomicU64, Ordering};
11use std::sync::Arc;
12use tracing::{debug, error, info};
13use url::Url;
14
15#[derive(Clone)]
16pub struct FlashbotsClient<T> {
17    pub flashbots_middleware: FlashbotsMiddleware<T>,
18    pub name: String,
19}
20
21impl<P> FlashbotsClient<P>
22where
23    P: Provider<Ethereum> + Send + Sync + Clone + 'static,
24{
25    pub fn new(provider: P, url: &str) -> Self {
26        let flashbots_middleware = Self::create_flashbots_middleware(provider, url);
27
28        let name = url.to_string();
29
30        FlashbotsClient { flashbots_middleware, name }
31    }
32
33    pub fn new_no_sign(provider: P, url: &str) -> Self {
34        let flashbots_client = FlashbotsClient::create_flashbots_no_signer_middleware(provider, url);
35
36        let name = url.to_string();
37
38        FlashbotsClient { flashbots_middleware: flashbots_client, name }
39    }
40
41    fn create_flashbots_middleware(provider: P, url: &str) -> FlashbotsMiddleware<P> {
42        let flashbots: FlashbotsMiddleware<P> = FlashbotsMiddleware::new(Url::parse(url).unwrap(), provider);
43
44        flashbots
45    }
46
47    fn create_flashbots_no_signer_middleware(provider: P, url: &str) -> FlashbotsMiddleware<P> {
48        let flashbots: FlashbotsMiddleware<P> = FlashbotsMiddleware::new_no_signer(Url::parse(url).unwrap(), provider);
49        flashbots
50    }
51
52    pub async fn call_bundle(&self, request: &BundleRequest) -> Result<SimulatedBundle> {
53        match self.flashbots_middleware.simulate_local_bundle(request).await {
54            Ok(x) => Ok(x),
55            Err(e) => {
56                error!("{}", e);
57                Err(eyre!("FLASHBOTS LOCAL ERROR"))
58            }
59        }
60    }
61
62    #[allow(dead_code)]
63    pub async fn send_bundle(&self, request: &BundleRequest) -> Result<()> {
64        match self.flashbots_middleware.send_bundle(request).await {
65            Ok(_resp) => {
66                info!("Bundle sent to : {}", self.name);
67                Ok(())
68            }
69            Err(error) => match error {
70                FlashbotsMiddlewareError::MissingParameters => {
71                    error!("{} : Missing paramter", self.name);
72                    Err(eyre!("FLASHBOTS_MISSING_PARAMETER"))
73                }
74                FlashbotsMiddlewareError::RelayError(x) => {
75                    error!("{} {}", self.name, x.to_string());
76                    Err(eyre!("FLASHBOTS_RELAY_ERROR"))
77                }
78                FlashbotsMiddlewareError::MiddlewareError(x) => {
79                    error!("{} {}", self.name, x.to_string());
80                    Err(eyre!("FLASHBOTS_MIDDLEWARE_ERROR"))
81                }
82            },
83        }
84    }
85
86    pub async fn send_signed_body(&self, body: String, signature: String) -> Result<()> {
87        match self.flashbots_middleware.relay().serialized_request::<SendBundleResponseType>(body, Some(signature)).await {
88            Ok(_resp) => {
89                debug!("Bundle sent to : {}", self.name);
90                Ok(())
91            }
92            Err(error) => {
93                error!("{} {}", self.name, error.to_string());
94                Err(eyre!("FLASHBOTS_RELAY_ERROR"))
95            }
96        }
97    }
98}
99
100pub struct Flashbots<P> {
101    req_id: AtomicU64,
102    signer: PrivateKeySigner,
103    provider: P,
104    simulation_client: FlashbotsClient<P>,
105    clients: Vec<Arc<FlashbotsClient<P>>>,
106}
107
108impl<P> Flashbots<P>
109where
110    P: Provider<Ethereum> + Send + Sync + Clone + 'static,
111{
112    pub fn new(provider: P, simulation_endpoint: &str, signer: Option<PrivateKeySigner>) -> Self {
113        let signer = signer.unwrap_or(PrivateKeySigner::random());
114        let simulation_client = FlashbotsClient::new(provider.clone(), simulation_endpoint);
115
116        Flashbots { req_id: AtomicU64::new(0), signer, provider, clients: vec![], simulation_client }
117    }
118
119    pub fn with_default_relays(self) -> Self {
120        let provider = self.provider.clone();
121
122        let flashbots = FlashbotsClient::new(provider.clone(), "https://relay.flashbots.net");
123        let beaverbuild = FlashbotsClient::new(provider.clone(), "https://rpc.beaverbuild.org/");
124        let titan = FlashbotsClient::new(provider.clone(), "https://rpc.titanbuilder.xyz");
125        let rsync = FlashbotsClient::new(provider.clone(), "https://rsync-builder.xyz");
126        //let builder0x69 = FlashbotsClient::new_no_sign(provider.clone(), "https://builder0x69.io");
127        let eden = FlashbotsClient::new(provider.clone(), "https://api.edennetwork.io/v1/bundle");
128        let eth_builder = FlashbotsClient::new_no_sign(provider.clone(), "https://eth-builder.com");
129        let secureapi = FlashbotsClient::new_no_sign(provider.clone(), "https://api.securerpc.com/v1");
130        //let blocknative = FlashbotsClient::new(provider.clone(), "https://api.blocknative.com/v1/auction");
131        let buildai = FlashbotsClient::new_no_sign(provider.clone(), "https://BuildAI.net");
132        let payloadde = FlashbotsClient::new_no_sign(provider.clone(), "https://rpc.payload.de");
133        let fibio = FlashbotsClient::new(provider.clone(), "https://rpc.f1b.io");
134        let loki = FlashbotsClient::new(provider.clone(), "https://rpc.lokibuilder.xyz");
135        let ibuilder = FlashbotsClient::new(provider.clone(), "https://rpc.ibuilder.xyz");
136        let jetbuilder = FlashbotsClient::new(provider.clone(), "https://rpc.jetbldr.xyz");
137        let penguinbuilder = FlashbotsClient::new(provider.clone(), "https://rpc.penguinbuild.org");
138        let gambitbuilder = FlashbotsClient::new(provider.clone(), "https://builder.gmbit.co/rpc");
139
140        let clients_vec = vec![
141            flashbots,
142            /* builder0x69,*/ titan,
143            fibio,
144            eden,
145            eth_builder,
146            beaverbuild,
147            secureapi,
148            rsync,
149            /*blocknative,*/ buildai,
150            payloadde,
151            loki,
152            ibuilder,
153            jetbuilder,
154            penguinbuilder,
155            gambitbuilder,
156        ];
157
158        let clients = clients_vec.into_iter().map(Arc::new).collect();
159
160        Self { clients, ..self }
161    }
162
163    pub fn with_relay(self, url: &str) -> Self {
164        let mut clients = self.clients;
165        clients.push(Arc::new(FlashbotsClient::new(self.provider.clone(), url)));
166        Self { clients, ..self }
167    }
168
169    pub fn with_relays(self, relays: Vec<RelayConfig>) -> Self {
170        let clients: Vec<Arc<FlashbotsClient<P>>> = relays
171            .into_iter()
172            .map(|relay| {
173                if relay.no_sign.unwrap_or(false) {
174                    Arc::new(FlashbotsClient::new_no_sign(self.provider.clone(), relay.url.as_str()))
175                } else {
176                    Arc::new(FlashbotsClient::new(self.provider.clone(), relay.url.as_str()))
177                }
178            })
179            .collect();
180        Self { clients, ..self }
181    }
182
183    pub async fn simulate_txes<TX>(
184        &self,
185        txs: Vec<TX>,
186        block_number: u64,
187        access_list_request: Option<Vec<TxHash>>,
188    ) -> Result<SimulatedBundle>
189    where
190        BundleTransaction: From<TX>,
191    {
192        let mut bundle = BundleRequest::new()
193            .set_target_block(U64::from(block_number + 1))
194            .set_simulation_block(U64::from(block_number))
195            .set_access_list_hashes(access_list_request);
196
197        for t in txs.into_iter() {
198            bundle = bundle.push_transaction(t);
199        }
200
201        self.simulation_client.call_bundle(&bundle).await
202    }
203
204    pub async fn broadcast_txes<TX>(&self, txs: Vec<TX>, target_block: u64) -> Result<()>
205    where
206        BundleTransaction: From<TX>,
207    {
208        let mut bundle = BundleRequest::new().set_target_block(U64::from(target_block));
209
210        for t in txs.into_iter() {
211            bundle = bundle.push_transaction(t);
212        }
213
214        let next_req_id = self.req_id.load(Ordering::SeqCst) + 1;
215        self.req_id.store(next_req_id, Ordering::SeqCst);
216
217        let (body, signature) = make_signed_body(next_req_id, "eth_sendBundle", bundle, &self.signer)?;
218
219        for client in self.clients.iter() {
220            let client_clone = client.clone();
221            let body_clone = body.clone();
222            let signature_clone = signature.clone();
223
224            tokio::task::spawn(async move {
225                debug!("Sending bundle to {}", client_clone.name);
226                let bundle_result = client_clone.send_signed_body(body_clone, signature_clone).await;
227                match bundle_result {
228                    Ok(_) => {
229                        debug!("Flashbots bundle broadcast successfully {}", client_clone.name);
230                    }
231                    Err(x) => {
232                        error!("Broadcasting error to {} : {}", client_clone.name, x.to_string());
233                    }
234                }
235            });
236        }
237
238        Ok(())
239    }
240}
241
242#[cfg(test)]
243mod test {
244    use alloy_primitives::Bytes;
245    use alloy_provider::ProviderBuilder;
246    use std::env;
247
248    use super::*;
249
250    #[tokio::test]
251    async fn test_client_send_bundle() -> Result<()> {
252        let _ = env_logger::try_init_from_env(env_logger::Env::default().default_filter_or("debug,flashbots=off"));
253        dotenvy::from_filename(".env.test").ok();
254        let node_url = Url::try_from(env::var("MAINNET_HTTP")?.as_str())?;
255
256        let provider = ProviderBuilder::new().disable_recommended_fillers().connect_http(node_url);
257        let block = provider.get_block_number().await?;
258
259        let flashbots_client = FlashbotsClient::new(provider.clone(), "https://relay.flashbots.net");
260
261        let tx = Bytes::from(vec![1, 1, 1, 1]);
262
263        let bundle_request = BundleRequest::new().set_target_block(U64::from(block)).push_transaction(tx);
264
265        match flashbots_client.send_bundle(&bundle_request).await {
266            Ok(resp) => {
267                debug!("{:?}", resp);
268                panic!("SHOULD_FAIL");
269            }
270            Err(e) => {
271                debug!("{}", e);
272                assert_eq!(e.to_string(), "FLASHBOTS_RELAY_ERROR".to_string());
273            }
274        }
275
276        Ok(())
277    }
278
279    #[tokio::test]
280    async fn test_send_bundle() -> Result<()> {
281        let _ = env_logger::try_init_from_env(env_logger::Env::default().default_filter_or("trace"));
282        dotenvy::from_filename(".env.test").ok();
283        let node_url = Url::try_from(env::var("MAINNET_HTTP")?.as_str())?;
284
285        let provider = ProviderBuilder::new().disable_recommended_fillers().connect_http(node_url);
286        let block = provider.get_block_number().await?;
287
288        let flashbots_client =
289            Flashbots::new(provider.clone(), "https://relay.flashbots.net", None).with_relay("https://relay.flashbots.net");
290
291        let tx = Bytes::from(vec![1, 1, 1, 1]);
292
293        match flashbots_client.broadcast_txes(vec![tx], block).await {
294            Ok(_resp) => {}
295            Err(e) => {
296                error!("{}", e);
297                panic!("SHOULD_NOT_FAIL");
298            }
299        }
300
301        Ok(())
302    }
303}