1use std::fmt::Display;
2
3use alloy_primitives::{Address, Bytes, U256};
4use bon::Builder;
5use serde::{Deserialize, Serialize};
6use tracing::debug;
7
8use crate::{
9 OdosError,
10 OdosRouterV2::{inputTokenInfo, outputTokenInfo, swapTokenInfo},
11 OdosV2Router::{swapCall, OdosV2RouterCalls},
12 Result,
13};
14
15#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
17#[serde(rename_all = "camelCase")]
18pub struct InputToken {
19 token_address: String,
21 amount: String,
23}
24
25impl InputToken {
26 pub fn new(token_address: Address, amount: U256) -> Self {
27 Self {
28 token_address: token_address.to_string(),
29 amount: amount.to_string(),
30 }
31 }
32}
33
34impl From<(Address, U256)> for InputToken {
35 fn from((token_address, amount): (Address, U256)) -> Self {
36 Self::new(token_address, amount)
37 }
38}
39
40impl Display for InputToken {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 write!(
43 f,
44 "InputToken {{ token_address: {}, amount: {} }}",
45 self.token_address, self.amount
46 )
47 }
48}
49
50#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
52#[serde(rename_all = "camelCase")]
53pub struct OutputToken {
54 token_address: String,
56 proportion: u32,
57}
58
59impl OutputToken {
60 pub fn new(token_address: Address, proportion: u32) -> Self {
61 Self {
62 token_address: token_address.to_string(),
63 proportion,
64 }
65 }
66}
67
68impl From<(Address, u32)> for OutputToken {
69 fn from((token_address, proportion): (Address, u32)) -> Self {
70 Self::new(token_address, proportion)
71 }
72}
73
74impl Display for OutputToken {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 write!(
77 f,
78 "OutputToken {{ token_address: {}, proportion: {} }}",
79 self.token_address, self.proportion
80 )
81 }
82}
83
84#[derive(Builder, Clone, Debug, Default, PartialEq, PartialOrd, Deserialize, Serialize)]
86#[serde(rename_all = "camelCase")]
87pub struct QuoteRequest {
88 chain_id: u64,
89 input_tokens: Vec<InputToken>,
90 output_tokens: Vec<OutputToken>,
91 slippage_limit_percent: f64,
92 user_addr: String,
94 compact: bool,
95 simple: bool,
96 referral_code: u32,
97 disable_rfqs: bool,
98 #[builder(default)]
99 source_blacklist: Vec<String>,
100}
101
102#[derive(Clone, Debug, PartialEq, PartialOrd, Deserialize, Serialize)]
104#[serde(rename_all = "camelCase")]
105pub struct SingleQuoteResponse {
106 block_number: u64,
107 data_gas_estimate: u64,
108 gas_estimate: f64,
109 gas_estimate_value: f64,
110 gwei_per_gas: f64,
111 in_amounts: Vec<String>,
112 in_tokens: Vec<Address>,
113 in_values: Vec<f64>,
114 net_out_value: f64,
115 out_amounts: Vec<String>,
116 out_tokens: Vec<Address>,
117 out_values: Vec<f64>,
118 partner_fee_percent: f64,
119 path_id: String,
120 path_viz: Option<String>,
121 percent_diff: f64,
122 price_impact: f64,
123}
124
125impl SingleQuoteResponse {
126 pub fn data_gas_estimate(&self) -> u64 {
128 self.data_gas_estimate
129 }
130
131 pub fn get_block_number(&self) -> u64 {
133 self.block_number
134 }
135
136 pub fn gas_estimate(&self) -> f64 {
138 self.gas_estimate
139 }
140
141 pub fn in_amounts_iter(&self) -> impl Iterator<Item = &String> {
143 self.in_amounts.iter()
144 }
145
146 pub fn in_amount_u256(&self) -> Result<U256> {
148 let amount_str = self
149 .in_amounts_iter()
150 .next()
151 .ok_or_else(|| OdosError::missing_data("Missing input amount"))?;
152 let amount: u128 = amount_str
153 .parse()
154 .map_err(|_| OdosError::invalid_input("Invalid input amount format"))?;
155 Ok(U256::from(amount))
156 }
157
158 pub fn out_amount(&self) -> Option<&String> {
160 self.out_amounts.first()
161 }
162
163 pub fn out_amounts_iter(&self) -> impl Iterator<Item = &String> {
165 self.out_amounts.iter()
166 }
167
168 pub fn in_tokens_iter(&self) -> impl Iterator<Item = &Address> {
170 self.in_tokens.iter()
171 }
172
173 pub fn first_in_token(&self) -> Option<&Address> {
175 self.in_tokens.first()
176 }
177
178 pub fn out_tokens_iter(&self) -> impl Iterator<Item = &Address> {
179 self.out_tokens.iter()
180 }
181
182 pub fn first_out_token(&self) -> Option<&Address> {
184 self.out_tokens.first()
185 }
186
187 pub fn out_values_iter(&self) -> impl Iterator<Item = &f64> {
189 self.out_values.iter()
190 }
191
192 pub fn path_id(&self) -> &str {
194 &self.path_id
195 }
196
197 pub fn path_definition_as_vec_u8(&self) -> Vec<u8> {
199 self.path_id().as_bytes().to_vec()
200 }
201
202 pub fn swap_input_token_and_amount(&self) -> Result<(Address, U256)> {
204 let input_token = *self
205 .in_tokens_iter()
206 .next()
207 .ok_or_else(|| OdosError::missing_data("Missing input token"))?;
208 let input_amount_in_u256 = self.in_amount_u256()?;
209
210 Ok((input_token, input_amount_in_u256))
211 }
212
213 pub fn price_impact(&self) -> f64 {
215 self.price_impact
216 }
217}
218
219#[derive(Clone, Debug)]
221pub struct SwapInputs {
222 executor: Address,
223 path_definition: Bytes,
224 input_token_info: inputTokenInfo,
225 output_token_info: outputTokenInfo,
226 value_out_min: U256,
227}
228
229impl TryFrom<OdosV2RouterCalls> for SwapInputs {
230 type Error = OdosError;
231
232 fn try_from(swap: OdosV2RouterCalls) -> std::result::Result<Self, Self::Error> {
233 match swap {
234 OdosV2RouterCalls::swap(call) => {
235 debug!(call = ?call);
236
237 let swapCall {
238 executor,
239 pathDefinition,
240 referralCode,
241 tokenInfo,
242 } = call;
243
244 let _referral_code = referralCode;
245
246 let swapTokenInfo {
247 inputToken,
248 inputAmount,
249 inputReceiver,
250 outputMin,
251 outputQuote,
252 outputReceiver,
253 outputToken,
254 } = tokenInfo;
255
256 let _output_quote = outputQuote;
257
258 Ok(Self {
259 executor,
260 path_definition: pathDefinition,
261 input_token_info: inputTokenInfo {
262 tokenAddress: inputToken,
263 amountIn: inputAmount,
264 receiver: inputReceiver,
265 },
266 output_token_info: outputTokenInfo {
267 tokenAddress: outputToken,
268 relativeValue: U256::from(1),
269 receiver: outputReceiver,
270 },
271 value_out_min: outputMin,
272 })
273 }
274 _ => Err(OdosError::invalid_input("Unexpected OdosV2RouterCalls")),
275 }
276 }
277}
278
279impl SwapInputs {
280 pub fn executor(&self) -> Address {
282 self.executor
283 }
284
285 pub fn path_definition(&self) -> &Bytes {
287 &self.path_definition
288 }
289
290 pub fn token_address(&self) -> Address {
292 self.input_token_info.tokenAddress
293 }
294
295 pub fn amount_in(&self) -> U256 {
297 self.input_token_info.amountIn
298 }
299
300 pub fn receiver(&self) -> Address {
302 self.input_token_info.receiver
303 }
304
305 pub fn relative_value(&self) -> U256 {
307 self.output_token_info.relativeValue
308 }
309
310 pub fn output_token_address(&self) -> Address {
312 self.output_token_info.tokenAddress
313 }
314
315 pub fn value_out_min(&self) -> U256 {
317 self.value_out_min
318 }
319}