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