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 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#[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 pub fn from_parts(market: Arc<Market>, supply: u64) -> Self {
170 Self { market, supply }
171 }
172
173 pub fn is_pure(&self) -> bool {
175 self.market.flag(MarketFlag::Pure)
176 }
177
178 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 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 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 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}