Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ pub struct CreateAssociatedTokenAccountInstructionData {
pub owner: Pubkey,
/// The mint for the associated token account
pub mint: Pubkey,
pub bump: u8,
/// Optional compressible configuration for the token account
pub compressible_config: Option<CompressibleExtensionInstructionData>,
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use crate::{
#[repr(C)]
#[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize, ZeroCopy)]
pub struct CreateAssociatedTokenAccount2InstructionData {
pub bump: u8,
/// Optional compressible configuration for the token account
pub compressible_config: Option<CompressibleExtensionInstructionData>,
}
34 changes: 10 additions & 24 deletions program-tests/compressed-token-test/tests/ctoken/create_ata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ async fn test_create_ata_failing() {

// Use different mint for this test
context.mint_pubkey = solana_sdk::pubkey::Pubkey::new_unique();
let (ata_pubkey, bump) =
let (ata_pubkey, _bump) =
derive_ctoken_ata(&context.owner_keypair.pubkey(), &context.mint_pubkey);

// Manually build instruction data with compress_to_account_pubkey (forbidden)
Expand All @@ -324,7 +324,6 @@ async fn test_create_ata_failing() {
let instruction_data = CreateAssociatedTokenAccountInstructionData {
owner: context.owner_keypair.pubkey().into(),
mint: context.mint_pubkey.into(),
bump,
compressible_config: Some(CompressibleExtensionInstructionData {
token_account_version: light_ctoken_types::state::TokenDataVersion::ShaFlat as u8,
rent_payment: 2,
Expand Down Expand Up @@ -364,9 +363,9 @@ async fn test_create_ata_failing() {
light_program_test::utils::assert::assert_rpc_error(result, 0, 2).unwrap();
}

// Test 5: Invalid PDA derivation (wrong bump)
// ATAs must use the correct bump derived from [owner, program_id, mint]
// Error: 21 (ProgramFailedToComplete - provided seeds do not result in valid address)
// Test 5: Invalid PDA derivation (wrong ATA address)
// ATAs must use the correct address derived from [owner, program_id, mint]
// Error: InvalidSeeds (14)
{
use anchor_lang::prelude::borsh::BorshSerialize;
use light_ctoken_types::instructions::{
Expand All @@ -377,20 +376,15 @@ async fn test_create_ata_failing() {

// Use different mint for this test
context.mint_pubkey = solana_sdk::pubkey::Pubkey::new_unique();
let (ata_pubkey, correct_bump) =
let (_ata_pubkey, _correct_bump) =
derive_ctoken_ata(&context.owner_keypair.pubkey(), &context.mint_pubkey);

// Manually build instruction data with WRONG bump
let wrong_bump = if correct_bump == 255 {
254
} else {
correct_bump + 1
};
// Use a WRONG ATA address (just generate a random one)
let wrong_ata_pubkey = solana_sdk::pubkey::Pubkey::new_unique();

let instruction_data = CreateAssociatedTokenAccountInstructionData {
owner: context.owner_keypair.pubkey().into(),
mint: context.mint_pubkey.into(),
bump: wrong_bump, // Wrong bump!
compressible_config: Some(CompressibleExtensionInstructionData {
token_account_version: light_ctoken_types::state::TokenDataVersion::ShaFlat as u8,
rent_payment: 2,
Expand All @@ -407,7 +401,7 @@ async fn test_create_ata_failing() {
program_id: light_compressed_token::ID,
accounts: vec![
solana_sdk::instruction::AccountMeta::new(payer_pubkey, true),
solana_sdk::instruction::AccountMeta::new(ata_pubkey, false),
solana_sdk::instruction::AccountMeta::new(wrong_ata_pubkey, false),
solana_sdk::instruction::AccountMeta::new_readonly(
solana_sdk::pubkey::Pubkey::default(),
false,
Expand All @@ -426,16 +420,8 @@ async fn test_create_ata_failing() {
.create_and_send_transaction(&[ix], &payer_pubkey, &[&context.payer])
.await;

// Wrong bump can trigger either ProgramFailedToComplete (21) or PrivilegeEscalation (19)
// depending on runtime state - accept either
let is_valid_error =
light_program_test::utils::assert::assert_rpc_error(result.clone(), 0, 21).is_ok()
|| light_program_test::utils::assert::assert_rpc_error(result, 0, 19).is_ok();

assert!(
is_valid_error,
"Expected either ProgramFailedToComplete (21) or PrivilegeEscalation (19)"
);
// Should fail with InvalidSeeds (14)
light_program_test::utils::assert::assert_rpc_error(result, 0, 14).unwrap();
}

// Test 6: Invalid config account owner
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ use crate::{
shared::{
convert_program_error, create_pda_account,
initialize_ctoken_account::initialize_ctoken_account, transfer_lamports_via_cpi,
validate_ata_derivation,
},
};

Expand Down Expand Up @@ -57,7 +56,6 @@ pub(crate) fn process_create_associated_token_account_with_mode<const IDEMPOTENT
account_infos,
&instruction_inputs.owner.to_bytes(),
&instruction_inputs.mint.to_bytes(),
instruction_inputs.bump,
instruction_inputs.compressible_config,
)
}
Expand All @@ -69,7 +67,6 @@ pub(crate) fn process_create_associated_token_account_inner<const IDEMPOTENT: bo
account_infos: &[AccountInfo],
owner_bytes: &[u8; 32],
mint_bytes: &[u8; 32],
bump: u8,
compressible_config: Option<CompressibleExtensionInstructionData>,
) -> Result<(), ProgramError> {
let mut iter = AccountIterator::new(account_infos);
Expand All @@ -78,10 +75,23 @@ pub(crate) fn process_create_associated_token_account_inner<const IDEMPOTENT: bo
let associated_token_account = iter.next_mut("associated_token_account")?;
let _system_program = iter.next_non_mut("system_program")?;

let program_id_solana =
solana_pubkey::Pubkey::new_from_array(crate::LIGHT_CPI_SIGNER.program_id);
let (expected_address, bump) = solana_pubkey::Pubkey::find_program_address(
&[
owner_bytes.as_ref(),
program_id_solana.as_ref(),
mint_bytes.as_ref(),
],
&program_id_solana,
);

if associated_token_account.key() != &expected_address.to_bytes() {
return Err(ProgramError::InvalidSeeds);
}

// If idempotent mode, check if account already exists
if IDEMPOTENT {
// Verify the PDA derivation is correct
validate_ata_derivation(associated_token_account, owner_bytes, mint_bytes, bump)?;
// If account is already owned by our program, it exists - return success
if associated_token_account.is_owned_by(&crate::LIGHT_CPI_SIGNER.program_id) {
return Ok(());
Expand All @@ -99,6 +109,7 @@ pub(crate) fn process_create_associated_token_account_inner<const IDEMPOTENT: bo
light_ctoken_types::BASE_TOKEN_ACCOUNT_SIZE as usize
};

let bump_seed = [bump];
let (compressible_config_account, custom_rent_payer) =
if let Some(compressible_config_ix_data) = compressible_config.as_ref() {
let (compressible_config_account, custom_rent_payer) = process_compressible_config(
Expand All @@ -107,14 +118,13 @@ pub(crate) fn process_create_associated_token_account_inner<const IDEMPOTENT: bo
token_account_size,
fee_payer,
associated_token_account,
bump,
&bump_seed,
owner_bytes,
mint_bytes,
)?;
(Some(compressible_config_account), custom_rent_payer)
} else {
// Create the PDA account (with rent-exempt balance only)
let bump_seed = [bump];
let seeds = [
Seed::from(owner_bytes.as_ref()),
Seed::from(crate::LIGHT_CPI_SIGNER.program_id.as_ref()),
Expand Down Expand Up @@ -153,7 +163,7 @@ fn process_compressible_config<'info>(
token_account_size: usize,
fee_payer: &'info AccountInfo,
associated_token_account: &'info AccountInfo,
ata_bump: u8,
ata_bump_seed: &[u8; 1],
owner_bytes: &[u8; 32],
mint_bytes: &[u8; 32],
) -> Result<(&'info CompressibleConfig, Option<Pubkey>), ProgramError> {
Expand Down Expand Up @@ -186,7 +196,6 @@ fn process_compressible_config<'info>(
);

// Build ATA seeds
let ata_bump_seed = [ata_bump];
let ata_seeds = [
Seed::from(owner_bytes.as_ref()),
Seed::from(crate::LIGHT_CPI_SIGNER.program_id.as_ref()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ fn process_create_associated_token_account2_inner<const IDEMPOTENT: bool>(
remaining_accounts,
owner.key(),
mint.key(),
instruction_inputs.bump,
instruction_inputs.compressible_config,
)
}
2 changes: 0 additions & 2 deletions programs/compressed-token/program/src/shared/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ pub mod owner_validation;
pub mod token_input;
pub mod token_output;
pub mod transfer_lamports;
pub mod validate_ata_derivation;

// Re-export AccountIterator from light-account-checks
pub use convert_program_error::convert_program_error;
pub use create_pda_account::{create_pda_account, verify_pda};
pub use light_account_checks::AccountIterator;
pub use mint_to_token_pool::mint_to_token_pool;
pub use transfer_lamports::*;
pub use validate_ata_derivation::validate_ata_derivation;

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -58,35 +58,12 @@ pub fn create_compressible_associated_token_account_idempotent(
pub fn create_compressible_associated_token_account_with_mode<const IDEMPOTENT: bool>(
inputs: CreateCompressibleAssociatedTokenAccountInputs,
) -> Result<Instruction> {
let (ata_pubkey, bump) = derive_ctoken_ata(&inputs.owner, &inputs.mint);
create_compressible_associated_token_account_with_bump_and_mode::<IDEMPOTENT>(
inputs, ata_pubkey, bump,
)
}

/// Creates a compressible associated token account instruction with a specified bump (non-idempotent)
pub fn create_compressible_associated_token_account_with_bump(
inputs: CreateCompressibleAssociatedTokenAccountInputs,
ata_pubkey: Pubkey,
bump: u8,
) -> Result<Instruction> {
create_compressible_associated_token_account_with_bump_and_mode::<false>(
inputs, ata_pubkey, bump,
)
}

/// Creates a compressible associated token account instruction with a specified bump and mode
pub fn create_compressible_associated_token_account_with_bump_and_mode<const IDEMPOTENT: bool>(
inputs: CreateCompressibleAssociatedTokenAccountInputs,
ata_pubkey: Pubkey,
bump: u8,
) -> Result<Instruction> {
let (ata_pubkey, _bump) = derive_ctoken_ata(&inputs.owner, &inputs.mint);
create_ata_instruction_unified::<IDEMPOTENT, true>(
inputs.payer,
inputs.owner,
inputs.mint,
ata_pubkey,
bump,
Some((
inputs.pre_pay_num_epochs,
inputs.lamports_per_write,
Expand Down Expand Up @@ -121,34 +98,8 @@ pub fn create_associated_token_account_with_mode<const IDEMPOTENT: bool>(
owner: Pubkey,
mint: Pubkey,
) -> Result<Instruction> {
let (ata_pubkey, bump) = derive_ctoken_ata(&owner, &mint);
create_associated_token_account_with_bump_and_mode::<IDEMPOTENT>(
payer, owner, mint, ata_pubkey, bump,
)
}

/// Creates a basic associated token account instruction with a specified bump (non-idempotent)
pub fn create_associated_token_account_with_bump(
payer: Pubkey,
owner: Pubkey,
mint: Pubkey,
ata_pubkey: Pubkey,
bump: u8,
) -> Result<Instruction> {
create_associated_token_account_with_bump_and_mode::<false>(
payer, owner, mint, ata_pubkey, bump,
)
}

/// Creates a basic associated token account instruction with specified bump and mode
pub fn create_associated_token_account_with_bump_and_mode<const IDEMPOTENT: bool>(
payer: Pubkey,
owner: Pubkey,
mint: Pubkey,
ata_pubkey: Pubkey,
bump: u8,
) -> Result<Instruction> {
create_ata_instruction_unified::<IDEMPOTENT, false>(payer, owner, mint, ata_pubkey, bump, None)
let (ata_pubkey, _bump) = derive_ctoken_ata(&owner, &mint);
create_ata_instruction_unified::<IDEMPOTENT, false>(payer, owner, mint, ata_pubkey, None)
}

/// Unified function to create ATA instructions with compile-time configuration
Expand All @@ -157,7 +108,6 @@ fn create_ata_instruction_unified<const IDEMPOTENT: bool, const COMPRESSIBLE: bo
owner: Pubkey,
mint: Pubkey,
ata_pubkey: Pubkey,
bump: u8,
compressible_config: Option<(u8, Option<u32>, Pubkey, Pubkey, TokenDataVersion)>, // (pre_pay_num_epochs, lamports_per_write, rent_sponsor, compressible_config_account, token_account_version)
) -> Result<Instruction> {
// Select discriminator based on idempotent mode
Expand Down Expand Up @@ -189,7 +139,6 @@ fn create_ata_instruction_unified<const IDEMPOTENT: bool, const COMPRESSIBLE: bo
let instruction_data = CreateAssociatedTokenAccountInstructionData {
owner: light_compressed_account::Pubkey::from(owner.to_bytes()),
mint: light_compressed_account::Pubkey::from(mint.to_bytes()),
bump,
compressible_config: compressible_extension,
};

Expand Down Expand Up @@ -261,24 +210,12 @@ pub fn create_compressible_associated_token_account2_idempotent(
fn create_compressible_associated_token_account2_with_mode<const IDEMPOTENT: bool>(
inputs: CreateCompressibleAssociatedTokenAccountInputs,
) -> Result<Instruction> {
let (ata_pubkey, bump) = derive_ctoken_ata(&inputs.owner, &inputs.mint);
create_compressible_associated_token_account2_with_bump_and_mode::<IDEMPOTENT>(
inputs, ata_pubkey, bump,
)
}

/// Creates a compressible associated token account instruction v2 with specified bump and mode
fn create_compressible_associated_token_account2_with_bump_and_mode<const IDEMPOTENT: bool>(
inputs: CreateCompressibleAssociatedTokenAccountInputs,
ata_pubkey: Pubkey,
bump: u8,
) -> Result<Instruction> {
let (ata_pubkey, _bump) = derive_ctoken_ata(&inputs.owner, &inputs.mint);
create_ata2_instruction_unified::<IDEMPOTENT, true>(
inputs.payer,
inputs.owner,
inputs.mint,
ata_pubkey,
bump,
Some((
inputs.pre_pay_num_epochs,
inputs.lamports_per_write,
Expand Down Expand Up @@ -315,21 +252,8 @@ fn create_associated_token_account2_with_mode<const IDEMPOTENT: bool>(
owner: Pubkey,
mint: Pubkey,
) -> Result<Instruction> {
let (ata_pubkey, bump) = derive_ctoken_ata(&owner, &mint);
create_associated_token_account2_with_bump_and_mode::<IDEMPOTENT>(
payer, owner, mint, ata_pubkey, bump,
)
}

/// Creates a basic associated token account instruction v2 with specified bump and mode
fn create_associated_token_account2_with_bump_and_mode<const IDEMPOTENT: bool>(
payer: Pubkey,
owner: Pubkey,
mint: Pubkey,
ata_pubkey: Pubkey,
bump: u8,
) -> Result<Instruction> {
create_ata2_instruction_unified::<IDEMPOTENT, false>(payer, owner, mint, ata_pubkey, bump, None)
let (ata_pubkey, _bump) = derive_ctoken_ata(&owner, &mint);
create_ata2_instruction_unified::<IDEMPOTENT, false>(payer, owner, mint, ata_pubkey, None)
}

/// Unified function to create ATA2 instructions with compile-time configuration
Expand All @@ -339,7 +263,6 @@ fn create_ata2_instruction_unified<const IDEMPOTENT: bool, const COMPRESSIBLE: b
owner: Pubkey,
mint: Pubkey,
ata_pubkey: Pubkey,
bump: u8,
compressible_config: Option<(u8, Option<u32>, Pubkey, Pubkey, TokenDataVersion)>,
) -> Result<Instruction> {
let discriminator = if IDEMPOTENT {
Expand Down Expand Up @@ -367,7 +290,6 @@ fn create_ata2_instruction_unified<const IDEMPOTENT: bool, const COMPRESSIBLE: b
};

let instruction_data = CreateAssociatedTokenAccount2InstructionData {
bump,
compressible_config: compressible_extension,
};

Expand Down
Loading
Loading