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