use light_compressed_account::instruction_data::compressed_proof::ValidityProof;
use light_compressed_token_sdk::compressed_token::{
transfer2::{
create_transfer2_instruction, Transfer2AccountsMetaConfig, Transfer2Config, Transfer2Inputs,
},
CTokenAccount2,
};
use light_token_interface::instructions::transfer2::{Compression, MultiTokenTransferOutputData};
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 TransferFromSpl {
pub amount: u64,
pub spl_interface_pda_bump: u8,
pub decimals: u8,
pub source_spl_token_account: Pubkey,
pub destination: Pubkey,
pub authority: Pubkey,
pub mint: Pubkey,
pub payer: Pubkey,
pub spl_interface_pda: Pubkey,
pub spl_token_program: Pubkey,
}
pub struct TransferFromSplCpi<'info> {
pub amount: u64,
pub spl_interface_pda_bump: u8,
pub decimals: u8,
pub source_spl_token_account: AccountInfo<'info>,
pub destination: AccountInfo<'info>,
pub authority: AccountInfo<'info>,
pub mint: AccountInfo<'info>,
pub payer: AccountInfo<'info>,
pub spl_interface_pda: AccountInfo<'info>,
pub spl_token_program: AccountInfo<'info>,
pub compressed_token_program_authority: AccountInfo<'info>,
pub system_program: AccountInfo<'info>,
}
impl<'info> TransferFromSplCpi<'info> {
pub fn instruction(&self) -> Result<Instruction, ProgramError> {
TransferFromSpl::from(self).instruction()
}
pub fn invoke(self) -> Result<(), ProgramError> {
let instruction = TransferFromSpl::from(&self).instruction()?;
let account_infos = [
self.compressed_token_program_authority, self.payer, self.mint, self.destination, self.authority, self.source_spl_token_account, self.spl_interface_pda, self.spl_token_program, self.system_program, ];
invoke(&instruction, &account_infos)
}
pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> {
let instruction = TransferFromSpl::from(&self).instruction()?;
let account_infos = [
self.compressed_token_program_authority, self.payer, self.mint, self.destination, self.authority, self.source_spl_token_account, self.spl_interface_pda, self.spl_token_program, self.system_program, ];
invoke_signed(&instruction, &account_infos, signer_seeds)
}
}
impl<'info> From<&TransferFromSplCpi<'info>> for TransferFromSpl {
fn from(account_infos: &TransferFromSplCpi<'info>) -> Self {
Self {
source_spl_token_account: *account_infos.source_spl_token_account.key,
destination: *account_infos.destination.key,
amount: account_infos.amount,
authority: *account_infos.authority.key,
mint: *account_infos.mint.key,
payer: *account_infos.payer.key,
spl_interface_pda: *account_infos.spl_interface_pda.key,
spl_interface_pda_bump: account_infos.spl_interface_pda_bump,
decimals: account_infos.decimals,
spl_token_program: *account_infos.spl_token_program.key,
}
}
}
impl TransferFromSpl {
pub fn instruction(self) -> Result<Instruction, ProgramError> {
let packed_accounts = vec![
AccountMeta::new_readonly(self.mint, false),
AccountMeta::new(self.destination, false),
AccountMeta::new_readonly(self.authority, true),
AccountMeta::new(self.source_spl_token_account, false),
AccountMeta::new(self.spl_interface_pda, false),
AccountMeta::new_readonly(self.spl_token_program, false),
AccountMeta::new_readonly(Pubkey::default(), false),
];
let wrap_from_spl = CTokenAccount2 {
inputs: vec![],
output: MultiTokenTransferOutputData::default(),
compression: Some(Compression::compress_spl(
self.amount,
0, 3, 2, 4, 0, self.spl_interface_pda_bump,
self.decimals,
)),
delegate_is_set: false,
method_used: true,
};
let unwrap_to_destination = CTokenAccount2 {
inputs: vec![],
output: MultiTokenTransferOutputData::default(),
compression: Some(Compression::decompress(self.amount, 0, 1)),
delegate_is_set: false,
method_used: true,
};
let inputs = Transfer2Inputs {
validity_proof: ValidityProof::new(None),
transfer_config: Transfer2Config::default().filter_zero_amount_outputs(),
meta_config: Transfer2AccountsMetaConfig::new_decompressed_accounts_only(
self.payer,
packed_accounts,
),
in_lamports: None,
out_lamports: None,
token_accounts: vec![wrap_from_spl, unwrap_to_destination],
output_queue: 0, in_tlv: None,
};
create_transfer2_instruction(inputs).map_err(ProgramError::from)
}
}