gmsol_store/ops/
order.rs

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