gmsol_model/market/
liquidity.rs

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
16/// A market for providing liquidity.
17pub trait LiquidityMarket<const DECIMALS: u8>:
18    PositionImpactMarket<DECIMALS> + BorrowingFeeMarket<DECIMALS>
19{
20    /// Get total supply of the market token.
21    fn total_supply(&self) -> Self::Num;
22
23    /// Get max pool value for deposit.
24    fn max_pool_value_for_deposit(&self, is_long_token: bool) -> crate::Result<Self::Num>;
25}
26
27/// A market for providing liquidity.
28pub trait LiquidityMarketMut<const DECIMALS: u8>:
29    SwapMarketMut<DECIMALS> + LiquidityMarket<DECIMALS>
30{
31    /// Perform mint.
32    fn mint(&mut self, amount: &Self::Num) -> Result<(), crate::Error>;
33
34    /// Perform burn.
35    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
58/// Extension trait of [`LiquidityMarket`].
59pub trait LiquidityMarketExt<const DECIMALS: u8>: LiquidityMarket<DECIMALS> {
60    /// Validate (primary) pool value for deposit.
61    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    /// Get the usd value of primary pool.
78    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        // Add total pending borrowing fees.
93        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        // Deduct net pnl.
116        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        // Deduct impact pool value.
132        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    /// Get market token price.
152    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
176/// Extension trait of [`LiquidityMarketMut`].
177pub trait LiquidityMarketMutExt<const DECIMALS: u8>: LiquidityMarketMut<DECIMALS> {
178    /// Create a [`Deposit`] action.
179    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    /// Create a [`Withdrawal`].
192    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 {}