use crate::{
bundle::BundleHash,
jsonrpc::{JsonRpcError, Request, Response},
};
use ethers::core::{
types::{H256, U64},
utils::keccak256,
};
use ethers::signers::Signer;
use reqwest::{Client, Error as ReqwestError};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::sync::atomic::{AtomicU64, Ordering};
use thiserror::Error;
use url::Url;
#[derive(Debug)]
pub struct Relay<S> {
id: AtomicU64,
client: Client,
url: Url,
signer: Option<S>,
}
#[derive(Error, Debug)]
pub enum RelayError<S: Signer> {
#[error(transparent)]
RequestError(#[from] ReqwestError),
#[error(transparent)]
JsonRpcError(#[from] JsonRpcError),
#[error("Client error: {text}")]
ClientError { text: String },
#[error(transparent)]
RequestSerdeJson(#[from] serde_json::Error),
#[error(transparent)]
SignerError(#[from(S::Error)] S::Error),
#[error("Deserialization error: {err}. Response: {text}")]
ResponseSerdeJson {
err: serde_json::Error,
text: String,
},
}
impl<S: Signer> Relay<S> {
pub fn new(url: impl Into<Url>, signer: Option<S>) -> Self {
Self {
id: AtomicU64::new(0),
client: Client::new(),
url: url.into(),
signer,
}
}
pub async fn request<T: Serialize + Send + Sync, R: DeserializeOwned>(
&self,
method: &str,
params: T,
) -> Result<R, RelayError<S>> {
let next_id = self.id.load(Ordering::SeqCst) + 1;
self.id.store(next_id, Ordering::SeqCst);
let payload = Request::new(next_id, method, params);
let mut req = self.client.post(self.url.as_ref());
if let Some(signer) = &self.signer {
let signature = signer
.sign_message(format!(
"0x{:x}",
H256::from(keccak256(
serde_json::to_string(&payload)
.map_err(RelayError::RequestSerdeJson)?
.as_bytes()
))
))
.await
.map_err(RelayError::SignerError)?;
req = req.header(
"X-Flashbots-Signature",
format!("{:?}:0x{}", signer.address(), signature),
);
}
let res = req.json(&payload).send().await?;
let status = res.error_for_status_ref();
match status {
Err(err) => {
let text = res.text().await?;
let status_code = err.status().unwrap();
if status_code.is_client_error() {
Err(RelayError::ClientError { text })
} else {
Err(RelayError::RequestError(err))
}
}
Ok(_) => {
let text = res.text().await?;
let res: Response<R> = serde_json::from_str(&text)
.map_err(|err| RelayError::ResponseSerdeJson { err, text })?;
Ok(res.data.into_result()?)
}
}
}
}
impl<S: Signer + Clone> Clone for Relay<S> {
fn clone(&self) -> Self {
Self {
id: AtomicU64::new(0),
client: self.client.clone(),
url: self.url.clone(),
signer: self.signer.clone(),
}
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SendBundleResponse {
pub(crate) bundle_hash: BundleHash,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct GetBundleStatsParams {
pub(crate) bundle_hash: BundleHash,
pub(crate) block_number: U64,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct GetUserStatsParams {
pub(crate) block_number: U64,
}