gmsol_store/instructions/
market.rs

1use crate::{
2    events::EventEmitter,
3    ops::market::MarketTransferOutOperation,
4    states::{
5        market::{
6            revertible::{Revertible, RevertibleMarket},
7            status::MarketStatus,
8            utils::ValidateMarketBalances,
9        },
10        Factor, HasMarketMeta,
11    },
12    ModelError,
13};
14
15use anchor_lang::prelude::*;
16use anchor_spl::token::{Mint, Token, TokenAccount};
17use gmsol_model::{
18    num::Unsigned, price::Prices, BalanceExt, Bank, BaseMarketMut, LiquidityMarketExt,
19    PnlFactorKind, PoolExt,
20};
21use gmsol_utils::InitSpace;
22
23use crate::{
24    constants,
25    states::{
26        market::config::{EntryArgs, MarketConfigBuffer},
27        Market, Seed, Store, TokenMapAccess, TokenMapHeader, TokenMapLoader,
28    },
29    utils::internal,
30    CoreError,
31};
32
33/// The accounts definition for [`initialize_market`](crate::gmsol_store::initialize_market).
34///
35/// *[See also the documentation for the instruction.](crate::gmsol_store::initialize_market)*
36#[derive(Accounts)]
37#[instruction(index_token_mint: Pubkey)]
38pub struct InitializeMarket<'info> {
39    /// The address authorized to execute this instruction.
40    #[account(mut)]
41    pub authority: Signer<'info>,
42    /// The store account.
43    #[account(has_one = token_map)]
44    pub store: AccountLoader<'info, Store>,
45    /// Market token mint.
46    #[account(
47        init,
48        payer = authority,
49        mint::decimals = constants::MARKET_TOKEN_DECIMALS,
50        // We directly use the store as the authority.
51        mint::authority = store.key(),
52        seeds = [
53            constants::MAREKT_TOKEN_MINT_SEED,
54            store.key().as_ref(),
55            index_token_mint.as_ref(),
56            long_token_mint.key().as_ref(),
57            short_token_mint.key().as_ref(),
58        ],
59        bump,
60    )]
61    pub market_token_mint: Account<'info, Mint>,
62    /// Long token.
63    pub long_token_mint: Account<'info, Mint>,
64    /// Short token.
65    pub short_token_mint: Account<'info, Mint>,
66    /// The market account.
67    #[account(
68        init,
69        payer = authority,
70        space = 8 + Market::INIT_SPACE,
71        seeds = [
72            Market::SEED,
73            store.key().as_ref(),
74            market_token_mint.key().as_ref(),
75        ],
76        bump,
77    )]
78    pub market: AccountLoader<'info, Market>,
79    /// The token map account.
80    #[account(has_one = store)]
81    pub token_map: AccountLoader<'info, TokenMapHeader>,
82    /// Long token vault must exist.
83    #[account(
84        token::mint = long_token_mint,
85        // We use the store as the authority of the token account.
86        token::authority = store,
87        seeds = [
88            constants::MARKET_VAULT_SEED,
89            store.key().as_ref(),
90            long_token_mint.key().as_ref(),
91        ],
92        bump,
93    )]
94    pub long_token_vault: Account<'info, TokenAccount>,
95    /// Short token vault must exist.
96    #[account(
97        token::mint = short_token_mint,
98        // We use the store as the authority of the token account.
99        token::authority = store,
100        seeds = [
101            constants::MARKET_VAULT_SEED,
102            store.key().as_ref(),
103            short_token_mint.key().as_ref(),
104        ],
105        bump,
106    )]
107    pub short_token_vault: Account<'info, TokenAccount>,
108    /// The system program.
109    pub system_program: Program<'info, System>,
110    pub token_program: Program<'info, Token>,
111}
112
113/// Initialize the account for [`Market`].
114///
115/// ## CHECK
116/// - Only MARKET_KEEPER can create new market.
117pub(crate) fn unchecked_initialize_market(
118    ctx: Context<InitializeMarket>,
119    index_token_mint: Pubkey,
120    name: &str,
121    enable: bool,
122) -> Result<()> {
123    {
124        let token_map = ctx.accounts.token_map.load_token_map()?;
125        require!(
126            token_map
127                .get(&index_token_mint)
128                .ok_or_else(|| error!(CoreError::NotFound))?
129                .is_enabled(),
130            CoreError::InvalidArgument
131        );
132
133        let long_token = &ctx.accounts.long_token_mint;
134        let long_token_config = token_map
135            .get(&long_token.key())
136            .ok_or_else(|| error!(CoreError::NotFound))?;
137        require!(
138            long_token_config.is_enabled(),
139            CoreError::TokenConfigDisabled
140        );
141        require!(
142            long_token_config.is_valid_pool_token_config(),
143            CoreError::InvalidArgument
144        );
145        // This is a redundant check to prevent the decimals in the token config from
146        // being inconsistent with the actual values.
147        require_eq!(
148            long_token_config.token_decimals(),
149            long_token.decimals,
150            CoreError::TokenDecimalsMismatched
151        );
152
153        let short_token = &ctx.accounts.short_token_mint;
154        let short_token_config = token_map
155            .get(&short_token.key())
156            .ok_or_else(|| error!(CoreError::NotFound))?;
157        require!(
158            short_token_config.is_enabled(),
159            CoreError::TokenConfigDisabled
160        );
161        require!(
162            short_token_config.is_valid_pool_token_config(),
163            CoreError::InvalidArgument
164        );
165        // This is a redundant check to prevent the decimals in the token config from
166        // being inconsistent with the actual values.
167        require_eq!(
168            short_token_config.token_decimals(),
169            short_token.decimals,
170            CoreError::TokenDecimalsMismatched
171        );
172    }
173    let market = &ctx.accounts.market;
174    market.load_init()?.init(
175        ctx.bumps.market,
176        ctx.accounts.store.key(),
177        name,
178        ctx.accounts.market_token_mint.key(),
179        index_token_mint,
180        ctx.accounts.long_token_mint.key(),
181        ctx.accounts.short_token_mint.key(),
182        enable,
183    )?;
184    Ok(())
185}
186
187impl<'info> internal::Authentication<'info> for InitializeMarket<'info> {
188    fn authority(&self) -> &Signer<'info> {
189        &self.authority
190    }
191
192    fn store(&self) -> &AccountLoader<'info, Store> {
193        &self.store
194    }
195}
196
197/// The accounts definition for [`toggle_market`](crate::gmsol_store::toggle_market).
198///
199/// *[See also the documentation for the instruction.](crate::gmsol_store::toggle_market)*
200#[derive(Accounts)]
201pub struct ToggleMarket<'info> {
202    /// The caller.
203    pub authority: Signer<'info>,
204    /// Store.
205    pub store: AccountLoader<'info, Store>,
206    /// Market.
207    #[account(mut, has_one = store)]
208    pub market: AccountLoader<'info, Market>,
209}
210
211/// Toggle Market.
212///
213/// ## CHECK
214/// - Only MARKET_KEEPER can toggle market.
215pub(crate) fn unchecked_toggle_market(ctx: Context<ToggleMarket>, enable: bool) -> Result<()> {
216    ctx.accounts.market.load_mut()?.set_enabled(enable);
217    Ok(())
218}
219
220impl<'info> internal::Authentication<'info> for ToggleMarket<'info> {
221    fn authority(&self) -> &Signer<'info> {
222        &self.authority
223    }
224
225    fn store(&self) -> &AccountLoader<'info, Store> {
226        &self.store
227    }
228}
229
230/// The accounts definition for [`market_transfer_in`](crate::gmsol_store::market_transfer_in).
231#[event_cpi]
232#[derive(Accounts)]
233pub struct MarketTransferIn<'info> {
234    /// Authority.
235    pub authority: Signer<'info>,
236    /// Store.
237    pub store: AccountLoader<'info, Store>,
238    /// The authority of the source account.
239    pub from_authority: Signer<'info>,
240    /// Market.
241    #[account(mut, has_one = store)]
242    pub market: AccountLoader<'info, Market>,
243    /// The source account.
244    #[account(mut, token::mint = vault.mint, constraint = from.key() != vault.key())]
245    pub from: Account<'info, TokenAccount>,
246    /// The market vault.
247    #[account(
248        mut,
249        token::authority = store,
250        seeds = [
251            constants::MARKET_VAULT_SEED,
252            store.key().as_ref(),
253            vault.mint.as_ref(),
254        ],
255        bump,
256    )]
257    pub vault: Account<'info, TokenAccount>,
258    /// Token Program.
259    pub token_program: Program<'info, Token>,
260}
261
262/// Transfer some tokens into the market.
263///
264/// ## CHECK
265/// - Only MARKET_KEEPER can transfer in tokens with this method.
266pub(crate) fn unchecked_market_transfer_in(
267    ctx: Context<MarketTransferIn>,
268    amount: u64,
269) -> Result<()> {
270    use anchor_spl::token;
271
272    {
273        let is_collateral_token = ctx
274            .accounts
275            .market
276            .load()?
277            .validated_meta(&ctx.accounts.store.key())?
278            .is_collateral_token(&ctx.accounts.from.mint);
279        require!(is_collateral_token, CoreError::InvalidArgument);
280    }
281
282    if amount != 0 {
283        token::transfer(
284            CpiContext::new(
285                ctx.accounts.token_program.to_account_info(),
286                token::Transfer {
287                    from: ctx.accounts.from.to_account_info(),
288                    to: ctx.accounts.vault.to_account_info(),
289                    authority: ctx.accounts.from_authority.to_account_info(),
290                },
291            ),
292            amount,
293        )?;
294        let event_emitter =
295            EventEmitter::new(&ctx.accounts.event_authority, ctx.bumps.event_authority);
296        let token = &ctx.accounts.vault.mint;
297        let mut market = RevertibleMarket::new(&ctx.accounts.market, event_emitter)?;
298        market
299            .record_transferred_in_by_token(token, &amount)
300            .map_err(ModelError::from)?;
301        market.commit();
302    }
303
304    Ok(())
305}
306
307impl<'info> internal::Authentication<'info> for MarketTransferIn<'info> {
308    fn authority(&self) -> &Signer<'info> {
309        &self.authority
310    }
311
312    fn store(&self) -> &AccountLoader<'info, Store> {
313        &self.store
314    }
315}
316
317/// The accounts definition for [`update_market_config`](crate::gmsol_store::update_market_config)
318/// and [`update_market_config_flag`](crate::gmsol_store::update_market_config_flag).
319#[derive(Accounts)]
320pub struct UpdateMarketConfig<'info> {
321    /// The caller.
322    pub authority: Signer<'info>,
323    /// Store.
324    pub store: AccountLoader<'info, Store>,
325    /// Market.
326    #[account(mut, has_one = store)]
327    pub market: AccountLoader<'info, Market>,
328}
329
330impl<'info> internal::Authentication<'info> for UpdateMarketConfig<'info> {
331    fn authority(&self) -> &Signer<'info> {
332        &self.authority
333    }
334
335    fn store(&self) -> &AccountLoader<'info, Store> {
336        &self.store
337    }
338}
339
340/// Update market config by key.
341///
342/// ## CHECK
343/// - Only MARKET_KEEPER can update the config of market.
344pub(crate) fn unchecked_update_market_config(
345    ctx: Context<UpdateMarketConfig>,
346    key: &str,
347    value: Factor,
348) -> Result<()> {
349    *ctx.accounts.market.load_mut()?.get_config_mut(key)? = value;
350    msg!(
351        "{}: set {} = {}",
352        ctx.accounts.market.load()?.meta.market_token_mint,
353        key,
354        value
355    );
356    Ok(())
357}
358
359/// Update market config flag by key.
360///
361/// ## CHECK
362/// - Only MARKET_KEEPER can update the config of market.
363pub(crate) fn unchecked_update_market_config_flag(
364    ctx: Context<UpdateMarketConfig>,
365    key: &str,
366    value: bool,
367) -> Result<()> {
368    let previous = ctx
369        .accounts
370        .market
371        .load_mut()?
372        .set_config_flag(key, value)?;
373    msg!(
374        "{}: set {} = {}, previous = {}",
375        ctx.accounts.market.load()?.meta.market_token_mint,
376        key,
377        value,
378        previous,
379    );
380    Ok(())
381}
382
383/// The accounts definition for [`update_market_config_with_buffer`](crate::gmsol_store::update_market_config_with_buffer).
384///
385/// *[See also the documentation for the instruction.](crate::gmsol_store::update_market_config_with_buffer)*
386#[derive(Accounts)]
387pub struct UpdateMarketConfigWithBuffer<'info> {
388    /// The caller.
389    pub authority: Signer<'info>,
390    /// Store.
391    pub store: AccountLoader<'info, Store>,
392    /// Market.
393    #[account(mut, has_one = store)]
394    pub market: AccountLoader<'info, Market>,
395    /// The buffer to use.
396    #[account(mut, has_one = store, has_one = authority @ CoreError::PermissionDenied)]
397    pub buffer: Account<'info, MarketConfigBuffer>,
398}
399
400/// Update market config with buffer.
401///
402/// ## CHECK
403/// - Only MARKET_KEEPER can udpate the config of market.
404pub(crate) fn unchecked_update_market_config_with_buffer(
405    ctx: Context<UpdateMarketConfigWithBuffer>,
406) -> Result<()> {
407    let buffer = &ctx.accounts.buffer;
408    require_gt!(
409        buffer.expiry,
410        Clock::get()?.unix_timestamp,
411        CoreError::InvalidArgument
412    );
413    ctx.accounts
414        .market
415        .load_mut()?
416        .update_config_with_buffer(buffer)?;
417    msg!(
418        "{} updated with buffer {}",
419        ctx.accounts.market.load()?.description()?,
420        buffer.key()
421    );
422    Ok(())
423}
424
425impl<'info> internal::Authentication<'info> for UpdateMarketConfigWithBuffer<'info> {
426    fn authority(&self) -> &Signer<'info> {
427        &self.authority
428    }
429
430    fn store(&self) -> &AccountLoader<'info, Store> {
431        &self.store
432    }
433}
434
435/// The accounts definition for read-only instructions for market.
436#[derive(Accounts)]
437pub struct ReadMarket<'info> {
438    /// Market.
439    pub market: AccountLoader<'info, Market>,
440}
441
442/// Get market status.
443pub(crate) fn get_market_status(
444    ctx: Context<ReadMarket>,
445    prices: &Prices<u128>,
446    maximize_pnl: bool,
447    maximize_pool_value: bool,
448) -> Result<MarketStatus> {
449    let market = ctx.accounts.market.load()?;
450    let status = MarketStatus::from_market(&market, prices, maximize_pnl, maximize_pool_value)
451        .map_err(ModelError::from)?;
452    Ok(status)
453}
454
455/// The accounts definition for read-only instructions for market.
456#[derive(Accounts)]
457pub struct ReadMarketWithToken<'info> {
458    /// Market.
459    #[account(
460        constraint = market.load()?.meta.market_token_mint == market_token.key() @ CoreError::InvalidArgument,
461    )]
462    pub market: AccountLoader<'info, Market>,
463    /// Market token.
464    pub market_token: Account<'info, Mint>,
465}
466
467/// Get market token price.
468pub(crate) fn get_market_token_price(
469    ctx: Context<ReadMarketWithToken>,
470    prices: &Prices<u128>,
471    pnl_factor: PnlFactorKind,
472    maximize: bool,
473) -> Result<u128> {
474    let market = ctx.accounts.market.load()?;
475    let liquidity_market = market.as_liquidity_market(&ctx.accounts.market_token);
476    let price = liquidity_market
477        .market_token_price(prices, pnl_factor, maximize)
478        .map_err(ModelError::from)?;
479    Ok(price)
480}
481
482/// The accounts definition for [`initialize_market_config_buffer`](crate::gmsol_store::initialize_market_config_buffer).
483///
484/// *[See also the documentation for the instruction.](crate::gmsol_store::initialize_market_config_buffer)*
485#[derive(Accounts)]
486pub struct InitializeMarketConfigBuffer<'info> {
487    /// The caller.
488    #[account(mut)]
489    pub authority: Signer<'info>,
490    /// Store.
491    pub store: AccountLoader<'info, Store>,
492    /// Buffer account to create.
493    #[account(init, payer = authority, space = 8 + MarketConfigBuffer::init_space(0))]
494    pub buffer: Account<'info, MarketConfigBuffer>,
495    /// System Program.
496    pub system_program: Program<'info, System>,
497}
498
499/// Initialize a market config buffer account.
500pub(crate) fn initialize_market_config_buffer(
501    ctx: Context<InitializeMarketConfigBuffer>,
502    expire_after_secs: u32,
503) -> Result<()> {
504    let buffer = &mut ctx.accounts.buffer;
505    buffer.authority = ctx.accounts.authority.key();
506    buffer.store = ctx.accounts.store.key();
507    buffer.expiry = Clock::get()?
508        .unix_timestamp
509        .saturating_add_unsigned(expire_after_secs as u64);
510    Ok(())
511}
512
513/// The accounts definition for [`set_market_config_buffer_authority`](crate::gmsol_store::set_market_config_buffer_authority).
514///
515/// *[See also the documentation for the instruction.](crate::gmsol_store::set_market_config_buffer_authority)*
516#[derive(Accounts)]
517pub struct SetMarketConfigBufferAuthority<'info> {
518    /// The authority.
519    #[account(mut)]
520    pub authority: Signer<'info>,
521    /// Buffer.
522    #[account(mut, has_one = authority @ CoreError::PermissionDenied)]
523    pub buffer: Account<'info, MarketConfigBuffer>,
524}
525
526/// Set the authority of the buffer account.
527pub(crate) fn set_market_config_buffer_authority(
528    ctx: Context<SetMarketConfigBufferAuthority>,
529    new_authority: Pubkey,
530) -> Result<()> {
531    ctx.accounts.buffer.authority = new_authority;
532    Ok(())
533}
534
535/// The accounts definition for [`close_market_config_buffer`](crate::gmsol_store::close_market_config_buffer).
536///
537/// *[See also the documentation for the instruction.](crate::gmsol_store::close_market_config_buffer)*
538#[derive(Accounts)]
539pub struct CloseMarketConfigBuffer<'info> {
540    /// The authority.
541    #[account(mut)]
542    pub authority: Signer<'info>,
543    /// Buffer.
544    #[account(mut, close = receiver, has_one = authority @ CoreError::PermissionDenied)]
545    pub buffer: Account<'info, MarketConfigBuffer>,
546    /// Receiver.
547    /// CHECK: Only used to receive funds after closing the buffer account.
548    #[account(mut)]
549    pub receiver: UncheckedAccount<'info>,
550}
551
552/// Close the buffer account.
553pub(crate) fn close_market_config_buffer(_ctx: Context<CloseMarketConfigBuffer>) -> Result<()> {
554    Ok(())
555}
556
557/// The accounts definition for [`push_to_market_config_buffer`](crate::gmsol_store::push_to_market_config_buffer).
558///
559/// *[See also the documentation for the instruction.](crate::gmsol_store::push_to_market_config_buffer)*
560#[derive(Accounts)]
561#[instruction(new_configs: Vec<(String, Factor)>)]
562pub struct PushToMarketConfigBuffer<'info> {
563    /// Authority.
564    #[account(mut)]
565    pub authority: Signer<'info>,
566    /// Buffer.
567    #[account(
568        mut,
569        has_one = authority @ CoreError::PermissionDenied,
570        realloc = 8 + buffer.space_after_push(new_configs.len()),
571        realloc::payer = authority,
572        realloc::zero = false,
573    )]
574    pub buffer: Account<'info, MarketConfigBuffer>,
575    system_program: Program<'info, System>,
576}
577
578/// Push to the buffer account.
579pub(crate) fn push_to_market_config_buffer(
580    ctx: Context<PushToMarketConfigBuffer>,
581    new_configs: Vec<EntryArgs>,
582) -> Result<()> {
583    let buffer = &mut ctx.accounts.buffer;
584    for entry in new_configs {
585        buffer.push(entry.try_into()?);
586    }
587    Ok(())
588}
589
590/// The accounts definition for [`toggle_gt_minting`](crate::gmsol_store::toggle_gt_minting).
591///
592/// *[See also the documentation for the instruction.](crate::gmsol_store::toggle_gt_minting)*
593#[derive(Accounts)]
594pub struct ToggleGTMinting<'info> {
595    /// The caller.
596    pub authority: Signer<'info>,
597    /// Store.
598    pub store: AccountLoader<'info, Store>,
599    /// Market.
600    #[account(mut, has_one = store)]
601    pub market: AccountLoader<'info, Market>,
602}
603
604/// Toggle GT Minting.
605///
606/// ## CHECK
607/// - Only MARKET_KEEPER can use this instruction.
608pub(crate) fn unchecked_toggle_gt_minting(
609    ctx: Context<ToggleGTMinting>,
610    enable: bool,
611) -> Result<()> {
612    ctx.accounts
613        .market
614        .load_mut()?
615        .set_is_gt_minting_enbaled(enable);
616    Ok(())
617}
618
619impl<'info> internal::Authentication<'info> for ToggleGTMinting<'info> {
620    fn authority(&self) -> &Signer<'info> {
621        &self.authority
622    }
623
624    fn store(&self) -> &AccountLoader<'info, Store> {
625        &self.store
626    }
627}
628
629/// The accounts definition for [`claim_fees_from_market`](crate::gmsol_store::claim_fees_from_market).
630///
631/// *[See also the documentation for the instruction.](crate::gmsol_store::claim_fees_from_market)*
632#[event_cpi]
633#[derive(Accounts)]
634pub struct ClaimFeesFromMarket<'info> {
635    pub authority: Signer<'info>,
636    pub store: AccountLoader<'info, Store>,
637    #[account(mut, has_one = store)]
638    pub market: AccountLoader<'info, Market>,
639    pub token_mint: InterfaceAccount<'info, anchor_spl::token_interface::Mint>,
640    #[account(
641        mut,
642        token::mint = token_mint,
643        token::authority = store,
644        token::token_program = token_program,
645        seeds = [
646            constants::MARKET_VAULT_SEED,
647            store.key().as_ref(),
648            token_mint.key().as_ref(),
649        ],
650        bump,
651    )]
652    pub vault: InterfaceAccount<'info, anchor_spl::token_interface::TokenAccount>,
653    #[account(
654        mut,
655        token::mint = token_mint,
656    )]
657    pub target: InterfaceAccount<'info, anchor_spl::token_interface::TokenAccount>,
658    pub token_program: Interface<'info, anchor_spl::token_interface::TokenInterface>,
659}
660
661/// Claim fees from the market.
662///
663/// # Errors
664/// - Only the receiver of treasury can claim fees.
665pub(crate) fn claim_fees_from_market(ctx: Context<ClaimFeesFromMarket>) -> Result<u64> {
666    // Validate the authority to be the receiver for the treasury.
667    ctx.accounts
668        .store
669        .load()?
670        .validate_not_restarted()?
671        .validate_claim_fees_address(ctx.accounts.authority.key)?;
672
673    let event_emitter = EventEmitter::new(&ctx.accounts.event_authority, ctx.bumps.event_authority);
674
675    let amount = {
676        let token = ctx.accounts.token_mint.key();
677
678        let mut market = RevertibleMarket::new(&ctx.accounts.market, event_emitter)?;
679        let is_long_token = market
680            .market_meta()
681            .to_token_side(&token)
682            .map_err(CoreError::from)?;
683        let the_opposite_side = !is_long_token;
684        let is_pure = market.market_meta().is_pure();
685        let pool = market.claimable_fee_pool_mut().map_err(ModelError::from)?;
686
687        let mut deltas = (0, 0);
688
689        // Saturating claim all fees from the pool.
690        let mut amount: u64 = pool
691            .amount(is_long_token)
692            .map_err(ModelError::from)?
693            .min(u128::from(u64::MAX))
694            .try_into()
695            .expect("must success");
696
697        deltas.0 = (u128::from(amount))
698            .to_opposite_signed()
699            .map_err(ModelError::from)?;
700
701        if is_pure {
702            let the_opposite_side_amount: u64 = pool
703                .amount(the_opposite_side)
704                .map_err(ModelError::from)?
705                .min(u128::from(u64::MAX))
706                .try_into()
707                .expect("must success");
708            deltas.1 = (u128::from(the_opposite_side_amount))
709                .to_opposite_signed()
710                .map_err(ModelError::from)?;
711            amount = amount
712                .checked_add(the_opposite_side_amount)
713                .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
714        }
715
716        if deltas.0 != 0 {
717            pool.apply_delta_amount(is_long_token, &deltas.0)
718                .map_err(ModelError::from)?;
719        }
720
721        if deltas.1 != 0 {
722            pool.apply_delta_amount(the_opposite_side, &deltas.1)
723                .map_err(ModelError::from)?;
724        }
725
726        market
727            .validate_market_balance_for_the_given_token(&token, amount)
728            .map_err(ModelError::from)?;
729        market.commit();
730
731        amount
732    };
733
734    // Transfer out the tokens.
735    let token = &ctx.accounts.token_mint;
736    MarketTransferOutOperation::builder()
737        .store(&ctx.accounts.store)
738        .market(&ctx.accounts.market)
739        .amount(amount)
740        .decimals(token.decimals)
741        .to(ctx.accounts.target.to_account_info())
742        .token_mint(token.to_account_info())
743        .vault(ctx.accounts.vault.to_account_info())
744        .token_program(ctx.accounts.token_program.to_account_info())
745        .event_emitter(event_emitter)
746        .build()
747        .execute()?;
748
749    msg!(
750        "Claimed `{}` {} from the {} market",
751        amount,
752        token.key(),
753        ctx.accounts.market.load()?.meta.market_token_mint
754    );
755    Ok(amount)
756}