kabu_types_swap/
swap_step.rs

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