manifest/program/processor/
global_evict.rs

1use std::cell::RefMut;
2
3use borsh::{BorshDeserialize, BorshSerialize};
4use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};
5
6use crate::{
7    global_vault_seeds_with_bump,
8    logs::{emit_stack, GlobalDepositLog, GlobalEvictLog, GlobalWithdrawLog},
9    program::get_mut_dynamic_account,
10    quantities::{GlobalAtoms, WrapperU64},
11    require,
12    state::GlobalRefMut,
13    validation::{get_global_vault_address, loaders::GlobalEvictContext},
14};
15use solana_program::program::invoke_signed;
16
17use super::invoke;
18
19#[derive(BorshDeserialize, BorshSerialize)]
20pub struct GlobalEvictParams {
21    // Deposit amount that must be greater than the evictee deposit amount
22    amount_atoms: u64,
23}
24
25impl GlobalEvictParams {
26    pub fn new(amount_atoms: u64) -> Self {
27        GlobalEvictParams { amount_atoms }
28    }
29}
30
31pub(crate) fn process_global_evict(
32    _program_id: &Pubkey,
33    accounts: &[AccountInfo],
34    data: &[u8],
35) -> ProgramResult {
36    let global_evict_context: GlobalEvictContext = GlobalEvictContext::load(accounts)?;
37    let GlobalEvictParams { amount_atoms } = GlobalEvictParams::try_from_slice(data)?;
38
39    let GlobalEvictContext {
40        payer,
41        global,
42        mint,
43        global_vault,
44        trader_token,
45        evictee_token,
46        token_program,
47    } = global_evict_context;
48
49    // 1. Withdraw for the evictee
50    // 2. Evict the seat on the global account and claim
51    // 3. Deposit for the evictor
52    let global_data: &mut RefMut<&mut [u8]> = &mut global.try_borrow_mut_data()?;
53    let mut global_dynamic_account: GlobalRefMut = get_mut_dynamic_account(global_data);
54    let evictee_balance: GlobalAtoms =
55        global_dynamic_account.get_balance_atoms(&evictee_token.get_owner());
56
57    {
58        // Do verifications that this is a valid eviction.
59        require!(
60            evictee_balance < GlobalAtoms::new(amount_atoms),
61            crate::program::ManifestError::InvalidEvict,
62            "Evictee balance {} is more than evictor wants to deposit",
63            evictee_balance.as_u64(),
64        )?;
65        global_dynamic_account.verify_min_balance(&evictee_token.get_owner())?;
66    }
67
68    // Withdraw
69    {
70        let evictee_balance: GlobalAtoms =
71            global_dynamic_account.get_balance_atoms(&evictee_token.get_owner());
72        global_dynamic_account.withdraw_global(&evictee_token.get_owner(), evictee_balance)?;
73
74        let (_, bump) = get_global_vault_address(mint.info.key);
75
76        // Do the token transfer
77        if *global_vault.owner == spl_token_2022::id() {
78            invoke_signed(
79                &spl_token_2022::instruction::transfer_checked(
80                    token_program.key,
81                    global_vault.key,
82                    mint.info.key,
83                    evictee_token.key,
84                    global_vault.key,
85                    &[],
86                    evictee_balance.into(),
87                    mint.mint.decimals,
88                )?,
89                &[
90                    token_program.as_ref().clone(),
91                    evictee_token.as_ref().clone(),
92                    mint.as_ref().clone(),
93                    global_vault.as_ref().clone(),
94                ],
95                global_vault_seeds_with_bump!(mint.info.key, bump),
96            )?;
97        } else {
98            invoke_signed(
99                &spl_token::instruction::transfer(
100                    token_program.key,
101                    global_vault.key,
102                    evictee_token.key,
103                    global_vault.key,
104                    &[],
105                    evictee_balance.into(),
106                )?,
107                &[
108                    token_program.as_ref().clone(),
109                    global_vault.as_ref().clone(),
110                    evictee_token.as_ref().clone(),
111                ],
112                global_vault_seeds_with_bump!(mint.info.key, bump),
113            )?;
114        }
115
116        emit_stack(GlobalWithdrawLog {
117            global: *global.key,
118            trader: *payer.key,
119            global_atoms: GlobalAtoms::new(amount_atoms),
120        })?;
121    }
122
123    // Evict
124    {
125        global_dynamic_account
126            .evict_and_take_seat(&evictee_token.get_owner(), &trader_token.get_owner())?;
127
128        emit_stack(GlobalEvictLog {
129            evictee: evictee_token.get_owner(),
130            evictor: trader_token.get_owner(),
131            evictor_atoms: GlobalAtoms::new(amount_atoms),
132            evictee_atoms: evictee_balance,
133        })?;
134    }
135
136    // Deposit
137    {
138        global_dynamic_account.deposit_global(payer.key, GlobalAtoms::new(amount_atoms))?;
139
140        // Do the token transfer
141        if *global_vault.owner == spl_token_2022::id() {
142            invoke(
143                &spl_token_2022::instruction::transfer_checked(
144                    token_program.key,
145                    trader_token.key,
146                    mint.info.key,
147                    global_vault.key,
148                    payer.key,
149                    &[],
150                    amount_atoms,
151                    mint.mint.decimals,
152                )?,
153                &[
154                    token_program.as_ref().clone(),
155                    trader_token.as_ref().clone(),
156                    mint.as_ref().clone(),
157                    global_vault.as_ref().clone(),
158                    payer.as_ref().clone(),
159                ],
160            )?;
161        } else {
162            invoke(
163                &spl_token::instruction::transfer(
164                    token_program.key,
165                    trader_token.key,
166                    global_vault.key,
167                    payer.key,
168                    &[],
169                    amount_atoms,
170                )?,
171                &[
172                    token_program.as_ref().clone(),
173                    trader_token.as_ref().clone(),
174                    global_vault.as_ref().clone(),
175                    payer.as_ref().clone(),
176                ],
177            )?;
178        }
179
180        emit_stack(GlobalDepositLog {
181            global: *global.key,
182            trader: *payer.key,
183            global_atoms: GlobalAtoms::new(amount_atoms),
184        })?;
185    }
186
187    Ok(())
188}