Skip to main content

spl_token_2022_interface/extension/group_pointer/
instruction.rs

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