use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID;
use solana_account_info::AccountInfo;
use solana_cpi::{invoke, invoke_signed};
use solana_instruction::{AccountMeta, Instruction};
use solana_program_error::ProgramError;
use solana_pubkey::Pubkey;
pub struct BurnChecked {
pub source: Pubkey,
pub mint: Pubkey,
pub amount: u64,
pub decimals: u8,
pub authority: Pubkey,
pub max_top_up: Option<u16>,
pub fee_payer: Option<Pubkey>,
}
pub struct BurnCheckedCpi<'info> {
pub source: AccountInfo<'info>,
pub mint: AccountInfo<'info>,
pub amount: u64,
pub decimals: u8,
pub authority: AccountInfo<'info>,
pub system_program: AccountInfo<'info>,
pub max_top_up: Option<u16>,
pub fee_payer: Option<AccountInfo<'info>>,
}
impl<'info> BurnCheckedCpi<'info> {
pub fn instruction(&self) -> Result<Instruction, ProgramError> {
BurnChecked::from(self).instruction()
}
pub fn invoke(self) -> Result<(), ProgramError> {
let instruction = BurnChecked::from(&self).instruction()?;
if let Some(fee_payer) = self.fee_payer {
let account_infos = [
self.source,
self.mint,
self.authority,
self.system_program,
fee_payer,
];
invoke(&instruction, &account_infos)
} else {
let account_infos = [self.source, self.mint, self.authority, self.system_program];
invoke(&instruction, &account_infos)
}
}
pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> {
let instruction = BurnChecked::from(&self).instruction()?;
if let Some(fee_payer) = self.fee_payer {
let account_infos = [
self.source,
self.mint,
self.authority,
self.system_program,
fee_payer,
];
invoke_signed(&instruction, &account_infos, signer_seeds)
} else {
let account_infos = [self.source, self.mint, self.authority, self.system_program];
invoke_signed(&instruction, &account_infos, signer_seeds)
}
}
}
impl<'info> From<&BurnCheckedCpi<'info>> for BurnChecked {
fn from(cpi: &BurnCheckedCpi<'info>) -> Self {
Self {
source: *cpi.source.key,
mint: *cpi.mint.key,
amount: cpi.amount,
decimals: cpi.decimals,
authority: *cpi.authority.key,
max_top_up: cpi.max_top_up,
fee_payer: cpi.fee_payer.as_ref().map(|a| *a.key),
}
}
}
impl BurnChecked {
pub fn instruction(self) -> Result<Instruction, ProgramError> {
let authority_meta = if self.max_top_up.is_some() && self.fee_payer.is_none() {
AccountMeta::new(self.authority, true)
} else {
AccountMeta::new_readonly(self.authority, true)
};
let mut accounts = vec![
AccountMeta::new(self.source, false),
AccountMeta::new(self.mint, false),
authority_meta,
AccountMeta::new_readonly(Pubkey::default(), false),
];
if let Some(fee_payer) = self.fee_payer {
accounts.push(AccountMeta::new(fee_payer, true));
}
Ok(Instruction {
program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID),
accounts,
data: {
let mut data = vec![15u8]; data.extend_from_slice(&self.amount.to_le_bytes());
data.push(self.decimals);
if let Some(max_top_up) = self.max_top_up {
data.extend_from_slice(&max_top_up.to_le_bytes());
}
data
},
})
}
}