use {
crate::instructions::{write_bytes, UNINIT_BYTE},
core::{mem::MaybeUninit, slice::from_raw_parts},
pinocchio::{
cpi::{invoke_signed_with_bounds, Signer},
error::ProgramError,
instruction::{InstructionAccount, InstructionView},
sysvars::{rent::Rent, Sysvar},
AccountView, Address, ProgramResult,
},
};
pub struct Funding<'a> {
pub from: &'a AccountView,
pub lamports: u64,
}
pub struct CreateAccountAllowPrefund<'a, 'b> {
pub to: &'a AccountView,
pub space: u64,
pub owner: &'b Address,
pub funding: Option<Funding<'a>>,
}
impl<'a, 'b> CreateAccountAllowPrefund<'a, 'b> {
#[inline(always)]
pub fn with_minimum_balance(
from: &'a AccountView,
to: &'a AccountView,
space: u64,
owner: &'b Address,
rent_sysvar: Option<&'a AccountView>,
) -> Result<Self, ProgramError> {
let required_lamports = if let Some(rent_sysvar) = rent_sysvar {
Rent::from_account_view(rent_sysvar)?.try_minimum_balance(space as usize)?
} else {
Rent::get()?.try_minimum_balance(space as usize)?
};
let lamports = required_lamports.saturating_sub(to.lamports());
Ok(Self {
to,
space,
owner,
funding: if lamports == 0 {
None
} else {
Some(Funding { from, lamports })
},
})
}
#[inline(always)]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
#[inline(always)]
pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
let mut instruction_data = [UNINIT_BYTE; 52];
write_bytes(&mut instruction_data[..4], &[13, 0, 0, 0]);
write_bytes(&mut instruction_data[12..20], &self.space.to_le_bytes());
write_bytes(&mut instruction_data[20..52], self.owner.as_ref());
let mut instruction_accounts = [const { MaybeUninit::<InstructionAccount>::uninit() }; 2];
instruction_accounts[0].write(InstructionAccount::writable_signer(self.to.address()));
let mut accounts = [const { MaybeUninit::<&AccountView>::uninit() }; 2];
accounts[0].write(self.to);
let expected_accounts = if let Some(funding) = &self.funding {
write_bytes(
&mut instruction_data[4..12],
&funding.lamports.to_le_bytes(),
);
instruction_accounts[1]
.write(InstructionAccount::writable_signer(funding.from.address()));
accounts[1].write(funding.from);
2
} else {
write_bytes(&mut instruction_data[4..12], &[0; 8]);
1
};
invoke_signed_with_bounds::<2, _>(
&InstructionView {
program_id: &crate::ID,
accounts: unsafe {
from_raw_parts(instruction_accounts.as_ptr() as _, expected_accounts)
},
data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 52) },
},
unsafe { from_raw_parts(accounts.as_ptr() as *const &AccountView, expected_accounts) },
signers,
)
}
}