spl_token_2022/extension/scaled_ui_amount/
processor.rs

1use {
2    crate::{
3        check_program_account,
4        error::TokenError,
5        extension::{
6            scaled_ui_amount::{
7                instruction::{
8                    InitializeInstructionData, ScaledUiAmountMintInstruction,
9                    UpdateMultiplierInstructionData,
10                },
11                PodF64, ScaledUiAmountConfig, UnixTimestamp,
12            },
13            BaseStateWithExtensionsMut, PodStateWithExtensionsMut,
14        },
15        instruction::{decode_instruction_data, decode_instruction_type},
16        pod::PodMint,
17        processor::Processor,
18    },
19    solana_program::{
20        account_info::{next_account_info, AccountInfo},
21        clock::Clock,
22        entrypoint::ProgramResult,
23        msg,
24        pubkey::Pubkey,
25        sysvar::Sysvar,
26    },
27    spl_pod::optional_keys::OptionalNonZeroPubkey,
28};
29
30fn try_validate_multiplier(multiplier: &PodF64) -> ProgramResult {
31    let float_multiplier = f64::from(*multiplier);
32    if float_multiplier.is_sign_positive() && float_multiplier.is_normal() {
33        Ok(())
34    } else {
35        Err(TokenError::InvalidScale.into())
36    }
37}
38
39fn process_initialize(
40    _program_id: &Pubkey,
41    accounts: &[AccountInfo],
42    authority: &OptionalNonZeroPubkey,
43    multiplier: &PodF64,
44) -> ProgramResult {
45    let account_info_iter = &mut accounts.iter();
46    let mint_account_info = next_account_info(account_info_iter)?;
47    let mut mint_data = mint_account_info.data.borrow_mut();
48    let mut mint = PodStateWithExtensionsMut::<PodMint>::unpack_uninitialized(&mut mint_data)?;
49
50    let extension = mint.init_extension::<ScaledUiAmountConfig>(true)?;
51    extension.authority = *authority;
52    try_validate_multiplier(multiplier)?;
53    extension.multiplier = *multiplier;
54    extension.new_multiplier_effective_timestamp = 0.into();
55    extension.new_multiplier = *multiplier;
56    Ok(())
57}
58
59fn process_update_multiplier(
60    program_id: &Pubkey,
61    accounts: &[AccountInfo],
62    new_multiplier: &PodF64,
63    effective_timestamp: &UnixTimestamp,
64) -> ProgramResult {
65    let account_info_iter = &mut accounts.iter();
66    let mint_account_info = next_account_info(account_info_iter)?;
67    let owner_info = next_account_info(account_info_iter)?;
68    let owner_info_data_len = owner_info.data_len();
69
70    let mut mint_data = mint_account_info.data.borrow_mut();
71    let mut mint = PodStateWithExtensionsMut::<PodMint>::unpack(&mut mint_data)?;
72    let extension = mint.get_extension_mut::<ScaledUiAmountConfig>()?;
73    let authority =
74        Option::<Pubkey>::from(extension.authority).ok_or(TokenError::NoAuthorityExists)?;
75
76    Processor::validate_owner(
77        program_id,
78        &authority,
79        owner_info,
80        owner_info_data_len,
81        account_info_iter.as_slice(),
82    )?;
83
84    try_validate_multiplier(new_multiplier)?;
85    let clock = Clock::get()?;
86    extension.new_multiplier = *new_multiplier;
87    let int_effective_timestamp = i64::from(*effective_timestamp);
88    // just floor it to 0
89    if int_effective_timestamp < 0 {
90        extension.new_multiplier_effective_timestamp = 0.into();
91    } else {
92        extension.new_multiplier_effective_timestamp = *effective_timestamp;
93    }
94    // if the new effective timestamp has already passed, also set the old
95    // multiplier, just to be clear
96    if clock.unix_timestamp >= int_effective_timestamp {
97        extension.multiplier = *new_multiplier;
98    }
99    Ok(())
100}
101
102pub(crate) fn process_instruction(
103    program_id: &Pubkey,
104    accounts: &[AccountInfo],
105    input: &[u8],
106) -> ProgramResult {
107    check_program_account(program_id)?;
108    match decode_instruction_type(input)? {
109        ScaledUiAmountMintInstruction::Initialize => {
110            msg!("ScaledUiAmountMintInstruction::Initialize");
111            let InitializeInstructionData {
112                authority,
113                multiplier,
114            } = decode_instruction_data(input)?;
115            process_initialize(program_id, accounts, authority, multiplier)
116        }
117        ScaledUiAmountMintInstruction::UpdateMultiplier => {
118            msg!("ScaledUiAmountMintInstruction::UpdateScale");
119            let UpdateMultiplierInstructionData {
120                effective_timestamp,
121                multiplier,
122            } = decode_instruction_data(input)?;
123            process_update_multiplier(program_id, accounts, multiplier, effective_timestamp)
124        }
125    }
126}