gmsol_model/market/
swap.rs1use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedSub, One, Signed, Zero};
2
3use crate::{
4 action::swap::Swap,
5 num::{Unsigned, UnsignedAbs},
6 params::{FeeParams, PriceImpactParams},
7 price::{Price, Prices},
8 Balance, BaseMarket, Pool,
9};
10
11use super::BaseMarketMut;
12
13pub trait SwapMarket<const DECIMALS: u8>: BaseMarket<DECIMALS> {
15 fn swap_impact_params(&self) -> crate::Result<PriceImpactParams<Self::Num>>;
17
18 fn swap_fee_params(&self) -> crate::Result<FeeParams<Self::Num>>;
20}
21
22pub trait SwapMarketMut<const DECIMALS: u8>:
24 SwapMarket<DECIMALS> + BaseMarketMut<DECIMALS>
25{
26 fn swap_impact_pool_mut(&mut self) -> crate::Result<&mut Self::Pool>;
30}
31
32impl<M: SwapMarket<DECIMALS>, const DECIMALS: u8> SwapMarket<DECIMALS> for &mut M {
33 fn swap_impact_params(&self) -> crate::Result<PriceImpactParams<Self::Num>> {
34 (**self).swap_impact_params()
35 }
36
37 fn swap_fee_params(&self) -> crate::Result<FeeParams<Self::Num>> {
38 (**self).swap_fee_params()
39 }
40}
41
42impl<M: SwapMarketMut<DECIMALS>, const DECIMALS: u8> SwapMarketMut<DECIMALS> for &mut M {
43 fn swap_impact_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
44 (**self).swap_impact_pool_mut()
45 }
46}
47
48pub trait SwapMarketExt<const DECIMALS: u8>: SwapMarket<DECIMALS> {
50 fn swap_impact_amount_with_cap(
52 &self,
53 is_long_token: bool,
54 price: &Price<Self::Num>,
55 usd_impact: &Self::Signed,
56 ) -> crate::Result<(Self::Signed, Self::Num)> {
57 if price.has_zero() {
58 return Err(crate::Error::DividedByZero);
59 }
60 if usd_impact.is_positive() {
61 let max_price = price.pick_price(true).to_signed()?;
62
63 let mut amount = usd_impact
64 .checked_div(&max_price)
65 .ok_or(crate::Error::Computation("calculating swap impact amount"))?;
66
67 let max_amount = if is_long_token {
68 self.swap_impact_pool()?.long_amount()?
69 } else {
70 self.swap_impact_pool()?.short_amount()?
71 }
72 .to_signed()?;
73
74 let capped_diff_value = if amount > max_amount {
75 let capped_diff_value = amount
76 .checked_sub(&max_amount)
77 .map(|diff_amount| diff_amount.unsigned_abs())
78 .and_then(|diff_amount| diff_amount.checked_mul(price.pick_price(true)))
79 .ok_or(crate::Error::Computation("calculating capped diff value"))?;
80 amount = max_amount;
81 capped_diff_value
82 } else {
83 Zero::zero()
84 };
85 Ok((amount, capped_diff_value))
86 } else if usd_impact.is_negative() {
87 let price = price.pick_price(false).to_signed()?;
88 let one = Self::Signed::one();
89 let amount = usd_impact
91 .checked_sub(&price)
92 .and_then(|a| a.checked_add(&one)?.checked_div(&price))
93 .ok_or(crate::Error::Computation(
94 "calculating round up swap impact amount",
95 ))?;
96 Ok((amount, Zero::zero()))
97 } else {
98 Ok((Zero::zero(), Zero::zero()))
99 }
100 }
101}
102
103impl<M: SwapMarket<DECIMALS> + ?Sized, const DECIMALS: u8> SwapMarketExt<DECIMALS> for M {}
104
105pub trait SwapMarketMutExt<const DECIMALS: u8>: SwapMarketMut<DECIMALS> {
107 fn swap(
109 &mut self,
110 is_token_in_long: bool,
111 token_in_amount: Self::Num,
112 prices: Prices<Self::Num>,
113 ) -> crate::Result<Swap<&mut Self, DECIMALS>>
114 where
115 Self: Sized,
116 {
117 Swap::try_new(self, is_token_in_long, token_in_amount, prices)
118 }
119
120 fn apply_swap_impact_value_with_cap(
126 &mut self,
127 is_long_token: bool,
128 price: &Price<Self::Num>,
129 usd_impact: &Self::Signed,
130 ) -> crate::Result<Self::Num> {
131 let (amount, _) = self.swap_impact_amount_with_cap(is_long_token, price, usd_impact)?;
132 let delta = amount
133 .checked_neg()
134 .ok_or(crate::Error::Computation("negating swap impact delta"))?;
135 if is_long_token {
136 self.swap_impact_pool_mut()?
137 .apply_delta_to_long_amount(&delta)?;
138 } else {
139 self.swap_impact_pool_mut()?
140 .apply_delta_to_short_amount(&delta)?;
141 }
142 Ok(delta.unsigned_abs())
143 }
144}
145
146impl<M: SwapMarketMut<DECIMALS>, const DECIMALS: u8> SwapMarketMutExt<DECIMALS> for M {}