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, Default, 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 #[builder(default)]
95 source_blacklist: Vec<String>,
96}
97
98#[derive(Clone, Debug, PartialEq, PartialOrd, Deserialize, Serialize)]
100#[serde(rename_all = "camelCase")]
101pub struct SingleQuoteResponse {
102 block_number: u64,
103 data_gas_estimate: u64,
104 gas_estimate: f64,
105 gas_estimate_value: f64,
106 gwei_per_gas: f64,
107 in_amounts: Vec<String>,
108 in_tokens: Vec<Address>,
109 in_values: Vec<f64>,
110 net_out_value: f64,
111 out_amounts: Vec<String>,
112 out_tokens: Vec<Address>,
113 out_values: Vec<f64>,
114 partner_fee_percent: f64,
115 path_id: String,
116 path_viz: Option<String>,
117 percent_diff: f64,
118 price_impact: f64,
119}
120
121impl SingleQuoteResponse {
122 pub fn data_gas_estimate(&self) -> u64 {
124 self.data_gas_estimate
125 }
126
127 pub fn get_block_number(&self) -> u64 {
129 self.block_number
130 }
131
132 pub fn gas_estimate(&self) -> f64 {
134 self.gas_estimate
135 }
136
137 pub fn in_amounts_iter(&self) -> impl Iterator<Item = &String> {
139 self.in_amounts.iter()
140 }
141
142 pub fn in_amount_u256(&self) -> anyhow::Result<U256> {
144 let amount_str = self
145 .in_amounts_iter()
146 .next()
147 .ok_or(anyhow::anyhow!("Missing input amount"))?;
148 let amount: u128 = amount_str.parse()?;
149 Ok(U256::from(amount))
150 }
151
152 pub fn out_amount(&self) -> Option<&String> {
154 self.out_amounts.first()
155 }
156
157 pub fn out_amounts_iter(&self) -> impl Iterator<Item = &String> {
159 self.out_amounts.iter()
160 }
161
162 pub fn in_tokens_iter(&self) -> impl Iterator<Item = &Address> {
164 self.in_tokens.iter()
165 }
166
167 pub fn first_in_token(&self) -> Option<&Address> {
169 self.in_tokens.first()
170 }
171
172 pub fn out_tokens_iter(&self) -> impl Iterator<Item = &Address> {
173 self.out_tokens.iter()
174 }
175
176 pub fn first_out_token(&self) -> Option<&Address> {
178 self.out_tokens.first()
179 }
180
181 pub fn out_values_iter(&self) -> impl Iterator<Item = &f64> {
183 self.out_values.iter()
184 }
185
186 pub fn path_id(&self) -> &str {
188 &self.path_id
189 }
190
191 pub fn path_definition_as_vec_u8(&self) -> Vec<u8> {
193 self.path_id().as_bytes().to_vec()
194 }
195
196 pub fn swap_input_token_and_amount(&self) -> anyhow::Result<(Address, U256)> {
198 let input_token = *self
199 .in_tokens_iter()
200 .next()
201 .ok_or(anyhow::anyhow!("Missing input token"))?;
202 let input_amount_in_u256 = self.in_amount_u256()?;
203
204 Ok((input_token, input_amount_in_u256))
205 }
206
207 pub fn price_impact(&self) -> f64 {
209 self.price_impact
210 }
211}
212
213#[derive(Clone, Debug)]
214pub struct SwapInputs {
215 executor: Address,
216 path_definition: Bytes,
217 input_token_info: inputTokenInfo,
218 output_token_info: outputTokenInfo,
219 value_out_min: U256,
220}
221
222impl TryFrom<OdosV2RouterCalls> for SwapInputs {
223 type Error = anyhow::Error;
224
225 fn try_from(swap: OdosV2RouterCalls) -> Result<Self, Self::Error> {
226 match swap {
227 OdosV2RouterCalls::swap(call) => {
228 debug!(call = ?call);
229
230 let swapCall {
231 executor,
232 pathDefinition,
233 referralCode,
234 tokenInfo,
235 } = call;
236
237 let _referral_code = referralCode;
238
239 let swapTokenInfo {
240 inputToken,
241 inputAmount,
242 inputReceiver,
243 outputMin,
244 outputQuote,
245 outputReceiver,
246 outputToken,
247 } = tokenInfo;
248
249 let _output_quote = outputQuote;
250
251 Ok(Self {
252 executor,
253 path_definition: pathDefinition,
254 input_token_info: inputTokenInfo {
255 tokenAddress: inputToken,
256 amountIn: inputAmount,
257 receiver: inputReceiver,
258 },
259 output_token_info: outputTokenInfo {
260 tokenAddress: outputToken,
261 relativeValue: U256::from(1),
262 receiver: outputReceiver,
263 },
264 value_out_min: outputMin,
265 })
266 }
267 _ => Err(anyhow::anyhow!("Unexpected OdosV2RouterCalls")),
268 }
269 }
270}
271
272impl SwapInputs {
273 pub fn executor(&self) -> Address {
275 self.executor
276 }
277
278 pub fn path_definition(&self) -> &Bytes {
280 &self.path_definition
281 }
282
283 pub fn token_address(&self) -> Address {
285 self.input_token_info.tokenAddress
286 }
287
288 pub fn amount_in(&self) -> U256 {
290 self.input_token_info.amountIn
291 }
292
293 pub fn receiver(&self) -> Address {
295 self.input_token_info.receiver
296 }
297
298 pub fn relative_value(&self) -> U256 {
300 self.output_token_info.relativeValue
301 }
302
303 pub fn output_token_address(&self) -> Address {
305 self.output_token_info.tokenAddress
306 }
307
308 pub fn value_out_min(&self) -> U256 {
310 self.value_out_min
311 }
312}