spl_token_2022/extension/
reallocate.rs1use {
2 crate::{
3 error::TokenError,
4 extension::{
5 set_account_type, AccountType, BaseStateWithExtensions, ExtensionType,
6 StateWithExtensions, StateWithExtensionsMut,
7 },
8 processor::Processor,
9 state::Account,
10 },
11 solana_program::{
12 account_info::{next_account_info, AccountInfo},
13 entrypoint::ProgramResult,
14 msg,
15 program::invoke,
16 program_option::COption,
17 pubkey::Pubkey,
18 system_instruction,
19 sysvar::{rent::Rent, Sysvar},
20 },
21};
22
23pub fn process_reallocate(
25 program_id: &Pubkey,
26 accounts: &[AccountInfo],
27 new_extension_types: Vec<ExtensionType>,
28) -> ProgramResult {
29 let account_info_iter = &mut accounts.iter();
30 let token_account_info = next_account_info(account_info_iter)?;
31 let payer_info = next_account_info(account_info_iter)?;
32 let system_program_info = next_account_info(account_info_iter)?;
33 let authority_info = next_account_info(account_info_iter)?;
34 let authority_info_data_len = authority_info.data_len();
35
36 let (mut current_extension_types, native_token_amount) = {
38 let token_account = token_account_info.data.borrow();
39 let account = StateWithExtensions::<Account>::unpack(&token_account)?;
40 Processor::validate_owner(
41 program_id,
42 &account.base.owner,
43 authority_info,
44 authority_info_data_len,
45 account_info_iter.as_slice(),
46 )?;
47 let native_token_amount = account.base.is_native().then_some(account.base.amount);
48 (account.get_extension_types()?, native_token_amount)
49 };
50
51 if new_extension_types
53 .iter()
54 .any(|extension_type| extension_type.get_account_type() != AccountType::Account)
55 {
56 return Err(TokenError::InvalidState.into());
57 }
58 current_extension_types.extend_from_slice(&new_extension_types);
61 let needed_account_len =
62 ExtensionType::try_calculate_account_len::<Account>(¤t_extension_types)?;
63
64 if token_account_info.data_len() >= needed_account_len {
66 return Ok(());
67 }
68
69 msg!(
71 "account needs realloc, +{:?} bytes",
72 needed_account_len - token_account_info.data_len()
73 );
74 token_account_info.realloc(needed_account_len, false)?;
75
76 let rent = Rent::get()?;
78 let new_rent_exempt_reserve = rent.minimum_balance(needed_account_len);
79
80 let current_lamport_reserve = token_account_info
81 .lamports()
82 .checked_sub(native_token_amount.unwrap_or(0))
83 .ok_or(TokenError::Overflow)?;
84 let lamports_diff = new_rent_exempt_reserve.saturating_sub(current_lamport_reserve);
85 if lamports_diff > 0 {
86 invoke(
87 &system_instruction::transfer(payer_info.key, token_account_info.key, lamports_diff),
88 &[
89 payer_info.clone(),
90 token_account_info.clone(),
91 system_program_info.clone(),
92 ],
93 )?;
94 }
95
96 let mut token_account_data = token_account_info.data.borrow_mut();
98 set_account_type::<Account>(&mut token_account_data)?;
99
100 if let Some(native_token_amount) = native_token_amount {
102 let mut token_account = StateWithExtensionsMut::<Account>::unpack(&mut token_account_data)?;
103 let minimum_lamports = new_rent_exempt_reserve
106 .checked_add(native_token_amount)
107 .ok_or(TokenError::Overflow)?;
108 if token_account_info.lamports() < minimum_lamports {
109 return Err(TokenError::InvalidState.into());
110 }
111 token_account.base.is_native = COption::Some(new_rent_exempt_reserve);
112 token_account.pack_base();
113 }
114
115 Ok(())
116}