gmsol_store/states/market/
pool.rs

1use anchor_lang::prelude::*;
2use borsh::{BorshDeserialize, BorshSerialize};
3use gmsol_model::PoolKind;
4
5/// A pool storage for market.
6#[zero_copy]
7#[cfg_attr(feature = "debug", derive(Debug))]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct PoolStorage {
10    pub(super) rev: u64,
11    padding: [u8; 8],
12    pool: Pool,
13}
14
15impl PoolStorage {
16    /// Set the pure flag.
17    pub(crate) fn set_is_pure(&mut self, is_pure: bool) {
18        self.pool.set_is_pure(is_pure);
19    }
20
21    /// Get pool.
22    pub fn pool(&self) -> &Pool {
23        &self.pool
24    }
25
26    /// Get pool mutably.
27    pub(super) fn pool_mut(&mut self) -> &mut Pool {
28        &mut self.pool
29    }
30}
31
32/// A pool for market.
33#[zero_copy]
34#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
35#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36#[derive(BorshSerialize, BorshDeserialize, InitSpace)]
37pub struct Pool {
38    /// Whether the pool only contains one kind of token,
39    /// i.e. a pure pool.
40    /// For a pure pool, only the `long_token_amount` field is used.
41    is_pure: u8,
42    #[cfg_attr(feature = "serde", serde(skip))]
43    #[cfg_attr(feature = "debug", debug(skip))]
44    padding: [u8; 15],
45    /// Long token amount.
46    pub(super) long_token_amount: u128,
47    /// Short token amount.
48    pub(super) short_token_amount: u128,
49}
50
51const PURE_VALUE: u8 = 1;
52
53impl Pool {
54    /// Set the pure flag.
55    fn set_is_pure(&mut self, is_pure: bool) {
56        self.is_pure = if is_pure { PURE_VALUE } else { 0 };
57    }
58
59    /// Is this a pure pool.
60    fn is_pure(&self) -> bool {
61        !matches!(self.is_pure, 0)
62    }
63}
64
65impl gmsol_model::Balance for Pool {
66    type Num = u128;
67
68    type Signed = i128;
69
70    /// Get the long token amount.
71    fn long_amount(&self) -> gmsol_model::Result<Self::Num> {
72        if self.is_pure() {
73            debug_assert_eq!(
74                self.short_token_amount, 0,
75                "short token amount must be zero"
76            );
77            // For pure pools, we must ensure that the long token amount
78            // plus the short token amount equals the total token amount.
79            // Therefore, we use `div_ceil` for the long token amount
80            // and `div` for the short token amount.
81            Ok(self.long_token_amount.div_ceil(2))
82        } else {
83            Ok(self.long_token_amount)
84        }
85    }
86
87    /// Get the short token amount.
88    fn short_amount(&self) -> gmsol_model::Result<Self::Num> {
89        if self.is_pure() {
90            debug_assert_eq!(
91                self.short_token_amount, 0,
92                "short token amount must be zero"
93            );
94            Ok(self.long_token_amount / 2)
95        } else {
96            Ok(self.short_token_amount)
97        }
98    }
99}
100
101impl gmsol_model::Pool for Pool {
102    fn apply_delta_to_long_amount(&mut self, delta: &Self::Signed) -> gmsol_model::Result<()> {
103        self.long_token_amount = self.long_token_amount.checked_add_signed(*delta).ok_or(
104            gmsol_model::Error::Computation("apply delta to long amount"),
105        )?;
106        Ok(())
107    }
108
109    fn apply_delta_to_short_amount(&mut self, delta: &Self::Signed) -> gmsol_model::Result<()> {
110        let amount = if self.is_pure() {
111            &mut self.long_token_amount
112        } else {
113            &mut self.short_token_amount
114        };
115        *amount = amount
116            .checked_add_signed(*delta)
117            .ok_or(gmsol_model::Error::Computation(
118                "apply delta to short amount",
119            ))?;
120        Ok(())
121    }
122
123    fn checked_apply_delta(
124        &self,
125        delta: gmsol_model::Delta<&Self::Signed>,
126    ) -> gmsol_model::Result<Self> {
127        let mut ans = *self;
128        if let Some(amount) = delta.long() {
129            ans.apply_delta_to_long_amount(amount)?;
130        }
131        if let Some(amount) = delta.short() {
132            ans.apply_delta_to_short_amount(amount)?;
133        }
134        Ok(ans)
135    }
136}
137
138/// Market Pools.
139#[zero_copy]
140#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
141#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
142pub struct Pools {
143    /// Primary Pool.
144    primary: PoolStorage,
145    /// Swap Impact Pool.
146    swap_impact: PoolStorage,
147    /// Claimable Fee Pool.
148    claimable_fee: PoolStorage,
149    /// Long open interest.
150    open_interest_for_long: PoolStorage,
151    /// Short open interest.
152    open_interest_for_short: PoolStorage,
153    /// Long open interest in tokens.
154    open_interest_in_tokens_for_long: PoolStorage,
155    /// Short open interest in tokens.
156    open_interest_in_tokens_for_short: PoolStorage,
157    /// Position Impact.
158    position_impact: PoolStorage,
159    /// Borrowing Factor.
160    borrowing_factor: PoolStorage,
161    /// Funding Amount Per Size for long.
162    funding_amount_per_size_for_long: PoolStorage,
163    /// Funding Amount Per Size for short.
164    funding_amount_per_size_for_short: PoolStorage,
165    /// Claimable Funding Amount Per Size for long.
166    claimable_funding_amount_per_size_for_long: PoolStorage,
167    /// Claimable Funding Amount Per Size for short.
168    claimable_funding_amount_per_size_for_short: PoolStorage,
169    /// Collateral sum pool for long.
170    collateral_sum_for_long: PoolStorage,
171    /// Collateral sum pool for short.
172    collateral_sum_for_short: PoolStorage,
173    /// Total borrowing pool.
174    total_borrowing: PoolStorage,
175    #[cfg_attr(feature = "debug", debug(skip))]
176    reserved: [PoolStorage; 16],
177}
178
179impl Pools {
180    pub(super) fn init(&mut self, is_pure: bool) {
181        self.primary.set_is_pure(is_pure);
182        self.swap_impact.set_is_pure(is_pure);
183        self.claimable_fee.set_is_pure(is_pure);
184        self.open_interest_for_long.set_is_pure(is_pure);
185        self.open_interest_for_short.set_is_pure(is_pure);
186        self.open_interest_in_tokens_for_long.set_is_pure(is_pure);
187        self.open_interest_in_tokens_for_short.set_is_pure(is_pure);
188        // Position impact pool must be impure.
189        self.position_impact.set_is_pure(false);
190        // Borrowing factor must be impure.
191        self.borrowing_factor.set_is_pure(false);
192        self.funding_amount_per_size_for_long.set_is_pure(is_pure);
193        self.funding_amount_per_size_for_short.set_is_pure(is_pure);
194        self.claimable_funding_amount_per_size_for_long
195            .set_is_pure(is_pure);
196        self.claimable_funding_amount_per_size_for_short
197            .set_is_pure(is_pure);
198        self.collateral_sum_for_long.set_is_pure(is_pure);
199        self.collateral_sum_for_short.set_is_pure(is_pure);
200        // Total borrowing pool must be impure.
201        self.total_borrowing.set_is_pure(false);
202    }
203
204    pub(super) fn get(&self, kind: PoolKind) -> Option<&PoolStorage> {
205        let pool = match kind {
206            PoolKind::Primary => &self.primary,
207            PoolKind::SwapImpact => &self.swap_impact,
208            PoolKind::ClaimableFee => &self.claimable_fee,
209            PoolKind::OpenInterestForLong => &self.open_interest_for_long,
210            PoolKind::OpenInterestForShort => &self.open_interest_for_short,
211            PoolKind::OpenInterestInTokensForLong => &self.open_interest_in_tokens_for_long,
212            PoolKind::OpenInterestInTokensForShort => &self.open_interest_in_tokens_for_short,
213            PoolKind::PositionImpact => &self.position_impact,
214            PoolKind::BorrowingFactor => &self.borrowing_factor,
215            PoolKind::FundingAmountPerSizeForLong => &self.funding_amount_per_size_for_long,
216            PoolKind::FundingAmountPerSizeForShort => &self.funding_amount_per_size_for_short,
217            PoolKind::ClaimableFundingAmountPerSizeForLong => {
218                &self.claimable_funding_amount_per_size_for_long
219            }
220            PoolKind::ClaimableFundingAmountPerSizeForShort => {
221                &self.claimable_funding_amount_per_size_for_short
222            }
223            PoolKind::CollateralSumForLong => &self.collateral_sum_for_long,
224            PoolKind::CollateralSumForShort => &self.collateral_sum_for_short,
225            PoolKind::TotalBorrowing => &self.total_borrowing,
226            _ => return None,
227        };
228        Some(pool)
229    }
230
231    pub(super) fn get_mut(&mut self, kind: PoolKind) -> Option<&mut PoolStorage> {
232        let pool = match kind {
233            PoolKind::Primary => &mut self.primary,
234            PoolKind::SwapImpact => &mut self.swap_impact,
235            PoolKind::ClaimableFee => &mut self.claimable_fee,
236            PoolKind::OpenInterestForLong => &mut self.open_interest_for_long,
237            PoolKind::OpenInterestForShort => &mut self.open_interest_for_short,
238            PoolKind::OpenInterestInTokensForLong => &mut self.open_interest_in_tokens_for_long,
239            PoolKind::OpenInterestInTokensForShort => &mut self.open_interest_in_tokens_for_short,
240            PoolKind::PositionImpact => &mut self.position_impact,
241            PoolKind::BorrowingFactor => &mut self.borrowing_factor,
242            PoolKind::FundingAmountPerSizeForLong => &mut self.funding_amount_per_size_for_long,
243            PoolKind::FundingAmountPerSizeForShort => &mut self.funding_amount_per_size_for_short,
244            PoolKind::ClaimableFundingAmountPerSizeForLong => {
245                &mut self.claimable_funding_amount_per_size_for_long
246            }
247            PoolKind::ClaimableFundingAmountPerSizeForShort => {
248                &mut self.claimable_funding_amount_per_size_for_short
249            }
250            PoolKind::CollateralSumForLong => &mut self.collateral_sum_for_long,
251            PoolKind::CollateralSumForShort => &mut self.collateral_sum_for_short,
252            PoolKind::TotalBorrowing => &mut self.total_borrowing,
253            _ => return None,
254        };
255        Some(pool)
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262    use crate::events::EventPool;
263
264    #[test]
265    fn test_event_pool() {
266        let pool = Pool {
267            is_pure: PURE_VALUE,
268            padding: Default::default(),
269            long_token_amount: u128::MAX,
270            short_token_amount: u128::MAX,
271        };
272
273        let event_pool = EventPool {
274            is_pure: pool.is_pure,
275            padding: pool.padding,
276            long_token_amount: pool.long_token_amount,
277            short_token_amount: pool.short_token_amount,
278        };
279
280        let mut data = Vec::with_capacity(Pool::INIT_SPACE);
281        pool.serialize(&mut data)
282            .expect("failed to serialize `Pool`");
283
284        let mut event_data = Vec::with_capacity(Pool::INIT_SPACE);
285        event_pool
286            .serialize(&mut event_data)
287            .expect("failed to serialize `EventPool`");
288
289        assert_eq!(data, event_data);
290    }
291}