ethers_flashbots/
relay.rs1use crate::{
2 bundle::BundleHash,
3 jsonrpc::{JsonRpcError, Request, Response},
4};
5use ethers::core::{
6 types::{H256, U64},
7 utils::keccak256,
8};
9use ethers::signers::Signer;
10use reqwest::{Client, Error as ReqwestError};
11use serde::{de::DeserializeOwned, Deserialize, Serialize};
12use std::sync::atomic::{AtomicU64, Ordering};
13use thiserror::Error;
14use url::Url;
15
16#[derive(Debug)]
25pub struct Relay<S> {
26 id: AtomicU64,
27 client: Client,
28 url: Url,
29 signer: Option<S>,
30}
31
32#[derive(Error, Debug)]
34pub enum RelayError<S: Signer> {
35 #[error(transparent)]
37 RequestError(#[from] ReqwestError),
38 #[error(transparent)]
40 JsonRpcError(#[from] JsonRpcError),
41 #[error("Client error: {text}")]
43 ClientError { text: String },
44 #[error(transparent)]
46 RequestSerdeJson(#[from] serde_json::Error),
47 #[error(transparent)]
49 SignerError(#[from(S::Error)] S::Error),
50 #[error("Deserialization error: {err}. Response: {text}")]
52 ResponseSerdeJson {
53 err: serde_json::Error,
54 text: String,
55 },
56}
57
58impl<S: Signer> Relay<S> {
59 pub fn new(url: impl Into<Url>, signer: Option<S>) -> Self {
61 Self {
62 id: AtomicU64::new(0),
63 client: Client::new(),
64 url: url.into(),
65 signer,
66 }
67 }
68
69 pub async fn request<T: Serialize + Send + Sync, R: DeserializeOwned>(
72 &self,
73 method: &str,
74 params: T,
75 ) -> Result<Option<R>, RelayError<S>> {
76 let next_id = self.id.load(Ordering::SeqCst) + 1;
77 self.id.store(next_id, Ordering::SeqCst);
78
79 let payload = Request::new(next_id, method, params);
80
81 let mut req = self.client.post(self.url.as_ref());
82
83 if let Some(signer) = &self.signer {
84 let signature = signer
85 .sign_message(format!(
86 "0x{:x}",
87 H256::from(keccak256(
88 serde_json::to_string(&payload)
89 .map_err(RelayError::RequestSerdeJson)?
90 .as_bytes()
91 ))
92 ))
93 .await
94 .map_err(RelayError::SignerError)?;
95
96 req = req.header(
97 "X-Flashbots-Signature",
98 format!("{:?}:0x{}", signer.address(), signature),
99 );
100 }
101
102 let res = req.json(&payload).send().await?;
103 let status = res.error_for_status_ref();
104
105 match status {
106 Err(err) => {
107 let text = res.text().await?;
108 let status_code = err.status().unwrap();
109 if status_code.is_client_error() {
110 Err(RelayError::ClientError { text })
112 } else {
113 Err(RelayError::RequestError(err))
115 }
116 }
117 Ok(_) => {
118 let text = res.text().await?;
119 let res: Response<R> = serde_json::from_str(&text)
120 .map_err(|err| RelayError::ResponseSerdeJson { err, text })?;
121
122 Ok(res.data.into_result()?)
123 }
124 }
125 }
126}
127
128impl<S: Signer + Clone> Clone for Relay<S> {
129 fn clone(&self) -> Self {
130 Self {
131 id: AtomicU64::new(0),
132 client: self.client.clone(),
133 url: self.url.clone(),
134 signer: self.signer.clone(),
135 }
136 }
137}
138
139#[derive(Deserialize)]
140#[serde(rename_all = "camelCase")]
141pub(crate) struct SendBundleResponse {
142 pub(crate) bundle_hash: Option<BundleHash>,
143}
144
145#[derive(Serialize)]
146#[serde(rename_all = "camelCase")]
147pub(crate) struct GetBundleStatsParams {
148 pub(crate) bundle_hash: BundleHash,
149 pub(crate) block_number: U64,
150}
151
152#[derive(Serialize)]
153#[serde(rename_all = "camelCase")]
154pub(crate) struct GetUserStatsParams {
155 pub(crate) block_number: U64,
156}