token_acl/instructions/
delete_config.rs

1use solana_cpi::invoke_signed;
2use solana_program::{account_info::AccountInfo, pubkey::Pubkey};
3use solana_program_error::{ProgramError, ProgramResult};
4use spl_token_2022::{extension::PodStateWithExtensions, instruction::AuthorityType, pod::PodMint};
5
6use crate::{
7    error::TokenAclError,
8    state::{load_mint_config, MintConfig},
9};
10
11pub struct DeleteConfig<'a> {
12    pub authority: &'a AccountInfo<'a>,
13    pub receiver: &'a AccountInfo<'a>,
14    pub mint: &'a AccountInfo<'a>,
15    pub mint_config: &'a AccountInfo<'a>,
16    pub token_program: &'a AccountInfo<'a>,
17}
18
19impl DeleteConfig<'_> {
20    pub const DISCRIMINATOR: u8 = 3;
21
22    pub fn process(&self, remaining_data: &[u8]) -> ProgramResult {
23        if remaining_data.len() != 32 {
24            return Err(ProgramError::InvalidInstructionData);
25        }
26        let new_freeze_authority =
27            Pubkey::try_from(remaining_data).map_err(|_| ProgramError::InvalidInstructionData)?;
28
29        // only set the freeze authority if the mint_config is still the freeze authority
30        // this also ensures that the mint still exists and is initialized
31        let mint_data = self.mint.data.borrow_mut();
32        let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data);
33        let set_freeze_authority = mint.and_then(|mint| Ok(mint.base.freeze_authority.unwrap_or(Pubkey::default()) == *self.mint_config.key)).unwrap_or(false);
34        drop(mint_data);
35         
36        let bump_seed = {
37            let data = &mut self.mint_config.data.borrow_mut();
38            let config = load_mint_config(data)?;
39
40            if config.freeze_authority != *self.authority.key {
41                return Err(TokenAclError::InvalidAuthority.into());
42            }
43
44            if config.mint != *self.mint.key {
45                return Err(TokenAclError::InvalidTokenMint.into());
46            }
47
48            [config.bump]
49        };
50
51        if set_freeze_authority {
52            let seeds = [MintConfig::SEED_PREFIX, self.mint.key.as_ref(), &bump_seed];
53
54            let ix = spl_token_2022::instruction::set_authority(
55                self.token_program.key,
56                self.mint.key,
57                Some(&new_freeze_authority),
58                AuthorityType::FreezeAccount,
59                self.mint_config.key,
60                &[],
61            )?;
62            invoke_signed(
63                &ix,
64                &[self.mint.clone(), self.mint_config.clone()],
65                &[&seeds],
66            )?;
67        }
68
69        **self.receiver.try_borrow_mut_lamports()? += self.mint_config.lamports();
70        **self.mint_config.try_borrow_mut_lamports()? = 0;
71        self.mint_config.realloc(0, false)?;
72        self.mint_config.assign(&Pubkey::default());
73
74        Ok(())
75    }
76}
77
78impl<'a> TryFrom<&'a [AccountInfo<'a>]> for DeleteConfig<'a> {
79    type Error = ProgramError;
80
81    fn try_from(accounts: &'a [AccountInfo<'a>]) -> Result<Self, Self::Error> {
82        let [authority, receiver, mint, mint_config, token_program] = &accounts else {
83            return Err(ProgramError::InvalidInstructionData);
84        };
85
86        if !authority.is_signer {
87            return Err(TokenAclError::InvalidAuthority.into());
88        }
89
90        if !spl_token_2022::check_id(token_program.key) {
91            return Err(TokenAclError::InvalidTokenProgram.into());
92        }
93
94        Ok(Self {
95            authority,
96            receiver,
97            mint,
98            mint_config,
99            token_program,
100        })
101    }
102}