Skip to main content

gmsol_store/ops/
order.rs

1use anchor_lang::prelude::*;
2use anchor_spl::token::{Mint, TokenAccount};
3use gmsol_callback::interface::ActionKind;
4use gmsol_model::{
5    action::decrease_position::{DecreasePositionFlags, DecreasePositionSwapType},
6    num::Unsigned,
7    price::Prices,
8    BaseMarket, BaseMarketExt, MarketAction, PnlFactorKind, Position as _, PositionMut,
9    PositionMutExt, PositionState, PositionStateExt,
10};
11use gmsol_utils::action::ActionCallbackKind;
12use typed_builder::TypedBuilder;
13
14use crate::{
15    events::{EventEmitter, OrderUpdated, PositionDecreased, PositionIncreased, TradeData},
16    states::{
17        callback::CallbackAuthority,
18        common::{
19            action::{Action, ActionExt, ActionParams, On},
20            swap::SwapActionParamsExt,
21        },
22        market::{
23            revertible::{
24                market::RevertibleMarket,
25                revertible_position::RevertiblePosition,
26                swap_market::{SwapDirection, SwapMarkets},
27                Revertible, Revision,
28            },
29            utils::{Adl, ClosableMarket, ValidateMarketBalances},
30        },
31        order::{Order, OrderActionParams, OrderKind, OrderTokenAccounts, TransferOut},
32        position::PositionKind,
33        user::UserHeader,
34        AmountKey, HasMarketMeta, Market, MarketPriceOptions, NonceBytes, Oracle, Position, Store,
35        ValidateOracleTime,
36    },
37    CoreError, ModelError,
38};
39
40use super::{
41    execution_fee::TransferExecutionFeeOperation,
42    market::{MarketTransferOutOperation, RemainingAccountsForMarket},
43};
44
45pub use gmsol_utils::order::PositionCutKind;
46
47/// Create Order Arguments.
48#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, InitSpace)]
49pub struct CreateOrderParams {
50    /// Order Kind.
51    pub kind: OrderKind,
52    /// Decrease Position Swap Type.
53    pub decrease_position_swap_type: Option<DecreasePositionSwapType>,
54    /// Execution fee in lamports.
55    pub execution_lamports: u64,
56    /// The length of the swap path.
57    pub swap_path_length: u8,
58    /// Initial collateral / swap in token amount.
59    pub initial_collateral_delta_amount: u64,
60    /// Size delta value.
61    pub size_delta_value: u128,
62    /// Is long.
63    pub is_long: bool,
64    /// Is collateral or the swap out token the long token.
65    pub is_collateral_long: bool,
66    /// Min output amount or value.
67    pub min_output: Option<u128>,
68    /// Trigger price.
69    pub trigger_price: Option<u128>,
70    /// Acceptable price.
71    pub acceptable_price: Option<u128>,
72    /// Whether to unwrap native token when sending funds back.
73    pub should_unwrap_native_token: bool,
74    /// Valid from timestamp.
75    pub valid_from_ts: Option<i64>,
76}
77
78impl ActionParams for CreateOrderParams {
79    fn execution_lamports(&self) -> u64 {
80        self.execution_lamports
81    }
82}
83
84impl CreateOrderParams {
85    /// Get the related position kind.
86    pub fn to_position_kind(&self) -> Result<PositionKind> {
87        if self.kind.is_swap() {
88            return err!(CoreError::PositionItNotRequired);
89        }
90        if self.is_long {
91            Ok(PositionKind::Long)
92        } else {
93            Ok(PositionKind::Short)
94        }
95    }
96
97    /// Get the collateral token or swap out token address.
98    pub fn collateral_token<'a>(&'a self, meta: &'a impl HasMarketMeta) -> &'a Pubkey {
99        let meta = meta.market_meta();
100        if self.is_collateral_long {
101            &meta.long_token_mint
102        } else {
103            &meta.short_token_mint
104        }
105    }
106}
107
108/// Operations for creating a new order.
109#[derive(TypedBuilder)]
110pub(crate) struct CreateOrderOperation<'a, 'info> {
111    order: AccountLoader<'info, Order>,
112    market: AccountLoader<'info, Market>,
113    store: AccountLoader<'info, Store>,
114    owner: AccountInfo<'info>,
115    receiver: AccountInfo<'info>,
116    #[builder(
117        default,
118        setter(
119            strip_option,
120            doc = "Set the creator of this order. CHECK: It must be the address deriving the order account",
121        )
122    )]
123    creator: Option<AccountInfo<'info>>,
124    nonce: &'a NonceBytes,
125    bump: u8,
126    params: &'a CreateOrderParams,
127    swap_path: &'info [AccountInfo<'info>],
128    callback_version: Option<u8>,
129    callback_authority: Option<&'a Account<'info, CallbackAuthority>>,
130    callback_program: Option<&'a AccountInfo<'info>>,
131    callback_shared_data_account: Option<&'a AccountInfo<'info>>,
132    callback_partitioned_data_account: Option<&'a AccountInfo<'info>>,
133    #[builder(default)]
134    allow_closed: bool,
135    #[builder(setter(into))]
136    event_emitter: Option<EventEmitter<'a, 'info>>,
137}
138
139impl<'a, 'info> CreateOrderOperation<'a, 'info> {
140    pub(crate) fn swap(
141        self,
142    ) -> CreateSwapOrderOperationBuilder<'a, 'info, ((CreateOrderOperation<'a, 'info>,), (), ())>
143    {
144        CreateSwapOrderOperation::builder().common(self)
145    }
146
147    pub(crate) fn increase(
148        self,
149    ) -> CreateIncreaseOrderOperationBuilder<
150        'a,
151        'info,
152        ((CreateOrderOperation<'a, 'info>,), (), (), (), ()),
153    > {
154        CreateIncreaseOrderOperation::builder().common(self)
155    }
156
157    pub(crate) fn decrease(
158        self,
159    ) -> CreateDecreaseOrderOperationBuilder<
160        'a,
161        'info,
162        ((CreateOrderOperation<'a, 'info>,), (), (), (), ()),
163    > {
164        CreateDecreaseOrderOperation::builder().common(self)
165    }
166
167    fn validate(&self) -> Result<()> {
168        self.market
169            .load()?
170            .validate_with_options(&self.store.key(), self.allow_closed)?;
171        ActionExt::validate_balance(&self.order, self.params.execution_lamports)?;
172        Ok(())
173    }
174
175    #[inline(never)]
176    fn init_with(
177        &self,
178        f: impl FnOnce(
179            &CreateOrderParams,
180            &mut OrderTokenAccounts,
181            &mut OrderActionParams,
182        ) -> Result<(Pubkey, Pubkey)>,
183        position: Option<&AccountInfo<'info>>,
184    ) -> Result<()> {
185        let id = self.market.load_mut()?.indexer_mut().next_order_id()?;
186        {
187            let mut order = self.order.load_init()?;
188            let Order {
189                header,
190                market_token,
191                tokens,
192                params,
193                swap,
194                ..
195            } = &mut *order;
196
197            header.init(
198                id,
199                self.store.key(),
200                self.market.key(),
201                self.owner.key(),
202                self.receiver.key(),
203                *self.nonce,
204                self.bump,
205                self.params.execution_lamports,
206                self.params.should_unwrap_native_token,
207            )?;
208
209            if let Some(creator) = self.creator.as_ref() {
210                header.unchecked_set_creator(creator.key());
211            }
212
213            *market_token = self.market.load()?.meta().market_token_mint;
214
215            let (from, to) = (f)(self.params, tokens, params)?;
216
217            let market = self.market.load()?;
218            let meta = market.meta();
219            let swap_path = self.swap_path;
220            // The secondary path is ignored.
221            swap.validate_and_init(
222                meta,
223                self.params.swap_path_length,
224                0,
225                swap_path,
226                &self.store.key(),
227                (&from, &from),
228                (&to, &from),
229            )?;
230        }
231        self.handle_created(position)
232    }
233
234    #[inline(never)]
235    fn handle_created(&self, position: Option<&AccountInfo<'info>>) -> Result<()> {
236        // Ensure that the discriminator is written to the account data.
237        self.order.exit(&crate::ID)?;
238        if let Some(version) = self.callback_version.as_ref() {
239            require_eq!(*version, 0, {
240                msg!("[Callback] orders currently support only callback version `0`");
241                CoreError::InvalidArgument
242            });
243
244            let authority = self
245                .callback_authority
246                .as_ref()
247                .ok_or_else(|| error!(CoreError::InvalidArgument))?;
248            let program = self
249                .callback_program
250                .as_ref()
251                .ok_or_else(|| error!(CoreError::InvalidArgument))?;
252            let shared_data = self
253                .callback_shared_data_account
254                .as_ref()
255                .ok_or_else(|| error!(CoreError::InvalidArgument))?;
256            let partitioned_data = self
257                .callback_partitioned_data_account
258                .as_ref()
259                .ok_or_else(|| error!(CoreError::InvalidArgument))?;
260            let position = position.unwrap_or(program);
261
262            self.order.load_mut()?.header.set_general_callback(
263                program.key,
264                *version,
265                shared_data.key,
266                partitioned_data.key,
267            )?;
268
269            self.order.load()?.header.invoke_general_callback(
270                On::Created(ActionKind::Order),
271                authority,
272                program,
273                shared_data,
274                partitioned_data,
275                &self.owner,
276                self.order.as_ref(),
277                &[position.clone()],
278            )?;
279        }
280
281        if let Some(event_emitter) = self.event_emitter {
282            event_emitter.emit_cpi(&OrderUpdated::new(
283                true,
284                &self.order.key(),
285                &*self.order.load()?,
286            )?)?;
287        }
288
289        Ok(())
290    }
291}
292
293/// Operation for creating a new swap order.
294#[derive(TypedBuilder)]
295pub(crate) struct CreateSwapOrderOperation<'a, 'info> {
296    common: CreateOrderOperation<'a, 'info>,
297    swap_in_token: &'a Account<'info, TokenAccount>,
298    swap_out_token: &'a Account<'info, TokenAccount>,
299}
300
301impl CreateSwapOrderOperation<'_, '_> {
302    pub(crate) fn execute(self) -> Result<()> {
303        self.common.validate()?;
304        self.validate_params_excluding_swap()?;
305
306        self.common.init_with(
307            |create, tokens, params| {
308                tokens.initial_collateral.init(self.swap_in_token);
309                tokens.final_output_token.init(self.swap_out_token);
310                params.init_swap(
311                    create.kind,
312                    self.swap_out_token.mint,
313                    create.initial_collateral_delta_amount,
314                    create.min_output,
315                    create.valid_from_ts,
316                )?;
317                Ok((self.swap_in_token.mint, self.swap_out_token.mint))
318            },
319            None,
320        )?;
321        Ok(())
322    }
323
324    fn validate_params_excluding_swap(&self) -> Result<()> {
325        require!(self.common.params.kind.is_swap(), CoreError::Internal);
326        require!(
327            self.common.params.initial_collateral_delta_amount != 0,
328            CoreError::EmptyOrder
329        );
330        require_gte!(
331            self.swap_in_token.amount,
332            self.common.params.initial_collateral_delta_amount,
333            CoreError::NotEnoughTokenAmount
334        );
335        require!(
336            self.common
337                .market
338                .load()?
339                .meta()
340                .is_collateral_token(&self.swap_out_token.mint),
341            CoreError::TokenMintMismatched
342        );
343        Ok(())
344    }
345}
346
347/// Operation for creating a new increase position order.
348#[derive(TypedBuilder)]
349pub(crate) struct CreateIncreaseOrderOperation<'a, 'info> {
350    common: CreateOrderOperation<'a, 'info>,
351    position: &'a AccountLoader<'info, Position>,
352    initial_collateral_token: &'a Account<'info, TokenAccount>,
353    long_token: &'a Account<'info, TokenAccount>,
354    short_token: &'a Account<'info, TokenAccount>,
355}
356
357impl CreateIncreaseOrderOperation<'_, '_> {
358    pub(crate) fn execute(self) -> Result<()> {
359        self.common.validate()?;
360        self.validate_params_excluding_swap()?;
361
362        let collateral_token = if self.common.params.is_collateral_long {
363            self.common.market.load()?.meta().long_token_mint
364        } else {
365            self.common.market.load()?.meta().short_token_mint
366        };
367
368        self.common.init_with(
369            |create, tokens, params| {
370                tokens
371                    .initial_collateral
372                    .init(self.initial_collateral_token);
373                tokens.long_token.init(self.long_token);
374                tokens.short_token.init(self.short_token);
375                params.init_increase(
376                    create.is_long,
377                    create.kind,
378                    self.position.key(),
379                    collateral_token,
380                    create.initial_collateral_delta_amount,
381                    create.size_delta_value,
382                    create.trigger_price,
383                    create.acceptable_price,
384                    create.min_output,
385                    create.valid_from_ts,
386                )?;
387                Ok((self.initial_collateral_token.mint, collateral_token))
388            },
389            Some(self.position.as_ref()),
390        )?;
391
392        Ok(())
393    }
394
395    fn validate_params_excluding_swap(&self) -> Result<()> {
396        require!(
397            self.common.params.kind.is_increase_position(),
398            CoreError::Internal
399        );
400        require!(
401            self.common.params.size_delta_value != 0
402                || self.common.params.initial_collateral_delta_amount != 0,
403            CoreError::EmptyOrder
404        );
405        require_gte!(
406            self.initial_collateral_token.amount,
407            self.common.params.initial_collateral_delta_amount,
408            CoreError::NotEnoughTokenAmount
409        );
410
411        {
412            let market = self.common.market.load()?;
413            require_keys_eq!(
414                market.meta().long_token_mint,
415                self.long_token.mint,
416                CoreError::TokenMintMismatched
417            );
418            require_keys_eq!(
419                market.meta().short_token_mint,
420                self.short_token.mint,
421                CoreError::TokenMintMismatched
422            );
423            self.position
424                .load()?
425                .validate_for_market(&market, self.common.allow_closed)
426                .map_err(ModelError::from)?;
427        }
428
429        Ok(())
430    }
431}
432
433/// Operation for creating a new decrease position order.
434#[derive(TypedBuilder)]
435pub(crate) struct CreateDecreaseOrderOperation<'a, 'info> {
436    common: CreateOrderOperation<'a, 'info>,
437    position: &'a AccountLoader<'info, Position>,
438    final_output_token: &'a Account<'info, TokenAccount>,
439    long_token: &'a Account<'info, TokenAccount>,
440    short_token: &'a Account<'info, TokenAccount>,
441}
442
443impl CreateDecreaseOrderOperation<'_, '_> {
444    pub(crate) fn execute(self) -> Result<()> {
445        self.common.validate()?;
446        self.validate_params_excluding_swap()?;
447
448        let collateral_token = if self.common.params.is_collateral_long {
449            self.common.market.load()?.meta().long_token_mint
450        } else {
451            self.common.market.load()?.meta().short_token_mint
452        };
453
454        self.common.init_with(
455            |create, tokens, params| {
456                tokens.final_output_token.init(self.final_output_token);
457                tokens.long_token.init(self.long_token);
458                tokens.short_token.init(self.short_token);
459                params.init_decrease(
460                    create.is_long,
461                    create.kind,
462                    self.position.key(),
463                    collateral_token,
464                    create.initial_collateral_delta_amount,
465                    create.size_delta_value,
466                    create.trigger_price,
467                    create.acceptable_price,
468                    create.min_output,
469                    create.decrease_position_swap_type.unwrap_or_default(),
470                    create.valid_from_ts,
471                )?;
472                Ok((collateral_token, self.final_output_token.mint))
473            },
474            Some(self.position.as_ref()),
475        )?;
476        Ok(())
477    }
478
479    fn validate_params_excluding_swap(&self) -> Result<()> {
480        require!(
481            self.common.params.kind.is_decrease_position(),
482            CoreError::Internal
483        );
484
485        // Note: Empty market decrease order is allowed so that the user
486        // can claim funding rebates without modifying the position.
487        require!(
488            self.common.params.size_delta_value != 0
489                || self.common.params.initial_collateral_delta_amount != 0
490                || self.common.params.kind.is_market_decrease(),
491            CoreError::EmptyOrder
492        );
493
494        {
495            let market = self.common.market.load()?;
496            require_keys_eq!(
497                market.meta().long_token_mint,
498                self.long_token.mint,
499                CoreError::TokenMintMismatched
500            );
501            require_keys_eq!(
502                market.meta().short_token_mint,
503                self.short_token.mint,
504                CoreError::TokenMintMismatched
505            );
506            self.position
507                .load()?
508                .validate_for_market(&market, self.common.allow_closed)
509                .map_err(ModelError::from)?;
510        }
511        Ok(())
512    }
513}
514
515/// Operation for processing [`TransferOut`].
516#[derive(TypedBuilder)]
517pub(crate) struct ProcessTransferOutOperation<'a, 'info> {
518    token_program: AccountInfo<'info>,
519    store: &'a AccountLoader<'info, Store>,
520    market: &'a AccountLoader<'info, Market>,
521    is_pnl_token_long_token: bool,
522    #[builder(default, setter(strip_option))]
523    final_output_market: Option<&'a AccountLoader<'info, Market>>,
524    final_output_token: Option<&'a Account<'info, Mint>>,
525    final_output_token_account: Option<AccountInfo<'info>>,
526    final_output_token_vault: Option<&'a Account<'info, TokenAccount>>,
527    long_token: Option<&'a Account<'info, Mint>>,
528    long_token_account: Option<AccountInfo<'info>>,
529    long_token_vault: Option<&'a Account<'info, TokenAccount>>,
530    short_token: Option<&'a Account<'info, Mint>>,
531    short_token_account: Option<AccountInfo<'info>>,
532    short_token_vault: Option<&'a Account<'info, TokenAccount>>,
533    pub(crate) claimable_long_token_account_for_user: Option<AccountInfo<'info>>,
534    pub(crate) claimable_short_token_account_for_user: Option<AccountInfo<'info>>,
535    pub(crate) claimable_pnl_token_account_for_holding: Option<AccountInfo<'info>>,
536    transfer_out: &'a TransferOut,
537    allow_closed: bool,
538    #[builder(setter(into))]
539    event_emitter: EventEmitter<'a, 'info>,
540}
541
542impl<'info> ProcessTransferOutOperation<'_, 'info> {
543    pub(crate) fn execute(self) -> Result<()> {
544        let TransferOut {
545            final_output_token,
546            secondary_output_token,
547            long_token,
548            short_token,
549            long_token_for_claimable_account_of_user,
550            short_token_for_claimable_account_of_user,
551            long_token_for_claimable_account_of_holding,
552            short_token_for_claimable_account_of_holding,
553            ..
554        } = self.transfer_out;
555
556        if *final_output_token != 0 {
557            let (token, market, vault, account) = self.final_output()?;
558            MarketTransferOutOperation::builder()
559                .store(self.store)
560                .market(market)
561                .amount(*final_output_token)
562                .to(account.clone())
563                .vault(vault.to_account_info())
564                .decimals(token.decimals)
565                .token_mint(token.to_account_info())
566                .token_program(self.token_program.clone())
567                .allow_closed(self.allow_closed)
568                .event_emitter(self.event_emitter)
569                .build()
570                .execute()?;
571        }
572
573        let (long_token_amount, short_token_amount) = if self.is_pnl_token_long_token {
574            (
575                secondary_output_token
576                    .checked_add(*long_token)
577                    .ok_or(error!(CoreError::TokenAmountOverflow))?,
578                *short_token,
579            )
580        } else {
581            (
582                *long_token,
583                secondary_output_token
584                    .checked_add(*short_token)
585                    .ok_or(error!(CoreError::TokenAmountOverflow))?,
586            )
587        };
588
589        if long_token_amount != 0 {
590            let (token, vault, account) = self.long_token()?;
591            MarketTransferOutOperation::builder()
592                .store(self.store)
593                .token_program(self.token_program.clone())
594                .market(self.market)
595                .amount(long_token_amount)
596                .vault(vault.to_account_info())
597                .decimals(token.decimals)
598                .token_mint(token.to_account_info())
599                .to(account.clone())
600                .allow_closed(self.allow_closed)
601                .event_emitter(self.event_emitter)
602                .build()
603                .execute()?;
604        }
605
606        if short_token_amount != 0 {
607            let (token, vault, account) = self.short_token()?;
608            MarketTransferOutOperation::builder()
609                .store(self.store)
610                .token_program(self.token_program.clone())
611                .market(self.market)
612                .amount(short_token_amount)
613                .vault(vault.to_account_info())
614                .decimals(token.decimals)
615                .token_mint(token.to_account_info())
616                .to(account.clone())
617                .allow_closed(self.allow_closed)
618                .event_emitter(self.event_emitter)
619                .build()
620                .execute()?;
621        }
622
623        if *long_token_for_claimable_account_of_user != 0 {
624            let (token, vault, account) = self.claimable_long_token_account_for_user()?;
625            MarketTransferOutOperation::builder()
626                .store(self.store)
627                .token_program(self.token_program.clone())
628                .market(self.market)
629                .amount(*long_token_for_claimable_account_of_user)
630                .vault(vault.to_account_info())
631                .decimals(token.decimals)
632                .token_mint(token.to_account_info())
633                .to(account.clone())
634                .allow_closed(self.allow_closed)
635                .event_emitter(self.event_emitter)
636                .build()
637                .execute()?;
638        }
639
640        if *short_token_for_claimable_account_of_user != 0 {
641            let (token, vault, account) = self.claimable_short_token_account_for_user()?;
642            MarketTransferOutOperation::builder()
643                .store(self.store)
644                .token_program(self.token_program.clone())
645                .market(self.market)
646                .amount(*short_token_for_claimable_account_of_user)
647                .vault(vault.to_account_info())
648                .decimals(token.decimals)
649                .token_mint(token.to_account_info())
650                .to(account.clone())
651                .allow_closed(self.allow_closed)
652                .event_emitter(self.event_emitter)
653                .build()
654                .execute()?;
655        }
656
657        if *long_token_for_claimable_account_of_holding != 0 {
658            let (token, vault, account) = self.claimable_long_token_account_for_holding()?;
659            MarketTransferOutOperation::builder()
660                .store(self.store)
661                .token_program(self.token_program.clone())
662                .market(self.market)
663                .amount(*long_token_for_claimable_account_of_holding)
664                .vault(vault.to_account_info())
665                .decimals(token.decimals)
666                .token_mint(token.to_account_info())
667                .to(account.clone())
668                .allow_closed(self.allow_closed)
669                .event_emitter(self.event_emitter)
670                .build()
671                .execute()?;
672        }
673
674        if *short_token_for_claimable_account_of_holding != 0 {
675            let (token, vault, account) = self.claimable_short_token_account_for_holding()?;
676            MarketTransferOutOperation::builder()
677                .store(self.store)
678                .token_program(self.token_program.clone())
679                .market(self.market)
680                .amount(*short_token_for_claimable_account_of_holding)
681                .vault(vault.to_account_info())
682                .decimals(token.decimals)
683                .token_mint(token.to_account_info())
684                .to(account.clone())
685                .event_emitter(self.event_emitter)
686                .allow_closed(self.allow_closed)
687                .build()
688                .execute()?;
689        }
690        Ok(())
691    }
692
693    #[allow(clippy::type_complexity)]
694    fn final_output(
695        &self,
696    ) -> Result<(
697        &Account<'info, Mint>,
698        &AccountLoader<'info, Market>,
699        &Account<'info, TokenAccount>,
700        &AccountInfo<'info>,
701    )> {
702        let token = self
703            .final_output_token
704            .ok_or(error!(CoreError::TokenMintNotProvided))?;
705        let market = self
706            .final_output_market
707            .ok_or(error!(CoreError::MarketMismatched))?;
708        let vault = self
709            .final_output_token_vault
710            .as_ref()
711            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
712        let account = self
713            .final_output_token_account
714            .as_ref()
715            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
716        Ok((token, market, vault, account))
717    }
718
719    fn long_token(
720        &self,
721    ) -> Result<(
722        &Account<'info, Mint>,
723        &Account<'info, TokenAccount>,
724        &AccountInfo<'info>,
725    )> {
726        let token = self
727            .long_token
728            .ok_or(error!(CoreError::TokenMintNotProvided))?;
729        let vault = self
730            .long_token_vault
731            .as_ref()
732            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
733        let account = self
734            .long_token_account
735            .as_ref()
736            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
737        Ok((token, vault, account))
738    }
739
740    fn short_token(
741        &self,
742    ) -> Result<(
743        &Account<'info, Mint>,
744        &Account<'info, TokenAccount>,
745        &AccountInfo<'info>,
746    )> {
747        let token = self
748            .short_token
749            .ok_or(error!(CoreError::TokenMintNotProvided))?;
750        let vault = self
751            .short_token_vault
752            .as_ref()
753            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
754        let account = self
755            .short_token_account
756            .as_ref()
757            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
758        Ok((token, vault, account))
759    }
760
761    fn claimable_long_token_account_for_user(
762        &self,
763    ) -> Result<(
764        &Account<'info, Mint>,
765        &Account<'info, TokenAccount>,
766        &AccountInfo<'info>,
767    )> {
768        let token = self
769            .long_token
770            .ok_or(error!(CoreError::TokenMintNotProvided))?;
771        let vault = self
772            .long_token_vault
773            .as_ref()
774            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
775        let account = self
776            .claimable_long_token_account_for_user
777            .as_ref()
778            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
779        Ok((token, vault, account))
780    }
781
782    fn claimable_short_token_account_for_user(
783        &self,
784    ) -> Result<(
785        &Account<'info, Mint>,
786        &Account<'info, TokenAccount>,
787        &AccountInfo<'info>,
788    )> {
789        let token = self
790            .short_token
791            .ok_or(error!(CoreError::TokenMintNotProvided))?;
792        let vault = self
793            .short_token_vault
794            .as_ref()
795            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
796        let account = self
797            .claimable_short_token_account_for_user
798            .as_ref()
799            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
800        Ok((token, vault, account))
801    }
802
803    fn claimable_long_token_account_for_holding(
804        &self,
805    ) -> Result<(
806        &Account<'info, Mint>,
807        &Account<'info, TokenAccount>,
808        &AccountInfo<'info>,
809    )> {
810        let token = self
811            .long_token
812            .ok_or(error!(CoreError::TokenMintNotProvided))?;
813        let vault = self
814            .long_token_vault
815            .as_ref()
816            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
817        let account = self
818            .claimable_pnl_token_account_for_holding
819            .as_ref()
820            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
821        Ok((token, vault, account))
822    }
823
824    fn claimable_short_token_account_for_holding(
825        &self,
826    ) -> Result<(
827        &Account<'info, Mint>,
828        &Account<'info, TokenAccount>,
829        &AccountInfo<'info>,
830    )> {
831        let token = self
832            .short_token
833            .ok_or(error!(CoreError::TokenMintNotProvided))?;
834        let vault = self
835            .short_token_vault
836            .as_ref()
837            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
838        let account = self
839            .claimable_pnl_token_account_for_holding
840            .as_ref()
841            .ok_or(error!(CoreError::TokenAccountNotProvided))?;
842        Ok((token, vault, account))
843    }
844}
845
846/// Operation for executing order.
847#[derive(TypedBuilder)]
848pub(crate) struct ExecuteOrderOperation<'a, 'info> {
849    executor: AccountInfo<'info>,
850    user: &'a AccountLoader<'info, UserHeader>,
851    store: &'a AccountLoader<'info, Store>,
852    market: &'a AccountLoader<'info, Market>,
853    order: &'a AccountLoader<'info, Order>,
854    owner: AccountInfo<'info>,
855    position: Option<&'a AccountLoader<'info, Position>>,
856    event: Option<&'a AccountLoader<'info, TradeData>>,
857    oracle: &'a Oracle,
858    remaining_accounts: &'info [AccountInfo<'info>],
859    throw_on_execution_error: bool,
860    #[builder(default)]
861    refund: u64,
862    #[builder(setter(into))]
863    event_emitter: EventEmitter<'a, 'info>,
864    callback_authority: Option<&'a Account<'info, CallbackAuthority>>,
865    callback_program: Option<&'a AccountInfo<'info>>,
866    callback_shared_data_account: Option<&'a AccountInfo<'info>>,
867    callback_partitioned_data_account: Option<&'a AccountInfo<'info>>,
868    #[builder(default)]
869    allow_closed: bool,
870}
871
872pub(crate) type RemovePosition = bool;
873pub(crate) type ShouldSendTradeEvent = bool;
874
875enum SecondaryOrderType {
876    Liquidation,
877    AutoDeleveraging,
878}
879
880impl ExecuteOrderOperation<'_, '_> {
881    fn price_options(&self) -> MarketPriceOptions {
882        MarketPriceOptions {
883            allow_index_closed: self.allow_closed,
884            allow_long_closed: false,
885            allow_short_closed: false,
886        }
887    }
888
889    fn prices(&self) -> Result<Prices<u128>> {
890        self.oracle
891            .market_prices_with_options(&*self.market.load()?, self.price_options())
892    }
893
894    #[inline(never)]
895    pub(crate) fn execute(
896        self,
897    ) -> Result<(RemovePosition, Box<TransferOut>, ShouldSendTradeEvent)> {
898        let mut remove_position = false;
899
900        self.order.load()?.validate_valid_from_ts()?;
901
902        match self.validate_oracle() {
903            Ok(()) => {}
904            Err(CoreError::OracleTimestampsAreLargerThanRequired)
905                if !self.throw_on_execution_error =>
906            {
907                msg!(
908                    "Order expired at {}",
909                    self.oracle_updated_before()
910                        .ok()
911                        .flatten()
912                        .expect("must have an expiration time"),
913                );
914                // NOTE: Currently, the position account is not closed on order expiry.
915                return Ok((false, Box::new(TransferOut::new_failed()), false));
916            }
917            Err(err) => {
918                return Err(error!(err));
919            }
920        }
921
922        let mut should_throw_error = false;
923        let prices = self.prices()?;
924        let discount = self.validate_and_get_order_fee_discount()?;
925        let res = match self.perform_execution(&mut should_throw_error, prices, discount) {
926            Ok((should_remove_position, mut transfer_out, should_send_trade_event)) => {
927                transfer_out.set_executed(true);
928                remove_position = should_remove_position;
929                Ok((transfer_out, should_send_trade_event))
930            }
931            Err(err) if !(should_throw_error || self.throw_on_execution_error) => {
932                msg!("Execute order error: {}", err);
933                remove_position = self
934                    .position
935                    .as_ref()
936                    .map(|a| Result::Ok(a.load()?.state.is_empty()))
937                    .transpose()?
938                    .unwrap_or(false);
939                Ok((Default::default(), false))
940            }
941            Err(err) => Err(err),
942        };
943
944        let (transfer_out, should_send_trade_event) = res?;
945
946        self.handle_executed(
947            transfer_out.executed(),
948            !remove_position,
949            should_send_trade_event,
950        )?;
951
952        let keep_position_account = self.order.load()?.params().should_keep_position_account();
953        if remove_position && !keep_position_account {
954            self.close_position()?;
955        }
956
957        Ok((remove_position, transfer_out, should_send_trade_event))
958    }
959
960    #[inline(never)]
961    fn validate_and_get_order_fee_discount(&self) -> Result<u128> {
962        require!(
963            self.user.load()?.is_initialized(),
964            CoreError::InvalidUserAccount
965        );
966        let (rank, is_referred) = {
967            let user = self.user.load()?;
968            (user.gt.rank(), user.referral.referrer().is_some())
969        };
970        let discount_factor = self
971            .store
972            .load()?
973            .order_fee_discount_factor(rank, is_referred)?;
974        msg!(
975            "[Order] apply a {} order fee discount (factor) for this {} rank {} user",
976            discount_factor,
977            if is_referred {
978                "referred"
979            } else {
980                "non-referred"
981            },
982            rank,
983        );
984        Ok(discount_factor)
985    }
986
987    #[inline(never)]
988    fn perform_execution(
989        &self,
990        should_throw_error: &mut bool,
991        prices: Prices<u128>,
992        order_fee_discount_factor: u128,
993    ) -> Result<(RemovePosition, Box<TransferOut>, ShouldSendTradeEvent)> {
994        self.validate_market()?;
995        self.validate_order(should_throw_error, &prices)?;
996
997        // Prepare execution context.
998        let gt_minting_enabled = self.market.load()?.is_gt_minting_enabled();
999        let current_market_token = self.market.load()?.market_meta().market_token_mint;
1000
1001        let remaining_accounts = RemainingAccountsForMarket::new(
1002            self.remaining_accounts,
1003            current_market_token,
1004            Some(self.order.load()?.swap()),
1005        )?;
1006        let virtual_inventories = remaining_accounts.load_virtual_inventories()?;
1007
1008        let mut market =
1009            RevertibleMarket::new(self.market, Some(&virtual_inventories), self.event_emitter)?
1010                .with_order_fee_discount_factor(order_fee_discount_factor);
1011        let mut swap_markets = SwapMarkets::new(
1012            &self.store.key(),
1013            remaining_accounts.swap_market_loaders(),
1014            Some(&current_market_token),
1015            &virtual_inventories,
1016            self.event_emitter,
1017        )?;
1018        let mut transfer_out = Box::default();
1019
1020        market.update_fees_state(&prices)?;
1021
1022        let kind = self.order.load()?.params.kind()?;
1023        let mut should_send_trade_event = false;
1024        let should_remove_position = match &kind {
1025            OrderKind::MarketSwap | OrderKind::LimitSwap => {
1026                execute_swap(
1027                    should_throw_error,
1028                    self.oracle,
1029                    &mut market,
1030                    &mut swap_markets,
1031                    &mut transfer_out,
1032                    &mut *self.order.load_mut()?,
1033                )?;
1034                market.commit();
1035                false
1036            }
1037            OrderKind::MarketIncrease
1038            | OrderKind::MarketDecrease
1039            | OrderKind::Liquidation
1040            | OrderKind::AutoDeleveraging
1041            | OrderKind::LimitIncrease
1042            | OrderKind::LimitDecrease
1043            | OrderKind::StopLossDecrease => {
1044                let position_loader = self
1045                    .position
1046                    .as_ref()
1047                    .ok_or(error!(CoreError::PositionIsRequired))?;
1048                let event_loader = self
1049                    .event
1050                    .as_ref()
1051                    .ok_or(error!(CoreError::EventBufferNotProvided))?;
1052                {
1053                    let position = position_loader.load()?;
1054                    let mut event = event_loader.load_mut()?;
1055                    let is_collateral_long = market
1056                        .market_meta()
1057                        .to_token_side(&position.collateral_token)
1058                        .map_err(CoreError::from)?;
1059                    event.init(
1060                        kind.is_increase_position(),
1061                        is_collateral_long,
1062                        position_loader.key(),
1063                        &position,
1064                        self.order.key(),
1065                    )?;
1066                    should_send_trade_event = true;
1067                }
1068                let mut position =
1069                    RevertiblePosition::new(market, position_loader, self.allow_closed)?;
1070
1071                position.on_validate().map_err(ModelError::from)?;
1072
1073                let (should_remove_position, paid_fee_value) = match kind {
1074                    OrderKind::MarketIncrease | OrderKind::LimitIncrease => {
1075                        let paid_fee_value = execute_increase_position(
1076                            self.oracle,
1077                            prices,
1078                            &mut position,
1079                            &mut swap_markets,
1080                            &mut transfer_out,
1081                            &mut *event_loader.load_mut()?,
1082                            &mut *self.order.load_mut()?,
1083                        )?;
1084                        (false, paid_fee_value)
1085                    }
1086                    OrderKind::Liquidation => execute_decrease_position(
1087                        self.oracle,
1088                        prices,
1089                        &mut position,
1090                        &mut swap_markets,
1091                        &mut transfer_out,
1092                        &mut *event_loader.load_mut()?,
1093                        &mut *self.order.load_mut()?,
1094                        true,
1095                        Some(SecondaryOrderType::Liquidation),
1096                    )?,
1097                    OrderKind::AutoDeleveraging => execute_decrease_position(
1098                        self.oracle,
1099                        prices,
1100                        &mut position,
1101                        &mut swap_markets,
1102                        &mut transfer_out,
1103                        &mut *event_loader.load_mut()?,
1104                        &mut *self.order.load_mut()?,
1105                        true,
1106                        Some(SecondaryOrderType::AutoDeleveraging),
1107                    )?,
1108                    OrderKind::MarketDecrease
1109                    | OrderKind::LimitDecrease
1110                    | OrderKind::StopLossDecrease => execute_decrease_position(
1111                        self.oracle,
1112                        prices,
1113                        &mut position,
1114                        &mut swap_markets,
1115                        &mut transfer_out,
1116                        &mut *event_loader.load_mut()?,
1117                        &mut *self.order.load_mut()?,
1118                        false,
1119                        None,
1120                    )?,
1121                    _ => unreachable!(),
1122                };
1123
1124                position.write_to_event(&mut *event_loader.load_mut()?)?;
1125                event_loader
1126                    .load_mut()?
1127                    .update_with_transfer_out(&transfer_out)?;
1128
1129                if gt_minting_enabled {
1130                    self.order.load_mut()?.unchecked_process_gt(
1131                        &mut *self.store.load_mut()?,
1132                        &mut *self.user.load_mut()?,
1133                        paid_fee_value,
1134                        position.event_emitter(),
1135                    )?;
1136                } else {
1137                    msg!("[GT] GT minting is disabled for this market");
1138                }
1139
1140                position.commit();
1141                msg!(
1142                    "[Position] executed with trade_id={}",
1143                    self.position
1144                        .as_ref()
1145                        .unwrap()
1146                        .load()
1147                        .unwrap()
1148                        .state
1149                        .trade_id
1150                );
1151                should_remove_position
1152            }
1153            _ => return err!(CoreError::UnknownOrderKind),
1154        };
1155        swap_markets.commit();
1156        virtual_inventories.commit();
1157        Ok((
1158            should_remove_position,
1159            transfer_out,
1160            should_send_trade_event,
1161        ))
1162    }
1163
1164    fn close_position(&self) -> Result<()> {
1165        let Some(position) = self.position else {
1166            return err!(CoreError::PositionIsRequired);
1167        };
1168
1169        let balance = position.to_account_info().lamports();
1170
1171        if balance < self.refund {
1172            msg!(
1173                "Warn: not enough balance to pay the executor, balance = {}, refund = {}",
1174                balance,
1175                self.refund,
1176            );
1177        }
1178
1179        let refund_to_owner = balance.saturating_sub(self.refund);
1180        let refund_to_executor = balance.checked_sub(refund_to_owner).expect("must success");
1181
1182        // Since the order account must be mutable and is owned by the current program,
1183        // we use it as an intermediary to distribute funds.
1184        position.close(self.order.to_account_info())?;
1185
1186        if refund_to_owner != 0 {
1187            self.order.sub_lamports(refund_to_owner)?;
1188            self.owner.add_lamports(refund_to_owner)?;
1189        }
1190
1191        if refund_to_executor != 0 {
1192            self.order.sub_lamports(refund_to_executor)?;
1193            self.executor.add_lamports(refund_to_executor)?;
1194        }
1195
1196        Ok(())
1197    }
1198
1199    fn validate_oracle(&self) -> crate::CoreResult<()> {
1200        self.oracle.validate_time(self)?;
1201        let (kind, is_long) = {
1202            let order = self.order.load().map_err(|_| CoreError::LoadAccountError)?;
1203            (
1204                order
1205                    .params
1206                    .kind()
1207                    .map_err(|_| CoreError::InvalidArgument)?,
1208                order
1209                    .params
1210                    .side()
1211                    .map_err(|_| CoreError::InvalidArgument)?
1212                    .is_long(),
1213            )
1214        };
1215        // Validate ADL state.
1216        if let OrderKind::AutoDeleveraging = kind {
1217            let max_staleness = *self
1218                .store
1219                .load()
1220                .map_err(|_| CoreError::LoadAccountError)?
1221                .get_amount_by_key(AmountKey::AdlPricesMaxStaleness)
1222                .ok_or(CoreError::Unimplemented)?;
1223            self.market
1224                .load()
1225                .map_err(|_| CoreError::LoadAccountError)?
1226                .validate_adl(self.oracle, is_long, max_staleness)?;
1227        };
1228        // Validate closed state.
1229        if self.allow_closed {
1230            let store = self.store.load().map_err(|_| CoreError::LoadAccountError)?;
1231            self.market
1232                .load()
1233                .map_err(|_| CoreError::LoadAccountError)?
1234                .validate_open_or_nonstale_oracle_with_store(self.oracle, &store)?;
1235        }
1236        Ok(())
1237    }
1238
1239    fn validate_market(&self) -> Result<()> {
1240        self.market
1241            .load()?
1242            .validate_with_options(&self.store.key(), self.allow_closed)?;
1243        Ok(())
1244    }
1245
1246    fn validate_order(&self, should_throw_error: &mut bool, prices: &Prices<u128>) -> Result<()> {
1247        self.validate_non_empty_order()?;
1248        match self.validate_trigger_price(prices) {
1249            Ok(()) => Ok(()),
1250            Err(err) => {
1251                if !self.order.load()?.params.kind()?.is_market() {
1252                    *should_throw_error = true;
1253                }
1254                Err(err)
1255            }
1256        }
1257    }
1258
1259    fn validate_non_empty_order(&self) -> Result<()> {
1260        let order = self.order.load()?;
1261        let params = &order.params;
1262        let kind = params.kind()?;
1263
1264        if kind.is_increase_position() || kind.is_decrease_position() {
1265            // Note: Empty market decrease order is allowed so that the user
1266            // can claim funding rebates without modifying the position.
1267            require!(
1268                params.size_delta_value != 0
1269                    || params.initial_collateral_delta_amount != 0
1270                    || kind.is_market_decrease(),
1271                CoreError::EmptyOrder
1272            );
1273        } else if kind.is_swap() {
1274            require!(
1275                params.initial_collateral_delta_amount != 0,
1276                CoreError::EmptyOrder
1277            );
1278        } else {
1279            unreachable!()
1280        }
1281        Ok(())
1282    }
1283
1284    fn validate_trigger_price(&self, prices: &Prices<u128>) -> Result<()> {
1285        self.order
1286            .load()?
1287            .validate_trigger_price(&prices.index_token_price)
1288    }
1289
1290    #[inline(never)]
1291    fn handle_executed(
1292        &self,
1293        success: bool,
1294        may_have_position: bool,
1295        has_trade_event: bool,
1296    ) -> Result<()> {
1297        match self.order.load()?.header.callback_kind()? {
1298            ActionCallbackKind::Disabled => {}
1299            ActionCallbackKind::General => {
1300                let authority = self.callback_authority.as_ref().ok_or_else(|| {
1301                    msg!("[Callback] callback is specified, but required accounts are missing");
1302                    error!(CoreError::InvalidArgument)
1303                })?;
1304                let program = self
1305                    .callback_program
1306                    .ok_or_else(|| error!(CoreError::InvalidArgument))?;
1307                let shared_data = self
1308                    .callback_shared_data_account
1309                    .ok_or_else(|| error!(CoreError::InvalidArgument))?;
1310                let partitioned_data = self
1311                    .callback_partitioned_data_account
1312                    .ok_or_else(|| error!(CoreError::InvalidArgument))?;
1313                let position = (may_have_position)
1314                    .then_some(())
1315                    .and_then(|_| self.position.as_ref().map(|p| p.as_ref()))
1316                    .unwrap_or(program);
1317                let trade_event = (has_trade_event)
1318                    .then_some(())
1319                    .and_then(|_| self.event.as_ref().map(|a| a.as_ref()))
1320                    .unwrap_or(program);
1321
1322                self.order.load()?.header.invoke_general_callback(
1323                    On::Executed(ActionKind::Order, success),
1324                    authority,
1325                    program,
1326                    shared_data,
1327                    partitioned_data,
1328                    &self.owner,
1329                    self.order.as_ref(),
1330                    &[position.clone(), trade_event.clone()],
1331                )?;
1332            }
1333            kind => {
1334                msg!("[Callback] unsupported callback kind: {}", kind);
1335            }
1336        }
1337        Ok(())
1338    }
1339}
1340
1341impl ValidateOracleTime for ExecuteOrderOperation<'_, '_> {
1342    fn oracle_updated_after(&self) -> crate::CoreResult<Option<i64>> {
1343        let (kind, updated_at, valid_from_ts) = {
1344            let order = self.order.load().map_err(|_| CoreError::LoadAccountError)?;
1345            (
1346                order
1347                    .params()
1348                    .kind()
1349                    .map_err(|_| CoreError::InvalidArgument)?,
1350                order.header.updated_at,
1351                order.params().valid_from_ts,
1352            )
1353        };
1354
1355        match kind {
1356            OrderKind::MarketSwap | OrderKind::MarketIncrease => Ok(Some(updated_at)),
1357            OrderKind::MarketDecrease => {
1358                let position = self
1359                    .position
1360                    .as_ref()
1361                    .ok_or(CoreError::PositionIsRequired)?
1362                    .load()
1363                    .map_err(|_| CoreError::LoadAccountError)?;
1364                Ok(Some(updated_at.max(position.state.increased_at)))
1365            }
1366            OrderKind::LimitSwap | OrderKind::LimitIncrease => {
1367                Ok(Some(updated_at.max(valid_from_ts)))
1368            }
1369            OrderKind::LimitDecrease | OrderKind::StopLossDecrease => {
1370                let position = self
1371                    .position
1372                    .as_ref()
1373                    .ok_or(CoreError::PositionIsRequired)?
1374                    .load()
1375                    .map_err(|_| CoreError::LoadAccountError)?;
1376                let last_updated = updated_at.max(position.state.increased_at);
1377                Ok(Some(last_updated.max(valid_from_ts)))
1378            }
1379            OrderKind::Liquidation => {
1380                let position = self
1381                    .position
1382                    .as_ref()
1383                    .ok_or(CoreError::PositionIsRequired)?
1384                    .load()
1385                    .map_err(|_| CoreError::LoadAccountError)?;
1386                Ok(Some(
1387                    position.state.increased_at.max(position.state.decreased_at),
1388                ))
1389            }
1390            // Ignore the check of oracle ts for ADL orders.
1391            OrderKind::AutoDeleveraging => Ok(None),
1392            _ => Err(CoreError::UnknownOrderKind),
1393        }
1394    }
1395
1396    fn oracle_updated_before(&self) -> crate::CoreResult<Option<i64>> {
1397        let (kind, updated_at) = {
1398            let order = self.order.load().map_err(|_| CoreError::LoadAccountError)?;
1399            (
1400                order
1401                    .params
1402                    .kind()
1403                    .map_err(|_| CoreError::InvalidArgument)?,
1404                order.header().updated_at,
1405            )
1406        };
1407        let ts = match kind {
1408            OrderKind::MarketSwap | OrderKind::MarketIncrease | OrderKind::MarketDecrease => {
1409                Some(updated_at)
1410            }
1411            _ => None,
1412        };
1413        ts.map(|ts| {
1414            self.store
1415                .load()
1416                .map_err(|_| CoreError::LoadAccountError)?
1417                .request_expiration_at(ts)
1418        })
1419        .transpose()
1420    }
1421
1422    fn oracle_updated_after_slot(&self) -> crate::CoreResult<Option<u64>> {
1423        let (kind, updated_at_slot) = {
1424            let order = self.order.load().map_err(|_| CoreError::LoadAccountError)?;
1425            (
1426                order
1427                    .params
1428                    .kind()
1429                    .map_err(|_| CoreError::InvalidArgument)?,
1430                order.header().updated_at_slot,
1431            )
1432        };
1433        let after = match kind {
1434            OrderKind::Liquidation | OrderKind::AutoDeleveraging => None,
1435            _ => Some(updated_at_slot),
1436        };
1437        Ok(after)
1438    }
1439}
1440
1441#[inline(never)]
1442fn execute_swap(
1443    should_throw_error: &mut bool,
1444    oracle: &Oracle,
1445    market: &mut RevertibleMarket<'_, '_>,
1446    swap_markets: &mut SwapMarkets<'_, '_>,
1447    transfer_out: &mut TransferOut,
1448    order: &mut Order,
1449) -> Result<()> {
1450    let swap_out_token = order
1451        .tokens
1452        .final_output_token
1453        .token()
1454        .ok_or(error!(CoreError::MissingFinalOutputToken))?;
1455    // Perform swap.
1456    let swap_out_amount = {
1457        let swap = &order.swap;
1458        let initial_collateral_token = order
1459            .tokens
1460            .initial_collateral
1461            .token()
1462            .ok_or(error!(CoreError::MissingInitialCollateralToken))?;
1463        let amount = order.params.initial_collateral_delta_amount;
1464        let (swap_out_amount, _) = swap_markets.revertible_swap(
1465            SwapDirection::Into(market),
1466            oracle,
1467            swap,
1468            (swap_out_token, swap_out_token),
1469            (Some(initial_collateral_token), None),
1470            (amount, 0),
1471        )?;
1472        swap_out_amount
1473    };
1474    if let Err(err) = order.validate_output_amount(swap_out_amount.into()) {
1475        if !order.params.kind()?.is_market() {
1476            *should_throw_error = true;
1477        }
1478        return Err(err);
1479    }
1480    transfer_out.transfer_out(false, swap_out_amount)?;
1481    Ok(())
1482}
1483
1484#[inline(never)]
1485fn execute_increase_position(
1486    oracle: &Oracle,
1487    prices: Prices<u128>,
1488    position: &mut RevertiblePosition<'_, '_>,
1489    swap_markets: &mut SwapMarkets<'_, '_>,
1490    transfer_out: &mut TransferOut,
1491    event: &mut TradeData,
1492    order: &mut Order,
1493) -> Result<u128> {
1494    let params = &order.params;
1495
1496    // Perform swap.
1497    let collateral_increment_amount = {
1498        let initial_collateral_token = order
1499            .tokens
1500            .initial_collateral
1501            .token()
1502            .ok_or(error!(CoreError::MissingInitialCollateralToken))?;
1503        let swap = &order.swap;
1504        let collateral_token = *position.collateral_token();
1505        let (collateral_increment_amount, _) = swap_markets.revertible_swap(
1506            SwapDirection::Into(position.market_mut()),
1507            oracle,
1508            swap,
1509            (collateral_token, collateral_token),
1510            (Some(initial_collateral_token), None),
1511            (params.initial_collateral_delta_amount, 0),
1512        )?;
1513        collateral_increment_amount
1514    };
1515
1516    // Validate that the collateral amount swapped out is sufficient.
1517    // Here, `min_output` refers to the minimum amount of collateral tokens expected
1518    // after the swap.
1519    order.validate_output_amount(collateral_increment_amount.into())?;
1520
1521    // Increase position.
1522    let (long_amount, short_amount, paid_order_fee_value) = {
1523        let size_delta_usd = params.size_delta_value;
1524        let acceptable_price = params.acceptable_price;
1525        let report = position
1526            .increase(
1527                prices,
1528                collateral_increment_amount.into(),
1529                size_delta_usd,
1530                Some(acceptable_price),
1531            )
1532            .and_then(|a| a.execute())
1533            .map_err(ModelError::from)?;
1534
1535        let (&long_amount, &short_amount) = report.claimable_funding_amounts();
1536        let paid_fee_value = *report.fees().paid_order_and_borrowing_fee_value();
1537        event.update_with_increase_report(&report)?;
1538
1539        position
1540            .event_emitter()
1541            .emit_cpi(&PositionIncreased::from_report(
1542                position.market().rev(),
1543                position.market().market_meta().market_token_mint,
1544                report,
1545            ))?;
1546        msg!("[Position] increased");
1547
1548        (long_amount, short_amount, paid_fee_value)
1549    };
1550
1551    // Process output amount.
1552    transfer_out.transfer_out_funding_amounts(&long_amount, &short_amount)?;
1553
1554    position.market().validate_market_balances(
1555        long_amount
1556            .try_into()
1557            .map_err(|_| error!(CoreError::TokenAmountOverflow))?,
1558        short_amount
1559            .try_into()
1560            .map_err(|_| error!(CoreError::TokenAmountOverflow))?,
1561    )?;
1562
1563    Ok(paid_order_fee_value)
1564}
1565
1566#[allow(clippy::too_many_arguments)]
1567#[inline(never)]
1568fn execute_decrease_position(
1569    oracle: &Oracle,
1570    prices: Prices<u128>,
1571    position: &mut RevertiblePosition<'_, '_>,
1572    swap_markets: &mut SwapMarkets<'_, '_>,
1573    transfer_out: &mut TransferOut,
1574    event: &mut TradeData,
1575    order: &mut Order,
1576    is_insolvent_close_allowed: bool,
1577    secondary_order_type: Option<SecondaryOrderType>,
1578) -> Result<(RemovePosition, u128)> {
1579    // Decrease position.
1580    let report = {
1581        let params = &order.params;
1582        let decrease_position_swap_type = params.decrease_position_swap_type()?;
1583        let collateral_withdrawal_amount = params.initial_collateral_delta_amount as u128;
1584        let size_delta_usd = params.size_delta_value;
1585        let acceptable_price = params.acceptable_price;
1586        let is_liquidation_order =
1587            matches!(secondary_order_type, Some(SecondaryOrderType::Liquidation));
1588        let is_adl_order = matches!(
1589            secondary_order_type,
1590            Some(SecondaryOrderType::AutoDeleveraging)
1591        );
1592
1593        let is_cap_size_delta_usd_allowed = matches!(
1594            order.params().kind()?,
1595            OrderKind::LimitDecrease | OrderKind::StopLossDecrease
1596        );
1597
1598        // Only required when the order is an ADL order.
1599        let mut pnl_factor_before_execution = None;
1600
1601        // Validate the liquidation is a fully close.
1602        if is_liquidation_order {
1603            require_gte!(
1604                size_delta_usd,
1605                *position.size_in_usd(),
1606                CoreError::InvalidArgument
1607            );
1608        }
1609
1610        // Validate that ADL is required.
1611        if is_adl_order {
1612            let Some(pnl_factor) = position
1613                .market()
1614                .pnl_factor_exceeded(&prices, PnlFactorKind::ForAdl, params.side()?.is_long())
1615                .map_err(ModelError::from)?
1616                .map(|exceeded| exceeded.pnl_factor)
1617            else {
1618                return err!(CoreError::AdlNotRequired);
1619            };
1620            pnl_factor_before_execution = Some(pnl_factor);
1621        }
1622
1623        let report = position
1624            .decrease(
1625                prices,
1626                size_delta_usd,
1627                Some(acceptable_price),
1628                collateral_withdrawal_amount,
1629                DecreasePositionFlags {
1630                    is_insolvent_close_allowed,
1631                    is_liquidation_order,
1632                    is_cap_size_delta_usd_allowed,
1633                },
1634            )
1635            .map(|a| a.set_swap(decrease_position_swap_type))
1636            .and_then(|a| a.execute())
1637            .map_err(ModelError::from)?;
1638
1639        // Validate that ADL is valid.
1640        if is_adl_order {
1641            let pnl_factor_after_execution = position
1642                .market()
1643                .pnl_factor(&prices, params.side()?.is_long(), true)
1644                .map_err(ModelError::from)?;
1645            require_gt!(
1646                pnl_factor_before_execution.expect("must be some"),
1647                pnl_factor_after_execution,
1648                CoreError::InvalidAdl
1649            );
1650            let min_pnl_factor = position
1651                .market()
1652                .pnl_factor_config(PnlFactorKind::MinAfterAdl, params.side()?.is_long())
1653                .and_then(|factor| factor.to_signed())
1654                .map_err(ModelError::from)?;
1655            require_gte!(
1656                pnl_factor_after_execution,
1657                min_pnl_factor,
1658                CoreError::InvalidAdl
1659            );
1660        }
1661
1662        event.update_with_decrease_report(&report, &prices)?;
1663        report
1664    };
1665    let should_remove_position = report.should_remove();
1666
1667    // Perform swaps.
1668    {
1669        require!(
1670            *report.secondary_output_amount() == 0
1671                || (report.is_output_token_long() != report.is_secondary_output_token_long()),
1672            CoreError::SameOutputTokensNotMerged,
1673        );
1674        let (is_output_token_long, output_amount, secondary_output_amount) = (
1675            report.is_output_token_long(),
1676            (*report.output_amount())
1677                .try_into()
1678                .map_err(|_| error!(CoreError::TokenAmountOverflow))?,
1679            (*report.secondary_output_amount())
1680                .try_into()
1681                .map_err(|_| error!(CoreError::TokenAmountOverflow))?,
1682        );
1683
1684        // Swap output token to the expected output token.
1685        let meta = *position.market().market_meta();
1686        let token_ins = if is_output_token_long {
1687            (Some(meta.long_token_mint), Some(meta.short_token_mint))
1688        } else {
1689            (Some(meta.short_token_mint), Some(meta.long_token_mint))
1690        };
1691
1692        // Since we have checked that secondary_amount must be zero if output_token == secondary_output_token,
1693        // the swap should still be correct.
1694
1695        let final_output_token = order
1696            .tokens
1697            .final_output_token
1698            .token()
1699            .ok_or(error!(CoreError::MissingFinalOutputToken))?;
1700        let secondary_output_token = order.secondary_output_token()?;
1701        let swap = &order.swap;
1702        let (output_amount, secondary_output_amount) = swap_markets.revertible_swap(
1703            SwapDirection::From(position.market_mut()),
1704            oracle,
1705            swap,
1706            (final_output_token, secondary_output_token),
1707            token_ins,
1708            (output_amount, secondary_output_amount),
1709        )?;
1710        order.validate_decrease_output_amounts(
1711            oracle,
1712            &final_output_token,
1713            output_amount,
1714            &secondary_output_token,
1715            secondary_output_amount,
1716        )?;
1717        transfer_out.transfer_out(false, output_amount)?;
1718        transfer_out.transfer_out(true, secondary_output_amount)?;
1719        event.set_final_output_token(&final_output_token);
1720    }
1721
1722    // Process other output amounts.
1723    {
1724        let (long_amount, short_amount) = report.claimable_funding_amounts();
1725        transfer_out.transfer_out_funding_amounts(long_amount, short_amount)?;
1726        transfer_out.process_claimable_collateral_for_decrease(&report)?;
1727    }
1728
1729    // Validate market balances.
1730    let mut long_transfer_out = transfer_out.total_long_token_amount()?;
1731    let mut short_transfer_out = transfer_out.total_short_token_amount()?;
1732    let mut add_to_amount = |is_long_token: bool, amount: u64| {
1733        let acc = if is_long_token {
1734            &mut long_transfer_out
1735        } else {
1736            &mut short_transfer_out
1737        };
1738        *acc = acc
1739            .checked_add(amount)
1740            .ok_or(error!(CoreError::TokenAmountOverflow))?;
1741        Result::Ok(())
1742    };
1743    let current_market_token = position.market().key();
1744    let meta = position.market().market_meta();
1745    let tokens = &order.tokens;
1746    let output_token_market = order
1747        .swap
1748        .last_market_token(true)
1749        .unwrap_or(&current_market_token);
1750    let secondary_token_market = order
1751        .swap
1752        .last_market_token(false)
1753        .unwrap_or(&current_market_token);
1754    if transfer_out.final_output_token != 0 && *output_token_market == current_market_token {
1755        (add_to_amount)(
1756            meta.to_token_side(
1757                tokens
1758                    .final_output_token
1759                    .token()
1760                    .as_ref()
1761                    .ok_or(error!(CoreError::MissingFinalOutputToken))?,
1762            )
1763            .map_err(CoreError::from)?,
1764            transfer_out.final_output_token,
1765        )?;
1766    }
1767    if transfer_out.secondary_output_token != 0 && *secondary_token_market == current_market_token {
1768        (add_to_amount)(
1769            order.params.side()?.is_long(),
1770            transfer_out.secondary_output_token,
1771        )?;
1772    }
1773    position
1774        .market()
1775        .validate_market_balances(long_transfer_out, short_transfer_out)?;
1776
1777    let paid_fee_value = *report.fees().paid_order_and_borrowing_fee_value();
1778
1779    msg!("[Position] decreased");
1780    position
1781        .event_emitter()
1782        .emit_cpi(&PositionDecreased::from_report(
1783            position.market().rev(),
1784            position.market().market_meta().market_token_mint,
1785            report,
1786        ))?;
1787
1788    Ok((should_remove_position, paid_fee_value))
1789}
1790
1791/// Position Cut Operation.
1792#[derive(TypedBuilder)]
1793pub struct PositionCutOperation<'a, 'info> {
1794    kind: PositionCutKind,
1795    #[builder(setter(
1796        doc = "Set the executor of this operation. CHECK: the address of the `order` must be derived from its address"
1797    ))]
1798    executor: AccountInfo<'info>,
1799    position: &'a AccountLoader<'info, Position>,
1800    event: &'a AccountLoader<'info, TradeData>,
1801    order: &'a AccountLoader<'info, Order>,
1802    market: &'a AccountLoader<'info, Market>,
1803    store: &'a AccountLoader<'info, Store>,
1804    oracle: &'a Oracle,
1805    owner: AccountInfo<'info>,
1806    user: &'a AccountLoader<'info, UserHeader>,
1807    nonce: &'a NonceBytes,
1808    order_bump: u8,
1809    long_token_mint: &'a Account<'info, Mint>,
1810    short_token_mint: &'a Account<'info, Mint>,
1811    long_token_account: &'a Account<'info, TokenAccount>,
1812    short_token_account: &'a Account<'info, TokenAccount>,
1813    long_token_vault: &'a Account<'info, TokenAccount>,
1814    short_token_vault: &'a Account<'info, TokenAccount>,
1815    claimable_long_token_account_for_user: AccountInfo<'info>,
1816    claimable_short_token_account_for_user: AccountInfo<'info>,
1817    claimable_pnl_token_account_for_holding: AccountInfo<'info>,
1818    token_program: AccountInfo<'info>,
1819    system_program: AccountInfo<'info>,
1820    refund: u64,
1821    should_unwrap_native_token: bool,
1822    allow_closed: bool,
1823    #[builder(setter(into))]
1824    event_emitter: EventEmitter<'a, 'info>,
1825    remaining_accounts: &'info [AccountInfo<'info>],
1826}
1827
1828impl PositionCutOperation<'_, '_> {
1829    pub(crate) fn execute(self) -> Result<ShouldSendTradeEvent> {
1830        let (size_in_usd, is_long, is_collateral_long) = {
1831            let position = self.position.load()?;
1832            let market = self.market.load()?;
1833            let is_collateral_token_long = market
1834                .meta
1835                .to_token_side(&position.collateral_token)
1836                .map_err(CoreError::from)?;
1837            (
1838                position.state.size_in_usd,
1839                position.try_is_long()?,
1840                is_collateral_token_long,
1841            )
1842        };
1843        self.create_order(size_in_usd, is_long, is_collateral_long)?;
1844        let (is_position_removed, transfer_out, should_send_trade_event) = self.execute_order()?;
1845        require!(transfer_out.executed(), CoreError::Internal);
1846        self.order.load_mut()?.header.completed()?;
1847        if is_position_removed {
1848            msg!("[Position] the position is removed");
1849        } else {
1850            msg!(
1851                "[Position] the position is not removed, setting the rent receiver to the executor"
1852            );
1853            self.order
1854                .load_mut()?
1855                .header
1856                .set_rent_receiver(self.executor.key());
1857        }
1858        self.process_transfer_out(&transfer_out, is_long, is_collateral_long)?;
1859        Ok(should_send_trade_event)
1860    }
1861
1862    #[inline(never)]
1863    fn create_order(
1864        &self,
1865        size_in_usd: u128,
1866        is_long: bool,
1867        is_collateral_long: bool,
1868    ) -> Result<()> {
1869        TransferExecutionFeeOperation::builder()
1870            .payment(self.order.to_account_info())
1871            .payer(self.executor.to_account_info())
1872            .execution_lamports(Order::MIN_EXECUTION_LAMPORTS)
1873            .system_program(self.system_program.to_account_info())
1874            .build()
1875            .execute()?;
1876        let params = CreateOrderParams {
1877            kind: self.kind.to_order_kind(),
1878            decrease_position_swap_type: Some(DecreasePositionSwapType::PnlTokenToCollateralToken),
1879            execution_lamports: Order::MIN_EXECUTION_LAMPORTS,
1880            swap_path_length: 0,
1881            initial_collateral_delta_amount: 0,
1882            size_delta_value: self.kind.size_delta_usd(size_in_usd),
1883            is_long,
1884            is_collateral_long,
1885            min_output: None,
1886            trigger_price: None,
1887            acceptable_price: None,
1888            should_unwrap_native_token: self.should_unwrap_native_token,
1889            valid_from_ts: None,
1890        };
1891        let output_token_account = if is_collateral_long {
1892            self.long_token_account
1893        } else {
1894            self.short_token_account
1895        };
1896        CreateOrderOperation::builder()
1897            .order(self.order.clone())
1898            .market(self.market.clone())
1899            .store(self.store.clone())
1900            .owner(self.owner.clone())
1901            .receiver(self.owner.clone())
1902            .creator(self.executor.clone())
1903            .nonce(self.nonce)
1904            .bump(self.order_bump)
1905            .params(&params)
1906            .swap_path(&[])
1907            .callback_version(None)
1908            .callback_authority(None)
1909            .callback_program(None)
1910            .callback_shared_data_account(None)
1911            .callback_partitioned_data_account(None)
1912            .allow_closed(self.allow_closed)
1913            .event_emitter(self.event_emitter)
1914            .build()
1915            .decrease()
1916            .position(self.position)
1917            .final_output_token(output_token_account)
1918            .long_token(self.long_token_account)
1919            .short_token(self.short_token_account)
1920            .build()
1921            .execute()?;
1922        // Make sure the discriminator is written to the account data.
1923        self.order.exit(&crate::ID)?;
1924        Ok(())
1925    }
1926
1927    #[inline(never)]
1928    fn execute_order(&self) -> Result<(RemovePosition, Box<TransferOut>, ShouldSendTradeEvent)> {
1929        ExecuteOrderOperation::builder()
1930            .store(self.store)
1931            .market(self.market)
1932            .order(self.order)
1933            .owner(self.owner.clone())
1934            .user(self.user)
1935            .position(Some(self.position))
1936            .event(Some(self.event))
1937            .oracle(self.oracle)
1938            .remaining_accounts(self.remaining_accounts)
1939            .throw_on_execution_error(true)
1940            .refund(self.refund)
1941            .executor(self.executor.clone())
1942            .event_emitter(self.event_emitter)
1943            .callback_authority(None)
1944            .callback_program(None)
1945            .callback_shared_data_account(None)
1946            .callback_partitioned_data_account(None)
1947            .allow_closed(self.allow_closed)
1948            .build()
1949            .execute()
1950    }
1951
1952    #[inline(never)]
1953    fn process_transfer_out(
1954        &self,
1955        transfer_out: &TransferOut,
1956        is_long: bool,
1957        is_collateral_long: bool,
1958    ) -> Result<()> {
1959        let (output_token, output_token_account, output_token_vault) = if is_collateral_long {
1960            (
1961                self.long_token_mint,
1962                self.long_token_account,
1963                self.long_token_vault,
1964            )
1965        } else {
1966            (
1967                self.short_token_mint,
1968                self.short_token_account,
1969                self.short_token_vault,
1970            )
1971        };
1972        ProcessTransferOutOperation::builder()
1973            .token_program(self.token_program.clone())
1974            .store(self.store)
1975            .market(self.market)
1976            .is_pnl_token_long_token(is_long)
1977            .final_output_market(self.market)
1978            .final_output_token(Some(output_token))
1979            .final_output_token_account(Some(output_token_account.to_account_info()))
1980            .final_output_token_vault(Some(output_token_vault))
1981            .long_token(Some(self.long_token_mint))
1982            .long_token_account(Some(self.long_token_account.to_account_info()))
1983            .long_token_vault(Some(self.long_token_vault))
1984            .short_token(Some(self.short_token_mint))
1985            .short_token_account(Some(self.short_token_account.to_account_info()))
1986            .short_token_vault(Some(self.short_token_vault))
1987            .claimable_long_token_account_for_user(Some(
1988                self.claimable_long_token_account_for_user.clone(),
1989            ))
1990            .claimable_short_token_account_for_user(Some(
1991                self.claimable_short_token_account_for_user.clone(),
1992            ))
1993            .claimable_pnl_token_account_for_holding(Some(
1994                self.claimable_pnl_token_account_for_holding.clone(),
1995            ))
1996            .transfer_out(transfer_out)
1997            .allow_closed(self.allow_closed)
1998            .event_emitter(self.event_emitter)
1999            .build()
2000            .execute()?;
2001        Ok(())
2002    }
2003}