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 pool::delta::{PoolDelta, PriceImpact},
8 price::{Price, Prices},
9 Balance, BalanceExt, BaseMarket, Pool,
10};
11
12use super::BaseMarketMut;
13
14pub trait SwapMarket<const DECIMALS: u8>: BaseMarket<DECIMALS> {
16 fn swap_impact_params(&self) -> crate::Result<PriceImpactParams<Self::Num>>;
18
19 fn swap_fee_params(&self) -> crate::Result<FeeParams<Self::Num>>;
21}
22
23pub trait SwapMarketMut<const DECIMALS: u8>:
25 SwapMarket<DECIMALS> + BaseMarketMut<DECIMALS>
26{
27 fn swap_impact_pool_mut(&mut self) -> crate::Result<&mut Self::Pool>;
31}
32
33impl<M: SwapMarket<DECIMALS>, const DECIMALS: u8> SwapMarket<DECIMALS> for &mut M {
34 fn swap_impact_params(&self) -> crate::Result<PriceImpactParams<Self::Num>> {
35 (**self).swap_impact_params()
36 }
37
38 fn swap_fee_params(&self) -> crate::Result<FeeParams<Self::Num>> {
39 (**self).swap_fee_params()
40 }
41}
42
43impl<M: SwapMarketMut<DECIMALS>, const DECIMALS: u8> SwapMarketMut<DECIMALS> for &mut M {
44 fn swap_impact_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
45 (**self).swap_impact_pool_mut()
46 }
47}
48
49pub trait SwapMarketExt<const DECIMALS: u8>: SwapMarket<DECIMALS> {
51 fn swap_impact_value(
53 &self,
54 liquidity_pool_delta: &PoolDelta<Self::Num>,
55 include_virtual_inventory_impact: bool,
56 ) -> crate::Result<PriceImpact<Self::Signed>> {
57 let params = self.swap_impact_params()?;
58
59 let impact = liquidity_pool_delta.price_impact(¶ms)?;
60
61 if !impact.value.is_negative() || !include_virtual_inventory_impact {
62 return Ok(impact);
63 }
64
65 let Some(virtual_inventory) = self.virtual_inventory_for_swaps_pool()? else {
66 return Ok(impact);
67 };
68
69 let delta = liquidity_pool_delta.delta();
70 let long_token_price = liquidity_pool_delta.long_token_price();
71 let short_token_price = liquidity_pool_delta.short_token_price();
72
73 let virtual_inventory_impact = virtual_inventory
74 .pool_delta_with_values(
75 delta.long_value().clone(),
76 delta.short_value().clone(),
77 long_token_price,
78 short_token_price,
79 )?
80 .price_impact(¶ms)?;
81
82 if virtual_inventory_impact.value < impact.value {
83 Ok(virtual_inventory_impact)
84 } else {
85 Ok(impact)
86 }
87 }
88
89 fn swap_impact_amount_with_cap(
91 &self,
92 is_long_token: bool,
93 price: &Price<Self::Num>,
94 usd_impact: &Self::Signed,
95 ) -> crate::Result<(Self::Signed, Self::Num)> {
96 if price.has_zero() {
97 return Err(crate::Error::DividedByZero);
98 }
99 if usd_impact.is_positive() {
100 let max_price = price.pick_price(true).to_signed()?;
101
102 let mut amount = usd_impact
103 .checked_div(&max_price)
104 .ok_or(crate::Error::Computation("calculating swap impact amount"))?;
105
106 let max_amount = if is_long_token {
107 self.swap_impact_pool()?.long_amount()?
108 } else {
109 self.swap_impact_pool()?.short_amount()?
110 }
111 .to_signed()?;
112
113 let capped_diff_value = if amount > max_amount {
114 let capped_diff_value = amount
115 .checked_sub(&max_amount)
116 .map(|diff_amount| diff_amount.unsigned_abs())
117 .and_then(|diff_amount| diff_amount.checked_mul(price.pick_price(true)))
118 .ok_or(crate::Error::Computation("calculating capped diff value"))?;
119 amount = max_amount;
120 capped_diff_value
121 } else {
122 Zero::zero()
123 };
124 Ok((amount, capped_diff_value))
125 } else if usd_impact.is_negative() {
126 let price = price.pick_price(false).to_signed()?;
127 let one = Self::Signed::one();
128 let amount = usd_impact
130 .checked_sub(&price)
131 .and_then(|a| a.checked_add(&one)?.checked_div(&price))
132 .ok_or(crate::Error::Computation(
133 "calculating round up swap impact amount",
134 ))?;
135 Ok((amount, Zero::zero()))
136 } else {
137 Ok((Zero::zero(), Zero::zero()))
138 }
139 }
140}
141
142impl<M: SwapMarket<DECIMALS> + ?Sized, const DECIMALS: u8> SwapMarketExt<DECIMALS> for M {}
143
144pub trait SwapMarketMutExt<const DECIMALS: u8>: SwapMarketMut<DECIMALS> {
146 fn swap(
148 &mut self,
149 is_token_in_long: bool,
150 token_in_amount: Self::Num,
151 prices: Prices<Self::Num>,
152 ) -> crate::Result<Swap<&mut Self, DECIMALS>>
153 where
154 Self: Sized,
155 {
156 Swap::try_new(self, is_token_in_long, token_in_amount, prices)
157 }
158
159 fn apply_swap_impact_value_with_cap(
165 &mut self,
166 is_long_token: bool,
167 price: &Price<Self::Num>,
168 usd_impact: &Self::Signed,
169 ) -> crate::Result<Self::Num> {
170 let (amount, _) = self.swap_impact_amount_with_cap(is_long_token, price, usd_impact)?;
171 let delta = amount
172 .checked_neg()
173 .ok_or(crate::Error::Computation("negating swap impact delta"))?;
174 if is_long_token {
175 self.swap_impact_pool_mut()?
176 .apply_delta_to_long_amount(&delta)?;
177 } else {
178 self.swap_impact_pool_mut()?
179 .apply_delta_to_short_amount(&delta)?;
180 }
181 Ok(delta.unsigned_abs())
182 }
183}
184
185impl<M: SwapMarketMut<DECIMALS>, const DECIMALS: u8> SwapMarketMutExt<DECIMALS> for M {}