hpl-toolkit 0.0.5

HPL toolkit
Documentation
use crate::errors::HplToolkitError;
use anchor_lang::prelude::{
    AccountInfo, Program, Rent, Result, SolanaSysvar, System, Sysvar, ToAccountInfo,
};
use anchor_lang::{solana_program, Lamports};

mod bpf_writer;
mod inner;
mod long_vec_map;
mod short_string;
mod short_vec;
mod types;
mod vec_map;

pub use bpf_writer::*;
pub use inner::*;
pub use long_vec_map::LongVecMap;
pub use short_string::ShortString;
pub use short_vec::ShortVec;
pub use types::*;
pub use vec_map::VecMap;

#[track_caller]
#[inline(always)]
pub const fn add_signed(a: usize, b: isize) -> usize {
    match b {
        x if x < 0 => a - x.wrapping_abs() as usize,
        x => a + x as usize,
    }
}

pub fn transfer<'info>(
    lamports: u64,
    from: &AccountInfo<'info>,
    to: &AccountInfo<'info>,
    system_program: &AccountInfo<'info>,
) -> Result<()> {
    if from.owner.eq(&anchor_lang::system_program::ID) {
        solana_program::program::invoke(
            &solana_program::system_instruction::transfer(
                from.key,
                to.key,
                u64::try_from(lamports).unwrap(),
            ),
            &[
                from.to_owned(),
                to.to_owned(),
                system_program.to_account_info(),
            ],
        )?;
    } else {
        from.sub_lamports(lamports)?;
        to.add_lamports(lamports)?;
    }
    Ok(())
}

pub fn reallocate_optimized<'info>(
    len: isize,
    account_info: &AccountInfo<'info>,
    payer_info: &AccountInfo<'info>,
    system_program: &AccountInfo<'info>,
) -> Result<()> {
    let curr_len = account_info.data_len();
    let new_len = add_signed(curr_len, len);

    let rent = solana_program::sysvar::rent::Rent::get()?;
    let curr_rent = rent.minimum_balance(curr_len);
    let new_rent = rent.minimum_balance(new_len);
    let rent_diff: isize = isize::try_from(new_rent).unwrap() - isize::try_from(curr_rent).unwrap();

    if rent_diff > 0 {
        transfer(
            u64::try_from(rent_diff).unwrap(),
            payer_info,
            account_info,
            system_program,
        )?;
    } else if rent_diff < 0 {
        transfer(
            u64::try_from(rent_diff * -1).unwrap(),
            account_info,
            payer_info,
            system_program,
        )?;
    } else {
        return Ok(());
    }
    if len.wrapping_abs() as usize > solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE {
        let mut updated_s = account_info.data_len().clone();
        while updated_s < new_len {
            updated_s += solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE;
            if updated_s > new_len {
                updated_s = new_len;
            }
            account_info.realloc(updated_s, false).unwrap()
        }
        while updated_s > new_len {
            updated_s -= solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE;
            if updated_s < new_len {
                updated_s = new_len;
            }
            account_info.realloc(updated_s, false).unwrap()
        }
        Ok(())
    } else {
        account_info
            .realloc(usize::try_from(new_len).unwrap(), false)
            .map_err(Into::into)
    }
}

pub fn reallocate<'info>(
    len: isize,
    account_info: AccountInfo<'info>,
    payer_info: AccountInfo<'info>,
    rent_sysvar: &Sysvar<'info, Rent>,
    system_program: &Program<'info, System>,
) -> Result<()> {
    let curr_len = account_info.data_len();
    let new_len = add_signed(curr_len, len);
    let curr_rent = rent_sysvar.minimum_balance(curr_len);
    let new_rent = rent_sysvar.minimum_balance(new_len);
    let rent_diff: isize = isize::try_from(new_rent).unwrap() - isize::try_from(curr_rent).unwrap();

    let account_info_borrow = account_info.clone();
    if rent_diff > 0 {
        solana_program::program::invoke(
            &solana_program::system_instruction::transfer(
                payer_info.key,
                account_info_borrow.key,
                u64::try_from(rent_diff).unwrap(),
            ),
            &[
                payer_info,
                account_info_borrow,
                system_program.to_account_info(),
            ],
        )?;
    } else if rent_diff < 0 {
        let parsed_rent_diff = u64::try_from(rent_diff * -1).unwrap();

        **payer_info.lamports.borrow_mut() = payer_info
            .lamports()
            .checked_add(parsed_rent_diff)
            .ok_or(HplToolkitError::Overflow)?;

        **account_info.lamports.borrow_mut() = account_info
            .lamports()
            .checked_sub(parsed_rent_diff)
            .ok_or(HplToolkitError::Overflow)?;
    } else {
        return Ok(());
    }
    if len.wrapping_abs() as usize > solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE {
        let mut updated_s = account_info.data_len().clone();
        while updated_s < new_len {
            updated_s += solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE;
            if updated_s > new_len {
                updated_s = new_len;
            }
            account_info.realloc(updated_s, false).unwrap()
        }
        while updated_s > new_len {
            updated_s -= solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE;
            if updated_s < new_len {
                updated_s = new_len;
            }
            account_info.realloc(updated_s, false).unwrap()
        }
        Ok(())
    } else {
        account_info
            .realloc(usize::try_from(new_len).unwrap(), false)
            .map_err(Into::into)
    }
}