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#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug)]
44pub struct CreateOrderParams {
45 pub kind: OrderKind,
47 pub decrease_position_swap_type: Option<DecreasePositionSwapType>,
49 pub execution_lamports: u64,
51 pub swap_path_length: u8,
53 pub initial_collateral_delta_amount: u64,
55 pub size_delta_value: u128,
57 pub is_long: bool,
59 pub is_collateral_long: bool,
61 pub min_output: Option<u128>,
63 pub trigger_price: Option<u128>,
65 pub acceptable_price: Option<u128>,
67 pub should_unwrap_native_token: bool,
69 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 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 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#[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 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#[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#[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#[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 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#[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#[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 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(¤t_market_token, self.remaining_accounts)?;
887 let mut swap_markets = SwapMarkets::new(
888 &self.store.key(),
889 &loaders,
890 Some(¤t_market_token),
891 self.event_emitter,
892 )?;
893 let mut transfer_out = Box::default();
894
895 {
896 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 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 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 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 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 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 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 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 order.validate_output_amount(collateral_increment_amount.into())?;
1370
1371 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 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 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 let mut pnl_factor_before_execution = None;
1450
1451 if is_liquidation_order {
1453 require_gte!(
1454 size_delta_usd,
1455 *position.size_in_usd(),
1456 CoreError::InvalidArgument
1457 );
1458 }
1459
1460 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 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 {
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 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 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 {
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 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(¤t_market_token);
1600 let secondary_token_market = order
1601 .swap
1602 .last_market_token(false)
1603 .unwrap_or(¤t_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#[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(¶ms)
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 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}