spl_token_2022/extension/transfer_hook/
instruction.rs

1#[cfg(feature = "serde-traits")]
2use serde::{Deserialize, Serialize};
3use {
4    crate::{
5        check_program_account,
6        instruction::{encode_instruction, TokenInstruction},
7    },
8    bytemuck::{Pod, Zeroable},
9    num_enum::{IntoPrimitive, TryFromPrimitive},
10    solana_program::{
11        instruction::{AccountMeta, Instruction},
12        program_error::ProgramError,
13        pubkey::Pubkey,
14    },
15    spl_pod::optional_keys::OptionalNonZeroPubkey,
16    std::convert::TryInto,
17};
18
19/// Transfer hook extension instructions
20#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
21#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
22#[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)]
23#[repr(u8)]
24pub enum TransferHookInstruction {
25    /// Initialize a new mint with a transfer hook program.
26    ///
27    /// Fails if the mint has already been initialized, so must be called before
28    /// `InitializeMint`.
29    ///
30    /// The mint must have exactly enough space allocated for the base mint (82
31    /// bytes), plus 83 bytes of padding, 1 byte reserved for the account type,
32    /// then space required for this extension, plus any others.
33    ///
34    /// Accounts expected by this instruction:
35    ///
36    ///   0. `[writable]` The mint to initialize.
37    ///
38    /// Data expected by this instruction:
39    ///   `crate::extension::transfer_hook::instruction::InitializeInstructionData`
40    Initialize,
41    /// Update the transfer hook program id. Only supported for mints that
42    /// include the `TransferHook` extension.
43    ///
44    /// Accounts expected by this instruction:
45    ///
46    ///   * Single authority
47    ///   0. `[writable]` The mint.
48    ///   1. `[signer]` The transfer hook authority.
49    ///
50    ///   * Multisignature authority
51    ///   0. `[writable]` The mint.
52    ///   1. `[]` The mint's transfer hook authority.
53    ///   2. `..2+M` `[signer]` M signer accounts.
54    ///
55    /// Data expected by this instruction:
56    ///   `crate::extension::transfer_hook::UpdateInstructionData`
57    Update,
58}
59
60/// Data expected by `Initialize`
61#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
62#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
63#[derive(Clone, Copy, Pod, Zeroable)]
64#[repr(C)]
65pub struct InitializeInstructionData {
66    /// The public key for the account that can update the program id
67    pub authority: OptionalNonZeroPubkey,
68    /// The program id that performs logic during transfers
69    pub program_id: OptionalNonZeroPubkey,
70}
71
72/// Data expected by `Update`
73#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
74#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
75#[derive(Clone, Copy, Pod, Zeroable)]
76#[repr(C)]
77pub struct UpdateInstructionData {
78    /// The program id that performs logic during transfers
79    pub program_id: OptionalNonZeroPubkey,
80}
81
82/// Create an `Initialize` instruction
83pub fn initialize(
84    token_program_id: &Pubkey,
85    mint: &Pubkey,
86    authority: Option<Pubkey>,
87    transfer_hook_program_id: Option<Pubkey>,
88) -> Result<Instruction, ProgramError> {
89    check_program_account(token_program_id)?;
90    let accounts = vec![AccountMeta::new(*mint, false)];
91    Ok(encode_instruction(
92        token_program_id,
93        accounts,
94        TokenInstruction::TransferHookExtension,
95        TransferHookInstruction::Initialize,
96        &InitializeInstructionData {
97            authority: authority.try_into()?,
98            program_id: transfer_hook_program_id.try_into()?,
99        },
100    ))
101}
102
103/// Create an `Update` instruction
104pub fn update(
105    token_program_id: &Pubkey,
106    mint: &Pubkey,
107    authority: &Pubkey,
108    signers: &[&Pubkey],
109    transfer_hook_program_id: Option<Pubkey>,
110) -> Result<Instruction, ProgramError> {
111    check_program_account(token_program_id)?;
112    let mut accounts = vec![
113        AccountMeta::new(*mint, false),
114        AccountMeta::new_readonly(*authority, signers.is_empty()),
115    ];
116    for signer_pubkey in signers.iter() {
117        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
118    }
119    Ok(encode_instruction(
120        token_program_id,
121        accounts,
122        TokenInstruction::TransferHookExtension,
123        TransferHookInstruction::Update,
124        &UpdateInstructionData {
125            program_id: transfer_hook_program_id.try_into()?,
126        },
127    ))
128}