gmsol_store/ops/
market.rs

1use anchor_lang::prelude::*;
2use anchor_spl::token::{Mint, TokenAccount};
3use gmsol_model::{
4    price::Prices, Bank, BorrowingFeeMarketMutExt, LiquidityMarketMutExt, MarketAction,
5    PerpMarketMutExt, PositionImpactMarketMutExt,
6};
7use typed_builder::TypedBuilder;
8
9use crate::{
10    events::{DepositExecuted, EventEmitter, MarketFeesUpdated, WithdrawalExecuted},
11    states::{
12        common::swap::{SwapActionParams, SwapActionParamsExt},
13        deposit::DepositActionParams,
14        market::{
15            revertible::{
16                liquidity_market::RevertibleLiquidityMarket,
17                market::SwapPricingKind,
18                swap_market::{SwapDirection, SwapMarkets},
19                Revertible, RevertibleMarket, Revision,
20            },
21            utils::ValidateMarketBalances,
22            HasMarketMeta,
23        },
24        withdrawal::WithdrawalActionParams,
25        Deposit, Market, Oracle, ShiftActionParams, Store,
26    },
27    CoreError, ModelError,
28};
29
30/// Operation for transferring funds into market valut.
31#[derive(TypedBuilder)]
32pub(crate) struct MarketTransferInOperation<'a, 'info> {
33    store: &'a AccountLoader<'info, Store>,
34    market: &'a AccountLoader<'info, Market>,
35    from: AccountInfo<'info>,
36    from_authority: AccountInfo<'info>,
37    vault: &'a Account<'info, TokenAccount>,
38    amount: u64,
39    token_program: AccountInfo<'info>,
40    signer_seeds: &'a [&'a [u8]],
41    #[builder(setter(into))]
42    event_emitter: EventEmitter<'a, 'info>,
43}
44
45impl MarketTransferInOperation<'_, '_> {
46    pub(crate) fn execute(self) -> Result<()> {
47        use anchor_spl::token;
48
49        self.market.load()?.validate(&self.store.key())?;
50
51        let amount = self.amount;
52        if amount != 0 {
53            token::transfer(
54                CpiContext::new(
55                    self.token_program,
56                    token::Transfer {
57                        from: self.from,
58                        to: self.vault.to_account_info(),
59                        authority: self.from_authority,
60                    },
61                )
62                .with_signer(&[self.signer_seeds]),
63                amount,
64            )?;
65            let token = &self.vault.mint;
66            let mut market = RevertibleMarket::new(self.market, self.event_emitter)?;
67            market
68                .record_transferred_in_by_token(token, &amount)
69                .map_err(ModelError::from)?;
70            market.commit();
71        }
72
73        Ok(())
74    }
75}
76
77/// Operation for transferring funds out of market vault.
78#[derive(TypedBuilder)]
79pub(crate) struct MarketTransferOutOperation<'a, 'info> {
80    store: &'a AccountLoader<'info, Store>,
81    market: &'a AccountLoader<'info, Market>,
82    amount: u64,
83    decimals: u8,
84    to: AccountInfo<'info>,
85    token_mint: AccountInfo<'info>,
86    vault: AccountInfo<'info>,
87    token_program: AccountInfo<'info>,
88    #[builder(setter(into))]
89    event_emitter: EventEmitter<'a, 'info>,
90}
91
92impl MarketTransferOutOperation<'_, '_> {
93    pub(crate) fn execute(self) -> Result<()> {
94        use crate::utils::internal::TransferUtils;
95
96        {
97            let market = self.market.load()?;
98            let meta = market.validated_meta(&self.store.key())?;
99            require!(
100                meta.is_collateral_token(&self.token_mint.key()),
101                CoreError::InvalidCollateralToken
102            );
103        }
104
105        let amount = self.amount;
106        if amount != 0 {
107            let decimals = self.decimals;
108            TransferUtils::new(
109                self.token_program.to_account_info(),
110                self.store,
111                self.token_mint.to_account_info(),
112            )
113            .transfer_out(self.vault.to_account_info(), self.to, amount, decimals)?;
114            let token = &self.token_mint.key();
115            let mut market = RevertibleMarket::new(self.market, self.event_emitter)?;
116            market
117                .record_transferred_out_by_token(token, &amount)
118                .map_err(ModelError::from)?;
119            market.commit();
120        }
121
122        Ok(())
123    }
124}
125
126/// Revertible Liquidity Market Operation.
127pub struct RevertibleLiquidityMarketOperation<'a, 'info> {
128    store: &'a AccountLoader<'info, Store>,
129    oracle: &'a Oracle,
130    market: &'a AccountLoader<'info, Market>,
131    market_token_mint: &'a mut Account<'info, Mint>,
132    token_program: AccountInfo<'info>,
133    swap: Option<&'a SwapActionParams>,
134    swap_markets: Vec<AccountLoader<'info, Market>>,
135    event_emitter: EventEmitter<'a, 'info>,
136}
137
138impl<'a, 'info> RevertibleLiquidityMarketOperation<'a, 'info> {
139    pub(crate) fn new(
140        store: &'a AccountLoader<'info, Store>,
141        oracle: &'a Oracle,
142        market: &'a AccountLoader<'info, Market>,
143        market_token_mint: &'a mut Account<'info, Mint>,
144        token_program: AccountInfo<'info>,
145        swap: Option<&'a SwapActionParams>,
146        remaining_accounts: &'info [AccountInfo<'info>],
147        event_emitter: EventEmitter<'a, 'info>,
148    ) -> Result<Self> {
149        let swap_markets = swap
150            .map(|swap| swap.unpack_markets_for_swap(&market_token_mint.key(), remaining_accounts))
151            .transpose()?
152            .unwrap_or_default();
153
154        Ok(Self {
155            store,
156            oracle,
157            market,
158            market_token_mint,
159            token_program,
160            swap,
161            swap_markets,
162            event_emitter,
163        })
164    }
165}
166
167impl<'info> RevertibleLiquidityMarketOperation<'_, 'info> {
168    pub(crate) fn op<'ctx>(&'ctx mut self) -> Result<Execute<'ctx, 'info>> {
169        let current_market_token = self.market_token_mint.key();
170        let market = RevertibleLiquidityMarket::from_revertible_market(
171            RevertibleMarket::new(self.market, self.event_emitter)?,
172            self.market_token_mint,
173            &self.token_program,
174            self.store,
175        )?;
176        let swap_markets = SwapMarkets::new(
177            &self.store.key(),
178            &self.swap_markets,
179            Some(&current_market_token),
180            self.event_emitter,
181        )?;
182        Ok(Execute {
183            output: (),
184            oracle: self.oracle,
185            swap: self.swap,
186            market,
187            swap_markets,
188            event_emitter: self.event_emitter,
189        })
190    }
191}
192
193#[must_use = "Revertible operation must be committed to take effect"]
194pub(crate) struct Execute<'a, 'info, T = ()> {
195    pub(crate) output: T,
196    oracle: &'a Oracle,
197    swap: Option<&'a SwapActionParams>,
198    market: RevertibleLiquidityMarket<'a, 'info>,
199    swap_markets: SwapMarkets<'a, 'info>,
200    event_emitter: EventEmitter<'a, 'info>,
201}
202
203impl<'a, 'info, T> Execute<'a, 'info, T> {
204    pub(crate) fn with_output<U>(self, output: U) -> Execute<'a, 'info, U> {
205        let Self {
206            oracle,
207            swap,
208            market,
209            swap_markets,
210            event_emitter: event_authority,
211            ..
212        } = self;
213
214        Execute {
215            output,
216            oracle,
217            swap,
218            market,
219            swap_markets,
220            event_emitter: event_authority,
221        }
222    }
223
224    pub(crate) fn market(&self) -> &RevertibleLiquidityMarket<'a, 'info> {
225        &self.market
226    }
227
228    pub(crate) fn market_mut(&mut self) -> &mut RevertibleLiquidityMarket<'a, 'info> {
229        &mut self.market
230    }
231
232    pub(crate) fn swap_markets(&self) -> &SwapMarkets<'_, 'info> {
233        &self.swap_markets
234    }
235
236    fn pre_execute(&mut self, prices: &Prices<u128>) -> Result<()> {
237        // Distribute position impact.
238        let distribute_position_impact = self
239            .market
240            .distribute_position_impact()
241            .map_err(ModelError::from)?
242            .execute()
243            .map_err(ModelError::from)?;
244
245        if *distribute_position_impact.distribution_amount() != 0 {
246            msg!("[Pre-execute] position impact distributed");
247        }
248
249        // Update borrowing state.
250        let borrowing = self
251            .market
252            .base_mut()
253            .update_borrowing(prices)
254            .and_then(|a| a.execute())
255            .map_err(ModelError::from)?;
256        msg!("[Pre-execute] borrowing state updated");
257
258        // Update funding state.
259        let funding = self
260            .market
261            .base_mut()
262            .update_funding(prices)
263            .and_then(|a| a.execute())
264            .map_err(ModelError::from)?;
265        msg!("[Pre-execute] funding state updated");
266
267        self.event_emitter
268            .emit_cpi(&MarketFeesUpdated::from_reports(
269                self.market.rev(),
270                self.market.market_meta().market_token_mint,
271                distribute_position_impact,
272                borrowing,
273                funding,
274            ))?;
275        Ok(())
276    }
277
278    fn validate_first_deposit(
279        &self,
280        receiver: &Pubkey,
281        params: &DepositActionParams,
282    ) -> Result<()> {
283        if self.market().market_token().supply == 0 {
284            Deposit::validate_first_deposit(
285                receiver,
286                params.min_market_token_amount,
287                self.market().base().as_ref(),
288            )?;
289        }
290
291        Ok(())
292    }
293
294    /// Swap and deposit into the current market.
295    ///
296    /// # CHECK
297    /// - `market_token_receiver` must be the correct escrow
298    ///   account for market token.
299    ///
300    /// # Errors
301    /// - Error if first deposit validation failed.
302    #[inline(never)]
303    pub(crate) fn unchecked_deposit(
304        mut self,
305        receiver: &Pubkey,
306        market_token_receiver: &'a AccountInfo<'info>,
307        params: &DepositActionParams,
308        initial_tokens: (Option<Pubkey>, Option<Pubkey>),
309        swap_pricing_kind: Option<SwapPricingKind>,
310    ) -> Result<Execute<'a, 'info, u64>> {
311        self.validate_first_deposit(receiver, params)?;
312
313        self.market = self
314            .market
315            .enable_mint(market_token_receiver)
316            .with_swap_pricing_kind(swap_pricing_kind.unwrap_or(SwapPricingKind::Deposit));
317
318        let prices = self.oracle.market_prices(&self.market)?;
319
320        self.pre_execute(&prices)?;
321
322        // Swap tokens into the target market.
323        let (long_token_amount, short_token_amount) = {
324            let meta = self.market.market_meta();
325            let expected_token_outs = (meta.long_token_mint, meta.short_token_mint);
326
327            match self.swap {
328                Some(swap) => self.swap_markets.revertible_swap(
329                    SwapDirection::Into(&mut self.market),
330                    self.oracle,
331                    swap,
332                    expected_token_outs,
333                    initial_tokens,
334                    (
335                        params.initial_long_token_amount,
336                        params.initial_short_token_amount,
337                    ),
338                )?,
339                None => {
340                    if params.initial_long_token_amount != 0 {
341                        require!(initial_tokens.0.is_none(), CoreError::InvalidArgument);
342                    }
343                    if params.initial_short_token_amount != 0 {
344                        require!(initial_tokens.1.is_none(), CoreError::InvalidArgument);
345                    }
346                    (
347                        params.initial_long_token_amount,
348                        params.initial_short_token_amount,
349                    )
350                }
351            }
352        };
353
354        // Perform the deposit.
355        let minted = {
356            let report = self
357                .market
358                .deposit(long_token_amount.into(), short_token_amount.into(), prices)
359                .and_then(|d| d.execute())
360                .map_err(ModelError::from)?;
361            self.market.validate_market_balances(0, 0)?;
362
363            let minted: u64 = (*report.minted())
364                .try_into()
365                .map_err(|_| error!(CoreError::TokenAmountOverflow))?;
366
367            params.validate_market_token_amount(minted)?;
368
369            self.event_emitter.emit_cpi(&DepositExecuted::from_report(
370                self.market.rev(),
371                self.market.market_meta().market_token_mint,
372                report,
373            ))?;
374            msg!("[Deposit] executed");
375
376            minted
377        };
378
379        Ok(self.with_output(minted))
380    }
381
382    /// Withdraw from the current market and swap.
383    ///
384    /// # CHECK
385    ///
386    /// # Errors
387    ///
388    pub(crate) fn unchecked_withdraw(
389        mut self,
390        market_token_vault: &'a AccountInfo<'info>,
391        params: &WithdrawalActionParams,
392        final_tokens: (Pubkey, Pubkey),
393        swap_pricing_kind: Option<SwapPricingKind>,
394    ) -> Result<Execute<'a, 'info, (u64, u64)>> {
395        self.market = self
396            .market
397            .enable_burn(market_token_vault)
398            .with_swap_pricing_kind(swap_pricing_kind.unwrap_or(SwapPricingKind::Withdrawal));
399
400        let prices = self.oracle.market_prices(&self.market)?;
401
402        self.pre_execute(&prices)?;
403
404        // Perform the withdrawal.
405        let (long_amount, short_amount) = {
406            let report = self
407                .market
408                .withdraw(params.market_token_amount.into(), prices)
409                .and_then(|w| w.execute())
410                .map_err(ModelError::from)?;
411            let (long_amount, short_amount) = (
412                (*report.long_token_output())
413                    .try_into()
414                    .map_err(|_| CoreError::TokenAmountOverflow)?,
415                (*report.short_token_output())
416                    .try_into()
417                    .map_err(|_| CoreError::TokenAmountOverflow)?,
418            );
419            // Validate current market.
420            self.market
421                .validate_market_balances(long_amount, short_amount)?;
422
423            self.event_emitter
424                .emit_cpi(&WithdrawalExecuted::from_report(
425                    self.market.rev(),
426                    self.market.market_meta().market_token_mint,
427                    report,
428                ))?;
429            msg!("[Withdrawal] executed");
430
431            (long_amount, short_amount)
432        };
433
434        // Perform the swap.
435        let (final_long_amount, final_short_amount) = {
436            let meta = *self.market.market_meta();
437            match self.swap {
438                Some(swap) => self.swap_markets.revertible_swap(
439                    SwapDirection::From(&mut self.market),
440                    self.oracle,
441                    swap,
442                    final_tokens,
443                    (Some(meta.long_token_mint), Some(meta.short_token_mint)),
444                    (long_amount, short_amount),
445                )?,
446                None => {
447                    require!(
448                        final_tokens == (meta.long_token_mint, meta.short_token_mint),
449                        CoreError::InvalidSwapPath
450                    );
451                    (long_amount, short_amount)
452                }
453            }
454        };
455
456        params.validate_output_amounts(final_long_amount, final_short_amount)?;
457
458        Ok(self.with_output((final_long_amount, final_short_amount)))
459    }
460
461    fn take_output<U>(self, new_output: U) -> (Execute<'a, 'info, U>, T) {
462        let Self {
463            output,
464            oracle,
465            swap,
466            market,
467            swap_markets,
468            event_emitter: event_authority,
469        } = self;
470
471        (
472            Execute {
473                output: new_output,
474                oracle,
475                swap,
476                market,
477                swap_markets,
478                event_emitter: event_authority,
479            },
480            output,
481        )
482    }
483
484    /// Shift market tokens.
485    /// # CHECK
486    ///
487    /// # Errors
488    ///
489    pub(crate) fn unchecked_shift(
490        self,
491        mut to_market: Self,
492        receiver: &Pubkey,
493        params: &ShiftActionParams,
494        from_market_token_vault: &'a AccountInfo<'info>,
495        to_market_token_account: &'a AccountInfo<'info>,
496    ) -> Result<(Self, Self, u64)> {
497        let meta = self.market().market_meta();
498        let (long_token, short_token) = (meta.long_token_mint, meta.short_token_mint);
499
500        // Perform the shift-withdrawal.
501        let (mut from_market, (long_amount, short_amount)) = {
502            let (op, output) = self.take_output(());
503            let mut withdrawal_params = WithdrawalActionParams::default();
504            withdrawal_params.market_token_amount = params.from_market_token_amount;
505            op.unchecked_withdraw(
506                from_market_token_vault,
507                &withdrawal_params,
508                (long_token, short_token),
509                Some(SwapPricingKind::Shift),
510            )?
511            .take_output(output)
512        };
513
514        // Transfer tokens from the `from_market` to `to_market`.
515        // The vaults are assumed to be shared.
516        {
517            from_market
518                .market_mut()
519                .record_transferred_out_by_token(&long_token, &long_amount)
520                .map_err(ModelError::from)?;
521            to_market
522                .market_mut()
523                .record_transferred_in_by_token(&long_token, &long_amount)
524                .map_err(ModelError::from)?;
525
526            from_market
527                .market_mut()
528                .record_transferred_out_by_token(&short_token, &short_amount)
529                .map_err(ModelError::from)?;
530            to_market
531                .market_mut()
532                .record_transferred_in_by_token(&short_token, &short_amount)
533                .map_err(ModelError::from)?;
534        }
535
536        // Perform the shift-deposit.
537        let (to_market, received) = {
538            let (op, output) = to_market.take_output(());
539            let mut deposit_params = DepositActionParams::default();
540            deposit_params.initial_long_token_amount = long_amount;
541            deposit_params.initial_short_token_amount = short_amount;
542            deposit_params.min_market_token_amount = params.min_to_market_token_amount;
543            op.unchecked_deposit(
544                receiver,
545                to_market_token_account,
546                &deposit_params,
547                (None, None),
548                Some(SwapPricingKind::Shift),
549            )?
550            .take_output(output)
551        };
552
553        Ok((from_market, to_market, received))
554    }
555}
556
557impl<T> Revertible for Execute<'_, '_, T> {
558    fn commit(self) {
559        self.market.commit();
560        self.swap_markets.commit();
561    }
562}