gmsol_store/states/market/
model.rs

1use anchor_spl::token::Mint;
2use gmsol_model::{
3    params::{
4        fee::{
5            BorrowingFeeKinkModelParams, BorrowingFeeKinkModelParamsForOneSide, BorrowingFeeParams,
6            FundingFeeParams, LiquidationFeeParams,
7        },
8        position::PositionImpactDistributionParams,
9        FeeParams, PositionParams, PriceImpactParams,
10    },
11    PoolKind,
12};
13
14use crate::constants;
15
16use super::{clock::AsClock, config::MarketConfigFlag, HasMarketMeta, Market, Pool};
17
18impl gmsol_model::BaseMarket<{ constants::MARKET_DECIMALS }> for Market {
19    type Num = u128;
20
21    type Signed = i128;
22
23    type Pool = Pool;
24
25    fn liquidity_pool(&self) -> gmsol_model::Result<&Self::Pool> {
26        self.try_pool(PoolKind::Primary)
27    }
28
29    fn claimable_fee_pool(&self) -> gmsol_model::Result<&Self::Pool> {
30        self.try_pool(PoolKind::ClaimableFee)
31    }
32
33    fn swap_impact_pool(&self) -> gmsol_model::Result<&Self::Pool> {
34        self.try_pool(PoolKind::SwapImpact)
35    }
36
37    fn open_interest_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
38        self.try_pool(if is_long {
39            PoolKind::OpenInterestForLong
40        } else {
41            PoolKind::OpenInterestForShort
42        })
43    }
44
45    fn open_interest_in_tokens_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
46        self.try_pool(if is_long {
47            PoolKind::OpenInterestInTokensForLong
48        } else {
49            PoolKind::OpenInterestInTokensForShort
50        })
51    }
52
53    fn collateral_sum_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
54        let kind = if is_long {
55            PoolKind::CollateralSumForLong
56        } else {
57            PoolKind::CollateralSumForShort
58        };
59        self.try_pool(kind)
60    }
61
62    fn usd_to_amount_divisor(&self) -> Self::Num {
63        constants::MARKET_USD_TO_AMOUNT_DIVISOR
64    }
65
66    fn max_pool_amount(&self, is_long_token: bool) -> gmsol_model::Result<Self::Num> {
67        if is_long_token {
68            Ok(self.config.max_pool_amount_for_long_token)
69        } else {
70            Ok(self.config.max_pool_amount_for_short_token)
71        }
72    }
73
74    fn pnl_factor_config(
75        &self,
76        kind: gmsol_model::PnlFactorKind,
77        is_long: bool,
78    ) -> gmsol_model::Result<Self::Num> {
79        use gmsol_model::PnlFactorKind;
80
81        match (kind, is_long) {
82            (PnlFactorKind::MaxAfterDeposit, true) => {
83                Ok(self.config.max_pnl_factor_for_long_deposit)
84            }
85            (PnlFactorKind::MaxAfterDeposit, false) => {
86                Ok(self.config.max_pnl_factor_for_short_deposit)
87            }
88            (PnlFactorKind::MaxAfterWithdrawal, true) => {
89                Ok(self.config.max_pnl_factor_for_long_withdrawal)
90            }
91            (PnlFactorKind::MaxAfterWithdrawal, false) => {
92                Ok(self.config.max_pnl_factor_for_short_withdrawal)
93            }
94            (PnlFactorKind::MaxForTrader, true) => Ok(self.config.max_pnl_factor_for_long_trader),
95            (PnlFactorKind::MaxForTrader, false) => Ok(self.config.max_pnl_factor_for_short_trader),
96            (PnlFactorKind::ForAdl, true) => Ok(self.config.max_pnl_factor_for_long_adl),
97            (PnlFactorKind::ForAdl, false) => Ok(self.config.max_pnl_factor_for_short_adl),
98            (PnlFactorKind::MinAfterAdl, true) => Ok(self.config.min_pnl_factor_after_long_adl),
99            (PnlFactorKind::MinAfterAdl, false) => Ok(self.config.min_pnl_factor_after_short_adl),
100            _ => Err(gmsol_model::Error::InvalidArgument("missing pnl factor")),
101        }
102    }
103
104    fn reserve_factor(&self) -> gmsol_model::Result<Self::Num> {
105        Ok(self.config.reserve_factor)
106    }
107
108    fn open_interest_reserve_factor(&self) -> gmsol_model::Result<Self::Num> {
109        Ok(self.config.open_interest_reserve_factor)
110    }
111
112    fn max_open_interest(&self, is_long: bool) -> gmsol_model::Result<Self::Num> {
113        if is_long {
114            Ok(self.config.max_open_interest_for_long)
115        } else {
116            Ok(self.config.max_open_interest_for_short)
117        }
118    }
119
120    fn ignore_open_interest_for_usage_factor(&self) -> gmsol_model::Result<bool> {
121        Ok(self
122            .config
123            .flag(MarketConfigFlag::IgnoreOpenInterestForUsageFactor))
124    }
125}
126
127impl gmsol_model::SwapMarket<{ constants::MARKET_DECIMALS }> for Market {
128    fn swap_impact_params(&self) -> gmsol_model::Result<PriceImpactParams<Self::Num>> {
129        Ok(PriceImpactParams::builder()
130            .exponent(self.config.swap_impact_exponent)
131            .positive_factor(self.config.swap_impact_positive_factor)
132            .negative_factor(self.config.swap_impact_negative_factor)
133            .build())
134    }
135
136    fn swap_fee_params(&self) -> gmsol_model::Result<FeeParams<Self::Num>> {
137        Ok(FeeParams::builder()
138            .fee_receiver_factor(self.config.swap_fee_receiver_factor)
139            .positive_impact_fee_factor(self.config.swap_fee_factor_for_positive_impact)
140            .negative_impact_fee_factor(self.config.swap_fee_factor_for_negative_impact)
141            .build())
142    }
143}
144
145impl gmsol_model::PositionImpactMarket<{ constants::MARKET_DECIMALS }> for Market {
146    fn position_impact_pool(&self) -> gmsol_model::Result<&Self::Pool> {
147        self.try_pool(PoolKind::PositionImpact)
148    }
149
150    fn position_impact_params(&self) -> gmsol_model::Result<PriceImpactParams<Self::Num>> {
151        let config = &self.config;
152        Ok(PriceImpactParams::builder()
153            .exponent(config.position_impact_exponent)
154            .positive_factor(config.position_impact_positive_factor)
155            .negative_factor(config.position_impact_negative_factor)
156            .build())
157    }
158
159    fn position_impact_distribution_params(
160        &self,
161    ) -> gmsol_model::Result<PositionImpactDistributionParams<Self::Num>> {
162        let config = &self.config;
163        Ok(PositionImpactDistributionParams::builder()
164            .distribute_factor(config.position_impact_distribute_factor)
165            .min_position_impact_pool_amount(config.min_position_impact_pool_amount)
166            .build())
167    }
168
169    fn passed_in_seconds_for_position_impact_distribution(&self) -> gmsol_model::Result<u64> {
170        AsClock::from(&self.clocks().price_impact_distribution).passed_in_seconds()
171    }
172}
173
174impl gmsol_model::BorrowingFeeMarket<{ constants::MARKET_DECIMALS }> for Market {
175    fn borrowing_factor_pool(&self) -> gmsol_model::Result<&Self::Pool> {
176        self.try_pool(PoolKind::BorrowingFactor)
177    }
178
179    fn total_borrowing_pool(&self) -> gmsol_model::Result<&Self::Pool> {
180        self.try_pool(PoolKind::TotalBorrowing)
181    }
182
183    fn borrowing_fee_params(&self) -> gmsol_model::Result<BorrowingFeeParams<Self::Num>> {
184        Ok(BorrowingFeeParams::builder()
185            .receiver_factor(self.config.borrowing_fee_receiver_factor)
186            .factor_for_long(self.config.borrowing_fee_factor_for_long)
187            .factor_for_short(self.config.borrowing_fee_factor_for_short)
188            .exponent_for_long(self.config.borrowing_fee_exponent_for_long)
189            .exponent_for_short(self.config.borrowing_fee_exponent_for_short)
190            .skip_borrowing_fee_for_smaller_side(
191                self.config
192                    .flag(MarketConfigFlag::SkipBorrowingFeeForSmallerSide),
193            )
194            .build())
195    }
196
197    fn passed_in_seconds_for_borrowing(&self) -> gmsol_model::Result<u64> {
198        AsClock::from(&self.clocks().borrowing).passed_in_seconds()
199    }
200
201    fn borrowing_fee_kink_model_params(
202        &self,
203    ) -> gmsol_model::Result<BorrowingFeeKinkModelParams<Self::Num>> {
204        Ok(BorrowingFeeKinkModelParams::builder()
205            .long(
206                BorrowingFeeKinkModelParamsForOneSide::builder()
207                    .optimal_usage_factor(self.config.borrowing_fee_optimal_usage_factor_for_long)
208                    .base_borrowing_factor(self.config.borrowing_fee_base_factor_for_long)
209                    .above_optimal_usage_borrowing_factor(
210                        self.config
211                            .borrowing_fee_above_optimal_usage_factor_for_long,
212                    )
213                    .build(),
214            )
215            .short(
216                BorrowingFeeKinkModelParamsForOneSide::builder()
217                    .optimal_usage_factor(self.config.borrowing_fee_optimal_usage_factor_for_short)
218                    .base_borrowing_factor(self.config.borrowing_fee_base_factor_for_short)
219                    .above_optimal_usage_borrowing_factor(
220                        self.config
221                            .borrowing_fee_above_optimal_usage_factor_for_short,
222                    )
223                    .build(),
224            )
225            .build())
226    }
227}
228
229impl gmsol_model::PerpMarket<{ constants::MARKET_DECIMALS }> for Market {
230    fn funding_factor_per_second(&self) -> &Self::Signed {
231        &self.state().funding_factor_per_second
232    }
233
234    fn funding_amount_per_size_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
235        let kind = if is_long {
236            PoolKind::FundingAmountPerSizeForLong
237        } else {
238            PoolKind::FundingAmountPerSizeForShort
239        };
240        self.try_pool(kind)
241    }
242
243    fn claimable_funding_amount_per_size_pool(
244        &self,
245        is_long: bool,
246    ) -> gmsol_model::Result<&Self::Pool> {
247        let kind = if is_long {
248            PoolKind::ClaimableFundingAmountPerSizeForLong
249        } else {
250            PoolKind::ClaimableFundingAmountPerSizeForShort
251        };
252        self.try_pool(kind)
253    }
254
255    fn funding_amount_per_size_adjustment(&self) -> Self::Num {
256        constants::FUNDING_AMOUNT_PER_SIZE_ADJUSTMENT
257    }
258
259    fn funding_fee_params(&self) -> gmsol_model::Result<FundingFeeParams<Self::Num>> {
260        Ok(FundingFeeParams::builder()
261            .exponent(self.config.funding_fee_exponent)
262            .funding_factor(self.config.funding_fee_factor)
263            .max_factor_per_second(self.config.funding_fee_max_factor_per_second)
264            .min_factor_per_second(self.config.funding_fee_min_factor_per_second)
265            .increase_factor_per_second(self.config.funding_fee_increase_factor_per_second)
266            .decrease_factor_per_second(self.config.funding_fee_decrease_factor_per_second)
267            .threshold_for_stable_funding(self.config.funding_fee_threshold_for_stable_funding)
268            .threshold_for_decrease_funding(self.config.funding_fee_threshold_for_decrease_funding)
269            .build())
270    }
271
272    fn position_params(&self) -> gmsol_model::Result<PositionParams<Self::Num>> {
273        Ok(PositionParams::new(
274            self.config.min_position_size_usd,
275            self.config.min_collateral_value,
276            self.config.min_collateral_factor,
277            self.config.max_positive_position_impact_factor,
278            self.config.max_negative_position_impact_factor,
279            self.config.max_position_impact_factor_for_liquidations,
280        ))
281    }
282
283    fn order_fee_params(&self) -> gmsol_model::Result<FeeParams<Self::Num>> {
284        Ok(FeeParams::builder()
285            .fee_receiver_factor(self.config.order_fee_receiver_factor)
286            .positive_impact_fee_factor(self.config.order_fee_factor_for_positive_impact)
287            .negative_impact_fee_factor(self.config.order_fee_factor_for_negative_impact)
288            .build())
289    }
290
291    fn min_collateral_factor_for_open_interest_multiplier(
292        &self,
293        is_long: bool,
294    ) -> gmsol_model::Result<Self::Num> {
295        if is_long {
296            Ok(self
297                .config
298                .min_collateral_factor_for_open_interest_multiplier_for_long)
299        } else {
300            Ok(self
301                .config
302                .min_collateral_factor_for_open_interest_multiplier_for_short)
303        }
304    }
305
306    fn liquidation_fee_params(&self) -> gmsol_model::Result<LiquidationFeeParams<Self::Num>> {
307        Ok(LiquidationFeeParams::builder()
308            .factor(self.config.liquidation_fee_factor)
309            .receiver_factor(self.config.liquidation_fee_receiver_factor)
310            .build())
311    }
312}
313
314/// As a liquidity market.
315pub struct AsLiquidityMarket<'a, M> {
316    market: &'a M,
317    mint: &'a Mint,
318}
319
320impl<'a, M> AsLiquidityMarket<'a, M> {
321    /// Create a new [`AsLiquidityMarket`].
322    pub fn new(market: &'a M, market_token: &'a Mint) -> Self {
323        Self {
324            market,
325            mint: market_token,
326        }
327    }
328}
329
330impl<M> HasMarketMeta for AsLiquidityMarket<'_, M>
331where
332    M: AsRef<Market>,
333{
334    fn is_pure(&self) -> bool {
335        self.market.as_ref().is_pure()
336    }
337
338    fn market_meta(&self) -> &super::MarketMeta {
339        self.market.as_ref().market_meta()
340    }
341}
342
343impl<M> gmsol_model::BaseMarket<{ constants::MARKET_DECIMALS }> for AsLiquidityMarket<'_, M>
344where
345    M: gmsol_model::BaseMarket<
346        { constants::MARKET_DECIMALS },
347        Num = u128,
348        Signed = i128,
349        Pool = Pool,
350    >,
351{
352    type Num = u128;
353
354    type Signed = i128;
355
356    type Pool = Pool;
357
358    fn liquidity_pool(&self) -> gmsol_model::Result<&Self::Pool> {
359        self.market.liquidity_pool()
360    }
361
362    fn claimable_fee_pool(&self) -> gmsol_model::Result<&Self::Pool> {
363        self.market.claimable_fee_pool()
364    }
365
366    fn swap_impact_pool(&self) -> gmsol_model::Result<&Self::Pool> {
367        self.market.swap_impact_pool()
368    }
369
370    fn open_interest_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
371        self.market.open_interest_pool(is_long)
372    }
373
374    fn open_interest_in_tokens_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
375        self.market.open_interest_in_tokens_pool(is_long)
376    }
377
378    fn collateral_sum_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
379        self.market.collateral_sum_pool(is_long)
380    }
381
382    fn usd_to_amount_divisor(&self) -> Self::Num {
383        self.market.usd_to_amount_divisor()
384    }
385
386    fn max_pool_amount(&self, is_long_token: bool) -> gmsol_model::Result<Self::Num> {
387        self.market.max_pool_amount(is_long_token)
388    }
389
390    fn pnl_factor_config(
391        &self,
392        kind: gmsol_model::PnlFactorKind,
393        is_long: bool,
394    ) -> gmsol_model::Result<Self::Num> {
395        self.market.pnl_factor_config(kind, is_long)
396    }
397
398    fn reserve_factor(&self) -> gmsol_model::Result<Self::Num> {
399        self.market.reserve_factor()
400    }
401
402    fn open_interest_reserve_factor(&self) -> gmsol_model::Result<Self::Num> {
403        self.market.open_interest_reserve_factor()
404    }
405
406    fn max_open_interest(&self, is_long: bool) -> gmsol_model::Result<Self::Num> {
407        self.market.max_open_interest(is_long)
408    }
409
410    fn ignore_open_interest_for_usage_factor(&self) -> gmsol_model::Result<bool> {
411        self.market.ignore_open_interest_for_usage_factor()
412    }
413}
414
415impl<M> gmsol_model::PositionImpactMarket<{ constants::MARKET_DECIMALS }>
416    for AsLiquidityMarket<'_, M>
417where
418    M: gmsol_model::PositionImpactMarket<
419        { constants::MARKET_DECIMALS },
420        Num = u128,
421        Signed = i128,
422        Pool = Pool,
423    >,
424{
425    fn position_impact_pool(&self) -> gmsol_model::Result<&Self::Pool> {
426        self.market.position_impact_pool()
427    }
428
429    fn position_impact_params(&self) -> gmsol_model::Result<PriceImpactParams<Self::Num>> {
430        self.market.position_impact_params()
431    }
432
433    fn position_impact_distribution_params(
434        &self,
435    ) -> gmsol_model::Result<PositionImpactDistributionParams<Self::Num>> {
436        self.market.position_impact_distribution_params()
437    }
438
439    fn passed_in_seconds_for_position_impact_distribution(&self) -> gmsol_model::Result<u64> {
440        self.market
441            .passed_in_seconds_for_position_impact_distribution()
442    }
443}
444
445impl<M> gmsol_model::BorrowingFeeMarket<{ constants::MARKET_DECIMALS }> for AsLiquidityMarket<'_, M>
446where
447    M: gmsol_model::BorrowingFeeMarket<
448        { constants::MARKET_DECIMALS },
449        Num = u128,
450        Signed = i128,
451        Pool = Pool,
452    >,
453{
454    fn borrowing_factor_pool(&self) -> gmsol_model::Result<&Self::Pool> {
455        self.market.borrowing_factor_pool()
456    }
457
458    fn total_borrowing_pool(&self) -> gmsol_model::Result<&Self::Pool> {
459        self.market.total_borrowing_pool()
460    }
461
462    fn borrowing_fee_params(&self) -> gmsol_model::Result<BorrowingFeeParams<Self::Num>> {
463        self.market.borrowing_fee_params()
464    }
465
466    fn passed_in_seconds_for_borrowing(&self) -> gmsol_model::Result<u64> {
467        self.market.passed_in_seconds_for_borrowing()
468    }
469
470    fn borrowing_fee_kink_model_params(
471        &self,
472    ) -> gmsol_model::Result<gmsol_model::params::fee::BorrowingFeeKinkModelParams<Self::Num>> {
473        self.market.borrowing_fee_kink_model_params()
474    }
475}
476
477impl<M> gmsol_model::LiquidityMarket<{ constants::MARKET_DECIMALS }> for AsLiquidityMarket<'_, M>
478where
479    M: gmsol_model::BorrowingFeeMarket<
480        { constants::MARKET_DECIMALS },
481        Num = u128,
482        Signed = i128,
483        Pool = Pool,
484    >,
485    M: gmsol_model::PositionImpactMarket<
486        { constants::MARKET_DECIMALS },
487        Num = u128,
488        Signed = i128,
489        Pool = Pool,
490    >,
491    M: AsRef<Market>,
492{
493    fn total_supply(&self) -> Self::Num {
494        self.mint.supply.into()
495    }
496
497    fn max_pool_value_for_deposit(&self, is_long_token: bool) -> gmsol_model::Result<Self::Num> {
498        self.market
499            .as_ref()
500            .max_pool_value_for_deposit(is_long_token)
501    }
502}