gmsol_programs/model/
market.rs

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