use {
crate::{
onchain::{write_bytes, UNINIT_BYTE},
program::ID,
},
core::{mem::MaybeUninit, slice::from_raw_parts},
pinocchio::{
account_info::AccountInfo,
instruction::{AccountMeta, Instruction, Signer},
program::invoke_signed,
pubkey::Pubkey,
ProgramResult,
},
};
pub struct RemoveEntry<'a> {
pub whitelist_account: &'a AccountInfo,
pub whitelist_authority: &'a AccountInfo,
pub whitelist_entry_account: &'a AccountInfo,
pub destination_account: &'a AccountInfo,
pub system_program: &'a AccountInfo,
pub program_authority: Pubkey,
}
impl<'a> RemoveEntry<'a> {
#[inline(always)]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
fn create_instruction_data(program_authority: &Pubkey) -> [MaybeUninit<u8>; 33] {
let mut instruction_data = [UNINIT_BYTE; 33];
write_bytes(&mut instruction_data, &[5]);
write_bytes(&mut instruction_data[1..33], program_authority.as_ref());
instruction_data
}
pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
let account_metas: [AccountMeta; 5] = [
AccountMeta::readonly(self.whitelist_account.key()),
AccountMeta::readonly_signer(self.whitelist_authority.key()),
AccountMeta::writable(self.whitelist_entry_account.key()),
AccountMeta::writable(self.destination_account.key()),
AccountMeta::readonly(self.system_program.key()),
];
let instruction_data = Self::create_instruction_data(&self.program_authority);
let instruction = Instruction {
program_id: &ID,
accounts: &account_metas,
data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) },
};
invoke_signed(
&instruction,
&[
self.whitelist_account,
self.whitelist_authority,
self.whitelist_entry_account,
self.destination_account,
self.system_program,
],
signers,
)
}
}
pub fn remove_entry(
whitelist_account: &AccountInfo,
whitelist_authority: &AccountInfo,
whitelist_entry_account: &AccountInfo,
destination_account: &AccountInfo,
system_program: &AccountInfo,
program_authority: Pubkey,
) -> ProgramResult {
let ix = RemoveEntry {
whitelist_account,
whitelist_authority,
whitelist_entry_account,
destination_account,
system_program,
program_authority,
};
ix.invoke()
}
pub fn remove_entry_with_seed(
whitelist_account: &AccountInfo,
whitelist_authority: &AccountInfo,
whitelist_entry_account: &AccountInfo,
destination_account: &AccountInfo,
system_program: &AccountInfo,
program_authority: Pubkey,
signer_seeds: &[Signer],
) -> ProgramResult {
let ix = RemoveEntry {
whitelist_account,
whitelist_authority,
whitelist_entry_account,
destination_account,
system_program,
program_authority,
};
ix.invoke_signed(signer_seeds)
}
#[cfg(test)]
mod tests {
use {super::*, crate::instructions::ProgramWhitelistInstruction};
#[test]
fn test_instruction_data_creation() {
let program_authority_bytes = [42u8; 32]; let program_authority = Pubkey::from(program_authority_bytes);
let instruction_data = RemoveEntry::create_instruction_data(&program_authority);
let instruction = Instruction {
program_id: &ID,
accounts: &[], data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) },
};
let parsed_ix = ProgramWhitelistInstruction::unpack(instruction.data).unwrap();
match parsed_ix {
ProgramWhitelistInstruction::RemoveEntry {
program_authority: parsed_program_authority,
} => {
assert_eq!(parsed_program_authority, program_authority);
}
_ => panic!("Parsed incorrect instruction type"),
}
}
}