use pinocchio::{
account_info::AccountInfo,
cpi::invoke_signed,
instruction::{AccountMeta, Instruction, Signer},
program_error::ProgramError,
pubkey::Pubkey,
sysvars::{rent::Rent, Sysvar},
ProgramResult,
};
use pinocchio_system::instructions::{Allocate, Assign, CreateAccount, Transfer};
use crate::{EMIT_EVENT_DISCRIMINATOR, MAX_CPI_INSTRUCTION_DATA_LEN};
pub struct CreateProgramAccount<'a> {
pub payer: &'a AccountInfo,
pub pda: &'a AccountInfo,
pub space: usize,
pub owner: &'a Pubkey,
}
impl CreateProgramAccount<'_> {
#[inline(always)]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
#[inline(always)]
pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
if self.pda.lamports() == 0 {
return CreateAccount {
from: self.payer,
to: self.pda,
lamports: Rent::get()?.minimum_balance(self.space).max(1),
space: self.space as u64,
owner: self.owner,
}
.invoke_signed(signers);
}
let required_lamports = Rent::get()?
.minimum_balance(self.space)
.max(1)
.saturating_sub(self.pda.lamports());
if required_lamports > 0 {
Transfer {
from: self.payer,
to: self.pda,
lamports: required_lamports,
}
.invoke_signed(signers)?;
}
Allocate {
account: self.pda,
space: self.space as u64,
}
.invoke_signed(signers)?;
Assign {
account: self.pda,
owner: self.owner,
}
.invoke_signed(signers)?;
Ok(())
}
}
pub struct ResizeProgramAccount<'a> {
pub payer: &'a AccountInfo,
pub pda: &'a AccountInfo,
pub space: usize,
pub program: &'a Pubkey,
}
impl ResizeProgramAccount<'_> {
#[inline(always)]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
#[inline(always)]
pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
if self.pda.owner() != self.program {
return Err(ProgramError::IllegalOwner);
}
let required_lamports = Rent::get()?
.minimum_balance(self.space)
.max(1)
.saturating_sub(self.pda.lamports());
if required_lamports > 0 {
Transfer {
from: self.payer,
to: self.pda,
lamports: required_lamports,
}
.invoke_signed(signers)?;
}
self.pda.resize(self.space)?;
Ok(())
}
}
pub struct CloseProgramAccount<'a> {
pub account: &'a AccountInfo,
pub destination: &'a AccountInfo,
}
impl CloseProgramAccount<'_> {
#[inline(always)]
pub fn invoke(&self) -> ProgramResult {
*self.destination.try_borrow_mut_lamports()? += *self.account.try_borrow_lamports()?;
*self.account.try_borrow_mut_lamports()? = 0;
self.account.resize(0)?;
self.account.close()
}
}
pub struct EmitEvent<'a> {
pub program_id: &'a Pubkey,
pub program: &'a AccountInfo,
pub event_authority: &'a AccountInfo,
pub data: &'a [u8],
}
impl EmitEvent<'_> {
pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
if self.data.len() > MAX_CPI_INSTRUCTION_DATA_LEN || self.data.is_empty() {
return Err(ProgramError::InvalidInstructionData);
}
let mut buf = [0; 1 + MAX_CPI_INSTRUCTION_DATA_LEN];
buf[0] = EMIT_EVENT_DISCRIMINATOR;
buf[1..1 + self.data.len()].copy_from_slice(self.data);
let instruction_data =
unsafe { core::slice::from_raw_parts(buf.as_ptr() as _, 1 + self.data.len()) };
let instruction = Instruction {
program_id: self.program_id,
accounts: &[AccountMeta::readonly_signer(self.event_authority.key())],
data: instruction_data,
};
invoke_signed(&instruction, &[self.event_authority, self.program], signers)?;
Ok(())
}
}