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_program_profiler::profile;
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 TransferToSpl {
pub source: Pubkey,
pub destination_spl_token_account: Pubkey,
pub amount: u64,
pub authority: Pubkey,
pub mint: Pubkey,
pub payer: Pubkey,
pub spl_interface_pda: Pubkey,
pub spl_interface_pda_bump: u8,
pub decimals: u8,
pub spl_token_program: Pubkey,
}
pub struct TransferToSplCpi<'info> {
pub source: AccountInfo<'info>,
pub destination_spl_token_account: AccountInfo<'info>,
pub amount: u64,
pub authority: AccountInfo<'info>,
pub mint: AccountInfo<'info>,
pub payer: AccountInfo<'info>,
pub spl_interface_pda: AccountInfo<'info>,
pub spl_interface_pda_bump: u8,
pub decimals: u8,
pub spl_token_program: AccountInfo<'info>,
pub compressed_token_program_authority: AccountInfo<'info>,
}
impl<'info> TransferToSplCpi<'info> {
pub fn instruction(&self) -> Result<Instruction, ProgramError> {
TransferToSpl::from(self).instruction()
}
pub fn invoke(self) -> Result<(), ProgramError> {
let instruction = TransferToSpl::from(&self).instruction()?;
let account_infos = [
self.compressed_token_program_authority, self.payer, self.mint, self.source, self.destination_spl_token_account, self.authority, self.spl_interface_pda, self.spl_token_program, ];
invoke(&instruction, &account_infos)
}
pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> {
let instruction = TransferToSpl::from(&self).instruction()?;
let account_infos = [
self.compressed_token_program_authority, self.payer, self.mint, self.source, self.destination_spl_token_account, self.authority, self.spl_interface_pda, self.spl_token_program, ];
invoke_signed(&instruction, &account_infos, signer_seeds)
}
}
impl<'info> From<&TransferToSplCpi<'info>> for TransferToSpl {
fn from(account_infos: &TransferToSplCpi<'info>) -> Self {
Self {
source: *account_infos.source.key,
destination_spl_token_account: *account_infos.destination_spl_token_account.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 TransferToSpl {
#[profile]
pub fn instruction(self) -> Result<Instruction, ProgramError> {
let packed_accounts = vec![
AccountMeta::new_readonly(self.mint, false),
AccountMeta::new(self.source, false),
AccountMeta::new(self.destination_spl_token_account, false),
AccountMeta::new_readonly(self.authority, true),
AccountMeta::new(self.spl_interface_pda, false),
AccountMeta::new_readonly(self.spl_token_program, false),
];
let compress_to_pool = CTokenAccount2 {
inputs: vec![],
output: MultiTokenTransferOutputData::default(),
compression: Some(Compression::compress(
self.amount,
0, 1, 3, )),
delegate_is_set: false,
method_used: true,
};
let decompress_to_spl = CTokenAccount2 {
inputs: vec![],
output: MultiTokenTransferOutputData::default(),
compression: Some(Compression::decompress_spl(
self.amount,
0, 2, 4, 0, self.spl_interface_pda_bump,
self.decimals,
)),
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![compress_to_pool, decompress_to_spl],
output_queue: 0, in_tlv: None,
};
create_transfer2_instruction(inputs).map_err(ProgramError::from)
}
}