1use std::fmt::{Display, Formatter};
2use std::sync::Arc;
3
4use alloy_primitives::{I256, U256};
5use eyre::{eyre, Result};
6use tracing::error;
7
8use crate::{PoolWrapper, SwapAmountType, SwapLine, Token};
9use alloy_primitives::Address;
10use kabu_evm_db::KabuDBError;
11use revm::DatabaseRef;
12
13#[derive(Clone, Debug)]
14pub struct SwapStep {
15 swap_line_vec: Vec<SwapLine>,
16 swap_from: Option<Address>,
17 swap_to: Address,
18}
19
20impl Display for SwapStep {
21 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
22 let paths = self.swap_line_vec.iter().map(|path| format!("{path}")).collect::<Vec<String>>().join(" / ");
23 write!(f, "{paths}")
24 }
25}
26
27impl SwapStep {
28 pub fn new(swap_to: Address) -> Self {
29 Self { swap_line_vec: Vec::new(), swap_to, swap_from: None }
30 }
31
32 pub fn get_mut_swap_line_by_index(&mut self, idx: usize) -> &mut SwapLine {
33 &mut self.swap_line_vec[idx]
34 }
35
36 pub fn swap_line_vec(&self) -> &Vec<SwapLine> {
37 &self.swap_line_vec
38 }
39
40 pub fn len(&self) -> usize {
41 self.swap_line_vec.len()
42 }
43
44 pub fn is_empty(&self) -> bool {
45 self.swap_line_vec.is_empty()
46 }
47
48 fn first_swap_line(&self) -> Option<&SwapLine> {
49 self.swap_line_vec.first()
50 }
51
52 pub fn first_token(&self) -> Option<&Arc<Token>> {
53 match self.first_swap_line() {
54 Some(s) => s.get_first_token(),
55 None => None,
56 }
57 }
58
59 pub fn last_token(&self) -> Option<&Arc<Token>> {
60 match self.first_swap_line() {
61 Some(s) => s.get_last_token(),
62 None => None,
63 }
64 }
65
66 pub fn add(&mut self, swap_path: SwapLine) -> &mut Self {
67 if self.is_empty()
68 || ((self.first_token().unwrap() == swap_path.get_first_token().unwrap())
69 && (self.last_token().unwrap() == swap_path.get_last_token().unwrap()))
70 {
71 self.swap_line_vec.push(swap_path);
72 } else {
73 error!(
74 "cannot add SwapPath {} != {} {} != {}",
75 self.first_token().unwrap().get_address(),
76 swap_path.get_first_token().unwrap().get_address(),
77 self.last_token().unwrap().get_address(),
78 swap_path.get_last_token().unwrap().get_address()
79 )
80 }
81 self
82 }
83
84 pub fn can_flash_swap(&self) -> bool {
85 for swap_line in self.swap_line_vec.iter() {
86 for pool in swap_line.pools().iter() {
87 if !pool.can_flash_swap() {
88 return false;
89 }
90 }
91 }
92 true
93 }
94
95 pub fn can_calculate_in_amount(&self) -> bool {
96 for swap_line in self.swap_line_vec.iter() {
97 for pool in swap_line.pools().iter() {
98 if !pool.can_calculate_in_amount() {
99 return false;
100 }
101 }
102 }
103 true
104 }
105
106 pub fn get_pools(&self) -> Vec<PoolWrapper> {
107 self.swap_line_vec.iter().flat_map(|sp| sp.pools().clone()).collect()
108 }
109
110 fn common_pools(swap_path_0: &SwapLine, swap_path_1: &SwapLine) -> usize {
111 let mut ret = 0;
112 for pool in swap_path_0.pools().iter() {
113 if swap_path_1.pools().contains(pool) {
114 ret += 1;
115 }
116 }
117 ret
118 }
119
120 pub fn merge_swap_paths(swap_path_0: SwapLine, swap_path_1: SwapLine, multicaller: Address) -> Result<(SwapStep, SwapStep)> {
121 let mut split_index_start = 0;
122 let mut split_index_end = 0;
123
124 if swap_path_0.get_first_token().unwrap() != swap_path_1.get_first_token().unwrap()
125 || swap_path_0.get_last_token().unwrap() != swap_path_1.get_last_token().unwrap()
126 {
127 return Err(eyre!("CANNOT_MERGE_DIFFERENT_TOKENS"));
128 }
129
130 for i in 0..swap_path_0.pools().len() {
131 if i >= swap_path_1.pools().len() {
132 break;
133 }
134 let pool_0 = &swap_path_0.pools()[i];
135 let pool_1 = &swap_path_1.pools()[i];
136
137 let token_0 = &swap_path_0.tokens()[i + 1];
138 let token_1 = &swap_path_1.tokens()[i + 1];
139
140 if pool_0 == pool_1 && token_0 == token_1 {
141 split_index_start += 1;
142 } else {
143 break;
144 }
145 }
146
147 for i in 0..swap_path_0.pools().len() {
148 if i >= swap_path_1.pools().len() {
149 break;
150 }
151 let pool_0 = &swap_path_0.pools()[swap_path_0.pools().len() - 1 - i];
152 let pool_1 = &swap_path_1.pools()[swap_path_1.pools().len() - 1 - i];
153
154 let token_0 = &swap_path_0.tokens()[swap_path_0.tokens().len() - 2 - i];
155 let token_1 = &swap_path_1.tokens()[swap_path_1.tokens().len() - 2 - i];
156
157 if pool_0 == pool_1 && token_0 == token_1 {
158 split_index_end += 1;
159 } else {
160 break;
161 }
162 }
163
164 if split_index_start > 0 && split_index_end > 0 {
165 return Err(eyre!("CANNOT_MERGE_BOTH_SIDES"));
166 }
167
168 let common_pools_count = Self::common_pools(&swap_path_0, &swap_path_1);
169 if (split_index_start > 0 && split_index_start != common_pools_count)
170 || (split_index_end > 0 && split_index_end != common_pools_count)
171 {
172 return Err(eyre!("MORE_COMMON_POOLS"));
173 }
174
175 if split_index_start > 0 {
176 let (mut split_0_0, mut split_0_1) = swap_path_0.split(split_index_start)?;
177 let (split_1_0, mut split_1_1) = swap_path_1.split(split_index_start)?;
178
179 let mut swap_step_0 = SwapStep::new(multicaller);
180 let mut swap_step_1 = SwapStep::new(multicaller);
181
182 if let SwapAmountType::Set(a0) = swap_path_0.amount_in {
183 if let SwapAmountType::Set(a1) = swap_path_1.amount_in {
184 split_0_0.amount_in = SwapAmountType::Set(a0.max(a1) >> 1);
185 }
186 }
187
188 if let SwapAmountType::Set(a0) = swap_path_0.amount_out {
189 if let SwapAmountType::Set(a1) = swap_path_1.amount_out {
190 split_0_1.amount_out = SwapAmountType::Set((a0.max(a1) * a0 / (a0 + a1)) >> 1);
191 split_1_1.amount_out = SwapAmountType::Set((a0.max(a1) * a1 / (a0 + a1)) >> 1);
192 }
193 }
194
195 swap_step_0.add(split_0_0);
196
197 swap_step_1.add(split_0_1);
198 swap_step_1.add(split_1_1);
199
200 return Ok((swap_step_0, swap_step_1));
201 }
202
203 if split_index_end > 0 {
204 let (mut split_0_0, mut split_0_1) = swap_path_0.split(swap_path_0.pools().len() - split_index_end)?;
205 let (mut split_1_0, split_1_1) = swap_path_1.split(swap_path_1.pools().len() - split_index_end)?;
206
207 let mut swap_step_0 = SwapStep::new(multicaller);
208 let mut swap_step_1 = SwapStep::new(multicaller);
209
210 if let SwapAmountType::Set(a0) = swap_path_0.amount_out {
211 if let SwapAmountType::Set(a1) = swap_path_1.amount_out {
212 split_0_1.amount_out = SwapAmountType::Set(a0.max(a1) >> 1);
213 }
214 }
215
216 if let SwapAmountType::Set(a0) = swap_path_0.amount_in {
226 if let SwapAmountType::Set(a1) = swap_path_1.amount_in {
227 split_0_0.amount_in = SwapAmountType::Set((a0.max(a1) * a0 / (a0 + a1)) >> 1);
228 split_1_0.amount_in = SwapAmountType::Set((a0.max(a1) * a1 / (a0 + a1)) >> 1);
229 }
230 }
231
232 swap_step_0.add(split_0_0);
243 swap_step_0.add(split_1_0);
244
245 swap_step_1.add(split_0_1);
246
247 return Ok((swap_step_0, swap_step_1));
248 }
249
250 Err(eyre!("CANNOT_MERGE"))
251 }
252
253 pub fn get_first_token_address(&self) -> Option<Address> {
254 let mut ret: Option<Address> = None;
255 for sp in self.swap_line_vec.iter() {
256 match sp.get_first_token() {
257 Some(token) => match ret {
258 Some(a) => {
259 if a != token.get_address() {
260 return None;
261 }
262 }
263 None => {
264 ret = Some(token.get_address());
265 }
266 },
267 _ => {
268 return None;
269 }
270 }
271 }
272 ret
273 }
274
275 pub fn get_first_pool(&self) -> Option<&PoolWrapper> {
276 if self.swap_line_vec.len() == 1 {
277 self.swap_line_vec.first().and_then(|x| x.path.pools.first())
278 } else {
279 None
280 }
281 }
282
283 pub fn get_last_pool(&self) -> Option<&PoolWrapper> {
284 if self.swap_line_vec.len() == 1 {
285 self.swap_line_vec.first().and_then(|x| x.path.pools.last())
286 } else {
287 None
288 }
289 }
290
291 pub fn get_first_token(&self) -> Option<&Arc<Token>> {
292 let mut ret: Option<&Arc<Token>> = None;
293 for sp in self.swap_line_vec.iter() {
294 match sp.get_first_token() {
295 Some(token) => match &ret {
296 Some(a) => {
297 if a.get_address() != token.get_address() {
298 return None;
299 }
300 }
301 None => {
302 ret = Some(token);
303 }
304 },
305 _ => {
306 return None;
307 }
308 }
309 }
310 ret
311 }
312
313 pub fn get_in_amount(&self) -> Result<U256> {
314 let mut in_amount = U256::ZERO;
315 for swap_path in self.swap_line_vec.iter() {
316 match swap_path.amount_in {
317 SwapAmountType::Set(amount) => in_amount += amount,
318 _ => return Err(eyre!("IN_AMOUNT_NOT_SET")),
319 }
320 }
321 Ok(in_amount)
322 }
323
324 pub fn get_out_amount(&self) -> Result<U256> {
325 let mut out_amount = U256::ZERO;
326 for swap_path in self.swap_line_vec.iter() {
327 match swap_path.amount_out {
328 SwapAmountType::Set(amount) => out_amount += amount,
329 _ => return Err(eyre!("IN_AMOUNT_NOT_SET")),
330 }
331 }
332 Ok(out_amount)
333 }
334
335 pub fn calculate_with_in_amount<DB: DatabaseRef<Error = KabuDBError>>(
336 &mut self,
337 db: &DB,
338 in_ammount: Option<U256>,
339 ) -> Result<(U256, u64)> {
340 let mut out_amount = U256::ZERO;
341 let mut gas_used = 0;
342
343 for swap_path in self.swap_line_vec.iter_mut() {
344 let cur_in_amount = match in_ammount {
345 Some(amount) => amount,
346 None => match swap_path.amount_in {
347 SwapAmountType::Set(amount) => amount,
348 _ => {
349 return Err(eyre!("IN_AMOUNT_NOT_SET"));
350 }
351 },
352 };
353 swap_path.amount_in = SwapAmountType::Set(cur_in_amount);
354
355 match swap_path.calculate_with_in_amount(db, cur_in_amount) {
356 Ok((amount, gas, calculation_results)) => {
357 out_amount += amount;
358 swap_path.amount_out = SwapAmountType::Set(amount);
359 gas_used += gas;
360 }
361 _ => {
362 return Err(eyre!("ERROR_CALCULATING_OUT_AMOUNT"));
363 }
364 }
365 }
366 Ok((out_amount, gas_used))
367 }
368
369 pub fn calculate_with_out_amount<DB: DatabaseRef<Error = KabuDBError>>(
370 &mut self,
371 db: &DB,
372 out_amount: Option<U256>,
373 ) -> Result<(U256, u64)> {
374 let mut in_amount = U256::ZERO;
375 let mut gas_used = 0;
376
377 for swap_path in self.swap_line_vec.iter_mut() {
378 let cur_out_amount = match out_amount {
379 Some(amount) => amount,
380 None => match swap_path.amount_out {
381 SwapAmountType::Set(amount) => amount,
382 _ => {
383 return Err(eyre!("IN_AMOUNT_NOT_SET"));
384 }
385 },
386 };
387
388 swap_path.amount_out = SwapAmountType::Set(cur_out_amount);
389
390 match swap_path.calculate_with_out_amount(db, cur_out_amount) {
391 Ok((amount, gas, calculation_results)) => {
392 in_amount += amount;
393 gas_used += gas;
394 swap_path.amount_in = SwapAmountType::Set(amount);
395 }
396 _ => {
397 return Err(eyre!("ERROR_CALCULATING_OUT_AMOUNT"));
398 }
399 }
400 }
401 Ok((in_amount, gas_used))
402 }
403
404 pub fn arb_result(swap_step_0: &SwapStep, swap_step_1: &SwapStep) -> I256 {
425 let in_amount: I256 = I256::try_from(swap_step_0.get_in_amount().unwrap_or(U256::MAX)).unwrap_or(I256::MAX);
426 let out_amount: I256 = I256::try_from(swap_step_1.get_out_amount().unwrap_or(U256::ZERO)).unwrap_or(I256::ZERO);
427 if in_amount.is_negative() {
428 I256::MIN
429 } else {
430 out_amount - in_amount
431 }
432 }
433
434 pub fn arb_profit(swap_step_0: &SwapStep, swap_step_1: &SwapStep) -> U256 {
435 let in_amount: U256 = swap_step_0.get_in_amount().unwrap_or(U256::MAX);
436 let out_amount: U256 = swap_step_1.get_out_amount().unwrap_or(U256::ZERO);
437 if in_amount >= out_amount {
438 U256::ZERO
439 } else {
440 out_amount - in_amount
441 }
442 }
443
444 pub fn arb_profit_eth(swap_step_0: &SwapStep, swap_step_1: &SwapStep) -> U256 {
445 match swap_step_0.get_first_token() {
446 Some(t) => {
447 let profit = Self::arb_profit(swap_step_0, swap_step_1);
448 t.calc_eth_value(profit).unwrap_or_default()
449 }
450 _ => U256::ZERO,
451 }
452 }
453
454 pub fn optimize_swap_steps<DB: DatabaseRef<Error = KabuDBError>>(
455 db: &DB,
456 swap_step_0: &SwapStep,
457 swap_step_1: &SwapStep,
458 middle_amount: Option<U256>,
459 ) -> Result<(SwapStep, SwapStep)> {
460 if swap_step_0.can_calculate_in_amount() {
461 SwapStep::optimize_with_middle_amount(db, swap_step_0, swap_step_1, middle_amount)
462 } else {
463 SwapStep::optimize_with_in_amount(db, swap_step_0, swap_step_1, middle_amount)
464 }
465 }
466
467 pub fn optimize_with_middle_amount<DB: DatabaseRef<Error = KabuDBError>>(
468 db: &DB,
469 swap_step_0: &SwapStep,
470 swap_step_1: &SwapStep,
471 middle_amount: Option<U256>,
472 ) -> Result<(SwapStep, SwapStep)> {
473 let mut step_0 = swap_step_0.clone();
474 let mut step_1 = swap_step_1.clone();
475 let mut best_profit: Option<I256> = None;
476
477 let (middle_amount, _) = match middle_amount {
478 Some(amount) => step_0.calculate_with_in_amount(db, Some(amount))?,
479 _ => step_0.calculate_with_in_amount(db, None)?,
480 };
481
482 let step_0_out_amount = step_0.get_out_amount()?;
483 let step_1_out_amount = step_1.get_out_amount()?;
484
485 for swap_path_1 in step_1.swap_line_vec.iter_mut() {
486 let in_amount = step_0_out_amount * swap_path_1.amount_out.unwrap() / step_1_out_amount;
487 swap_path_1.amount_in = SwapAmountType::Set(in_amount);
488 }
489
490 step_1.calculate_with_in_amount(db, None);
491
492 let cur_profit = Self::arb_result(&step_0, &step_1);
493 if cur_profit.is_positive() {
494 best_profit = Some(cur_profit);
495 }
496
497 let denominator = U256::from(10000);
505 let step_multiplier = U256::from(500);
506
507 let mut step_0_calc = step_0.clone();
508 let mut step_1_calc = step_1.clone();
509
510 let mut counter = 0;
511
512 let step = middle_amount * step_multiplier / denominator;
513
514 loop {
515 counter += 1;
516 if counter > 30 {
517 return if Self::arb_result(&step_0, &step_1).is_positive() { Ok((step_0, step_1)) } else { Err(eyre!("TOO_MANY_STEPS")) };
518 }
519
520 let step0in = step_0.get_in_amount()?;
521 let step0out = step_0.get_out_amount()?;
522 let step1in = step_1.get_in_amount()?;
523 let step1out = step_1.get_out_amount()?;
524 let profit = Self::arb_result(&step_0, &step_1);
525
526 for (i, swap_path_0_calc) in step_0_calc.swap_line_vec.iter_mut().enumerate() {
529 let amount_out = step_0.swap_line_vec[i].amount_out.unwrap();
530
531 if amount_out <= step {
532 return Ok((step_0, step_1));
533 }
534
535 let new_out_amount = amount_out.checked_add(step);
536 match new_out_amount {
537 Some(new_out_amount) => {
538 if swap_path_0_calc.amount_out.unwrap() != new_out_amount {
539 let new_amount_out = amount_out + step;
540 let (in_amount, gas, calculation_results) =
541 swap_path_0_calc.calculate_with_out_amount(db, new_amount_out).unwrap_or((U256::MAX, 0, vec![]));
542 swap_path_0_calc.amount_in = SwapAmountType::Set(in_amount);
543 swap_path_0_calc.amount_out = SwapAmountType::Set(new_amount_out);
544 swap_path_0_calc.gas_used = Some(gas);
545 swap_path_0_calc.calculation_results = calculation_results;
546 }
547 }
548 None => {
549 swap_path_0_calc.amount_in = SwapAmountType::Set(U256::MAX);
550 }
551 }
552 }
553
554 for (i, swap_path_1_calc) in step_1_calc.swap_line_vec.iter_mut().enumerate() {
555 let amount_in = step_1.swap_line_vec[i].amount_in.unwrap();
556
557 if amount_in <= step || amount_in == U256::MAX {
558 return Ok((step_0, step_1));
559 }
560
561 let new_amount_in = amount_in.checked_add(step);
562
563 match new_amount_in {
564 Some(new_amount_in) => {
565 if swap_path_1_calc.amount_in.unwrap() != new_amount_in {
566 let (out_amount, gas, calculation_results) =
567 swap_path_1_calc.calculate_with_in_amount(db, new_amount_in).unwrap_or((U256::ZERO, 0, vec![]));
568 swap_path_1_calc.amount_out = SwapAmountType::Set(out_amount);
569 swap_path_1_calc.amount_in = SwapAmountType::Set(new_amount_in);
570 swap_path_1_calc.gas_used = Some(gas);
571 }
572 }
573 None => {
574 swap_path_1_calc.amount_out = SwapAmountType::Set(U256::ZERO);
575 }
576 }
577 }
578
579 let mut best_merged_step_0: Option<SwapStep> = None;
580
581 for i in 0..step_0.swap_line_vec.len() {
582 let mut merged_step_0 = SwapStep::new(step_0.swap_to);
583 for ci in 0..step_0.swap_line_vec.len() {
584 merged_step_0.add(if ci == i { step_0_calc.swap_line_vec[ci].clone() } else { step_0.swap_line_vec[ci].clone() });
585 }
586 if best_merged_step_0.is_none() || best_merged_step_0.clone().unwrap().get_in_amount()? > merged_step_0.get_in_amount()? {
587 best_merged_step_0 = Some(merged_step_0);
588 }
589 }
590
591 let mut best_merged_step_1: Option<SwapStep> = None;
592
593 for i in 0..step_1.swap_line_vec.len() {
594 let mut merged_step_1 = SwapStep::new(step_1.swap_to);
595 for ci in 0..step_1.swap_line_vec.len() {
596 merged_step_1.add(if ci == i { step_1_calc.swap_line_vec[ci].clone() } else { step_1.swap_line_vec[ci].clone() });
597 }
598 if best_merged_step_1.is_none() || best_merged_step_1.clone().unwrap().get_out_amount()? < merged_step_1.get_out_amount()? {
599 best_merged_step_1 = Some(merged_step_1);
600 }
601 }
602
603 if best_merged_step_0.is_none() || best_merged_step_1.is_none() {
606 return if Self::arb_result(&step_0, &step_1).is_positive() {
608 Ok((step_0, step_1))
609 } else {
610 Err(eyre!("CANNOT_OPTIMIZE_SWAP_STEP"))
611 };
612 }
613
614 let best_merged_step_0 = best_merged_step_0.unwrap();
615 let best_merged_step_1 = best_merged_step_1.unwrap();
616
617 let cur_profit = Self::arb_result(&best_merged_step_0, &best_merged_step_1);
618
619 if best_profit.is_none() || best_profit.unwrap() < cur_profit {
620 step_0 = best_merged_step_0;
621 step_1 = best_merged_step_1;
622 best_profit = Some(cur_profit);
623 } else {
624 return if Self::arb_result(&step_0, &step_1).is_positive() {
626 Ok((step_0, step_1))
627 } else {
628 Err(eyre!("CANNOT_OPTIMIZE_SWAP_STEP"))
629 };
630 }
631 }
632 }
633
634 pub fn optimize_with_in_amount<DB: DatabaseRef<Error = KabuDBError>>(
635 db: &DB,
636 swap_step_0: &SwapStep,
637 swap_step_1: &SwapStep,
638 in_amount: Option<U256>,
639 ) -> Result<(SwapStep, SwapStep)> {
640 let mut step_0 = swap_step_0.clone();
641 let mut step_1 = swap_step_1.clone();
642 let mut best_profit: Option<I256> = None;
643
644 match in_amount {
645 Some(amount) => step_0.calculate_with_in_amount(db, Some(amount))?,
646 _ => step_0.calculate_with_in_amount(db, None)?,
647 };
648 let in_amount = step_0.get_in_amount()?;
649
650 let step_0_out_amount = step_0.get_out_amount()?;
651 let step_1_out_amount = step_1.get_out_amount()?;
652
653 for swap_path_1 in step_1.swap_line_vec.iter_mut() {
654 let in_amount = step_0_out_amount * swap_path_1.amount_out.unwrap() / step_1_out_amount;
655 swap_path_1.amount_in = SwapAmountType::Set(in_amount);
656 }
657 let _ = step_1.calculate_with_in_amount(db, None)?;
658
659 let cur_profit = Self::arb_result(&step_0, &step_1);
663 if cur_profit.is_positive() {
664 best_profit = Some(cur_profit);
665 }
666
667 let step_0_in_amount = step_0.get_in_amount().unwrap_or(U256::MAX);
673 let step_1_out_amount = step_1.get_out_amount().unwrap_or(U256::ZERO);
674
675 let denominator = U256::from(10000);
676 let step_multiplier = U256::from(500);
677
678 let mut step_0_calc = step_0.clone();
679 let mut step_1_calc = step_1.clone();
680
681 let mut counter = 0;
682
683 let step = in_amount * step_multiplier / denominator;
684
685 loop {
686 counter += 1;
687 if counter > 30 {
688 return if Self::arb_result(&step_0, &step_1).is_positive() { Ok((step_0, step_1)) } else { Err(eyre!("TOO_MANY_STEPS")) };
689 }
690
691 let step0in = step_0.get_in_amount()?;
692 let step0out = step_0.get_out_amount()?;
693 let step1in = step_1.get_in_amount()?;
694 let step1out = step_1.get_out_amount()?;
695 let profit = Self::arb_result(&step_0, &step_1);
696
697 for (i, swap_path_0_calc) in step_0_calc.swap_line_vec.iter_mut().enumerate() {
700 if step_0.swap_line_vec[i].amount_in.unwrap() > step
701 && swap_path_0_calc.amount_in.unwrap() != step_0.swap_line_vec[i].amount_in.unwrap() - step
702 {
703 let new_amount_in = step_0.swap_line_vec[i].amount_in.unwrap() + step;
704 let (amount_out, gas, calculation_results) =
705 swap_path_0_calc.calculate_with_in_amount(db, new_amount_in).unwrap_or((U256::ZERO, 0, vec![]));
706 swap_path_0_calc.amount_in = SwapAmountType::Set(new_amount_in);
707 swap_path_0_calc.amount_out = SwapAmountType::Set(amount_out);
708 swap_path_0_calc.gas_used = Some(gas);
709 swap_path_0_calc.calculation_results = calculation_results;
710 }
711 }
712
713 let mut best_merged_step_0: Option<SwapStep> = None;
714
715 for i in 0..step_0.swap_line_vec.len() {
716 let mut merged_step_0 = SwapStep::new(step_0.swap_to);
717 for ci in 0..step_0.swap_line_vec.len() {
718 merged_step_0.add(if ci == i { step_0_calc.swap_line_vec[ci].clone() } else { step_0.swap_line_vec[ci].clone() });
719 }
720 if step_0.get_in_amount()? < step || merged_step_0.get_in_amount()? != step_0.get_in_amount()? + step {
721 continue;
723 }
724
725 if best_merged_step_0.is_none() || best_merged_step_0.clone().unwrap().get_out_amount()? < merged_step_0.get_out_amount()? {
726 best_merged_step_0 = Some(merged_step_0);
727 }
728 }
729
730 if best_merged_step_0.is_none() {
731 break;
733 };
734
735 let middle_amount_step = best_merged_step_0.clone().unwrap().get_out_amount()? - step_1.get_in_amount()?;
736
737 for (i, swap_path_1_calc) in step_1_calc.swap_line_vec.iter_mut().enumerate() {
738 if step_1.swap_line_vec[i].amount_in.unwrap() > middle_amount_step
739 && swap_path_1_calc.amount_in.unwrap() != step_1.swap_line_vec[i].amount_in.unwrap() - middle_amount_step
740 {
741 let new_amount_in = step_1.swap_line_vec[i].amount_in.unwrap() + middle_amount_step;
742 let (out_amount, gas, calculation_results) =
743 swap_path_1_calc.calculate_with_in_amount(db, new_amount_in).unwrap_or_default();
744 swap_path_1_calc.amount_out = SwapAmountType::Set(out_amount);
745 swap_path_1_calc.amount_in = SwapAmountType::Set(new_amount_in);
746 swap_path_1_calc.gas_used = Some(gas);
747 swap_path_1_calc.calculation_results = calculation_results;
748 }
749 }
750
751 let mut best_merged_step_1: Option<SwapStep> = None;
752
753 for i in 0..step_1.swap_line_vec.len() {
754 let mut merged_step_1 = SwapStep::new(step_1.swap_to);
755 for ci in 0..step_1.swap_line_vec.len() {
756 merged_step_1.add(if ci == i { step_1_calc.swap_line_vec[ci].clone() } else { step_1.swap_line_vec[ci].clone() });
757 }
758 if merged_step_1.get_in_amount()? != best_merged_step_0.clone().unwrap().get_out_amount()? {
759 continue;
760 }
761
762 if best_merged_step_1.is_none() || best_merged_step_1.clone().unwrap().get_out_amount()? < merged_step_1.get_out_amount()? {
763 best_merged_step_1 = Some(merged_step_1);
764 }
765 }
766
767 if best_merged_step_0.is_none() || best_merged_step_1.is_none() {
770 return if Self::arb_result(&step_0, &step_1).is_positive() {
773 Ok((step_0, step_1))
774 } else {
775 Err(eyre!("CANNOT_OPTIMIZE_SWAP_STEP"))
777 };
778 }
779 let best_merged_step_0 = best_merged_step_0.unwrap();
780 let best_merged_step_1 = best_merged_step_1.unwrap();
781
782 let cur_profit = Self::arb_result(&best_merged_step_0, &best_merged_step_1);
783
784 if best_profit.is_none() || best_profit.unwrap() < cur_profit {
785 step_0 = best_merged_step_0;
786 step_1 = best_merged_step_1;
787 best_profit = Some(cur_profit);
788 } else {
789 return if Self::arb_result(&step_0, &step_1).is_positive() {
792 Ok((step_0, step_1))
793 } else {
794 Err(eyre!("CANNOT_OPTIMIZE_SWAP_STEP"))
795 };
796 }
797 }
798
799 if Self::arb_result(&step_0, &step_1).is_positive() {
800 Ok((step_0, step_1))
801 } else {
802 Err(eyre!("OPTIMIZATION_FAILED"))
803 }
804 }
805}