manifest/program/processor/
deposit.rs

1use std::cell::RefMut;
2
3use crate::{
4    logs::{emit_stack, DepositLog},
5    state::MarketRefMut,
6    validation::{
7        loaders::DepositContext, MintAccountInfo, Signer, TokenAccountInfo, TokenProgram,
8    },
9};
10use borsh::{BorshDeserialize, BorshSerialize};
11use hypertree::DataIndex;
12use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};
13
14use super::{get_trader_index_with_hint, shared::get_mut_dynamic_account};
15
16#[cfg(feature = "certora")]
17use early_panic::early_panic;
18#[cfg(feature = "certora")]
19use solana_cvt::token::{spl_token_2022_transfer, spl_token_transfer};
20
21#[derive(BorshDeserialize, BorshSerialize)]
22pub struct DepositParams {
23    pub amount_atoms: u64,
24    pub trader_index_hint: Option<DataIndex>,
25}
26
27impl DepositParams {
28    pub fn new(amount_atoms: u64, trader_index_hint: Option<DataIndex>) -> Self {
29        DepositParams {
30            amount_atoms,
31            trader_index_hint,
32        }
33    }
34}
35
36pub(crate) fn process_deposit(
37    program_id: &Pubkey,
38    accounts: &[AccountInfo],
39    data: &[u8],
40) -> ProgramResult {
41    let params: DepositParams = DepositParams::try_from_slice(data)?;
42    process_deposit_core(program_id, accounts, params)
43}
44
45#[cfg_attr(all(feature = "certora", not(feature = "certora-test")), early_panic)]
46pub(crate) fn process_deposit_core(
47    _program_id: &Pubkey,
48    accounts: &[AccountInfo],
49    params: DepositParams,
50) -> ProgramResult {
51    let deposit_context: DepositContext = DepositContext::load(accounts)?;
52    let DepositParams {
53        amount_atoms,
54        trader_index_hint,
55    } = params;
56    // Due to transfer fees, this might not be what you expect.
57    let mut deposited_amount_atoms: u64 = amount_atoms;
58
59    let DepositContext {
60        market,
61        payer,
62        trader_token,
63        vault,
64        token_program,
65        mint,
66    } = deposit_context;
67
68    let market_data: &mut RefMut<&mut [u8]> = &mut market.try_borrow_mut_data()?;
69    let mut dynamic_account: MarketRefMut = get_mut_dynamic_account(market_data);
70
71    // Validation already verifies that the mint is either base or quote.
72    let is_base: bool =
73        &trader_token.try_borrow_data()?[0..32] == dynamic_account.get_base_mint().as_ref();
74
75    if *vault.owner == spl_token_2022::id() {
76        let before_vault_balance_atoms: u64 = vault.get_balance_atoms();
77        spl_token_2022_transfer_from_trader_to_vault(
78            &token_program,
79            &trader_token,
80            Some(mint),
81            if is_base {
82                dynamic_account.fixed.get_base_mint()
83            } else {
84                dynamic_account.get_quote_mint()
85            },
86            &vault,
87            &payer,
88            amount_atoms,
89            if is_base {
90                dynamic_account.fixed.get_base_mint_decimals()
91            } else {
92                dynamic_account.fixed.get_quote_mint_decimals()
93            },
94        )?;
95
96        let after_vault_balance_atoms: u64 = vault.get_balance_atoms();
97        deposited_amount_atoms = after_vault_balance_atoms
98            .checked_sub(before_vault_balance_atoms)
99            .unwrap();
100    } else {
101        spl_token_transfer_from_trader_to_vault(
102            &token_program,
103            &trader_token,
104            &vault,
105            &payer,
106            amount_atoms,
107        )?;
108    }
109
110    let trader_index: DataIndex =
111        get_trader_index_with_hint(trader_index_hint, &dynamic_account, &payer)?;
112    dynamic_account.deposit(trader_index, deposited_amount_atoms, is_base)?;
113
114    emit_stack(DepositLog {
115        market: *market.key,
116        trader: *payer.key,
117        mint: if is_base {
118            *dynamic_account.get_base_mint()
119        } else {
120            *dynamic_account.get_quote_mint()
121        },
122        amount_atoms: deposited_amount_atoms,
123    })?;
124
125    Ok(())
126}
127
128/** Transfer from base (quote) trader to base (quote) vault using SPL Token **/
129#[cfg(not(feature = "certora"))]
130fn spl_token_transfer_from_trader_to_vault<'a, 'info>(
131    token_program: &TokenProgram<'a, 'info>,
132    trader_account: &TokenAccountInfo<'a, 'info>,
133    vault: &TokenAccountInfo<'a, 'info>,
134    payer: &Signer<'a, 'info>,
135    amount: u64,
136) -> ProgramResult {
137    crate::program::invoke(
138        &spl_token::instruction::transfer(
139            token_program.key,
140            trader_account.key,
141            vault.key,
142            payer.key,
143            &[],
144            amount,
145        )?,
146        &[
147            token_program.as_ref().clone(),
148            trader_account.as_ref().clone(),
149            vault.as_ref().clone(),
150            payer.as_ref().clone(),
151        ],
152    )
153}
154#[cfg(feature = "certora")]
155/** (Summary) Transfer from base (quote) trader to base (quote) vault using SPL Token **/
156fn spl_token_transfer_from_trader_to_vault<'a, 'info>(
157    _token_program: &TokenProgram<'a, 'info>,
158    trader_account: &TokenAccountInfo<'a, 'info>,
159    vault: &TokenAccountInfo<'a, 'info>,
160    payer: &Signer<'a, 'info>,
161    amount: u64,
162) -> ProgramResult {
163    spl_token_transfer(trader_account.info, vault.info, payer.info, amount)
164}
165
166/** Transfer from base (quote) trader to base (quote) vault using SPL Token 2022 **/
167#[cfg(not(feature = "certora"))]
168fn spl_token_2022_transfer_from_trader_to_vault<'a, 'info>(
169    token_program: &TokenProgram<'a, 'info>,
170    trader_account: &TokenAccountInfo<'a, 'info>,
171    mint: Option<MintAccountInfo<'a, 'info>>,
172    mint_pubkey: &Pubkey,
173    vault: &TokenAccountInfo<'a, 'info>,
174    payer: &Signer<'a, 'info>,
175    amount: u64,
176    decimals: u8,
177) -> ProgramResult {
178    crate::program::invoke(
179        &spl_token_2022::instruction::transfer_checked(
180            token_program.key,
181            trader_account.key,
182            mint_pubkey,
183            vault.key,
184            payer.key,
185            &[],
186            amount,
187            decimals,
188        )?,
189        &[
190            token_program.as_ref().clone(),
191            trader_account.as_ref().clone(),
192            vault.as_ref().clone(),
193            mint.unwrap().as_ref().clone(),
194            payer.as_ref().clone(),
195        ],
196    )
197}
198
199#[cfg(feature = "certora")]
200/** (Summary) Transfer from base (quote) trader to base (quote) vault using SPL Token 2022 **/
201fn spl_token_2022_transfer_from_trader_to_vault<'a, 'info>(
202    _token_program: &TokenProgram<'a, 'info>,
203    trader_account: &TokenAccountInfo<'a, 'info>,
204    _mint: Option<MintAccountInfo<'a, 'info>>,
205    _mint_pubkey: &Pubkey,
206    vault: &TokenAccountInfo<'a, 'info>,
207    payer: &Signer<'a, 'info>,
208    amount: u64,
209    _decimals: u8,
210) -> ProgramResult {
211    spl_token_2022_transfer(trader_account.info, vault.info, payer.info, amount)
212}