1use anchor_lang::prelude::*;
2use borsh::{BorshDeserialize, BorshSerialize};
3use gmsol_model::{
4 action::decrease_position::{DecreasePositionReport, DecreasePositionSwapType},
5 price::Price,
6};
7use gmsol_utils::InitSpace as _;
8
9use crate::{
10 events::{EventEmitter, GtUpdated, OrderRemoved},
11 utils::pubkey::optional_address,
12 CoreError,
13};
14
15use super::{
16 common::{
17 action::{Action, ActionHeader, ActionSigner, Closable},
18 swap::SwapActionParams,
19 token::TokenAndAccount,
20 },
21 user::UserHeader,
22 Oracle, Seed, Store,
23};
24
25pub use gmsol_utils::order::{OrderKind, OrderSide};
26
27#[derive(AnchorSerialize, AnchorDeserialize, Clone, InitSpace, Copy)]
29#[cfg_attr(feature = "debug", derive(Debug))]
30pub struct UpdateOrderParams {
31 pub size_delta_value: Option<u128>,
33 pub acceptable_price: Option<u128>,
35 pub trigger_price: Option<u128>,
37 pub min_output: Option<u128>,
39 pub valid_from_ts: Option<i64>,
41}
42
43impl UpdateOrderParams {
44 pub fn is_empty(&self) -> bool {
46 self.size_delta_value.is_none()
47 && self.acceptable_price.is_none()
48 && self.trigger_price.is_none()
49 && self.min_output.is_none()
50 && self.valid_from_ts.is_none()
51 }
52}
53
54#[zero_copy]
56#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
57#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
58#[derive(BorshSerialize, BorshDeserialize, Default, InitSpace)]
59pub struct TransferOut {
60 executed: u8,
62 #[cfg_attr(feature = "debug", debug(skip))]
63 padding_0: [u8; 7],
64 pub final_output_token: u64,
66 pub secondary_output_token: u64,
68 pub long_token: u64,
70 pub short_token: u64,
72 pub long_token_for_claimable_account_of_user: u64,
74 pub short_token_for_claimable_account_of_user: u64,
76 pub long_token_for_claimable_account_of_holding: u64,
78 pub short_token_for_claimable_account_of_holding: u64,
80}
81
82#[cfg(test)]
83impl From<crate::events::EventTransferOut> for TransferOut {
84 fn from(event: crate::events::EventTransferOut) -> Self {
85 let crate::events::EventTransferOut {
86 executed,
87 padding_0,
88 final_output_token,
89 secondary_output_token,
90 long_token,
91 short_token,
92 long_token_for_claimable_account_of_user,
93 short_token_for_claimable_account_of_user,
94 long_token_for_claimable_account_of_holding,
95 short_token_for_claimable_account_of_holding,
96 } = event;
97
98 Self {
99 executed,
100 padding_0,
101 final_output_token,
102 secondary_output_token,
103 long_token,
104 short_token,
105 long_token_for_claimable_account_of_user,
106 short_token_for_claimable_account_of_user,
107 long_token_for_claimable_account_of_holding,
108 short_token_for_claimable_account_of_holding,
109 }
110 }
111}
112
113pub enum CollateralReceiver {
115 Collateral,
116 ClaimableForHolding,
117 ClaimableForUser,
118}
119
120impl TransferOut {
121 const EXECUTED: u8 = u8::MAX;
122 const NOT_EXECUTED: u8 = 0;
123
124 pub fn executed(&self) -> bool {
126 !self.executed == Self::NOT_EXECUTED
127 }
128
129 pub fn is_user_output_empty(&self) -> bool {
131 self.final_output_token == 0
132 && self.secondary_output_token == 0
133 && self.long_token == 0
134 && self.short_token == 0
135 && self.long_token_for_claimable_account_of_user == 0
136 && self.short_token_for_claimable_account_of_user == 0
137 }
138
139 pub(crate) fn set_executed(&mut self, executed: bool) -> &mut Self {
140 self.executed = if executed {
141 Self::EXECUTED
142 } else {
143 Self::NOT_EXECUTED
144 };
145 self
146 }
147
148 pub(crate) fn new_failed() -> Self {
149 Self {
150 executed: Self::NOT_EXECUTED,
151 ..Default::default()
152 }
153 }
154
155 pub(crate) fn total_long_token_amount(&self) -> Result<u64> {
156 self.long_token
157 .checked_add(self.long_token_for_claimable_account_of_user)
158 .and_then(|a| a.checked_add(self.long_token_for_claimable_account_of_holding))
159 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))
160 }
161
162 pub(crate) fn total_short_token_amount(&self) -> Result<u64> {
163 self.short_token
164 .checked_add(self.short_token_for_claimable_account_of_user)
165 .and_then(|a| a.checked_add(self.short_token_for_claimable_account_of_holding))
166 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))
167 }
168
169 pub(crate) fn transfer_out(&mut self, is_secondary: bool, amount: u64) -> Result<()> {
170 if amount == 0 {
171 return Ok(());
172 }
173 if is_secondary {
174 self.secondary_output_token = self
175 .secondary_output_token
176 .checked_add(amount)
177 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
178 } else {
179 self.final_output_token = self
180 .final_output_token
181 .checked_add(amount)
182 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
183 }
184 Ok(())
185 }
186
187 pub(crate) fn transfer_out_funding_amounts(
188 &mut self,
189 long_amount: &u128,
190 short_amount: &u128,
191 ) -> Result<()> {
192 self.transfer_out_collateral(
193 true,
194 CollateralReceiver::Collateral,
195 (*long_amount)
196 .try_into()
197 .map_err(|_| error!(CoreError::TokenAmountOverflow))?,
198 )?;
199 self.transfer_out_collateral(
200 false,
201 CollateralReceiver::Collateral,
202 (*short_amount)
203 .try_into()
204 .map_err(|_| error!(CoreError::TokenAmountOverflow))?,
205 )?;
206 Ok(())
207 }
208
209 pub(crate) fn process_claimable_collateral_for_decrease(
210 &mut self,
211 report: &DecreasePositionReport<u128, i128>,
212 ) -> Result<()> {
213 let for_holding = report.claimable_collateral_for_holding();
214 require!(
215 *for_holding.output_token_amount() == 0,
216 CoreError::ClaimableCollateralForHoldingCannotBeInOutputTokens,
217 );
218
219 let is_output_token_long = report.is_output_token_long();
220 let is_secondary_token_long = report.is_secondary_output_token_long();
221
222 let secondary_amount = (*for_holding.secondary_output_token_amount())
223 .try_into()
224 .map_err(|_| error!(CoreError::TokenAmountOverflow))?;
225 self.transfer_out_collateral(
226 is_secondary_token_long,
227 CollateralReceiver::ClaimableForHolding,
228 secondary_amount,
229 )?;
230
231 let for_user = report.claimable_collateral_for_user();
232 self.transfer_out_collateral(
233 is_output_token_long,
234 CollateralReceiver::ClaimableForUser,
235 (*for_user.output_token_amount())
236 .try_into()
237 .map_err(|_| error!(CoreError::TokenAmountOverflow))?,
238 )?;
239 self.transfer_out_collateral(
240 is_secondary_token_long,
241 CollateralReceiver::ClaimableForUser,
242 (*for_user.secondary_output_token_amount())
243 .try_into()
244 .map_err(|_| error!(CoreError::TokenAmountOverflow))?,
245 )?;
246 Ok(())
247 }
248
249 pub(crate) fn transfer_out_collateral(
250 &mut self,
251 is_long: bool,
252 to: CollateralReceiver,
253 amount: u64,
254 ) -> Result<()> {
255 if amount == 0 {
256 return Ok(());
257 }
258 match to {
259 CollateralReceiver::Collateral => {
260 if is_long {
261 self.long_token = self
262 .long_token
263 .checked_add(amount)
264 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
265 } else {
266 self.short_token = self
267 .short_token
268 .checked_add(amount)
269 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
270 }
271 }
272 CollateralReceiver::ClaimableForHolding => {
273 if is_long {
274 self.long_token_for_claimable_account_of_holding = self
275 .long_token_for_claimable_account_of_holding
276 .checked_add(amount)
277 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
278 } else {
279 self.short_token_for_claimable_account_of_holding = self
280 .short_token_for_claimable_account_of_holding
281 .checked_add(amount)
282 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
283 }
284 }
285 CollateralReceiver::ClaimableForUser => {
286 if is_long {
287 self.long_token_for_claimable_account_of_user = self
288 .long_token_for_claimable_account_of_user
289 .checked_add(amount)
290 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
291 } else {
292 self.short_token_for_claimable_account_of_user = self
293 .short_token_for_claimable_account_of_user
294 .checked_add(amount)
295 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
296 }
297 }
298 }
299 Ok(())
300 }
301}
302
303#[account(zero_copy)]
305#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
306#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
307pub struct Order {
308 pub(crate) header: ActionHeader,
310 pub(crate) market_token: Pubkey,
312 pub(crate) tokens: OrderTokenAccounts,
314 pub(crate) swap: SwapActionParams,
316 #[cfg_attr(feature = "debug", debug(skip))]
317 padding_0: [u8; 4],
318 pub(crate) params: OrderActionParams,
320 pub(crate) gt_reward: u64,
321 #[cfg_attr(feature = "debug", debug(skip))]
322 padding_1: [u8; 8],
323 #[cfg_attr(feature = "debug", debug(skip))]
324 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
325 reserved: [u8; 128],
326}
327
328impl Seed for Order {
329 const SEED: &'static [u8] = b"order";
331}
332
333impl gmsol_utils::InitSpace for Order {
334 const INIT_SPACE: usize = std::mem::size_of::<Self>();
335}
336
337impl Action for Order {
338 const MIN_EXECUTION_LAMPORTS: u64 = 300_000;
339
340 fn header(&self) -> &ActionHeader {
341 &self.header
342 }
343}
344
345impl Closable for Order {
346 type ClosedEvent = OrderRemoved;
347
348 fn to_closed_event(&self, address: &Pubkey, reason: &str) -> Result<Self::ClosedEvent> {
349 OrderRemoved::new(
350 self.header.id,
351 self.header.store,
352 *address,
353 self.params.kind()?,
354 self.market_token,
355 self.header.owner,
356 self.header.action_state()?,
357 reason,
358 )
359 }
360}
361
362impl Order {
363 pub(crate) fn position_cut_rent(is_pure: bool, include_execution_fee: bool) -> Result<u64> {
365 use anchor_spl::token::TokenAccount;
366
367 let rent = Rent::get()?;
368 let amount = rent.minimum_balance(Self::INIT_SPACE + 8)
369 + rent.minimum_balance(TokenAccount::LEN) * if is_pure { 1 } else { 2 }
370 + if include_execution_fee {
371 Self::MIN_EXECUTION_LAMPORTS
372 } else {
373 0
374 };
375
376 Ok(amount)
377 }
378
379 pub fn signer(&self) -> ActionSigner {
381 self.header.signer(Self::SEED)
382 }
383
384 pub fn validate_valid_from_ts(&self) -> Result<()> {
386 if self.params.kind()?.is_market() {
387 return Ok(());
388 }
389 require_gte!(Clock::get()?.unix_timestamp, self.params.valid_from_ts);
390 Ok(())
391 }
392
393 pub fn validate_trigger_price(&self, index_price: &Price<u128>) -> Result<()> {
395 let params = &self.params;
396 let kind = params.kind()?;
397 let is_long = params.side()?.is_long();
398 let trigger_price = ¶ms.trigger_price;
399 match kind {
400 OrderKind::LimitIncrease => {
401 if is_long {
402 require_gte!(
403 trigger_price,
404 index_price.pick_price(true),
405 CoreError::InvalidTriggerPrice
406 );
407 } else {
408 require_gte!(
409 index_price.pick_price(false),
410 trigger_price,
411 CoreError::InvalidTriggerPrice
412 );
413 }
414 }
415 OrderKind::LimitDecrease => {
416 if is_long {
417 require_gte!(
418 index_price.pick_price(false),
419 trigger_price,
420 CoreError::InvalidTriggerPrice
421 );
422 } else {
423 require_gte!(
424 trigger_price,
425 index_price.pick_price(true),
426 CoreError::InvalidTriggerPrice
427 );
428 }
429 }
430 OrderKind::StopLossDecrease => {
431 if is_long {
432 require_gte!(
433 trigger_price,
434 index_price.pick_price(false),
435 CoreError::InvalidTriggerPrice
436 );
437 } else {
438 require_gte!(
439 index_price.pick_price(true),
440 trigger_price,
441 CoreError::InvalidTriggerPrice
442 );
443 }
444 }
445 OrderKind::LimitSwap => {
446 }
450 OrderKind::MarketSwap
451 | OrderKind::MarketIncrease
452 | OrderKind::MarketDecrease
453 | OrderKind::Liquidation
454 | OrderKind::AutoDeleveraging => {}
455 _ => return err!(CoreError::UnknownOrderKind),
456 }
457
458 Ok(())
459 }
460
461 pub fn validate_output_amount(&self, output_amount: u128) -> Result<()> {
463 require_gte!(
464 output_amount,
465 self.params.min_output,
466 CoreError::InsufficientOutputAmount
467 );
468 Ok(())
469 }
470
471 #[inline(never)]
472 pub(crate) fn validate_decrease_output_amounts(
473 &self,
474 oracle: &Oracle,
475 output_token: &Pubkey,
476 output_amount: u64,
477 secondary_output_token: &Pubkey,
478 secondary_output_amount: u64,
479 ) -> Result<()> {
480 let mut total = 0u128;
481 {
482 let price = oracle.get_primary_price(output_token, false)?.min;
483 let output_value = u128::from(output_amount).saturating_mul(price);
484 total = total.saturating_add(output_value);
485 }
486 {
487 let price = oracle.get_primary_price(secondary_output_token, false)?.min;
488 let output_value = u128::from(secondary_output_amount).saturating_mul(price);
489 total = total.saturating_add(output_value);
490 }
491
492 self.validate_output_amount(total)?;
494 Ok(())
495 }
496
497 pub fn secondary_output_token(&self) -> Result<Pubkey> {
499 if self.params.side()?.is_long() {
500 self.tokens.long_token.token()
501 } else {
502 self.tokens.short_token.token()
503 }
504 .ok_or_else(|| error!(CoreError::MissingPoolTokens))
505 }
506
507 pub fn params(&self) -> &OrderActionParams {
509 &self.params
510 }
511
512 pub fn swap(&self) -> &SwapActionParams {
514 &self.swap
515 }
516
517 pub fn market_token(&self) -> &Pubkey {
519 &self.market_token
520 }
521
522 pub fn tokens(&self) -> &OrderTokenAccounts {
524 &self.tokens
525 }
526
527 #[inline(never)]
530 pub(crate) fn unchecked_process_gt(
531 &mut self,
532 store: &mut Store,
533 user: &mut UserHeader,
534 paid_fee_value: u128,
535 event_emitter: &EventEmitter,
536 ) -> Result<()> {
537 if paid_fee_value == 0 {
538 msg!("[GT] GT is not minted unless an order fee is paid");
539 return Ok(());
540 }
541
542 let next_paid_fee_value = user.gt.paid_fee_value().saturating_add(paid_fee_value);
544 let minted_fee_value = user.gt.minted_fee_value();
545
546 require_gte!(
547 next_paid_fee_value,
548 minted_fee_value,
549 CoreError::InvalidUserAccount
550 );
551
552 let value_to_mint_for = next_paid_fee_value.saturating_sub(minted_fee_value);
553
554 let (minted, delta_minted_value, minting_cost) =
555 store.gt().get_mint_amount(value_to_mint_for)?;
556
557 let next_minted_value = minted_fee_value
558 .checked_add(delta_minted_value)
559 .ok_or_else(|| error!(CoreError::ValueOverflow))?;
560
561 store.gt_mut().mint_to(user, minted)?;
562
563 self.gt_reward = minted;
564 user.gt.paid_fee_value = next_paid_fee_value;
565 user.gt.minted_fee_value = next_minted_value;
566
567 event_emitter
568 .emit_cpi(&GtUpdated::minted(
569 minting_cost,
570 minted,
571 store.gt(),
572 Some(user),
573 ))
574 .expect("failed to emit GT minted event");
575
576 Ok(())
577 }
578
579 pub(crate) fn update(&mut self, id: u64, params: &UpdateOrderParams) -> Result<()> {
580 let current = &mut self.params;
581 require!(current.is_updatable()?, CoreError::InvalidArgument);
582 require!(!params.is_empty(), CoreError::InvalidArgument);
583
584 self.header.id = id;
585
586 if let Some(size_delta_value) = params.size_delta_value {
587 current.size_delta_value = size_delta_value;
588 }
589
590 if let Some(acceptable_price) = params.acceptable_price {
591 current.acceptable_price = acceptable_price;
592 }
593
594 if let Some(trigger_price) = params.trigger_price {
595 current.trigger_price = trigger_price;
596 }
597
598 if let Some(min_output) = params.min_output {
599 if matches!(current.kind()?, OrderKind::LimitSwap) {
600 require_neq!(min_output, 0, CoreError::InvalidArgument);
601 }
602 current.min_output = min_output;
603 }
604
605 if let Some(ts) = params.valid_from_ts {
606 current.valid_from_ts = ts;
607 }
608
609 self.header.updated()?;
610
611 Ok(())
612 }
613}
614
615#[zero_copy]
617#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
618#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
619pub struct OrderTokenAccounts {
620 pub(crate) initial_collateral: TokenAndAccount,
622 pub(crate) final_output_token: TokenAndAccount,
624 pub(crate) long_token: TokenAndAccount,
626 pub(crate) short_token: TokenAndAccount,
628 #[cfg_attr(feature = "debug", debug(skip))]
629 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
630 reserved: [u8; 128],
631}
632
633impl OrderTokenAccounts {
634 pub fn initial_collateral(&self) -> &TokenAndAccount {
638 &self.initial_collateral
639 }
640
641 pub fn final_output_token(&self) -> &TokenAndAccount {
645 &self.final_output_token
646 }
647
648 pub fn long_token(&self) -> &TokenAndAccount {
652 &self.long_token
653 }
654
655 pub fn short_token(&self) -> &TokenAndAccount {
659 &self.short_token
660 }
661}
662
663#[zero_copy]
665#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
666#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
667pub struct OrderActionParams {
668 kind: u8,
670 side: u8,
672 decrease_position_swap_type: u8,
674 #[cfg_attr(feature = "debug", debug(skip))]
675 padding_1: [u8; 5],
676 collateral_token: Pubkey,
678 position: Pubkey,
680 pub(crate) initial_collateral_delta_amount: u64,
682 pub(crate) size_delta_value: u128,
684 min_output: u128,
688 pub(crate) trigger_price: u128,
690 pub(crate) acceptable_price: u128,
692 pub(crate) valid_from_ts: i64,
693 #[cfg_attr(feature = "debug", debug(skip))]
694 padding_2: [u8; 8],
695 #[cfg_attr(feature = "debug", debug(skip))]
696 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
697 reserved: [u8; 64],
698}
699
700impl OrderActionParams {
701 const DEFAULT_VALID_FROM_TS: i64 = 0;
702
703 pub(crate) fn init_swap(
704 &mut self,
705 kind: OrderKind,
706 collateral_token: Pubkey,
707 swap_in_amount: u64,
708 min_output: Option<u128>,
709 valid_from_ts: Option<i64>,
710 ) -> Result<()> {
711 self.kind = kind.into();
712 self.collateral_token = collateral_token;
713 self.initial_collateral_delta_amount = swap_in_amount;
714 match kind {
715 OrderKind::MarketSwap => {
716 self.min_output = min_output.unwrap_or(0);
717 self.valid_from_ts = Self::DEFAULT_VALID_FROM_TS;
718 }
719 OrderKind::LimitSwap => {
720 let Some(min_output) = min_output else {
721 return err!(CoreError::InvalidMinOutputAmount);
722 };
723 require!(min_output != 0, CoreError::Internal);
724 self.min_output = min_output;
725
726 self.valid_from_ts = valid_from_ts.unwrap_or(Self::DEFAULT_VALID_FROM_TS);
727 }
728 _ => {
729 return err!(CoreError::Internal);
730 }
731 }
732 Ok(())
733 }
734
735 #[allow(clippy::too_many_arguments)]
736 pub(crate) fn init_increase(
737 &mut self,
738 is_long: bool,
739 kind: OrderKind,
740 position: Pubkey,
741 collateral_token: Pubkey,
742 initial_collateral_delta_amount: u64,
743 size_delta_value: u128,
744 trigger_price: Option<u128>,
745 acceptable_price: Option<u128>,
746 min_output: Option<u128>,
747 valid_from_ts: Option<i64>,
748 ) -> Result<()> {
749 self.kind = kind.into();
750 self.side = if is_long {
751 OrderSide::Long
752 } else {
753 OrderSide::Short
754 }
755 .into();
756 self.collateral_token = collateral_token;
757 self.initial_collateral_delta_amount = initial_collateral_delta_amount;
758 self.size_delta_value = size_delta_value;
759 self.position = position;
760 self.min_output = min_output.unwrap_or(0);
761 match acceptable_price {
762 Some(price) => {
763 self.acceptable_price = price;
764 }
765 None => {
766 if is_long {
767 self.acceptable_price = u128::MAX;
768 } else {
769 self.acceptable_price = u128::MIN;
770 }
771 }
772 }
773 match kind {
774 OrderKind::MarketIncrease => {
775 require!(trigger_price.is_none(), CoreError::InvalidTriggerPrice);
776 self.valid_from_ts = Self::DEFAULT_VALID_FROM_TS;
777 }
778 OrderKind::LimitIncrease => {
779 let Some(price) = trigger_price else {
780 return err!(CoreError::InvalidTriggerPrice);
781 };
782 self.trigger_price = price;
783 self.valid_from_ts = valid_from_ts.unwrap_or(Self::DEFAULT_VALID_FROM_TS);
784 }
785 _ => {
786 return err!(CoreError::Internal);
787 }
788 }
789 Ok(())
790 }
791
792 #[allow(clippy::too_many_arguments)]
793 pub(crate) fn init_decrease(
794 &mut self,
795 is_long: bool,
796 kind: OrderKind,
797 position: Pubkey,
798 collateral_token: Pubkey,
799 initial_collateral_delta_amount: u64,
800 size_delta_value: u128,
801 trigger_price: Option<u128>,
802 acceptable_price: Option<u128>,
803 min_output: Option<u128>,
804 swap_type: DecreasePositionSwapType,
805 valid_from_ts: Option<i64>,
806 ) -> Result<()> {
807 self.kind = kind.into();
808 self.side = if is_long {
809 OrderSide::Long
810 } else {
811 OrderSide::Short
812 }
813 .into();
814 self.decrease_position_swap_type = swap_type.into();
815 self.position = position;
816 self.collateral_token = collateral_token;
817 self.initial_collateral_delta_amount = initial_collateral_delta_amount;
818 self.size_delta_value = size_delta_value;
819 self.min_output = min_output.unwrap_or(0);
820 match acceptable_price {
821 Some(price) => {
822 self.acceptable_price = price;
823 }
824 None => {
825 if is_long {
826 self.acceptable_price = u128::MIN;
827 } else {
828 self.acceptable_price = u128::MAX;
829 }
830 }
831 }
832 match kind {
833 OrderKind::MarketDecrease | OrderKind::Liquidation | OrderKind::AutoDeleveraging => {
834 require!(trigger_price.is_none(), CoreError::InvalidTriggerPrice);
835 self.valid_from_ts = Self::DEFAULT_VALID_FROM_TS;
836 }
837 OrderKind::LimitDecrease | OrderKind::StopLossDecrease => {
838 let Some(price) = trigger_price else {
839 return err!(CoreError::InvalidTriggerPrice);
840 };
841 self.trigger_price = price;
842 self.valid_from_ts = valid_from_ts.unwrap_or(Self::DEFAULT_VALID_FROM_TS);
843 }
844 _ => {
845 return err!(CoreError::Internal);
846 }
847 }
848 Ok(())
849 }
850
851 pub fn kind(&self) -> Result<OrderKind> {
853 self.kind
854 .try_into()
855 .map_err(|_| error!(CoreError::UnknownOrderKind))
856 }
857
858 pub fn decrease_position_swap_type(&self) -> Result<DecreasePositionSwapType> {
860 let ty = self
861 .decrease_position_swap_type
862 .try_into()
863 .map_err(|_| CoreError::UnknownDecreasePositionSwapType)?;
864 Ok(ty)
865 }
866
867 pub fn is_updatable(&self) -> Result<bool> {
869 let kind = self.kind()?;
870 Ok(matches!(
871 kind,
872 OrderKind::LimitSwap
873 | OrderKind::LimitIncrease
874 | OrderKind::LimitDecrease
875 | OrderKind::StopLossDecrease
876 ))
877 }
878
879 pub fn side(&self) -> Result<OrderSide> {
881 self.side
882 .try_into()
883 .map_err(|_| error!(CoreError::UnknownOrderSide))
884 }
885
886 pub fn position(&self) -> Option<&Pubkey> {
888 optional_address(&self.position)
889 }
890
891 pub fn amount(&self) -> u64 {
893 self.initial_collateral_delta_amount
894 }
895
896 pub fn size(&self) -> u128 {
898 self.size_delta_value
899 }
900
901 pub fn acceptable_price(&self) -> u128 {
903 self.acceptable_price
904 }
905
906 pub fn trigger_price(&self) -> u128 {
908 self.trigger_price
909 }
910
911 pub fn min_output(&self) -> u128 {
913 self.min_output
914 }
915
916 pub fn valid_from_ts(&self) -> i64 {
918 self.valid_from_ts
919 }
920}