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