spl_token_2022/extension/group_pointer/
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/// Group pointer 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 GroupPointerInstruction {
25    /// Initialize a new mint with a group pointer
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::group_pointer::instruction::InitializeInstructionData`
40    Initialize,
41    /// Update the group pointer address. Only supported for mints that
42    /// include the `GroupPointer` extension.
43    ///
44    /// Accounts expected by this instruction:
45    ///
46    ///   * Single authority
47    ///   0. `[writable]` The mint.
48    ///   1. `[signer]` The group pointer authority.
49    ///
50    ///   * Multisignature authority
51    ///   0. `[writable]` The mint.
52    ///   1. `[]` The mint's group pointer authority.
53    ///   2. `..2+M` `[signer]` M signer accounts.
54    ///
55    /// Data expected by this instruction:
56    ///   `crate::extension::group_pointer::instruction::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 group address
67    pub authority: OptionalNonZeroPubkey,
68    /// The account address that holds the group
69    pub group_address: 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 new account address that holds the group configurations
79    pub group_address: OptionalNonZeroPubkey,
80}
81
82/// Create an `Initialize` instruction
83pub fn initialize(
84    token_program_id: &Pubkey,
85    mint: &Pubkey,
86    authority: Option<Pubkey>,
87    group_address: 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::GroupPointerExtension,
95        GroupPointerInstruction::Initialize,
96        &InitializeInstructionData {
97            authority: authority.try_into()?,
98            group_address: group_address.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    group_address: 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::GroupPointerExtension,
123        GroupPointerInstruction::Update,
124        &UpdateInstructionData {
125            group_address: group_address.try_into()?,
126        },
127    ))
128}