spl_token_2022/extension/transfer_hook/
processor.rs

1use {
2    crate::{
3        check_program_account,
4        error::TokenError,
5        extension::{
6            transfer_hook::{
7                instruction::{
8                    InitializeInstructionData, TransferHookInstruction, UpdateInstructionData,
9                },
10                TransferHook,
11            },
12            BaseStateWithExtensionsMut, PodStateWithExtensionsMut,
13        },
14        instruction::{decode_instruction_data, decode_instruction_type},
15        pod::PodMint,
16        processor::Processor,
17    },
18    solana_account_info::{next_account_info, AccountInfo},
19    solana_msg::msg,
20    solana_program_error::{ProgramError, ProgramResult},
21    solana_pubkey::Pubkey,
22    spl_pod::optional_keys::OptionalNonZeroPubkey,
23};
24
25fn process_initialize(
26    program_id: &Pubkey,
27    accounts: &[AccountInfo],
28    authority: &OptionalNonZeroPubkey,
29    transfer_hook_program_id: &OptionalNonZeroPubkey,
30) -> ProgramResult {
31    let account_info_iter = &mut accounts.iter();
32    let mint_account_info = next_account_info(account_info_iter)?;
33    let mut mint_data = mint_account_info.data.borrow_mut();
34    let mut mint = PodStateWithExtensionsMut::<PodMint>::unpack_uninitialized(&mut mint_data)?;
35
36    let extension = mint.init_extension::<TransferHook>(true)?;
37    extension.authority = *authority;
38
39    if let Some(transfer_hook_program_id) = Option::<Pubkey>::from(*transfer_hook_program_id) {
40        if transfer_hook_program_id == *program_id {
41            return Err(ProgramError::IncorrectProgramId);
42        }
43    } else if Option::<Pubkey>::from(*authority).is_none() {
44        msg!("The transfer hook extension requires at least an authority or a program id for initialization, neither was provided");
45        Err(TokenError::InvalidInstruction)?;
46    }
47    extension.program_id = *transfer_hook_program_id;
48    Ok(())
49}
50
51fn process_update(
52    program_id: &Pubkey,
53    accounts: &[AccountInfo],
54    new_program_id: &OptionalNonZeroPubkey,
55) -> ProgramResult {
56    let account_info_iter = &mut accounts.iter();
57    let mint_account_info = next_account_info(account_info_iter)?;
58    let owner_info = next_account_info(account_info_iter)?;
59    let owner_info_data_len = owner_info.data_len();
60
61    let mut mint_data = mint_account_info.data.borrow_mut();
62    let mut mint = PodStateWithExtensionsMut::<PodMint>::unpack(&mut mint_data)?;
63    let extension = mint.get_extension_mut::<TransferHook>()?;
64    let authority =
65        Option::<Pubkey>::from(extension.authority).ok_or(TokenError::NoAuthorityExists)?;
66
67    Processor::validate_owner(
68        program_id,
69        &authority,
70        owner_info,
71        owner_info_data_len,
72        account_info_iter.as_slice(),
73    )?;
74
75    if let Some(new_program_id) = Option::<Pubkey>::from(*new_program_id) {
76        if new_program_id == *program_id {
77            return Err(ProgramError::IncorrectProgramId);
78        }
79    }
80
81    extension.program_id = *new_program_id;
82    Ok(())
83}
84
85pub(crate) fn process_instruction(
86    program_id: &Pubkey,
87    accounts: &[AccountInfo],
88    input: &[u8],
89) -> ProgramResult {
90    check_program_account(program_id)?;
91    match decode_instruction_type(input)? {
92        TransferHookInstruction::Initialize => {
93            msg!("TransferHookInstruction::Initialize");
94            let InitializeInstructionData {
95                authority,
96                program_id: transfer_hook_program_id,
97            } = decode_instruction_data(input)?;
98            process_initialize(program_id, accounts, authority, transfer_hook_program_id)
99        }
100        TransferHookInstruction::Update => {
101            msg!("TransferHookInstruction::Update");
102            let UpdateInstructionData {
103                program_id: transfer_hook_program_id,
104            } = decode_instruction_data(input)?;
105            process_update(program_id, accounts, transfer_hook_program_id)
106        }
107    }
108}