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<Vec<u8>> = vec![];
125 let mut initial_protocol_data: Vec<u8> = vec![];
126 for swap in grouped_swap.swaps.iter() {
127 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
128 if encoding_context.group_token_in == swap.token_in {
129 initial_protocol_data = protocol_data;
130 } else {
131 grouped_protocol_data.push(protocol_data);
132 }
133 }
134
135 if !grouped_protocol_data.is_empty() {
136 initial_protocol_data.extend(ple_encode(grouped_protocol_data));
137 }
138
139 let swap_data = self.encode_swap_header(
140 Bytes::from_str(swap_encoder.executor_address())
141 .map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?,
142 initial_protocol_data,
143 );
144 Ok(EncodedSolution {
145 function_signature: self.function_signature.clone(),
146 interacting_with: self.router_address.clone(),
147 swaps: swap_data,
148 permit: None,
149 n_tokens: 0,
150 })
151 }
152
153 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
154 self.swap_encoder_registry
155 .get_encoder(protocol_system)
156 }
157
158 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
159 Box::new(self.clone())
160 }
161}
162
163#[derive(Clone)]
175pub struct SequentialSwapStrategyEncoder {
176 swap_encoder_registry: SwapEncoderRegistry,
177 function_signature: String,
178 router_address: Bytes,
179 native_address: Bytes,
180 wrapped_address: Bytes,
181 sequential_swap_validator: SequentialSwapValidator,
182 transfer_optimization: TransferOptimization,
183}
184
185impl SequentialSwapStrategyEncoder {
186 pub fn new(
187 chain: Chain,
188 swap_encoder_registry: SwapEncoderRegistry,
189 user_transfer_type: UserTransferType,
190 router_address: Bytes,
191 ) -> Result<Self, EncodingError> {
192 let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
193 "sequentialSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
194 } else {
195 "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
196
197 }.to_string();
198 let native_token_address = chain.native_token().address;
199 let wrapped_token_address = chain.wrapped_native_token().address;
200 Ok(Self {
201 function_signature,
202 swap_encoder_registry,
203 router_address: router_address.clone(),
204 native_address: native_token_address.clone(),
205 wrapped_address: wrapped_token_address.clone(),
206 sequential_swap_validator: SequentialSwapValidator,
207 transfer_optimization: TransferOptimization::new(
208 native_token_address,
209 wrapped_token_address,
210 user_transfer_type,
211 router_address,
212 ),
213 })
214 }
215
216 fn encode_swap_header(&self, executor_address: Bytes, protocol_data: Vec<u8>) -> Vec<u8> {
219 let mut encoded = Vec::new();
220 encoded.extend(executor_address.to_vec());
221 encoded.extend(protocol_data);
222 encoded
223 }
224}
225
226impl StrategyEncoder for SequentialSwapStrategyEncoder {
227 fn encode_strategy(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
228 self.sequential_swap_validator
229 .validate_swap_path(
230 &solution.swaps,
231 &solution.given_token,
232 &solution.checked_token,
233 &solution.native_action,
234 &self.native_address,
235 &self.wrapped_address,
236 )?;
237
238 let grouped_swaps = group_swaps(&solution.swaps);
239
240 let (mut wrap, mut unwrap) = (false, false);
241 if let Some(action) = &solution.native_action {
242 match *action {
243 NativeAction::Wrap => wrap = true,
244 NativeAction::Unwrap => unwrap = true,
245 }
246 }
247
248 let mut swaps = vec![];
249 let mut next_in_between_swap_optimization_allowed = true;
250 for (i, grouped_swap) in grouped_swaps.iter().enumerate() {
251 let protocol = &grouped_swap.protocol_system;
252 let swap_encoder = self
253 .get_swap_encoder(protocol)
254 .ok_or_else(|| {
255 EncodingError::InvalidInput(format!(
256 "Swap encoder not found for protocol: {protocol}",
257 ))
258 })?;
259
260 let in_between_swap_optimization_allowed = next_in_between_swap_optimization_allowed;
261 let next_swap = grouped_swaps.get(i + 1);
262 let (swap_receiver, next_swap_optimization) = self
263 .transfer_optimization
264 .get_receiver(&solution.receiver, next_swap, unwrap)?;
265 next_in_between_swap_optimization_allowed = next_swap_optimization;
266
267 let transfer = self
268 .transfer_optimization
269 .get_transfers(
270 grouped_swap,
271 &solution.given_token,
272 wrap,
273 in_between_swap_optimization_allowed,
274 );
275 let encoding_context = EncodingContext {
276 receiver: swap_receiver,
277 exact_out: solution.exact_out,
278 router_address: Some(self.router_address.clone()),
279 group_token_in: grouped_swap.token_in.clone(),
280 group_token_out: grouped_swap.token_out.clone(),
281 transfer_type: transfer,
282 };
283
284 let mut grouped_protocol_data: Vec<Vec<u8>> = vec![];
285 let mut initial_protocol_data: Vec<u8> = vec![];
286 for swap in grouped_swap.swaps.iter() {
287 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
288 if encoding_context.group_token_in == swap.token_in {
289 initial_protocol_data = protocol_data;
290 } else {
291 grouped_protocol_data.push(protocol_data);
292 }
293 }
294
295 if !grouped_protocol_data.is_empty() {
296 initial_protocol_data.extend(ple_encode(grouped_protocol_data));
297 }
298
299 let swap_data = self.encode_swap_header(
300 Bytes::from_str(swap_encoder.executor_address()).map_err(|_| {
301 EncodingError::FatalError("Invalid executor address".to_string())
302 })?,
303 initial_protocol_data,
304 );
305 swaps.push(swap_data);
306 }
307
308 let encoded_swaps = ple_encode(swaps);
309 Ok(EncodedSolution {
310 interacting_with: self.router_address.clone(),
311 function_signature: self.function_signature.clone(),
312 swaps: encoded_swaps,
313 permit: None,
314 n_tokens: 0,
315 })
316 }
317
318 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
319 self.swap_encoder_registry
320 .get_encoder(protocol_system)
321 }
322
323 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
324 Box::new(self.clone())
325 }
326}
327
328#[derive(Clone)]
340pub struct SplitSwapStrategyEncoder {
341 swap_encoder_registry: SwapEncoderRegistry,
342 function_signature: String,
343 native_address: Bytes,
344 wrapped_address: Bytes,
345 split_swap_validator: SplitSwapValidator,
346 router_address: Bytes,
347 transfer_optimization: TransferOptimization,
348}
349
350impl SplitSwapStrategyEncoder {
351 pub fn new(
352 chain: Chain,
353 swap_encoder_registry: SwapEncoderRegistry,
354 user_transfer_type: UserTransferType,
355 router_address: Bytes,
356 ) -> Result<Self, EncodingError> {
357 let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
358 "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
359 } else {
360 "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
361 }.to_string();
362 let native_token_address = chain.native_token().address;
363 let wrapped_token_address = chain.wrapped_native_token().address;
364 Ok(Self {
365 function_signature,
366 swap_encoder_registry,
367 native_address: native_token_address.clone(),
368 wrapped_address: wrapped_token_address.clone(),
369 split_swap_validator: SplitSwapValidator,
370 router_address: router_address.clone(),
371 transfer_optimization: TransferOptimization::new(
372 native_token_address,
373 wrapped_token_address,
374 user_transfer_type,
375 router_address,
376 ),
377 })
378 }
379
380 fn encode_swap_header(
383 &self,
384 token_in: U8,
385 token_out: U8,
386 split: U24,
387 executor_address: Bytes,
388 protocol_data: Vec<u8>,
389 ) -> Vec<u8> {
390 let mut encoded = Vec::new();
391 encoded.push(token_in.to_be_bytes_vec()[0]);
392 encoded.push(token_out.to_be_bytes_vec()[0]);
393 encoded.extend_from_slice(&split.to_be_bytes_vec());
394 encoded.extend(executor_address.to_vec());
395 encoded.extend(protocol_data);
396 encoded
397 }
398}
399
400impl StrategyEncoder for SplitSwapStrategyEncoder {
401 fn encode_strategy(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
402 self.split_swap_validator
403 .validate_split_percentages(&solution.swaps)?;
404 self.split_swap_validator
405 .validate_swap_path(
406 &solution.swaps,
407 &solution.given_token,
408 &solution.checked_token,
409 &solution.native_action,
410 &self.native_address,
411 &self.wrapped_address,
412 )?;
413
414 let solution_tokens: HashSet<&Bytes> = vec![&solution.given_token, &solution.checked_token]
417 .into_iter()
418 .collect();
419
420 let grouped_swaps = group_swaps(&solution.swaps);
421
422 let intermediary_tokens: HashSet<&Bytes> = grouped_swaps
423 .iter()
424 .flat_map(|grouped_swap| vec![&grouped_swap.token_in, &grouped_swap.token_out])
425 .collect();
426 let mut intermediary_tokens: Vec<&Bytes> = intermediary_tokens
427 .difference(&solution_tokens)
428 .cloned()
429 .collect();
430 intermediary_tokens.sort();
433
434 let (mut unwrap, mut wrap) = (false, false);
435 if let Some(action) = &solution.native_action {
436 match *action {
437 NativeAction::Wrap => wrap = true,
438 NativeAction::Unwrap => unwrap = true,
439 }
440 }
441
442 let mut tokens = Vec::with_capacity(2 + intermediary_tokens.len());
443 if wrap {
444 tokens.push(&self.wrapped_address);
445 } else {
446 tokens.push(&solution.given_token);
447 }
448 tokens.extend(intermediary_tokens);
449
450 if unwrap {
451 tokens.push(&self.wrapped_address);
452 } else {
453 tokens.push(&solution.checked_token);
454 }
455
456 let mut swaps = vec![];
457 for grouped_swap in grouped_swaps.iter() {
458 let protocol = &grouped_swap.protocol_system;
459 let swap_encoder = self
460 .get_swap_encoder(protocol)
461 .ok_or_else(|| {
462 EncodingError::InvalidInput(format!(
463 "Swap encoder not found for protocol: {protocol}",
464 ))
465 })?;
466
467 let swap_receiver = if !unwrap && grouped_swap.token_out == solution.checked_token {
468 solution.receiver.clone()
469 } else {
470 self.router_address.clone()
471 };
472 let transfer = self
473 .transfer_optimization
474 .get_transfers(grouped_swap, &solution.given_token, wrap, false);
475 let encoding_context = EncodingContext {
476 receiver: swap_receiver,
477 exact_out: solution.exact_out,
478 router_address: Some(self.router_address.clone()),
479 group_token_in: grouped_swap.token_in.clone(),
480 group_token_out: grouped_swap.token_out.clone(),
481 transfer_type: transfer,
482 };
483
484 let mut grouped_protocol_data: Vec<Vec<u8>> = vec![];
485 let mut initial_protocol_data: Vec<u8> = vec![];
486 for swap in grouped_swap.swaps.iter() {
487 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
488 if encoding_context.group_token_in == swap.token_in {
489 initial_protocol_data = protocol_data;
490 } else {
491 grouped_protocol_data.push(protocol_data);
492 }
493 }
494
495 if !grouped_protocol_data.is_empty() {
496 initial_protocol_data.extend(ple_encode(grouped_protocol_data));
497 }
498
499 let swap_data = self.encode_swap_header(
500 get_token_position(&tokens, &grouped_swap.token_in)?,
501 get_token_position(&tokens, &grouped_swap.token_out)?,
502 percentage_to_uint24(grouped_swap.split),
503 Bytes::from_str(swap_encoder.executor_address()).map_err(|_| {
504 EncodingError::FatalError("Invalid executor address".to_string())
505 })?,
506 initial_protocol_data,
507 );
508 swaps.push(swap_data);
509 }
510
511 let encoded_swaps = ple_encode(swaps);
512 let tokens_len = if solution.given_token == solution.checked_token {
513 tokens.len() - 1
514 } else {
515 tokens.len()
516 };
517 Ok(EncodedSolution {
518 interacting_with: self.router_address.clone(),
519 function_signature: self.function_signature.clone(),
520 swaps: encoded_swaps,
521 permit: None,
522 n_tokens: tokens_len,
523 })
524 }
525
526 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
527 self.swap_encoder_registry
528 .get_encoder(protocol_system)
529 }
530
531 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
532 Box::new(self.clone())
533 }
534}
535
536#[cfg(test)]
537mod tests {
538 use std::{collections::HashMap, str::FromStr};
539
540 use alloy::{hex::encode, primitives::hex};
541 use num_bigint::{BigInt, BigUint};
542 use tycho_common::{
543 models::{protocol::ProtocolComponent, Chain},
544 Bytes,
545 };
546
547 use super::*;
548
549 fn eth_chain() -> Chain {
550 Chain::Ethereum
551 }
552
553 fn weth() -> Bytes {
554 Bytes::from(hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").to_vec())
555 }
556
557 fn get_swap_encoder_registry() -> SwapEncoderRegistry {
558 let eth_chain = eth_chain();
559 SwapEncoderRegistry::new(Some("config/test_executor_addresses.json".to_string()), eth_chain)
560 .unwrap()
561 }
562
563 fn router_address() -> Bytes {
564 Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap()
565 }
566
567 mod single {
568 use super::*;
569 use crate::encoding::models::SwapBuilder;
570 #[test]
571 fn test_single_swap_strategy_encoder() {
572 let checked_amount = BigUint::from_str("2018817438608734439720").unwrap();
575 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
576 let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
577
578 let swap = SwapBuilder::new(
579 ProtocolComponent {
580 id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
581 protocol_system: "uniswap_v2".to_string(),
582 ..Default::default()
583 },
584 weth.clone(),
585 dai.clone(),
586 )
587 .build();
588 let swap_encoder_registry = get_swap_encoder_registry();
589 let encoder = SingleSwapStrategyEncoder::new(
590 eth_chain(),
591 swap_encoder_registry,
592 UserTransferType::TransferFromPermit2,
593 router_address(),
594 )
595 .unwrap();
596 let solution = Solution {
597 exact_out: false,
598 given_token: weth,
599 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
600 checked_token: dai,
601 checked_amount: checked_amount.clone(),
602 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
603 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
604 swaps: vec![swap],
605 ..Default::default()
606 };
607
608 let encoded_solution = encoder
609 .encode_strategy(&solution)
610 .unwrap();
611
612 let expected_swap = String::from(concat!(
613 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "00", ));
621 let hex_calldata = encode(&encoded_solution.swaps);
622
623 assert_eq!(hex_calldata, expected_swap);
624 assert_eq!(encoded_solution.function_signature, "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string());
625 assert_eq!(encoded_solution.interacting_with, router_address());
626 }
627
628 #[test]
629 fn test_single_swap_strategy_encoder_no_transfer_in() {
630 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
634 let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
635
636 let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap();
637
638 let swap = SwapBuilder::new(
639 ProtocolComponent {
640 id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
641 protocol_system: "uniswap_v2".to_string(),
642 ..Default::default()
643 },
644 weth.clone(),
645 dai.clone(),
646 )
647 .build();
648 let swap_encoder_registry = get_swap_encoder_registry();
649 let encoder = SingleSwapStrategyEncoder::new(
650 eth_chain(),
651 swap_encoder_registry,
652 UserTransferType::None,
653 router_address(),
654 )
655 .unwrap();
656 let solution = Solution {
657 exact_out: false,
658 given_token: weth,
659 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
660 checked_token: dai,
661 checked_amount,
662 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
663 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
664 swaps: vec![swap],
665 ..Default::default()
666 };
667
668 let encoded_solution = encoder
669 .encode_strategy(&solution)
670 .unwrap();
671
672 let expected_input = [
673 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "01", ]
681 .join("");
682
683 let hex_calldata = encode(&encoded_solution.swaps);
684
685 assert_eq!(hex_calldata, expected_input);
686 assert_eq!(
687 encoded_solution.function_signature,
688 "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
689 .to_string()
690 );
691 assert_eq!(encoded_solution.interacting_with, router_address());
692 }
693 }
694
695 mod sequential {
696 use super::*;
697 use crate::encoding::models::SwapBuilder;
698
699 #[test]
700 fn test_sequential_swap_strategy_encoder_no_permit2() {
701 let weth = weth();
706 let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap();
707 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
708
709 let swap_weth_wbtc = SwapBuilder::new(
710 ProtocolComponent {
711 id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(),
712 protocol_system: "uniswap_v2".to_string(),
713 ..Default::default()
714 },
715 weth.clone(),
716 wbtc.clone(),
717 )
718 .build();
719 let swap_wbtc_usdc = SwapBuilder::new(
720 ProtocolComponent {
721 id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(),
722 protocol_system: "uniswap_v2".to_string(),
723 ..Default::default()
724 },
725 wbtc.clone(),
726 usdc.clone(),
727 )
728 .build();
729 let swap_encoder_registry = get_swap_encoder_registry();
730 let encoder = SequentialSwapStrategyEncoder::new(
731 eth_chain(),
732 swap_encoder_registry,
733 UserTransferType::TransferFrom,
734 router_address(),
735 )
736 .unwrap();
737 let solution = Solution {
738 exact_out: false,
739 given_token: weth,
740 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
741 checked_token: usdc,
742 checked_amount: BigUint::from_str("26173932").unwrap(),
743 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
744 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
745 swaps: vec![swap_weth_wbtc, swap_wbtc_usdc],
746 ..Default::default()
747 };
748
749 let encoded_solution = encoder
750 .encode_strategy(&solution)
751 .unwrap();
752
753 let hex_calldata = encode(&encoded_solution.swaps);
754
755 let expected = String::from(concat!(
756 "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "bb2b8038a1640196fbe3e38816f3e67cba72d940", "004375dff511095cc5a197a54140a24efef3a416", "00", "00", "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "2260fac5e5542a773aa44fbcfedf7c193bc2c599", "004375dff511095cc5a197a54140a24efef3a416", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "01", "02", ));
773
774 assert_eq!(hex_calldata, expected);
775 assert_eq!(
776 encoded_solution.function_signature,
777 "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
778 .to_string()
779 );
780 assert_eq!(encoded_solution.interacting_with, router_address());
781 }
782 }
783
784 mod split {
785 use super::*;
786 use crate::encoding::models::SwapBuilder;
787
788 #[test]
789 fn test_split_input_cyclic_swap() {
790 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
799 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
800
801 let swap_usdc_weth_pool1 = SwapBuilder::new(
803 ProtocolComponent {
804 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
807 static_attributes: {
808 let mut attrs = HashMap::new();
809 attrs.insert(
810 "fee".to_string(),
811 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
812 );
813 attrs
814 },
815 ..Default::default()
816 },
817 usdc.clone(),
818 weth.clone(),
819 )
820 .split(0.6f64)
821 .build();
822
823 let swap_usdc_weth_pool2 = SwapBuilder::new(
825 ProtocolComponent {
826 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
829 static_attributes: {
830 let mut attrs = HashMap::new();
831 attrs.insert(
832 "fee".to_string(),
833 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
834 );
835 attrs
836 },
837 ..Default::default()
838 },
839 usdc.clone(),
840 weth.clone(),
841 )
842 .build();
843
844 let swap_weth_usdc_pool2 = SwapBuilder::new(
846 ProtocolComponent {
847 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
850 static_attributes: {
851 let mut attrs = HashMap::new();
852 attrs.insert(
853 "fee".to_string(),
854 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
855 );
856 attrs
857 },
858 ..Default::default()
859 },
860 weth.clone(),
861 usdc.clone(),
862 )
863 .build();
864 let swap_encoder_registry = get_swap_encoder_registry();
865 let encoder = SplitSwapStrategyEncoder::new(
866 eth_chain(),
867 swap_encoder_registry,
868 UserTransferType::TransferFromPermit2,
869 Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
870 )
871 .unwrap();
872
873 let solution = Solution {
874 exact_out: false,
875 given_token: usdc.clone(),
876 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
878 checked_amount: BigUint::from_str("99574171").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
882 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
883 swaps: vec![swap_usdc_weth_pool1, swap_usdc_weth_pool2, swap_weth_usdc_pool2],
884 ..Default::default()
885 };
886
887 let encoded_solution = encoder
888 .encode_strategy(&solution)
889 .unwrap();
890
891 let hex_calldata = hex::encode(&encoded_solution.swaps);
892
893 let expected_swaps = [
894 "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", ]
929 .join("");
930 assert_eq!(hex_calldata, expected_swaps);
931 assert_eq!(
932 encoded_solution.function_signature,
933 "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
934 .to_string()
935 );
936 assert_eq!(encoded_solution.interacting_with, router_address());
937 }
938
939 #[test]
940 fn test_split_output_cyclic_swap() {
941 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
950 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
951
952 let swap_usdc_weth_v2 = SwapBuilder::new(
953 ProtocolComponent {
954 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
956 static_attributes: {
957 let mut attrs = HashMap::new();
958 attrs.insert(
959 "fee".to_string(),
960 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
961 );
962 attrs
963 },
964 ..Default::default()
965 },
966 usdc.clone(),
967 weth.clone(),
968 )
969 .build();
970
971 let swap_weth_usdc_v3_pool1 = SwapBuilder::new(
972 ProtocolComponent {
973 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
976 static_attributes: {
977 let mut attrs = HashMap::new();
978 attrs.insert(
979 "fee".to_string(),
980 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
981 );
982 attrs
983 },
984 ..Default::default()
985 },
986 weth.clone(),
987 usdc.clone(),
988 )
989 .split(0.6f64)
990 .build();
991
992 let swap_weth_usdc_v3_pool2 = SwapBuilder::new(
993 ProtocolComponent {
994 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
997 static_attributes: {
998 let mut attrs = HashMap::new();
999 attrs.insert(
1000 "fee".to_string(),
1001 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
1002 );
1003 attrs
1004 },
1005 ..Default::default()
1006 },
1007 weth.clone(),
1008 usdc.clone(),
1009 )
1010 .build();
1011
1012 let swap_encoder_registry = get_swap_encoder_registry();
1013 let encoder = SplitSwapStrategyEncoder::new(
1014 eth_chain(),
1015 swap_encoder_registry,
1016 UserTransferType::TransferFrom,
1017 Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
1018 )
1019 .unwrap();
1020
1021 let solution = Solution {
1022 exact_out: false,
1023 given_token: usdc.clone(),
1024 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
1026 checked_amount: BigUint::from_str("99025908").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1030 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1031 swaps: vec![swap_usdc_weth_v2, swap_weth_usdc_v3_pool1, swap_weth_usdc_v3_pool2],
1032 ..Default::default()
1033 };
1034
1035 let encoded_solution = encoder
1036 .encode_strategy(&solution)
1037 .unwrap();
1038
1039 let hex_calldata = hex::encode(&encoded_solution.swaps);
1040
1041 let expected_swaps = [
1042 "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", ]
1077 .join("");
1078
1079 assert_eq!(hex_calldata, expected_swaps);
1080 assert_eq!(
1081 encoded_solution.function_signature,
1082 "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
1083 .to_string()
1084 );
1085 assert_eq!(encoded_solution.interacting_with, router_address());
1086 }
1087 }
1088}