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