1use std::collections::HashSet;
2
3use alloy::primitives::{aliases::U24, U8};
4use tycho_common::{models::Chain, Bytes};
5
6use crate::encoding::{
7 errors::EncodingError,
8 evm::{
9 constants::NON_PLE_ENCODED_PROTOCOLS,
10 group_swaps::group_swaps,
11 strategy_encoder::{
12 strategy_validators::{SequentialSwapValidator, SplitSwapValidator, SwapValidator},
13 transfer_optimizations::TransferOptimization,
14 },
15 swap_encoder::swap_encoder_registry::SwapEncoderRegistry,
16 utils::{get_token_position, percentage_to_uint24, ple_encode},
17 },
18 models::{EncodedSolution, EncodingContext, NativeAction, Solution, UserTransferType},
19 strategy_encoder::StrategyEncoder,
20 swap_encoder::SwapEncoder,
21};
22
23#[derive(Clone)]
33pub struct SingleSwapStrategyEncoder {
34 swap_encoder_registry: SwapEncoderRegistry,
35 function_signature: String,
36 router_address: Bytes,
37 transfer_optimization: TransferOptimization,
38 historical_trade: bool,
39}
40
41impl SingleSwapStrategyEncoder {
42 pub fn new(
43 chain: Chain,
44 swap_encoder_registry: SwapEncoderRegistry,
45 user_transfer_type: UserTransferType,
46 router_address: Bytes,
47 historical_trade: bool,
48 ) -> Result<Self, EncodingError> {
49 let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
50 "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
51 } else {
52 "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
53 }.to_string();
54
55 Ok(Self {
56 function_signature,
57 swap_encoder_registry,
58 router_address: router_address.clone(),
59 transfer_optimization: TransferOptimization::new(
60 chain.native_token().address,
61 chain.wrapped_native_token().address,
62 user_transfer_type,
63 router_address,
64 ),
65 historical_trade,
66 })
67 }
68
69 fn encode_swap_header(&self, executor_address: Bytes, protocol_data: Vec<u8>) -> Vec<u8> {
72 let mut encoded = Vec::new();
73 encoded.extend(executor_address.to_vec());
74 encoded.extend(protocol_data);
75 encoded
76 }
77}
78
79impl StrategyEncoder for SingleSwapStrategyEncoder {
80 fn encode_strategy(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
81 let grouped_swaps = group_swaps(&solution.swaps);
82 let number_of_groups = grouped_swaps.len();
83 if number_of_groups != 1 {
84 return Err(EncodingError::InvalidInput(format!(
85 "Single strategy only supports exactly one swap for non-groupable protocols. Found {number_of_groups}",
86 )))
87 }
88
89 let grouped_swap = grouped_swaps
90 .first()
91 .ok_or_else(|| EncodingError::FatalError("Swap grouping failed".to_string()))?;
92
93 if grouped_swap.split != 0f64 {
94 return Err(EncodingError::InvalidInput(
95 "Splits not supported for single swaps.".to_string(),
96 ))
97 }
98
99 let (mut unwrap, mut wrap) = (false, false);
100 if let Some(action) = &solution.native_action {
101 match *action {
102 NativeAction::Wrap => wrap = true,
103 NativeAction::Unwrap => unwrap = true,
104 }
105 }
106 let protocol = &grouped_swap.protocol_system;
107 let swap_encoder = self
108 .get_swap_encoder(protocol)
109 .ok_or_else(|| {
110 EncodingError::InvalidInput(format!(
111 "Swap encoder not found for protocol: {protocol}"
112 ))
113 })?;
114
115 let swap_receiver =
116 if !unwrap { solution.receiver.clone() } else { self.router_address.clone() };
117
118 let transfer = self
119 .transfer_optimization
120 .get_transfers(grouped_swap, &solution.given_token, wrap, false);
121 let encoding_context = EncodingContext {
122 receiver: swap_receiver,
123 exact_out: solution.exact_out,
124 router_address: Some(self.router_address.clone()),
125 group_token_in: grouped_swap.token_in.clone(),
126 group_token_out: grouped_swap.token_out.clone(),
127 transfer_type: transfer,
128 historical_trade: self.historical_trade,
129 };
130
131 let mut grouped_protocol_data: Vec<Vec<u8>> = vec![];
132 let mut initial_protocol_data: Vec<u8> = vec![];
133 for swap in grouped_swap.swaps.iter() {
134 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
135 if encoding_context.group_token_in == swap.token_in {
136 initial_protocol_data = protocol_data;
137 } else {
138 grouped_protocol_data.push(protocol_data);
139 }
140 }
141
142 if !grouped_protocol_data.is_empty() {
143 if NON_PLE_ENCODED_PROTOCOLS.contains(grouped_swap.protocol_system.as_str()) {
144 for protocol_data in grouped_protocol_data {
145 initial_protocol_data.extend(protocol_data);
146 }
147 } else {
148 initial_protocol_data.extend(ple_encode(grouped_protocol_data));
149 }
150 }
151
152 let swap_data =
153 self.encode_swap_header(swap_encoder.executor_address().clone(), initial_protocol_data);
154 Ok(EncodedSolution {
155 function_signature: self.function_signature.clone(),
156 interacting_with: self.router_address.clone(),
157 swaps: swap_data,
158 permit: None,
159 n_tokens: 0,
160 })
161 }
162
163 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
164 self.swap_encoder_registry
165 .get_encoder(protocol_system)
166 }
167
168 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
169 Box::new(self.clone())
170 }
171}
172
173#[derive(Clone)]
187pub struct SequentialSwapStrategyEncoder {
188 swap_encoder_registry: SwapEncoderRegistry,
189 function_signature: String,
190 router_address: Bytes,
191 native_address: Bytes,
192 wrapped_address: Bytes,
193 sequential_swap_validator: SequentialSwapValidator,
194 transfer_optimization: TransferOptimization,
195 historical_trade: bool,
196}
197
198impl SequentialSwapStrategyEncoder {
199 pub fn new(
200 chain: Chain,
201 swap_encoder_registry: SwapEncoderRegistry,
202 user_transfer_type: UserTransferType,
203 router_address: Bytes,
204 historical_trade: bool,
205 ) -> Result<Self, EncodingError> {
206 let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
207 "sequentialSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
208 } else {
209 "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
210
211 }.to_string();
212 let native_token_address = chain.native_token().address;
213 let wrapped_token_address = chain.wrapped_native_token().address;
214 Ok(Self {
215 function_signature,
216 swap_encoder_registry,
217 router_address: router_address.clone(),
218 native_address: native_token_address.clone(),
219 wrapped_address: wrapped_token_address.clone(),
220 sequential_swap_validator: SequentialSwapValidator,
221 transfer_optimization: TransferOptimization::new(
222 native_token_address,
223 wrapped_token_address,
224 user_transfer_type,
225 router_address,
226 ),
227 historical_trade,
228 })
229 }
230
231 fn encode_swap_header(&self, executor_address: Bytes, protocol_data: Vec<u8>) -> Vec<u8> {
234 let mut encoded = Vec::new();
235 encoded.extend(executor_address.to_vec());
236 encoded.extend(protocol_data);
237 encoded
238 }
239}
240
241impl StrategyEncoder for SequentialSwapStrategyEncoder {
242 fn encode_strategy(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
243 self.sequential_swap_validator
244 .validate_swap_path(
245 &solution.swaps,
246 &solution.given_token,
247 &solution.checked_token,
248 &solution.native_action,
249 &self.native_address,
250 &self.wrapped_address,
251 )?;
252
253 let grouped_swaps = group_swaps(&solution.swaps);
254
255 let (mut wrap, mut unwrap) = (false, false);
256 if let Some(action) = &solution.native_action {
257 match *action {
258 NativeAction::Wrap => wrap = true,
259 NativeAction::Unwrap => unwrap = true,
260 }
261 }
262
263 let mut swaps = vec![];
264 let mut next_in_between_swap_optimization_allowed = true;
265 for (i, grouped_swap) in grouped_swaps.iter().enumerate() {
266 let protocol = &grouped_swap.protocol_system;
267 let swap_encoder = self
268 .get_swap_encoder(protocol)
269 .ok_or_else(|| {
270 EncodingError::InvalidInput(format!(
271 "Swap encoder not found for protocol: {protocol}",
272 ))
273 })?;
274
275 let in_between_swap_optimization_allowed = next_in_between_swap_optimization_allowed;
276 let next_swap = grouped_swaps.get(i + 1);
277 let (swap_receiver, next_swap_optimization) = self
278 .transfer_optimization
279 .get_receiver(&solution.receiver, next_swap, unwrap)?;
280 next_in_between_swap_optimization_allowed = next_swap_optimization;
281
282 let transfer = self
283 .transfer_optimization
284 .get_transfers(
285 grouped_swap,
286 &solution.given_token,
287 wrap,
288 in_between_swap_optimization_allowed,
289 );
290 let encoding_context = EncodingContext {
291 receiver: swap_receiver,
292 exact_out: solution.exact_out,
293 router_address: Some(self.router_address.clone()),
294 group_token_in: grouped_swap.token_in.clone(),
295 group_token_out: grouped_swap.token_out.clone(),
296 transfer_type: transfer,
297 historical_trade: self.historical_trade,
298 };
299
300 let mut grouped_protocol_data: Vec<Vec<u8>> = vec![];
301 let mut initial_protocol_data: Vec<u8> = vec![];
302 for swap in grouped_swap.swaps.iter() {
303 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
304 if encoding_context.group_token_in == swap.token_in {
305 initial_protocol_data = protocol_data;
306 } else {
307 grouped_protocol_data.push(protocol_data);
308 }
309 }
310
311 if !grouped_protocol_data.is_empty() {
312 if NON_PLE_ENCODED_PROTOCOLS.contains(grouped_swap.protocol_system.as_str()) {
313 for protocol_data in grouped_protocol_data {
314 initial_protocol_data.extend(protocol_data);
315 }
316 } else {
317 initial_protocol_data.extend(ple_encode(grouped_protocol_data));
318 }
319 }
320
321 let swap_data = self
322 .encode_swap_header(swap_encoder.executor_address().clone(), initial_protocol_data);
323 swaps.push(swap_data);
324 }
325
326 let encoded_swaps = ple_encode(swaps);
327 Ok(EncodedSolution {
328 interacting_with: self.router_address.clone(),
329 function_signature: self.function_signature.clone(),
330 swaps: encoded_swaps,
331 permit: None,
332 n_tokens: 0,
333 })
334 }
335
336 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
337 self.swap_encoder_registry
338 .get_encoder(protocol_system)
339 }
340
341 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
342 Box::new(self.clone())
343 }
344}
345
346#[derive(Clone)]
360pub struct SplitSwapStrategyEncoder {
361 swap_encoder_registry: SwapEncoderRegistry,
362 function_signature: String,
363 native_address: Bytes,
364 wrapped_address: Bytes,
365 split_swap_validator: SplitSwapValidator,
366 router_address: Bytes,
367 transfer_optimization: TransferOptimization,
368 historical_trade: bool,
369}
370
371impl SplitSwapStrategyEncoder {
372 pub fn new(
373 chain: Chain,
374 swap_encoder_registry: SwapEncoderRegistry,
375 user_transfer_type: UserTransferType,
376 router_address: Bytes,
377 historical_trade: bool,
378 ) -> Result<Self, EncodingError> {
379 let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
380 "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
381 } else {
382 "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
383 }.to_string();
384 let native_token_address = chain.native_token().address;
385 let wrapped_token_address = chain.wrapped_native_token().address;
386 Ok(Self {
387 function_signature,
388 swap_encoder_registry,
389 native_address: native_token_address.clone(),
390 wrapped_address: wrapped_token_address.clone(),
391 split_swap_validator: SplitSwapValidator,
392 router_address: router_address.clone(),
393 transfer_optimization: TransferOptimization::new(
394 native_token_address,
395 wrapped_token_address,
396 user_transfer_type,
397 router_address,
398 ),
399 historical_trade,
400 })
401 }
402
403 fn encode_swap_header(
406 &self,
407 token_in: U8,
408 token_out: U8,
409 split: U24,
410 executor_address: Bytes,
411 protocol_data: Vec<u8>,
412 ) -> Vec<u8> {
413 let mut encoded = Vec::new();
414 encoded.push(token_in.to_be_bytes_vec()[0]);
415 encoded.push(token_out.to_be_bytes_vec()[0]);
416 encoded.extend_from_slice(&split.to_be_bytes_vec());
417 encoded.extend(executor_address.to_vec());
418 encoded.extend(protocol_data);
419 encoded
420 }
421}
422
423impl StrategyEncoder for SplitSwapStrategyEncoder {
424 fn encode_strategy(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
425 self.split_swap_validator
426 .validate_split_percentages(&solution.swaps)?;
427 self.split_swap_validator
428 .validate_swap_path(
429 &solution.swaps,
430 &solution.given_token,
431 &solution.checked_token,
432 &solution.native_action,
433 &self.native_address,
434 &self.wrapped_address,
435 )?;
436
437 let solution_tokens: HashSet<&Bytes> = vec![&solution.given_token, &solution.checked_token]
440 .into_iter()
441 .collect();
442
443 let grouped_swaps = group_swaps(&solution.swaps);
444
445 let intermediary_tokens: HashSet<&Bytes> = grouped_swaps
446 .iter()
447 .flat_map(|grouped_swap| vec![&grouped_swap.token_in, &grouped_swap.token_out])
448 .collect();
449 let mut intermediary_tokens: Vec<&Bytes> = intermediary_tokens
450 .difference(&solution_tokens)
451 .cloned()
452 .collect();
453 intermediary_tokens.sort();
456
457 let (mut unwrap, mut wrap) = (false, false);
458 if let Some(action) = &solution.native_action {
459 match *action {
460 NativeAction::Wrap => wrap = true,
461 NativeAction::Unwrap => unwrap = true,
462 }
463 }
464
465 let mut tokens = Vec::with_capacity(2 + intermediary_tokens.len());
466 if wrap {
467 tokens.push(&self.wrapped_address);
468 } else {
469 tokens.push(&solution.given_token);
470 }
471 tokens.extend(intermediary_tokens);
472
473 if unwrap {
474 tokens.push(&self.wrapped_address);
475 } else {
476 tokens.push(&solution.checked_token);
477 }
478
479 let mut swaps = vec![];
480 for grouped_swap in grouped_swaps.iter() {
481 let protocol = &grouped_swap.protocol_system;
482 let swap_encoder = self
483 .get_swap_encoder(protocol)
484 .ok_or_else(|| {
485 EncodingError::InvalidInput(format!(
486 "Swap encoder not found for protocol: {protocol}",
487 ))
488 })?;
489
490 let swap_receiver = if !unwrap && grouped_swap.token_out == solution.checked_token {
491 solution.receiver.clone()
492 } else {
493 self.router_address.clone()
494 };
495 let transfer = self
496 .transfer_optimization
497 .get_transfers(grouped_swap, &solution.given_token, wrap, false);
498 let encoding_context = EncodingContext {
499 receiver: swap_receiver,
500 exact_out: solution.exact_out,
501 router_address: Some(self.router_address.clone()),
502 group_token_in: grouped_swap.token_in.clone(),
503 group_token_out: grouped_swap.token_out.clone(),
504 transfer_type: transfer,
505 historical_trade: self.historical_trade,
506 };
507
508 let mut grouped_protocol_data: Vec<Vec<u8>> = vec![];
509 let mut initial_protocol_data: Vec<u8> = vec![];
510 for swap in grouped_swap.swaps.iter() {
511 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
512 if encoding_context.group_token_in == swap.token_in {
513 initial_protocol_data = protocol_data;
514 } else {
515 grouped_protocol_data.push(protocol_data);
516 }
517 }
518
519 if !grouped_protocol_data.is_empty() {
520 if NON_PLE_ENCODED_PROTOCOLS.contains(grouped_swap.protocol_system.as_str()) {
521 for protocol_data in grouped_protocol_data {
522 initial_protocol_data.extend(protocol_data);
523 }
524 } else {
525 initial_protocol_data.extend(ple_encode(grouped_protocol_data));
526 }
527 }
528
529 let swap_data = self.encode_swap_header(
530 get_token_position(&tokens, &grouped_swap.token_in)?,
531 get_token_position(&tokens, &grouped_swap.token_out)?,
532 percentage_to_uint24(grouped_swap.split),
533 swap_encoder.executor_address().clone(),
534 initial_protocol_data,
535 );
536 swaps.push(swap_data);
537 }
538
539 let encoded_swaps = ple_encode(swaps);
540 let tokens_len = if solution.given_token == solution.checked_token {
541 tokens.len() - 1
542 } else {
543 tokens.len()
544 };
545 Ok(EncodedSolution {
546 interacting_with: self.router_address.clone(),
547 function_signature: self.function_signature.clone(),
548 swaps: encoded_swaps,
549 permit: None,
550 n_tokens: tokens_len,
551 })
552 }
553
554 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
555 self.swap_encoder_registry
556 .get_encoder(protocol_system)
557 }
558
559 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
560 Box::new(self.clone())
561 }
562}
563
564#[cfg(test)]
565mod tests {
566 use std::{collections::HashMap, fs, str::FromStr};
567
568 use alloy::{hex::encode, primitives::hex};
569 use num_bigint::{BigInt, BigUint};
570 use tycho_common::{
571 models::{protocol::ProtocolComponent, Chain},
572 Bytes,
573 };
574
575 use super::*;
576
577 fn eth_chain() -> Chain {
578 Chain::Ethereum
579 }
580
581 fn weth() -> Bytes {
582 Bytes::from(hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").to_vec())
583 }
584
585 fn get_swap_encoder_registry() -> SwapEncoderRegistry {
586 let executors_addresses =
587 fs::read_to_string("config/test_executor_addresses.json").unwrap();
588 let eth_chain = eth_chain();
589 SwapEncoderRegistry::new(Some(executors_addresses), eth_chain).unwrap()
590 }
591
592 fn router_address() -> Bytes {
593 Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap()
594 }
595
596 mod single {
597 use super::*;
598 use crate::encoding::models::SwapBuilder;
599 #[test]
600 fn test_single_swap_strategy_encoder() {
601 let checked_amount = BigUint::from_str("2018817438608734439720").unwrap();
604 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
605 let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
606
607 let swap = SwapBuilder::new(
608 ProtocolComponent {
609 id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
610 protocol_system: "uniswap_v2".to_string(),
611 ..Default::default()
612 },
613 weth.clone(),
614 dai.clone(),
615 )
616 .build();
617 let swap_encoder_registry = get_swap_encoder_registry();
618 let encoder = SingleSwapStrategyEncoder::new(
619 eth_chain(),
620 swap_encoder_registry,
621 UserTransferType::TransferFromPermit2,
622 router_address(),
623 false,
624 )
625 .unwrap();
626 let solution = Solution {
627 exact_out: false,
628 given_token: weth,
629 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
630 checked_token: dai,
631 checked_amount: checked_amount.clone(),
632 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
633 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
634 swaps: vec![swap],
635 ..Default::default()
636 };
637
638 let encoded_solution = encoder
639 .encode_strategy(&solution)
640 .unwrap();
641
642 let expected_swap = String::from(concat!(
643 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "00", ));
651 let hex_calldata = encode(&encoded_solution.swaps);
652
653 assert_eq!(hex_calldata, expected_swap);
654 assert_eq!(encoded_solution.function_signature, "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string());
655 assert_eq!(encoded_solution.interacting_with, router_address());
656 }
657
658 #[test]
659 fn test_single_swap_strategy_encoder_no_transfer_in() {
660 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
664 let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
665
666 let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap();
667
668 let swap = SwapBuilder::new(
669 ProtocolComponent {
670 id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
671 protocol_system: "uniswap_v2".to_string(),
672 ..Default::default()
673 },
674 weth.clone(),
675 dai.clone(),
676 )
677 .build();
678 let swap_encoder_registry = get_swap_encoder_registry();
679 let encoder = SingleSwapStrategyEncoder::new(
680 eth_chain(),
681 swap_encoder_registry,
682 UserTransferType::None,
683 router_address(),
684 false,
685 )
686 .unwrap();
687 let solution = Solution {
688 exact_out: false,
689 given_token: weth,
690 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
691 checked_token: dai,
692 checked_amount,
693 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
694 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
695 swaps: vec![swap],
696 ..Default::default()
697 };
698
699 let encoded_solution = encoder
700 .encode_strategy(&solution)
701 .unwrap();
702
703 let expected_input = [
704 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "01", ]
712 .join("");
713
714 let hex_calldata = encode(&encoded_solution.swaps);
715
716 assert_eq!(hex_calldata, expected_input);
717 assert_eq!(
718 encoded_solution.function_signature,
719 "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
720 .to_string()
721 );
722 assert_eq!(encoded_solution.interacting_with, router_address());
723 }
724 }
725
726 mod sequential {
727 use super::*;
728 use crate::encoding::models::SwapBuilder;
729
730 #[test]
731 fn test_sequential_swap_strategy_encoder_no_permit2() {
732 let weth = weth();
737 let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap();
738 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
739
740 let swap_weth_wbtc = SwapBuilder::new(
741 ProtocolComponent {
742 id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(),
743 protocol_system: "uniswap_v2".to_string(),
744 ..Default::default()
745 },
746 weth.clone(),
747 wbtc.clone(),
748 )
749 .build();
750 let swap_wbtc_usdc = SwapBuilder::new(
751 ProtocolComponent {
752 id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(),
753 protocol_system: "uniswap_v2".to_string(),
754 ..Default::default()
755 },
756 wbtc.clone(),
757 usdc.clone(),
758 )
759 .build();
760 let swap_encoder_registry = get_swap_encoder_registry();
761 let encoder = SequentialSwapStrategyEncoder::new(
762 eth_chain(),
763 swap_encoder_registry,
764 UserTransferType::TransferFrom,
765 router_address(),
766 false,
767 )
768 .unwrap();
769 let solution = Solution {
770 exact_out: false,
771 given_token: weth,
772 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
773 checked_token: usdc,
774 checked_amount: BigUint::from_str("26173932").unwrap(),
775 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
776 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
777 swaps: vec![swap_weth_wbtc, swap_wbtc_usdc],
778 ..Default::default()
779 };
780
781 let encoded_solution = encoder
782 .encode_strategy(&solution)
783 .unwrap();
784
785 let hex_calldata = encode(&encoded_solution.swaps);
786
787 let expected = String::from(concat!(
788 "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "bb2b8038a1640196fbe3e38816f3e67cba72d940", "004375dff511095cc5a197a54140a24efef3a416", "00", "00", "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "2260fac5e5542a773aa44fbcfedf7c193bc2c599", "004375dff511095cc5a197a54140a24efef3a416", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "01", "02", ));
805
806 assert_eq!(hex_calldata, expected);
807 assert_eq!(
808 encoded_solution.function_signature,
809 "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
810 .to_string()
811 );
812 assert_eq!(encoded_solution.interacting_with, router_address());
813 }
814 }
815
816 mod split {
817 use super::*;
818 use crate::encoding::models::SwapBuilder;
819
820 #[test]
821 fn test_split_input_cyclic_swap() {
822 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
831 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
832
833 let swap_usdc_weth_pool1 = SwapBuilder::new(
835 ProtocolComponent {
836 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
839 static_attributes: {
840 let mut attrs = HashMap::new();
841 attrs.insert(
842 "fee".to_string(),
843 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
844 );
845 attrs
846 },
847 ..Default::default()
848 },
849 usdc.clone(),
850 weth.clone(),
851 )
852 .split(0.6f64)
853 .build();
854
855 let swap_usdc_weth_pool2 = SwapBuilder::new(
857 ProtocolComponent {
858 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
861 static_attributes: {
862 let mut attrs = HashMap::new();
863 attrs.insert(
864 "fee".to_string(),
865 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
866 );
867 attrs
868 },
869 ..Default::default()
870 },
871 usdc.clone(),
872 weth.clone(),
873 )
874 .build();
875
876 let swap_weth_usdc_pool2 = SwapBuilder::new(
878 ProtocolComponent {
879 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
882 static_attributes: {
883 let mut attrs = HashMap::new();
884 attrs.insert(
885 "fee".to_string(),
886 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
887 );
888 attrs
889 },
890 ..Default::default()
891 },
892 weth.clone(),
893 usdc.clone(),
894 )
895 .build();
896 let swap_encoder_registry = get_swap_encoder_registry();
897 let encoder = SplitSwapStrategyEncoder::new(
898 eth_chain(),
899 swap_encoder_registry,
900 UserTransferType::TransferFromPermit2,
901 Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
902 false,
903 )
904 .unwrap();
905
906 let solution = Solution {
907 exact_out: false,
908 given_token: usdc.clone(),
909 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
911 checked_amount: BigUint::from_str("99574171").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
915 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
916 swaps: vec![swap_usdc_weth_pool1, swap_usdc_weth_pool2, swap_weth_usdc_pool2],
917 ..Default::default()
918 };
919
920 let encoded_solution = encoder
921 .encode_strategy(&solution)
922 .unwrap();
923
924 let hex_calldata = hex::encode(&encoded_solution.swaps);
925
926 let expected_swaps = [
927 "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", ]
962 .join("");
963 assert_eq!(hex_calldata, expected_swaps);
964 assert_eq!(
965 encoded_solution.function_signature,
966 "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
967 .to_string()
968 );
969 assert_eq!(encoded_solution.interacting_with, router_address());
970 }
971
972 #[test]
973 fn test_split_output_cyclic_swap() {
974 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
983 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
984
985 let swap_usdc_weth_v2 = SwapBuilder::new(
986 ProtocolComponent {
987 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
989 static_attributes: {
990 let mut attrs = HashMap::new();
991 attrs.insert(
992 "fee".to_string(),
993 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
994 );
995 attrs
996 },
997 ..Default::default()
998 },
999 usdc.clone(),
1000 weth.clone(),
1001 )
1002 .build();
1003
1004 let swap_weth_usdc_v3_pool1 = SwapBuilder::new(
1005 ProtocolComponent {
1006 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
1009 static_attributes: {
1010 let mut attrs = HashMap::new();
1011 attrs.insert(
1012 "fee".to_string(),
1013 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
1014 );
1015 attrs
1016 },
1017 ..Default::default()
1018 },
1019 weth.clone(),
1020 usdc.clone(),
1021 )
1022 .split(0.6f64)
1023 .build();
1024
1025 let swap_weth_usdc_v3_pool2 = SwapBuilder::new(
1026 ProtocolComponent {
1027 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
1030 static_attributes: {
1031 let mut attrs = HashMap::new();
1032 attrs.insert(
1033 "fee".to_string(),
1034 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
1035 );
1036 attrs
1037 },
1038 ..Default::default()
1039 },
1040 weth.clone(),
1041 usdc.clone(),
1042 )
1043 .build();
1044
1045 let swap_encoder_registry = get_swap_encoder_registry();
1046 let encoder = SplitSwapStrategyEncoder::new(
1047 eth_chain(),
1048 swap_encoder_registry,
1049 UserTransferType::TransferFrom,
1050 Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
1051 false,
1052 )
1053 .unwrap();
1054
1055 let solution = Solution {
1056 exact_out: false,
1057 given_token: usdc.clone(),
1058 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
1060 checked_amount: BigUint::from_str("99025908").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1064 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1065 swaps: vec![swap_usdc_weth_v2, swap_weth_usdc_v3_pool1, swap_weth_usdc_v3_pool2],
1066 ..Default::default()
1067 };
1068
1069 let encoded_solution = encoder
1070 .encode_strategy(&solution)
1071 .unwrap();
1072
1073 let hex_calldata = hex::encode(&encoded_solution.swaps);
1074
1075 let expected_swaps = [
1076 "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", ]
1111 .join("");
1112
1113 assert_eq!(hex_calldata, expected_swaps);
1114 assert_eq!(
1115 encoded_solution.function_signature,
1116 "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
1117 .to_string()
1118 );
1119 assert_eq!(encoded_solution.interacting_with, router_address());
1120 }
1121 }
1122}