spl_token_2022/extension/scaled_ui_amount/
instruction.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#[cfg(feature = "serde-traits")]
use serde::{Deserialize, Serialize};
use {
    crate::{
        check_program_account,
        extension::scaled_ui_amount::{PodF64, UnixTimestamp},
        instruction::{encode_instruction, TokenInstruction},
    },
    bytemuck::{Pod, Zeroable},
    num_enum::{IntoPrimitive, TryFromPrimitive},
    solana_program::{
        instruction::{AccountMeta, Instruction},
        program_error::ProgramError,
        pubkey::Pubkey,
    },
    spl_pod::optional_keys::OptionalNonZeroPubkey,
    std::convert::TryInto,
};

/// Interesting-bearing mint extension instructions
#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
#[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)]
#[repr(u8)]
pub enum ScaledUiAmountMintInstruction {
    /// Initialize a new mint with scaled UI amounts.
    ///
    /// Fails if the mint has already been initialized, so must be called before
    /// `InitializeMint`.
    ///
    /// Fails if the multiplier is less than or equal to 0 or if it's
    /// [subnormal](https://en.wikipedia.org/wiki/Subnormal_number).
    ///
    /// The mint must have exactly enough space allocated for the base mint (82
    /// bytes), plus 83 bytes of padding, 1 byte reserved for the account type,
    /// then space required for this extension, plus any others.
    ///
    /// Accounts expected by this instruction:
    ///
    ///   0. `[writable]` The mint to initialize.
    ///
    /// Data expected by this instruction:
    ///   `crate::extension::scaled_ui_amount::instruction::InitializeInstructionData`
    Initialize,
    /// Update the multiplier. Only supported for mints that include the
    /// `ScaledUiAmount` extension.
    ///
    /// Fails if the multiplier is less than or equal to 0 or if it's
    /// [subnormal](https://en.wikipedia.org/wiki/Subnormal_number).
    ///
    /// The authority provides a new multiplier and a UNIX timestamp on which
    /// it should take effect. If the timestamp is before the current time,
    /// immediately sets the multiplier.
    ///
    /// Accounts expected by this instruction:
    ///
    ///   * Single authority
    ///   0. `[writable]` The mint.
    ///   1. `[signer]` The multiplier authority.
    ///
    ///   * Multisignature authority
    ///   0. `[writable]` The mint.
    ///   1. `[]` The mint's multisignature multiplier authority.
    ///   2. `..2+M` `[signer]` M signer accounts.
    ///
    /// Data expected by this instruction:
    ///   `crate::extension::scaled_ui_amount::instruction::UpdateMultiplierInstructionData`
    UpdateMultiplier,
}

/// Data expected by `ScaledUiAmountMint::Initialize`
#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct InitializeInstructionData {
    /// The public key for the account that can update the multiplier
    pub authority: OptionalNonZeroPubkey,
    /// The initial multiplier
    pub multiplier: PodF64,
}

/// Data expected by `ScaledUiAmountMint::UpdateMultiplier`
#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct UpdateMultiplierInstructionData {
    /// The new multiplier
    pub multiplier: PodF64,
    /// Timestamp at which the new multiplier will take effect
    pub effective_timestamp: UnixTimestamp,
}

/// Create an `Initialize` instruction
pub fn initialize(
    token_program_id: &Pubkey,
    mint: &Pubkey,
    authority: Option<Pubkey>,
    multiplier: f64,
) -> Result<Instruction, ProgramError> {
    check_program_account(token_program_id)?;
    let accounts = vec![AccountMeta::new(*mint, false)];
    Ok(encode_instruction(
        token_program_id,
        accounts,
        TokenInstruction::ScaledUiAmountExtension,
        ScaledUiAmountMintInstruction::Initialize,
        &InitializeInstructionData {
            authority: authority.try_into()?,
            multiplier: multiplier.into(),
        },
    ))
}

/// Create an `UpdateMultiplier` instruction
pub fn update_multiplier(
    token_program_id: &Pubkey,
    mint: &Pubkey,
    authority: &Pubkey,
    signers: &[&Pubkey],
    multiplier: f64,
    effective_timestamp: i64,
) -> Result<Instruction, ProgramError> {
    check_program_account(token_program_id)?;
    let mut accounts = vec![
        AccountMeta::new(*mint, false),
        AccountMeta::new_readonly(*authority, signers.is_empty()),
    ];
    for signer_pubkey in signers.iter() {
        accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
    }
    Ok(encode_instruction(
        token_program_id,
        accounts,
        TokenInstruction::ScaledUiAmountExtension,
        ScaledUiAmountMintInstruction::UpdateMultiplier,
        &UpdateMultiplierInstructionData {
            effective_timestamp: effective_timestamp.into(),
            multiplier: multiplier.into(),
        },
    ))
}