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    /// Returns the market token value if evaluated, otherwise `None`.
152    fn market_token_value(
153        &self,
154        amount: &Self::Num,
155        prices: &Prices<Self::Num>,
156        pnl_factor: PnlFactorKind,
157        maximize: bool,
158    ) -> crate::Result<Option<MarketTokenValue<Self::Num>>> {
159        let supply = self.total_supply();
160        if supply.is_zero() {
161            return Ok(None);
162        }
163        let pool_value = self.pool_value(prices, pnl_factor, maximize)?;
164        if pool_value.is_negative() {
165            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."));
166        }
167        let value =
168            crate::utils::market_token_amount_to_usd(amount, &pool_value.unsigned_abs(), &supply)
169                .ok_or(crate::Error::Computation("calculating market token price"))?;
170
171        Ok(Some(MarketTokenValue {
172            supply,
173            pool_value,
174            value,
175        }))
176    }
177
178    /// Get market token price.
179    fn market_token_price(
180        &self,
181        prices: &Prices<Self::Num>,
182        pnl_factor: PnlFactorKind,
183        maximize: bool,
184    ) -> crate::Result<Self::Num> {
185        let one = Self::Num::UNIT
186            .checked_div(&self.usd_to_amount_divisor())
187            .ok_or(crate::Error::Computation("calculating one market token"))?;
188        match self.market_token_value(&one, prices, pnl_factor, maximize)? {
189            Some(result) => Ok(result.value),
190            None => Ok(Self::Num::UNIT),
191        }
192    }
193}
194
195impl<M: LiquidityMarket<DECIMALS>, const DECIMALS: u8> LiquidityMarketExt<DECIMALS> for M {}
196
197/// Extension trait of [`LiquidityMarketMut`].
198pub trait LiquidityMarketMutExt<const DECIMALS: u8>: LiquidityMarketMut<DECIMALS> {
199    /// Create a [`Deposit`] action.
200    fn deposit(
201        &mut self,
202        long_token_amount: Self::Num,
203        short_token_amount: Self::Num,
204        prices: Prices<Self::Num>,
205    ) -> Result<Deposit<&mut Self, DECIMALS>, crate::Error>
206    where
207        Self: Sized,
208    {
209        Deposit::try_new(self, long_token_amount, short_token_amount, prices)
210    }
211
212    /// Create a [`Withdrawal`].
213    fn withdraw(
214        &mut self,
215        market_token_amount: Self::Num,
216        prices: Prices<Self::Num>,
217    ) -> crate::Result<Withdrawal<&mut Self, DECIMALS>>
218    where
219        Self: Sized,
220    {
221        Withdrawal::try_new(self, market_token_amount, prices)
222    }
223}
224
225impl<M: LiquidityMarketMut<DECIMALS>, const DECIMALS: u8> LiquidityMarketMutExt<DECIMALS> for M {}
226
227/// Result of a market token valuation.
228#[derive(Debug, Clone)]
229pub struct MarketTokenValue<T: Unsigned> {
230    /// Total supply of the market token.
231    pub supply: T,
232    /// Total value of the pool.
233    pub pool_value: T::Signed,
234    /// Computed value of the given amount.
235    pub value: T,
236}