1use std::sync::Arc;
2
3#[cfg(feature = "evm")]
4use alloy::primitives::{Address, U256};
5use clap::ValueEnum;
6use num_bigint::BigUint;
7use serde::{Deserialize, Serialize};
8use tycho_common::{
9 models::{protocol::ProtocolComponent, token::Token},
10 simulation::protocol_sim::ProtocolSim,
11 Bytes,
12};
13
14use crate::encoding::serde_primitives::biguint_string;
15
16#[derive(Clone, Debug, PartialEq, ValueEnum, Serialize, Deserialize, Default)]
32pub enum UserTransferType {
33 TransferFromPermit2,
34 #[default]
35 TransferFrom,
36 UseVaultsFunds,
37}
38
39#[derive(Clone, Debug, Default, Deserialize, Serialize)]
44pub struct ClientFeeParams {
45 client_fee_bps: u32,
47 client_fee_receiver: Bytes,
49 #[serde(with = "biguint_string")]
52 max_client_contribution: BigUint,
53 #[serde(with = "biguint_string")]
55 deadline: BigUint,
56 client_signature: Bytes,
58}
59
60impl ClientFeeParams {
61 pub fn new(
63 client_fee_receiver: Bytes,
64 client_signature: Bytes,
65 deadline: BigUint,
66 client_fee_bps: u32,
67 ) -> Self {
68 Self {
69 client_fee_bps,
70 client_fee_receiver,
71 max_client_contribution: BigUint::ZERO,
72 deadline,
73 client_signature,
74 }
75 }
76
77 pub fn new_without_fee(
79 client_fee_receiver: Bytes,
80 client_signature: Bytes,
81 deadline: BigUint,
82 ) -> Self {
83 Self {
84 client_fee_bps: 0,
85 client_fee_receiver,
86 max_client_contribution: BigUint::ZERO,
87 deadline,
88 client_signature,
89 }
90 }
91
92 pub fn with_max_client_contribution(mut self, max_client_contribution: BigUint) -> Self {
93 self.max_client_contribution = max_client_contribution;
94 self
95 }
96}
97
98#[cfg(feature = "evm")]
99impl ClientFeeParams {
100 pub fn into_abi_params(self) -> (u32, Address, U256, U256, Vec<u8>) {
102 let receiver = if self.client_fee_receiver.is_empty() {
103 Address::ZERO
104 } else {
105 Address::from_slice(&self.client_fee_receiver)
106 };
107 (
108 self.client_fee_bps,
109 receiver,
110 U256::from_be_slice(
111 &self
112 .max_client_contribution
113 .to_bytes_be(),
114 ),
115 U256::from_be_slice(&self.deadline.to_bytes_be()),
116 self.client_signature.to_vec(),
117 )
118 }
119}
120
121#[derive(Clone, Debug, Deserialize, Serialize)]
124pub struct Solution {
125 sender: Bytes,
127 receiver: Bytes,
129 token_in: Bytes,
131 #[serde(with = "biguint_string")]
133 amount_in: BigUint,
134 token_out: Bytes,
136 #[serde(with = "biguint_string")]
139 expected_amount_out: BigUint,
140 #[serde(with = "biguint_string")]
143 min_amount_out: BigUint,
144 swaps: Vec<Swap>,
146 user_transfer_type: UserTransferType,
148}
149
150impl Solution {
151 #[allow(clippy::too_many_arguments)]
152 pub fn new(
153 sender: Bytes,
154 receiver: Bytes,
155 token_in: Bytes,
156 token_out: Bytes,
157 amount_in: BigUint,
158 expected_amount_out: BigUint,
159 min_amount_out: BigUint,
160 swaps: Vec<Swap>,
161 ) -> Self {
162 Self {
163 sender,
164 receiver,
165 token_in,
166 token_out,
167 amount_in,
168 expected_amount_out,
169 min_amount_out,
170 swaps,
171 user_transfer_type: UserTransferType::TransferFrom,
172 }
173 }
174 pub fn sender(&self) -> &Bytes {
175 &self.sender
176 }
177 pub fn receiver(&self) -> &Bytes {
178 &self.receiver
179 }
180
181 pub fn token_in(&self) -> &Bytes {
182 &self.token_in
183 }
184
185 pub fn amount_in(&self) -> &BigUint {
186 &self.amount_in
187 }
188
189 pub fn token_out(&self) -> &Bytes {
190 &self.token_out
191 }
192
193 pub fn expected_amount_out(&self) -> &BigUint {
194 &self.expected_amount_out
195 }
196
197 pub fn min_amount_out(&self) -> &BigUint {
198 &self.min_amount_out
199 }
200
201 pub fn swaps(&self) -> &[Swap] {
202 &self.swaps
203 }
204
205 pub fn user_transfer_type(&self) -> &UserTransferType {
206 &self.user_transfer_type
207 }
208
209 pub fn with_swaps(mut self, swaps: Vec<Swap>) -> Self {
210 self.swaps = swaps;
211 self
212 }
213
214 pub fn with_user_transfer_type(mut self, user_transfer_type: UserTransferType) -> Self {
215 self.user_transfer_type = user_transfer_type;
216 self
217 }
218}
219
220#[derive(Clone, Debug, Deserialize, Serialize)]
222pub struct Swap {
223 component: ProtocolComponent,
225 token_in: Token,
227 token_out: Token,
229 #[serde(default)]
231 split: f64,
232 user_data: Option<Bytes>,
234 #[serde(skip)]
236 protocol_state: Option<Arc<dyn ProtocolSim>>,
237 estimated_amount_in: Option<BigUint>,
240 estimated_gas: BigUint,
242}
243
244impl Swap {
245 pub fn new<T: Into<ProtocolComponent>>(
246 component: T,
247 token_in: Token,
248 token_out: Token,
249 estimated_gas: BigUint,
250 ) -> Self {
251 Self {
252 component: component.into(),
253 token_in,
254 token_out,
255 split: 0.0,
256 user_data: None,
257 protocol_state: None,
258 estimated_amount_in: None,
259 estimated_gas,
260 }
261 }
262
263 pub fn with_split(mut self, split: f64) -> Self {
265 self.split = split;
266 self
267 }
268
269 pub fn with_user_data(mut self, user_data: Bytes) -> Self {
271 self.user_data = Some(user_data);
272 self
273 }
274
275 pub fn with_protocol_state(mut self, protocol_state: Arc<dyn ProtocolSim>) -> Self {
277 self.protocol_state = Some(protocol_state);
278 self
279 }
280
281 pub fn with_estimated_amount_in(mut self, estimated_amount_in: BigUint) -> Self {
283 self.estimated_amount_in = Some(estimated_amount_in);
284 self
285 }
286
287 pub fn component(&self) -> &ProtocolComponent {
288 &self.component
289 }
290
291 pub fn token_in(&self) -> &Token {
292 &self.token_in
293 }
294
295 pub fn token_out(&self) -> &Token {
296 &self.token_out
297 }
298
299 pub fn split(&self) -> f64 {
300 self.split
301 }
302
303 pub fn user_data(&self) -> &Option<Bytes> {
304 &self.user_data
305 }
306
307 pub fn protocol_state(&self) -> &Option<Arc<dyn ProtocolSim>> {
308 &self.protocol_state
309 }
310
311 pub fn estimated_amount_in(&self) -> &Option<BigUint> {
312 &self.estimated_amount_in
313 }
314
315 pub fn estimated_gas(&self) -> &BigUint {
316 &self.estimated_gas
317 }
318}
319
320impl PartialEq for Swap {
321 fn eq(&self, other: &Self) -> bool {
322 self.component() == other.component() &&
323 self.token_in().address == other.token_in().address &&
324 self.token_out().address == other.token_out().address &&
325 self.split() == other.split() &&
326 self.user_data() == other.user_data() &&
327 self.estimated_amount_in() == other.estimated_amount_in() &&
328 self.estimated_gas() == other.estimated_gas()
329 }
330}
331
332#[derive(Clone, Debug)]
341pub struct EncodedSolution {
342 swaps: Vec<u8>,
344 interacting_with: Bytes,
346 function_signature: String,
348 n_tokens: usize,
350 estimated_gas: BigUint,
352}
353
354impl EncodedSolution {
355 pub(crate) fn new(
356 swaps: Vec<u8>,
357 interacting_with: Bytes,
358 function_signature: String,
359 n_tokens: usize,
360 estimated_gas: BigUint,
361 ) -> Self {
362 Self { swaps, interacting_with, function_signature, n_tokens, estimated_gas }
363 }
364
365 pub fn swaps(&self) -> &[u8] {
366 &self.swaps
367 }
368
369 pub fn interacting_with(&self) -> &Bytes {
370 &self.interacting_with
371 }
372
373 pub fn function_signature(&self) -> &str {
374 &self.function_signature
375 }
376
377 pub fn n_tokens(&self) -> usize {
378 self.n_tokens
379 }
380
381 pub fn estimated_gas(&self) -> &BigUint {
382 &self.estimated_gas
383 }
384
385 pub fn client_fee_signature_offset(&self) -> usize {
387 let name = self
388 .function_signature
389 .split('(')
390 .next()
391 .unwrap_or("");
392 let head_params = match name {
393 "singleSwap" |
394 "singleSwapUsingVault" |
395 "sequentialSwap" |
396 "sequentialSwapUsingVault" => 8,
397 "splitSwap" | "splitSwapUsingVault" => 9,
398 "singleSwapPermit2" | "sequentialSwapPermit2" => 15,
399 "splitSwapPermit2" => 16,
400 _ => 0,
401 };
402 4 + head_params * 32 + 192
404 }
405}
406
407#[derive(Debug, Clone)]
414pub struct PermitSingle {
415 details: PermitDetails,
416 spender: Bytes,
417 sig_deadline: BigUint,
418}
419
420impl PermitSingle {
421 pub fn new(details: PermitDetails, spender: Bytes, sig_deadline: BigUint) -> Self {
422 Self { details, spender, sig_deadline }
423 }
424
425 pub fn details(&self) -> &PermitDetails {
426 &self.details
427 }
428
429 pub fn spender(&self) -> &Bytes {
430 &self.spender
431 }
432
433 pub fn sig_deadline(&self) -> &BigUint {
434 &self.sig_deadline
435 }
436}
437
438#[derive(Debug, Clone)]
446pub struct PermitDetails {
447 token: Bytes,
448 amount: BigUint,
449 expiration: BigUint,
450 nonce: BigUint,
451}
452
453impl PermitDetails {
454 pub fn new(token: Bytes, amount: BigUint, expiration: BigUint, nonce: BigUint) -> Self {
455 Self { token, amount, expiration, nonce }
456 }
457
458 pub fn token(&self) -> &Bytes {
459 &self.token
460 }
461
462 pub fn amount(&self) -> &BigUint {
463 &self.amount
464 }
465
466 pub fn expiration(&self) -> &BigUint {
467 &self.expiration
468 }
469
470 pub fn nonce(&self) -> &BigUint {
471 &self.nonce
472 }
473}
474
475impl PartialEq for PermitSingle {
476 fn eq(&self, other: &Self) -> bool {
477 self.details == other.details && self.spender == other.spender
478 }
480}
481
482impl PartialEq for PermitDetails {
483 fn eq(&self, other: &Self) -> bool {
484 self.token == other.token && self.amount == other.amount && self.nonce == other.nonce
485 }
487}
488
489#[derive(Clone, Debug)]
498pub struct EncodingContext {
499 pub router_address: Option<Bytes>,
500 pub group_token_in: Bytes,
501 pub group_token_out: Bytes,
502}
503
504#[derive(PartialEq)]
505pub enum Strategy {
506 Single,
507 Sequential,
508 Split,
509}
510
511#[cfg(any(test, feature = "test-utils"))]
514pub fn default_token(address: Bytes) -> Token {
515 Token::new(&address, "", 0, 0, &[Some(60_000u64)], Default::default(), 100)
516}
517
518#[cfg(test)]
519mod tests {
520 use super::*;
521
522 struct MockProtocolComponent {
523 id: String,
524 protocol_system: String,
525 }
526
527 impl From<MockProtocolComponent> for ProtocolComponent {
528 fn from(component: MockProtocolComponent) -> Self {
529 ProtocolComponent {
530 id: component.id,
531 protocol_system: component.protocol_system,
532 tokens: vec![],
533 protocol_type_name: "".to_string(),
534 chain: Default::default(),
535 contract_addresses: vec![],
536 static_attributes: Default::default(),
537 change: Default::default(),
538 creation_tx: Default::default(),
539 created_at: Default::default(),
540 }
541 }
542 }
543
544 #[test]
545 fn test_swap_new() {
546 let component = MockProtocolComponent {
547 id: "i-am-an-id".to_string(),
548 protocol_system: "uniswap_v2".to_string(),
549 };
550 let user_data = Bytes::from("0x1234");
551 let swap = Swap::new(
552 component,
553 default_token(Bytes::from("0x12")),
554 default_token(Bytes::from("0x34")),
555 BigUint::ZERO,
556 )
557 .with_split(0.5)
558 .with_user_data(user_data.clone());
559
560 assert_eq!(swap.token_in().address, Bytes::from("0x12"));
561 assert_eq!(swap.token_out().address, Bytes::from("0x34"));
562 assert_eq!(swap.component().protocol_system, "uniswap_v2");
563 assert_eq!(swap.component().id, "i-am-an-id");
564 assert_eq!(swap.split(), 0.5);
565 assert_eq!(swap.user_data(), &Some(user_data));
566 }
567}