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::Swap;
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 = Swap::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 let swap_encoder_registry = get_swap_encoder_registry();
617 let encoder = SingleSwapStrategyEncoder::new(
618 eth_chain(),
619 swap_encoder_registry,
620 UserTransferType::TransferFromPermit2,
621 router_address(),
622 false,
623 )
624 .unwrap();
625 let solution = Solution {
626 exact_out: false,
627 given_token: weth,
628 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
629 checked_token: dai,
630 checked_amount: checked_amount.clone(),
631 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
632 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
633 swaps: vec![swap],
634 ..Default::default()
635 };
636
637 let encoded_solution = encoder
638 .encode_strategy(&solution)
639 .unwrap();
640
641 let expected_swap = String::from(concat!(
642 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "00", ));
650 let hex_calldata = encode(&encoded_solution.swaps);
651
652 assert_eq!(hex_calldata, expected_swap);
653 assert_eq!(encoded_solution.function_signature, "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string());
654 assert_eq!(encoded_solution.interacting_with, router_address());
655 }
656
657 #[test]
658 fn test_single_swap_strategy_encoder_no_transfer_in() {
659 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
663 let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
664
665 let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap();
666
667 let swap = Swap::new(
668 ProtocolComponent {
669 id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
670 protocol_system: "uniswap_v2".to_string(),
671 ..Default::default()
672 },
673 weth.clone(),
674 dai.clone(),
675 );
676 let swap_encoder_registry = get_swap_encoder_registry();
677 let encoder = SingleSwapStrategyEncoder::new(
678 eth_chain(),
679 swap_encoder_registry,
680 UserTransferType::None,
681 router_address(),
682 false,
683 )
684 .unwrap();
685 let solution = Solution {
686 exact_out: false,
687 given_token: weth,
688 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
689 checked_token: dai,
690 checked_amount,
691 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
692 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
693 swaps: vec![swap],
694 ..Default::default()
695 };
696
697 let encoded_solution = encoder
698 .encode_strategy(&solution)
699 .unwrap();
700
701 let expected_input = [
702 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "01", ]
710 .join("");
711
712 let hex_calldata = encode(&encoded_solution.swaps);
713
714 assert_eq!(hex_calldata, expected_input);
715 assert_eq!(
716 encoded_solution.function_signature,
717 "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
718 .to_string()
719 );
720 assert_eq!(encoded_solution.interacting_with, router_address());
721 }
722 }
723
724 mod sequential {
725 use super::*;
726 use crate::encoding::models::Swap;
727
728 #[test]
729 fn test_sequential_swap_strategy_encoder_no_permit2() {
730 let weth = weth();
735 let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap();
736 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
737
738 let swap_weth_wbtc = Swap::new(
739 ProtocolComponent {
740 id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(),
741 protocol_system: "uniswap_v2".to_string(),
742 ..Default::default()
743 },
744 weth.clone(),
745 wbtc.clone(),
746 );
747 let swap_wbtc_usdc = Swap::new(
748 ProtocolComponent {
749 id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(),
750 protocol_system: "uniswap_v2".to_string(),
751 ..Default::default()
752 },
753 wbtc.clone(),
754 usdc.clone(),
755 );
756 let swap_encoder_registry = get_swap_encoder_registry();
757 let encoder = SequentialSwapStrategyEncoder::new(
758 eth_chain(),
759 swap_encoder_registry,
760 UserTransferType::TransferFrom,
761 router_address(),
762 false,
763 )
764 .unwrap();
765 let solution = Solution {
766 exact_out: false,
767 given_token: weth,
768 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
769 checked_token: usdc,
770 checked_amount: BigUint::from_str("26173932").unwrap(),
771 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
772 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
773 swaps: vec![swap_weth_wbtc, swap_wbtc_usdc],
774 ..Default::default()
775 };
776
777 let encoded_solution = encoder
778 .encode_strategy(&solution)
779 .unwrap();
780
781 let hex_calldata = encode(&encoded_solution.swaps);
782
783 let expected = String::from(concat!(
784 "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "bb2b8038a1640196fbe3e38816f3e67cba72d940", "004375dff511095cc5a197a54140a24efef3a416", "00", "00", "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "2260fac5e5542a773aa44fbcfedf7c193bc2c599", "004375dff511095cc5a197a54140a24efef3a416", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "01", "02", ));
801
802 assert_eq!(hex_calldata, expected);
803 assert_eq!(
804 encoded_solution.function_signature,
805 "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
806 .to_string()
807 );
808 assert_eq!(encoded_solution.interacting_with, router_address());
809 }
810 }
811
812 mod split {
813 use super::*;
814 use crate::encoding::models::Swap;
815
816 #[test]
817 fn test_split_input_cyclic_swap() {
818 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
827 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
828
829 let swap_usdc_weth_pool1 = Swap::new(
831 ProtocolComponent {
832 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
835 static_attributes: {
836 let mut attrs = HashMap::new();
837 attrs.insert(
838 "fee".to_string(),
839 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
840 );
841 attrs
842 },
843 ..Default::default()
844 },
845 usdc.clone(),
846 weth.clone(),
847 )
848 .split(0.6f64);
849
850 let swap_usdc_weth_pool2 = Swap::new(
852 ProtocolComponent {
853 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
856 static_attributes: {
857 let mut attrs = HashMap::new();
858 attrs.insert(
859 "fee".to_string(),
860 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
861 );
862 attrs
863 },
864 ..Default::default()
865 },
866 usdc.clone(),
867 weth.clone(),
868 );
869
870 let swap_weth_usdc_pool2 = Swap::new(
872 ProtocolComponent {
873 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
876 static_attributes: {
877 let mut attrs = HashMap::new();
878 attrs.insert(
879 "fee".to_string(),
880 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
881 );
882 attrs
883 },
884 ..Default::default()
885 },
886 weth.clone(),
887 usdc.clone(),
888 );
889 let swap_encoder_registry = get_swap_encoder_registry();
890 let encoder = SplitSwapStrategyEncoder::new(
891 eth_chain(),
892 swap_encoder_registry,
893 UserTransferType::TransferFromPermit2,
894 Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
895 false,
896 )
897 .unwrap();
898
899 let solution = Solution {
900 exact_out: false,
901 given_token: usdc.clone(),
902 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
904 checked_amount: BigUint::from_str("99574171").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
908 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
909 swaps: vec![swap_usdc_weth_pool1, swap_usdc_weth_pool2, swap_weth_usdc_pool2],
910 ..Default::default()
911 };
912
913 let encoded_solution = encoder
914 .encode_strategy(&solution)
915 .unwrap();
916
917 let hex_calldata = hex::encode(&encoded_solution.swaps);
918
919 let expected_swaps = [
920 "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", ]
955 .join("");
956 assert_eq!(hex_calldata, expected_swaps);
957 assert_eq!(
958 encoded_solution.function_signature,
959 "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
960 .to_string()
961 );
962 assert_eq!(encoded_solution.interacting_with, router_address());
963 }
964
965 #[test]
966 fn test_split_output_cyclic_swap() {
967 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
976 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
977
978 let swap_usdc_weth_v2 = Swap::new(
979 ProtocolComponent {
980 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
982 static_attributes: {
983 let mut attrs = HashMap::new();
984 attrs.insert(
985 "fee".to_string(),
986 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
987 );
988 attrs
989 },
990 ..Default::default()
991 },
992 usdc.clone(),
993 weth.clone(),
994 );
995
996 let swap_weth_usdc_v3_pool1 = Swap::new(
997 ProtocolComponent {
998 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
1001 static_attributes: {
1002 let mut attrs = HashMap::new();
1003 attrs.insert(
1004 "fee".to_string(),
1005 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
1006 );
1007 attrs
1008 },
1009 ..Default::default()
1010 },
1011 weth.clone(),
1012 usdc.clone(),
1013 )
1014 .split(0.6f64);
1015
1016 let swap_weth_usdc_v3_pool2 = Swap::new(
1017 ProtocolComponent {
1018 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
1021 static_attributes: {
1022 let mut attrs = HashMap::new();
1023 attrs.insert(
1024 "fee".to_string(),
1025 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
1026 );
1027 attrs
1028 },
1029 ..Default::default()
1030 },
1031 weth.clone(),
1032 usdc.clone(),
1033 );
1034
1035 let swap_encoder_registry = get_swap_encoder_registry();
1036 let encoder = SplitSwapStrategyEncoder::new(
1037 eth_chain(),
1038 swap_encoder_registry,
1039 UserTransferType::TransferFrom,
1040 Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
1041 false,
1042 )
1043 .unwrap();
1044
1045 let solution = Solution {
1046 exact_out: false,
1047 given_token: usdc.clone(),
1048 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
1050 checked_amount: BigUint::from_str("99025908").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1054 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1055 swaps: vec![swap_usdc_weth_v2, swap_weth_usdc_v3_pool1, swap_weth_usdc_v3_pool2],
1056 ..Default::default()
1057 };
1058
1059 let encoded_solution = encoder
1060 .encode_strategy(&solution)
1061 .unwrap();
1062
1063 let hex_calldata = hex::encode(&encoded_solution.swaps);
1064
1065 let expected_swaps = [
1066 "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", ]
1101 .join("");
1102
1103 assert_eq!(hex_calldata, expected_swaps);
1104 assert_eq!(
1105 encoded_solution.function_signature,
1106 "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
1107 .to_string()
1108 );
1109 assert_eq!(encoded_solution.interacting_with, router_address());
1110 }
1111 }
1112}