1#![warn(missing_docs)]
4#![warn(unused_extern_crates)]
5#![forbid(unsafe_code)]
6#![forbid(where_clauses_object_safety)]
7
8mod types;
9use std::str::FromStr;
10
11use reqwest::{IntoUrl, Url};
12pub use types::*;
13
14pub use reqwest;
16
17use ethers_core::types::{Bytes, H160, H256, U64};
18use once_cell::sync::Lazy;
19
20static DEFAULT_URL: Lazy<reqwest::Url> =
21 Lazy::new(|| "https://relay.gelato.digital/".parse().unwrap());
22
23#[derive(Debug, Clone)]
25pub struct GelatoClient {
26 url: reqwest::Url,
27 client: reqwest::Client,
28}
29
30impl Default for GelatoClient {
31 fn default() -> Self {
32 Self {
33 url: DEFAULT_URL.clone(),
34 client: Default::default(),
35 }
36 }
37}
38
39impl GelatoClient {
40 pub fn new<S>(url: S) -> Result<Self, reqwest::Error>
46 where
47 S: IntoUrl,
48 {
49 Ok(Self {
50 url: url.into_url()?,
51 ..Default::default()
52 })
53 }
54
55 async fn get(&self, url: Url) -> Result<reqwest::Response, reqwest::Error> {
56 self.client.get(url).send().await
57 }
58
59 pub fn new_with_client<S>(
65 url: S,
66 client: reqwest::Client,
67 ) -> Result<Self, <reqwest::Url as FromStr>::Err>
68 where
69 S: AsRef<str>,
70 {
71 Ok(Self {
72 url: url.as_ref().parse()?,
73 client,
74 })
75 }
76
77 pub async fn send_relay_transaction(
79 &self,
80 chain_id: usize,
81 dest: H160,
82 data: Bytes,
83 fee_token: FeeToken,
84 relayer_fee: U64,
85 ) -> Result<RelayResponse, reqwest::Error> {
86 let params = RelayRequest {
87 dest,
88 data,
89 token: fee_token,
90 relayer_fee,
91 };
92
93 let url = format!("{}/relays/{}", &self.url, chain_id);
94 let res = reqwest::Client::new()
95 .post(url)
96 .json(¶ms)
97 .send()
98 .await?;
99
100 res.json().await
101 }
102
103 fn send_forward_request_url(&self, chain_id: usize) -> Url {
104 self.url
105 .join("metabox-relays/")
106 .unwrap()
107 .join(&format!("{}", chain_id))
108 .unwrap()
109 }
110
111 pub async fn send_forward_request(
125 &self,
126 params: &ForwardRequest,
127 ) -> Result<RelayResponse, reqwest::Error> {
128 self.client
129 .post(self.send_forward_request_url(params.chain_id))
130 .json(¶ms)
131 .send()
132 .await?
133 .json()
134 .await
135 }
136
137 pub async fn is_chain_supported(&self, chain_id: usize) -> Result<bool, reqwest::Error> {
139 Ok(self.get_gelato_relay_chains().await?.contains(&chain_id))
140 }
141
142 fn relay_chains_url(&self) -> reqwest::Url {
143 self.url.join("relays/").unwrap()
144 }
145
146 pub async fn get_gelato_relay_chains(&self) -> Result<Vec<usize>, reqwest::Error> {
148 let res = self.client.get(self.relay_chains_url()).send().await?;
149 Ok(res.json::<RelayChainsResponse>().await?.relays().collect())
150 }
151
152 fn get_estimated_fee_url(
153 &self,
154 chain_id: usize,
155 payment_token: FeeToken,
156 gas_limit: usize,
157 is_high_priority: bool,
158 ) -> Url {
159 let mut url = self
160 .url
161 .join("oracles/")
162 .unwrap()
163 .join(&format!("{}/", chain_id))
164 .unwrap()
165 .join("estimate")
166 .unwrap();
167
168 let payment_token = format!("{:?}", *payment_token);
169
170 url.query_pairs_mut()
171 .append_pair("paymentToken", &payment_token)
172 .append_pair("gasLimit", &gas_limit.to_string())
173 .append_pair("isHighPriority", &is_high_priority.to_string());
174 url
175 }
176
177 pub async fn get_estimated_fee(
182 &self,
183 chain_id: usize,
184 payment_token: impl Into<FeeToken>,
185 gas_limit: usize,
186 is_high_priority: bool,
187 ) -> Result<usize, reqwest::Error> {
188 let url =
189 self.get_estimated_fee_url(chain_id, payment_token.into(), gas_limit, is_high_priority);
190
191 Ok(reqwest::get(url)
192 .await?
193 .json::<EstimatedFeeResponse>()
194 .await?
195 .estimated_fee())
196 }
197
198 fn get_task_status_url(&self, task_id: H256) -> Url {
199 self.url
200 .join("/tasks/GelatoMetaBox/")
201 .unwrap()
202 .join(&format!("{:?}/", task_id))
203 .unwrap()
204 }
205
206 pub async fn get_task_status(
208 &self,
209 task_id: H256,
210 ) -> Result<Option<TransactionStatus>, reqwest::Error> {
211 Ok(self
212 .get(self.get_task_status_url(task_id))
213 .await?
214 .json::<TaskStatusResponse>()
215 .await?
216 .into_iter()
217 .next())
218 }
219}