1use crate::builder_setter;
2
3use serde::{Deserialize, Serialize};
4use thiserror::Error;
5use crate::common::token::TokenInfo;
6
7#[derive(Error, Debug, Eq, PartialEq)]
9pub enum SwapDetailsBuilderError {
10 #[error("Missing {0}")]
12 MissingField(&'static str),
13
14 #[error("Invalid slippage value. It should be between 0 and 50.")]
16 InvalidSlippage,
17
18 #[error("Invalid fee value. It should be between 0 and 3.")]
19 InvalidFee
20}
21
22#[derive(Error, Debug, Eq, PartialEq)]
24pub enum QuoteDetailsBuilderError {
25 #[error("Missing {0}")]
27 MissingField(&'static str),
28
29 #[error("Invalid fee value. It should be between 0 and 3.")]
30 InvalidFee
31}
32#[derive(Debug, Clone)]
34pub struct SwapDetails {
35 pub src: String, pub dst: String, pub amount: String, pub from: String, pub slippage: usize, pub fee : Option<u8>,
43 pub protocols : Option<String>,
44 pub gas_price : Option<String>,
45 pub complexity_level : Option<u128>,
46 pub parts : Option<u128>,
47 pub main_route_parts : Option<u128>,
48 pub gas_limit : Option<u128>,
49
50 pub include_tokens_info : Option<bool>,
51 pub include_protocols : Option<bool>,
52 pub include_gas : Option<bool>,
53 pub connector_tokens : Option<String>,
54 pub permit : Option<String>,
55 pub receiver : Option<String>,
56 pub referrer : Option<String>,
57
58 pub disable_estimate: Option<bool>, pub allow_partial_fill: Option<bool>, }
61
62#[derive(Default)]
64pub struct SwapDetailsBuilder {
65 src: Option<String>,
66 dst: Option<String>,
67 amount: Option<String>,
68 from_addr: Option<String>,
69 slippage: Option<usize>,
70
71 fee : Option<u8>,
73 protocols : Option<String>,
74 gas_price : Option<String>,
75 complexity_level : Option<u128>,
76 parts : Option<u128>,
77 main_route_parts : Option<u128>,
78 gas_limit : Option<u128>,
79
80 include_tokens_info : Option<bool>,
81 include_protocols : Option<bool>,
82 include_gas : Option<bool>,
83 connector_tokens : Option<String>,
84 permit : Option<String>,
85 receiver : Option<String>,
86 referrer : Option<String>,
87
88 disable_estimate: Option<bool>, allow_partial_fill: Option<bool>, }
91
92
93#[derive(Deserialize, Debug)]
95pub struct SwapResponse {
96 #[serde(rename = "fromToken")]
97 pub from_token: Option<TokenInfo>,
98
99 #[serde(rename = "toToken")]
100 pub to_token: Option<TokenInfo>,
101
102 #[serde(rename = "toAmount")]
103 pub to_amount: String,
104
105 pub protocols: Option<Vec<Vec<Vec<SelectedProtocol>>>>,
106
107
108 #[serde(rename = "tx")]
109 pub transaction : SwapTranactionData
110}
111
112
113#[derive(Deserialize, Debug)]
115pub struct SwapTranactionData {
116 pub from : String,
117 pub to : String,
118 pub data : String,
119 pub value : String,
120
121 #[serde(rename = "gasPrice")]
122 pub gas_price : String,
123
124
125 pub gas : u128
126}
127
128#[derive(Error, Debug)]
133pub enum SwapError {
134 #[error("Network error: {0}")]
139 Network(reqwest::Error),
140
141 #[error("JSON parsing error: {0}")]
146 JsonParse(serde_json::Error),
147
148 #[error("Swap request error: {description}")]
153 SwapRequest {
154 description: String,
155 error: String,
156 status_code: u16,
157 request_id: String,
158 },
159
160 #[error("Other error: {0}")]
164 Other(String),
165}
166
167
168#[derive(serde::Deserialize)]
173pub struct SwapRequestError {
174 pub error: String,
176
177 pub description: String,
179
180 #[serde(rename = "statusCode")]
182 pub status_code: u16,
183
184 #[serde(rename = "requestId")]
186 pub request_id: String,
187
188 pub meta: Vec<HttpExceptionMeta>,
190}
191
192
193#[derive(serde::Deserialize)]
198pub struct HttpExceptionMeta {
199 #[serde(rename = "type")]
201 pub type_field: String,
202
203 pub value: String,
205}
206
207#[derive(Debug, Serialize, Deserialize)]
208pub struct SelectedProtocol {
209 pub name: String,
210 pub part: f64,
211
212 #[serde(rename = "fromTokenAddress")]
213 pub from_token_address: String,
214
215 #[serde(rename = "toTokenAddress")]
216 pub to_token_address: String,
217}
218
219
220impl SwapDetailsBuilder {
221 pub fn new() -> Self {
223 SwapDetailsBuilder::default()
224 }
225
226 builder_setter!(src, String);
227 builder_setter!(dst, String);
228 builder_setter!(amount, String);
229 builder_setter!(from_addr, String);
230
231 builder_setter!(protocols, String);
232 builder_setter!(gas_price, String);
233 builder_setter!(complexity_level, u128);
234 builder_setter!(parts, u128);
235 builder_setter!(main_route_parts, u128);
236 builder_setter!(gas_limit, u128);
237
238 builder_setter!(include_tokens_info, bool);
239 builder_setter!(include_protocols, bool);
240 builder_setter!(include_gas, bool);
241
242
243 builder_setter!(connector_tokens, String);
244 builder_setter!(permit, String);
245 builder_setter!(receiver, String);
246 builder_setter!(referrer, String);
247
248 builder_setter!(disable_estimate, bool);
249 builder_setter!(allow_partial_fill, bool);
250
251 pub fn fee(mut self, fee: u8) -> Result<Self, SwapDetailsBuilderError> {
253 if fee > 3 {
254 return Err(SwapDetailsBuilderError::InvalidFee);
255 }
256 self.fee = Some(fee);
257 Ok(self)
258 }
259
260
261
262 pub fn slippage(mut self, slippage: usize) -> Result<Self, SwapDetailsBuilderError> {
264 if slippage > 50 {
265 return Err(SwapDetailsBuilderError::InvalidSlippage);
266 }
267 self.slippage = Some(slippage);
268 Ok(self)
269 }
270
271 pub fn build(self) -> Result<SwapDetails, SwapDetailsBuilderError> {
273 Ok(SwapDetails {
274 src: self
275 .src
276 .ok_or(SwapDetailsBuilderError::MissingField("src"))?,
277 dst: self
278 .dst
279 .ok_or(SwapDetailsBuilderError::MissingField("dst"))?,
280 amount: self
281 .amount
282 .ok_or(SwapDetailsBuilderError::MissingField("amount"))?
283 .to_string(),
284 from: self
285 .from_addr
286 .ok_or(SwapDetailsBuilderError::MissingField("from_addr"))?,
287 slippage: self
288 .slippage
289 .ok_or(SwapDetailsBuilderError::MissingField("slippage"))?,
290
291
292 fee: self.fee,
293 protocols: self.protocols,
294 gas_price: self.gas_price,
295 complexity_level: self.complexity_level,
296 parts: self.parts,
297 main_route_parts: self.main_route_parts,
298 gas_limit: self.gas_limit,
299 include_tokens_info: self.include_tokens_info,
300 include_protocols: self.include_protocols,
301 include_gas: self.include_gas,
302 connector_tokens: self.connector_tokens,
303 permit: self.permit,
304 receiver: self.receiver,
305 referrer: self.referrer,
306 disable_estimate: self.disable_estimate,
307 allow_partial_fill: self.allow_partial_fill,
308 })
309 }
310}
311
312
313
314#[derive(Debug, Clone)]
316pub struct QuoteDetails {
317 pub src: String, pub dst: String, pub amount: String, pub fee : Option<u8>,
323 pub protocols : Option<String>,
324 pub gas_price : Option<String>,
325 pub complexity_level : Option<u128>,
326 pub parts : Option<u128>,
327 pub main_route_parts : Option<u128>,
328 pub gas_limit : Option<u128>,
329
330 pub include_tokens_info : Option<bool>,
331 pub include_protocols : Option<bool>,
332 pub include_gas : Option<bool>,
333 pub connector_tokens : Option<String>,
334}
335
336
337#[derive(Default)]
339pub struct QuoteDetailsBuilder {
340 pub src: Option<String>,
341 pub dst: Option<String>,
342 pub amount: Option<String>,
343
344 pub fee : Option<u8>,
346 pub protocols : Option<String>,
347 pub gas_price : Option<String>,
348 pub complexity_level : Option<u128>,
349 pub parts : Option<u128>,
350 pub main_route_parts : Option<u128>,
351 pub gas_limit : Option<u128>,
352
353 pub include_tokens_info : Option<bool>,
354 pub include_protocols : Option<bool>,
355 pub include_gas : Option<bool>,
356 pub connector_tokens : Option<String>,
357
358}
359
360impl QuoteDetailsBuilder {
361 pub fn new() -> Self {
362 QuoteDetailsBuilder::default()
363 }
364
365
366 builder_setter!(src, String);
367 builder_setter!(dst, String);
368 builder_setter!(amount, String);
369
370 builder_setter!(protocols, String);
371 builder_setter!(gas_price, String);
372 builder_setter!(complexity_level, u128);
373 builder_setter!(parts, u128);
374 builder_setter!(main_route_parts, u128);
375 builder_setter!(gas_limit, u128);
376
377 builder_setter!(include_tokens_info, bool);
378 builder_setter!(include_protocols, bool);
379 builder_setter!(include_gas, bool);
380 builder_setter!(connector_tokens, String);
381
382 pub fn fee(mut self, fee: u8) -> Result<Self, QuoteDetailsBuilderError> {
384 if fee > 3 {
385 return Err(QuoteDetailsBuilderError::InvalidFee);
386 }
387 self.fee = Some(fee);
388 Ok(self)
389 }
390
391
392 pub fn build(self) -> Result<QuoteDetails, QuoteDetailsBuilderError> {
394 Ok(QuoteDetails {
395 src: self
396 .src
397 .ok_or(QuoteDetailsBuilderError::MissingField("src"))?,
398 dst: self
399 .dst
400 .ok_or(QuoteDetailsBuilderError::MissingField("dst"))?,
401 amount: self
402 .amount
403 .ok_or(QuoteDetailsBuilderError::MissingField("amount"))?
404 .to_string(),
405
406
407 fee: self.fee,
408 protocols: self.protocols,
409 gas_price: self.gas_price,
410 complexity_level: self.complexity_level,
411 parts: self.parts,
412 main_route_parts: self.main_route_parts,
413 gas_limit: self.gas_limit,
414 include_tokens_info: self.include_tokens_info,
415 include_protocols: self.include_protocols,
416 include_gas: self.include_gas,
417 connector_tokens: self.connector_tokens,
418 })
419 }
420
421}
422
423
424#[derive(Deserialize, Debug)]
426pub struct QuoteResponse {
427 #[serde(rename = "fromToken")]
428 pub from_token: Option<TokenInfo>,
429
430 #[serde(rename = "toToken")]
431 pub to_token: Option<TokenInfo>,
432
433 #[serde(rename = "toAmount")]
434 pub to_amount: String,
435 pub protocols: Option<Vec<Vec<Vec<SelectedProtocol>>>>,
436}
437
438
439
440#[cfg(test)]
442mod tests {
443 use super::*;
444
445 #[test]
447 fn test_valid_swap_details_builder() {
448 let swap_details = SwapDetailsBuilder::new()
449 .src("from_token".to_string())
450 .dst("to_token".to_string())
451 .amount("1000".to_string())
452 .from_addr("from_addr".to_string())
453 .slippage(5)
454 .expect("Invalid slippage")
455 .disable_estimate(false)
456 .allow_partial_fill(false)
457 .build()
458 .expect("Failed to build SwapDetails");
459
460 assert_eq!(swap_details.src, "from_token");
461 assert_eq!(swap_details.dst, "to_token");
462 assert_eq!(swap_details.amount, "1000");
463 assert_eq!(swap_details.from, "from_addr");
464 assert_eq!(swap_details.slippage, 5);
465 assert!(!swap_details.disable_estimate.unwrap());
466 assert!(!swap_details.allow_partial_fill.unwrap());
467 }
468
469 #[test]
471 fn test_invalid_slippage_in_builder() {
472 let result = SwapDetailsBuilder::new()
473 .src("from_token".to_string())
474 .dst("to_token".to_string())
475 .amount("1000".to_string())
476 .from_addr("from_addr".to_string())
477 .slippage(102);
478
479 assert!(result.is_err());
480 if let Err(err) = result {
481 assert_eq!(err, SwapDetailsBuilderError::InvalidSlippage);
482 }
483 }
484}