spl_associated_token_account/tools/
account.rs

1//! Account utility functions
2
3use {
4    solana_program::{
5        account_info::AccountInfo,
6        entrypoint::ProgramResult,
7        program::{get_return_data, invoke, invoke_signed},
8        program_error::ProgramError,
9        pubkey::Pubkey,
10        rent::Rent,
11        system_instruction,
12    },
13    spl_token_2022::extension::ExtensionType,
14    std::convert::TryInto,
15};
16
17/// Creates associated token account using Program Derived Address for the given
18/// seeds
19pub fn create_pda_account<'a>(
20    payer: &AccountInfo<'a>,
21    rent: &Rent,
22    space: usize,
23    owner: &Pubkey,
24    system_program: &AccountInfo<'a>,
25    new_pda_account: &AccountInfo<'a>,
26    new_pda_signer_seeds: &[&[u8]],
27) -> ProgramResult {
28    if new_pda_account.lamports() > 0 {
29        let required_lamports = rent
30            .minimum_balance(space)
31            .max(1)
32            .saturating_sub(new_pda_account.lamports());
33
34        if required_lamports > 0 {
35            invoke(
36                &system_instruction::transfer(payer.key, new_pda_account.key, required_lamports),
37                &[
38                    payer.clone(),
39                    new_pda_account.clone(),
40                    system_program.clone(),
41                ],
42            )?;
43        }
44
45        invoke_signed(
46            &system_instruction::allocate(new_pda_account.key, space as u64),
47            &[new_pda_account.clone(), system_program.clone()],
48            &[new_pda_signer_seeds],
49        )?;
50
51        invoke_signed(
52            &system_instruction::assign(new_pda_account.key, owner),
53            &[new_pda_account.clone(), system_program.clone()],
54            &[new_pda_signer_seeds],
55        )
56    } else {
57        invoke_signed(
58            &system_instruction::create_account(
59                payer.key,
60                new_pda_account.key,
61                rent.minimum_balance(space).max(1),
62                space as u64,
63                owner,
64            ),
65            &[
66                payer.clone(),
67                new_pda_account.clone(),
68                system_program.clone(),
69            ],
70            &[new_pda_signer_seeds],
71        )
72    }
73}
74
75/// Determines the required initial data length for a new token account based on
76/// the extensions initialized on the Mint
77pub fn get_account_len<'a>(
78    mint: &AccountInfo<'a>,
79    spl_token_program: &AccountInfo<'a>,
80    extension_types: &[ExtensionType],
81) -> Result<usize, ProgramError> {
82    invoke(
83        &spl_token_2022::instruction::get_account_data_size(
84            spl_token_program.key,
85            mint.key,
86            extension_types,
87        )?,
88        &[mint.clone(), spl_token_program.clone()],
89    )?;
90    get_return_data()
91        .ok_or(ProgramError::InvalidInstructionData)
92        .and_then(|(key, data)| {
93            if key != *spl_token_program.key {
94                return Err(ProgramError::IncorrectProgramId);
95            }
96            data.try_into()
97                .map(usize::from_le_bytes)
98                .map_err(|_| ProgramError::InvalidInstructionData)
99        })
100}