hpl_toolkit/utils/
mod.rs

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}