gmsol_store/states/market/
model.rs1use 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
314pub struct AsLiquidityMarket<'a, M> {
316 market: &'a M,
317 mint: &'a Mint,
318}
319
320impl<'a, M> AsLiquidityMarket<'a, M> {
321 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}