1use std::{collections::HashSet, str::FromStr};
2
3use alloy::primitives::{aliases::U24, U8};
4use tycho_common::{models::Chain, Bytes};
5
6use crate::encoding::{
7 errors::EncodingError,
8 evm::{
9 group_swaps::group_swaps,
10 strategy_encoder::{
11 strategy_validators::{SequentialSwapValidator, SplitSwapValidator, SwapValidator},
12 transfer_optimizations::TransferOptimization,
13 },
14 swap_encoder::swap_encoder_registry::SwapEncoderRegistry,
15 utils::{get_token_position, percentage_to_uint24, ple_encode},
16 },
17 models::{EncodedSolution, EncodingContext, NativeAction, Solution, UserTransferType},
18 strategy_encoder::StrategyEncoder,
19 swap_encoder::SwapEncoder,
20};
21
22#[derive(Clone)]
30pub struct SingleSwapStrategyEncoder {
31 swap_encoder_registry: SwapEncoderRegistry,
32 function_signature: String,
33 router_address: Bytes,
34 transfer_optimization: TransferOptimization,
35}
36
37impl SingleSwapStrategyEncoder {
38 pub fn new(
39 chain: Chain,
40 swap_encoder_registry: SwapEncoderRegistry,
41 user_transfer_type: UserTransferType,
42 router_address: Bytes,
43 ) -> Result<Self, EncodingError> {
44 let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
45 "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
46 } else {
47 "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
48 }.to_string();
49
50 Ok(Self {
51 function_signature,
52 swap_encoder_registry,
53 router_address: router_address.clone(),
54 transfer_optimization: TransferOptimization::new(
55 chain.native_token().address,
56 chain.wrapped_native_token().address,
57 user_transfer_type,
58 router_address,
59 ),
60 })
61 }
62
63 fn encode_swap_header(&self, executor_address: Bytes, protocol_data: Vec<u8>) -> Vec<u8> {
66 let mut encoded = Vec::new();
67 encoded.extend(executor_address.to_vec());
68 encoded.extend(protocol_data);
69 encoded
70 }
71}
72
73impl StrategyEncoder for SingleSwapStrategyEncoder {
74 fn encode_strategy(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
75 let grouped_swaps = group_swaps(&solution.swaps);
76 let number_of_groups = grouped_swaps.len();
77 if number_of_groups != 1 {
78 return Err(EncodingError::InvalidInput(format!(
79 "Single strategy only supports exactly one swap for non-groupable protocols. Found {number_of_groups}",
80 )))
81 }
82
83 let grouped_swap = grouped_swaps
84 .first()
85 .ok_or_else(|| EncodingError::FatalError("Swap grouping failed".to_string()))?;
86
87 if grouped_swap.split != 0f64 {
88 return Err(EncodingError::InvalidInput(
89 "Splits not supported for single swaps.".to_string(),
90 ))
91 }
92
93 let (mut unwrap, mut wrap) = (false, false);
94 if let Some(action) = &solution.native_action {
95 match *action {
96 NativeAction::Wrap => wrap = true,
97 NativeAction::Unwrap => unwrap = true,
98 }
99 }
100 let protocol = &grouped_swap.protocol_system;
101 let swap_encoder = self
102 .get_swap_encoder(protocol)
103 .ok_or_else(|| {
104 EncodingError::InvalidInput(format!(
105 "Swap encoder not found for protocol: {protocol}"
106 ))
107 })?;
108
109 let swap_receiver =
110 if !unwrap { solution.receiver.clone() } else { self.router_address.clone() };
111
112 let transfer = self
113 .transfer_optimization
114 .get_transfers(grouped_swap, &solution.given_token, wrap, false);
115 let encoding_context = EncodingContext {
116 receiver: swap_receiver,
117 exact_out: solution.exact_out,
118 router_address: Some(self.router_address.clone()),
119 group_token_in: grouped_swap.token_in.clone(),
120 group_token_out: grouped_swap.token_out.clone(),
121 transfer_type: transfer,
122 };
123
124 let mut grouped_protocol_data: Vec<u8> = vec![];
125 for swap in grouped_swap.swaps.iter() {
126 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
127 grouped_protocol_data.extend(protocol_data);
128 }
129
130 let swap_data = self.encode_swap_header(
131 Bytes::from_str(swap_encoder.executor_address())
132 .map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?,
133 grouped_protocol_data,
134 );
135 Ok(EncodedSolution {
136 function_signature: self.function_signature.clone(),
137 interacting_with: self.router_address.clone(),
138 swaps: swap_data,
139 permit: None,
140 n_tokens: 0,
141 })
142 }
143
144 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
145 self.swap_encoder_registry
146 .get_encoder(protocol_system)
147 }
148
149 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
150 Box::new(self.clone())
151 }
152}
153
154#[derive(Clone)]
166pub struct SequentialSwapStrategyEncoder {
167 swap_encoder_registry: SwapEncoderRegistry,
168 function_signature: String,
169 router_address: Bytes,
170 native_address: Bytes,
171 wrapped_address: Bytes,
172 sequential_swap_validator: SequentialSwapValidator,
173 transfer_optimization: TransferOptimization,
174}
175
176impl SequentialSwapStrategyEncoder {
177 pub fn new(
178 chain: Chain,
179 swap_encoder_registry: SwapEncoderRegistry,
180 user_transfer_type: UserTransferType,
181 router_address: Bytes,
182 ) -> Result<Self, EncodingError> {
183 let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
184 "sequentialSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
185 } else {
186 "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
187
188 }.to_string();
189 let native_token_address = chain.native_token().address;
190 let wrapped_token_address = chain.wrapped_native_token().address;
191 Ok(Self {
192 function_signature,
193 swap_encoder_registry,
194 router_address: router_address.clone(),
195 native_address: native_token_address.clone(),
196 wrapped_address: wrapped_token_address.clone(),
197 sequential_swap_validator: SequentialSwapValidator,
198 transfer_optimization: TransferOptimization::new(
199 native_token_address,
200 wrapped_token_address,
201 user_transfer_type,
202 router_address,
203 ),
204 })
205 }
206
207 fn encode_swap_header(&self, executor_address: Bytes, protocol_data: Vec<u8>) -> Vec<u8> {
210 let mut encoded = Vec::new();
211 encoded.extend(executor_address.to_vec());
212 encoded.extend(protocol_data);
213 encoded
214 }
215}
216
217impl StrategyEncoder for SequentialSwapStrategyEncoder {
218 fn encode_strategy(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
219 self.sequential_swap_validator
220 .validate_swap_path(
221 &solution.swaps,
222 &solution.given_token,
223 &solution.checked_token,
224 &solution.native_action,
225 &self.native_address,
226 &self.wrapped_address,
227 )?;
228
229 let grouped_swaps = group_swaps(&solution.swaps);
230
231 let (mut wrap, mut unwrap) = (false, false);
232 if let Some(action) = &solution.native_action {
233 match *action {
234 NativeAction::Wrap => wrap = true,
235 NativeAction::Unwrap => unwrap = true,
236 }
237 }
238
239 let mut swaps = vec![];
240 let mut next_in_between_swap_optimization_allowed = true;
241 for (i, grouped_swap) in grouped_swaps.iter().enumerate() {
242 let protocol = &grouped_swap.protocol_system;
243 let swap_encoder = self
244 .get_swap_encoder(protocol)
245 .ok_or_else(|| {
246 EncodingError::InvalidInput(format!(
247 "Swap encoder not found for protocol: {protocol}",
248 ))
249 })?;
250
251 let in_between_swap_optimization_allowed = next_in_between_swap_optimization_allowed;
252 let next_swap = grouped_swaps.get(i + 1);
253 let (swap_receiver, next_swap_optimization) = self
254 .transfer_optimization
255 .get_receiver(&solution.receiver, next_swap, unwrap)?;
256 next_in_between_swap_optimization_allowed = next_swap_optimization;
257
258 let transfer = self
259 .transfer_optimization
260 .get_transfers(
261 grouped_swap,
262 &solution.given_token,
263 wrap,
264 in_between_swap_optimization_allowed,
265 );
266 let encoding_context = EncodingContext {
267 receiver: swap_receiver,
268 exact_out: solution.exact_out,
269 router_address: Some(self.router_address.clone()),
270 group_token_in: grouped_swap.token_in.clone(),
271 group_token_out: grouped_swap.token_out.clone(),
272 transfer_type: transfer,
273 };
274
275 let mut grouped_protocol_data: Vec<u8> = vec![];
276 for swap in grouped_swap.swaps.iter() {
277 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
278 grouped_protocol_data.extend(protocol_data);
279 }
280
281 let swap_data = self.encode_swap_header(
282 Bytes::from_str(swap_encoder.executor_address()).map_err(|_| {
283 EncodingError::FatalError("Invalid executor address".to_string())
284 })?,
285 grouped_protocol_data,
286 );
287 swaps.push(swap_data);
288 }
289
290 let encoded_swaps = ple_encode(swaps);
291 Ok(EncodedSolution {
292 interacting_with: self.router_address.clone(),
293 function_signature: self.function_signature.clone(),
294 swaps: encoded_swaps,
295 permit: None,
296 n_tokens: 0,
297 })
298 }
299
300 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
301 self.swap_encoder_registry
302 .get_encoder(protocol_system)
303 }
304
305 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
306 Box::new(self.clone())
307 }
308}
309
310#[derive(Clone)]
322pub struct SplitSwapStrategyEncoder {
323 swap_encoder_registry: SwapEncoderRegistry,
324 function_signature: String,
325 native_address: Bytes,
326 wrapped_address: Bytes,
327 split_swap_validator: SplitSwapValidator,
328 router_address: Bytes,
329 transfer_optimization: TransferOptimization,
330}
331
332impl SplitSwapStrategyEncoder {
333 pub fn new(
334 chain: Chain,
335 swap_encoder_registry: SwapEncoderRegistry,
336 user_transfer_type: UserTransferType,
337 router_address: Bytes,
338 ) -> Result<Self, EncodingError> {
339 let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
340 "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
341 } else {
342 "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
343 }.to_string();
344 let native_token_address = chain.native_token().address;
345 let wrapped_token_address = chain.wrapped_native_token().address;
346 Ok(Self {
347 function_signature,
348 swap_encoder_registry,
349 native_address: native_token_address.clone(),
350 wrapped_address: wrapped_token_address.clone(),
351 split_swap_validator: SplitSwapValidator,
352 router_address: router_address.clone(),
353 transfer_optimization: TransferOptimization::new(
354 native_token_address,
355 wrapped_token_address,
356 user_transfer_type,
357 router_address,
358 ),
359 })
360 }
361
362 fn encode_swap_header(
365 &self,
366 token_in: U8,
367 token_out: U8,
368 split: U24,
369 executor_address: Bytes,
370 protocol_data: Vec<u8>,
371 ) -> Vec<u8> {
372 let mut encoded = Vec::new();
373 encoded.push(token_in.to_be_bytes_vec()[0]);
374 encoded.push(token_out.to_be_bytes_vec()[0]);
375 encoded.extend_from_slice(&split.to_be_bytes_vec());
376 encoded.extend(executor_address.to_vec());
377 encoded.extend(protocol_data);
378 encoded
379 }
380}
381
382impl StrategyEncoder for SplitSwapStrategyEncoder {
383 fn encode_strategy(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
384 self.split_swap_validator
385 .validate_split_percentages(&solution.swaps)?;
386 self.split_swap_validator
387 .validate_swap_path(
388 &solution.swaps,
389 &solution.given_token,
390 &solution.checked_token,
391 &solution.native_action,
392 &self.native_address,
393 &self.wrapped_address,
394 )?;
395
396 let solution_tokens: HashSet<&Bytes> = vec![&solution.given_token, &solution.checked_token]
399 .into_iter()
400 .collect();
401
402 let grouped_swaps = group_swaps(&solution.swaps);
403
404 let intermediary_tokens: HashSet<&Bytes> = grouped_swaps
405 .iter()
406 .flat_map(|grouped_swap| vec![&grouped_swap.token_in, &grouped_swap.token_out])
407 .collect();
408 let mut intermediary_tokens: Vec<&Bytes> = intermediary_tokens
409 .difference(&solution_tokens)
410 .cloned()
411 .collect();
412 intermediary_tokens.sort();
415
416 let (mut unwrap, mut wrap) = (false, false);
417 if let Some(action) = &solution.native_action {
418 match *action {
419 NativeAction::Wrap => wrap = true,
420 NativeAction::Unwrap => unwrap = true,
421 }
422 }
423
424 let mut tokens = Vec::with_capacity(2 + intermediary_tokens.len());
425 if wrap {
426 tokens.push(&self.wrapped_address);
427 } else {
428 tokens.push(&solution.given_token);
429 }
430 tokens.extend(intermediary_tokens);
431
432 if unwrap {
433 tokens.push(&self.wrapped_address);
434 } else {
435 tokens.push(&solution.checked_token);
436 }
437
438 let mut swaps = vec![];
439 for grouped_swap in grouped_swaps.iter() {
440 let protocol = &grouped_swap.protocol_system;
441 let swap_encoder = self
442 .get_swap_encoder(protocol)
443 .ok_or_else(|| {
444 EncodingError::InvalidInput(format!(
445 "Swap encoder not found for protocol: {protocol}",
446 ))
447 })?;
448
449 let swap_receiver = if !unwrap && grouped_swap.token_out == solution.checked_token {
450 solution.receiver.clone()
451 } else {
452 self.router_address.clone()
453 };
454 let transfer = self
455 .transfer_optimization
456 .get_transfers(grouped_swap, &solution.given_token, wrap, false);
457 let encoding_context = EncodingContext {
458 receiver: swap_receiver,
459 exact_out: solution.exact_out,
460 router_address: Some(self.router_address.clone()),
461 group_token_in: grouped_swap.token_in.clone(),
462 group_token_out: grouped_swap.token_out.clone(),
463 transfer_type: transfer,
464 };
465
466 let mut grouped_protocol_data: Vec<u8> = vec![];
467 for swap in grouped_swap.swaps.iter() {
468 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
469 grouped_protocol_data.extend(protocol_data);
470 }
471
472 let swap_data = self.encode_swap_header(
473 get_token_position(&tokens, &grouped_swap.token_in)?,
474 get_token_position(&tokens, &grouped_swap.token_out)?,
475 percentage_to_uint24(grouped_swap.split),
476 Bytes::from_str(swap_encoder.executor_address()).map_err(|_| {
477 EncodingError::FatalError("Invalid executor address".to_string())
478 })?,
479 grouped_protocol_data,
480 );
481 swaps.push(swap_data);
482 }
483
484 let encoded_swaps = ple_encode(swaps);
485 let tokens_len = if solution.given_token == solution.checked_token {
486 tokens.len() - 1
487 } else {
488 tokens.len()
489 };
490 Ok(EncodedSolution {
491 interacting_with: self.router_address.clone(),
492 function_signature: self.function_signature.clone(),
493 swaps: encoded_swaps,
494 permit: None,
495 n_tokens: tokens_len,
496 })
497 }
498
499 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
500 self.swap_encoder_registry
501 .get_encoder(protocol_system)
502 }
503
504 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
505 Box::new(self.clone())
506 }
507}
508
509#[cfg(test)]
510mod tests {
511 use std::{collections::HashMap, str::FromStr};
512
513 use alloy::{hex::encode, primitives::hex};
514 use num_bigint::{BigInt, BigUint};
515 use tycho_common::{
516 models::{protocol::ProtocolComponent, Chain},
517 Bytes,
518 };
519
520 use super::*;
521
522 fn eth_chain() -> Chain {
523 Chain::Ethereum
524 }
525
526 fn weth() -> Bytes {
527 Bytes::from(hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").to_vec())
528 }
529
530 fn get_swap_encoder_registry() -> SwapEncoderRegistry {
531 let eth_chain = eth_chain();
532 SwapEncoderRegistry::new(Some("config/test_executor_addresses.json".to_string()), eth_chain)
533 .unwrap()
534 }
535
536 fn router_address() -> Bytes {
537 Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap()
538 }
539
540 mod single {
541 use super::*;
542 use crate::encoding::models::SwapBuilder;
543 #[test]
544 fn test_single_swap_strategy_encoder() {
545 let checked_amount = BigUint::from_str("2018817438608734439720").unwrap();
548 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
549 let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
550
551 let swap = SwapBuilder::new(
552 ProtocolComponent {
553 id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
554 protocol_system: "uniswap_v2".to_string(),
555 ..Default::default()
556 },
557 weth.clone(),
558 dai.clone(),
559 )
560 .build();
561 let swap_encoder_registry = get_swap_encoder_registry();
562 let encoder = SingleSwapStrategyEncoder::new(
563 eth_chain(),
564 swap_encoder_registry,
565 UserTransferType::TransferFromPermit2,
566 router_address(),
567 )
568 .unwrap();
569 let solution = Solution {
570 exact_out: false,
571 given_token: weth,
572 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
573 checked_token: dai,
574 checked_amount: checked_amount.clone(),
575 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
576 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
577 swaps: vec![swap],
578 ..Default::default()
579 };
580
581 let encoded_solution = encoder
582 .encode_strategy(&solution)
583 .unwrap();
584
585 let expected_swap = String::from(concat!(
586 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "00", ));
594 let hex_calldata = encode(&encoded_solution.swaps);
595
596 assert_eq!(hex_calldata, expected_swap);
597 assert_eq!(encoded_solution.function_signature, "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string());
598 assert_eq!(encoded_solution.interacting_with, router_address());
599 }
600
601 #[test]
602 fn test_single_swap_strategy_encoder_no_transfer_in() {
603 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
607 let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
608
609 let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap();
610
611 let swap = SwapBuilder::new(
612 ProtocolComponent {
613 id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
614 protocol_system: "uniswap_v2".to_string(),
615 ..Default::default()
616 },
617 weth.clone(),
618 dai.clone(),
619 )
620 .build();
621 let swap_encoder_registry = get_swap_encoder_registry();
622 let encoder = SingleSwapStrategyEncoder::new(
623 eth_chain(),
624 swap_encoder_registry,
625 UserTransferType::None,
626 router_address(),
627 )
628 .unwrap();
629 let solution = Solution {
630 exact_out: false,
631 given_token: weth,
632 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
633 checked_token: dai,
634 checked_amount,
635 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
636 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
637 swaps: vec![swap],
638 ..Default::default()
639 };
640
641 let encoded_solution = encoder
642 .encode_strategy(&solution)
643 .unwrap();
644
645 let expected_input = [
646 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "01", ]
654 .join("");
655
656 let hex_calldata = encode(&encoded_solution.swaps);
657
658 assert_eq!(hex_calldata, expected_input);
659 assert_eq!(
660 encoded_solution.function_signature,
661 "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
662 .to_string()
663 );
664 assert_eq!(encoded_solution.interacting_with, router_address());
665 }
666 }
667
668 mod sequential {
669 use super::*;
670 use crate::encoding::models::SwapBuilder;
671
672 #[test]
673 fn test_sequential_swap_strategy_encoder_no_permit2() {
674 let weth = weth();
679 let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap();
680 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
681
682 let swap_weth_wbtc = SwapBuilder::new(
683 ProtocolComponent {
684 id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(),
685 protocol_system: "uniswap_v2".to_string(),
686 ..Default::default()
687 },
688 weth.clone(),
689 wbtc.clone(),
690 )
691 .build();
692 let swap_wbtc_usdc = SwapBuilder::new(
693 ProtocolComponent {
694 id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(),
695 protocol_system: "uniswap_v2".to_string(),
696 ..Default::default()
697 },
698 wbtc.clone(),
699 usdc.clone(),
700 )
701 .build();
702 let swap_encoder_registry = get_swap_encoder_registry();
703 let encoder = SequentialSwapStrategyEncoder::new(
704 eth_chain(),
705 swap_encoder_registry,
706 UserTransferType::TransferFrom,
707 router_address(),
708 )
709 .unwrap();
710 let solution = Solution {
711 exact_out: false,
712 given_token: weth,
713 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
714 checked_token: usdc,
715 checked_amount: BigUint::from_str("26173932").unwrap(),
716 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
717 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
718 swaps: vec![swap_weth_wbtc, swap_wbtc_usdc],
719 ..Default::default()
720 };
721
722 let encoded_solution = encoder
723 .encode_strategy(&solution)
724 .unwrap();
725
726 let hex_calldata = encode(&encoded_solution.swaps);
727
728 let expected = String::from(concat!(
729 "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "bb2b8038a1640196fbe3e38816f3e67cba72d940", "004375dff511095cc5a197a54140a24efef3a416", "00", "00", "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "2260fac5e5542a773aa44fbcfedf7c193bc2c599", "004375dff511095cc5a197a54140a24efef3a416", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "01", "02", ));
746
747 assert_eq!(hex_calldata, expected);
748 assert_eq!(
749 encoded_solution.function_signature,
750 "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
751 .to_string()
752 );
753 assert_eq!(encoded_solution.interacting_with, router_address());
754 }
755 }
756
757 mod split {
758 use super::*;
759 use crate::encoding::models::SwapBuilder;
760
761 #[test]
762 fn test_split_input_cyclic_swap() {
763 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
772 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
773
774 let swap_usdc_weth_pool1 = SwapBuilder::new(
776 ProtocolComponent {
777 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
780 static_attributes: {
781 let mut attrs = HashMap::new();
782 attrs.insert(
783 "fee".to_string(),
784 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
785 );
786 attrs
787 },
788 ..Default::default()
789 },
790 usdc.clone(),
791 weth.clone(),
792 )
793 .split(0.6f64)
794 .build();
795
796 let swap_usdc_weth_pool2 = SwapBuilder::new(
798 ProtocolComponent {
799 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
802 static_attributes: {
803 let mut attrs = HashMap::new();
804 attrs.insert(
805 "fee".to_string(),
806 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
807 );
808 attrs
809 },
810 ..Default::default()
811 },
812 usdc.clone(),
813 weth.clone(),
814 )
815 .build();
816
817 let swap_weth_usdc_pool2 = SwapBuilder::new(
819 ProtocolComponent {
820 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
823 static_attributes: {
824 let mut attrs = HashMap::new();
825 attrs.insert(
826 "fee".to_string(),
827 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
828 );
829 attrs
830 },
831 ..Default::default()
832 },
833 weth.clone(),
834 usdc.clone(),
835 )
836 .build();
837 let swap_encoder_registry = get_swap_encoder_registry();
838 let encoder = SplitSwapStrategyEncoder::new(
839 eth_chain(),
840 swap_encoder_registry,
841 UserTransferType::TransferFromPermit2,
842 Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
843 )
844 .unwrap();
845
846 let solution = Solution {
847 exact_out: false,
848 given_token: usdc.clone(),
849 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
851 checked_amount: BigUint::from_str("99574171").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
855 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
856 swaps: vec![swap_usdc_weth_pool1, swap_usdc_weth_pool2, swap_weth_usdc_pool2],
857 ..Default::default()
858 };
859
860 let encoded_solution = encoder
861 .encode_strategy(&solution)
862 .unwrap();
863
864 let hex_calldata = hex::encode(&encoded_solution.swaps);
865
866 let expected_swaps = [
867 "006e", "00", "01", "999999", "2e234dae75c793f67a35089c9d99245e1c58470b", "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "0001f4", "3ede3eca2a72b3aecc820e955b36f38437d01395", "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", "01", "00", "006e", "00", "01", "000000", "2e234dae75c793f67a35089c9d99245e1c58470b", "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "000bb8", "3ede3eca2a72b3aecc820e955b36f38437d01395", "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", "01", "00", "0057", "01", "00", "000000", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "01", ]
902 .join("");
903 assert_eq!(hex_calldata, expected_swaps);
904 assert_eq!(
905 encoded_solution.function_signature,
906 "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
907 .to_string()
908 );
909 assert_eq!(encoded_solution.interacting_with, router_address());
910 }
911
912 #[test]
913 fn test_split_output_cyclic_swap() {
914 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
923 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
924
925 let swap_usdc_weth_v2 = SwapBuilder::new(
926 ProtocolComponent {
927 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
929 static_attributes: {
930 let mut attrs = HashMap::new();
931 attrs.insert(
932 "fee".to_string(),
933 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
934 );
935 attrs
936 },
937 ..Default::default()
938 },
939 usdc.clone(),
940 weth.clone(),
941 )
942 .build();
943
944 let swap_weth_usdc_v3_pool1 = SwapBuilder::new(
945 ProtocolComponent {
946 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
949 static_attributes: {
950 let mut attrs = HashMap::new();
951 attrs.insert(
952 "fee".to_string(),
953 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
954 );
955 attrs
956 },
957 ..Default::default()
958 },
959 weth.clone(),
960 usdc.clone(),
961 )
962 .split(0.6f64)
963 .build();
964
965 let swap_weth_usdc_v3_pool2 = SwapBuilder::new(
966 ProtocolComponent {
967 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
970 static_attributes: {
971 let mut attrs = HashMap::new();
972 attrs.insert(
973 "fee".to_string(),
974 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
975 );
976 attrs
977 },
978 ..Default::default()
979 },
980 weth.clone(),
981 usdc.clone(),
982 )
983 .build();
984
985 let swap_encoder_registry = get_swap_encoder_registry();
986 let encoder = SplitSwapStrategyEncoder::new(
987 eth_chain(),
988 swap_encoder_registry,
989 UserTransferType::TransferFrom,
990 Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
991 )
992 .unwrap();
993
994 let solution = Solution {
995 exact_out: false,
996 given_token: usdc.clone(),
997 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
999 checked_amount: BigUint::from_str("99025908").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1003 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1004 swaps: vec![swap_usdc_weth_v2, swap_weth_usdc_v3_pool1, swap_weth_usdc_v3_pool2],
1005 ..Default::default()
1006 };
1007
1008 let encoded_solution = encoder
1009 .encode_strategy(&solution)
1010 .unwrap();
1011
1012 let hex_calldata = hex::encode(&encoded_solution.swaps);
1013
1014 let expected_swaps = [
1015 "0057", "00", "01", "000000", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", "3ede3eca2a72b3aecc820e955b36f38437d01395", "01", "00", "006e", "01", "00", "999999", "2e234dae75c793f67a35089c9d99245e1c58470b", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "0001f4", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", "00", "01", "006e", "01", "00", "000000", "2e234dae75c793f67a35089c9d99245e1c58470b", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "000bb8", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", "00", "01", ]
1050 .join("");
1051
1052 assert_eq!(hex_calldata, expected_swaps);
1053 assert_eq!(
1054 encoded_solution.function_signature,
1055 "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
1056 .to_string()
1057 );
1058 assert_eq!(encoded_solution.interacting_with, router_address());
1059 }
1060 }
1061}