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 let registry = SwapEncoderRegistry::new(eth_chain);
590 registry
591 .add_default_encoders(Some(executors_addresses))
592 .unwrap()
593 }
594
595 fn router_address() -> Bytes {
596 Bytes::from_str("0x6bc529DC7B81A031828dDCE2BC419d01FF268C66").unwrap()
597 }
598
599 mod single {
600 use super::*;
601 use crate::encoding::models::Swap;
602 #[test]
603 fn test_single_swap_strategy_encoder() {
604 let checked_amount = BigUint::from_str("2018817438608734439720").unwrap();
607 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
608 let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
609
610 let swap = Swap::new(
611 ProtocolComponent {
612 id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
613 protocol_system: "uniswap_v2".to_string(),
614 ..Default::default()
615 },
616 weth.clone(),
617 dai.clone(),
618 );
619 let swap_encoder_registry = get_swap_encoder_registry();
620 let encoder = SingleSwapStrategyEncoder::new(
621 eth_chain(),
622 swap_encoder_registry,
623 UserTransferType::TransferFromPermit2,
624 router_address(),
625 false,
626 )
627 .unwrap();
628 let solution = Solution {
629 exact_out: false,
630 given_token: weth,
631 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
632 checked_token: dai,
633 checked_amount: checked_amount.clone(),
634 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
635 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
636 swaps: vec![swap],
637 ..Default::default()
638 };
639
640 let encoded_solution = encoder
641 .encode_strategy(&solution)
642 .unwrap();
643
644 let expected_swap = String::from(concat!(
645 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "00", ));
653 let hex_calldata = encode(&encoded_solution.swaps);
654
655 assert_eq!(hex_calldata, expected_swap);
656 assert_eq!(encoded_solution.function_signature, "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string());
657 assert_eq!(encoded_solution.interacting_with, router_address());
658 }
659
660 #[test]
661 fn test_single_swap_strategy_encoder_no_transfer_in() {
662 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
666 let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
667
668 let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap();
669
670 let swap = Swap::new(
671 ProtocolComponent {
672 id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
673 protocol_system: "uniswap_v2".to_string(),
674 ..Default::default()
675 },
676 weth.clone(),
677 dai.clone(),
678 );
679 let swap_encoder_registry = get_swap_encoder_registry();
680 let encoder = SingleSwapStrategyEncoder::new(
681 eth_chain(),
682 swap_encoder_registry,
683 UserTransferType::None,
684 router_address(),
685 false,
686 )
687 .unwrap();
688 let solution = Solution {
689 exact_out: false,
690 given_token: weth,
691 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
692 checked_token: dai,
693 checked_amount,
694 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
695 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
696 swaps: vec![swap],
697 ..Default::default()
698 };
699
700 let encoded_solution = encoder
701 .encode_strategy(&solution)
702 .unwrap();
703
704 let expected_input = [
705 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "01", ]
713 .join("");
714
715 let hex_calldata = encode(&encoded_solution.swaps);
716
717 assert_eq!(hex_calldata, expected_input);
718 assert_eq!(
719 encoded_solution.function_signature,
720 "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
721 .to_string()
722 );
723 assert_eq!(encoded_solution.interacting_with, router_address());
724 }
725 }
726
727 mod sequential {
728 use super::*;
729 use crate::encoding::models::Swap;
730
731 #[test]
732 fn test_sequential_swap_strategy_encoder_no_permit2() {
733 let weth = weth();
738 let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap();
739 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
740
741 let swap_weth_wbtc = Swap::new(
742 ProtocolComponent {
743 id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(),
744 protocol_system: "uniswap_v2".to_string(),
745 ..Default::default()
746 },
747 weth.clone(),
748 wbtc.clone(),
749 );
750 let swap_wbtc_usdc = Swap::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 let swap_encoder_registry = get_swap_encoder_registry();
760 let encoder = SequentialSwapStrategyEncoder::new(
761 eth_chain(),
762 swap_encoder_registry,
763 UserTransferType::TransferFrom,
764 router_address(),
765 false,
766 )
767 .unwrap();
768 let solution = Solution {
769 exact_out: false,
770 given_token: weth,
771 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
772 checked_token: usdc,
773 checked_amount: BigUint::from_str("26173932").unwrap(),
774 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
775 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
776 swaps: vec![swap_weth_wbtc, swap_wbtc_usdc],
777 ..Default::default()
778 };
779
780 let encoded_solution = encoder
781 .encode_strategy(&solution)
782 .unwrap();
783
784 let hex_calldata = encode(&encoded_solution.swaps);
785
786 let expected = String::from(concat!(
787 "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "bb2b8038a1640196fbe3e38816f3e67cba72d940", "004375dff511095cc5a197a54140a24efef3a416", "00", "00", "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "2260fac5e5542a773aa44fbcfedf7c193bc2c599", "004375dff511095cc5a197a54140a24efef3a416", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "01", "02", ));
804
805 assert_eq!(hex_calldata, expected);
806 assert_eq!(
807 encoded_solution.function_signature,
808 "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
809 .to_string()
810 );
811 assert_eq!(encoded_solution.interacting_with, router_address());
812 }
813 }
814
815 mod split {
816 use super::*;
817 use crate::encoding::models::Swap;
818
819 #[test]
820 fn test_split_input_cyclic_swap() {
821 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
830 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
831
832 let swap_usdc_weth_pool1 = Swap::new(
834 ProtocolComponent {
835 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
838 static_attributes: {
839 let mut attrs = HashMap::new();
840 attrs.insert(
841 "fee".to_string(),
842 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
843 );
844 attrs
845 },
846 ..Default::default()
847 },
848 usdc.clone(),
849 weth.clone(),
850 )
851 .split(0.6f64);
852
853 let swap_usdc_weth_pool2 = Swap::new(
855 ProtocolComponent {
856 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
859 static_attributes: {
860 let mut attrs = HashMap::new();
861 attrs.insert(
862 "fee".to_string(),
863 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
864 );
865 attrs
866 },
867 ..Default::default()
868 },
869 usdc.clone(),
870 weth.clone(),
871 );
872
873 let swap_weth_usdc_pool2 = Swap::new(
875 ProtocolComponent {
876 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
879 static_attributes: {
880 let mut attrs = HashMap::new();
881 attrs.insert(
882 "fee".to_string(),
883 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
884 );
885 attrs
886 },
887 ..Default::default()
888 },
889 weth.clone(),
890 usdc.clone(),
891 );
892 let swap_encoder_registry = get_swap_encoder_registry();
893 let encoder = SplitSwapStrategyEncoder::new(
894 eth_chain(),
895 swap_encoder_registry,
896 UserTransferType::TransferFromPermit2,
897 Bytes::from("0x6bc529DC7B81A031828dDCE2BC419d01FF268C66"),
898 false,
899 )
900 .unwrap();
901
902 let solution = Solution {
903 exact_out: false,
904 given_token: usdc.clone(),
905 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
907 checked_amount: BigUint::from_str("99574171").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
911 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
912 swaps: vec![swap_usdc_weth_pool1, swap_usdc_weth_pool2, swap_weth_usdc_pool2],
913 ..Default::default()
914 };
915
916 let encoded_solution = encoder
917 .encode_strategy(&solution)
918 .unwrap();
919
920 let hex_calldata = hex::encode(&encoded_solution.swaps);
921
922 let expected_swaps = [
923 "006e", "00", "01", "999999", "2e234dae75c793f67a35089c9d99245e1c58470b", "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "0001f4", "6bc529dc7b81a031828ddce2bc419d01ff268c66", "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", "01", "00", "006e", "00", "01", "000000", "2e234dae75c793f67a35089c9d99245e1c58470b", "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "000bb8", "6bc529dc7b81a031828ddce2bc419d01ff268c66", "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", "01", "00", "0057", "01", "00", "000000", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "01", ]
958 .join("");
959 assert_eq!(hex_calldata, expected_swaps);
960 assert_eq!(
961 encoded_solution.function_signature,
962 "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
963 .to_string()
964 );
965 assert_eq!(encoded_solution.interacting_with, router_address());
966 }
967
968 #[test]
969 fn test_split_output_cyclic_swap() {
970 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
979 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
980
981 let swap_usdc_weth_v2 = Swap::new(
982 ProtocolComponent {
983 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
985 static_attributes: {
986 let mut attrs = HashMap::new();
987 attrs.insert(
988 "fee".to_string(),
989 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
990 );
991 attrs
992 },
993 ..Default::default()
994 },
995 usdc.clone(),
996 weth.clone(),
997 );
998
999 let swap_weth_usdc_v3_pool1 = Swap::new(
1000 ProtocolComponent {
1001 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
1004 static_attributes: {
1005 let mut attrs = HashMap::new();
1006 attrs.insert(
1007 "fee".to_string(),
1008 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
1009 );
1010 attrs
1011 },
1012 ..Default::default()
1013 },
1014 weth.clone(),
1015 usdc.clone(),
1016 )
1017 .split(0.6f64);
1018
1019 let swap_weth_usdc_v3_pool2 = Swap::new(
1020 ProtocolComponent {
1021 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
1024 static_attributes: {
1025 let mut attrs = HashMap::new();
1026 attrs.insert(
1027 "fee".to_string(),
1028 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
1029 );
1030 attrs
1031 },
1032 ..Default::default()
1033 },
1034 weth.clone(),
1035 usdc.clone(),
1036 );
1037
1038 let swap_encoder_registry = get_swap_encoder_registry();
1039 let encoder = SplitSwapStrategyEncoder::new(
1040 eth_chain(),
1041 swap_encoder_registry,
1042 UserTransferType::TransferFrom,
1043 Bytes::from("0x6bc529DC7B81A031828dDCE2BC419d01FF268C66"),
1044 false,
1045 )
1046 .unwrap();
1047
1048 let solution = Solution {
1049 exact_out: false,
1050 given_token: usdc.clone(),
1051 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
1053 checked_amount: BigUint::from_str("99025908").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1057 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1058 swaps: vec![swap_usdc_weth_v2, swap_weth_usdc_v3_pool1, swap_weth_usdc_v3_pool2],
1059 ..Default::default()
1060 };
1061
1062 let encoded_solution = encoder
1063 .encode_strategy(&solution)
1064 .unwrap();
1065
1066 let hex_calldata = hex::encode(&encoded_solution.swaps);
1067
1068 let expected_swaps = [
1069 "0057", "00", "01", "000000", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", "6bc529dc7b81a031828ddce2bc419d01ff268c66", "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", ]
1104 .join("");
1105
1106 assert_eq!(hex_calldata, expected_swaps);
1107 assert_eq!(
1108 encoded_solution.function_signature,
1109 "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
1110 .to_string()
1111 );
1112 assert_eq!(encoded_solution.interacting_with, router_address());
1113 }
1114 }
1115}