gmsol_store/states/market/revertible/
market.rs

1use std::{borrow::Borrow, cell::RefMut};
2
3use anchor_lang::prelude::*;
4use gmsol_model::{
5    params::{
6        fee::{BorrowingFeeParams, FundingFeeParams},
7        position::PositionImpactDistributionParams,
8        FeeParams, PositionParams, PriceImpactParams,
9    },
10    PoolKind,
11};
12
13use crate::{
14    constants, debug_msg,
15    events::EventEmitter,
16    states::{
17        market::{
18            clock::{AsClock, AsClockMut},
19            Clocks, Pool,
20        },
21        Factor, HasMarketMeta, Market, MarketMeta, OtherState,
22    },
23    CoreError,
24};
25
26use super::{Revertible, Revision};
27
28/// Swap Pricing Kind.
29#[derive(Clone, Copy)]
30pub enum SwapPricingKind {
31    /// Swap.
32    Swap,
33    /// Deposit.
34    Deposit,
35    /// Withdrawal.
36    Withdrawal,
37    /// Shift.
38    Shift,
39}
40
41/// Revertible Market.
42pub struct RevertibleMarket<'a, 'info> {
43    pub(super) market: RefMut<'a, Market>,
44    order_fee_discount_factor: u128,
45    event_emitter: EventEmitter<'a, 'info>,
46    swap_pricing: SwapPricingKind,
47}
48
49impl Key for RevertibleMarket<'_, '_> {
50    fn key(&self) -> Pubkey {
51        self.market.meta.market_token_mint
52    }
53}
54
55impl Revision for RevertibleMarket<'_, '_> {
56    fn rev(&self) -> u64 {
57        self.market.buffer.rev()
58    }
59}
60
61impl AsRef<Market> for RevertibleMarket<'_, '_> {
62    fn as_ref(&self) -> &Market {
63        &self.market
64    }
65}
66
67impl<'a, 'info> RevertibleMarket<'a, 'info> {
68    pub(crate) fn new(
69        market: &'a AccountLoader<'info, Market>,
70        event_emitter: EventEmitter<'a, 'info>,
71    ) -> Result<Self> {
72        let mut market = market.load_mut()?;
73        market.buffer.start_revertible_operation();
74        Ok(Self {
75            market,
76            order_fee_discount_factor: 0,
77            event_emitter,
78            swap_pricing: SwapPricingKind::Swap,
79        })
80    }
81
82    pub(crate) fn with_order_fee_discount_factor(mut self, discount: u128) -> Self {
83        self.order_fee_discount_factor = discount;
84        self
85    }
86
87    pub(crate) fn set_swap_pricing_kind(&mut self, kind: SwapPricingKind) {
88        self.swap_pricing = kind;
89    }
90
91    pub(crate) fn event_emitter(&self) -> &EventEmitter<'a, 'info> {
92        &self.event_emitter
93    }
94
95    fn pool(&self, kind: PoolKind) -> gmsol_model::Result<&Pool> {
96        let Market { state, buffer, .. } = &*self.market;
97        buffer
98            .pool(kind, state)
99            .ok_or(gmsol_model::Error::MissingPoolKind(kind))
100    }
101
102    fn pool_mut(&mut self, kind: PoolKind) -> gmsol_model::Result<&mut Pool> {
103        let Market { state, buffer, .. } = &mut *self.market;
104        buffer
105            .pool_mut(kind, state)
106            .ok_or(gmsol_model::Error::MissingPoolKind(kind))
107    }
108
109    fn other(&self) -> &OtherState {
110        let Market { state, buffer, .. } = &*self.market;
111        buffer.other(state)
112    }
113
114    fn other_mut(&mut self) -> &mut OtherState {
115        let Market { state, buffer, .. } = &mut *self.market;
116        buffer.other_mut(state)
117    }
118
119    fn clocks(&self) -> &Clocks {
120        let Market { state, buffer, .. } = &*self.market;
121        buffer.clocks(state)
122    }
123
124    fn clocks_mut(&mut self) -> &mut Clocks {
125        let Market { state, buffer, .. } = &mut *self.market;
126        buffer.clocks_mut(state)
127    }
128
129    fn balance_for_token(&self, is_long_token: bool) -> u64 {
130        let other = self.other();
131        if is_long_token || self.market.is_pure() {
132            other.long_token_balance
133        } else {
134            other.short_token_balance
135        }
136    }
137
138    /// Record transferred in.
139    fn record_transferred_in(&mut self, is_long_token: bool, amount: u64) -> Result<()> {
140        #[cfg(feature = "debug-msg")]
141        let mint = self.market.meta.market_token_mint;
142        let is_pure = self.market.is_pure();
143        let other = self.other_mut();
144
145        debug_msg!(
146            "[Balance updating] {}: {},{}(+{},{is_long_token})",
147            mint,
148            other.long_token_balance,
149            other.short_token_balance,
150            amount,
151        );
152
153        if is_pure || is_long_token {
154            other.long_token_balance = other
155                .long_token_balance
156                .checked_add(amount)
157                .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
158        } else {
159            other.short_token_balance = other
160                .short_token_balance
161                .checked_add(amount)
162                .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
163        }
164
165        debug_msg!(
166            "[Balance updated (to be committed)] {}: {},{}",
167            mint,
168            other.long_token_balance,
169            other.short_token_balance
170        );
171        Ok(())
172    }
173
174    /// Record transferred out.
175    fn record_transferred_out(&mut self, is_long_token: bool, amount: u64) -> Result<()> {
176        #[cfg(feature = "debug-msg")]
177        let mint = self.market.meta.market_token_mint;
178        let is_pure = self.market.is_pure();
179        let other = self.other_mut();
180
181        debug_msg!(
182            "[Balance updating] {}: {},{}(-{},{is_long_token})",
183            mint,
184            other.long_token_balance,
185            other.short_token_balance,
186            amount,
187        );
188
189        if is_pure || is_long_token {
190            other.long_token_balance = other
191                .long_token_balance
192                .checked_sub(amount)
193                .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
194        } else {
195            other.short_token_balance = other
196                .short_token_balance
197                .checked_sub(amount)
198                .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
199        }
200
201        debug_msg!(
202            "[Balance updated (to be committed)] {}: {},{}",
203            mint,
204            other.long_token_balance,
205            other.short_token_balance
206        );
207        Ok(())
208    }
209
210    /// Next trade id.
211    ///
212    /// This method is idempotent, meaning that multiple calls to it
213    /// result in the same state changes as a single call.
214    pub(crate) fn next_trade_id(&mut self) -> Result<u64> {
215        let next_trade_id = self
216            .market
217            .state
218            .other
219            .trade_count
220            .checked_add(1)
221            .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
222        self.other_mut().trade_count = next_trade_id;
223        Ok(next_trade_id)
224    }
225}
226
227impl Revertible for RevertibleMarket<'_, '_> {
228    fn commit(mut self) {
229        let Market {
230            meta,
231            state,
232            buffer,
233            ..
234        } = &mut *self.market;
235        buffer.commit_to_storage(state, &meta.market_token_mint, &self.event_emitter);
236        debug_msg!(
237            "[Balance committed] {}: {},{}",
238            meta.market_token_mint,
239            state.other.long_token_balance,
240            state.other.short_token_balance
241        );
242    }
243}
244
245impl HasMarketMeta for RevertibleMarket<'_, '_> {
246    fn is_pure(&self) -> bool {
247        self.market.is_pure()
248    }
249    fn market_meta(&self) -> &MarketMeta {
250        self.market.market_meta()
251    }
252}
253
254impl gmsol_model::Bank<Pubkey> for RevertibleMarket<'_, '_> {
255    type Num = u64;
256
257    fn record_transferred_in_by_token<Q: ?Sized + Borrow<Pubkey>>(
258        &mut self,
259        token: &Q,
260        amount: &Self::Num,
261    ) -> gmsol_model::Result<()> {
262        let is_long_token = self.market.meta.to_token_side(token.borrow())?;
263        self.record_transferred_in(is_long_token, *amount)?;
264        Ok(())
265    }
266
267    fn record_transferred_out_by_token<Q: ?Sized + Borrow<Pubkey>>(
268        &mut self,
269        token: &Q,
270        amount: &Self::Num,
271    ) -> gmsol_model::Result<()> {
272        let is_long_token = self.market.meta.to_token_side(token.borrow())?;
273        self.record_transferred_out(is_long_token, *amount)?;
274        Ok(())
275    }
276
277    fn balance<Q: Borrow<Pubkey> + ?Sized>(&self, token: &Q) -> gmsol_model::Result<Self::Num> {
278        let side = self.market.meta.to_token_side(token.borrow())?;
279        Ok(self.balance_for_token(side))
280    }
281}
282
283impl gmsol_model::BaseMarket<{ constants::MARKET_DECIMALS }> for RevertibleMarket<'_, '_> {
284    type Num = u128;
285
286    type Signed = i128;
287
288    type Pool = Pool;
289
290    fn liquidity_pool(&self) -> gmsol_model::Result<&Self::Pool> {
291        self.pool(PoolKind::Primary)
292    }
293
294    fn claimable_fee_pool(&self) -> gmsol_model::Result<&Self::Pool> {
295        self.pool(PoolKind::ClaimableFee)
296    }
297
298    fn swap_impact_pool(&self) -> gmsol_model::Result<&Self::Pool> {
299        self.pool(PoolKind::SwapImpact)
300    }
301
302    fn open_interest_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
303        self.pool(if is_long {
304            PoolKind::OpenInterestForLong
305        } else {
306            PoolKind::OpenInterestForShort
307        })
308    }
309
310    fn open_interest_in_tokens_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
311        self.pool(if is_long {
312            PoolKind::OpenInterestInTokensForLong
313        } else {
314            PoolKind::OpenInterestInTokensForShort
315        })
316    }
317
318    fn collateral_sum_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
319        self.pool(if is_long {
320            PoolKind::CollateralSumForLong
321        } else {
322            PoolKind::CollateralSumForShort
323        })
324    }
325
326    fn usd_to_amount_divisor(&self) -> Self::Num {
327        self.market.usd_to_amount_divisor()
328    }
329
330    fn max_pool_amount(&self, is_long_token: bool) -> gmsol_model::Result<Self::Num> {
331        self.market.max_pool_amount(is_long_token)
332    }
333
334    fn pnl_factor_config(
335        &self,
336        kind: gmsol_model::PnlFactorKind,
337        is_long: bool,
338    ) -> gmsol_model::Result<Self::Num> {
339        self.market.pnl_factor_config(kind, is_long)
340    }
341
342    fn reserve_factor(&self) -> gmsol_model::Result<Self::Num> {
343        self.market.reserve_factor()
344    }
345
346    fn open_interest_reserve_factor(&self) -> gmsol_model::Result<Self::Num> {
347        self.market.open_interest_reserve_factor()
348    }
349
350    fn max_open_interest(&self, is_long: bool) -> gmsol_model::Result<Self::Num> {
351        self.market.max_open_interest(is_long)
352    }
353
354    fn ignore_open_interest_for_usage_factor(&self) -> gmsol_model::Result<bool> {
355        self.market.ignore_open_interest_for_usage_factor()
356    }
357}
358
359impl gmsol_model::BaseMarketMut<{ constants::MARKET_DECIMALS }> for RevertibleMarket<'_, '_> {
360    fn liquidity_pool_mut(&mut self) -> gmsol_model::Result<&mut Self::Pool> {
361        self.pool_mut(PoolKind::Primary)
362    }
363
364    fn claimable_fee_pool_mut(&mut self) -> gmsol_model::Result<&mut Self::Pool> {
365        self.pool_mut(PoolKind::ClaimableFee)
366    }
367}
368
369impl gmsol_model::SwapMarket<{ constants::MARKET_DECIMALS }> for RevertibleMarket<'_, '_> {
370    fn swap_impact_params(&self) -> gmsol_model::Result<PriceImpactParams<Factor>> {
371        self.market.swap_impact_params()
372    }
373
374    fn swap_fee_params(&self) -> gmsol_model::Result<FeeParams<Factor>> {
375        match self.swap_pricing {
376            SwapPricingKind::Shift => {
377                let params = self.market.swap_fee_params()?;
378                Ok(FeeParams::builder()
379                    .fee_receiver_factor(*params.receiver_factor())
380                    .positive_impact_fee_factor(0)
381                    .negative_impact_fee_factor(0)
382                    .build())
383            }
384            SwapPricingKind::Swap => self.market.swap_fee_params(),
385            SwapPricingKind::Deposit | SwapPricingKind::Withdrawal => {
386                // We currently do not have separate swap fees params specifically
387                // for deposits and withdrawals.
388                self.market.swap_fee_params()
389            }
390        }
391    }
392}
393
394impl gmsol_model::SwapMarketMut<{ constants::MARKET_DECIMALS }> for RevertibleMarket<'_, '_> {
395    fn swap_impact_pool_mut(&mut self) -> gmsol_model::Result<&mut Self::Pool> {
396        self.pool_mut(PoolKind::SwapImpact)
397    }
398}
399
400impl gmsol_model::PositionImpactMarket<{ constants::MARKET_DECIMALS }>
401    for RevertibleMarket<'_, '_>
402{
403    fn position_impact_pool(&self) -> gmsol_model::Result<&Self::Pool> {
404        self.pool(PoolKind::PositionImpact)
405    }
406
407    fn position_impact_params(&self) -> gmsol_model::Result<PriceImpactParams<Self::Num>> {
408        self.market.position_impact_params()
409    }
410
411    fn position_impact_distribution_params(
412        &self,
413    ) -> gmsol_model::Result<PositionImpactDistributionParams<Self::Num>> {
414        self.market.position_impact_distribution_params()
415    }
416
417    fn passed_in_seconds_for_position_impact_distribution(&self) -> gmsol_model::Result<u64> {
418        AsClock::from(&self.clocks().price_impact_distribution).passed_in_seconds()
419    }
420}
421
422impl gmsol_model::PositionImpactMarketMut<{ constants::MARKET_DECIMALS }>
423    for RevertibleMarket<'_, '_>
424{
425    fn position_impact_pool_mut(&mut self) -> gmsol_model::Result<&mut Self::Pool> {
426        self.pool_mut(PoolKind::PositionImpact)
427    }
428
429    fn just_passed_in_seconds_for_position_impact_distribution(
430        &mut self,
431    ) -> gmsol_model::Result<u64> {
432        AsClockMut::from(&mut self.clocks_mut().price_impact_distribution).just_passed_in_seconds()
433    }
434}
435
436impl gmsol_model::BorrowingFeeMarket<{ constants::MARKET_DECIMALS }> for RevertibleMarket<'_, '_> {
437    fn borrowing_factor_pool(&self) -> gmsol_model::Result<&Self::Pool> {
438        self.pool(PoolKind::BorrowingFactor)
439    }
440
441    fn total_borrowing_pool(&self) -> gmsol_model::Result<&Self::Pool> {
442        self.pool(PoolKind::TotalBorrowing)
443    }
444
445    fn borrowing_fee_params(&self) -> gmsol_model::Result<BorrowingFeeParams<Self::Num>> {
446        self.market.borrowing_fee_params()
447    }
448
449    fn passed_in_seconds_for_borrowing(&self) -> gmsol_model::Result<u64> {
450        AsClock::from(&self.clocks().borrowing).passed_in_seconds()
451    }
452
453    fn borrowing_fee_kink_model_params(
454        &self,
455    ) -> gmsol_model::Result<gmsol_model::params::fee::BorrowingFeeKinkModelParams<Self::Num>> {
456        self.market.borrowing_fee_kink_model_params()
457    }
458}
459
460impl gmsol_model::PerpMarket<{ constants::MARKET_DECIMALS }> for RevertibleMarket<'_, '_> {
461    fn funding_factor_per_second(&self) -> &Self::Signed {
462        &self.other().funding_factor_per_second
463    }
464
465    fn funding_amount_per_size_pool(&self, is_long: bool) -> gmsol_model::Result<&Self::Pool> {
466        self.pool(if is_long {
467            PoolKind::FundingAmountPerSizeForLong
468        } else {
469            PoolKind::FundingAmountPerSizeForShort
470        })
471    }
472
473    fn claimable_funding_amount_per_size_pool(
474        &self,
475        is_long: bool,
476    ) -> gmsol_model::Result<&Self::Pool> {
477        self.pool(if is_long {
478            PoolKind::ClaimableFundingAmountPerSizeForLong
479        } else {
480            PoolKind::ClaimableFundingAmountPerSizeForShort
481        })
482    }
483
484    fn funding_amount_per_size_adjustment(&self) -> Self::Num {
485        self.market.funding_amount_per_size_adjustment()
486    }
487
488    fn funding_fee_params(&self) -> gmsol_model::Result<FundingFeeParams<Self::Num>> {
489        self.market.funding_fee_params()
490    }
491
492    fn position_params(&self) -> gmsol_model::Result<PositionParams<Self::Num>> {
493        self.market.position_params()
494    }
495
496    fn order_fee_params(&self) -> gmsol_model::Result<FeeParams<Self::Num>> {
497        let params = self.market.order_fee_params()?;
498        Ok(params.with_discount_factor(self.order_fee_discount_factor))
499    }
500
501    fn min_collateral_factor_for_open_interest_multiplier(
502        &self,
503        is_long: bool,
504    ) -> gmsol_model::Result<Self::Num> {
505        self.market
506            .min_collateral_factor_for_open_interest_multiplier(is_long)
507    }
508
509    fn liquidation_fee_params(
510        &self,
511    ) -> gmsol_model::Result<gmsol_model::params::fee::LiquidationFeeParams<Self::Num>> {
512        self.market.liquidation_fee_params()
513    }
514}
515
516impl gmsol_model::BorrowingFeeMarketMut<{ constants::MARKET_DECIMALS }>
517    for RevertibleMarket<'_, '_>
518{
519    fn just_passed_in_seconds_for_borrowing(&mut self) -> gmsol_model::Result<u64> {
520        AsClockMut::from(&mut self.clocks_mut().borrowing).just_passed_in_seconds()
521    }
522
523    fn borrowing_factor_pool_mut(&mut self) -> gmsol_model::Result<&mut Self::Pool> {
524        self.pool_mut(PoolKind::BorrowingFactor)
525    }
526}
527
528impl gmsol_model::PerpMarketMut<{ constants::MARKET_DECIMALS }> for RevertibleMarket<'_, '_> {
529    fn just_passed_in_seconds_for_funding(&mut self) -> gmsol_model::Result<u64> {
530        AsClockMut::from(&mut self.clocks_mut().funding).just_passed_in_seconds()
531    }
532
533    fn funding_factor_per_second_mut(&mut self) -> &mut Self::Signed {
534        &mut self.other_mut().funding_factor_per_second
535    }
536
537    fn open_interest_pool_mut(&mut self, is_long: bool) -> gmsol_model::Result<&mut Self::Pool> {
538        self.pool_mut(if is_long {
539            PoolKind::OpenInterestForLong
540        } else {
541            PoolKind::OpenInterestForShort
542        })
543    }
544
545    fn open_interest_in_tokens_pool_mut(
546        &mut self,
547        is_long: bool,
548    ) -> gmsol_model::Result<&mut Self::Pool> {
549        self.pool_mut(if is_long {
550            PoolKind::OpenInterestInTokensForLong
551        } else {
552            PoolKind::OpenInterestInTokensForShort
553        })
554    }
555
556    fn funding_amount_per_size_pool_mut(
557        &mut self,
558        is_long: bool,
559    ) -> gmsol_model::Result<&mut Self::Pool> {
560        self.pool_mut(if is_long {
561            PoolKind::FundingAmountPerSizeForLong
562        } else {
563            PoolKind::FundingAmountPerSizeForShort
564        })
565    }
566
567    fn claimable_funding_amount_per_size_pool_mut(
568        &mut self,
569        is_long: bool,
570    ) -> gmsol_model::Result<&mut Self::Pool> {
571        self.pool_mut(if is_long {
572            PoolKind::ClaimableFundingAmountPerSizeForLong
573        } else {
574            PoolKind::ClaimableFundingAmountPerSizeForShort
575        })
576    }
577
578    fn collateral_sum_pool_mut(&mut self, is_long: bool) -> gmsol_model::Result<&mut Self::Pool> {
579        self.pool_mut(if is_long {
580            PoolKind::CollateralSumForLong
581        } else {
582            PoolKind::CollateralSumForShort
583        })
584    }
585
586    fn total_borrowing_pool_mut(&mut self) -> gmsol_model::Result<&mut Self::Pool> {
587        self.pool_mut(PoolKind::TotalBorrowing)
588    }
589}