manifest/program/processor/
global_withdraw.rs

1use std::cell::RefMut;
2
3use borsh::{BorshDeserialize, BorshSerialize};
4use solana_program::{
5    account_info::AccountInfo, entrypoint::ProgramResult, program::invoke_signed, pubkey::Pubkey,
6};
7
8use crate::{
9    global_vault_seeds_with_bump,
10    logs::{emit_stack, GlobalWithdrawLog},
11    program::get_mut_dynamic_account,
12    quantities::{GlobalAtoms, WrapperU64},
13    state::GlobalRefMut,
14    validation::{get_global_vault_address, loaders::GlobalWithdrawContext},
15};
16
17#[derive(BorshDeserialize, BorshSerialize)]
18pub struct GlobalWithdrawParams {
19    pub amount_atoms: u64,
20    // No trader index hint because global account is small so there is not much
21    // benefit from hinted indices, unlike the market which can get large. Also,
22    // seats are not permanent like on a market due to eviction, so it is more
23    // likely that a client could send a bad request. Just look it up for them.
24}
25
26impl GlobalWithdrawParams {
27    pub fn new(amount_atoms: u64) -> Self {
28        GlobalWithdrawParams { amount_atoms }
29    }
30}
31
32pub(crate) fn process_global_withdraw(
33    _program_id: &Pubkey,
34    accounts: &[AccountInfo],
35    data: &[u8],
36) -> ProgramResult {
37    let global_withdraw_context: GlobalWithdrawContext = GlobalWithdrawContext::load(accounts)?;
38    let GlobalWithdrawParams { amount_atoms } = GlobalWithdrawParams::try_from_slice(data)?;
39
40    let GlobalWithdrawContext {
41        payer,
42        global,
43        mint,
44        global_vault,
45        trader_token,
46        token_program,
47    } = global_withdraw_context;
48
49    let global_data: &mut RefMut<&mut [u8]> = &mut global.try_borrow_mut_data()?;
50    let mut global_dynamic_account: GlobalRefMut = get_mut_dynamic_account(global_data);
51    global_dynamic_account.withdraw_global(payer.key, GlobalAtoms::new(amount_atoms))?;
52
53    let (_, bump) = get_global_vault_address(mint.info.key);
54
55    // Do the token transfer
56    if *global_vault.owner == spl_token_2022::id() {
57        invoke_signed(
58            &spl_token_2022::instruction::transfer_checked(
59                token_program.key,
60                global_vault.key,
61                mint.info.key,
62                trader_token.key,
63                global_vault.key,
64                &[],
65                amount_atoms,
66                mint.mint.decimals,
67            )?,
68            &[
69                token_program.as_ref().clone(),
70                trader_token.as_ref().clone(),
71                mint.as_ref().clone(),
72                global_vault.as_ref().clone(),
73            ],
74            global_vault_seeds_with_bump!(mint.info.key, bump),
75        )?;
76    } else {
77        invoke_signed(
78            &spl_token::instruction::transfer(
79                token_program.key,
80                global_vault.key,
81                trader_token.key,
82                global_vault.key,
83                &[],
84                amount_atoms,
85            )?,
86            &[
87                token_program.as_ref().clone(),
88                global_vault.as_ref().clone(),
89                trader_token.as_ref().clone(),
90            ],
91            global_vault_seeds_with_bump!(mint.info.key, bump),
92        )?;
93    }
94
95    emit_stack(GlobalWithdrawLog {
96        global: *global.key,
97        trader: *payer.key,
98        global_atoms: GlobalAtoms::new(amount_atoms),
99    })?;
100
101    Ok(())
102}