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)]
30pub struct SingleSwapStrategyEncoder {
31 swap_encoder_registry: SwapEncoderRegistry,
32 function_signature: String,
33 router_address: Bytes,
34 transfer_optimization: TransferOptimization,
35}
36
37impl SingleSwapStrategyEncoder {
38 pub fn new(
39 chain: Chain,
40 swap_encoder_registry: SwapEncoderRegistry,
41 user_transfer_type: UserTransferType,
42 router_address: Bytes,
43 ) -> Result<Self, EncodingError> {
44 let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
45 "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
46 } else {
47 "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
48 }.to_string();
49
50 Ok(Self {
51 function_signature,
52 swap_encoder_registry,
53 router_address: router_address.clone(),
54 transfer_optimization: TransferOptimization::new(
55 chain.native_token().address,
56 chain.wrapped_native_token().address,
57 user_transfer_type,
58 router_address,
59 ),
60 })
61 }
62
63 fn encode_swap_header(&self, executor_address: Bytes, protocol_data: Vec<u8>) -> Vec<u8> {
66 let mut encoded = Vec::new();
67 encoded.extend(executor_address.to_vec());
68 encoded.extend(protocol_data);
69 encoded
70 }
71}
72
73impl StrategyEncoder for SingleSwapStrategyEncoder {
74 fn encode_strategy(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
75 let grouped_swaps = group_swaps(&solution.swaps);
76 let number_of_groups = grouped_swaps.len();
77 if number_of_groups != 1 {
78 return Err(EncodingError::InvalidInput(format!(
79 "Single strategy only supports exactly one swap for non-groupable protocols. Found {number_of_groups}",
80 )))
81 }
82
83 let grouped_swap = grouped_swaps
84 .first()
85 .ok_or_else(|| EncodingError::FatalError("Swap grouping failed".to_string()))?;
86
87 if grouped_swap.split != 0f64 {
88 return Err(EncodingError::InvalidInput(
89 "Splits not supported for single swaps.".to_string(),
90 ))
91 }
92
93 let (mut unwrap, mut wrap) = (false, false);
94 if let Some(action) = &solution.native_action {
95 match *action {
96 NativeAction::Wrap => wrap = true,
97 NativeAction::Unwrap => unwrap = true,
98 }
99 }
100 let protocol = &grouped_swap.protocol_system;
101 let swap_encoder = self
102 .get_swap_encoder(protocol)
103 .ok_or_else(|| {
104 EncodingError::InvalidInput(format!(
105 "Swap encoder not found for protocol: {protocol}"
106 ))
107 })?;
108
109 let swap_receiver =
110 if !unwrap { solution.receiver.clone() } else { self.router_address.clone() };
111
112 let transfer = self
113 .transfer_optimization
114 .get_transfers(grouped_swap, &solution.given_token, wrap, false);
115 let encoding_context = EncodingContext {
116 receiver: swap_receiver,
117 exact_out: solution.exact_out,
118 router_address: Some(self.router_address.clone()),
119 group_token_in: grouped_swap.token_in.clone(),
120 group_token_out: grouped_swap.token_out.clone(),
121 transfer_type: transfer,
122 };
123
124 let mut grouped_protocol_data: Vec<u8> = vec![];
125 for swap in grouped_swap.swaps.iter() {
126 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
127 grouped_protocol_data.extend(protocol_data);
128 }
129
130 let swap_data = self.encode_swap_header(
131 Bytes::from_str(swap_encoder.executor_address())
132 .map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?,
133 grouped_protocol_data,
134 );
135 Ok(EncodedSolution {
136 function_signature: self.function_signature.clone(),
137 interacting_with: self.router_address.clone(),
138 swaps: swap_data,
139 permit: None,
140 n_tokens: 0,
141 })
142 }
143
144 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
145 self.swap_encoder_registry
146 .get_encoder(protocol_system)
147 }
148
149 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
150 Box::new(self.clone())
151 }
152}
153
154#[derive(Clone)]
166pub struct SequentialSwapStrategyEncoder {
167 swap_encoder_registry: SwapEncoderRegistry,
168 function_signature: String,
169 router_address: Bytes,
170 native_address: Bytes,
171 wrapped_address: Bytes,
172 sequential_swap_validator: SequentialSwapValidator,
173 transfer_optimization: TransferOptimization,
174}
175
176impl SequentialSwapStrategyEncoder {
177 pub fn new(
178 chain: Chain,
179 swap_encoder_registry: SwapEncoderRegistry,
180 user_transfer_type: UserTransferType,
181 router_address: Bytes,
182 ) -> Result<Self, EncodingError> {
183 let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
184 "sequentialSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
185 } else {
186 "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
187
188 }.to_string();
189 let native_token_address = chain.native_token().address;
190 let wrapped_token_address = chain.wrapped_native_token().address;
191 Ok(Self {
192 function_signature,
193 swap_encoder_registry,
194 router_address: router_address.clone(),
195 native_address: native_token_address.clone(),
196 wrapped_address: wrapped_token_address.clone(),
197 sequential_swap_validator: SequentialSwapValidator,
198 transfer_optimization: TransferOptimization::new(
199 native_token_address,
200 wrapped_token_address,
201 user_transfer_type,
202 router_address,
203 ),
204 })
205 }
206
207 fn encode_swap_header(&self, executor_address: Bytes, protocol_data: Vec<u8>) -> Vec<u8> {
210 let mut encoded = Vec::new();
211 encoded.extend(executor_address.to_vec());
212 encoded.extend(protocol_data);
213 encoded
214 }
215}
216
217impl StrategyEncoder for SequentialSwapStrategyEncoder {
218 fn encode_strategy(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
219 self.sequential_swap_validator
220 .validate_swap_path(
221 &solution.swaps,
222 &solution.given_token,
223 &solution.checked_token,
224 &solution.native_action,
225 &self.native_address,
226 &self.wrapped_address,
227 )?;
228
229 let grouped_swaps = group_swaps(&solution.swaps);
230
231 let (mut wrap, mut unwrap) = (false, false);
232 if let Some(action) = &solution.native_action {
233 match *action {
234 NativeAction::Wrap => wrap = true,
235 NativeAction::Unwrap => unwrap = true,
236 }
237 }
238
239 let mut swaps = vec![];
240 let mut next_in_between_swap_optimization_allowed = true;
241 for (i, grouped_swap) in grouped_swaps.iter().enumerate() {
242 let protocol = &grouped_swap.protocol_system;
243 let swap_encoder = self
244 .get_swap_encoder(protocol)
245 .ok_or_else(|| {
246 EncodingError::InvalidInput(format!(
247 "Swap encoder not found for protocol: {protocol}",
248 ))
249 })?;
250
251 let in_between_swap_optimization_allowed = next_in_between_swap_optimization_allowed;
252 let next_swap = grouped_swaps.get(i + 1);
253 let (swap_receiver, next_swap_optimization) = self
254 .transfer_optimization
255 .get_receiver(&solution.receiver, next_swap, unwrap)?;
256 next_in_between_swap_optimization_allowed = next_swap_optimization;
257
258 let transfer = self
259 .transfer_optimization
260 .get_transfers(
261 grouped_swap,
262 &solution.given_token,
263 wrap,
264 in_between_swap_optimization_allowed,
265 );
266 let encoding_context = EncodingContext {
267 receiver: swap_receiver,
268 exact_out: solution.exact_out,
269 router_address: Some(self.router_address.clone()),
270 group_token_in: grouped_swap.token_in.clone(),
271 group_token_out: grouped_swap.token_out.clone(),
272 transfer_type: transfer,
273 };
274
275 let mut grouped_protocol_data: Vec<u8> = vec![];
276 for swap in grouped_swap.swaps.iter() {
277 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
278 grouped_protocol_data.extend(protocol_data);
279 }
280
281 let swap_data = self.encode_swap_header(
282 Bytes::from_str(swap_encoder.executor_address()).map_err(|_| {
283 EncodingError::FatalError("Invalid executor address".to_string())
284 })?,
285 grouped_protocol_data,
286 );
287 swaps.push(swap_data);
288 }
289
290 let encoded_swaps = ple_encode(swaps);
291 Ok(EncodedSolution {
292 interacting_with: self.router_address.clone(),
293 function_signature: self.function_signature.clone(),
294 swaps: encoded_swaps,
295 permit: None,
296 n_tokens: 0,
297 })
298 }
299
300 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
301 self.swap_encoder_registry
302 .get_encoder(protocol_system)
303 }
304
305 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
306 Box::new(self.clone())
307 }
308}
309
310#[derive(Clone)]
322pub struct SplitSwapStrategyEncoder {
323 swap_encoder_registry: SwapEncoderRegistry,
324 function_signature: String,
325 native_address: Bytes,
326 wrapped_address: Bytes,
327 split_swap_validator: SplitSwapValidator,
328 router_address: Bytes,
329 transfer_optimization: TransferOptimization,
330}
331
332impl SplitSwapStrategyEncoder {
333 pub fn new(
334 chain: Chain,
335 swap_encoder_registry: SwapEncoderRegistry,
336 user_transfer_type: UserTransferType,
337 router_address: Bytes,
338 ) -> Result<Self, EncodingError> {
339 let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
340 "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
341 } else {
342 "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
343 }.to_string();
344 let native_token_address = chain.native_token().address;
345 let wrapped_token_address = chain.wrapped_native_token().address;
346 Ok(Self {
347 function_signature,
348 swap_encoder_registry,
349 native_address: native_token_address.clone(),
350 wrapped_address: wrapped_token_address.clone(),
351 split_swap_validator: SplitSwapValidator,
352 router_address: router_address.clone(),
353 transfer_optimization: TransferOptimization::new(
354 native_token_address,
355 wrapped_token_address,
356 user_transfer_type,
357 router_address,
358 ),
359 })
360 }
361
362 fn encode_swap_header(
365 &self,
366 token_in: U8,
367 token_out: U8,
368 split: U24,
369 executor_address: Bytes,
370 protocol_data: Vec<u8>,
371 ) -> Vec<u8> {
372 let mut encoded = Vec::new();
373 encoded.push(token_in.to_be_bytes_vec()[0]);
374 encoded.push(token_out.to_be_bytes_vec()[0]);
375 encoded.extend_from_slice(&split.to_be_bytes_vec());
376 encoded.extend(executor_address.to_vec());
377 encoded.extend(protocol_data);
378 encoded
379 }
380}
381
382impl StrategyEncoder for SplitSwapStrategyEncoder {
383 fn encode_strategy(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
384 self.split_swap_validator
385 .validate_split_percentages(&solution.swaps)?;
386 self.split_swap_validator
387 .validate_swap_path(
388 &solution.swaps,
389 &solution.given_token,
390 &solution.checked_token,
391 &solution.native_action,
392 &self.native_address,
393 &self.wrapped_address,
394 )?;
395
396 let solution_tokens: HashSet<&Bytes> = vec![&solution.given_token, &solution.checked_token]
399 .into_iter()
400 .collect();
401
402 let grouped_swaps = group_swaps(&solution.swaps);
403
404 let intermediary_tokens: HashSet<&Bytes> = grouped_swaps
405 .iter()
406 .flat_map(|grouped_swap| vec![&grouped_swap.token_in, &grouped_swap.token_out])
407 .collect();
408 let mut intermediary_tokens: Vec<&Bytes> = intermediary_tokens
409 .difference(&solution_tokens)
410 .cloned()
411 .collect();
412 intermediary_tokens.sort();
415
416 let (mut unwrap, mut wrap) = (false, false);
417 if let Some(action) = &solution.native_action {
418 match *action {
419 NativeAction::Wrap => wrap = true,
420 NativeAction::Unwrap => unwrap = true,
421 }
422 }
423
424 let mut tokens = Vec::with_capacity(2 + intermediary_tokens.len());
425 if wrap {
426 tokens.push(&self.wrapped_address);
427 } else {
428 tokens.push(&solution.given_token);
429 }
430 tokens.extend(intermediary_tokens);
431
432 if unwrap {
433 tokens.push(&self.wrapped_address);
434 } else {
435 tokens.push(&solution.checked_token);
436 }
437
438 let mut swaps = vec![];
439 for grouped_swap in grouped_swaps.iter() {
440 let protocol = &grouped_swap.protocol_system;
441 let swap_encoder = self
442 .get_swap_encoder(protocol)
443 .ok_or_else(|| {
444 EncodingError::InvalidInput(format!(
445 "Swap encoder not found for protocol: {protocol}",
446 ))
447 })?;
448
449 let swap_receiver = if !unwrap && grouped_swap.token_out == solution.checked_token {
450 solution.receiver.clone()
451 } else {
452 self.router_address.clone()
453 };
454 let transfer = self
455 .transfer_optimization
456 .get_transfers(grouped_swap, &solution.given_token, wrap, false);
457 let encoding_context = EncodingContext {
458 receiver: swap_receiver,
459 exact_out: solution.exact_out,
460 router_address: Some(self.router_address.clone()),
461 group_token_in: grouped_swap.token_in.clone(),
462 group_token_out: grouped_swap.token_out.clone(),
463 transfer_type: transfer,
464 };
465
466 let mut grouped_protocol_data: Vec<u8> = vec![];
467 for swap in grouped_swap.swaps.iter() {
468 let protocol_data = swap_encoder.encode_swap(swap, &encoding_context)?;
469 grouped_protocol_data.extend(protocol_data);
470 }
471
472 let swap_data = self.encode_swap_header(
473 get_token_position(&tokens, &grouped_swap.token_in)?,
474 get_token_position(&tokens, &grouped_swap.token_out)?,
475 percentage_to_uint24(grouped_swap.split),
476 Bytes::from_str(swap_encoder.executor_address()).map_err(|_| {
477 EncodingError::FatalError("Invalid executor address".to_string())
478 })?,
479 grouped_protocol_data,
480 );
481 swaps.push(swap_data);
482 }
483
484 let encoded_swaps = ple_encode(swaps);
485 let tokens_len = if solution.given_token == solution.checked_token {
486 tokens.len() - 1
487 } else {
488 tokens.len()
489 };
490 Ok(EncodedSolution {
491 interacting_with: self.router_address.clone(),
492 function_signature: self.function_signature.clone(),
493 swaps: encoded_swaps,
494 permit: None,
495 n_tokens: tokens_len,
496 })
497 }
498
499 fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
500 self.swap_encoder_registry
501 .get_encoder(protocol_system)
502 }
503
504 fn clone_box(&self) -> Box<dyn StrategyEncoder> {
505 Box::new(self.clone())
506 }
507}
508
509#[cfg(test)]
510mod tests {
511 use std::{collections::HashMap, str::FromStr};
512
513 use alloy::{hex::encode, primitives::hex};
514 use num_bigint::{BigInt, BigUint};
515 use tycho_common::{
516 models::{protocol::ProtocolComponent, Chain},
517 Bytes,
518 };
519
520 use super::*;
521 use crate::encoding::models::Swap;
522
523 fn eth_chain() -> Chain {
524 Chain::Ethereum
525 }
526
527 fn weth() -> Bytes {
528 Bytes::from(hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").to_vec())
529 }
530
531 fn get_swap_encoder_registry() -> SwapEncoderRegistry {
532 let eth_chain = eth_chain();
533 SwapEncoderRegistry::new(Some("config/test_executor_addresses.json".to_string()), eth_chain)
534 .unwrap()
535 }
536
537 fn router_address() -> Bytes {
538 Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap()
539 }
540
541 mod single {
542
543 use super::*;
544 #[test]
545 fn test_single_swap_strategy_encoder() {
546 let checked_amount = BigUint::from_str("2018817438608734439720").unwrap();
549 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
550 let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
551
552 let swap = Swap {
553 component: ProtocolComponent {
554 id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
555 protocol_system: "uniswap_v2".to_string(),
556 ..Default::default()
557 },
558 token_in: weth.clone(),
559 token_out: dai.clone(),
560 split: 0f64,
561 user_data: None,
562 protocol_state: None,
563 };
564 let swap_encoder_registry = get_swap_encoder_registry();
565 let encoder = SingleSwapStrategyEncoder::new(
566 eth_chain(),
567 swap_encoder_registry,
568 UserTransferType::TransferFromPermit2,
569 router_address(),
570 )
571 .unwrap();
572 let solution = Solution {
573 exact_out: false,
574 given_token: weth,
575 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
576 checked_token: dai,
577 checked_amount: checked_amount.clone(),
578 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
579 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
580 swaps: vec![swap],
581 ..Default::default()
582 };
583
584 let encoded_solution = encoder
585 .encode_strategy(&solution)
586 .unwrap();
587
588 let expected_swap = String::from(concat!(
589 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "00", ));
597 let hex_calldata = encode(&encoded_solution.swaps);
598
599 assert_eq!(hex_calldata, expected_swap);
600 assert_eq!(encoded_solution.function_signature, "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string());
601 assert_eq!(encoded_solution.interacting_with, router_address());
602 }
603
604 #[test]
605 fn test_single_swap_strategy_encoder_no_transfer_in() {
606 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
610 let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
611
612 let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap();
613
614 let swap = Swap {
615 component: ProtocolComponent {
616 id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
617 protocol_system: "uniswap_v2".to_string(),
618 ..Default::default()
619 },
620 token_in: weth.clone(),
621 token_out: dai.clone(),
622 split: 0f64,
623 user_data: None,
624 protocol_state: None,
625 };
626 let swap_encoder_registry = get_swap_encoder_registry();
627 let encoder = SingleSwapStrategyEncoder::new(
628 eth_chain(),
629 swap_encoder_registry,
630 UserTransferType::None,
631 router_address(),
632 )
633 .unwrap();
634 let solution = Solution {
635 exact_out: false,
636 given_token: weth,
637 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
638 checked_token: dai,
639 checked_amount,
640 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
641 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
642 swaps: vec![swap],
643 ..Default::default()
644 };
645
646 let encoded_solution = encoder
647 .encode_strategy(&solution)
648 .unwrap();
649
650 let expected_input = [
651 "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "a478c2975ab1ea89e8196811f51a7b7ade33eb11", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "00", "01", ]
659 .join("");
660
661 let hex_calldata = encode(&encoded_solution.swaps);
662
663 assert_eq!(hex_calldata, expected_input);
664 assert_eq!(
665 encoded_solution.function_signature,
666 "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
667 .to_string()
668 );
669 assert_eq!(encoded_solution.interacting_with, router_address());
670 }
671 }
672
673 mod sequential {
674 use super::*;
675
676 #[test]
677 fn test_sequential_swap_strategy_encoder_no_permit2() {
678 let weth = weth();
683 let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap();
684 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
685
686 let swap_weth_wbtc = Swap {
687 component: ProtocolComponent {
688 id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(),
689 protocol_system: "uniswap_v2".to_string(),
690 ..Default::default()
691 },
692 token_in: weth.clone(),
693 token_out: wbtc.clone(),
694 split: 0f64,
695 user_data: None,
696 protocol_state: None,
697 };
698 let swap_wbtc_usdc = Swap {
699 component: ProtocolComponent {
700 id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(),
701 protocol_system: "uniswap_v2".to_string(),
702 ..Default::default()
703 },
704 token_in: wbtc.clone(),
705 token_out: usdc.clone(),
706 split: 0f64,
707 user_data: None,
708 protocol_state: None,
709 };
710 let swap_encoder_registry = get_swap_encoder_registry();
711 let encoder = SequentialSwapStrategyEncoder::new(
712 eth_chain(),
713 swap_encoder_registry,
714 UserTransferType::TransferFrom,
715 router_address(),
716 )
717 .unwrap();
718 let solution = Solution {
719 exact_out: false,
720 given_token: weth,
721 given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
722 checked_token: usdc,
723 checked_amount: BigUint::from_str("26173932").unwrap(),
724 sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
725 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
726 swaps: vec![swap_weth_wbtc, swap_wbtc_usdc],
727 ..Default::default()
728 };
729
730 let encoded_solution = encoder
731 .encode_strategy(&solution)
732 .unwrap();
733
734 let hex_calldata = encode(&encoded_solution.swaps);
735
736 let expected = String::from(concat!(
737 "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "bb2b8038a1640196fbe3e38816f3e67cba72d940", "004375dff511095cc5a197a54140a24efef3a416", "00", "00", "0052", "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "2260fac5e5542a773aa44fbcfedf7c193bc2c599", "004375dff511095cc5a197a54140a24efef3a416", "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", "01", "02", ));
754
755 assert_eq!(hex_calldata, expected);
756 assert_eq!(
757 encoded_solution.function_signature,
758 "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
759 .to_string()
760 );
761 assert_eq!(encoded_solution.interacting_with, router_address());
762 }
763 }
764
765 mod split {
766 use super::*;
767
768 #[test]
769 fn test_split_input_cyclic_swap() {
770 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
779 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
780
781 let swap_usdc_weth_pool1 = Swap {
783 component: ProtocolComponent {
784 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
787 static_attributes: {
788 let mut attrs = HashMap::new();
789 attrs.insert(
790 "fee".to_string(),
791 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
792 );
793 attrs
794 },
795 ..Default::default()
796 },
797 token_in: usdc.clone(),
798 token_out: weth.clone(),
799 split: 0.6f64, user_data: None,
801 protocol_state: None,
802 };
803
804 let swap_usdc_weth_pool2 = Swap {
806 component: ProtocolComponent {
807 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
810 static_attributes: {
811 let mut attrs = HashMap::new();
812 attrs.insert(
813 "fee".to_string(),
814 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
815 );
816 attrs
817 },
818 ..Default::default()
819 },
820 token_in: usdc.clone(),
821 token_out: weth.clone(),
822 split: 0f64,
823 user_data: None, protocol_state: None,
825 };
826
827 let swap_weth_usdc_pool2 = Swap {
829 component: ProtocolComponent {
830 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
833 static_attributes: {
834 let mut attrs = HashMap::new();
835 attrs.insert(
836 "fee".to_string(),
837 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
838 );
839 attrs
840 },
841 ..Default::default()
842 },
843 token_in: weth.clone(),
844 token_out: usdc.clone(),
845 split: 0.0f64,
846 user_data: None,
847 protocol_state: None,
848 };
849
850 let swap_encoder_registry = get_swap_encoder_registry();
851 let encoder = SplitSwapStrategyEncoder::new(
852 eth_chain(),
853 swap_encoder_registry,
854 UserTransferType::TransferFromPermit2,
855 Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
856 )
857 .unwrap();
858
859 let solution = Solution {
860 exact_out: false,
861 given_token: usdc.clone(),
862 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
864 checked_amount: BigUint::from_str("99574171").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
868 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
869 swaps: vec![swap_usdc_weth_pool1, swap_usdc_weth_pool2, swap_weth_usdc_pool2],
870 ..Default::default()
871 };
872
873 let encoded_solution = encoder
874 .encode_strategy(&solution)
875 .unwrap();
876
877 let hex_calldata = hex::encode(&encoded_solution.swaps);
878
879 let expected_swaps = [
880 "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", ]
915 .join("");
916 assert_eq!(hex_calldata, expected_swaps);
917 assert_eq!(
918 encoded_solution.function_signature,
919 "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
920 .to_string()
921 );
922 assert_eq!(encoded_solution.interacting_with, router_address());
923 }
924
925 #[test]
926 fn test_split_output_cyclic_swap() {
927 let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
936 let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
937
938 let swap_usdc_weth_v2 = Swap {
939 component: ProtocolComponent {
940 id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), protocol_system: "uniswap_v2".to_string(),
942 static_attributes: {
943 let mut attrs = HashMap::new();
944 attrs.insert(
945 "fee".to_string(),
946 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
947 );
948 attrs
949 },
950 ..Default::default()
951 },
952 token_in: usdc.clone(),
953 token_out: weth.clone(),
954 split: 0.0f64,
955 user_data: None,
956 protocol_state: None,
957 };
958
959 let swap_weth_usdc_v3_pool1 = Swap {
960 component: ProtocolComponent {
961 id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), protocol_system: "uniswap_v3".to_string(),
964 static_attributes: {
965 let mut attrs = HashMap::new();
966 attrs.insert(
967 "fee".to_string(),
968 Bytes::from(BigInt::from(500).to_signed_bytes_be()),
969 );
970 attrs
971 },
972 ..Default::default()
973 },
974 token_in: weth.clone(),
975 token_out: usdc.clone(),
976 split: 0.6f64,
977 user_data: None,
978 protocol_state: None,
979 };
980
981 let swap_weth_usdc_v3_pool2 = Swap {
982 component: ProtocolComponent {
983 id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), protocol_system: "uniswap_v3".to_string(),
986 static_attributes: {
987 let mut attrs = HashMap::new();
988 attrs.insert(
989 "fee".to_string(),
990 Bytes::from(BigInt::from(3000).to_signed_bytes_be()),
991 );
992 attrs
993 },
994 ..Default::default()
995 },
996 token_in: weth.clone(),
997 token_out: usdc.clone(),
998 split: 0.0f64,
999 user_data: None,
1000 protocol_state: None,
1001 };
1002
1003 let swap_encoder_registry = get_swap_encoder_registry();
1004 let encoder = SplitSwapStrategyEncoder::new(
1005 eth_chain(),
1006 swap_encoder_registry,
1007 UserTransferType::TransferFrom,
1008 Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
1009 )
1010 .unwrap();
1011
1012 let solution = Solution {
1013 exact_out: false,
1014 given_token: usdc.clone(),
1015 given_amount: BigUint::from_str("100000000").unwrap(), checked_token: usdc.clone(),
1017 checked_amount: BigUint::from_str("99025908").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1021 receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
1022 swaps: vec![swap_usdc_weth_v2, swap_weth_usdc_v3_pool1, swap_weth_usdc_v3_pool2],
1023 ..Default::default()
1024 };
1025
1026 let encoded_solution = encoder
1027 .encode_strategy(&solution)
1028 .unwrap();
1029
1030 let hex_calldata = hex::encode(&encoded_solution.swaps);
1031
1032 let expected_swaps = [
1033 "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", ]
1068 .join("");
1069
1070 assert_eq!(hex_calldata, expected_swaps);
1071 assert_eq!(
1072 encoded_solution.function_signature,
1073 "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
1074 .to_string()
1075 );
1076 assert_eq!(encoded_solution.interacting_with, router_address());
1077 }
1078 }
1079}