gmsol_programs/model/
market.rs

1use std::{
2    borrow::Borrow,
3    ops::{Deref, DerefMut},
4    sync::Arc,
5};
6
7use anchor_lang::prelude::Pubkey;
8use bitmaps::Bitmap;
9use gmsol_model::{
10    params::{
11        fee::{
12            BorrowingFeeKinkModelParams, BorrowingFeeKinkModelParamsForOneSide, BorrowingFeeParams,
13            FundingFeeParams, LiquidationFeeParams,
14        },
15        position::PositionImpactDistributionParams,
16        FeeParams, PositionParams, PriceImpactParams,
17    },
18    PoolKind,
19};
20
21use crate::{
22    constants,
23    gmsol_store::{
24        accounts::Market,
25        types::{MarketConfig, MarketMeta, Pool, PoolStorage, Pools},
26    },
27};
28
29use super::clock::{AsClock, AsClockMut};
30
31impl MarketMeta {
32    /// Get token side.
33    pub fn token_side(&self, token: &Pubkey) -> gmsol_model::Result<bool> {
34        if *token == self.long_token_mint {
35            Ok(true)
36        } else if *token == self.short_token_mint {
37            Ok(false)
38        } else {
39            Err(gmsol_model::Error::InvalidArgument("not a pool token"))
40        }
41    }
42}
43
44impl Pools {
45    fn get(&self, kind: PoolKind) -> Option<&PoolStorage> {
46        let pool = match kind {
47            PoolKind::Primary => &self.primary,
48            PoolKind::SwapImpact => &self.swap_impact,
49            PoolKind::ClaimableFee => &self.claimable_fee,
50            PoolKind::OpenInterestForLong => &self.open_interest_for_long,
51            PoolKind::OpenInterestForShort => &self.open_interest_for_short,
52            PoolKind::OpenInterestInTokensForLong => &self.open_interest_in_tokens_for_long,
53            PoolKind::OpenInterestInTokensForShort => &self.open_interest_in_tokens_for_short,
54            PoolKind::PositionImpact => &self.position_impact,
55            PoolKind::BorrowingFactor => &self.borrowing_factor,
56            PoolKind::FundingAmountPerSizeForLong => &self.funding_amount_per_size_for_long,
57            PoolKind::FundingAmountPerSizeForShort => &self.funding_amount_per_size_for_short,
58            PoolKind::ClaimableFundingAmountPerSizeForLong => {
59                &self.claimable_funding_amount_per_size_for_long
60            }
61            PoolKind::ClaimableFundingAmountPerSizeForShort => {
62                &self.claimable_funding_amount_per_size_for_short
63            }
64            PoolKind::CollateralSumForLong => &self.collateral_sum_for_long,
65            PoolKind::CollateralSumForShort => &self.collateral_sum_for_short,
66            PoolKind::TotalBorrowing => &self.total_borrowing,
67            _ => return None,
68        };
69        Some(pool)
70    }
71
72    fn get_mut(&mut self, kind: PoolKind) -> Option<&mut PoolStorage> {
73        let pool = match kind {
74            PoolKind::Primary => &mut self.primary,
75            PoolKind::SwapImpact => &mut self.swap_impact,
76            PoolKind::ClaimableFee => &mut self.claimable_fee,
77            PoolKind::OpenInterestForLong => &mut self.open_interest_for_long,
78            PoolKind::OpenInterestForShort => &mut self.open_interest_for_short,
79            PoolKind::OpenInterestInTokensForLong => &mut self.open_interest_in_tokens_for_long,
80            PoolKind::OpenInterestInTokensForShort => &mut self.open_interest_in_tokens_for_short,
81            PoolKind::PositionImpact => &mut self.position_impact,
82            PoolKind::BorrowingFactor => &mut self.borrowing_factor,
83            PoolKind::FundingAmountPerSizeForLong => &mut self.funding_amount_per_size_for_long,
84            PoolKind::FundingAmountPerSizeForShort => &mut self.funding_amount_per_size_for_short,
85            PoolKind::ClaimableFundingAmountPerSizeForLong => {
86                &mut self.claimable_funding_amount_per_size_for_long
87            }
88            PoolKind::ClaimableFundingAmountPerSizeForShort => {
89                &mut self.claimable_funding_amount_per_size_for_short
90            }
91            PoolKind::CollateralSumForLong => &mut self.collateral_sum_for_long,
92            PoolKind::CollateralSumForShort => &mut self.collateral_sum_for_short,
93            PoolKind::TotalBorrowing => &mut self.total_borrowing,
94            _ => return None,
95        };
96        Some(pool)
97    }
98}
99
100#[repr(u8)]
101enum MarketConfigFlag {
102    SkipBorrowingFeeForSmallerSide,
103    IgnoreOpenInterestForUsageFactor,
104}
105
106type MarketConfigFlags = Bitmap<{ constants::NUM_MARKET_CONFIG_FLAGS }>;
107
108impl MarketConfig {
109    fn flag(&self, flag: MarketConfigFlag) -> bool {
110        MarketConfigFlags::from_value(self.flag.value).get(flag as usize)
111    }
112}
113
114#[repr(u8)]
115#[allow(dead_code)]
116enum MarketFlag {
117    Enabled,
118    Pure,
119    AutoDeleveragingEnabledForLong,
120    AutoDeleveragingEnabledForShort,
121    GTEnabled,
122}
123
124type MarketFlags = Bitmap<{ constants::NUM_MARKET_FLAGS }>;
125
126impl Market {
127    fn try_pool(&self, kind: PoolKind) -> gmsol_model::Result<&Pool> {
128        Ok(&self
129            .state
130            .pools
131            .get(kind)
132            .ok_or(gmsol_model::Error::MissingPoolKind(kind))?
133            .pool)
134    }
135
136    fn try_pool_mut(&mut self, kind: PoolKind) -> gmsol_model::Result<&mut Pool> {
137        Ok(&mut self
138            .state
139            .pools
140            .get_mut(kind)
141            .ok_or(gmsol_model::Error::MissingPoolKind(kind))?
142            .pool)
143    }
144
145    fn flag(&self, flag: MarketFlag) -> bool {
146        MarketFlags::from_value(self.flags.value).get(flag as usize)
147    }
148}
149
150/// Market Model.
151#[derive(Debug, Clone)]
152pub struct MarketModel {
153    market: Arc<Market>,
154    supply: u64,
155}
156
157impl Deref for MarketModel {
158    type Target = Market;
159
160    fn deref(&self) -> &Self::Target {
161        &self.market
162    }
163}
164
165impl MarketModel {
166    /// Create from parts.
167    pub fn from_parts(market: Arc<Market>, supply: u64) -> Self {
168        Self { market, supply }
169    }
170
171    /// Get whether it is a pure market.
172    pub fn is_pure(&self) -> bool {
173        self.market.flag(MarketFlag::Pure)
174    }
175
176    /// Record transferred in.
177    fn record_transferred_in(
178        &mut self,
179        is_long_token: bool,
180        amount: u64,
181    ) -> gmsol_model::Result<()> {
182        let is_pure = self.market.flag(MarketFlag::Pure);
183        let other = &self.market.state.other;
184
185        if is_pure || is_long_token {
186            self.make_market_mut().state.other.long_token_balance =
187                other.long_token_balance.checked_add(amount).ok_or(
188                    gmsol_model::Error::Computation("increasing long token balance"),
189                )?;
190        } else {
191            self.make_market_mut().state.other.short_token_balance =
192                other.short_token_balance.checked_add(amount).ok_or(
193                    gmsol_model::Error::Computation("increasing short token balance"),
194                )?;
195        }
196
197        Ok(())
198    }
199
200    /// Record transferred out.
201    fn record_transferred_out(
202        &mut self,
203        is_long_token: bool,
204        amount: u64,
205    ) -> gmsol_model::Result<()> {
206        let is_pure = self.market.flag(MarketFlag::Pure);
207        let other = &self.market.state.other;
208
209        if is_pure || is_long_token {
210            self.make_market_mut().state.other.long_token_balance =
211                other.long_token_balance.checked_sub(amount).ok_or(
212                    gmsol_model::Error::Computation("decreasing long token balance"),
213                )?;
214        } else {
215            self.make_market_mut().state.other.short_token_balance =
216                other.short_token_balance.checked_sub(amount).ok_or(
217                    gmsol_model::Error::Computation("decreasing long token balance"),
218                )?;
219        }
220
221        Ok(())
222    }
223
224    fn balance_for_token(&self, is_long_token: bool) -> u64 {
225        let other = &self.state.other;
226        if is_long_token || self.market.flag(MarketFlag::Pure) {
227            other.long_token_balance
228        } else {
229            other.short_token_balance
230        }
231    }
232
233    fn make_market_mut(&mut self) -> &mut Market {
234        Arc::make_mut(&mut self.market)
235    }
236
237    /// Returns the time in seconds since last funding fee state update.
238    pub fn passed_in_seconds_for_funding(&self) -> gmsol_model::Result<u64> {
239        AsClock::from(&self.state.clocks.funding).passed_in_seconds()
240    }
241}
242
243impl gmsol_model::BaseMarket<{ constants::MARKET_DECIMALS }> for MarketModel {
244    type Num = u128;
245
246    type Signed = i128;
247
248    type Pool = Pool;
249
250    fn liquidity_pool(&self) -> gmsol_model::Result<&Self::Pool> {
251        self.try_pool(PoolKind::Primary)
252    }
253
254    fn claimable_fee_pool(&self) -> gmsol_model::Result<&Self::Pool> {
255        self.try_pool(PoolKind::ClaimableFee)
256    }
257
258    fn swap_impact_pool(&self) -> gmsol_model::Result<&Self::Pool> {
259        self.try_pool(PoolKind::SwapImpact)
260    }
261
262    fn open_interest_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
263        self.try_pool(if is_long {
264            PoolKind::OpenInterestForLong
265        } else {
266            PoolKind::OpenInterestForShort
267        })
268    }
269
270    fn open_interest_in_tokens_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
271        self.try_pool(if is_long {
272            PoolKind::OpenInterestInTokensForLong
273        } else {
274            PoolKind::OpenInterestInTokensForShort
275        })
276    }
277
278    fn collateral_sum_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
279        let kind = if is_long {
280            PoolKind::CollateralSumForLong
281        } else {
282            PoolKind::CollateralSumForShort
283        };
284        self.try_pool(kind)
285    }
286
287    fn virtual_inventory_for_swaps_pool(
288        &self,
289    ) -> gmsol_model::Result<Option<impl Deref<Target = Self::Pool>>> {
290        Ok(None::<&Self::Pool>)
291    }
292
293    fn virtual_inventory_for_positions_pool(
294        &self,
295    ) -> gmsol_model::Result<Option<impl Deref<Target = Self::Pool>>> {
296        Ok(None::<&Self::Pool>)
297    }
298
299    fn usd_to_amount_divisor(&self) -> Self::Num {
300        constants::MARKET_USD_TO_AMOUNT_DIVISOR
301    }
302
303    fn max_pool_amount(&self, is_long_token: bool) -> gmsol_model::Result<Self::Num> {
304        if is_long_token {
305            Ok(self.config.max_pool_amount_for_long_token)
306        } else {
307            Ok(self.config.max_pool_amount_for_short_token)
308        }
309    }
310
311    fn pnl_factor_config(
312        &self,
313        kind: gmsol_model::PnlFactorKind,
314        is_long: bool,
315    ) -> gmsol_model::Result<Self::Num> {
316        use gmsol_model::PnlFactorKind;
317
318        match (kind, is_long) {
319            (PnlFactorKind::MaxAfterDeposit, true) => {
320                Ok(self.config.max_pnl_factor_for_long_deposit)
321            }
322            (PnlFactorKind::MaxAfterDeposit, false) => {
323                Ok(self.config.max_pnl_factor_for_short_deposit)
324            }
325            (PnlFactorKind::MaxAfterWithdrawal, true) => {
326                Ok(self.config.max_pnl_factor_for_long_withdrawal)
327            }
328            (PnlFactorKind::MaxAfterWithdrawal, false) => {
329                Ok(self.config.max_pnl_factor_for_short_withdrawal)
330            }
331            (PnlFactorKind::MaxForTrader, true) => Ok(self.config.max_pnl_factor_for_long_trader),
332            (PnlFactorKind::MaxForTrader, false) => Ok(self.config.max_pnl_factor_for_short_trader),
333            (PnlFactorKind::ForAdl, true) => Ok(self.config.max_pnl_factor_for_long_adl),
334            (PnlFactorKind::ForAdl, false) => Ok(self.config.max_pnl_factor_for_short_adl),
335            (PnlFactorKind::MinAfterAdl, true) => Ok(self.config.min_pnl_factor_after_long_adl),
336            (PnlFactorKind::MinAfterAdl, false) => Ok(self.config.min_pnl_factor_after_short_adl),
337            _ => Err(gmsol_model::Error::InvalidArgument("missing pnl factor")),
338        }
339    }
340
341    fn reserve_factor(&self) -> gmsol_model::Result<Self::Num> {
342        Ok(self.config.reserve_factor)
343    }
344
345    fn open_interest_reserve_factor(&self) -> gmsol_model::Result<Self::Num> {
346        Ok(self.config.open_interest_reserve_factor)
347    }
348
349    fn max_open_interest(&self, is_long: bool) -> gmsol_model::Result<Self::Num> {
350        if is_long {
351            Ok(self.config.max_open_interest_for_long)
352        } else {
353            Ok(self.config.max_open_interest_for_short)
354        }
355    }
356
357    fn ignore_open_interest_for_usage_factor(&self) -> gmsol_model::Result<bool> {
358        Ok(self
359            .config
360            .flag(MarketConfigFlag::IgnoreOpenInterestForUsageFactor))
361    }
362}
363
364impl gmsol_model::SwapMarket<{ constants::MARKET_DECIMALS }> for MarketModel {
365    fn swap_impact_params(&self) -> gmsol_model::Result<PriceImpactParams<Self::Num>> {
366        Ok(PriceImpactParams::builder()
367            .exponent(self.config.swap_impact_exponent)
368            .positive_factor(self.config.swap_impact_positive_factor)
369            .negative_factor(self.config.swap_impact_negative_factor)
370            .build())
371    }
372
373    fn swap_fee_params(&self) -> gmsol_model::Result<FeeParams<Self::Num>> {
374        Ok(FeeParams::builder()
375            .fee_receiver_factor(self.config.swap_fee_receiver_factor)
376            .positive_impact_fee_factor(self.config.swap_fee_factor_for_positive_impact)
377            .negative_impact_fee_factor(self.config.swap_fee_factor_for_negative_impact)
378            .build())
379    }
380}
381
382impl gmsol_model::PositionImpactMarket<{ constants::MARKET_DECIMALS }> for MarketModel {
383    fn position_impact_pool(&self) -> gmsol_model::Result<&Self::Pool> {
384        self.try_pool(PoolKind::PositionImpact)
385    }
386
387    fn position_impact_params(&self) -> gmsol_model::Result<PriceImpactParams<Self::Num>> {
388        let config = &self.config;
389        Ok(PriceImpactParams::builder()
390            .exponent(config.position_impact_exponent)
391            .positive_factor(config.position_impact_positive_factor)
392            .negative_factor(config.position_impact_negative_factor)
393            .build())
394    }
395
396    fn position_impact_distribution_params(
397        &self,
398    ) -> gmsol_model::Result<PositionImpactDistributionParams<Self::Num>> {
399        let config = &self.config;
400        Ok(PositionImpactDistributionParams::builder()
401            .distribute_factor(config.position_impact_distribute_factor)
402            .min_position_impact_pool_amount(config.min_position_impact_pool_amount)
403            .build())
404    }
405
406    fn passed_in_seconds_for_position_impact_distribution(&self) -> gmsol_model::Result<u64> {
407        AsClock::from(&self.state.clocks.price_impact_distribution).passed_in_seconds()
408    }
409}
410
411impl gmsol_model::BorrowingFeeMarket<{ constants::MARKET_DECIMALS }> for MarketModel {
412    fn borrowing_factor_pool(&self) -> gmsol_model::Result<&Self::Pool> {
413        self.try_pool(PoolKind::BorrowingFactor)
414    }
415
416    fn total_borrowing_pool(&self) -> gmsol_model::Result<&Self::Pool> {
417        self.try_pool(PoolKind::TotalBorrowing)
418    }
419
420    fn borrowing_fee_params(&self) -> gmsol_model::Result<BorrowingFeeParams<Self::Num>> {
421        Ok(BorrowingFeeParams::builder()
422            .receiver_factor(self.config.borrowing_fee_receiver_factor)
423            .factor_for_long(self.config.borrowing_fee_factor_for_long)
424            .factor_for_short(self.config.borrowing_fee_factor_for_short)
425            .exponent_for_long(self.config.borrowing_fee_exponent_for_long)
426            .exponent_for_short(self.config.borrowing_fee_exponent_for_short)
427            .skip_borrowing_fee_for_smaller_side(
428                self.config
429                    .flag(MarketConfigFlag::SkipBorrowingFeeForSmallerSide),
430            )
431            .build())
432    }
433
434    fn passed_in_seconds_for_borrowing(&self) -> gmsol_model::Result<u64> {
435        AsClock::from(&self.state.clocks.borrowing).passed_in_seconds()
436    }
437
438    fn borrowing_fee_kink_model_params(
439        &self,
440    ) -> gmsol_model::Result<BorrowingFeeKinkModelParams<Self::Num>> {
441        Ok(BorrowingFeeKinkModelParams::builder()
442            .long(
443                BorrowingFeeKinkModelParamsForOneSide::builder()
444                    .optimal_usage_factor(self.config.borrowing_fee_optimal_usage_factor_for_long)
445                    .base_borrowing_factor(self.config.borrowing_fee_base_factor_for_long)
446                    .above_optimal_usage_borrowing_factor(
447                        self.config
448                            .borrowing_fee_above_optimal_usage_factor_for_long,
449                    )
450                    .build(),
451            )
452            .short(
453                BorrowingFeeKinkModelParamsForOneSide::builder()
454                    .optimal_usage_factor(self.config.borrowing_fee_optimal_usage_factor_for_short)
455                    .base_borrowing_factor(self.config.borrowing_fee_base_factor_for_short)
456                    .above_optimal_usage_borrowing_factor(
457                        self.config
458                            .borrowing_fee_above_optimal_usage_factor_for_short,
459                    )
460                    .build(),
461            )
462            .build())
463    }
464}
465
466impl gmsol_model::PerpMarket<{ constants::MARKET_DECIMALS }> for MarketModel {
467    fn funding_factor_per_second(&self) -> &Self::Signed {
468        &self.state.other.funding_factor_per_second
469    }
470
471    fn funding_amount_per_size_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
472        let kind = if is_long {
473            PoolKind::FundingAmountPerSizeForLong
474        } else {
475            PoolKind::FundingAmountPerSizeForShort
476        };
477        self.try_pool(kind)
478    }
479
480    fn claimable_funding_amount_per_size_pool(
481        &self,
482        is_long: bool,
483    ) -> gmsol_model::Result<&Self::Pool> {
484        let kind = if is_long {
485            PoolKind::ClaimableFundingAmountPerSizeForLong
486        } else {
487            PoolKind::ClaimableFundingAmountPerSizeForShort
488        };
489        self.try_pool(kind)
490    }
491
492    fn funding_amount_per_size_adjustment(&self) -> Self::Num {
493        constants::FUNDING_AMOUNT_PER_SIZE_ADJUSTMENT
494    }
495
496    fn funding_fee_params(&self) -> gmsol_model::Result<FundingFeeParams<Self::Num>> {
497        Ok(FundingFeeParams::builder()
498            .exponent(self.config.funding_fee_exponent)
499            .funding_factor(self.config.funding_fee_factor)
500            .max_factor_per_second(self.config.funding_fee_max_factor_per_second)
501            .min_factor_per_second(self.config.funding_fee_min_factor_per_second)
502            .increase_factor_per_second(self.config.funding_fee_increase_factor_per_second)
503            .decrease_factor_per_second(self.config.funding_fee_decrease_factor_per_second)
504            .threshold_for_stable_funding(self.config.funding_fee_threshold_for_stable_funding)
505            .threshold_for_decrease_funding(self.config.funding_fee_threshold_for_decrease_funding)
506            .build())
507    }
508
509    fn position_params(&self) -> gmsol_model::Result<PositionParams<Self::Num>> {
510        Ok(PositionParams::new(
511            self.config.min_position_size_usd,
512            self.config.min_collateral_value,
513            self.config.min_collateral_factor,
514            self.config.max_positive_position_impact_factor,
515            self.config.max_negative_position_impact_factor,
516            self.config.max_position_impact_factor_for_liquidations,
517        ))
518    }
519
520    fn order_fee_params(&self) -> gmsol_model::Result<FeeParams<Self::Num>> {
521        Ok(FeeParams::builder()
522            .fee_receiver_factor(self.config.order_fee_receiver_factor)
523            .positive_impact_fee_factor(self.config.order_fee_factor_for_positive_impact)
524            .negative_impact_fee_factor(self.config.order_fee_factor_for_negative_impact)
525            .build())
526    }
527
528    fn min_collateral_factor_for_open_interest_multiplier(
529        &self,
530        is_long: bool,
531    ) -> gmsol_model::Result<Self::Num> {
532        if is_long {
533            Ok(self
534                .config
535                .min_collateral_factor_for_open_interest_multiplier_for_long)
536        } else {
537            Ok(self
538                .config
539                .min_collateral_factor_for_open_interest_multiplier_for_short)
540        }
541    }
542
543    fn liquidation_fee_params(&self) -> gmsol_model::Result<LiquidationFeeParams<Self::Num>> {
544        Ok(LiquidationFeeParams::builder()
545            .factor(self.config.liquidation_fee_factor)
546            .receiver_factor(self.config.liquidation_fee_receiver_factor)
547            .build())
548    }
549}
550
551impl gmsol_model::LiquidityMarket<{ constants::MARKET_DECIMALS }> for MarketModel {
552    fn total_supply(&self) -> Self::Num {
553        u128::from(self.supply)
554    }
555
556    fn max_pool_value_for_deposit(&self, is_long_token: bool) -> gmsol_model::Result<Self::Num> {
557        if is_long_token {
558            Ok(self.config.max_pool_value_for_deposit_for_long_token)
559        } else {
560            Ok(self.config.max_pool_value_for_deposit_for_short_token)
561        }
562    }
563}
564
565impl gmsol_model::Bank<Pubkey> for MarketModel {
566    type Num = u64;
567
568    fn record_transferred_in_by_token<Q: ?Sized + Borrow<Pubkey>>(
569        &mut self,
570        token: &Q,
571        amount: &Self::Num,
572    ) -> gmsol_model::Result<()> {
573        let is_long_token = self.market.meta.token_side(token.borrow())?;
574        self.record_transferred_in(is_long_token, *amount)?;
575        Ok(())
576    }
577
578    fn record_transferred_out_by_token<Q: ?Sized + Borrow<Pubkey>>(
579        &mut self,
580        token: &Q,
581        amount: &Self::Num,
582    ) -> gmsol_model::Result<()> {
583        let is_long_token = self.market.meta.token_side(token.borrow())?;
584        self.record_transferred_out(is_long_token, *amount)?;
585        Ok(())
586    }
587
588    fn balance<Q: Borrow<Pubkey> + ?Sized>(&self, token: &Q) -> gmsol_model::Result<Self::Num> {
589        let side = self.market.meta.token_side(token.borrow())?;
590        Ok(self.balance_for_token(side))
591    }
592}
593
594impl gmsol_model::BaseMarketMut<{ constants::MARKET_DECIMALS }> for MarketModel {
595    fn liquidity_pool_mut(&mut self) -> gmsol_model::Result<&mut Self::Pool> {
596        self.make_market_mut().try_pool_mut(PoolKind::Primary)
597    }
598
599    fn claimable_fee_pool_mut(&mut self) -> gmsol_model::Result<&mut Self::Pool> {
600        self.make_market_mut().try_pool_mut(PoolKind::ClaimableFee)
601    }
602
603    fn virtual_inventory_for_swaps_pool_mut(
604        &mut self,
605    ) -> gmsol_model::Result<Option<impl DerefMut<Target = Self::Pool>>> {
606        Ok(None::<&mut Self::Pool>)
607    }
608}
609
610impl gmsol_model::SwapMarketMut<{ constants::MARKET_DECIMALS }> for MarketModel {
611    fn swap_impact_pool_mut(&mut self) -> gmsol_model::Result<&mut Self::Pool> {
612        self.make_market_mut().try_pool_mut(PoolKind::SwapImpact)
613    }
614}
615
616impl gmsol_model::PositionImpactMarketMut<{ constants::MARKET_DECIMALS }> for MarketModel {
617    fn position_impact_pool_mut(&mut self) -> gmsol_model::Result<&mut Self::Pool> {
618        self.make_market_mut()
619            .try_pool_mut(PoolKind::PositionImpact)
620    }
621
622    fn just_passed_in_seconds_for_position_impact_distribution(
623        &mut self,
624    ) -> gmsol_model::Result<u64> {
625        AsClockMut::from(
626            &mut self
627                .make_market_mut()
628                .state
629                .clocks
630                .price_impact_distribution,
631        )
632        .just_passed_in_seconds()
633    }
634}
635
636impl gmsol_model::PerpMarketMut<{ constants::MARKET_DECIMALS }> for MarketModel {
637    fn just_passed_in_seconds_for_funding(&mut self) -> gmsol_model::Result<u64> {
638        AsClockMut::from(&mut self.make_market_mut().state.clocks.funding).just_passed_in_seconds()
639    }
640
641    fn funding_factor_per_second_mut(&mut self) -> &mut Self::Signed {
642        &mut self.make_market_mut().state.other.funding_factor_per_second
643    }
644
645    fn open_interest_pool_mut(&mut self, is_long: bool) -> gmsol_model::Result<&mut Self::Pool> {
646        self.make_market_mut().try_pool_mut(if is_long {
647            PoolKind::OpenInterestForLong
648        } else {
649            PoolKind::OpenInterestForShort
650        })
651    }
652
653    fn open_interest_in_tokens_pool_mut(
654        &mut self,
655        is_long: bool,
656    ) -> gmsol_model::Result<&mut Self::Pool> {
657        self.make_market_mut().try_pool_mut(if is_long {
658            PoolKind::OpenInterestInTokensForLong
659        } else {
660            PoolKind::OpenInterestInTokensForShort
661        })
662    }
663
664    fn funding_amount_per_size_pool_mut(
665        &mut self,
666        is_long: bool,
667    ) -> gmsol_model::Result<&mut Self::Pool> {
668        self.make_market_mut().try_pool_mut(if is_long {
669            PoolKind::FundingAmountPerSizeForLong
670        } else {
671            PoolKind::FundingAmountPerSizeForShort
672        })
673    }
674
675    fn claimable_funding_amount_per_size_pool_mut(
676        &mut self,
677        is_long: bool,
678    ) -> gmsol_model::Result<&mut Self::Pool> {
679        self.make_market_mut().try_pool_mut(if is_long {
680            PoolKind::ClaimableFundingAmountPerSizeForLong
681        } else {
682            PoolKind::ClaimableFundingAmountPerSizeForShort
683        })
684    }
685
686    fn collateral_sum_pool_mut(&mut self, is_long: bool) -> gmsol_model::Result<&mut Self::Pool> {
687        self.make_market_mut().try_pool_mut(if is_long {
688            PoolKind::CollateralSumForLong
689        } else {
690            PoolKind::CollateralSumForShort
691        })
692    }
693
694    fn total_borrowing_pool_mut(&mut self) -> gmsol_model::Result<&mut Self::Pool> {
695        self.make_market_mut()
696            .try_pool_mut(PoolKind::TotalBorrowing)
697    }
698
699    fn virtual_inventory_for_positions_pool_mut(
700        &mut self,
701    ) -> gmsol_model::Result<Option<impl DerefMut<Target = Self::Pool>>> {
702        Ok(None::<&mut Self::Pool>)
703    }
704}
705
706impl gmsol_model::LiquidityMarketMut<{ constants::MARKET_DECIMALS }> for MarketModel {
707    fn mint(&mut self, amount: &Self::Num) -> gmsol_model::Result<()> {
708        let new_mint: u64 = (*amount)
709            .try_into()
710            .map_err(|_| gmsol_model::Error::Overflow)?;
711        let new_supply = self
712            .supply
713            .checked_add(new_mint)
714            .ok_or(gmsol_model::Error::Overflow)?;
715        self.supply = new_supply;
716        Ok(())
717    }
718
719    fn burn(&mut self, amount: &Self::Num) -> gmsol_model::Result<()> {
720        let new_burn: u64 = (*amount)
721            .try_into()
722            .map_err(|_| gmsol_model::Error::Overflow)?;
723        let new_supply = self
724            .supply
725            .checked_sub(new_burn)
726            .ok_or(gmsol_model::Error::Overflow)?;
727        self.supply = new_supply;
728        Ok(())
729    }
730}