tensor_toolbox/token_2022/
token.rs1use anchor_lang::{
2 context::CpiContext,
3 error::ErrorCode,
4 solana_program::{
5 account_info::AccountInfo, msg, program_pack::IsInitialized, rent::Rent, sysvar::Sysvar,
6 },
7 system_program::create_account,
8 Result,
9};
10use anchor_spl::{
11 token_2022::InitializeAccount3,
12 token_interface::{
13 initialize_account3,
14 spl_token_2022::{
15 extension::{BaseStateWithExtensions, StateWithExtensions},
16 state::{Account, Mint},
17 },
18 },
19};
20
21use super::extension::{get_extension_types, IExtensionType};
22
23pub struct InitializeTokenAccount<'a, 'b, 'info> {
25 pub token_info: &'a AccountInfo<'info>,
26 pub mint: &'a AccountInfo<'info>,
27 pub authority: &'a AccountInfo<'info>,
28 pub payer: &'a AccountInfo<'info>,
29 pub system_program: &'a AccountInfo<'info>,
30 pub token_program: &'a AccountInfo<'info>,
31 pub signer_seeds: &'a [&'b [u8]],
32}
33
34pub fn safe_initialize_token_account(
44 input: InitializeTokenAccount<'_, '_, '_>,
45 allow_existing: bool,
46) -> Result<()> {
47 let mint_data = &input.mint.data.borrow();
48 let mint = StateWithExtensions::<Mint>::unpack(mint_data)?;
49 let extensions = IExtensionType::get_required_init_account_extensions(&get_extension_types(
51 mint.get_tlv_data(),
52 )?);
53
54 if input.token_info.owner == &anchor_lang::solana_program::system_program::ID
56 && input.token_info.lamports() == 0
57 {
58 let required_length = IExtensionType::try_calculate_account_len::<Account>(&extensions)?;
62
63 let lamports = Rent::get()?.minimum_balance(required_length);
64 let cpi_accounts = anchor_lang::system_program::CreateAccount {
65 from: input.payer.clone(),
66 to: input.token_info.clone(),
67 };
68
69 let cpi_context = CpiContext::new(input.system_program.clone(), cpi_accounts);
71 create_account(
72 cpi_context.with_signer(&[input.signer_seeds]),
73 lamports,
74 required_length as u64,
75 input.token_program.key,
76 )?;
77
78 let cpi_ctx = anchor_lang::context::CpiContext::new(
80 input.token_program.clone(),
81 InitializeAccount3 {
82 account: input.token_info.clone(),
83 mint: input.mint.clone(),
84 authority: input.authority.clone(),
85 },
86 );
87 initialize_account3(cpi_ctx)?;
88 } else if allow_existing {
89 if input.token_info.owner != input.token_program.key {
91 msg!("Invalid token account owner");
92 return Err(ErrorCode::AccountOwnedByWrongProgram.into());
93 }
94
95 let token_data = &input.token_info.data.borrow();
96 let token = StateWithExtensions::<Account>::unpack(token_data)?;
97
98 if !token.base.is_initialized() {
99 msg!("Token account is not initialized");
100 return Err(ErrorCode::AccountNotInitialized.into());
101 }
102
103 if token.base.mint != *input.mint.key {
104 msg!("Invalid mint on token account");
105 return Err(ErrorCode::ConstraintTokenMint.into());
106 }
107
108 if token.base.owner != *input.authority.key {
109 msg!("Invalid token owner");
110 return Err(ErrorCode::ConstraintOwner.into());
111 }
112 } else {
113 msg!("Token account already exists (reinitialization not allowed)");
115 return Err(ErrorCode::ConstraintState.into());
116 }
117
118 Ok(())
119}