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")]
144 min_amount_out: BigUint,
145 swaps: Vec<Swap>,
147 user_transfer_type: UserTransferType,
149}
150
151impl Solution {
152 #[allow(clippy::too_many_arguments)]
153 pub fn new(
154 sender: Bytes,
155 receiver: Bytes,
156 token_in: Bytes,
157 token_out: Bytes,
158 amount_in: BigUint,
159 expected_amount_out: BigUint,
160 min_amount_out: BigUint,
161 swaps: Vec<Swap>,
162 ) -> Self {
163 Self {
164 sender,
165 receiver,
166 token_in,
167 token_out,
168 amount_in,
169 expected_amount_out,
170 min_amount_out,
171 swaps,
172 user_transfer_type: UserTransferType::TransferFrom,
173 }
174 }
175 pub fn sender(&self) -> &Bytes {
176 &self.sender
177 }
178 pub fn receiver(&self) -> &Bytes {
179 &self.receiver
180 }
181
182 pub fn token_in(&self) -> &Bytes {
183 &self.token_in
184 }
185
186 pub fn amount_in(&self) -> &BigUint {
187 &self.amount_in
188 }
189
190 pub fn token_out(&self) -> &Bytes {
191 &self.token_out
192 }
193
194 pub fn expected_amount_out(&self) -> &BigUint {
195 &self.expected_amount_out
196 }
197
198 pub fn min_amount_out(&self) -> &BigUint {
199 &self.min_amount_out
200 }
201
202 pub fn swaps(&self) -> &[Swap] {
203 &self.swaps
204 }
205
206 pub fn user_transfer_type(&self) -> &UserTransferType {
207 &self.user_transfer_type
208 }
209
210 pub fn with_swaps(mut self, swaps: Vec<Swap>) -> Self {
211 self.swaps = swaps;
212 self
213 }
214
215 pub fn with_user_transfer_type(mut self, user_transfer_type: UserTransferType) -> Self {
216 self.user_transfer_type = user_transfer_type;
217 self
218 }
219}
220
221#[derive(Clone, Debug, Deserialize, Serialize)]
223pub struct Swap {
224 component: ProtocolComponent,
226 token_in: Token,
228 token_out: Token,
230 #[serde(default)]
232 split: f64,
233 user_data: Option<Bytes>,
235 #[serde(skip)]
237 protocol_state: Option<Arc<dyn ProtocolSim>>,
238 estimated_amount_in: Option<BigUint>,
241 estimated_gas: BigUint,
243}
244
245impl Swap {
246 pub fn new<T: Into<ProtocolComponent>>(
247 component: T,
248 token_in: Token,
249 token_out: Token,
250 estimated_gas: BigUint,
251 ) -> Self {
252 Self {
253 component: component.into(),
254 token_in,
255 token_out,
256 split: 0.0,
257 user_data: None,
258 protocol_state: None,
259 estimated_amount_in: None,
260 estimated_gas,
261 }
262 }
263
264 pub fn with_split(mut self, split: f64) -> Self {
266 self.split = split;
267 self
268 }
269
270 pub fn with_user_data(mut self, user_data: Bytes) -> Self {
272 self.user_data = Some(user_data);
273 self
274 }
275
276 pub fn with_protocol_state(mut self, protocol_state: Arc<dyn ProtocolSim>) -> Self {
278 self.protocol_state = Some(protocol_state);
279 self
280 }
281
282 pub fn with_estimated_amount_in(mut self, estimated_amount_in: BigUint) -> Self {
284 self.estimated_amount_in = Some(estimated_amount_in);
285 self
286 }
287
288 pub fn component(&self) -> &ProtocolComponent {
289 &self.component
290 }
291
292 pub fn token_in(&self) -> &Token {
293 &self.token_in
294 }
295
296 pub fn token_out(&self) -> &Token {
297 &self.token_out
298 }
299
300 pub fn split(&self) -> f64 {
301 self.split
302 }
303
304 pub fn user_data(&self) -> &Option<Bytes> {
305 &self.user_data
306 }
307
308 pub fn protocol_state(&self) -> &Option<Arc<dyn ProtocolSim>> {
309 &self.protocol_state
310 }
311
312 pub fn estimated_amount_in(&self) -> &Option<BigUint> {
313 &self.estimated_amount_in
314 }
315
316 pub fn estimated_gas(&self) -> &BigUint {
317 &self.estimated_gas
318 }
319}
320
321impl PartialEq for Swap {
322 fn eq(&self, other: &Self) -> bool {
323 self.component() == other.component() &&
324 self.token_in().address == other.token_in().address &&
325 self.token_out().address == other.token_out().address &&
326 self.split() == other.split() &&
327 self.user_data() == other.user_data() &&
328 self.estimated_amount_in() == other.estimated_amount_in() &&
329 self.estimated_gas() == other.estimated_gas()
330 }
331}
332
333#[derive(Clone, Debug)]
342pub struct EncodedSolution {
343 swaps: Vec<u8>,
345 interacting_with: Bytes,
347 function_signature: String,
349 n_tokens: usize,
351 estimated_gas: BigUint,
353}
354
355impl EncodedSolution {
356 pub(crate) fn new(
357 swaps: Vec<u8>,
358 interacting_with: Bytes,
359 function_signature: String,
360 n_tokens: usize,
361 estimated_gas: BigUint,
362 ) -> Self {
363 Self { swaps, interacting_with, function_signature, n_tokens, estimated_gas }
364 }
365
366 pub fn swaps(&self) -> &[u8] {
367 &self.swaps
368 }
369
370 pub fn interacting_with(&self) -> &Bytes {
371 &self.interacting_with
372 }
373
374 pub fn function_signature(&self) -> &str {
375 &self.function_signature
376 }
377
378 pub fn n_tokens(&self) -> usize {
379 self.n_tokens
380 }
381
382 pub fn estimated_gas(&self) -> &BigUint {
383 &self.estimated_gas
384 }
385
386 pub fn client_fee_signature_offset(&self) -> usize {
388 let name = self
389 .function_signature
390 .split('(')
391 .next()
392 .unwrap_or("");
393 let head_params = match name {
394 "singleSwap" |
395 "singleSwapUsingVault" |
396 "sequentialSwap" |
397 "sequentialSwapUsingVault" => 8,
398 "splitSwap" | "splitSwapUsingVault" => 9,
399 "singleSwapPermit2" | "sequentialSwapPermit2" => 15,
400 "splitSwapPermit2" => 16,
401 _ => 0,
402 };
403 4 + head_params * 32 + 192
405 }
406}
407
408#[derive(Debug, Clone)]
415pub struct PermitSingle {
416 details: PermitDetails,
417 spender: Bytes,
418 sig_deadline: BigUint,
419}
420
421impl PermitSingle {
422 pub fn new(details: PermitDetails, spender: Bytes, sig_deadline: BigUint) -> Self {
423 Self { details, spender, sig_deadline }
424 }
425
426 pub fn details(&self) -> &PermitDetails {
427 &self.details
428 }
429
430 pub fn spender(&self) -> &Bytes {
431 &self.spender
432 }
433
434 pub fn sig_deadline(&self) -> &BigUint {
435 &self.sig_deadline
436 }
437}
438
439#[derive(Debug, Clone)]
447pub struct PermitDetails {
448 token: Bytes,
449 amount: BigUint,
450 expiration: BigUint,
451 nonce: BigUint,
452}
453
454impl PermitDetails {
455 pub fn new(token: Bytes, amount: BigUint, expiration: BigUint, nonce: BigUint) -> Self {
456 Self { token, amount, expiration, nonce }
457 }
458
459 pub fn token(&self) -> &Bytes {
460 &self.token
461 }
462
463 pub fn amount(&self) -> &BigUint {
464 &self.amount
465 }
466
467 pub fn expiration(&self) -> &BigUint {
468 &self.expiration
469 }
470
471 pub fn nonce(&self) -> &BigUint {
472 &self.nonce
473 }
474}
475
476impl PartialEq for PermitSingle {
477 fn eq(&self, other: &Self) -> bool {
478 self.details == other.details && self.spender == other.spender
479 }
481}
482
483impl PartialEq for PermitDetails {
484 fn eq(&self, other: &Self) -> bool {
485 self.token == other.token && self.amount == other.amount && self.nonce == other.nonce
486 }
488}
489
490#[derive(Clone, Debug)]
499pub struct EncodingContext {
500 pub router_address: Option<Bytes>,
501 pub group_token_in: Bytes,
502 pub group_token_out: Bytes,
503}
504
505#[derive(PartialEq)]
506pub enum Strategy {
507 Single,
508 Sequential,
509 Split,
510}
511
512#[cfg(any(test, feature = "test-utils"))]
515pub fn default_token(address: Bytes) -> Token {
516 Token::new(&address, "", 0, 0, &[Some(60_000u64)], Default::default(), 100)
517}
518
519#[cfg(test)]
520mod tests {
521 use super::*;
522
523 struct MockProtocolComponent {
524 id: String,
525 protocol_system: String,
526 }
527
528 impl From<MockProtocolComponent> for ProtocolComponent {
529 fn from(component: MockProtocolComponent) -> Self {
530 ProtocolComponent {
531 id: component.id,
532 protocol_system: component.protocol_system,
533 tokens: vec![],
534 protocol_type_name: "".to_string(),
535 chain: Default::default(),
536 contract_addresses: vec![],
537 static_attributes: Default::default(),
538 change: Default::default(),
539 creation_tx: Default::default(),
540 created_at: Default::default(),
541 }
542 }
543 }
544
545 #[test]
546 fn test_swap_new() {
547 let component = MockProtocolComponent {
548 id: "i-am-an-id".to_string(),
549 protocol_system: "uniswap_v2".to_string(),
550 };
551 let user_data = Bytes::from("0x1234");
552 let swap = Swap::new(
553 component,
554 default_token(Bytes::from("0x12")),
555 default_token(Bytes::from("0x34")),
556 BigUint::ZERO,
557 )
558 .with_split(0.5)
559 .with_user_data(user_data.clone());
560
561 assert_eq!(swap.token_in().address, Bytes::from("0x12"));
562 assert_eq!(swap.token_out().address, Bytes::from("0x34"));
563 assert_eq!(swap.component().protocol_system, "uniswap_v2");
564 assert_eq!(swap.component().id, "i-am-an-id");
565 assert_eq!(swap.split(), 0.5);
566 assert_eq!(swap.user_data(), &Some(user_data));
567 }
568}