1use std::{collections::HashSet, str::FromStr};
2
3use alloy::primitives::{aliases::U24, U8};
4use tycho_common::{models::Chain, Bytes};
5
6use crate::encoding::{
7 errors::EncodingError,
8 evm::{
9 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 = self.encode_swap_header(
153 Bytes::from_str(swap_encoder.executor_address())
154 .map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?,
155 initial_protocol_data,
156 );
157 Ok(EncodedSolution {
158 function_signature: self.function_signature.clone(),
159 interacting_with: self.router_address.clone(),
160 swaps: swap_data,
161 permit: None,
162 n_tokens: 0,
163 })
164 }
165
166 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
167 self.swap_encoder_registry
168 .get_encoder(protocol_system)
169 }
170
171 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
172 Box::new(self.clone())
173 }
174}
175
176#[derive(Clone)]
190pub struct SequentialSwapStrategyEncoder {
191 swap_encoder_registry: SwapEncoderRegistry,
192 function_signature: String,
193 router_address: Bytes,
194 native_address: Bytes,
195 wrapped_address: Bytes,
196 sequential_swap_validator: SequentialSwapValidator,
197 transfer_optimization: TransferOptimization,
198 historical_trade: bool,
199}
200
201impl SequentialSwapStrategyEncoder {
202 pub fn new(
203 chain: Chain,
204 swap_encoder_registry: SwapEncoderRegistry,
205 user_transfer_type: UserTransferType,
206 router_address: Bytes,
207 historical_trade: bool,
208 ) -> Result<Self, EncodingError> {
209 let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
210 "sequentialSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
211 } else {
212 "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
213
214 }.to_string();
215 let native_token_address = chain.native_token().address;
216 let wrapped_token_address = chain.wrapped_native_token().address;
217 Ok(Self {
218 function_signature,
219 swap_encoder_registry,
220 router_address: router_address.clone(),
221 native_address: native_token_address.clone(),
222 wrapped_address: wrapped_token_address.clone(),
223 sequential_swap_validator: SequentialSwapValidator,
224 transfer_optimization: TransferOptimization::new(
225 native_token_address,
226 wrapped_token_address,
227 user_transfer_type,
228 router_address,
229 ),
230 historical_trade,
231 })
232 }
233
234 fn encode_swap_header(&self, executor_address: Bytes, protocol_data: Vec<u8>) -> Vec<u8> {
237 let mut encoded = Vec::new();
238 encoded.extend(executor_address.to_vec());
239 encoded.extend(protocol_data);
240 encoded
241 }
242}
243
244impl StrategyEncoder for SequentialSwapStrategyEncoder {
245 fn encode_strategy(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
246 self.sequential_swap_validator
247 .validate_swap_path(
248 &solution.swaps,
249 &solution.given_token,
250 &solution.checked_token,
251 &solution.native_action,
252 &self.native_address,
253 &self.wrapped_address,
254 )?;
255
256 let grouped_swaps = group_swaps(&solution.swaps);
257
258 let (mut wrap, mut unwrap) = (false, false);
259 if let Some(action) = &solution.native_action {
260 match *action {
261 NativeAction::Wrap => wrap = true,
262 NativeAction::Unwrap => unwrap = true,
263 }
264 }
265
266 let mut swaps = vec![];
267 let mut next_in_between_swap_optimization_allowed = true;
268 for (i, grouped_swap) in grouped_swaps.iter().enumerate() {
269 let protocol = &grouped_swap.protocol_system;
270 let swap_encoder = self
271 .get_swap_encoder(protocol)
272 .ok_or_else(|| {
273 EncodingError::InvalidInput(format!(
274 "Swap encoder not found for protocol: {protocol}",
275 ))
276 })?;
277
278 let in_between_swap_optimization_allowed = next_in_between_swap_optimization_allowed;
279 let next_swap = grouped_swaps.get(i + 1);
280 let (swap_receiver, next_swap_optimization) = self
281 .transfer_optimization
282 .get_receiver(&solution.receiver, next_swap, unwrap)?;
283 next_in_between_swap_optimization_allowed = next_swap_optimization;
284
285 let transfer = self
286 .transfer_optimization
287 .get_transfers(
288 grouped_swap,
289 &solution.given_token,
290 wrap,
291 in_between_swap_optimization_allowed,
292 );
293 let encoding_context = EncodingContext {
294 receiver: swap_receiver,
295 exact_out: solution.exact_out,
296 router_address: Some(self.router_address.clone()),
297 group_token_in: grouped_swap.token_in.clone(),
298 group_token_out: grouped_swap.token_out.clone(),
299 transfer_type: transfer,
300 historical_trade: self.historical_trade,
301 };
302
303 let mut grouped_protocol_data: Vec<Vec<u8>> = vec![];
304 let mut initial_protocol_data: Vec<u8> = vec![];
305 for swap in grouped_swap.swaps.iter() {
306 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
307 if encoding_context.group_token_in == swap.token_in {
308 initial_protocol_data = protocol_data;
309 } else {
310 grouped_protocol_data.push(protocol_data);
311 }
312 }
313
314 if !grouped_protocol_data.is_empty() {
315 if NON_PLE_ENCODED_PROTOCOLS.contains(grouped_swap.protocol_system.as_str()) {
316 for protocol_data in grouped_protocol_data {
317 initial_protocol_data.extend(protocol_data);
318 }
319 } else {
320 initial_protocol_data.extend(ple_encode(grouped_protocol_data));
321 }
322 }
323
324 let swap_data = self.encode_swap_header(
325 Bytes::from_str(swap_encoder.executor_address()).map_err(|_| {
326 EncodingError::FatalError("Invalid executor address".to_string())
327 })?,
328 initial_protocol_data,
329 );
330 swaps.push(swap_data);
331 }
332
333 let encoded_swaps = ple_encode(swaps);
334 Ok(EncodedSolution {
335 interacting_with: self.router_address.clone(),
336 function_signature: self.function_signature.clone(),
337 swaps: encoded_swaps,
338 permit: None,
339 n_tokens: 0,
340 })
341 }
342
343 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
344 self.swap_encoder_registry
345 .get_encoder(protocol_system)
346 }
347
348 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
349 Box::new(self.clone())
350 }
351}
352
353#[derive(Clone)]
367pub struct SplitSwapStrategyEncoder {
368 swap_encoder_registry: SwapEncoderRegistry,
369 function_signature: String,
370 native_address: Bytes,
371 wrapped_address: Bytes,
372 split_swap_validator: SplitSwapValidator,
373 router_address: Bytes,
374 transfer_optimization: TransferOptimization,
375 historical_trade: bool,
376}
377
378impl SplitSwapStrategyEncoder {
379 pub fn new(
380 chain: Chain,
381 swap_encoder_registry: SwapEncoderRegistry,
382 user_transfer_type: UserTransferType,
383 router_address: Bytes,
384 historical_trade: bool,
385 ) -> Result<Self, EncodingError> {
386 let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
387 "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
388 } else {
389 "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
390 }.to_string();
391 let native_token_address = chain.native_token().address;
392 let wrapped_token_address = chain.wrapped_native_token().address;
393 Ok(Self {
394 function_signature,
395 swap_encoder_registry,
396 native_address: native_token_address.clone(),
397 wrapped_address: wrapped_token_address.clone(),
398 split_swap_validator: SplitSwapValidator,
399 router_address: router_address.clone(),
400 transfer_optimization: TransferOptimization::new(
401 native_token_address,
402 wrapped_token_address,
403 user_transfer_type,
404 router_address,
405 ),
406 historical_trade,
407 })
408 }
409
410 fn encode_swap_header(
413 &self,
414 token_in: U8,
415 token_out: U8,
416 split: U24,
417 executor_address: Bytes,
418 protocol_data: Vec<u8>,
419 ) -> Vec<u8> {
420 let mut encoded = Vec::new();
421 encoded.push(token_in.to_be_bytes_vec()[0]);
422 encoded.push(token_out.to_be_bytes_vec()[0]);
423 encoded.extend_from_slice(&split.to_be_bytes_vec());
424 encoded.extend(executor_address.to_vec());
425 encoded.extend(protocol_data);
426 encoded
427 }
428}
429
430impl StrategyEncoder for SplitSwapStrategyEncoder {
431 fn encode_strategy(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
432 self.split_swap_validator
433 .validate_split_percentages(&solution.swaps)?;
434 self.split_swap_validator
435 .validate_swap_path(
436 &solution.swaps,
437 &solution.given_token,
438 &solution.checked_token,
439 &solution.native_action,
440 &self.native_address,
441 &self.wrapped_address,
442 )?;
443
444 let solution_tokens: HashSet<&Bytes> = vec![&solution.given_token, &solution.checked_token]
447 .into_iter()
448 .collect();
449
450 let grouped_swaps = group_swaps(&solution.swaps);
451
452 let intermediary_tokens: HashSet<&Bytes> = grouped_swaps
453 .iter()
454 .flat_map(|grouped_swap| vec![&grouped_swap.token_in, &grouped_swap.token_out])
455 .collect();
456 let mut intermediary_tokens: Vec<&Bytes> = intermediary_tokens
457 .difference(&solution_tokens)
458 .cloned()
459 .collect();
460 intermediary_tokens.sort();
463
464 let (mut unwrap, mut wrap) = (false, false);
465 if let Some(action) = &solution.native_action {
466 match *action {
467 NativeAction::Wrap => wrap = true,
468 NativeAction::Unwrap => unwrap = true,
469 }
470 }
471
472 let mut tokens = Vec::with_capacity(2 + intermediary_tokens.len());
473 if wrap {
474 tokens.push(&self.wrapped_address);
475 } else {
476 tokens.push(&solution.given_token);
477 }
478 tokens.extend(intermediary_tokens);
479
480 if unwrap {
481 tokens.push(&self.wrapped_address);
482 } else {
483 tokens.push(&solution.checked_token);
484 }
485
486 let mut swaps = vec![];
487 for grouped_swap in grouped_swaps.iter() {
488 let protocol = &grouped_swap.protocol_system;
489 let swap_encoder = self
490 .get_swap_encoder(protocol)
491 .ok_or_else(|| {
492 EncodingError::InvalidInput(format!(
493 "Swap encoder not found for protocol: {protocol}",
494 ))
495 })?;
496
497 let swap_receiver = if !unwrap && grouped_swap.token_out == solution.checked_token {
498 solution.receiver.clone()
499 } else {
500 self.router_address.clone()
501 };
502 let transfer = self
503 .transfer_optimization
504 .get_transfers(grouped_swap, &solution.given_token, wrap, false);
505 let encoding_context = EncodingContext {
506 receiver: swap_receiver,
507 exact_out: solution.exact_out,
508 router_address: Some(self.router_address.clone()),
509 group_token_in: grouped_swap.token_in.clone(),
510 group_token_out: grouped_swap.token_out.clone(),
511 transfer_type: transfer,
512 historical_trade: self.historical_trade,
513 };
514
515 let mut grouped_protocol_data: Vec<Vec<u8>> = vec![];
516 let mut initial_protocol_data: Vec<u8> = vec![];
517 for swap in grouped_swap.swaps.iter() {
518 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
519 if encoding_context.group_token_in == swap.token_in {
520 initial_protocol_data = protocol_data;
521 } else {
522 grouped_protocol_data.push(protocol_data);
523 }
524 }
525
526 if !grouped_protocol_data.is_empty() {
527 if NON_PLE_ENCODED_PROTOCOLS.contains(grouped_swap.protocol_system.as_str()) {
528 for protocol_data in grouped_protocol_data {
529 initial_protocol_data.extend(protocol_data);
530 }
531 } else {
532 initial_protocol_data.extend(ple_encode(grouped_protocol_data));
533 }
534 }
535
536 let swap_data = self.encode_swap_header(
537 get_token_position(&tokens, &grouped_swap.token_in)?,
538 get_token_position(&tokens, &grouped_swap.token_out)?,
539 percentage_to_uint24(grouped_swap.split),
540 Bytes::from_str(swap_encoder.executor_address()).map_err(|_| {
541 EncodingError::FatalError("Invalid executor address".to_string())
542 })?,
543 initial_protocol_data,
544 );
545 swaps.push(swap_data);
546 }
547
548 let encoded_swaps = ple_encode(swaps);
549 let tokens_len = if solution.given_token == solution.checked_token {
550 tokens.len() - 1
551 } else {
552 tokens.len()
553 };
554 Ok(EncodedSolution {
555 interacting_with: self.router_address.clone(),
556 function_signature: self.function_signature.clone(),
557 swaps: encoded_swaps,
558 permit: None,
559 n_tokens: tokens_len,
560 })
561 }
562
563 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
564 self.swap_encoder_registry
565 .get_encoder(protocol_system)
566 }
567
568 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
569 Box::new(self.clone())
570 }
571}
572
573#[cfg(test)]
574mod tests {
575 use std::{collections::HashMap, fs, str::FromStr};
576
577 use alloy::{hex::encode, primitives::hex};
578 use num_bigint::{BigInt, BigUint};
579 use tycho_common::{
580 models::{protocol::ProtocolComponent, Chain},
581 Bytes,
582 };
583
584 use super::*;
585
586 fn eth_chain() -> Chain {
587 Chain::Ethereum
588 }
589
590 fn weth() -> Bytes {
591 Bytes::from(hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").to_vec())
592 }
593
594 fn get_swap_encoder_registry() -> SwapEncoderRegistry {
595 let executors_addresses =
596 fs::read_to_string("config/test_executor_addresses.json").unwrap();
597 let eth_chain = eth_chain();
598 SwapEncoderRegistry::new(Some(executors_addresses), eth_chain).unwrap()
599 }
600
601 fn router_address() -> Bytes {
602 Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap()
603 }
604
605 mod single {
606 use super::*;
607 use crate::encoding::models::SwapBuilder;
608 #[test]
609 fn test_single_swap_strategy_encoder() {
610 let checked_amount = BigUint::from_str("2018817438608734439720").unwrap();
613 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
614 let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
615
616 let swap = SwapBuilder::new(
617 ProtocolComponent {
618 id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
619 protocol_system: "uniswap_v2".to_string(),
620 ..Default::default()
621 },
622 weth.clone(),
623 dai.clone(),
624 )
625 .build();
626 let swap_encoder_registry = get_swap_encoder_registry();
627 let encoder = SingleSwapStrategyEncoder::new(
628 eth_chain(),
629 swap_encoder_registry,
630 UserTransferType::TransferFromPermit2,
631 router_address(),
632 false,
633 )
634 .unwrap();
635 let solution = Solution {
636 exact_out: false,
637 given_token: weth,
638 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
639 checked_token: dai,
640 checked_amount: checked_amount.clone(),
641 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
642 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
643 swaps: vec![swap],
644 ..Default::default()
645 };
646
647 let encoded_solution = encoder
648 .encode_strategy(&solution)
649 .unwrap();
650
651 let expected_swap = String::from(concat!(
652 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "00", ));
660 let hex_calldata = encode(&encoded_solution.swaps);
661
662 assert_eq!(hex_calldata, expected_swap);
663 assert_eq!(encoded_solution.function_signature, "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string());
664 assert_eq!(encoded_solution.interacting_with, router_address());
665 }
666
667 #[test]
668 fn test_single_swap_strategy_encoder_no_transfer_in() {
669 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
673 let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
674
675 let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap();
676
677 let swap = SwapBuilder::new(
678 ProtocolComponent {
679 id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
680 protocol_system: "uniswap_v2".to_string(),
681 ..Default::default()
682 },
683 weth.clone(),
684 dai.clone(),
685 )
686 .build();
687 let swap_encoder_registry = get_swap_encoder_registry();
688 let encoder = SingleSwapStrategyEncoder::new(
689 eth_chain(),
690 swap_encoder_registry,
691 UserTransferType::None,
692 router_address(),
693 false,
694 )
695 .unwrap();
696 let solution = Solution {
697 exact_out: false,
698 given_token: weth,
699 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
700 checked_token: dai,
701 checked_amount,
702 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
703 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
704 swaps: vec![swap],
705 ..Default::default()
706 };
707
708 let encoded_solution = encoder
709 .encode_strategy(&solution)
710 .unwrap();
711
712 let expected_input = [
713 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "01", ]
721 .join("");
722
723 let hex_calldata = encode(&encoded_solution.swaps);
724
725 assert_eq!(hex_calldata, expected_input);
726 assert_eq!(
727 encoded_solution.function_signature,
728 "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
729 .to_string()
730 );
731 assert_eq!(encoded_solution.interacting_with, router_address());
732 }
733 }
734
735 mod sequential {
736 use super::*;
737 use crate::encoding::models::SwapBuilder;
738
739 #[test]
740 fn test_sequential_swap_strategy_encoder_no_permit2() {
741 let weth = weth();
746 let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap();
747 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
748
749 let swap_weth_wbtc = SwapBuilder::new(
750 ProtocolComponent {
751 id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(),
752 protocol_system: "uniswap_v2".to_string(),
753 ..Default::default()
754 },
755 weth.clone(),
756 wbtc.clone(),
757 )
758 .build();
759 let swap_wbtc_usdc = SwapBuilder::new(
760 ProtocolComponent {
761 id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(),
762 protocol_system: "uniswap_v2".to_string(),
763 ..Default::default()
764 },
765 wbtc.clone(),
766 usdc.clone(),
767 )
768 .build();
769 let swap_encoder_registry = get_swap_encoder_registry();
770 let encoder = SequentialSwapStrategyEncoder::new(
771 eth_chain(),
772 swap_encoder_registry,
773 UserTransferType::TransferFrom,
774 router_address(),
775 false,
776 )
777 .unwrap();
778 let solution = Solution {
779 exact_out: false,
780 given_token: weth,
781 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
782 checked_token: usdc,
783 checked_amount: BigUint::from_str("26173932").unwrap(),
784 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
785 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
786 swaps: vec![swap_weth_wbtc, swap_wbtc_usdc],
787 ..Default::default()
788 };
789
790 let encoded_solution = encoder
791 .encode_strategy(&solution)
792 .unwrap();
793
794 let hex_calldata = encode(&encoded_solution.swaps);
795
796 let expected = String::from(concat!(
797 "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "bb2b8038a1640196fbe3e38816f3e67cba72d940", "004375dff511095cc5a197a54140a24efef3a416", "00", "00", "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "2260fac5e5542a773aa44fbcfedf7c193bc2c599", "004375dff511095cc5a197a54140a24efef3a416", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "01", "02", ));
814
815 assert_eq!(hex_calldata, expected);
816 assert_eq!(
817 encoded_solution.function_signature,
818 "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
819 .to_string()
820 );
821 assert_eq!(encoded_solution.interacting_with, router_address());
822 }
823 }
824
825 mod split {
826 use super::*;
827 use crate::encoding::models::SwapBuilder;
828
829 #[test]
830 fn test_split_input_cyclic_swap() {
831 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
840 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
841
842 let swap_usdc_weth_pool1 = SwapBuilder::new(
844 ProtocolComponent {
845 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
848 static_attributes: {
849 let mut attrs = HashMap::new();
850 attrs.insert(
851 "fee".to_string(),
852 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
853 );
854 attrs
855 },
856 ..Default::default()
857 },
858 usdc.clone(),
859 weth.clone(),
860 )
861 .split(0.6f64)
862 .build();
863
864 let swap_usdc_weth_pool2 = SwapBuilder::new(
866 ProtocolComponent {
867 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
870 static_attributes: {
871 let mut attrs = HashMap::new();
872 attrs.insert(
873 "fee".to_string(),
874 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
875 );
876 attrs
877 },
878 ..Default::default()
879 },
880 usdc.clone(),
881 weth.clone(),
882 )
883 .build();
884
885 let swap_weth_usdc_pool2 = SwapBuilder::new(
887 ProtocolComponent {
888 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
891 static_attributes: {
892 let mut attrs = HashMap::new();
893 attrs.insert(
894 "fee".to_string(),
895 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
896 );
897 attrs
898 },
899 ..Default::default()
900 },
901 weth.clone(),
902 usdc.clone(),
903 )
904 .build();
905 let swap_encoder_registry = get_swap_encoder_registry();
906 let encoder = SplitSwapStrategyEncoder::new(
907 eth_chain(),
908 swap_encoder_registry,
909 UserTransferType::TransferFromPermit2,
910 Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
911 false,
912 )
913 .unwrap();
914
915 let solution = Solution {
916 exact_out: false,
917 given_token: usdc.clone(),
918 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
920 checked_amount: BigUint::from_str("99574171").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
924 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
925 swaps: vec![swap_usdc_weth_pool1, swap_usdc_weth_pool2, swap_weth_usdc_pool2],
926 ..Default::default()
927 };
928
929 let encoded_solution = encoder
930 .encode_strategy(&solution)
931 .unwrap();
932
933 let hex_calldata = hex::encode(&encoded_solution.swaps);
934
935 let expected_swaps = [
936 "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", ]
971 .join("");
972 assert_eq!(hex_calldata, expected_swaps);
973 assert_eq!(
974 encoded_solution.function_signature,
975 "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
976 .to_string()
977 );
978 assert_eq!(encoded_solution.interacting_with, router_address());
979 }
980
981 #[test]
982 fn test_split_output_cyclic_swap() {
983 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
992 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
993
994 let swap_usdc_weth_v2 = SwapBuilder::new(
995 ProtocolComponent {
996 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
998 static_attributes: {
999 let mut attrs = HashMap::new();
1000 attrs.insert(
1001 "fee".to_string(),
1002 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
1003 );
1004 attrs
1005 },
1006 ..Default::default()
1007 },
1008 usdc.clone(),
1009 weth.clone(),
1010 )
1011 .build();
1012
1013 let swap_weth_usdc_v3_pool1 = SwapBuilder::new(
1014 ProtocolComponent {
1015 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
1018 static_attributes: {
1019 let mut attrs = HashMap::new();
1020 attrs.insert(
1021 "fee".to_string(),
1022 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
1023 );
1024 attrs
1025 },
1026 ..Default::default()
1027 },
1028 weth.clone(),
1029 usdc.clone(),
1030 )
1031 .split(0.6f64)
1032 .build();
1033
1034 let swap_weth_usdc_v3_pool2 = SwapBuilder::new(
1035 ProtocolComponent {
1036 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
1039 static_attributes: {
1040 let mut attrs = HashMap::new();
1041 attrs.insert(
1042 "fee".to_string(),
1043 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
1044 );
1045 attrs
1046 },
1047 ..Default::default()
1048 },
1049 weth.clone(),
1050 usdc.clone(),
1051 )
1052 .build();
1053
1054 let swap_encoder_registry = get_swap_encoder_registry();
1055 let encoder = SplitSwapStrategyEncoder::new(
1056 eth_chain(),
1057 swap_encoder_registry,
1058 UserTransferType::TransferFrom,
1059 Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
1060 false,
1061 )
1062 .unwrap();
1063
1064 let solution = Solution {
1065 exact_out: false,
1066 given_token: usdc.clone(),
1067 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
1069 checked_amount: BigUint::from_str("99025908").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1073 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1074 swaps: vec![swap_usdc_weth_v2, swap_weth_usdc_v3_pool1, swap_weth_usdc_v3_pool2],
1075 ..Default::default()
1076 };
1077
1078 let encoded_solution = encoder
1079 .encode_strategy(&solution)
1080 .unwrap();
1081
1082 let hex_calldata = hex::encode(&encoded_solution.swaps);
1083
1084 let expected_swaps = [
1085 "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", ]
1120 .join("");
1121
1122 assert_eq!(hex_calldata, expected_swaps);
1123 assert_eq!(
1124 encoded_solution.function_signature,
1125 "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
1126 .to_string()
1127 );
1128 assert_eq!(encoded_solution.interacting_with, router_address());
1129 }
1130 }
1131}