1use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
2
3use crate::{
4 action::{deposit::Deposit, withdraw::Withdrawal},
5 fixed::FixedPointOps,
6 market::utils::MarketUtils,
7 num::{Unsigned, UnsignedAbs},
8 price::Prices,
9 BorrowingFeeMarket, PnlFactorKind, PositionImpactMarket,
10};
11
12use super::{
13 get_msg_by_side, BaseMarketExt, BorrowingFeeMarketExt, PositionImpactMarketExt, SwapMarketMut,
14};
15
16pub trait LiquidityMarket<const DECIMALS: u8>:
18 PositionImpactMarket<DECIMALS> + BorrowingFeeMarket<DECIMALS>
19{
20 fn total_supply(&self) -> Self::Num;
22
23 fn max_pool_value_for_deposit(&self, is_long_token: bool) -> crate::Result<Self::Num>;
25}
26
27pub trait LiquidityMarketMut<const DECIMALS: u8>:
29 SwapMarketMut<DECIMALS> + LiquidityMarket<DECIMALS>
30{
31 fn mint(&mut self, amount: &Self::Num) -> Result<(), crate::Error>;
33
34 fn burn(&mut self, amount: &Self::Num) -> crate::Result<()>;
36}
37
38impl<M: LiquidityMarket<DECIMALS>, const DECIMALS: u8> LiquidityMarket<DECIMALS> for &mut M {
39 fn total_supply(&self) -> Self::Num {
40 (**self).total_supply()
41 }
42
43 fn max_pool_value_for_deposit(&self, is_long_token: bool) -> crate::Result<Self::Num> {
44 (**self).max_pool_value_for_deposit(is_long_token)
45 }
46}
47
48impl<M: LiquidityMarketMut<DECIMALS>, const DECIMALS: u8> LiquidityMarketMut<DECIMALS> for &mut M {
49 fn mint(&mut self, amount: &Self::Num) -> Result<(), crate::Error> {
50 (**self).mint(amount)
51 }
52
53 fn burn(&mut self, amount: &Self::Num) -> crate::Result<()> {
54 (**self).burn(amount)
55 }
56}
57
58pub trait LiquidityMarketExt<const DECIMALS: u8>: LiquidityMarket<DECIMALS> {
60 fn validate_pool_value_for_deposit(
62 &self,
63 prices: &Prices<Self::Num>,
64 is_long_token: bool,
65 ) -> crate::Result<()> {
66 let pool_value = self.pool_value_without_pnl_for_one_side(prices, is_long_token, true)?;
67 let max_pool_value = self.max_pool_value_for_deposit(is_long_token)?;
68 if pool_value > max_pool_value {
69 Err(crate::Error::MaxPoolValueExceeded(get_msg_by_side(
70 is_long_token,
71 )))
72 } else {
73 Ok(())
74 }
75 }
76
77 fn pool_value(
79 &self,
80 prices: &Prices<Self::Num>,
81 pnl_factor: PnlFactorKind,
82 maximize: bool,
83 ) -> crate::Result<Self::Signed> {
84 let long_value = self.pool_value_without_pnl_for_one_side(prices, true, maximize)?;
85 let short_value = self.pool_value_without_pnl_for_one_side(prices, false, maximize)?;
86
87 let mut pool_value = long_value
88 .checked_add(&short_value)
89 .ok_or(crate::Error::Overflow)?
90 .to_signed()?;
91
92 let total_borrowing_fees = {
94 let for_long = self.total_pending_borrowing_fees(prices, true)?;
95 let for_short = self.total_pending_borrowing_fees(prices, false)?;
96 for_long
97 .checked_add(&for_short)
98 .ok_or(crate::Error::Computation(
99 "calculating total pending borrowing fees for pool value",
100 ))?
101 };
102 let total_borrowing_fees_for_pool = <Self::Num>::UNIT
103 .checked_sub(self.borrowing_fee_params()?.receiver_factor())
104 .and_then(|factor| crate::utils::apply_factor(&total_borrowing_fees, &factor))
105 .ok_or(crate::Error::Computation(
106 "calculating total borrowing fees for pool",
107 ))?
108 .to_signed()?;
109 pool_value = pool_value
110 .checked_add(&total_borrowing_fees_for_pool)
111 .ok_or(crate::Error::Computation(
112 "adding total borrowing fees for pool",
113 ))?;
114
115 let long_pnl = {
117 let pnl = self.pnl(&prices.index_token_price, true, !maximize)?;
118 self.cap_pnl(true, &pnl, &long_value, pnl_factor)?
119 };
120 let short_pnl = {
121 let pnl = self.pnl(&prices.index_token_price, false, !maximize)?;
122 self.cap_pnl(false, &pnl, &short_value, pnl_factor)?
123 };
124 let net_pnl = long_pnl
125 .checked_add(&short_pnl)
126 .ok_or(crate::Error::Computation("calculating net pnl"))?;
127 pool_value = pool_value
128 .checked_sub(&net_pnl)
129 .ok_or(crate::Error::Computation("deducting net pnl"))?;
130
131 let impact_pool_value = {
133 let duration = self.passed_in_seconds_for_position_impact_distribution()?;
134 let amount = self
135 .pending_position_impact_pool_distribution_amount(duration)?
136 .1;
137 let price = prices.index_token_price.pick_price(!maximize);
138 amount
139 .checked_mul(price)
140 .ok_or(crate::Error::Computation("calculating impact pool value"))?
141 }
142 .to_signed()?;
143
144 pool_value = pool_value
145 .checked_sub(&impact_pool_value)
146 .ok_or(crate::Error::Computation("deducting impact pool value"))?;
147
148 Ok(pool_value)
149 }
150
151 fn market_token_price(
153 &self,
154 prices: &Prices<Self::Num>,
155 pnl_factor: PnlFactorKind,
156 maximize: bool,
157 ) -> crate::Result<Self::Num> {
158 let supply = self.total_supply();
159 if supply.is_zero() {
160 return Ok(Self::Num::UNIT);
161 }
162 let pool_value = self.pool_value(prices, pnl_factor, maximize)?;
163 if pool_value.is_negative() {
164 return Err(crate::Error::InvalidPoolValue("the pool value is negative. Calculation of the market token price is currently unsupported when the pool value is negative."));
165 }
166 let one = Self::Num::UNIT
167 .checked_div(&self.usd_to_amount_divisor())
168 .ok_or(crate::Error::Computation("calculating one market token"))?;
169 crate::utils::market_token_amount_to_usd(&one, &pool_value.unsigned_abs(), &supply)
170 .ok_or(crate::Error::Computation("calculating market token price"))
171 }
172}
173
174impl<M: LiquidityMarket<DECIMALS>, const DECIMALS: u8> LiquidityMarketExt<DECIMALS> for M {}
175
176pub trait LiquidityMarketMutExt<const DECIMALS: u8>: LiquidityMarketMut<DECIMALS> {
178 fn deposit(
180 &mut self,
181 long_token_amount: Self::Num,
182 short_token_amount: Self::Num,
183 prices: Prices<Self::Num>,
184 ) -> Result<Deposit<&mut Self, DECIMALS>, crate::Error>
185 where
186 Self: Sized,
187 {
188 Deposit::try_new(self, long_token_amount, short_token_amount, prices)
189 }
190
191 fn withdraw(
193 &mut self,
194 market_token_amount: Self::Num,
195 prices: Prices<Self::Num>,
196 ) -> crate::Result<Withdrawal<&mut Self, DECIMALS>>
197 where
198 Self: Sized,
199 {
200 Withdrawal::try_new(self, market_token_amount, prices)
201 }
202}
203
204impl<M: LiquidityMarketMut<DECIMALS>, const DECIMALS: u8> LiquidityMarketMutExt<DECIMALS> for M {}