kabu_broadcast_flashbots/client/
relay.rs1use std::sync::atomic::{AtomicU64, Ordering};
2use std::sync::Arc;
3
4use crate::client::jsonrpc::{JsonRpcError, Request, Response};
5use alloy_primitives::{hex, keccak256};
6use alloy_signer::Signer;
7use alloy_signer_local::PrivateKeySigner;
8use reqwest::{Client, Error as ReqwestError};
9use serde::{de::DeserializeOwned, Serialize};
10use thiserror::Error;
11use tracing::{debug, trace};
12use url::Url;
13
14#[derive(Clone, Debug, PartialEq, Eq, Hash)]
16pub struct RelayConfig {
17 pub id: u16,
18 pub name: String,
19 pub url: String,
20 pub no_sign: Option<bool>,
21}
22
23#[derive(Clone)]
32pub struct Relay {
33 id: Arc<AtomicU64>,
34 client: Client,
35 url: Url,
36 signer: Option<PrivateKeySigner>,
37}
38
39#[derive(Debug, Error)]
41pub enum RelayError {
42 #[error(transparent)]
44 RequestError(#[from] ReqwestError),
45 #[error(transparent)]
47 JsonRpcError(#[from] JsonRpcError),
48 #[error("Client error: {text}")]
50 ClientError { text: String },
51 #[error(transparent)]
53 RequestSerdeJson(#[from] serde_json::Error),
54 #[error(transparent)]
56 SignerError(alloy_signer::Error),
57 #[error("Deserialization error: {err}. Response: {text}")]
59 ResponseSerdeJson { err: serde_json::Error, text: String },
60}
61
62impl Relay {
63 pub fn new(url: impl Into<Url>, signer: Option<PrivateKeySigner>) -> Self {
65 let client = Client::new();
67
68 Self { id: Arc::new(AtomicU64::new(0)), client, url: url.into(), signer }
69 }
70
71 pub async fn request<T: Serialize + Send + Sync, R: DeserializeOwned>(&self, method: &str, params: T) -> Result<R, RelayError> {
74 let next_id = self.id.load(Ordering::SeqCst) + 1;
75 self.id.store(next_id, Ordering::SeqCst);
76
77 let payload = Request::new(next_id, method, params);
78
79 let body = serde_json::to_string(&payload).map_err(RelayError::RequestSerdeJson)?;
80
81 let body_hash = keccak256(body.clone()).to_string();
82 trace!("Body hash : {} {}", body_hash, body);
83
84 let mut req = self.client.post(self.url.as_ref()).body(body).header("Content-Type", "application/json");
85
86 if let Some(signer) = &self.signer {
87 trace!("Signer on wallet : {}", signer.address());
88 let signature = signer.sign_message(body_hash.as_bytes()).await.map_err(RelayError::SignerError)?;
89
90 req = req.header("X-Flashbots-Signature", format!("{}:0x{}", signer.address(), hex::encode(signature.as_bytes())));
91 }
92
93 let res = req.send().await?;
94 let status = res.error_for_status_ref();
95
96 match status {
97 Err(err) => {
98 let text = res.text().await?;
99 let status_code = err.status().unwrap();
100 if status_code.is_client_error() {
101 Err(RelayError::ClientError { text })
103 } else {
104 Err(RelayError::RequestError(err))
106 }
107 }
108 Ok(_) => {
109 let text = res.text().await?;
110 debug!("Flashbots response: {}", text);
111 let res: Response<R> = serde_json::from_str(&text).map_err(|err| RelayError::ResponseSerdeJson { err, text })?;
112
113 Ok(res.data.into_result()?)
114 }
115 }
116 }
117
118 pub async fn serialized_request<R: DeserializeOwned>(&self, body: String, signature: Option<String>) -> Result<R, RelayError> {
119 let mut req = self.client.post(self.url.as_ref()).body(body).header("Content-Type", "application/json");
120
121 if let Some(signature) = signature {
122 req = req.header("X-Flashbots-Signature", signature);
123 }
124
125 let res = req.send().await?;
126 let status = res.error_for_status_ref();
127
128 match status {
129 Err(err) => {
130 let text = res.text().await?;
131 let status_code = err.status().unwrap();
132 if status_code.is_client_error() {
133 Err(RelayError::ClientError { text })
135 } else {
136 Err(RelayError::RequestError(err))
138 }
139 }
140 Ok(_) => {
141 let text = res.text().await?;
142 debug!("Flashbots response: {}", text);
143 let res: Response<R> = serde_json::from_str(&text).map_err(|err| RelayError::ResponseSerdeJson { err, text })?;
144
145 Ok(res.data.into_result()?)
146 }
147 }
148 }
149}