use {
crate::{
instructions::{
account_borrow_failed_error, invalid_argument_error, write_bytes, CpiWriter,
UNINIT_BYTE, UNINIT_CPI_ACCOUNT, UNINIT_INSTRUCTION_ACCOUNT,
},
TokenInterface,
},
core::{marker::PhantomData, mem::MaybeUninit, slice::from_raw_parts},
solana_account_view::AccountView,
solana_address::Address,
solana_instruction_view::{
cpi::{invoke_unchecked, CpiAccount},
InstructionAccount, InstructionView,
},
solana_program_error::{ProgramError, ProgramResult},
};
const DISCRIMINATOR: u8 = 0;
const ACCOUNTS_LEN: usize = 2;
const MAX_DATA_LEN: usize = 67;
pub struct InitializeMint<'account, 'address, Program: TokenInterface> {
pub mint: &'account AccountView,
pub rent_sysvar: &'account AccountView,
pub decimals: u8,
pub mint_authority: &'address Address,
pub freeze_authority: Option<&'address Address>,
_program: PhantomData<Program>,
}
impl<'account, 'address, Program: TokenInterface> InitializeMint<'account, 'address, Program> {
pub const DISCRIMINATOR: u8 = DISCRIMINATOR;
pub const ACCOUNTS_LEN: usize = ACCOUNTS_LEN;
pub const MAX_DATA_LEN: usize = MAX_DATA_LEN;
#[inline(always)]
pub fn new(
mint: &'account AccountView,
rent_sysvar: &'account AccountView,
decimals: u8,
mint_authority: &'address Address,
freeze_authority: Option<&'address Address>,
) -> Self {
Self {
mint,
rent_sysvar,
decimals,
mint_authority,
freeze_authority,
_program: PhantomData,
}
}
#[inline(always)]
pub fn invoke(&self) -> ProgramResult {
self.invoke_with_unverified_program(&Program::ID)
}
#[inline(always)]
pub fn invoke_with_program(&self, program: &Address) -> ProgramResult {
Program::verify(program)?;
self.invoke_with_unverified_program(program)
}
#[inline(always)]
pub fn invoke_with_unverified_program(&self, program: &Address) -> ProgramResult {
let mut instruction_accounts = [UNINIT_INSTRUCTION_ACCOUNT; ACCOUNTS_LEN];
let written_instruction_accounts =
self.write_instruction_accounts(&mut instruction_accounts)?;
let mut accounts = [UNINIT_CPI_ACCOUNT; ACCOUNTS_LEN];
let written_accounts = self.write_accounts(&mut accounts)?;
let mut instruction_data = [UNINIT_BYTE; MAX_DATA_LEN];
let written_instruction_data = self.write_instruction_data(&mut instruction_data)?;
unsafe {
invoke_unchecked(
&InstructionView {
program_id: program,
accounts: from_raw_parts(
instruction_accounts.as_ptr() as _,
written_instruction_accounts,
),
data: from_raw_parts(instruction_data.as_ptr() as _, written_instruction_data),
},
from_raw_parts(accounts.as_ptr() as _, written_accounts),
);
}
Ok(())
}
}
impl<Program: TokenInterface> CpiWriter for InitializeMint<'_, '_, Program> {
#[inline(always)]
fn write_accounts<'cpi>(
&self,
accounts: &mut [MaybeUninit<CpiAccount<'cpi>>],
) -> Result<usize, ProgramError>
where
Self: 'cpi,
{
write_accounts(self.mint, self.rent_sysvar, accounts)
}
#[inline(always)]
fn write_instruction_accounts<'cpi>(
&self,
accounts: &mut [MaybeUninit<InstructionAccount<'cpi>>],
) -> Result<usize, ProgramError>
where
Self: 'cpi,
{
write_instruction_accounts(self.mint, self.rent_sysvar, accounts)
}
#[inline(always)]
fn write_instruction_data(&self, data: &mut [MaybeUninit<u8>]) -> Result<usize, ProgramError> {
write_instruction_data(
self.decimals,
self.mint_authority,
self.freeze_authority,
data,
)
}
}
impl<Program: TokenInterface> super::batch::IntoBatch<Program> for InitializeMint<'_, '_, Program> {
#[inline(always)]
fn into_batch<'account, 'state>(
self,
batch: &mut super::batch::Batch<'account, 'state, Program>,
) -> ProgramResult
where
Self: 'account + 'state,
{
batch.push(
|accounts| write_accounts(self.mint, self.rent_sysvar, accounts),
|accounts| write_instruction_accounts(self.mint, self.rent_sysvar, accounts),
|data| {
write_instruction_data(
self.decimals,
self.mint_authority,
self.freeze_authority,
data,
)
},
)
}
}
#[inline(always)]
fn write_accounts<'account, 'out>(
mint: &'account AccountView,
rent_sysvar: &'account AccountView,
accounts: &mut [MaybeUninit<CpiAccount<'out>>],
) -> Result<usize, ProgramError>
where
'account: 'out,
{
if accounts.len() < ACCOUNTS_LEN {
return Err(invalid_argument_error());
}
if mint.is_borrowed() {
return Err(account_borrow_failed_error());
}
CpiAccount::init_from_account_view(mint, &mut accounts[0]);
CpiAccount::init_from_account_view(rent_sysvar, &mut accounts[1]);
Ok(ACCOUNTS_LEN)
}
#[inline(always)]
fn write_instruction_accounts<'account, 'out>(
mint: &'account AccountView,
rent_sysvar: &'account AccountView,
accounts: &mut [MaybeUninit<InstructionAccount<'out>>],
) -> Result<usize, ProgramError>
where
'account: 'out,
{
if accounts.len() < ACCOUNTS_LEN {
return Err(invalid_argument_error());
}
accounts[0].write(InstructionAccount::writable(mint.address()));
accounts[1].write(InstructionAccount::readonly(rent_sysvar.address()));
Ok(ACCOUNTS_LEN)
}
#[inline(always)]
fn write_instruction_data(
decimals: u8,
mint_authority: &Address,
freeze_authority: Option<&Address>,
data: &mut [MaybeUninit<u8>],
) -> Result<usize, ProgramError> {
if data.len() < MAX_DATA_LEN {
return Err(invalid_argument_error());
}
data[0].write(DISCRIMINATOR);
data[1].write(decimals);
write_bytes(&mut data[2..34], mint_authority.as_array());
if let Some(freeze_authority) = freeze_authority {
data[34].write(1);
write_bytes(&mut data[35..MAX_DATA_LEN], freeze_authority.as_array());
Ok(MAX_DATA_LEN)
} else {
data[34].write(0);
Ok(35)
}
}