1use crate::errors::HplToolkitError;
2use anchor_lang::prelude::{
3 AccountInfo, Program, Rent, Result, SolanaSysvar, System, Sysvar, ToAccountInfo,
4};
5use anchor_lang::{solana_program, Lamports};
6
7mod bpf_writer;
8mod inner;
9mod long_vec_map;
10mod short_string;
11mod short_vec;
12mod types;
13mod vec_map;
14
15pub use bpf_writer::*;
16pub use inner::*;
17pub use long_vec_map::LongVecMap;
18pub use short_string::ShortString;
19pub use short_vec::ShortVec;
20pub use types::*;
21pub use vec_map::VecMap;
22
23#[track_caller]
24#[inline(always)]
25pub const fn add_signed(a: usize, b: isize) -> usize {
26 match b {
27 x if x < 0 => a - x.wrapping_abs() as usize,
28 x => a + x as usize,
29 }
30}
31
32pub fn transfer<'info>(
33 lamports: u64,
34 from: &AccountInfo<'info>,
35 to: &AccountInfo<'info>,
36 system_program: &AccountInfo<'info>,
37) -> Result<()> {
38 if from.owner.eq(&anchor_lang::system_program::ID) {
39 solana_program::program::invoke(
40 &solana_program::system_instruction::transfer(
41 from.key,
42 to.key,
43 u64::try_from(lamports).unwrap(),
44 ),
45 &[
46 from.to_owned(),
47 to.to_owned(),
48 system_program.to_account_info(),
49 ],
50 )?;
51 } else {
52 from.sub_lamports(lamports)?;
53 to.add_lamports(lamports)?;
54 }
55 Ok(())
56}
57
58pub fn reallocate_optimized<'info>(
59 len: isize,
60 account_info: &AccountInfo<'info>,
61 payer_info: &AccountInfo<'info>,
62 system_program: &AccountInfo<'info>,
63) -> Result<()> {
64 let curr_len = account_info.data_len();
65 let new_len = add_signed(curr_len, len);
66
67 let rent = solana_program::sysvar::rent::Rent::get()?;
68 let curr_rent = rent.minimum_balance(curr_len);
69 let new_rent = rent.minimum_balance(new_len);
70 let rent_diff: isize = isize::try_from(new_rent).unwrap() - isize::try_from(curr_rent).unwrap();
71
72 if rent_diff > 0 {
73 transfer(
74 u64::try_from(rent_diff).unwrap(),
75 payer_info,
76 account_info,
77 system_program,
78 )?;
79 } else if rent_diff < 0 {
80 transfer(
81 u64::try_from(rent_diff * -1).unwrap(),
82 account_info,
83 payer_info,
84 system_program,
85 )?;
86 } else {
87 return Ok(());
88 }
89 if len.wrapping_abs() as usize > solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE {
90 let mut updated_s = account_info.data_len().clone();
91 while updated_s < new_len {
92 updated_s += solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE;
93 if updated_s > new_len {
94 updated_s = new_len;
95 }
96 account_info.realloc(updated_s, false).unwrap()
97 }
98 while updated_s > new_len {
99 updated_s -= solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE;
100 if updated_s < new_len {
101 updated_s = new_len;
102 }
103 account_info.realloc(updated_s, false).unwrap()
104 }
105 Ok(())
106 } else {
107 account_info
108 .realloc(usize::try_from(new_len).unwrap(), false)
109 .map_err(Into::into)
110 }
111}
112
113pub fn reallocate<'info>(
114 len: isize,
115 account_info: AccountInfo<'info>,
116 payer_info: AccountInfo<'info>,
117 rent_sysvar: &Sysvar<'info, Rent>,
118 system_program: &Program<'info, System>,
119) -> Result<()> {
120 let curr_len = account_info.data_len();
121 let new_len = add_signed(curr_len, len);
122 let curr_rent = rent_sysvar.minimum_balance(curr_len);
123 let new_rent = rent_sysvar.minimum_balance(new_len);
124 let rent_diff: isize = isize::try_from(new_rent).unwrap() - isize::try_from(curr_rent).unwrap();
125
126 let account_info_borrow = account_info.clone();
127 if rent_diff > 0 {
128 solana_program::program::invoke(
129 &solana_program::system_instruction::transfer(
130 payer_info.key,
131 account_info_borrow.key,
132 u64::try_from(rent_diff).unwrap(),
133 ),
134 &[
135 payer_info,
136 account_info_borrow,
137 system_program.to_account_info(),
138 ],
139 )?;
140 } else if rent_diff < 0 {
141 let parsed_rent_diff = u64::try_from(rent_diff * -1).unwrap();
142
143 **payer_info.lamports.borrow_mut() = payer_info
144 .lamports()
145 .checked_add(parsed_rent_diff)
146 .ok_or(HplToolkitError::Overflow)?;
147
148 **account_info.lamports.borrow_mut() = account_info
149 .lamports()
150 .checked_sub(parsed_rent_diff)
151 .ok_or(HplToolkitError::Overflow)?;
152 } else {
153 return Ok(());
154 }
155 if len.wrapping_abs() as usize > solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE {
156 let mut updated_s = account_info.data_len().clone();
157 while updated_s < new_len {
158 updated_s += solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE;
159 if updated_s > new_len {
160 updated_s = new_len;
161 }
162 account_info.realloc(updated_s, false).unwrap()
163 }
164 while updated_s > new_len {
165 updated_s -= solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE;
166 if updated_s < new_len {
167 updated_s = new_len;
168 }
169 account_info.realloc(updated_s, false).unwrap()
170 }
171 Ok(())
172 } else {
173 account_info
174 .realloc(usize::try_from(new_len).unwrap(), false)
175 .map_err(Into::into)
176 }
177}