spl_token_2022/extension/transfer_hook/
processor.rs1use {
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}