phoenix/program/processor/
withdraw.rs1use crate::{
2 program::{
3 dispatch_market::load_with_dispatch_mut,
4 error::{assert_with_msg, PhoenixError},
5 loaders::CancelOrWithdrawContext as Withdraw,
6 token_utils::try_withdraw,
7 validation::checkers::phoenix_checkers::MarketAccountInfo,
8 MarketHeader, PhoenixMarketContext, PhoenixVaultContext,
9 },
10 quantities::{BaseLots, QuoteLots, WrapperU64},
11 state::MatchingEngineResponse,
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(Debug, Default, Clone, Copy, PartialEq, Eq, BorshDeserialize, BorshSerialize)]
21pub struct WithdrawParams {
22 pub quote_lots_to_withdraw: Option<u64>,
23 pub base_lots_to_withdraw: Option<u64>,
24}
25
26pub(crate) fn process_withdraw_funds<'a, 'info>(
27 _program_id: &Pubkey,
28 market_context: &PhoenixMarketContext<'a, 'info>,
29 accounts: &'a [AccountInfo<'info>],
30 data: &[u8],
31) -> ProgramResult {
32 let Withdraw { vault_context } = Withdraw::load(market_context, accounts)?;
33 let WithdrawParams {
34 quote_lots_to_withdraw,
35 base_lots_to_withdraw,
36 } = WithdrawParams::try_from_slice(data)?;
37 let PhoenixMarketContext {
38 market_info,
39 signer: trader,
40 } = market_context;
41 process_withdraw(
42 market_info,
43 trader.as_ref().clone(),
44 vault_context,
45 quote_lots_to_withdraw,
46 base_lots_to_withdraw,
47 false,
48 )
49}
50
51#[allow(clippy::too_many_arguments)]
52pub(crate) fn process_withdraw<'a, 'info>(
53 market_info: &MarketAccountInfo<'a, 'info>,
54 trader: AccountInfo<'info>,
55 vault_context: PhoenixVaultContext<'a, 'info>,
56 quote_lots_to_withdraw: Option<u64>,
57 base_lots_to_withdraw: Option<u64>,
58 evict_seat: bool,
59) -> ProgramResult {
60 sol_log_compute_units();
61
62 let PhoenixVaultContext {
63 base_account,
64 quote_account,
65 base_vault,
66 quote_vault,
67 token_program,
68 } = vault_context;
69 let MatchingEngineResponse {
70 num_quote_lots_out,
71 num_base_lots_out,
72 ..
73 } = {
74 sol_log_compute_units();
75 let market_bytes = &mut market_info.try_borrow_mut_data()?[size_of::<MarketHeader>()..];
76 let market = load_with_dispatch_mut(&market_info.size_params, market_bytes)?.inner;
77 let response = market
78 .claim_funds(
79 trader.key,
80 quote_lots_to_withdraw.map(QuoteLots::new),
81 base_lots_to_withdraw.map(BaseLots::new),
82 evict_seat,
83 )
84 .ok_or(PhoenixError::WithdrawFundsError)?;
85 sol_log_compute_units();
86 if evict_seat {
87 assert_with_msg(
88 market.get_trader_index(trader.key).is_none(),
89 PhoenixError::EvictionError,
90 "Trader was not evicted, there are still orders on the book",
91 )?;
92 }
93 response
94 };
95 sol_log_compute_units();
96 let header = market_info.get_header()?;
97
98 try_withdraw(
99 market_info.key,
100 &header.base_params,
101 &header.quote_params,
102 token_program.as_ref(),
103 quote_account.as_ref(),
104 quote_vault,
105 base_account.as_ref(),
106 base_vault,
107 num_quote_lots_out * header.get_quote_lot_size(),
108 num_base_lots_out * header.get_base_lot_size(),
109 )?;
110
111 Ok(())
112}