phoenix/program/processor/
reduce_order.rs1use crate::{
2 program::{
3 assert_with_msg, dispatch_market::load_with_dispatch_mut, error::PhoenixError,
4 loaders::CancelOrWithdrawContext as Cancel, token_utils::try_withdraw, MarketHeader,
5 PhoenixMarketContext, PhoenixVaultContext,
6 },
7 quantities::{BaseLots, Ticks, WrapperU64},
8 state::{
9 markets::{FIFOOrderId, MarketEvent},
10 MatchingEngineResponse, Side,
11 },
12};
13use borsh::{BorshDeserialize, BorshSerialize};
14use solana_program::{
15 account_info::AccountInfo, entrypoint::ProgramResult, log::sol_log_compute_units,
16 pubkey::Pubkey,
17};
18use std::mem::size_of;
19
20#[derive(BorshDeserialize, BorshSerialize, Clone, Copy)]
21pub struct CancelOrderParams {
22 pub side: Side,
23 pub price_in_ticks: u64,
24 pub order_sequence_number: u64,
25}
26
27#[derive(BorshDeserialize, BorshSerialize, Clone, Copy)]
28pub struct ReduceOrderParams {
29 pub base_params: CancelOrderParams,
30 pub size: u64,
32}
33
34pub(crate) fn process_reduce_order<'a, 'info>(
35 _program_id: &Pubkey,
36 market_context: &PhoenixMarketContext<'a, 'info>,
37 accounts: &'a [AccountInfo<'info>],
38 data: &[u8],
39 withdraw_funds: bool,
40 record_event_fn: &mut dyn FnMut(MarketEvent<Pubkey>),
41) -> ProgramResult {
42 sol_log_compute_units();
43 let ReduceOrderParams { base_params, size } = ReduceOrderParams::try_from_slice(data)?;
44 let CancelOrderParams {
45 side,
46 price_in_ticks,
47 order_sequence_number,
48 } = base_params;
49 let order_id = FIFOOrderId::new(Ticks::new(price_in_ticks), order_sequence_number);
50
51 let vault_context_option = if withdraw_funds {
52 let Cancel { vault_context } = Cancel::load(market_context, accounts)?;
53 Some(vault_context)
54 } else {
55 None
56 };
57
58 let PhoenixMarketContext {
59 market_info,
60 signer: trader,
61 } = market_context;
62
63 let MatchingEngineResponse {
64 num_quote_lots_out,
65 num_base_lots_out,
66 ..
67 } = {
68 let market_bytes = &mut market_info.try_borrow_mut_data()?[size_of::<MarketHeader>()..];
69 let market = load_with_dispatch_mut(&market_info.size_params, market_bytes)?.inner;
70 sol_log_compute_units();
71 market
72 .reduce_order(
73 trader.key,
74 &order_id,
75 side,
76 Some(BaseLots::new(size)),
77 vault_context_option.is_some(),
78 record_event_fn,
79 )
80 .ok_or(PhoenixError::ReduceOrderError)?
81 };
82 sol_log_compute_units();
83
84 let header = market_info.get_header()?;
85
86 if let Some(PhoenixVaultContext {
87 base_account,
88 quote_account,
89 base_vault,
90 quote_vault,
91 token_program,
92 }) = vault_context_option
93 {
94 try_withdraw(
95 market_info.key,
96 &header.base_params,
97 &header.quote_params,
98 token_program.as_ref(),
99 quote_account.as_ref(),
100 quote_vault,
101 base_account.as_ref(),
102 base_vault,
103 num_quote_lots_out * header.get_quote_lot_size(),
104 num_base_lots_out * header.get_base_lot_size(),
105 )?;
106 } else {
107 assert_with_msg(
110 num_quote_lots_out == 0,
111 PhoenixError::ReduceOrderError,
112 "WARNING: num_quote_lots_out must be 0",
113 )?;
114 assert_with_msg(
115 num_base_lots_out == 0,
116 PhoenixError::ReduceOrderError,
117 "WARNING: num_base_lots_out must be 0",
118 )?;
119 }
120 Ok(())
121}