gmsol_store/instructions/exchange/
order.rs

1use std::collections::HashSet;
2
3use anchor_lang::prelude::*;
4use anchor_spl::{
5    associated_token::AssociatedToken,
6    token::{transfer_checked, Mint, Token, TokenAccount, TransferChecked},
7};
8use gmsol_model::utils::apply_factor;
9use gmsol_utils::InitSpace;
10
11use crate::{
12    constants,
13    events::{EventEmitter, GtUpdated, OrderCreated},
14    ops::{
15        execution_fee::TransferExecutionFeeOperation,
16        order::{CreateOrderOperation, CreateOrderParams},
17    },
18    order::internal::Close,
19    states::{
20        common::action::Action,
21        feature::ActionDisabledFlag,
22        order::{Order, OrderKind},
23        position::PositionKind,
24        user::UserHeader,
25        HasMarketMeta, Market, NonceBytes, Position, RoleKey, Seed, Store, StoreWalletSigner,
26        UpdateOrderParams,
27    },
28    utils::{internal, token::is_associated_token_account_or_owner},
29    CoreError,
30};
31
32/// The accounts definition for the [`prepare_position`](crate::gmsol_store::prepare_position)
33/// instruction.
34#[derive(Accounts)]
35#[instruction(params: CreateOrderParams)]
36pub struct PreparePosition<'info> {
37    /// The owner of the order to be created.
38    #[account(mut)]
39    pub owner: Signer<'info>,
40    /// Store.
41    pub store: AccountLoader<'info, Store>,
42    /// Market.
43    #[account(has_one = store)]
44    pub market: AccountLoader<'info, Market>,
45    /// The position.
46    #[account(
47        init_if_needed,
48        payer = owner,
49        space = 8 + Position::INIT_SPACE,
50        seeds = [
51            Position::SEED,
52            store.key().as_ref(),
53            owner.key().as_ref(),
54            market.load()?.meta().market_token_mint.as_ref(),
55            params.collateral_token(market.load()?.meta()).as_ref(),
56            &[params.to_position_kind()? as u8],
57        ],
58        bump,
59    )]
60    pub position: AccountLoader<'info, Position>,
61    /// The system program.
62    pub system_program: Program<'info, System>,
63}
64
65pub(crate) fn prepare_position(
66    ctx: Context<PreparePosition>,
67    params: &CreateOrderParams,
68) -> Result<()> {
69    let store = ctx.accounts.store.key();
70    let meta = *ctx.accounts.market.load()?.meta();
71    let market_token = meta.market_token_mint;
72    let collateral_token = params.collateral_token(&meta);
73    validate_and_initialize_position_if_needed(
74        &ctx.accounts.position,
75        ctx.bumps.position,
76        params.to_position_kind()?,
77        &ctx.accounts.owner,
78        collateral_token,
79        &market_token,
80        meta.is_pure(),
81        &store,
82        ctx.accounts.system_program.to_account_info(),
83    )?;
84    Ok(())
85}
86
87#[allow(clippy::too_many_arguments)]
88fn validate_and_initialize_position_if_needed<'info>(
89    position_loader: &AccountLoader<'info, Position>,
90    bump: u8,
91    kind: PositionKind,
92    owner: &AccountInfo<'info>,
93    collateral_token: &Pubkey,
94    market_token: &Pubkey,
95    is_pure_market: bool,
96    store: &Pubkey,
97    system_program: AccountInfo<'info>,
98) -> Result<()> {
99    let mut should_transfer_in = false;
100
101    let owner_key = owner.key;
102    match position_loader.load_init() {
103        Ok(mut position) => {
104            position.try_init(
105                kind,
106                bump,
107                *store,
108                owner_key,
109                market_token,
110                collateral_token,
111            )?;
112            should_transfer_in = true;
113            drop(position);
114            position_loader.exit(&crate::ID)?;
115        }
116        Err(Error::AnchorError(err)) => {
117            if err.error_code_number != ErrorCode::AccountDiscriminatorAlreadySet as u32 {
118                return Err(Error::AnchorError(err));
119            }
120        }
121        Err(err) => {
122            return Err(err);
123        }
124    }
125    validate_position(
126        &*position_loader.load()?,
127        bump,
128        kind,
129        owner_key,
130        collateral_token,
131        market_token,
132        store,
133    )?;
134
135    if should_transfer_in {
136        TransferExecutionFeeOperation::builder()
137            .payment(position_loader.to_account_info())
138            .payer(owner.clone())
139            .execution_lamports(Order::position_cut_rent(is_pure_market, true)?)
140            .system_program(system_program)
141            .build()
142            .execute()?;
143    }
144    Ok(())
145}
146
147fn validate_position(
148    position: &Position,
149    bump: u8,
150    kind: PositionKind,
151    owner: &Pubkey,
152    collateral_token: &Pubkey,
153    market_token: &Pubkey,
154    store: &Pubkey,
155) -> Result<()> {
156    require_eq!(position.bump, bump, CoreError::InvalidPosition);
157    require_eq!(position.kind()?, kind, CoreError::InvalidPosition);
158    require_keys_eq!(position.owner, *owner, CoreError::InvalidPosition);
159    require_keys_eq!(
160        position.collateral_token,
161        *collateral_token,
162        CoreError::InvalidPosition
163    );
164    require_keys_eq!(
165        position.market_token,
166        *market_token,
167        CoreError::InvalidPosition
168    );
169    require_keys_eq!(position.store, *store, CoreError::InvalidPosition);
170    Ok(())
171}
172
173/// The accounts definitions for [`create_order`](crate::gmsol_store::create_order) instruction.
174///
175/// Remaining accounts expected by this instruction:
176///
177///   - 0..M. `[]` M market accounts, where M represents the length of the
178///     swap path for initial collateral token or final output token.
179#[derive(Accounts)]
180#[instruction(nonce: [u8; 32], params: CreateOrderParams)]
181pub struct CreateOrder<'info> {
182    /// The owner of the order to be created.
183    #[account(mut)]
184    pub owner: Signer<'info>,
185    /// The receiver of the output funds.
186    /// CHECK: only the address is used.
187    pub receiver: UncheckedAccount<'info>,
188    /// Store.
189    pub store: AccountLoader<'info, Store>,
190    /// Market.
191    #[account(mut, has_one = store)]
192    pub market: AccountLoader<'info, Market>,
193    /// User Account.
194    #[account(
195        mut,
196        constraint = user.load()?.is_initialized() @ CoreError::InvalidUserAccount,
197        has_one = owner,
198        has_one = store,
199        seeds = [UserHeader::SEED, store.key().as_ref(), owner.key().as_ref()],
200        bump = user.load()?.bump,
201    )]
202    pub user: AccountLoader<'info, UserHeader>,
203    /// The order to be created.
204    #[account(
205        init,
206        space = 8 + Order::INIT_SPACE,
207        payer = owner,
208        seeds = [Order::SEED, store.key().as_ref(), owner.key().as_ref(), &nonce],
209        bump,
210    )]
211    pub order: AccountLoader<'info, Order>,
212    /// The related position.
213    #[account(
214        mut,
215        has_one = store,
216        has_one = owner,
217        constraint = position.load()?.market_token == market.load()?.meta().market_token_mint @ CoreError::MarketTokenMintMismatched,
218        constraint = position.load()?.collateral_token == *params.collateral_token(&*market.load()?) @ CoreError::InvalidPosition,
219        constraint = position.load()?.kind()? == params.to_position_kind()? @ CoreError::InvalidPosition,
220        seeds = [
221            Position::SEED,
222            store.key().as_ref(),
223            owner.key().as_ref(),
224            market.load()?.meta().market_token_mint.as_ref(),
225            params.collateral_token(market.load()?.meta()).as_ref(),
226            &[params.to_position_kind()? as u8],
227        ],
228        bump = position.load()?.bump,
229    )]
230    pub position: Option<AccountLoader<'info, Position>>,
231    /// Initial collateral token / swap in token.
232    /// Only required by increase and swap orders.
233    pub initial_collateral_token: Option<Box<Account<'info, Mint>>>,
234    /// Final output token.
235    /// Used as collateral token / swap out token for increase and swap orders;
236    /// and used as final output token for decrease orders.
237    ///
238    /// For the case of increase or swap orders, it will be checked to be a valid
239    /// collateral / swap out token.
240    pub final_output_token: Box<Account<'info, Mint>>,
241    /// Long token of the market.
242    #[account(constraint = market.load()?.meta().long_token_mint == long_token.key())]
243    pub long_token: Option<Box<Account<'info, Mint>>>,
244    /// Short token of the market.
245    #[account(constraint = market.load()?.meta().short_token_mint == short_token.key())]
246    pub short_token: Option<Box<Account<'info, Mint>>>,
247    /// Initial collateral token escrow account.
248    /// Only requried by increase and swap orders.
249    #[account(
250        mut,
251        associated_token::mint = initial_collateral_token,
252        associated_token::authority = order,
253    )]
254    pub initial_collateral_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
255    /// Final output token escrow account.
256    /// Only required by decrease and swap orders.
257    #[account(
258        mut,
259        associated_token::mint = final_output_token,
260        associated_token::authority = order,
261    )]
262    pub final_output_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
263    /// Long token escrow.
264    /// Only required by increase and decrease orders.
265    #[account(
266        mut,
267        associated_token::mint = long_token,
268        associated_token::authority = order,
269    )]
270    pub long_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
271    /// Short token escrow.
272    /// Only required by increase and decrease orders.
273    #[account(
274        mut,
275        associated_token::mint = short_token,
276        associated_token::authority = order,
277    )]
278    pub short_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
279    /// The source initial token account.
280    /// Only requried by increase and swap orders.
281    #[account(
282        mut,
283        token::mint = initial_collateral_token,
284    )]
285    pub initial_collateral_token_source: Option<Box<Account<'info, TokenAccount>>>,
286    /// The system program.
287    pub system_program: Program<'info, System>,
288    /// The token program.
289    pub token_program: Program<'info, Token>,
290    /// The associated token program.
291    pub associated_token_program: Program<'info, AssociatedToken>,
292}
293
294impl<'info> internal::Create<'info, Order> for CreateOrder<'info> {
295    type CreateParams = CreateOrderParams;
296
297    fn action(&self) -> AccountInfo<'info> {
298        self.order.to_account_info()
299    }
300
301    fn payer(&self) -> AccountInfo<'info> {
302        self.owner.to_account_info()
303    }
304
305    fn system_program(&self) -> AccountInfo<'info> {
306        self.system_program.to_account_info()
307    }
308
309    fn validate(&self, params: &Self::CreateParams) -> Result<()> {
310        self.store
311            .load()?
312            .validate_not_restarted()?
313            .validate_feature_enabled(
314                params
315                    .kind
316                    .try_into()
317                    .map_err(CoreError::from)
318                    .map_err(|err| error!(err))?,
319                ActionDisabledFlag::Create,
320            )?;
321        Ok(())
322    }
323
324    fn create_impl(
325        &mut self,
326        params: &Self::CreateParams,
327        nonce: &NonceBytes,
328        bumps: &Self::Bumps,
329        remaining_accounts: &'info [AccountInfo<'info>],
330    ) -> Result<()> {
331        self.transfer_tokens(params)?;
332
333        let ops = CreateOrderOperation::builder()
334            .order(self.order.clone())
335            .market(self.market.clone())
336            .store(self.store.clone())
337            .owner(self.owner.to_account_info())
338            .receiver(self.receiver.to_account_info())
339            .nonce(nonce)
340            .bump(bumps.order)
341            .params(params)
342            .swap_path(remaining_accounts)
343            .build();
344
345        let kind = params.kind;
346        match kind {
347            OrderKind::MarketSwap | OrderKind::LimitSwap => {
348                let swap_in = self
349                    .initial_collateral_token_escrow
350                    .as_ref()
351                    .ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
352                let swap_out = self
353                    .final_output_token_escrow
354                    .as_ref()
355                    .ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
356                ops.swap()
357                    .swap_in_token(swap_in.as_ref())
358                    .swap_out_token(swap_out.as_ref())
359                    .build()
360                    .execute()?;
361            }
362            OrderKind::MarketIncrease | OrderKind::LimitIncrease => {
363                let initial_collateral = self
364                    .initial_collateral_token_escrow
365                    .as_ref()
366                    .ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
367                let long_token = self
368                    .long_token_escrow
369                    .as_ref()
370                    .ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
371                let short_token = self
372                    .short_token_escrow
373                    .as_ref()
374                    .ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
375                ops.increase()
376                    .position(
377                        self.position
378                            .as_ref()
379                            .ok_or_else(|| error!(CoreError::PositionIsRequired))?,
380                    )
381                    .initial_collateral_token(initial_collateral.as_ref())
382                    .long_token(long_token.as_ref())
383                    .short_token(short_token.as_ref())
384                    .build()
385                    .execute()?;
386            }
387            OrderKind::MarketDecrease | OrderKind::LimitDecrease | OrderKind::StopLossDecrease => {
388                let final_output = self
389                    .final_output_token_escrow
390                    .as_ref()
391                    .ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
392                let long_token = self
393                    .long_token_escrow
394                    .as_ref()
395                    .ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
396                let short_token = self
397                    .short_token_escrow
398                    .as_ref()
399                    .ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
400                ops.decrease()
401                    .position(
402                        self.position
403                            .as_ref()
404                            .ok_or_else(|| error!(CoreError::PositionIsRequired))?,
405                    )
406                    .final_output_token(final_output.as_ref())
407                    .long_token(long_token.as_ref())
408                    .short_token(short_token.as_ref())
409                    .build()
410                    .execute()?;
411            }
412            _ => {
413                return err!(CoreError::OrderKindNotAllowed);
414            }
415        }
416        emit!(OrderCreated::new(
417            self.store.key(),
418            self.order.key(),
419            self.position.as_ref().map(|a| a.key()),
420        )?);
421        Ok(())
422    }
423}
424
425impl CreateOrder<'_> {
426    fn transfer_tokens(&mut self, params: &CreateOrderParams) -> Result<()> {
427        let kind = params.kind;
428        if !matches!(
429            kind,
430            OrderKind::MarketSwap
431                | OrderKind::LimitSwap
432                | OrderKind::MarketIncrease
433                | OrderKind::LimitIncrease
434        ) {
435            return Ok(());
436        }
437        let amount = params.initial_collateral_delta_amount;
438        if amount != 0 {
439            let token = self
440                .initial_collateral_token
441                .as_ref()
442                .ok_or_else(|| error!(CoreError::MissingInitialCollateralToken))?;
443            let from = self
444                .initial_collateral_token_source
445                .as_ref()
446                .ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
447            let to = self
448                .initial_collateral_token_escrow
449                .as_mut()
450                .ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
451
452            transfer_checked(
453                CpiContext::new(
454                    self.token_program.to_account_info(),
455                    TransferChecked {
456                        from: from.to_account_info(),
457                        mint: token.to_account_info(),
458                        to: to.to_account_info(),
459                        authority: self.owner.to_account_info(),
460                    },
461                ),
462                amount,
463                token.decimals,
464            )?;
465
466            to.reload()?;
467        }
468        Ok(())
469    }
470}
471
472/// The accounts definition for the [`close_order`](crate::gmsol_store::close_order) instruction.
473#[event_cpi]
474#[derive(Accounts)]
475pub struct CloseOrder<'info> {
476    /// The executor of this instruction.
477    pub executor: Signer<'info>,
478    /// The store.
479    #[account(mut)]
480    pub store: AccountLoader<'info, Store>,
481    /// The store wallet.
482    #[account(mut, seeds = [Store::WALLET_SEED, store.key().as_ref()], bump)]
483    pub store_wallet: SystemAccount<'info>,
484    /// The owner of the order.
485    /// CHECK: only used to validate and receive input funds.
486    #[account(mut)]
487    pub owner: UncheckedAccount<'info>,
488    /// The receiver of the order.
489    /// CHECK: only used to validate and receive output funds.
490    #[account(mut)]
491    pub receiver: UncheckedAccount<'info>,
492    /// The rent receiver of the order.
493    /// CHECK: only used to validate and receive rent.
494    #[account(mut)]
495    pub rent_receiver: UncheckedAccount<'info>,
496    /// User Account.
497    #[account(
498        mut,
499        constraint = user.load()?.is_initialized() @ CoreError::InvalidUserAccount,
500        has_one = owner,
501        has_one = store,
502        seeds = [UserHeader::SEED, store.key().as_ref(), owner.key().as_ref()],
503        bump = user.load()?.bump,
504    )]
505    pub user: AccountLoader<'info, UserHeader>,
506    /// Referrer User Account.
507    #[account(
508        mut,
509        constraint = referrer_user.key() != user.key() @ CoreError::InvalidArgument,
510        constraint = referrer_user.load()?.is_initialized() @ CoreError::InvalidUserAccount,
511        constraint = referrer_user.load()?.owner == *user.load()?.referral().referrer().ok_or(CoreError::InvalidArgument)? @ CoreError::InvalidArgument,
512        has_one = store,
513        seeds = [UserHeader::SEED, store.key().as_ref(), user.load()?.referral().referrer().ok_or(CoreError::InvalidArgument)?.as_ref()],
514        bump = referrer_user.load()?.bump,
515    )]
516    pub referrer_user: Option<AccountLoader<'info, UserHeader>>,
517    /// Order to close.
518    #[account(
519        mut,
520        constraint = order.load()?.header.store == store.key() @ CoreError::StoreMismatched,
521        constraint = order.load()?.header.owner == owner.key() @ CoreError::OwnerMismatched,
522        constraint = order.load()?.header.receiver() == receiver.key() @ CoreError::ReceiverMismatched,
523        constraint = order.load()?.header.rent_receiver() == rent_receiver.key @ CoreError::RentReceiverMismatched,
524        constraint = order.load()?.tokens.initial_collateral.account() == initial_collateral_token_escrow.as_ref().map(|a| a.key()) @ CoreError::TokenAccountMismatched,
525        constraint = order.load()?.tokens.final_output_token.account() == final_output_token_escrow.as_ref().map(|a| a.key()) @ CoreError::TokenAccountMismatched,
526        constraint = order.load()?.tokens.long_token.account() == long_token_escrow.as_ref().map(|a| a.key())@ CoreError::TokenAccountMismatched,
527        constraint = order.load()?.tokens.short_token.account() == short_token_escrow.as_ref().map(|a| a.key())@ CoreError::TokenAccountMismatched,
528    )]
529    pub order: AccountLoader<'info, Order>,
530    /// Initial collateral token.
531    pub initial_collateral_token: Option<Box<Account<'info, Mint>>>,
532    /// Final output token.
533    pub final_output_token: Option<Box<Account<'info, Mint>>>,
534    /// Long token.
535    pub long_token: Option<Box<Account<'info, Mint>>>,
536    /// Short token.
537    pub short_token: Option<Box<Account<'info, Mint>>>,
538    /// The escrow account for initial collateral tokens.
539    #[account(
540        mut,
541        associated_token::mint = initial_collateral_token,
542        associated_token::authority = order,
543    )]
544    pub initial_collateral_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
545    /// The escrow account for final output tokens.
546    #[account(
547        mut,
548        associated_token::mint = final_output_token,
549        associated_token::authority = order,
550    )]
551    pub final_output_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
552    /// The escrow account for long tokens.
553    #[account(
554        mut,
555        associated_token::mint = long_token,
556        associated_token::authority = order,
557    )]
558    pub long_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
559    /// The escrow account for short tokens.
560    #[account(
561        mut,
562        associated_token::mint = short_token,
563        associated_token::authority = order,
564    )]
565    pub short_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
566    /// The ATA for initial collateral token of the owner.
567    /// CHECK: should be checked during the execution.
568    #[account(
569        mut,
570        constraint = is_associated_token_account_or_owner(initial_collateral_token_ata.key, owner.key, &initial_collateral_token.as_ref().map(|a| a.key()).expect("must provide")) @ CoreError::NotAnATA,
571    )]
572    pub initial_collateral_token_ata: Option<UncheckedAccount<'info>>,
573    /// The ATA for final output token of the receiver.
574    /// CHECK: should be checked during the execution.
575    #[account(
576        mut,
577        constraint = is_associated_token_account_or_owner(final_output_token_ata.key, receiver.key, &final_output_token.as_ref().map(|a| a.key()).expect("must provide")) @ CoreError::NotAnATA,
578    )]
579    pub final_output_token_ata: Option<UncheckedAccount<'info>>,
580    /// The ATA for long token of the receiver.
581    /// CHECK: should be checked during the execution.
582    #[account(
583        mut,
584        constraint = is_associated_token_account_or_owner(long_token_ata.key, receiver.key, &long_token.as_ref().map(|a| a.key()).expect("must provide")) @ CoreError::NotAnATA,
585    )]
586    pub long_token_ata: Option<UncheckedAccount<'info>>,
587    /// The ATA for initial collateral token of the receiver.
588    /// CHECK: should be checked during the execution.
589    #[account(
590        mut,
591        constraint = is_associated_token_account_or_owner(short_token_ata.key, receiver.key, &short_token.as_ref().map(|a| a.key()).expect("must provide")) @ CoreError::NotAnATA,
592    )]
593    pub short_token_ata: Option<UncheckedAccount<'info>>,
594    /// The system program.
595    pub system_program: Program<'info, System>,
596    /// The token program.
597    pub token_program: Program<'info, Token>,
598    /// The associated token program.
599    pub associated_token_program: Program<'info, AssociatedToken>,
600}
601
602impl<'info> internal::Authentication<'info> for CloseOrder<'info> {
603    fn authority(&self) -> &Signer<'info> {
604        &self.executor
605    }
606
607    fn store(&self) -> &AccountLoader<'info, Store> {
608        &self.store
609    }
610}
611
612impl<'info> internal::Close<'info, Order> for CloseOrder<'info> {
613    fn expected_keeper_role(&self) -> &str {
614        RoleKey::ORDER_KEEPER
615    }
616
617    fn rent_receiver(&self) -> AccountInfo<'info> {
618        self.rent_receiver.to_account_info()
619    }
620
621    fn validate(&self) -> Result<()> {
622        let order = self.order.load()?;
623        if order.header.action_state()?.is_pending() {
624            self.store
625                .load()?
626                .validate_not_restarted()?
627                .validate_feature_enabled(
628                    order
629                        .params()
630                        .kind()?
631                        .try_into()
632                        .map_err(CoreError::from)
633                        .map_err(|err| error!(err))?,
634                    ActionDisabledFlag::Cancel,
635                )?;
636        }
637        Ok(())
638    }
639
640    fn store_wallet_bump(&self, bumps: &Self::Bumps) -> u8 {
641        bumps.store_wallet
642    }
643
644    fn process(
645        &self,
646        init_if_needed: bool,
647        store_wallet_signer: &StoreWalletSigner,
648        event_emitter: &EventEmitter<'_, 'info>,
649    ) -> Result<internal::Success> {
650        let transfer_success = self.transfer_to_atas(init_if_needed, store_wallet_signer)?;
651        let process_success = self.process_gt_reward(event_emitter)?;
652        Ok(transfer_success && process_success)
653    }
654
655    fn event_authority(&self, bumps: &Self::Bumps) -> (AccountInfo<'info>, u8) {
656        (
657            self.event_authority.to_account_info(),
658            bumps.event_authority,
659        )
660    }
661
662    fn action(&self) -> &AccountLoader<'info, Order> {
663        &self.order
664    }
665}
666
667impl<'info> CloseOrder<'info> {
668    fn transfer_to_atas(
669        &self,
670        init_if_needed: bool,
671        store_wallet_signer: &StoreWalletSigner,
672    ) -> Result<internal::Success> {
673        use crate::utils::token::TransferAllFromEscrowToATA;
674
675        let signer = self.order.load()?.signer();
676        let seeds = signer.as_seeds();
677
678        let mut seen = HashSet::<_>::default();
679
680        let builder = TransferAllFromEscrowToATA::builder()
681            .store_wallet(self.store_wallet.to_account_info())
682            .store_wallet_signer(store_wallet_signer)
683            .system_program(self.system_program.to_account_info())
684            .token_program(self.token_program.to_account_info())
685            .associated_token_program(self.associated_token_program.to_account_info())
686            .payer(self.executor.to_account_info())
687            .escrow_authority(self.order.to_account_info())
688            .escrow_authority_seeds(&seeds)
689            .rent_receiver(self.rent_receiver())
690            .init_if_needed(init_if_needed)
691            .should_unwrap_native(self.order.load()?.header().should_unwrap_native_token());
692
693        let state = self.order.load()?.header().action_state()?;
694
695        // If the order is not completed, transfer input funds to the owner before transferring output funds.
696        if !state.is_completed() {
697            let (escrow, ata, token) = (
698                self.initial_collateral_token_escrow.as_ref(),
699                self.initial_collateral_token_ata.as_ref(),
700                self.initial_collateral_token.as_ref(),
701            );
702
703            if let Some(escrow) = escrow {
704                seen.insert(escrow.key());
705
706                let ata = ata.ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
707                let token = token.ok_or_else(|| error!(CoreError::TokenMintNotProvided))?;
708
709                if !builder
710                    .clone()
711                    .mint(token.to_account_info())
712                    .decimals(token.decimals)
713                    .ata(ata.to_account_info())
714                    .escrow(escrow.to_account_info())
715                    .owner(self.owner.to_account_info())
716                    .build()
717                    .unchecked_execute()?
718                {
719                    return Ok(false);
720                }
721            }
722        }
723
724        // Transfer output funds to the owner.
725        for (escrow, ata, token) in [
726            (
727                self.final_output_token_escrow.as_ref(),
728                self.final_output_token_ata.as_ref(),
729                self.final_output_token.as_ref(),
730            ),
731            (
732                self.long_token_escrow.as_ref(),
733                self.long_token_ata.as_ref(),
734                self.long_token.as_ref(),
735            ),
736            (
737                self.short_token_escrow.as_ref(),
738                self.short_token_ata.as_ref(),
739                self.short_token.as_ref(),
740            ),
741        ] {
742            if let Some(escrow) = escrow {
743                if !seen.insert(escrow.key()) {
744                    continue;
745                }
746                let ata = ata.ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
747                let token = token.ok_or_else(|| error!(CoreError::TokenMintNotProvided))?;
748
749                if !builder
750                    .clone()
751                    .mint(token.to_account_info())
752                    .decimals(token.decimals)
753                    .ata(ata.to_account_info())
754                    .escrow(escrow.to_account_info())
755                    .owner(self.receiver.to_account_info())
756                    .build()
757                    .unchecked_execute()?
758                {
759                    return Ok(false);
760                }
761            }
762        }
763
764        // If the order is completed, transfer input funds to the owner after transferring output funds.
765        if state.is_completed() {
766            let (escrow, ata, token) = (
767                self.initial_collateral_token_escrow.as_ref(),
768                self.initial_collateral_token_ata.as_ref(),
769                self.initial_collateral_token.as_ref(),
770            );
771
772            if let Some(escrow) = escrow {
773                if !seen.contains(&escrow.key()) {
774                    let ata = ata.ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
775                    let token = token.ok_or_else(|| error!(CoreError::TokenMintNotProvided))?;
776
777                    if !builder
778                        .clone()
779                        .mint(token.to_account_info())
780                        .decimals(token.decimals)
781                        .ata(ata.to_account_info())
782                        .escrow(escrow.to_account_info())
783                        .owner(self.owner.to_account_info())
784                        .build()
785                        .unchecked_execute()?
786                    {
787                        return Ok(false);
788                    }
789                }
790            }
791        }
792
793        Ok(true)
794    }
795
796    fn process_gt_reward(
797        &self,
798        event_emitter: &EventEmitter<'_, 'info>,
799    ) -> Result<internal::Success> {
800        let amount = self.order.load()?.gt_reward;
801        if amount != 0 {
802            self.mint_gt_reward_for_referrer(amount, event_emitter)?;
803
804            self.order.load_mut()?.gt_reward = 0;
805        }
806
807        Ok(true)
808    }
809
810    fn mint_gt_reward_for_referrer(
811        &self,
812        amount: u64,
813        event_emitter: &EventEmitter<'_, 'info>,
814    ) -> Result<()> {
815        // Mint referral reward for the referrer.
816        let Some(referrer) = self.user.load()?.referral().referrer().copied() else {
817            return Ok(());
818        };
819
820        let referrer_user = self
821            .referrer_user
822            .as_ref()
823            .ok_or_else(|| error!(CoreError::InvalidArgument))?;
824
825        require_keys_eq!(
826            referrer_user.load()?.owner,
827            referrer,
828            CoreError::InvalidArgument
829        );
830
831        let factor = self
832            .store
833            .load()?
834            .gt()
835            .referral_reward_factor(referrer_user.load()?.gt.rank())?;
836
837        let reward: u64 =
838            apply_factor::<_, { constants::MARKET_DECIMALS }>(&(amount as u128), &factor)
839                .ok_or_else(|| error!(CoreError::InvalidGTConfig))?
840                .try_into()
841                .map_err(|_| error!(CoreError::TokenAmountOverflow))?;
842
843        if reward != 0 {
844            let mut store = self.store.load_mut()?;
845            let mut referrer_user = referrer_user.load_mut()?;
846
847            store.gt_mut().mint_to(&mut referrer_user, reward)?;
848
849            event_emitter.emit_cpi(&GtUpdated::rewarded(
850                reward,
851                store.gt(),
852                Some(&referrer_user),
853            ))?;
854        }
855
856        Ok(())
857    }
858}
859
860/// The accounts definitions for [`update_order`](crate::gmsol_store::update_order).
861#[derive(Accounts)]
862pub struct UpdateOrder<'info> {
863    /// Owner.
864    pub owner: Signer<'info>,
865    /// Store.
866    pub store: AccountLoader<'info, Store>,
867    /// Market.
868    #[account(mut, has_one = store)]
869    pub market: AccountLoader<'info, Market>,
870    /// Order to update.
871    #[account(
872        mut,
873        constraint = order.load()?.header.store == store.key() @ CoreError::StoreMismatched,
874        constraint = order.load()?.header.market == market.key() @ CoreError::MarketMismatched,
875        constraint = order.load()?.header.owner== owner.key() @ CoreError::OwnerMismatched,
876    )]
877    pub order: AccountLoader<'info, Order>,
878}
879
880pub(crate) fn update_order(ctx: Context<UpdateOrder>, params: &UpdateOrderParams) -> Result<()> {
881    // Validate feature enabled.
882    {
883        let order = ctx.accounts.order.load()?;
884        ctx.accounts
885            .store
886            .load()?
887            .validate_not_restarted()?
888            .validate_feature_enabled(
889                order
890                    .params()
891                    .kind()?
892                    .try_into()
893                    .map_err(CoreError::from)
894                    .map_err(|err| error!(err))?,
895                ActionDisabledFlag::Update,
896            )?;
897    }
898
899    let id = ctx
900        .accounts
901        .market
902        .load_mut()?
903        .indexer_mut()
904        .next_order_id()?;
905    ctx.accounts.order.load_mut()?.update(id, params)?;
906    Ok(())
907}
908
909/// The accounts definition for the [`cancel_order_if_no_position`](crate::gmsol_store::cancel_order_if_no_position)
910/// instruction.
911#[derive(Accounts)]
912pub struct CancelOrderIfNoPosition<'info> {
913    /// Authority.
914    pub authority: Signer<'info>,
915    /// Store.
916    pub store: AccountLoader<'info, Store>,
917    /// Order to check.
918    #[account(
919        mut,
920        constraint = order.load()?.header.store == store.key() @ CoreError::StoreMismatched,
921        constraint = order.load()?.params.position().copied() == Some(position.key()) @ CoreError::PositionMismatched,
922    )]
923    pub order: AccountLoader<'info, Order>,
924    /// Validate that the position does not exist (or is owned by the system program).
925    pub position: SystemAccount<'info>,
926}
927
928/// Cancel order if the position does not exist (or is owned by the system program).
929/// # CHECK
930/// Only [`ORDER_KEEPER`](crate::states::roles::RoleKey::ORDER_KEEPER) can use.
931pub(crate) fn unchecked_cancel_order_if_no_position(
932    ctx: Context<CancelOrderIfNoPosition>,
933) -> Result<()> {
934    // Order must be in the pending state which is checked before the transition.
935    ctx.accounts.order.load_mut()?.header.cancelled()
936}
937
938impl<'info> internal::Authentication<'info> for CancelOrderIfNoPosition<'info> {
939    fn authority(&self) -> &Signer<'info> {
940        &self.authority
941    }
942
943    fn store(&self) -> &AccountLoader<'info, Store> {
944        &self.store
945    }
946}