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 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#[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 pub fn from_parts(market: Arc<Market>, supply: u64) -> Self {
164 Self { market, supply }
165 }
166
167 pub fn is_pure(&self) -> bool {
169 self.market.flag(MarketFlag::Pure)
170 }
171
172 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 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 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}