use crate::builder_setter;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::common::token::TokenInfo;
#[derive(Error, Debug, Eq, PartialEq)]
pub enum SwapDetailsBuilderError {
#[error("Missing {0}")]
MissingField(&'static str),
#[error("Invalid slippage value. It should be between 0 and 50.")]
InvalidSlippage,
#[error("Invalid fee value. It should be between 0 and 3.")]
InvalidFee
}
#[derive(Error, Debug, Eq, PartialEq)]
pub enum QuoteDetailsBuilderError {
#[error("Missing {0}")]
MissingField(&'static str),
#[error("Invalid fee value. It should be between 0 and 3.")]
InvalidFee
}
#[derive(Debug, Clone)]
pub struct SwapDetails {
pub src: String, pub dst: String, pub amount: String, pub from: String, pub slippage: usize,
pub fee : Option<u8>,
pub protocols : Option<String>,
pub gas_price : Option<String>,
pub complexity_level : Option<u128>,
pub parts : Option<u128>,
pub main_route_parts : Option<u128>,
pub gas_limit : Option<u128>,
pub include_tokens_info : Option<bool>,
pub include_protocols : Option<bool>,
pub include_gas : Option<bool>,
pub connector_tokens : Option<String>,
pub permit : Option<String>,
pub receiver : Option<String>,
pub referrer : Option<String>,
pub disable_estimate: Option<bool>, pub allow_partial_fill: Option<bool>, }
#[derive(Default)]
pub struct SwapDetailsBuilder {
src: Option<String>,
dst: Option<String>,
amount: Option<String>,
from_addr: Option<String>,
slippage: Option<usize>,
fee : Option<u8>,
protocols : Option<String>,
gas_price : Option<String>,
complexity_level : Option<u128>,
parts : Option<u128>,
main_route_parts : Option<u128>,
gas_limit : Option<u128>,
include_tokens_info : Option<bool>,
include_protocols : Option<bool>,
include_gas : Option<bool>,
connector_tokens : Option<String>,
permit : Option<String>,
receiver : Option<String>,
referrer : Option<String>,
disable_estimate: Option<bool>, allow_partial_fill: Option<bool>, }
#[derive(Deserialize, Debug)]
pub struct SwapResponse {
#[serde(rename = "fromToken")]
pub from_token: Option<TokenInfo>,
#[serde(rename = "toToken")]
pub to_token: Option<TokenInfo>,
#[serde(rename = "toAmount")]
pub to_amount: String,
pub protocols: Option<Vec<Vec<Vec<SelectedProtocol>>>>,
#[serde(rename = "tx")]
pub transaction : SwapTranactionData
}
#[derive(Deserialize, Debug)]
pub struct SwapTranactionData {
pub from : String,
pub to : String,
pub data : String,
pub value : String,
#[serde(rename = "gasPrice")]
pub gas_price : String,
pub gas : u128
}
#[derive(Error, Debug)]
pub enum SwapError {
#[error("Network error: {0}")]
Network(reqwest::Error),
#[error("JSON parsing error: {0}")]
JsonParse(serde_json::Error),
#[error("Swap request error: {description}")]
SwapRequest {
description: String,
error: String,
status_code: u16,
request_id: String,
},
#[error("Other error: {0}")]
Other(String),
}
#[derive(serde::Deserialize)]
pub struct SwapRequestError {
pub error: String,
pub description: String,
#[serde(rename = "statusCode")]
pub status_code: u16,
#[serde(rename = "requestId")]
pub request_id: String,
pub meta: Vec<HttpExceptionMeta>,
}
#[derive(serde::Deserialize)]
pub struct HttpExceptionMeta {
#[serde(rename = "type")]
pub type_field: String,
pub value: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SelectedProtocol {
pub name: String,
pub part: f64,
#[serde(rename = "fromTokenAddress")]
pub from_token_address: String,
#[serde(rename = "toTokenAddress")]
pub to_token_address: String,
}
impl SwapDetailsBuilder {
pub fn new() -> Self {
SwapDetailsBuilder::default()
}
builder_setter!(src, String);
builder_setter!(dst, String);
builder_setter!(amount, String);
builder_setter!(from_addr, String);
builder_setter!(protocols, String);
builder_setter!(gas_price, String);
builder_setter!(complexity_level, u128);
builder_setter!(parts, u128);
builder_setter!(main_route_parts, u128);
builder_setter!(gas_limit, u128);
builder_setter!(include_tokens_info, bool);
builder_setter!(include_protocols, bool);
builder_setter!(include_gas, bool);
builder_setter!(connector_tokens, String);
builder_setter!(permit, String);
builder_setter!(receiver, String);
builder_setter!(referrer, String);
builder_setter!(disable_estimate, bool);
builder_setter!(allow_partial_fill, bool);
pub fn fee(mut self, fee: u8) -> Result<Self, SwapDetailsBuilderError> {
if fee > 3 {
return Err(SwapDetailsBuilderError::InvalidFee);
}
self.fee = Some(fee);
Ok(self)
}
pub fn slippage(mut self, slippage: usize) -> Result<Self, SwapDetailsBuilderError> {
if slippage > 50 {
return Err(SwapDetailsBuilderError::InvalidSlippage);
}
self.slippage = Some(slippage);
Ok(self)
}
pub fn build(self) -> Result<SwapDetails, SwapDetailsBuilderError> {
Ok(SwapDetails {
src: self
.src
.ok_or(SwapDetailsBuilderError::MissingField("src"))?,
dst: self
.dst
.ok_or(SwapDetailsBuilderError::MissingField("dst"))?,
amount: self
.amount
.ok_or(SwapDetailsBuilderError::MissingField("amount"))?
.to_string(),
from: self
.from_addr
.ok_or(SwapDetailsBuilderError::MissingField("from_addr"))?,
slippage: self
.slippage
.ok_or(SwapDetailsBuilderError::MissingField("slippage"))?,
fee: self.fee,
protocols: self.protocols,
gas_price: self.gas_price,
complexity_level: self.complexity_level,
parts: self.parts,
main_route_parts: self.main_route_parts,
gas_limit: self.gas_limit,
include_tokens_info: self.include_tokens_info,
include_protocols: self.include_protocols,
include_gas: self.include_gas,
connector_tokens: self.connector_tokens,
permit: self.permit,
receiver: self.receiver,
referrer: self.referrer,
disable_estimate: self.disable_estimate,
allow_partial_fill: self.allow_partial_fill,
})
}
}
#[derive(Debug, Clone)]
pub struct QuoteDetails {
pub src: String, pub dst: String, pub amount: String,
pub fee : Option<u8>,
pub protocols : Option<String>,
pub gas_price : Option<String>,
pub complexity_level : Option<u128>,
pub parts : Option<u128>,
pub main_route_parts : Option<u128>,
pub gas_limit : Option<u128>,
pub include_tokens_info : Option<bool>,
pub include_protocols : Option<bool>,
pub include_gas : Option<bool>,
pub connector_tokens : Option<String>,
}
#[derive(Default)]
pub struct QuoteDetailsBuilder {
pub src: Option<String>,
pub dst: Option<String>,
pub amount: Option<String>,
pub fee : Option<u8>,
pub protocols : Option<String>,
pub gas_price : Option<String>,
pub complexity_level : Option<u128>,
pub parts : Option<u128>,
pub main_route_parts : Option<u128>,
pub gas_limit : Option<u128>,
pub include_tokens_info : Option<bool>,
pub include_protocols : Option<bool>,
pub include_gas : Option<bool>,
pub connector_tokens : Option<String>,
}
impl QuoteDetailsBuilder {
pub fn new() -> Self {
QuoteDetailsBuilder::default()
}
builder_setter!(src, String);
builder_setter!(dst, String);
builder_setter!(amount, String);
builder_setter!(protocols, String);
builder_setter!(gas_price, String);
builder_setter!(complexity_level, u128);
builder_setter!(parts, u128);
builder_setter!(main_route_parts, u128);
builder_setter!(gas_limit, u128);
builder_setter!(include_tokens_info, bool);
builder_setter!(include_protocols, bool);
builder_setter!(include_gas, bool);
builder_setter!(connector_tokens, String);
pub fn fee(mut self, fee: u8) -> Result<Self, QuoteDetailsBuilderError> {
if fee > 3 {
return Err(QuoteDetailsBuilderError::InvalidFee);
}
self.fee = Some(fee);
Ok(self)
}
pub fn build(self) -> Result<QuoteDetails, QuoteDetailsBuilderError> {
Ok(QuoteDetails {
src: self
.src
.ok_or(QuoteDetailsBuilderError::MissingField("src"))?,
dst: self
.dst
.ok_or(QuoteDetailsBuilderError::MissingField("dst"))?,
amount: self
.amount
.ok_or(QuoteDetailsBuilderError::MissingField("amount"))?
.to_string(),
fee: self.fee,
protocols: self.protocols,
gas_price: self.gas_price,
complexity_level: self.complexity_level,
parts: self.parts,
main_route_parts: self.main_route_parts,
gas_limit: self.gas_limit,
include_tokens_info: self.include_tokens_info,
include_protocols: self.include_protocols,
include_gas: self.include_gas,
connector_tokens: self.connector_tokens,
})
}
}
#[derive(Deserialize, Debug)]
pub struct QuoteResponse {
#[serde(rename = "fromToken")]
pub from_token: Option<TokenInfo>,
#[serde(rename = "toToken")]
pub to_token: Option<TokenInfo>,
#[serde(rename = "toAmount")]
pub to_amount: String,
pub protocols: Option<Vec<Vec<Vec<SelectedProtocol>>>>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_swap_details_builder() {
let swap_details = SwapDetailsBuilder::new()
.src("from_token".to_string())
.dst("to_token".to_string())
.amount("1000".to_string())
.from_addr("from_addr".to_string())
.slippage(5)
.expect("Invalid slippage")
.disable_estimate(false)
.allow_partial_fill(false)
.build()
.expect("Failed to build SwapDetails");
assert_eq!(swap_details.src, "from_token");
assert_eq!(swap_details.dst, "to_token");
assert_eq!(swap_details.amount, "1000");
assert_eq!(swap_details.from, "from_addr");
assert_eq!(swap_details.slippage, 5);
assert!(!swap_details.disable_estimate.unwrap());
assert!(!swap_details.allow_partial_fill.unwrap());
}
#[test]
fn test_invalid_slippage_in_builder() {
let result = SwapDetailsBuilder::new()
.src("from_token".to_string())
.dst("to_token".to_string())
.amount("1000".to_string())
.from_addr("from_addr".to_string())
.slippage(102);
assert!(result.is_err());
if let Err(err) = result {
assert_eq!(err, SwapDetailsBuilderError::InvalidSlippage);
}
}
}