kabu_broadcast_flashbots/
flashbots.rs1use 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 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 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 titan,
143 fibio,
144 eden,
145 eth_builder,
146 beaverbuild,
147 secureapi,
148 rsync,
149 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}