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#[derive(Accounts)]
35#[instruction(params: CreateOrderParams)]
36pub struct PreparePosition<'info> {
37 #[account(mut)]
39 pub owner: Signer<'info>,
40 pub store: AccountLoader<'info, Store>,
42 #[account(has_one = store)]
44 pub market: AccountLoader<'info, Market>,
45 #[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 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#[derive(Accounts)]
180#[instruction(nonce: [u8; 32], params: CreateOrderParams)]
181pub struct CreateOrder<'info> {
182 #[account(mut)]
184 pub owner: Signer<'info>,
185 pub receiver: UncheckedAccount<'info>,
188 pub store: AccountLoader<'info, Store>,
190 #[account(mut, has_one = store)]
192 pub market: AccountLoader<'info, Market>,
193 #[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 #[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 #[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 pub initial_collateral_token: Option<Box<Account<'info, Mint>>>,
234 pub final_output_token: Box<Account<'info, Mint>>,
241 #[account(constraint = market.load()?.meta().long_token_mint == long_token.key())]
243 pub long_token: Option<Box<Account<'info, Mint>>>,
244 #[account(constraint = market.load()?.meta().short_token_mint == short_token.key())]
246 pub short_token: Option<Box<Account<'info, Mint>>>,
247 #[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 #[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 #[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 #[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 #[account(
282 mut,
283 token::mint = initial_collateral_token,
284 )]
285 pub initial_collateral_token_source: Option<Box<Account<'info, TokenAccount>>>,
286 pub system_program: Program<'info, System>,
288 pub token_program: Program<'info, Token>,
290 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#[event_cpi]
474#[derive(Accounts)]
475pub struct CloseOrder<'info> {
476 pub executor: Signer<'info>,
478 #[account(mut)]
480 pub store: AccountLoader<'info, Store>,
481 #[account(mut, seeds = [Store::WALLET_SEED, store.key().as_ref()], bump)]
483 pub store_wallet: SystemAccount<'info>,
484 #[account(mut)]
487 pub owner: UncheckedAccount<'info>,
488 #[account(mut)]
491 pub receiver: UncheckedAccount<'info>,
492 #[account(mut)]
495 pub rent_receiver: UncheckedAccount<'info>,
496 #[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 #[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 #[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 pub initial_collateral_token: Option<Box<Account<'info, Mint>>>,
532 pub final_output_token: Option<Box<Account<'info, Mint>>>,
534 pub long_token: Option<Box<Account<'info, Mint>>>,
536 pub short_token: Option<Box<Account<'info, Mint>>>,
538 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 pub system_program: Program<'info, System>,
596 pub token_program: Program<'info, Token>,
598 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 !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 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 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 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#[derive(Accounts)]
862pub struct UpdateOrder<'info> {
863 pub owner: Signer<'info>,
865 pub store: AccountLoader<'info, Store>,
867 #[account(mut, has_one = store)]
869 pub market: AccountLoader<'info, Market>,
870 #[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 {
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#[derive(Accounts)]
912pub struct CancelOrderIfNoPosition<'info> {
913 pub authority: Signer<'info>,
915 pub store: AccountLoader<'info, Store>,
917 #[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 pub position: SystemAccount<'info>,
926}
927
928pub(crate) fn unchecked_cancel_order_if_no_position(
932 ctx: Context<CancelOrderIfNoPosition>,
933) -> Result<()> {
934 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}