spherenet_program_whitelist_interface/onchain/
remove_entry.rs

1//! Onchain functionality for the `RemoveEntry` instruction
2
3use {
4    crate::{
5        onchain::{write_bytes, UNINIT_BYTE},
6        program::ID,
7    },
8    core::{mem::MaybeUninit, slice::from_raw_parts},
9    pinocchio::{
10        account_info::AccountInfo,
11        instruction::{AccountMeta, Instruction, Signer},
12        program::invoke_signed,
13        pubkey::Pubkey,
14        ProgramResult,
15    },
16};
17
18/// Closes a specific whitelist entry account, transferring its rent
19/// lamports to a designated destination account.
20///
21/// ### Accounts:
22///   0. `[]` The whitelist account (Used for validation)
23///   1. `[SIGNER]` The whitelist authority
24///   2. `[WRITE]` The whitelist entry account to close
25///   3. `[WRITE]` The destination account for reclaimed lamports
26///   4. `[]` System program
27pub struct RemoveEntry<'a> {
28    /// The whitelist account (Used for validation)
29    pub whitelist_account: &'a AccountInfo,
30    /// The whitelist authority
31    pub whitelist_authority: &'a AccountInfo,
32    /// The whitelist entry account to close
33    pub whitelist_entry_account: &'a AccountInfo,
34    /// The destination account for reclaimed lamports
35    pub destination_account: &'a AccountInfo,
36    /// System program
37    pub system_program: &'a AccountInfo,
38    /// program authority public key
39    pub program_authority: Pubkey,
40}
41
42impl<'a> RemoveEntry<'a> {
43    #[inline(always)]
44    pub fn invoke(&self) -> ProgramResult {
45        self.invoke_signed(&[])
46    }
47
48    /// Creates the instruction data for the RemoveEntry instruction
49    fn create_instruction_data(program_authority: &Pubkey) -> [MaybeUninit<u8>; 33] {
50        let mut instruction_data = [UNINIT_BYTE; 33];
51
52        // Set discriminator as u8 at offset [0]
53        write_bytes(&mut instruction_data, &[5]);
54        // Set program_authority as Pubkey at offset [1..33]
55        write_bytes(&mut instruction_data[1..33], program_authority.as_ref());
56
57        instruction_data
58    }
59
60    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
61        // account metadata
62        let account_metas: [AccountMeta; 5] = [
63            AccountMeta::readonly(self.whitelist_account.key()),
64            AccountMeta::readonly_signer(self.whitelist_authority.key()),
65            AccountMeta::writable(self.whitelist_entry_account.key()),
66            AccountMeta::writable(self.destination_account.key()),
67            AccountMeta::readonly(self.system_program.key()),
68        ];
69
70        // Create instruction data
71        let instruction_data = Self::create_instruction_data(&self.program_authority);
72
73        let instruction = Instruction {
74            program_id: &ID,
75            accounts: &account_metas,
76            data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) },
77        };
78
79        invoke_signed(
80            &instruction,
81            &[
82                self.whitelist_account,
83                self.whitelist_authority,
84                self.whitelist_entry_account,
85                self.destination_account,
86                self.system_program,
87            ],
88            signers,
89        )
90    }
91}
92
93/// Convenience helper for the `RemoveEntry` instruction
94pub fn remove_entry(
95    whitelist_account: &AccountInfo,
96    whitelist_authority: &AccountInfo,
97    whitelist_entry_account: &AccountInfo,
98    destination_account: &AccountInfo,
99    system_program: &AccountInfo,
100    program_authority: Pubkey,
101) -> ProgramResult {
102    let ix = RemoveEntry {
103        whitelist_account,
104        whitelist_authority,
105        whitelist_entry_account,
106        destination_account,
107        system_program,
108        program_authority,
109    };
110    ix.invoke()
111}
112
113/// Convenience helper for the `RemoveEntry` instruction with the given
114/// signers.
115pub fn remove_entry_with_seed(
116    whitelist_account: &AccountInfo,
117    whitelist_authority: &AccountInfo,
118    whitelist_entry_account: &AccountInfo,
119    destination_account: &AccountInfo,
120    system_program: &AccountInfo,
121    program_authority: Pubkey,
122    signer_seeds: &[Signer],
123) -> ProgramResult {
124    let ix = RemoveEntry {
125        whitelist_account,
126        whitelist_authority,
127        whitelist_entry_account,
128        destination_account,
129        system_program,
130        program_authority,
131    };
132    ix.invoke_signed(signer_seeds)
133}
134
135#[cfg(test)]
136mod tests {
137    use {super::*, crate::instructions::ProgramWhitelistInstruction};
138
139    #[test]
140    fn test_instruction_data_creation() {
141        // Create a dummy pubkey for program authority
142        let program_authority_bytes = [42u8; 32]; // Use a fixed value for testing
143        let program_authority = Pubkey::from(program_authority_bytes);
144
145        // Create instruction data using our function
146        let instruction_data = RemoveEntry::create_instruction_data(&program_authority);
147
148        // Create an instruction with this data for parsing
149        let instruction = Instruction {
150            program_id: &ID,
151            accounts: &[], // Accounts not relevant for this test
152            data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) },
153        };
154
155        // Parse the instruction data
156        let parsed_ix = ProgramWhitelistInstruction::unpack(instruction.data).unwrap();
157
158        // Verify that the parsed instruction matches what we expect
159        match parsed_ix {
160            ProgramWhitelistInstruction::RemoveEntry {
161                program_authority: parsed_program_authority,
162            } => {
163                assert_eq!(parsed_program_authority, program_authority);
164            }
165            _ => panic!("Parsed incorrect instruction type"),
166        }
167    }
168}