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