phoenix/program/processor/
fees.rs

1use std::mem::size_of;
2
3use crate::{
4    program::{
5        assert_with_msg, load_with_dispatch_mut,
6        token_utils::{get_decimal_string, maybe_invoke_withdraw},
7        ChangeFeeRecipientContext, CollectFeesContext, MarketHeader, PhoenixMarketContext,
8    },
9    quantities::{QuoteLots, WrapperU64},
10    state::markets::MarketEvent,
11};
12use solana_program::{
13    account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError,
14    pubkey::Pubkey,
15};
16
17pub(crate) fn process_collect_fees<'a, 'info>(
18    _program_id: &Pubkey,
19    market_context: &PhoenixMarketContext<'a, 'info>,
20    accounts: &'a [AccountInfo<'info>],
21    _data: &[u8],
22    record_event_fn: &mut dyn FnMut(MarketEvent<Pubkey>),
23) -> ProgramResult {
24    let CollectFeesContext {
25        fee_recipient_token_account,
26        quote_vault,
27        token_program,
28    } = CollectFeesContext::load(market_context, accounts)?;
29
30    let PhoenixMarketContext {
31        market_info,
32        signer: _,
33    } = market_context;
34
35    let num_quote_lots_out = {
36        let market_bytes = &mut market_info.try_borrow_mut_data()?[size_of::<MarketHeader>()..];
37        let market = load_with_dispatch_mut(&market_info.size_params, market_bytes)?.inner;
38        market.collect_fees(record_event_fn)
39    };
40
41    let header = market_info.get_header()?;
42    let quote_atoms_collected = num_quote_lots_out * header.get_quote_lot_size();
43    phoenix_log!(
44        "Collected {} in fees",
45        get_decimal_string(quote_atoms_collected.as_u64(), header.quote_params.decimals)
46    );
47
48    maybe_invoke_withdraw(
49        market_info.key,
50        &header.quote_params.mint_key,
51        header.quote_params.vault_bump as u8,
52        quote_atoms_collected.as_u64(),
53        token_program.as_ref(),
54        fee_recipient_token_account.as_ref(),
55        &quote_vault,
56    )?;
57    Ok(())
58}
59
60pub(crate) fn process_change_fee_recipient<'a, 'info>(
61    _program_id: &Pubkey,
62    market_context: &PhoenixMarketContext<'a, 'info>,
63    accounts: &'a [AccountInfo<'info>],
64    _data: &[u8],
65) -> ProgramResult {
66    let ChangeFeeRecipientContext {
67        new_fee_recipient,
68        previous_fee_recipient,
69    } = ChangeFeeRecipientContext::load(market_context, accounts)?;
70    let PhoenixMarketContext { market_info, .. } = market_context;
71
72    let uncollected_fees = {
73        let market_bytes = &mut market_info.try_borrow_mut_data()?[size_of::<MarketHeader>()..];
74        load_with_dispatch_mut(&market_info.size_params, market_bytes)?
75            .inner
76            .get_uncollected_fee_amount()
77    };
78
79    let mut header = market_info.get_header_mut()?;
80    if uncollected_fees > QuoteLots::ZERO {
81        assert_with_msg(
82            previous_fee_recipient.is_some(),
83            ProgramError::MissingRequiredSignature,
84            "Previous fee recipient must sign if there are uncollected fees",
85        )?;
86    }
87    header.fee_recipient = *new_fee_recipient.key;
88    Ok(())
89}