1use std::ops::Deref;
2
3use anchor_lang::prelude::*;
4use anchor_spl::token::{Mint, Token, TokenAccount};
5
6use crate::{
7 constants,
8 events::{EventEmitter, TradeData, TradeEventRef},
9 ops::{
10 execution_fee::PayExecutionFeeOperation,
11 market::{MarketTransferInOperation, MarketTransferOutOperation},
12 order::{
13 ExecuteOrderOperation, ProcessTransferOutOperation, RemovePosition,
14 ShouldSendTradeEvent,
15 },
16 },
17 states::{
18 common::{
19 action::{ActionExt, ActionSigner},
20 swap::SwapActionParamsExt,
21 },
22 feature::ActionDisabledFlag,
23 order::{Order, TransferOut},
24 position::Position,
25 user::UserHeader,
26 Chainlink, Market, Oracle, Seed, Store, TokenMapHeader, TokenMapLoader,
27 },
28 utils::{internal, pubkey::DEFAULT_PUBKEY},
29 CoreError,
30};
31
32#[derive(Accounts)]
34#[instruction(index: u16)]
35pub struct PrepareTradeEventBuffer<'info> {
36 #[account(mut)]
38 pub authority: Signer<'info>,
39 pub store: AccountLoader<'info, Store>,
41 #[account(
43 init_if_needed,
44 payer = authority,
45 space = 8 + <TradeData as gmsol_utils::InitSpace>::INIT_SPACE,
47 seeds = [TradeData::SEED, store.key().as_ref(), authority.key().as_ref(), &index.to_le_bytes()],
48 bump,
49 )]
50 pub event: AccountLoader<'info, TradeData>,
51 pub system_program: Program<'info, System>,
53}
54
55pub(crate) fn prepare_trade_event_buffer(
56 ctx: Context<PrepareTradeEventBuffer>,
57 _index: u16,
58) -> Result<()> {
59 match ctx.accounts.event.load_init() {
60 Ok(mut event) => {
61 require_keys_eq!(event.authority, DEFAULT_PUBKEY, CoreError::Internal);
62 event.store = ctx.accounts.store.key();
63 event.authority = ctx.accounts.authority.key();
64 }
65 Err(Error::AnchorError(err)) => {
66 if err.error_code_number != ErrorCode::AccountDiscriminatorAlreadySet as u32 {
67 return Err(Error::AnchorError(err));
68 }
69 }
70 Err(err) => {
71 return Err(err);
72 }
73 }
74 ctx.accounts.event.exit(&crate::ID)?;
75 require_keys_eq!(
76 ctx.accounts.event.load()?.store,
77 ctx.accounts.store.key(),
78 CoreError::PermissionDenied
79 );
80 require_keys_eq!(
81 ctx.accounts.event.load()?.authority,
82 ctx.accounts.authority.key(),
83 CoreError::PermissionDenied
84 );
85 Ok(())
86}
87
88pub(crate) fn get_pnl_token(
89 position: &Option<AccountLoader<'_, Position>>,
90 market: &Market,
91) -> Result<Pubkey> {
92 let is_long = position
93 .as_ref()
94 .ok_or_else(|| error!(CoreError::PositionIsRequired))?
95 .load()?
96 .try_is_long()?;
97 if is_long {
98 Ok(market.meta().long_token_mint)
99 } else {
100 Ok(market.meta.short_token_mint)
101 }
102}
103
104pub(crate) fn check_delegation(account: &TokenAccount, target: Pubkey) -> Result<bool> {
105 let is_matched = account
106 .delegate
107 .map(|delegate| delegate == target)
108 .ok_or_else(|| error!(CoreError::NoDelegatedAuthorityIsSet))?;
109 Ok(is_matched)
110}
111
112pub(crate) fn validated_recent_timestamp(config: &Store, timestamp: i64) -> Result<i64> {
113 let recent_time_window = config.amount.recent_time_window;
114 let expiration_time = timestamp.saturating_add_unsigned(recent_time_window);
115 let clock = Clock::get()?;
116 if timestamp <= clock.unix_timestamp && clock.unix_timestamp <= expiration_time {
117 Ok(timestamp)
118 } else {
119 err!(CoreError::InvalidArgument)
120 }
121}
122
123#[event_cpi]
131#[derive(Accounts)]
132#[instruction(recent_timestamp: i64)]
133pub struct ExecuteIncreaseOrSwapOrder<'info> {
134 pub authority: Signer<'info>,
136 #[account(mut, has_one = token_map)]
138 pub store: AccountLoader<'info, Store>,
139 #[account(has_one = store)]
141 pub token_map: AccountLoader<'info, TokenMapHeader>,
142 #[account(mut, has_one = store)]
144 pub oracle: AccountLoader<'info, Oracle>,
145 #[account(mut, has_one = store)]
147 pub market: AccountLoader<'info, Market>,
148 #[account(mut)]
151 pub owner: UncheckedAccount<'info>,
152 #[account(
154 mut,
155 constraint = user.load()?.is_initialized() @ CoreError::InvalidUserAccount,
156 has_one = owner,
157 has_one = store,
158 seeds = [UserHeader::SEED, store.key().as_ref(), owner.key().as_ref()],
159 bump = user.load()?.bump,
160 )]
161 pub user: AccountLoader<'info, UserHeader>,
162 #[account(
164 mut,
165 constraint = order.load()?.header.store == store.key() @ CoreError::StoreMismatched,
166 constraint = order.load()?.header.market == market.key() @ CoreError::MarketMismatched,
167 constraint = order.load()?.header.owner == owner.key() @ CoreError::OwnerMismatched,
168 constraint = order.load()?.params.position().copied() == position.as_ref().map(|p| p.key()) @ CoreError::PositionMismatched,
169 constraint = order.load()?.tokens.initial_collateral.account() == initial_collateral_token_escrow.as_ref().map(|a| a.key()) @ CoreError::TokenAccountMismatched,
170 constraint = order.load()?.tokens.final_output_token.account() == final_output_token_escrow.as_ref().map(|a| a.key()) @ CoreError::TokenAccountMismatched,
171 constraint = order.load()?.tokens.long_token.account() == long_token_escrow.as_ref().map(|a| a.key())@ CoreError::TokenAccountMismatched,
172 constraint = order.load()?.tokens.short_token.account() == short_token_escrow.as_ref().map(|a| a.key())@ CoreError::TokenAccountMismatched,
173 )]
174 pub order: AccountLoader<'info, Order>,
175 #[account(
176 mut,
177 constraint = position.load()?.owner == order.load()?.header.owner,
178 constraint = position.load()?.store == store.key(),
179 seeds = [
180 Position::SEED,
181 store.key().as_ref(),
182 order.load()?.header.owner.as_ref(),
183 position.load()?.market_token.as_ref(),
184 position.load()?.collateral_token.as_ref(),
185 &[position.load()?.kind],
186 ],
187 bump = position.load()?.bump,
188 )]
189 pub position: Option<AccountLoader<'info, Position>>,
190 #[account(mut, has_one = store, has_one = authority)]
192 pub event: Option<AccountLoader<'info, TradeData>>,
193 pub initial_collateral_token: Option<Box<Account<'info, Mint>>>,
195 pub final_output_token: Option<Box<Account<'info, Mint>>>,
197 pub long_token: Option<Box<Account<'info, Mint>>>,
199 pub short_token: Option<Box<Account<'info, Mint>>>,
201 #[account(
203 mut,
204 associated_token::mint = initial_collateral_token,
205 associated_token::authority = order,
206 )]
207 pub initial_collateral_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
208 #[account(
210 mut,
211 associated_token::mint = final_output_token,
212 associated_token::authority = order,
213 )]
214 pub final_output_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
215 #[account(
217 mut,
218 associated_token::mint = long_token,
219 associated_token::authority = order,
220 )]
221 pub long_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
222 #[account(
224 mut,
225 associated_token::mint = short_token,
226 associated_token::authority = order,
227 )]
228 pub short_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
229 #[account(
231 mut,
232 token::mint = initial_collateral_token,
233 token::authority = store,
234 seeds = [
235 constants::MARKET_VAULT_SEED,
236 store.key().as_ref(),
237 initial_collateral_token_vault.mint.as_ref(),
238 ],
239 bump,
240 )]
241 pub initial_collateral_token_vault: Option<Box<Account<'info, TokenAccount>>>,
242 #[account(
244 mut,
245 token::mint = final_output_token,
246 token::authority = store,
247 seeds = [
248 constants::MARKET_VAULT_SEED,
249 store.key().as_ref(),
250 final_output_token_vault.mint.as_ref(),
251 ],
252 bump,
253 )]
254 pub final_output_token_vault: Option<Box<Account<'info, TokenAccount>>>,
255 #[account(
257 mut,
258 token::mint = long_token,
259 token::authority = store,
260 seeds = [
261 constants::MARKET_VAULT_SEED,
262 store.key().as_ref(),
263 long_token_vault.mint.as_ref(),
264 ],
265 bump,
266 )]
267 pub long_token_vault: Option<Box<Account<'info, TokenAccount>>>,
268 #[account(
270 mut,
271 token::mint = short_token,
272 token::authority = store,
273 seeds = [
274 constants::MARKET_VAULT_SEED,
275 store.key().as_ref(),
276 short_token_vault.mint.as_ref(),
277 ],
278 bump,
279 )]
280 pub short_token_vault: Option<Box<Account<'info, TokenAccount>>>,
281 pub token_program: Program<'info, Token>,
283 pub system_program: Program<'info, System>,
285 pub chainlink_program: Option<Program<'info, Chainlink>>,
287}
288
289#[inline(never)]
290pub(crate) fn unchecked_execute_increase_or_swap_order<'info>(
291 mut ctx: Context<'_, '_, 'info, 'info, ExecuteIncreaseOrSwapOrder<'info>>,
292 _recent_timestamp: i64,
293 execution_fee: u64,
294 throw_on_execution_error: bool,
295) -> Result<()> {
296 let accounts = &mut ctx.accounts;
297
298 let kind = accounts.order.load()?.params().kind()?;
299
300 require!(
302 kind.is_increase_position() || kind.is_swap(),
303 CoreError::InvalidArgument
304 );
305
306 accounts.store.load()?.validate_feature_enabled(
308 kind.try_into()
309 .map_err(CoreError::from)
310 .map_err(|err| error!(err))?,
311 ActionDisabledFlag::Execute,
312 )?;
313
314 let remaining_accounts = ctx.remaining_accounts;
315 let signer = accounts.order.load()?.signer();
316
317 let event_authority = accounts.event_authority.clone();
318 let event_emitter = EventEmitter::new(&event_authority, ctx.bumps.event_authority);
319
320 accounts.transfer_tokens_in(&signer, remaining_accounts, &event_emitter)?;
321
322 let (is_position_removed, transfer_out, should_send_trade_event) =
323 accounts.perform_execution(remaining_accounts, throw_on_execution_error, &event_emitter)?;
324
325 if transfer_out.executed() {
326 accounts.order.load_mut()?.header.completed()?;
327 accounts.process_transfer_out(remaining_accounts, &transfer_out, &event_emitter)?;
328 } else {
329 accounts.order.load_mut()?.header.cancelled()?;
330 accounts.transfer_tokens_out(remaining_accounts, &event_emitter)?;
331 }
332
333 if should_send_trade_event {
334 let event_loader = accounts.event.clone();
335 let event = event_loader
336 .as_ref()
337 .ok_or_else(|| error!(CoreError::EventBufferNotProvided))?
338 .load()?;
339 let event = TradeEventRef::from(&*event);
340 event_emitter.emit_cpi(&event)?;
341 }
342
343 if is_position_removed {
344 msg!("[Position] the position is removed");
345 }
346
347 ctx.accounts.pay_execution_fee(execution_fee)?;
349
350 Ok(())
351}
352
353impl<'info> internal::Authentication<'info> for ExecuteIncreaseOrSwapOrder<'info> {
354 fn authority(&self) -> &Signer<'info> {
355 &self.authority
356 }
357
358 fn store(&self) -> &AccountLoader<'info, Store> {
359 &self.store
360 }
361}
362
363impl<'info> ExecuteIncreaseOrSwapOrder<'info> {
364 #[inline(never)]
365 fn transfer_tokens_in(
366 &self,
367 signer: &ActionSigner,
368 remaining_accounts: &'info [AccountInfo<'info>],
369 event_emitter: &EventEmitter<'_, 'info>,
370 ) -> Result<()> {
371 if let Some(escrow) = self.initial_collateral_token_escrow.as_ref() {
372 let store = &self.store.key();
373 let market = self
374 .order
375 .load()?
376 .swap
377 .find_and_unpack_first_market(store, true, remaining_accounts)?
378 .unwrap_or(self.market.clone());
379 let vault = self
380 .initial_collateral_token_vault
381 .as_ref()
382 .ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
383 let amount = self.order.load()?.params.initial_collateral_delta_amount;
384 MarketTransferInOperation::builder()
385 .store(&self.store)
386 .from_authority(self.order.to_account_info())
387 .token_program(self.token_program.to_account_info())
388 .signer_seeds(&signer.as_seeds())
389 .market(&market)
390 .from(escrow.to_account_info())
391 .vault(vault)
392 .amount(amount)
393 .event_emitter(*event_emitter)
394 .build()
395 .execute()?;
396 }
397 Ok(())
398 }
399
400 #[inline(never)]
401 fn transfer_tokens_out(
402 &self,
403 remaining_accounts: &'info [AccountInfo<'info>],
404 event_emitter: &EventEmitter<'_, 'info>,
405 ) -> Result<()> {
406 if let Some(escrow) = self.initial_collateral_token_escrow.as_ref() {
407 let store = &self.store.key();
408 let market = self
409 .order
410 .load()?
411 .swap
412 .find_and_unpack_first_market(store, true, remaining_accounts)?
413 .unwrap_or(self.market.clone());
414 let vault = self
415 .initial_collateral_token_vault
416 .as_ref()
417 .ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
418 let token = self
419 .initial_collateral_token
420 .as_ref()
421 .ok_or_else(|| error!(CoreError::TokenMintNotProvided))?;
422 let amount = self.order.load()?.params.initial_collateral_delta_amount;
423 MarketTransferOutOperation::builder()
424 .store(&self.store)
425 .token_program(self.token_program.to_account_info())
426 .market(&market)
427 .to(escrow.to_account_info())
428 .vault(vault.to_account_info())
429 .amount(amount)
430 .decimals(token.decimals)
431 .token_mint(token.to_account_info())
432 .event_emitter(*event_emitter)
433 .build()
434 .execute()?;
435 }
436 Ok(())
437 }
438
439 #[inline(never)]
440 fn perform_execution(
441 &mut self,
442 remaining_accounts: &'info [AccountInfo<'info>],
443 throw_on_execution_error: bool,
444 event_emitter: &EventEmitter<'_, 'info>,
445 ) -> Result<(RemovePosition, Box<TransferOut>, ShouldSendTradeEvent)> {
446 let feeds = self
448 .order
449 .load()?
450 .swap
451 .to_feeds(&self.token_map.load_token_map()?)
452 .map_err(CoreError::from)?;
453 let ops = ExecuteOrderOperation::builder()
454 .store(&self.store)
455 .market(&self.market)
456 .owner(self.owner.to_account_info())
457 .user(&self.user)
458 .order(&self.order)
459 .position(self.position.as_ref())
460 .event(self.event.as_ref())
461 .throw_on_execution_error(throw_on_execution_error)
462 .executor(self.authority.to_account_info())
463 .event_emitter(*event_emitter);
464
465 self.oracle.load_mut()?.with_prices(
466 &self.store,
467 &self.token_map,
468 &feeds.tokens,
469 remaining_accounts,
470 self.chainlink_program.as_ref(),
471 |oracle, remaining_accounts| {
472 ops.oracle(oracle)
473 .remaining_accounts(remaining_accounts)
474 .build()
475 .execute()
476 },
477 )
478 }
479
480 #[inline(never)]
481 fn process_transfer_out(
482 &self,
483 remaining_accounts: &'info [AccountInfo<'info>],
484 transfer_out: &TransferOut,
485 event_emitter: &EventEmitter<'_, 'info>,
486 ) -> Result<()> {
487 let is_pnl_token_long_token = self.order.load()?.params.side()?.is_long();
488 let final_output_market = self
489 .order
490 .load()?
491 .swap
492 .find_and_unpack_last_market(&self.store.key(), true, remaining_accounts)?
493 .unwrap_or(self.market.clone());
494 ProcessTransferOutOperation::builder()
495 .token_program(self.token_program.to_account_info())
496 .store(&self.store)
497 .market(&self.market)
498 .is_pnl_token_long_token(is_pnl_token_long_token)
499 .final_output_token(self.final_output_token.as_deref())
500 .final_output_market(&final_output_market)
501 .final_output_token_account(
502 self.final_output_token_escrow
503 .as_ref()
504 .map(|a| a.to_account_info()),
505 )
506 .final_output_token_vault(self.final_output_token_vault.as_deref())
507 .long_token(self.long_token.as_deref())
508 .long_token_account(self.long_token_escrow.as_ref().map(|a| a.to_account_info()))
509 .long_token_vault(self.long_token_vault.as_deref())
510 .short_token(self.short_token.as_deref())
511 .short_token_account(
512 self.short_token_escrow
513 .as_ref()
514 .map(|a| a.to_account_info()),
515 )
516 .short_token_vault(self.short_token_vault.as_deref())
517 .claimable_long_token_account_for_user(None)
518 .claimable_short_token_account_for_user(None)
519 .claimable_pnl_token_account_for_holding(None)
520 .transfer_out(transfer_out)
521 .event_emitter(*event_emitter)
522 .build()
523 .execute()?;
524 Ok(())
525 }
526
527 #[inline(never)]
528 fn pay_execution_fee(&self, execution_fee: u64) -> Result<()> {
529 let execution_lamports = self.order.load()?.execution_lamports(execution_fee);
530 PayExecutionFeeOperation::builder()
531 .payer(self.order.to_account_info())
532 .receiver(self.authority.to_account_info())
533 .execution_lamports(execution_lamports)
534 .build()
535 .execute()?;
536 Ok(())
537 }
538}
539
540#[event_cpi]
549#[derive(Accounts)]
550#[instruction(recent_timestamp: i64)]
551pub struct ExecuteDecreaseOrder<'info> {
552 pub authority: Signer<'info>,
554 #[account(mut, has_one = token_map)]
556 pub store: AccountLoader<'info, Store>,
557 #[account(has_one = store)]
559 pub token_map: AccountLoader<'info, TokenMapHeader>,
560 #[account(mut, has_one = store)]
562 pub oracle: AccountLoader<'info, Oracle>,
563 #[account(mut, has_one = store)]
565 pub market: AccountLoader<'info, Market>,
566 #[account(mut)]
569 pub owner: UncheckedAccount<'info>,
570 #[account(
572 mut,
573 constraint = user.load()?.is_initialized() @ CoreError::InvalidUserAccount,
574 has_one = owner,
575 has_one = store,
576 seeds = [UserHeader::SEED, store.key().as_ref(), owner.key().as_ref()],
577 bump = user.load()?.bump,
578 )]
579 pub user: AccountLoader<'info, UserHeader>,
580 #[account(
582 mut,
583 constraint = order.load()?.header.store == store.key() @ CoreError::StoreMismatched,
584 constraint = order.load()?.header.market == market.key() @ CoreError::MarketMismatched,
585 constraint = order.load()?.header.owner == owner.key() @ CoreError::OwnerMismatched,
586 constraint = order.load()?.params.position().copied() == Some(position.key()) @ CoreError::PositionMismatched,
587 constraint = order.load()?.tokens.final_output_token.account() == Some(final_output_token_escrow.key()) @ CoreError::TokenAccountMismatched,
588 constraint = order.load()?.tokens.long_token.account() == Some(long_token_escrow.key()) @ CoreError::TokenAccountMismatched,
589 constraint = order.load()?.tokens.short_token.account() == Some(short_token_escrow.key()) @ CoreError::TokenAccountMismatched,
590 )]
591 pub order: AccountLoader<'info, Order>,
592 #[account(
593 mut,
594 constraint = position.load()?.owner == order.load()?.header.owner,
595 constraint = position.load()?.store == store.key(),
596 seeds = [
597 Position::SEED,
598 store.key().as_ref(),
599 order.load()?.header.owner.as_ref(),
600 position.load()?.market_token.as_ref(),
601 position.load()?.collateral_token.as_ref(),
602 &[position.load()?.kind],
603 ],
604 bump = position.load()?.bump,
605 )]
606 pub position: AccountLoader<'info, Position>,
607 #[account(mut, has_one = store, has_one = authority)]
609 pub event: AccountLoader<'info, TradeData>,
610 pub final_output_token: Box<Account<'info, Mint>>,
612 pub long_token: Box<Account<'info, Mint>>,
614 pub short_token: Box<Account<'info, Mint>>,
616 #[account(
618 mut,
619 associated_token::mint = final_output_token,
620 associated_token::authority = order,
621 )]
622 pub final_output_token_escrow: Box<Account<'info, TokenAccount>>,
623 #[account(
625 mut,
626 associated_token::mint = long_token,
627 associated_token::authority = order,
628 )]
629 pub long_token_escrow: Box<Account<'info, TokenAccount>>,
630 #[account(
632 mut,
633 associated_token::mint = short_token,
634 associated_token::authority = order,
635 )]
636 pub short_token_escrow: Box<Account<'info, TokenAccount>>,
637 #[account(
639 mut,
640 token::mint = final_output_token,
641 token::authority = store,
642 seeds = [
643 constants::MARKET_VAULT_SEED,
644 store.key().as_ref(),
645 final_output_token_vault.mint.as_ref(),
646 ],
647 bump,
648 )]
649 pub final_output_token_vault: Box<Account<'info, TokenAccount>>,
650 #[account(
652 mut,
653 token::mint = long_token,
654 token::authority = store,
655 seeds = [
656 constants::MARKET_VAULT_SEED,
657 store.key().as_ref(),
658 long_token_vault.mint.as_ref(),
659 ],
660 bump,
661 )]
662 pub long_token_vault: Box<Account<'info, TokenAccount>>,
663 #[account(
665 mut,
666 token::mint = short_token,
667 token::authority = store,
668 seeds = [
669 constants::MARKET_VAULT_SEED,
670 store.key().as_ref(),
671 short_token_vault.mint.as_ref(),
672 ],
673 bump,
674 )]
675 pub short_token_vault: Box<Account<'info, TokenAccount>>,
676 #[account(
677 mut,
678 token::mint = market.load()?.meta().long_token_mint,
679 token::authority = store,
680 constraint = check_delegation(&claimable_long_token_account_for_user, order.load()?.header.owner)?,
681 seeds = [
682 constants::CLAIMABLE_ACCOUNT_SEED,
683 store.key().as_ref(),
684 market.load()?.meta().long_token_mint.as_ref(),
685 order.load()?.header.owner.as_ref(),
686 &store.load()?.claimable_time_key(validated_recent_timestamp(store.load()?.deref(), recent_timestamp)?)?,
687 ],
688 bump,
689 )]
690 pub claimable_long_token_account_for_user: Box<Account<'info, TokenAccount>>,
691 #[account(
692 mut,
693 token::mint = market.load()?.meta().short_token_mint,
694 token::authority = store,
695 constraint = check_delegation(&claimable_short_token_account_for_user, order.load()?.header.owner)?,
696 seeds = [
697 constants::CLAIMABLE_ACCOUNT_SEED,
698 store.key().as_ref(),
699 market.load()?.meta().short_token_mint.as_ref(),
700 order.load()?.header.owner.as_ref(),
701 &store.load()?.claimable_time_key(validated_recent_timestamp(store.load()?.deref(), recent_timestamp)?)?,
702 ],
703 bump,
704 )]
705 pub claimable_short_token_account_for_user: Box<Account<'info, TokenAccount>>,
706 #[account(
707 mut,
708 token::mint = get_pnl_token(&Some(position.clone()), market.load()?.deref())?,
709 token::authority = store,
710 constraint = check_delegation(&claimable_pnl_token_account_for_holding, store.load()?.address.holding)?,
711 seeds = [
712 constants::CLAIMABLE_ACCOUNT_SEED,
713 store.key().as_ref(),
714 get_pnl_token(&Some(position.clone()), market.load()?.deref())?.as_ref(),
715 store.load()?.address.holding.as_ref(),
716 &store.load()?.claimable_time_key(validated_recent_timestamp(store.load()?.deref(), recent_timestamp)?)?,
717 ],
718 bump,
719 )]
720 pub claimable_pnl_token_account_for_holding: Box<Account<'info, TokenAccount>>,
721 pub token_program: Program<'info, Token>,
723 pub system_program: Program<'info, System>,
725 pub chainlink_program: Option<Program<'info, Chainlink>>,
727}
728
729pub(crate) fn unchecked_execute_decrease_order<'info>(
730 mut ctx: Context<'_, '_, 'info, 'info, ExecuteDecreaseOrder<'info>>,
731 _recent_timestamp: i64,
732 execution_fee: u64,
733 throw_on_execution_error: bool,
734) -> Result<()> {
735 let accounts = &mut ctx.accounts;
736 let remaining_accounts = ctx.remaining_accounts;
737
738 let kind = accounts.order.load()?.params().kind()?;
739
740 require!(kind.is_decrease_position(), CoreError::InvalidArgument);
742
743 accounts.store.load()?.validate_feature_enabled(
745 kind.try_into()
746 .map_err(CoreError::from)
747 .map_err(|err| error!(err))?,
748 ActionDisabledFlag::Execute,
749 )?;
750
751 let event_authority = accounts.event_authority.clone();
752 let event_emitter = EventEmitter::new(&event_authority, ctx.bumps.event_authority);
753 let (is_position_removed, transfer_out, should_send_trade_event) =
754 accounts.perform_execution(remaining_accounts, throw_on_execution_error, &event_emitter)?;
755
756 if transfer_out.executed() {
757 accounts.order.load_mut()?.header.completed()?;
758 accounts.process_transfer_out(remaining_accounts, &transfer_out, &event_emitter)?;
759 } else {
760 accounts.order.load_mut()?.header.cancelled()?;
761 }
762
763 if should_send_trade_event {
764 let event_loader = accounts.event.clone();
765 let event = event_loader.load()?;
766 let event = TradeEventRef::from(&*event);
767 event_emitter.emit_cpi(&event)?;
768 }
769
770 if is_position_removed {
771 msg!("[Position] the position is removed");
772 }
773
774 ctx.accounts.pay_execution_fee(execution_fee)?;
776
777 Ok(())
778}
779
780impl<'info> internal::Authentication<'info> for ExecuteDecreaseOrder<'info> {
781 fn authority(&self) -> &Signer<'info> {
782 &self.authority
783 }
784
785 fn store(&self) -> &AccountLoader<'info, Store> {
786 &self.store
787 }
788}
789
790impl<'info> ExecuteDecreaseOrder<'info> {
791 #[inline(never)]
792 fn perform_execution(
793 &mut self,
794 remaining_accounts: &'info [AccountInfo<'info>],
795 throw_on_execution_error: bool,
796 event_emitter: &EventEmitter<'_, 'info>,
797 ) -> Result<(RemovePosition, Box<TransferOut>, ShouldSendTradeEvent)> {
798 let feeds = self
800 .order
801 .load()?
802 .swap
803 .to_feeds(&self.token_map.load_token_map()?)
804 .map_err(CoreError::from)?;
805 let ops = ExecuteOrderOperation::builder()
806 .store(&self.store)
807 .market(&self.market)
808 .owner(self.owner.to_account_info())
809 .user(&self.user)
810 .order(&self.order)
811 .position(Some(&self.position))
812 .event(Some(&self.event))
813 .throw_on_execution_error(throw_on_execution_error)
814 .executor(self.authority.to_account_info())
815 .event_emitter(*event_emitter);
816
817 self.oracle.load_mut()?.with_prices(
818 &self.store,
819 &self.token_map,
820 &feeds.tokens,
821 remaining_accounts,
822 self.chainlink_program.as_ref(),
823 #[inline(never)]
824 |oracle, remaining_accounts| {
825 ops.oracle(oracle)
826 .remaining_accounts(remaining_accounts)
827 .build()
828 .execute()
829 },
830 )
831 }
832
833 #[inline(never)]
834 fn process_transfer_out(
835 &self,
836 remaining_accounts: &'info [AccountInfo<'info>],
837 transfer_out: &TransferOut,
838 event_emitter: &EventEmitter<'_, 'info>,
839 ) -> Result<()> {
840 let is_pnl_token_long_token = self.order.load()?.params.side()?.is_long();
841 let final_output_market = self
842 .order
843 .load()?
844 .swap
845 .find_and_unpack_last_market(&self.store.key(), true, remaining_accounts)?
846 .unwrap_or(self.market.clone());
847 ProcessTransferOutOperation::builder()
848 .token_program(self.token_program.to_account_info())
849 .store(&self.store)
850 .market(&self.market)
851 .is_pnl_token_long_token(is_pnl_token_long_token)
852 .final_output_market(&final_output_market)
853 .final_output_token(Some(&self.final_output_token))
854 .final_output_token_account(Some(self.final_output_token_escrow.to_account_info()))
855 .final_output_token_vault(Some(&*self.final_output_token_vault))
856 .long_token(Some(&self.long_token))
857 .long_token_account(Some(self.long_token_escrow.to_account_info()))
858 .long_token_vault(Some(&*self.long_token_vault))
859 .short_token(Some(&self.short_token))
860 .short_token_account(Some(self.short_token_escrow.to_account_info()))
861 .short_token_vault(Some(&*self.short_token_vault))
862 .claimable_long_token_account_for_user(Some(
863 self.claimable_long_token_account_for_user.to_account_info(),
864 ))
865 .claimable_short_token_account_for_user(Some(
866 self.claimable_short_token_account_for_user
867 .to_account_info(),
868 ))
869 .claimable_pnl_token_account_for_holding(Some(
870 self.claimable_pnl_token_account_for_holding
871 .to_account_info(),
872 ))
873 .transfer_out(transfer_out)
874 .event_emitter(*event_emitter)
875 .build()
876 .execute()?;
877 Ok(())
878 }
879
880 #[inline(never)]
881 fn pay_execution_fee(&self, execution_fee: u64) -> Result<()> {
882 let execution_lamports = self.order.load()?.execution_lamports(execution_fee);
883 PayExecutionFeeOperation::builder()
884 .payer(self.order.to_account_info())
885 .receiver(self.authority.to_account_info())
886 .execution_lamports(execution_lamports)
887 .build()
888 .execute()?;
889 Ok(())
890 }
891}