diff --git a/program-tests/compressed-token-test/tests/transfer2/spl_ctoken.rs b/program-tests/compressed-token-test/tests/transfer2/spl_ctoken.rs index bde10a7ec7..032a4836ad 100644 --- a/program-tests/compressed-token-test/tests/transfer2/spl_ctoken.rs +++ b/program-tests/compressed-token-test/tests/transfer2/spl_ctoken.rs @@ -189,8 +189,7 @@ async fn test_spl_to_ctoken_transfer() { #[tokio::test] async fn test_ctoken_to_spl_with_compress_and_close() { use light_compressed_token_sdk::{ - instructions::create_ctoken_to_spl_transfer_and_close_instruction, - token_pool::find_token_pool_pda_with_index, + instructions::CtokenToSplTransferAndClose, token_pool::find_token_pool_pda_with_index, }; let mut rpc = LightProgramTest::new(ProgramTestConfig::new(true, None)) @@ -277,22 +276,20 @@ async fn test_ctoken_to_spl_with_compress_and_close() { // Now transfer back using CompressAndClose instead of regular transfer println!("Testing reverse transfer with CompressAndClose: ctoken to SPL"); - // Get token pool PDA let (token_pool_pda, token_pool_pda_bump) = find_token_pool_pda_with_index(&mint, 0); - // Create instruction using compress_and_close variant - // Note: Using spl_token::ID because create_mint_helper creates Token (not Token-2022) mints - let transfer_ix = create_ctoken_to_spl_transfer_and_close_instruction( - associated_token_account, - spl_token_account_keypair.pubkey(), - transfer_amount, - recipient.pubkey(), + let transfer_ix = CtokenToSplTransferAndClose { + source_ctoken_account: associated_token_account, + destination_spl_token_account: spl_token_account_keypair.pubkey(), + amount: transfer_amount, + authority: recipient.pubkey(), mint, - payer.pubkey(), + payer: payer.pubkey(), token_pool_pda, token_pool_pda_bump, - anchor_spl::token::ID, - ) + spl_token_program: anchor_spl::token::ID, + } + .instruction() .unwrap(); // Execute transaction diff --git a/sdk-libs/compressed-token-sdk/src/instructions/mod.rs b/sdk-libs/compressed-token-sdk/src/instructions/mod.rs index b2e9c96602..d792f07df7 100644 --- a/sdk-libs/compressed-token-sdk/src/instructions/mod.rs +++ b/sdk-libs/compressed-token-sdk/src/instructions/mod.rs @@ -47,10 +47,11 @@ pub use mint_to_compressed::{ create_mint_to_compressed_instruction, get_mint_to_compressed_instruction_account_metas, DecompressedMintConfig, MintToCompressedInputs, MintToCompressedMetaConfig, }; -pub use transfer_ctoken::{transfer_ctoken, transfer_ctoken_signed}; +pub use transfer_ctoken::{TransferCtoken, TransferCtokenAccountInfos}; pub use transfer_interface::{ - create_ctoken_to_spl_transfer_and_close_instruction, create_transfer_ctoken_to_spl_instruction, - create_transfer_spl_to_ctoken_instruction, transfer_interface, transfer_interface_signed, + CtokenToSplTransferAndClose, CtokenToSplTransferAndCloseAccountInfos, SplBridgeConfig, + TransferCtokenToSpl, TransferCtokenToSplAccountInfos, TransferInterface, TransferSplToCtoken, + TransferSplToCtokenAccountInfos, }; pub use update_compressed_mint::{ update_compressed_mint, update_compressed_mint_cpi, UpdateCompressedMintInputs, diff --git a/sdk-libs/compressed-token-sdk/src/instructions/transfer_ctoken.rs b/sdk-libs/compressed-token-sdk/src/instructions/transfer_ctoken.rs index c5f1b97dac..2a92e3e084 100644 --- a/sdk-libs/compressed-token-sdk/src/instructions/transfer_ctoken.rs +++ b/sdk-libs/compressed-token-sdk/src/instructions/transfer_ctoken.rs @@ -5,64 +5,64 @@ use solana_instruction::{AccountMeta, Instruction}; use solana_program_error::ProgramError; use solana_pubkey::Pubkey; -/// Create a c-token transfer instruction. -/// -/// # Arguments -/// * `source` - Source token account -/// * `destination` - Destination token account -/// * `amount` - Amount to transfer -/// * `authority` - Authority pubkey -/// -/// # Returns -/// `Instruction` -fn create_transfer_ctoken_instruction( - source: Pubkey, - destination: Pubkey, - amount: u64, - authority: Pubkey, -) -> Instruction { - Instruction { - program_id: Pubkey::from(C_TOKEN_PROGRAM_ID), - accounts: vec![ - AccountMeta::new(source, false), - AccountMeta::new(destination, false), - AccountMeta::new_readonly(authority, true), - ], - data: { - // TODO: check why we have 2 discriminators - let mut data = vec![3u8]; - data.push(3u8); - data.extend_from_slice(&amount.to_le_bytes()); - data - }, - } +pub struct TransferCtoken { + pub source: Pubkey, + pub destination: Pubkey, + pub amount: u64, + pub authority: Pubkey, +} + +pub struct TransferCtokenAccountInfos<'info> { + pub source: AccountInfo<'info>, + pub destination: AccountInfo<'info>, + pub amount: u64, + pub authority: AccountInfo<'info>, } -/// Transfer c-tokens -pub fn transfer_ctoken<'info>( - from: &AccountInfo<'info>, - to: &AccountInfo<'info>, - authority: &AccountInfo<'info>, - amount: u64, -) -> Result<(), ProgramError> { - let ix = create_transfer_ctoken_instruction(*from.key, *to.key, amount, *authority.key); +impl<'info> TransferCtokenAccountInfos<'info> { + pub fn instruction(&self) -> Result { + TransferCtoken::from(self).instruction() + } + + pub fn invoke(self) -> Result<(), ProgramError> { + let instruction = TransferCtoken::from(&self).instruction()?; + let account_infos = [self.source, self.destination, self.authority]; + invoke(&instruction, &account_infos) + } - invoke(&ix, &[from.clone(), to.clone(), authority.clone()]) + pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { + let instruction = TransferCtoken::from(&self).instruction()?; + let account_infos = [self.source, self.destination, self.authority]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } } -/// Transfer c-tokens CPI -pub fn transfer_ctoken_signed<'info>( - from: &AccountInfo<'info>, - to: &AccountInfo<'info>, - authority: &AccountInfo<'info>, - amount: u64, - signer_seeds: &[&[&[u8]]], -) -> Result<(), ProgramError> { - let ix = create_transfer_ctoken_instruction(*from.key, *to.key, amount, *authority.key); +impl<'info> From<&TransferCtokenAccountInfos<'info>> for TransferCtoken { + fn from(account_infos: &TransferCtokenAccountInfos<'info>) -> Self { + Self { + source: *account_infos.source.key, + destination: *account_infos.destination.key, + amount: account_infos.amount, + authority: *account_infos.authority.key, + } + } +} - invoke_signed( - &ix, - &[from.clone(), to.clone(), authority.clone()], - signer_seeds, - ) +impl TransferCtoken { + pub fn instruction(self) -> Result { + Ok(Instruction { + program_id: Pubkey::from(C_TOKEN_PROGRAM_ID), + accounts: vec![ + AccountMeta::new(self.source, false), + AccountMeta::new(self.destination, false), + AccountMeta::new_readonly(self.authority, true), + ], + data: { + let mut data = vec![3u8]; + data.push(3u8); + data.extend_from_slice(&self.amount.to_le_bytes()); + data + }, + }) + } } diff --git a/sdk-libs/compressed-token-sdk/src/instructions/transfer_interface.rs b/sdk-libs/compressed-token-sdk/src/instructions/transfer_interface.rs index 1a0cbe2175..afdb76f8b2 100644 --- a/sdk-libs/compressed-token-sdk/src/instructions/transfer_interface.rs +++ b/sdk-libs/compressed-token-sdk/src/instructions/transfer_interface.rs @@ -7,7 +7,7 @@ use solana_instruction::{AccountMeta, Instruction}; use solana_program_error::ProgramError; use solana_pubkey::Pubkey; -use super::transfer_ctoken::{transfer_ctoken, transfer_ctoken_signed}; +use super::transfer_ctoken::TransferCtokenAccountInfos; use crate::{ account2::CTokenAccount2, error::TokenSdkError, @@ -18,598 +18,640 @@ use crate::{ utils::is_ctoken_account, }; -#[allow(clippy::too_many_arguments)] -#[profile] -pub fn create_transfer_spl_to_ctoken_instruction( - source_spl_token_account: Pubkey, - to: Pubkey, - amount: u64, - authority: Pubkey, - mint: Pubkey, - payer: Pubkey, - token_pool_pda: Pubkey, - token_pool_pda_bump: u8, - spl_token_program: Pubkey, -) -> Result { - let packed_accounts = vec![ - // Mint (index 0) - AccountMeta::new_readonly(mint, false), - // Destination token account (index 1) - AccountMeta::new(to, false), - // Authority for compression (index 2) - signer - AccountMeta::new_readonly(authority, true), - // Source SPL token account (index 3) - writable - AccountMeta::new(source_spl_token_account, false), - // Token pool PDA (index 4) - writable - AccountMeta::new(token_pool_pda, false), - // SPL Token program (index 5) - needed for CPI - AccountMeta::new_readonly(spl_token_program, false), - ]; - - let wrap_spl_to_ctoken_account = CTokenAccount2 { - inputs: vec![], - output: MultiTokenTransferOutputData::default(), - compression: Some(Compression::compress_spl( - amount, - 0, // mint - 3, // source or recpient - 2, // authority - 4, // pool_account_index: - 0, // pool_index - token_pool_pda_bump, - )), - delegate_is_set: false, - method_used: true, - }; - - let ctoken_account = CTokenAccount2 { - inputs: vec![], - output: MultiTokenTransferOutputData::default(), - compression: Some(Compression::decompress_ctoken(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( - payer, - packed_accounts, - ), - in_lamports: None, - out_lamports: None, - token_accounts: vec![wrap_spl_to_ctoken_account, ctoken_account], - output_queue: 0, // Decompressed accounts only, no output queue needed - }; - - create_transfer2_instruction(inputs) +pub struct TransferSplToCtoken { + pub amount: u64, + pub token_pool_pda_bump: u8, + pub source_spl_token_account: Pubkey, + pub to: Pubkey, + pub authority: Pubkey, + pub mint: Pubkey, + pub payer: Pubkey, + pub token_pool_pda: Pubkey, + pub spl_token_program: Pubkey, } -#[allow(clippy::too_many_arguments)] -#[profile] -pub fn create_transfer_ctoken_to_spl_instruction( - source_ctoken_account: Pubkey, - destination_spl_token_account: Pubkey, - amount: u64, - authority: Pubkey, - mint: Pubkey, - payer: Pubkey, - token_pool_pda: Pubkey, - token_pool_pda_bump: u8, - spl_token_program: Pubkey, -) -> Result { - let packed_accounts = vec![ - // Mint (index 0) - AccountMeta::new_readonly(mint, false), - // Source ctoken account (index 1) - writable - AccountMeta::new(source_ctoken_account, false), - // Destination SPL token account (index 2) - writable - AccountMeta::new(destination_spl_token_account, false), - // Authority (index 3) - signer - AccountMeta::new_readonly(authority, true), - // Token pool PDA (index 4) - writable - AccountMeta::new(token_pool_pda, false), - // SPL Token program (index 5) - needed for CPI - AccountMeta::new_readonly(spl_token_program, false), - ]; - - // First operation: compress from ctoken account to pool using compress_spl - let compress_to_pool = CTokenAccount2 { - inputs: vec![], - output: MultiTokenTransferOutputData::default(), - compression: Some(Compression::compress_ctoken( - amount, 0, // mint index - 1, // source ctoken account index - 3, // authority index - )), - delegate_is_set: false, - method_used: true, - }; - - // Second operation: decompress from pool to SPL token account using decompress_spl - let decompress_to_spl = CTokenAccount2 { - inputs: vec![], - output: MultiTokenTransferOutputData::default(), - compression: Some(Compression::decompress_spl( - amount, - 0, // mint index - 2, // destination SPL token account index - 4, // pool_account_index - 0, // pool_index (TODO: make dynamic) - token_pool_pda_bump, - )), - 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( - payer, - packed_accounts, - ), - in_lamports: None, - out_lamports: None, - token_accounts: vec![compress_to_pool, decompress_to_spl], - output_queue: 0, // Decompressed accounts only, no output queue needed - }; - - create_transfer2_instruction(inputs) +pub struct TransferSplToCtokenAccountInfos<'info> { + pub source_spl_token_account: AccountInfo<'info>, + pub to: AccountInfo<'info>, + pub amount: u64, + pub authority: AccountInfo<'info>, + pub mint: AccountInfo<'info>, + pub payer: AccountInfo<'info>, + pub token_pool_pda: AccountInfo<'info>, + pub token_pool_pda_bump: u8, + pub spl_token_program: AccountInfo<'info>, + pub compressed_token_program_authority: AccountInfo<'info>, + pub destination_ctoken_account: AccountInfo<'info>, } -#[allow(clippy::too_many_arguments)] -#[profile] -pub fn create_ctoken_to_spl_transfer_and_close_instruction( - source_ctoken_account: Pubkey, - destination_spl_token_account: Pubkey, - amount: u64, - authority: Pubkey, - mint: Pubkey, - payer: Pubkey, - token_pool_pda: Pubkey, - token_pool_pda_bump: u8, - spl_token_program: Pubkey, -) -> Result { - let packed_accounts = vec![ - // Mint (index 0) - AccountMeta::new_readonly(mint, false), - // Source ctoken account (index 1) - writable - AccountMeta::new(source_ctoken_account, false), - // Destination SPL token account (index 2) - writable - AccountMeta::new(destination_spl_token_account, false), - // Authority (index 3) - signer - AccountMeta::new(authority, true), - // Token pool PDA (index 4) - writable - AccountMeta::new(token_pool_pda, false), - // SPL Token program (index 5) - needed for CPI - AccountMeta::new_readonly(spl_token_program, false), - ]; - - // First operation: compress from ctoken account to pool using compress_and_close - let compress_to_pool = CTokenAccount2 { - inputs: vec![], - output: MultiTokenTransferOutputData::default(), - compression: Some(Compression::compress_and_close_ctoken( - amount, 0, // mint index - 1, // source ctoken account index - 3, // authority index - 0, // no rent sponsor - 0, // no compressed account - 3, // destination is authority - )), - delegate_is_set: false, - method_used: true, - }; - - // Second operation: decompress from pool to SPL token account using decompress_spl - let decompress_to_spl = CTokenAccount2 { - inputs: vec![], - output: MultiTokenTransferOutputData::default(), - compression: Some(Compression::decompress_spl( - amount, - 0, // mint index - 2, // destination SPL token account index - 4, // pool_account_index - 0, // pool_index (TODO: make dynamic) - token_pool_pda_bump, - )), - 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( - payer, - packed_accounts, - ), - in_lamports: None, - out_lamports: None, - token_accounts: vec![compress_to_pool, decompress_to_spl], - output_queue: 0, // Decompressed accounts only, no output queue needed - }; - - create_transfer2_instruction(inputs) +impl<'info> TransferSplToCtokenAccountInfos<'info> { + pub fn instruction(&self) -> Result { + TransferSplToCtoken::from(self).instruction() + } + + pub fn invoke(self) -> Result<(), ProgramError> { + let instruction = TransferSplToCtoken::from(&self).instruction()?; + let account_infos = [ + self.payer, + self.compressed_token_program_authority, + self.mint, // Index 0: Mint + self.destination_ctoken_account, // Index 1: Destination owner + self.authority, // Index 2: Authority (signer) + self.source_spl_token_account, // Index 3: Source SPL token account + self.token_pool_pda, // Index 4: Token pool PDA + self.spl_token_program, // Index 5: SPL Token program + ]; + invoke(&instruction, &account_infos) + } + + pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { + let instruction = TransferSplToCtoken::from(&self).instruction()?; + let account_infos = [ + self.payer, + self.compressed_token_program_authority, + self.mint, // Index 0: Mint + self.destination_ctoken_account, // Index 1: Destination owner + self.authority, // Index 2: Authority (signer) + self.source_spl_token_account, // Index 3: Source SPL token account + self.token_pool_pda, // Index 4: Token pool PDA + self.spl_token_program, // Index 5: SPL Token program + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } +} + +impl<'info> From<&TransferSplToCtokenAccountInfos<'info>> for TransferSplToCtoken { + fn from(account_infos: &TransferSplToCtokenAccountInfos<'info>) -> Self { + Self { + source_spl_token_account: *account_infos.source_spl_token_account.key, + to: *account_infos.to.key, + amount: account_infos.amount, + authority: *account_infos.authority.key, + mint: *account_infos.mint.key, + payer: *account_infos.payer.key, + token_pool_pda: *account_infos.token_pool_pda.key, + token_pool_pda_bump: account_infos.token_pool_pda_bump, + spl_token_program: *account_infos.spl_token_program.key, + } + } } -/// Transfer SPL tokens to compressed tokens -#[allow(clippy::too_many_arguments)] -pub fn transfer_spl_to_ctoken<'info>( - payer: AccountInfo<'info>, - authority: AccountInfo<'info>, - source_spl_token_account: AccountInfo<'info>, - destination_ctoken_account: AccountInfo<'info>, - mint: AccountInfo<'info>, - spl_token_program: AccountInfo<'info>, - compressed_token_pool_pda: AccountInfo<'info>, - compressed_token_pool_pda_bump: u8, - compressed_token_program_authority: AccountInfo<'info>, - amount: u64, -) -> Result<(), ProgramError> { - let instruction = create_transfer_spl_to_ctoken_instruction( - *source_spl_token_account.key, - *destination_ctoken_account.key, - amount, - *authority.key, - *mint.key, - *payer.key, - *compressed_token_pool_pda.key, - compressed_token_pool_pda_bump, - *spl_token_program.key, - ) - .map_err(|_| ProgramError::InvalidInstructionData)?; - - // let mut account_infos = remaining_accounts.to_vec(); - let account_infos = vec![ - payer, - compressed_token_program_authority, - mint, // Index 0: Mint - destination_ctoken_account, // Index 1: Destination owner - authority, // Index 2: Authority (signer) - source_spl_token_account, // Index 3: Source SPL token account - compressed_token_pool_pda, // Index 4: Token pool PDA - spl_token_program, // Index 5: SPL Token program - ]; - - invoke(&instruction, &account_infos)?; - Ok(()) +impl TransferSplToCtoken { + pub fn instruction(self) -> Result { + let packed_accounts = vec![ + // Mint (index 0) + AccountMeta::new_readonly(self.mint, false), + // Destination token account (index 1) + AccountMeta::new(self.to, false), + // Authority for compression (index 2) - signer + AccountMeta::new_readonly(self.authority, true), + // Source SPL token account (index 3) - writable + AccountMeta::new(self.source_spl_token_account, false), + // Token pool PDA (index 4) - writable + AccountMeta::new(self.token_pool_pda, false), + // SPL Token program (index 5) - needed for CPI + AccountMeta::new_readonly(self.spl_token_program, false), + ]; + + let wrap_spl_to_ctoken_account = CTokenAccount2 { + inputs: vec![], + output: MultiTokenTransferOutputData::default(), + compression: Some(Compression::compress_spl( + self.amount, + 0, // mint + 3, // source or recipient + 2, // authority + 4, // pool_account_index: + 0, // pool_index + self.token_pool_pda_bump, + )), + delegate_is_set: false, + method_used: true, + }; + + let ctoken_account = CTokenAccount2 { + inputs: vec![], + output: MultiTokenTransferOutputData::default(), + compression: Some(Compression::decompress_ctoken(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_spl_to_ctoken_account, ctoken_account], + output_queue: 0, // Decompressed accounts only, no output queue needed + }; + + create_transfer2_instruction(inputs).map_err(ProgramError::from) + } } -// TODO: must test this. -/// Transfer SPL tokens to compressed tokens via CPI signer -#[allow(clippy::too_many_arguments)] -pub fn transfer_spl_to_ctoken_signed<'info>( - payer: AccountInfo<'info>, - authority: AccountInfo<'info>, - source_spl_token_account: AccountInfo<'info>, - destination_ctoken_account: AccountInfo<'info>, - mint: AccountInfo<'info>, - spl_token_program: AccountInfo<'info>, - compressed_token_pool_pda: AccountInfo<'info>, - compressed_token_pool_pda_bump: u8, - compressed_token_program_authority: AccountInfo<'info>, - amount: u64, - signer_seeds: &[&[&[u8]]], -) -> Result<(), ProgramError> { - let instruction = create_transfer_spl_to_ctoken_instruction( - *source_spl_token_account.key, - *destination_ctoken_account.key, - amount, - *authority.key, - *mint.key, - *payer.key, - *compressed_token_pool_pda.key, - compressed_token_pool_pda_bump, - *spl_token_program.key, - ) - .map_err(|_| ProgramError::InvalidInstructionData)?; - - let account_infos = vec![ - payer, - compressed_token_program_authority, - mint, // Index 0: Mint - destination_ctoken_account, // Index 1: Destination owner - authority, // Index 2: Authority (signer) - source_spl_token_account, // Index 3: Source SPL token account - compressed_token_pool_pda, // Index 4: Token pool PDA - spl_token_program, // Index 5: SPL Token program - ]; - - invoke_signed(&instruction, &account_infos, signer_seeds)?; - Ok(()) +pub struct TransferCtokenToSpl { + pub source_ctoken_account: Pubkey, + pub destination_spl_token_account: Pubkey, + pub amount: u64, + pub authority: Pubkey, + pub mint: Pubkey, + pub payer: Pubkey, + pub token_pool_pda: Pubkey, + pub token_pool_pda_bump: u8, + pub spl_token_program: Pubkey, } -// TODO: TEST. -/// Transfer compressed tokens to SPL tokens -#[allow(clippy::too_many_arguments)] -pub fn transfer_ctoken_to_spl<'info>( - payer: AccountInfo<'info>, - authority: AccountInfo<'info>, - source_ctoken_account: AccountInfo<'info>, - destination_spl_token_account: AccountInfo<'info>, - mint: AccountInfo<'info>, - spl_token_program: AccountInfo<'info>, - compressed_token_pool_pda: AccountInfo<'info>, - compressed_token_pool_pda_bump: u8, - compressed_token_program_authority: AccountInfo<'info>, - amount: u64, -) -> Result<(), ProgramError> { - let instruction = create_transfer_ctoken_to_spl_instruction( - *source_ctoken_account.key, - *destination_spl_token_account.key, - amount, - *authority.key, - *mint.key, - *payer.key, - *compressed_token_pool_pda.key, - compressed_token_pool_pda_bump, - *spl_token_program.key, - ) - .map_err(|_| ProgramError::InvalidInstructionData)?; - - let account_infos = vec![ - payer, - compressed_token_program_authority, - mint, // Index 0: Mint - destination_spl_token_account, // Index 1: Destination owner - authority, // Index 2: Authority (signer) - source_ctoken_account, // Index 3: Source SPL token account - compressed_token_pool_pda, // Index 4: Token pool PDA - spl_token_program, // Index 5: SPL Token program - ]; - - invoke(&instruction, &account_infos)?; - Ok(()) +pub struct TransferCtokenToSplAccountInfos<'info> { + pub source_ctoken_account: 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 token_pool_pda: AccountInfo<'info>, + pub token_pool_pda_bump: u8, + pub spl_token_program: AccountInfo<'info>, + pub compressed_token_program_authority: AccountInfo<'info>, } -/// Transfer compressed tokens to SPL tokens via CPI signer -#[allow(clippy::too_many_arguments)] -pub fn transfer_ctoken_to_spl_signed<'info>( - payer: AccountInfo<'info>, - authority: AccountInfo<'info>, - source_ctoken_account: AccountInfo<'info>, - destination_spl_token_account: AccountInfo<'info>, - mint: AccountInfo<'info>, - spl_token_program: AccountInfo<'info>, - compressed_token_pool_pda: AccountInfo<'info>, - compressed_token_pool_pda_bump: u8, - compressed_token_program_authority: AccountInfo<'info>, - amount: u64, - signer_seeds: &[&[&[u8]]], -) -> Result<(), ProgramError> { - let instruction = create_transfer_ctoken_to_spl_instruction( - *source_ctoken_account.key, - *destination_spl_token_account.key, - amount, - *authority.key, - *mint.key, - *payer.key, - *compressed_token_pool_pda.key, - compressed_token_pool_pda_bump, - *spl_token_program.key, - ) - .map_err(|_| ProgramError::InvalidInstructionData)?; - - let account_infos = vec![ - payer, - compressed_token_program_authority, - mint, // Index 0: Mint - destination_spl_token_account, // Index 1: Destination owner - authority, // Index 2: Authority (signer) - source_ctoken_account, // Index 3: Source SPL token account - compressed_token_pool_pda, // Index 4: Token pool PDA - spl_token_program, // Index 5: SPL Token program - ]; - - invoke_signed(&instruction, &account_infos, signer_seeds)?; - Ok(()) +impl<'info> TransferCtokenToSplAccountInfos<'info> { + pub fn instruction(&self) -> Result { + TransferCtokenToSpl::from(self).instruction() + } + + pub fn invoke(self) -> Result<(), ProgramError> { + let instruction = TransferCtokenToSpl::from(&self).instruction()?; + let account_infos = [ + self.payer, + self.compressed_token_program_authority, + self.mint, // Index 0: Mint + self.source_ctoken_account, // Index 1: Source ctoken account + self.authority, // Index 3: Authority (signer) + self.destination_spl_token_account, // Index 2: Destination SPL token account + self.token_pool_pda, // Index 4: Token pool PDA + self.spl_token_program, // Index 5: SPL Token program + ]; + invoke(&instruction, &account_infos) + } + + pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { + let instruction = TransferCtokenToSpl::from(&self).instruction()?; + let account_infos = [ + self.payer, + self.compressed_token_program_authority, + self.mint, // Index 0: Mint + self.source_ctoken_account, // Index 1: Source ctoken account + self.authority, // Index 3: Authority (signer) + self.destination_spl_token_account, // Index 2: Destination SPL token account + self.token_pool_pda, // Index 4: Token pool PDA + self.spl_token_program, // Index 5: SPL Token program + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } } -/// Unified transfer interface for ctoken<->ctoken and ctoken<->spl transfers -/// -/// # Arguments -/// * `source_account` - Source token account (can be ctoken or SPL) -/// * `destination_account` - Destination token account (can be ctoken or SPL) -/// * `authority` - Authority for the transfer (must be signer) -/// * `amount` - Amount to transfer -/// * `payer` - Payer for the transaction -/// * `compressed_token_program_authority` - Compressed token program authority -/// * `mint` - Optional mint account (required for SPL<->ctoken transfers) -/// * `spl_token_program` - Optional SPL token program (required for SPL<->ctoken transfers) -/// * `compressed_token_pool_pda` - Optional token pool PDA (required for SPL<->ctoken transfers) -/// * `compressed_token_pool_pda_bump` - Optional bump seed for token pool PDA -/// -/// # Errors -/// * `SplBridgeConfigRequired` - If transferring to/from SPL without required accounts -/// * `UseRegularSplTransfer` - If both source and destination are SPL accounts -/// * `CannotDetermineAccountType` - If account type cannot be determined -#[allow(clippy::too_many_arguments)] -pub fn transfer_interface<'info>( - source_account: &AccountInfo<'info>, - destination_account: &AccountInfo<'info>, - authority: &AccountInfo<'info>, - amount: u64, - payer: &AccountInfo<'info>, - compressed_token_program_authority: &AccountInfo<'info>, - mint: Option<&AccountInfo<'info>>, - spl_token_program: Option<&AccountInfo<'info>>, - compressed_token_pool_pda: Option<&AccountInfo<'info>>, - compressed_token_pool_pda_bump: Option, -) -> Result<(), ProgramError> { - let source_is_ctoken = - is_ctoken_account(source_account).map_err(|_| ProgramError::InvalidAccountData)?; - let dest_is_ctoken = - is_ctoken_account(destination_account).map_err(|_| ProgramError::InvalidAccountData)?; - - match (source_is_ctoken, dest_is_ctoken) { - (true, true) => transfer_ctoken(source_account, destination_account, authority, amount), - - (true, false) => { - let (mint_acct, spl_program, pool_pda, bump) = match ( - mint, - spl_token_program, - compressed_token_pool_pda, - compressed_token_pool_pda_bump, - ) { - (Some(m), Some(p), Some(pd), Some(b)) => (m, p, pd, b), - _ => { - return Err(ProgramError::Custom( - TokenSdkError::IncompleteSplBridgeConfig.into(), - )) - } - }; - - transfer_ctoken_to_spl( - payer.clone(), - authority.clone(), - source_account.clone(), - destination_account.clone(), - mint_acct.clone(), - spl_program.clone(), - pool_pda.clone(), - bump, - compressed_token_program_authority.clone(), - amount, - ) +impl<'info> From<&TransferCtokenToSplAccountInfos<'info>> for TransferCtokenToSpl { + fn from(account_infos: &TransferCtokenToSplAccountInfos<'info>) -> Self { + Self { + source_ctoken_account: *account_infos.source_ctoken_account.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, + token_pool_pda: *account_infos.token_pool_pda.key, + token_pool_pda_bump: account_infos.token_pool_pda_bump, + spl_token_program: *account_infos.spl_token_program.key, } + } +} - (false, true) => { - let (mint_acct, spl_program, pool_pda, bump) = match ( - mint, - spl_token_program, - compressed_token_pool_pda, - compressed_token_pool_pda_bump, - ) { - (Some(m), Some(p), Some(pd), Some(b)) => (m, p, pd, b), - _ => { - return Err(ProgramError::Custom( - TokenSdkError::IncompleteSplBridgeConfig.into(), - )) - } - }; - - transfer_spl_to_ctoken( - payer.clone(), - authority.clone(), - source_account.clone(), - destination_account.clone(), - mint_acct.clone(), - spl_program.clone(), - pool_pda.clone(), - bump, - compressed_token_program_authority.clone(), - amount, - ) +impl TransferCtokenToSpl { + #[profile] + pub fn instruction(self) -> Result { + let packed_accounts = vec![ + // Mint (index 0) + AccountMeta::new_readonly(self.mint, false), + // Source ctoken account (index 1) - writable + AccountMeta::new(self.source_ctoken_account, false), + // Destination SPL token account (index 2) - writable + AccountMeta::new(self.destination_spl_token_account, false), + // Authority (index 3) - signer + AccountMeta::new_readonly(self.authority, true), + // Token pool PDA (index 4) - writable + AccountMeta::new(self.token_pool_pda, false), + // SPL Token program (index 5) - needed for CPI + AccountMeta::new_readonly(self.spl_token_program, false), + ]; + + // First operation: compress from ctoken account to pool using compress_spl + let compress_to_pool = CTokenAccount2 { + inputs: vec![], + output: MultiTokenTransferOutputData::default(), + compression: Some(Compression::compress_ctoken( + self.amount, + 0, // mint index + 1, // source ctoken account index + 3, // authority index + )), + delegate_is_set: false, + method_used: true, + }; + + // Second operation: decompress from pool to SPL token account using decompress_spl + let decompress_to_spl = CTokenAccount2 { + inputs: vec![], + output: MultiTokenTransferOutputData::default(), + compression: Some(Compression::decompress_spl( + self.amount, + 0, // mint index + 2, // destination SPL token account index + 4, // pool_account_index + 0, // pool_index (TODO: make dynamic) + self.token_pool_pda_bump, + )), + 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, // Decompressed accounts only, no output queue needed + }; + + create_transfer2_instruction(inputs).map_err(ProgramError::from) + } +} + +pub struct CtokenToSplTransferAndClose { + pub source_ctoken_account: Pubkey, + pub destination_spl_token_account: Pubkey, + pub amount: u64, + pub authority: Pubkey, + pub mint: Pubkey, + pub payer: Pubkey, + pub token_pool_pda: Pubkey, + pub token_pool_pda_bump: u8, + pub spl_token_program: Pubkey, +} + +pub struct CtokenToSplTransferAndCloseAccountInfos<'info> { + pub source_ctoken_account: 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 token_pool_pda: AccountInfo<'info>, + pub token_pool_pda_bump: u8, + pub spl_token_program: AccountInfo<'info>, + pub compressed_token_program_authority: AccountInfo<'info>, +} + +impl<'info> CtokenToSplTransferAndCloseAccountInfos<'info> { + pub fn instruction(&self) -> Result { + CtokenToSplTransferAndClose::from(self).instruction() + } + + pub fn invoke(self) -> Result<(), ProgramError> { + let instruction = CtokenToSplTransferAndClose::from(&self).instruction()?; + let account_infos = [ + self.payer, + self.compressed_token_program_authority, + self.mint, // Index 0: Mint + self.source_ctoken_account, // Index 1: Source ctoken account + self.authority, // Index 3: Authority (signer) + self.destination_spl_token_account, // Index 2: Destination SPL token account + self.token_pool_pda, // Index 4: Token pool PDA + self.spl_token_program, // Index 5: SPL Token program + ]; + invoke(&instruction, &account_infos) + } + + pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { + let instruction = CtokenToSplTransferAndClose::from(&self).instruction()?; + let account_infos = [ + self.payer, + self.compressed_token_program_authority, + self.mint, // Index 0: Mint + self.source_ctoken_account, // Index 1: Source ctoken account + self.authority, // Index 3: Authority (signer) + self.destination_spl_token_account, // Index 2: Destination SPL token account + self.token_pool_pda, // Index 4: Token pool PDA + self.spl_token_program, // Index 5: SPL Token program + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } +} + +impl<'info> From<&CtokenToSplTransferAndCloseAccountInfos<'info>> for CtokenToSplTransferAndClose { + fn from(account_infos: &CtokenToSplTransferAndCloseAccountInfos<'info>) -> Self { + Self { + source_ctoken_account: *account_infos.source_ctoken_account.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, + token_pool_pda: *account_infos.token_pool_pda.key, + token_pool_pda_bump: account_infos.token_pool_pda_bump, + spl_token_program: *account_infos.spl_token_program.key, } + } +} - // spl -> spl: Not supported - (false, false) => Err(ProgramError::Custom( - TokenSdkError::UseRegularSplTransfer.into(), - )), +impl CtokenToSplTransferAndClose { + #[profile] + pub fn instruction(self) -> Result { + let packed_accounts = vec![ + // Mint (index 0) + AccountMeta::new_readonly(self.mint, false), + // Source ctoken account (index 1) - writable + AccountMeta::new(self.source_ctoken_account, false), + // Destination SPL token account (index 2) - writable + AccountMeta::new(self.destination_spl_token_account, false), + // Authority (index 3) - signer + AccountMeta::new(self.authority, true), + // Token pool PDA (index 4) - writable + AccountMeta::new(self.token_pool_pda, false), + // SPL Token program (index 5) - needed for CPI + AccountMeta::new_readonly(self.spl_token_program, false), + ]; + + // First operation: compress from ctoken account to pool using compress_and_close + let compress_to_pool = CTokenAccount2 { + inputs: vec![], + output: MultiTokenTransferOutputData::default(), + compression: Some(Compression::compress_and_close_ctoken( + self.amount, + 0, // mint index + 1, // source ctoken account index + 3, // authority index + 0, // no rent sponsor + 0, // no compressed account + 3, // destination is authority + )), + delegate_is_set: false, + method_used: true, + }; + + // Second operation: decompress from pool to SPL token account using decompress_spl + let decompress_to_spl = CTokenAccount2 { + inputs: vec![], + output: MultiTokenTransferOutputData::default(), + compression: Some(Compression::decompress_spl( + self.amount, + 0, // mint index + 2, // destination SPL token account index + 4, // pool_account_index + 0, // pool_index (TODO: make dynamic) + self.token_pool_pda_bump, + )), + 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, // Decompressed accounts only, no output queue needed + }; + + create_transfer2_instruction(inputs).map_err(ProgramError::from) } } -/// Unified transfer interface with CPI -#[allow(clippy::too_many_arguments)] -pub fn transfer_interface_signed<'info>( - source_account: &AccountInfo<'info>, - destination_account: &AccountInfo<'info>, - authority: &AccountInfo<'info>, - amount: u64, - payer: &AccountInfo<'info>, - compressed_token_program_authority: &AccountInfo<'info>, - mint: Option<&AccountInfo<'info>>, - spl_token_program: Option<&AccountInfo<'info>>, - compressed_token_pool_pda: Option<&AccountInfo<'info>>, - compressed_token_pool_pda_bump: Option, - signer_seeds: &[&[&[u8]]], -) -> Result<(), ProgramError> { - // Determine account types - let source_is_ctoken = - is_ctoken_account(source_account).map_err(|_| ProgramError::InvalidAccountData)?; - let dest_is_ctoken = - is_ctoken_account(destination_account).map_err(|_| ProgramError::InvalidAccountData)?; - - match (source_is_ctoken, dest_is_ctoken) { - // ctoken -> ctoken: Direct transfer (bridge accounts not needed) - (true, true) => transfer_ctoken_signed( +pub struct SplBridgeConfig<'info> { + pub mint: AccountInfo<'info>, + pub spl_token_program: AccountInfo<'info>, + pub token_pool_pda: AccountInfo<'info>, + pub token_pool_pda_bump: u8, +} + +pub struct TransferInterface<'info> { + pub source_account: AccountInfo<'info>, + pub destination_account: AccountInfo<'info>, + pub authority: AccountInfo<'info>, + pub payer: AccountInfo<'info>, + pub compressed_token_program_authority: AccountInfo<'info>, + pub amount: u64, + pub spl_bridge_config: Option>, +} + +impl<'info> TransferInterface<'info> { + /// # Arguments + /// * `source_account` - Source token account (can be ctoken or SPL) + /// * `destination_account` - Destination token account (can be ctoken or SPL) + /// * `authority` - Authority for the transfer (must be signer) + /// * `amount` - Amount to transfer + /// * `payer` - Payer for the transaction + /// * `compressed_token_program_authority` - Compressed token program authority + /// * `mint` - Optional mint account (required for SPL<->ctoken transfers) + /// * `spl_token_program` - Optional SPL token program (required for SPL<->ctoken transfers) + /// * `compressed_token_pool_pda` - Optional token pool PDA (required for SPL<->ctoken transfers) + /// * `compressed_token_pool_pda_bump` - Optional bump seed for token pool PDA + pub fn new( + source_account: AccountInfo<'info>, + destination_account: AccountInfo<'info>, + authority: AccountInfo<'info>, + amount: u64, + payer: AccountInfo<'info>, + compressed_token_program_authority: AccountInfo<'info>, + ) -> Self { + Self { source_account, destination_account, authority, amount, - signer_seeds, - ), - - // ctoken -> spl: Requires bridge accounts - (true, false) => { - // Validate all required accounts are provided - let (mint_acct, spl_program, pool_pda, bump) = match ( - mint, - spl_token_program, - compressed_token_pool_pda, - compressed_token_pool_pda_bump, - ) { - (Some(m), Some(p), Some(pd), Some(b)) => (m, p, pd, b), - _ => { - return Err(ProgramError::Custom( - TokenSdkError::IncompleteSplBridgeConfig.into(), - )) - } - }; - - transfer_ctoken_to_spl_signed( - payer.clone(), - authority.clone(), - source_account.clone(), - destination_account.clone(), - mint_acct.clone(), - spl_program.clone(), - pool_pda.clone(), - bump, - compressed_token_program_authority.clone(), - amount, - signer_seeds, - ) + payer, + compressed_token_program_authority, + spl_bridge_config: None, } + } + + /// # Arguments + /// * `mint` - mint account (required for SPL<->ctoken transfers) + /// * `spl_token_program` - SPL token program (required for SPL<->ctoken transfers) + /// * `compressed_token_pool_pda` - token pool PDA (required for SPL<->ctoken transfers) + /// * `compressed_token_pool_pda_bump` - bump seed for token pool PDA + pub fn with_spl_bridge( + mut self, + mint: AccountInfo<'info>, + spl_token_program: AccountInfo<'info>, + token_pool_pda: AccountInfo<'info>, + token_pool_pda_bump: u8, + ) -> Self { + self.spl_bridge_config = Some(SplBridgeConfig { + mint, + spl_token_program, + token_pool_pda, + token_pool_pda_bump, + }); + self + } + + pub fn with_spl_bridge_config(mut self, config: Option>) -> Self { + self.spl_bridge_config = config; + self + } - // spl -> ctoken: Requires bridge accounts - (false, true) => { - // Validate all required accounts are provided - let (mint_acct, spl_program, pool_pda, bump) = match ( - mint, - spl_token_program, - compressed_token_pool_pda, - compressed_token_pool_pda_bump, - ) { - (Some(m), Some(p), Some(pd), Some(b)) => (m, p, pd, b), - _ => { - return Err(ProgramError::Custom( - TokenSdkError::IncompleteSplBridgeConfig.into(), - )) + /// # Errors + /// * `SplBridgeConfigRequired` - If transferring to/from SPL without required accounts + /// * `UseRegularSplTransfer` - If both source and destination are SPL accounts + /// * `CannotDetermineAccountType` - If account type cannot be determined + pub fn invoke(self) -> Result<(), ProgramError> { + let source_is_ctoken = is_ctoken_account(&self.source_account) + .map_err(|_| ProgramError::InvalidAccountData)?; + let dest_is_ctoken = is_ctoken_account(&self.destination_account) + .map_err(|_| ProgramError::InvalidAccountData)?; + + match (source_is_ctoken, dest_is_ctoken) { + (true, true) => TransferCtokenAccountInfos { + source: self.source_account.clone(), + destination: self.destination_account.clone(), + amount: self.amount, + authority: self.authority.clone(), + } + .invoke(), + + (true, false) => { + let config = self.spl_bridge_config.ok_or_else(|| { + ProgramError::Custom(TokenSdkError::IncompleteSplBridgeConfig.into()) + })?; + + TransferCtokenToSplAccountInfos { + source_ctoken_account: self.source_account.clone(), + destination_spl_token_account: self.destination_account.clone(), + amount: self.amount, + authority: self.authority.clone(), + mint: config.mint.clone(), + payer: self.payer.clone(), + token_pool_pda: config.token_pool_pda.clone(), + token_pool_pda_bump: config.token_pool_pda_bump, + spl_token_program: config.spl_token_program.clone(), + compressed_token_program_authority: self + .compressed_token_program_authority + .clone(), } - }; - - transfer_spl_to_ctoken_signed( - payer.clone(), - authority.clone(), - source_account.clone(), - destination_account.clone(), - mint_acct.clone(), - spl_program.clone(), - pool_pda.clone(), - bump, - compressed_token_program_authority.clone(), - amount, - signer_seeds, - ) + .invoke() + } + + (false, true) => { + let config = self.spl_bridge_config.ok_or_else(|| { + ProgramError::Custom(TokenSdkError::IncompleteSplBridgeConfig.into()) + })?; + + TransferSplToCtokenAccountInfos { + source_spl_token_account: self.source_account.clone(), + to: self.destination_account.clone(), + amount: self.amount, + authority: self.authority.clone(), + mint: config.mint.clone(), + payer: self.payer.clone(), + token_pool_pda: config.token_pool_pda.clone(), + token_pool_pda_bump: config.token_pool_pda_bump, + spl_token_program: config.spl_token_program.clone(), + compressed_token_program_authority: self + .compressed_token_program_authority + .clone(), + destination_ctoken_account: self.destination_account.clone(), + } + .invoke() + } + + (false, false) => Err(ProgramError::Custom( + TokenSdkError::UseRegularSplTransfer.into(), + )), } + } - // spl -> spl: Not supported - (false, false) => Err(ProgramError::Custom( - TokenSdkError::UseRegularSplTransfer.into(), - )), + /// # Errors + /// * `SplBridgeConfigRequired` - If transferring to/from SPL without required accounts + /// * `UseRegularSplTransfer` - If both source and destination are SPL accounts + /// * `CannotDetermineAccountType` - If account type cannot be determined + pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { + let source_is_ctoken = is_ctoken_account(&self.source_account) + .map_err(|_| ProgramError::InvalidAccountData)?; + let dest_is_ctoken = is_ctoken_account(&self.destination_account) + .map_err(|_| ProgramError::InvalidAccountData)?; + + match (source_is_ctoken, dest_is_ctoken) { + (true, true) => TransferCtokenAccountInfos { + source: self.source_account.clone(), + destination: self.destination_account.clone(), + amount: self.amount, + authority: self.authority.clone(), + } + .invoke_signed(signer_seeds), + + (true, false) => { + let config = self.spl_bridge_config.ok_or_else(|| { + ProgramError::Custom(TokenSdkError::IncompleteSplBridgeConfig.into()) + })?; + + TransferCtokenToSplAccountInfos { + source_ctoken_account: self.source_account.clone(), + destination_spl_token_account: self.destination_account.clone(), + amount: self.amount, + authority: self.authority.clone(), + mint: config.mint.clone(), + payer: self.payer.clone(), + token_pool_pda: config.token_pool_pda.clone(), + token_pool_pda_bump: config.token_pool_pda_bump, + spl_token_program: config.spl_token_program.clone(), + compressed_token_program_authority: self + .compressed_token_program_authority + .clone(), + } + .invoke_signed(signer_seeds) + } + + (false, true) => { + let config = self.spl_bridge_config.ok_or_else(|| { + ProgramError::Custom(TokenSdkError::IncompleteSplBridgeConfig.into()) + })?; + + TransferSplToCtokenAccountInfos { + source_spl_token_account: self.source_account.clone(), + to: self.destination_account.clone(), + amount: self.amount, + authority: self.authority.clone(), + mint: config.mint.clone(), + payer: self.payer.clone(), + token_pool_pda: config.token_pool_pda.clone(), + token_pool_pda_bump: config.token_pool_pda_bump, + spl_token_program: config.spl_token_program.clone(), + compressed_token_program_authority: self + .compressed_token_program_authority + .clone(), + destination_ctoken_account: self.destination_account.clone(), + } + .invoke_signed(signer_seeds) + } + + (false, false) => Err(ProgramError::Custom( + TokenSdkError::UseRegularSplTransfer.into(), + )), + } } } diff --git a/sdk-libs/token-client/src/actions/transfer2/ctoken_to_spl.rs b/sdk-libs/token-client/src/actions/transfer2/ctoken_to_spl.rs index a8775e12f1..656c455fa5 100644 --- a/sdk-libs/token-client/src/actions/transfer2/ctoken_to_spl.rs +++ b/sdk-libs/token-client/src/actions/transfer2/ctoken_to_spl.rs @@ -3,8 +3,8 @@ use light_client::{ rpc::{Rpc, RpcError}, }; use light_compressed_token_sdk::{ - instructions::create_transfer_ctoken_to_spl_instruction, - token_pool::find_token_pool_pda_with_index, SPL_TOKEN_PROGRAM_ID, + instructions::TransferCtokenToSpl, token_pool::find_token_pool_pda_with_index, + SPL_TOKEN_PROGRAM_ID, }; use solana_keypair::Keypair; use solana_pubkey::Pubkey; @@ -21,24 +21,22 @@ pub async fn transfer_ctoken_to_spl( mint: Pubkey, payer: &Keypair, ) -> Result { - // Derive token pool PDA with bump let (token_pool_pda, token_pool_pda_bump) = find_token_pool_pda_with_index(&mint, 0); - // Create the transfer instruction - let transfer_ix = create_transfer_ctoken_to_spl_instruction( + let transfer_ix = TransferCtokenToSpl { source_ctoken_account, destination_spl_token_account, amount, - authority.pubkey(), + authority: authority.pubkey(), mint, - payer.pubkey(), + payer: payer.pubkey(), token_pool_pda, token_pool_pda_bump, - Pubkey::new_from_array(SPL_TOKEN_PROGRAM_ID), // TODO: make dynamic - ) + spl_token_program: Pubkey::new_from_array(SPL_TOKEN_PROGRAM_ID), // TODO: make dynamic + } + .instruction() .map_err(|e| RpcError::AssertRpcError(format!("Failed to create instruction: {:?}", e)))?; - // Build and send transaction let mut signers = vec![payer]; if authority.pubkey() != payer.pubkey() { signers.push(authority); diff --git a/sdk-libs/token-client/src/actions/transfer2/spl_to_ctoken.rs b/sdk-libs/token-client/src/actions/transfer2/spl_to_ctoken.rs index 890e95b793..c928275e8f 100644 --- a/sdk-libs/token-client/src/actions/transfer2/spl_to_ctoken.rs +++ b/sdk-libs/token-client/src/actions/transfer2/spl_to_ctoken.rs @@ -3,8 +3,8 @@ use light_client::{ rpc::{Rpc, RpcError}, }; use light_compressed_token_sdk::{ - instructions::create_transfer_spl_to_ctoken_instruction, - token_pool::find_token_pool_pda_with_index, SPL_TOKEN_PROGRAM_ID, + instructions::TransferSplToCtoken, token_pool::find_token_pool_pda_with_index, + SPL_TOKEN_PROGRAM_ID, }; use solana_keypair::Keypair; use solana_pubkey::Pubkey; @@ -32,19 +32,20 @@ pub async fn spl_to_ctoken_transfer( let mint = pod_account.mint; - let (token_pool_pda, bump) = find_token_pool_pda_with_index(&mint, 0); + let (token_pool_pda, token_pool_pda_bump) = find_token_pool_pda_with_index(&mint, 0); - let ix = create_transfer_spl_to_ctoken_instruction( + let ix = TransferSplToCtoken { + amount, + token_pool_pda_bump, source_spl_token_account, to, - amount, - authority.pubkey(), + authority: authority.pubkey(), mint, - payer.pubkey(), + payer: payer.pubkey(), token_pool_pda, - bump, - Pubkey::new_from_array(SPL_TOKEN_PROGRAM_ID), // TODO: make dynamic - ) + spl_token_program: Pubkey::new_from_array(SPL_TOKEN_PROGRAM_ID), // TODO: make dynamic + } + .instruction() .map_err(|e| RpcError::CustomError(e.to_string()))?; let mut signers = vec![payer];