1use crate::jsonrpc::{JsonRpcError, Request, Response};
2use ethers::signers::Signer;
3use reqwest::{header::HeaderValue, Client, ClientBuilder, Error as ReqwestError};
4use serde::{de::DeserializeOwned, Serialize};
5use std::sync::atomic::{AtomicU64, Ordering};
6use thiserror::Error;
7use url::Url;
8
9#[derive(Debug)]
18pub struct Relay<S> {
19 id: AtomicU64,
20 client: Client,
21 url: Url,
22 signer: Option<S>,
23}
24
25#[derive(Error, Debug)]
27pub enum RelayError {
28 #[error("Client error: {text}")]
29 ClientError { text: String },
30 #[error("Failed to send request")]
31 RequestError(#[from] ReqwestError),
32 #[error("Error response: {text}")]
33 ResponseSerdeJson { text: String },
34}
35
36impl<S: Signer> Relay<S> {
37 pub fn new(url: impl Into<Url>, signer: Option<S>, authorization_key: String) -> Self {
39 let client_builder = ClientBuilder::new();
40
41 let mut headers = reqwest::header::HeaderMap::new();
42 headers.insert(
43 reqwest::header::AUTHORIZATION,
44 HeaderValue::from_str(&authorization_key).unwrap(),
45 );
46
47 let client_builder = client_builder.default_headers(headers);
48
49 Self {
50 id: AtomicU64::new(0),
51 client: client_builder.build().unwrap(),
52 url: url.into(),
53 signer,
54 }
55 }
56
57 pub async fn request<T: Serialize + Send + Sync, R: DeserializeOwned>(
60 &self,
61 method: &str,
62 params: T,
63 ) -> Result<Option<R>, RelayError> {
64 let next_id = self.id.load(Ordering::SeqCst) + 1;
65 self.id.store(next_id, Ordering::SeqCst);
66
67 let payload = Request::new(next_id, method, params);
68
69 let req = self.client.post(self.url.as_ref());
70
71 let res = req.json(&payload).send().await?;
72 let status = res.error_for_status_ref();
73
74 match status {
75 Err(err) => {
76 let text = res.text().await?;
77 let status_code = err.status().unwrap();
78 if status_code.is_client_error() {
79 Err(RelayError::ClientError { text })
81 } else {
82 Err(RelayError::RequestError(err))
84 }
85 }
86 Ok(_) => {
87 let text = res.text().await?;
88 let res: Response<R> = serde_json::from_str(&text).unwrap();
89 let result: Result<Option<R>, JsonRpcError> = res.data.into_result();
90
91 match result {
92 Ok(result) => Ok(result),
93 Err(err) => Err(RelayError::ResponseSerdeJson {
94 text: format!("{:?}", err),
95 }),
96 }
97 }
98 }
99 }
100}
101
102impl<S: Signer + Clone> Clone for Relay<S> {
103 fn clone(&self) -> Self {
104 Self {
105 id: AtomicU64::new(0),
106 client: self.client.clone(),
107 url: self.url.clone(),
108 signer: self.signer.clone(),
109 }
110 }
111}