use crate::account_view::AccountView;
use crate::address::Address;
use crate::error::ProgramError;
use crate::ProgramResult;
#[inline]
pub fn close_and_transfer(
source: &AccountView<'_>,
destination: &AccountView<'_>,
) -> ProgramResult {
if crate::address::address_eq(source.address(), destination.address()) {
return Err(ProgramError::InvalidArgument);
}
source.require_writable()?;
destination.require_writable()?;
source.check_borrow_mut()?;
let lamports = source.lamports();
let credited = destination
.lamports()
.checked_add(lamports)
.ok_or(ProgramError::ArithmeticOverflow)?;
source.close()?;
destination.set_lamports(credited);
Ok(())
}
#[inline]
pub fn transfer_lamports(
from: &AccountView<'_>,
to: &AccountView<'_>,
amount: u64,
) -> ProgramResult {
from.require_writable()?;
to.require_writable()?;
let from_lamports = from.lamports();
if from_lamports < amount {
return Err(ProgramError::InsufficientFunds);
}
if crate::address::address_eq(from.address(), to.address()) {
return Ok(());
}
let to_lamports = to.lamports();
let new_to = to_lamports
.checked_add(amount)
.ok_or(ProgramError::ArithmeticOverflow)?;
from.set_lamports(from_lamports - amount);
to.set_lamports(new_to);
Ok(())
}
#[inline]
pub fn require_rent_exempt(account: &AccountView<'_>) -> ProgramResult {
let min = crate::sysvar::rent_exempt_minimum(account.data_len());
if account.lamports() >= min {
Ok(())
} else {
Err(ProgramError::AccountNotRentExempt)
}
}
#[inline]
pub fn require_rent_exempt_with(
rent: &crate::sysvar::Rent,
account: &AccountView<'_>,
) -> ProgramResult {
let min = rent.minimum_balance(account.data_len());
if account.lamports() >= min {
Ok(())
} else {
Err(ProgramError::AccountNotRentExempt)
}
}
#[inline]
pub fn require_same_address(a: &AccountView<'_>, b: &AccountView<'_>) -> ProgramResult {
if crate::address::address_eq(a.address(), b.address()) {
Ok(())
} else {
Err(ProgramError::InvalidArgument)
}
}
#[inline]
pub fn require_address(account: &AccountView<'_>, expected: &Address) -> ProgramResult {
if crate::address::address_eq(account.address(), expected) {
Ok(())
} else {
Err(ProgramError::InvalidArgument)
}
}
#[inline]
pub fn require_account_type(
account: &AccountView<'_>,
expected_disc: u8,
expected_owner: &Address,
) -> ProgramResult {
if account.disc() != expected_disc {
return Err(ProgramError::InvalidAccountData);
}
account.require_owned_by(expected_owner)
}
#[inline]
pub fn zero_data(account: &AccountView<'_>) -> ProgramResult {
crate::mem::zero_account_data(account)
}
#[inline]
pub fn realloc_checked(
account: &AccountView<'_>,
new_len: usize,
payer: Option<&AccountView<'_>>,
) -> ProgramResult {
account.check_resize(new_len)?;
let min = crate::sysvar::rent_exempt_minimum(new_len);
let current = account.lamports();
if current < min {
if let Some(payer) = payer {
if crate::address::address_eq(account.address(), payer.address()) {
return Err(ProgramError::InvalidArgument);
}
let deficit = min - current;
transfer_lamports(payer, account, deficit)?;
} else {
return Err(ProgramError::AccountNotRentExempt);
}
}
account.resize(new_len)
}
#[inline]
pub fn realloc_checked_with(
rent: &crate::sysvar::Rent,
account: &AccountView<'_>,
new_len: usize,
payer: Option<&AccountView<'_>>,
) -> ProgramResult {
account.check_resize(new_len)?;
let min = rent.minimum_balance(new_len);
let current = account.lamports();
if current < min {
if let Some(payer) = payer {
if crate::address::address_eq(account.address(), payer.address()) {
return Err(ProgramError::InvalidArgument);
}
let deficit = min - current;
transfer_lamports(payer, account, deficit)?;
} else {
return Err(ProgramError::AccountNotRentExempt);
}
}
account.resize(new_len)
}
#[cfg(feature = "cpi")]
pub struct ResizeWithPayer<'a, 'info> {
pub account: &'a AccountView<'info>,
pub payer: &'a AccountView<'info>,
pub system_program: &'a AccountView<'info>,
pub program_id: &'a Address,
pub new_len: usize,
}
#[cfg(feature = "cpi")]
impl ResizeWithPayer<'_, '_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
#[inline]
pub fn invoke_signed(&self, signers: &[crate::instruction::Signer<'_, '_>]) -> ProgramResult {
self.invoke_signed_with_rent(&crate::sysvar::Rent::get()?, signers)
}
#[inline]
pub fn invoke_signed_with_rent(
&self,
rent: &crate::sysvar::Rent,
signers: &[crate::instruction::Signer<'_, '_>],
) -> ProgramResult {
self.account.require_owned_by(self.program_id)?;
self.account.require_writable()?;
self.account.check_resize(self.new_len)?;
let deficit = rent
.minimum_balance(self.new_len)
.saturating_sub(self.account.lamports());
if deficit > 0 {
if crate::address::address_eq(self.account.address(), self.payer.address()) {
return Err(ProgramError::InvalidArgument);
}
require_address(self.system_program, &AccountView::SYSTEM_PROGRAM_ID)?;
if !self.system_program.executable() {
return Err(ProgramError::IncorrectProgramId);
}
self.payer
.require_owned_by(&AccountView::SYSTEM_PROGRAM_ID)?;
if !self.payer.is_data_empty() {
return Err(ProgramError::InvalidAccountData);
}
crate::system::Transfer {
from: self.payer,
to: self.account,
lamports: deficit,
}
.invoke_signed(signers)?;
}
self.account.resize(self.new_len)
}
}