From eabac758619ab331000d812387cc57fb25de0db9 Mon Sep 17 00:00:00 2001 From: ananas Date: Fri, 16 Jan 2026 16:01:37 +0000 Subject: [PATCH 1/2] refactor: sdk, rename CompressedMint -> Mint revert photon api renamings cleanup feat: transfer interface spl -> spl , t22 -> t22 transfer support --- Cargo.lock | 2 +- program-libs/token-interface/src/error.rs | 24 +- .../src/instructions/mint_action/builder.rs | 25 +- ...se_cmint.rs => compress_and_close_mint.rs} | 22 +- .../instructions/mint_action/cpi_context.rs | 2 +- .../mint_action/decompress_mint.rs | 6 +- .../mint_action/instruction_data.rs | 43 +- .../src/instructions/mint_action/mod.rs | 4 +- .../src/state/mint/compressed_mint.rs | 48 +- .../token-interface/src/state/mint/top_up.rs | 24 +- .../src/state/mint/zero_copy.rs | 155 ++-- .../token-interface/tests/compressed_mint.rs | 141 ++-- .../tests/cross_deserialization.rs | 68 +- .../token-interface/tests/hash_tests.rs | 54 +- .../tests/mint_borsh_zero_copy.rs | 72 +- .../token-interface/tests/mint_compat.rs | 28 +- .../tests/light_token/approve_revoke.rs | 4 +- .../tests/light_token/burn.rs | 62 +- .../light_token/spl_instruction_compat.rs | 40 +- .../compressed-token-test/tests/mint/burn.rs | 28 +- .../tests/mint/cpi_context.rs | 20 +- .../tests/mint/edge_cases.rs | 6 +- .../tests/mint/failing.rs | 48 +- .../tests/mint/functional.rs | 164 ++-- .../tests/mint/mint_to.rs | 32 +- .../tests/mint/random.rs | 4 +- .../tests/transfer2/compress_failing.rs | 4 +- .../tests/transfer2/decompress_failing.rs | 2 +- .../no_system_program_cpi_failing.rs | 4 +- .../registry-test/tests/compressible.rs | 129 ++-- program-tests/utils/src/assert_claim.rs | 20 +- program-tests/utils/src/assert_ctoken_burn.rs | 62 +- .../utils/src/assert_ctoken_mint_to.rs | 62 +- program-tests/utils/src/assert_metadata.rs | 26 +- program-tests/utils/src/assert_mint_action.rs | 75 +- .../utils/src/assert_mint_to_compressed.rs | 10 +- program-tests/utils/src/mint_assert.rs | 16 +- programs/compressed-token/anchor/src/lib.rs | 2 +- .../compressed_token/mint_action/accounts.rs | 2 +- .../actions/compress_and_close_cmint.rs | 18 +- .../mint_action/actions/create_mint.rs | 8 +- .../mint_action/actions/decompress_mint.rs | 10 +- .../mint_action/actions/mint_to.rs | 5 +- .../mint_action/actions/mint_to_ctoken.rs | 4 +- .../mint_action/actions/process_actions.rs | 6 +- .../mint_action/actions/update_metadata.rs | 10 +- .../mint_action/mint_input.rs | 4 +- .../mint_action/mint_output.rs | 12 +- .../compressed_token/mint_action/processor.rs | 8 +- .../mint_action/zero_copy_config.rs | 8 +- .../program/src/compressible/claim.rs | 4 +- .../program/src/shared/compressible_top_up.rs | 4 +- .../program/src/shared/cpi_bytes_size.rs | 10 +- .../program/tests/allocation_test.rs | 26 +- .../program/tests/exact_allocation_test.rs | 32 +- .../compressed-token/program/tests/mint.rs | 53 +- .../program/tests/mint_action.rs | 32 +- sdk-libs/program-test/src/compressible.rs | 24 +- .../token-client/src/actions/mint_action.rs | 12 +- .../src/actions/update_compressed_mint.rs | 8 +- .../src/instructions/create_mint.rs | 86 +-- .../src/instructions/mint_action.rs | 89 ++- .../src/instructions/mint_to_compressed.rs | 24 +- .../instructions/update_compressed_mint.rs | 26 +- sdk-libs/token-sdk/Cargo.toml | 1 - .../create_compressed_mint/account_metas.rs | 14 +- .../v2/create_compressed_mint/instruction.rs | 26 +- .../v2/create_compressed_mint/mod.rs | 5 +- .../v2/mint_action/account_metas.rs | 34 +- .../v2/mint_to_compressed/instruction.rs | 12 +- .../update_compressed_mint/account_metas.rs | 4 +- .../v2/update_compressed_mint/instruction.rs | 30 +- .../v2/update_compressed_mint/mod.rs | 5 +- sdk-libs/token-sdk/src/error.rs | 3 + sdk-libs/token-sdk/src/token/burn.rs | 22 +- sdk-libs/token-sdk/src/token/burn_checked.rs | 22 +- sdk-libs/token-sdk/src/token/create_mint.rs | 97 ++- .../token-sdk/src/token/decompress_mint.rs | 36 +- sdk-libs/token-sdk/src/token/mint_to.rs | 22 +- .../token-sdk/src/token/mint_to_checked.rs | 22 +- sdk-libs/token-sdk/src/token/mod.rs | 6 +- .../token-sdk/src/token/transfer_interface.rs | 455 ++++++++--- sdk-libs/token-sdk/src/utils.rs | 24 +- .../account_infos/create_compressed_mint.rs | 28 +- .../src/instruction/update_compressed_mint.rs | 12 +- .../src/state.rs | 48 +- .../tests/basic_test.rs | 8 + sdk-tests/sdk-light-token-test/Cargo.toml | 1 + sdk-tests/sdk-light-token-test/src/burn.rs | 8 +- .../src/{create_cmint.rs => create_mint.rs} | 160 ++-- .../src/ctoken_mint_to.rs | 4 +- .../src/decompress_mint.rs | 12 +- sdk-tests/sdk-light-token-test/src/lib.rs | 20 +- .../src/transfer_checked.rs | 4 +- ...enario_cmint.rs => scenario_light_mint.rs} | 25 +- ...> scenario_light_mint_compression_only.rs} | 4 +- .../sdk-light-token-test/tests/shared.rs | 325 +++----- .../tests/test_approve_revoke.rs | 10 +- .../sdk-light-token-test/tests/test_burn.rs | 42 +- .../sdk-light-token-test/tests/test_close.rs | 4 +- .../tests/test_create_ata.rs | 6 +- ...st_create_cmint.rs => test_create_mint.rs} | 72 +- .../tests/test_create_token_account.rs | 6 +- .../tests/test_ctoken_mint_to.rs | 196 ++--- .../tests/test_decompress_cmint.rs | 728 ------------------ .../tests/test_decompress_mint.rs | 216 ++++++ .../tests/test_freeze_thaw.rs | 76 +- .../tests/test_transfer.rs | 4 +- .../tests/test_transfer_checked.rs | 23 +- .../tests/test_transfer_interface.rs | 465 +++++++++++ .../src/mint_compressed_tokens_cpi_write.rs | 5 +- .../src/pda_ctoken/processor.rs | 4 +- sdk-tests/sdk-token-test/tests/ctoken_pda.rs | 14 +- .../tests/decompress_full_cpi.rs | 11 +- sdk-tests/sdk-token-test/tests/pda_ctoken.rs | 14 +- .../sdk-token-test/tests/test_4_transfer2.rs | 20 +- .../tests/test_compress_full_and_close.rs | 20 +- 117 files changed, 2769 insertions(+), 2729 deletions(-) rename program-libs/token-interface/src/instructions/mint_action/{compress_and_close_cmint.rs => compress_and_close_mint.rs} (58%) rename sdk-tests/sdk-light-token-test/src/{create_cmint.rs => create_mint.rs} (60%) rename sdk-tests/sdk-light-token-test/tests/{scenario_cmint.rs => scenario_light_mint.rs} (95%) rename sdk-tests/sdk-light-token-test/tests/{scenario_cmint_compression_only.rs => scenario_light_mint_compression_only.rs} (98%) rename sdk-tests/sdk-light-token-test/tests/{test_create_cmint.rs => test_create_mint.rs} (76%) delete mode 100644 sdk-tests/sdk-light-token-test/tests/test_decompress_cmint.rs create mode 100644 sdk-tests/sdk-light-token-test/tests/test_decompress_mint.rs diff --git a/Cargo.lock b/Cargo.lock index 8ce880c0c9..ddcc2a7f08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4258,7 +4258,6 @@ dependencies = [ "solana-program-error 2.2.2", "solana-pubkey 2.4.0", "spl-pod", - "spl-token-2022 7.0.0", "thiserror 2.0.17", ] @@ -6025,6 +6024,7 @@ dependencies = [ "light-sdk", "light-sdk-types", "light-test-utils", + "light-token-client", "light-token-interface", "light-token-sdk", "light-token-types", diff --git a/program-libs/token-interface/src/error.rs b/program-libs/token-interface/src/error.rs index c7cf343b71..7ea2d994a6 100644 --- a/program-libs/token-interface/src/error.rs +++ b/program-libs/token-interface/src/error.rs @@ -136,17 +136,17 @@ pub enum TokenError { #[error("Calculated top-up exceeds sender's max_top_up limit")] MaxTopUpExceeded, - #[error("CMint account has invalid owner")] - InvalidCMintOwner, + #[error("Mint account has invalid owner")] + InvalidMintOwner, - #[error("CMint account is not initialized")] - CMintNotInitialized, + #[error("Mint account is not initialized")] + MintNotInitialized, - #[error("Failed to borrow CMint account data")] - CMintBorrowFailed, + #[error("Failed to borrow Mint account data")] + MintBorrowFailed, - #[error("Failed to deserialize CMint account data")] - CMintDeserializationFailed, + #[error("Failed to deserialize Mint account data")] + MintDeserializationFailed, #[error("CompressedOnly tokens cannot have compressed outputs - must decompress only")] CompressedOnlyBlocksTransfer, @@ -254,10 +254,10 @@ impl From for u32 { TokenError::TooManySeeds(_) => 18041, TokenError::WriteTopUpExceedsMaximum => 18042, TokenError::MaxTopUpExceeded => 18043, - TokenError::InvalidCMintOwner => 18044, - TokenError::CMintNotInitialized => 18045, - TokenError::CMintBorrowFailed => 18046, - TokenError::CMintDeserializationFailed => 18047, + TokenError::InvalidMintOwner => 18044, + TokenError::MintNotInitialized => 18045, + TokenError::MintBorrowFailed => 18046, + TokenError::MintDeserializationFailed => 18047, TokenError::CompressedOnlyBlocksTransfer => 18048, TokenError::OutTlvOutputCountMismatch => 18049, TokenError::InLamportsUnimplemented => 18050, diff --git a/program-libs/token-interface/src/instructions/mint_action/builder.rs b/program-libs/token-interface/src/instructions/mint_action/builder.rs index 99e2f77133..0048d773fb 100644 --- a/program-libs/token-interface/src/instructions/mint_action/builder.rs +++ b/program-libs/token-interface/src/instructions/mint_action/builder.rs @@ -4,10 +4,10 @@ use light_compressed_account::instruction_data::{ }; use crate::instructions::mint_action::{ - Action, CompressAndCloseCMintAction, CompressedMintInstructionData, CompressedMintWithContext, - CpiContext, CreateMint, DecompressMintAction, MintActionCompressedInstructionData, - MintToAction, MintToCompressedAction, RemoveMetadataKeyAction, UpdateAuthority, - UpdateMetadataAuthorityAction, UpdateMetadataFieldAction, + Action, CompressAndCloseMintAction, CpiContext, CreateMint, DecompressMintAction, + MintActionCompressedInstructionData, MintInstructionData, MintToAction, MintToCompressedAction, + MintWithContext, RemoveMetadataKeyAction, UpdateAuthority, UpdateMetadataAuthorityAction, + UpdateMetadataFieldAction, }; /// Discriminator for MintAction instruction @@ -22,11 +22,8 @@ impl InstructionDiscriminator for MintActionCompressedInstructionData { impl LightInstructionData for MintActionCompressedInstructionData {} impl MintActionCompressedInstructionData { - /// Create instruction data from CompressedMintWithContext (for existing mints) - pub fn new( - mint_with_context: CompressedMintWithContext, - proof: Option, - ) -> Self { + /// Create instruction data from MintWithContext (for existing mints) + pub fn new(mint_with_context: MintWithContext, proof: Option) -> Self { Self { leaf_index: mint_with_context.leaf_index, prove_by_index: mint_with_context.prove_by_index, @@ -44,7 +41,7 @@ impl MintActionCompressedInstructionData { pub fn new_mint( address_merkle_tree_root_index: u16, proof: CompressedProof, - mint: CompressedMintInstructionData, + mint: MintInstructionData, ) -> Self { Self { leaf_index: 0, // New mint has no existing leaf @@ -62,7 +59,7 @@ impl MintActionCompressedInstructionData { /// Create instruction data for new mint creation via CPI context write pub fn new_mint_write_to_cpi_context( address_merkle_tree_root_index: u16, - mint: CompressedMintInstructionData, + mint: MintInstructionData, cpi_context: CpiContext, ) -> Self { Self { @@ -126,9 +123,9 @@ impl MintActionCompressedInstructionData { self } - #[must_use = "with_compress_and_close_cmint returns a new value"] - pub fn with_compress_and_close_cmint(mut self, action: CompressAndCloseCMintAction) -> Self { - self.actions.push(Action::CompressAndCloseCMint(action)); + #[must_use = "with_compress_and_close_mint returns a new value"] + pub fn with_compress_and_close_mint(mut self, action: CompressAndCloseMintAction) -> Self { + self.actions.push(Action::CompressAndCloseMint(action)); self } diff --git a/program-libs/token-interface/src/instructions/mint_action/compress_and_close_cmint.rs b/program-libs/token-interface/src/instructions/mint_action/compress_and_close_mint.rs similarity index 58% rename from program-libs/token-interface/src/instructions/mint_action/compress_and_close_cmint.rs rename to program-libs/token-interface/src/instructions/mint_action/compress_and_close_mint.rs index 3f3bbd0b17..05403de68d 100644 --- a/program-libs/token-interface/src/instructions/mint_action/compress_and_close_cmint.rs +++ b/program-libs/token-interface/src/instructions/mint_action/compress_and_close_mint.rs @@ -2,39 +2,39 @@ use light_zero_copy::ZeroCopy; use crate::{AnchorDeserialize, AnchorSerialize}; -/// Action to compress and close a CMint Solana account. +/// Action to compress and close a Mint Solana account. /// The compressed mint state is always preserved. /// /// ## Requirements -/// - CMint must exist (cmint_decompressed = true) - unless idempotent is set +/// - Mint must exist (mint_decompressed = true) - unless idempotent is set /// - is_compressible() must return true (rent expired) /// - Cannot be combined with DecompressMint in same instruction /// /// ## Note -/// CompressAndCloseCMint is **permissionless** - anyone can compress and close a CMint +/// CompressAndCloseMint is **permissionless** - anyone can compress and close a Mint /// provided is_compressible() returns true. All lamports are returned to rent_sponsor. #[repr(C)] #[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, ZeroCopy)] -pub struct CompressAndCloseCMintAction { - /// If non-zero, succeed silently when CMint doesn't exist or cannot be compressed. +pub struct CompressAndCloseMintAction { + /// If non-zero, succeed silently when Mint doesn't exist or cannot be compressed. /// Useful for foresters to handle already-compressed mints without failing. pub idempotent: u8, } -impl CompressAndCloseCMintAction { +impl CompressAndCloseMintAction { /// Returns true if this action should succeed silently when: - /// - CMint doesn't exist (already compressed) - /// - CMint cannot be compressed (rent not expired) + /// - Mint doesn't exist (already compressed) + /// - Mint cannot be compressed (rent not expired) #[inline(always)] pub fn is_idempotent(&self) -> bool { self.idempotent != 0 } } -impl ZCompressAndCloseCMintAction<'_> { +impl ZCompressAndCloseMintAction<'_> { /// Returns true if this action should succeed silently when: - /// - CMint doesn't exist (already compressed) - /// - CMint cannot be compressed (rent not expired) + /// - Mint doesn't exist (already compressed) + /// - Mint cannot be compressed (rent not expired) #[inline(always)] pub fn is_idempotent(&self) -> bool { self.idempotent != 0 diff --git a/program-libs/token-interface/src/instructions/mint_action/cpi_context.rs b/program-libs/token-interface/src/instructions/mint_action/cpi_context.rs index b48b1006ee..89f35fb7cd 100644 --- a/program-libs/token-interface/src/instructions/mint_action/cpi_context.rs +++ b/program-libs/token-interface/src/instructions/mint_action/cpi_context.rs @@ -15,7 +15,7 @@ pub struct CpiContext { pub token_out_queue_index: u8, // Index of the compressed account that should receive the new address (0 = mint, 1+ = token accounts) pub assigned_account_index: u8, - /// Placeholder to enable cmints in multiple address trees. + /// Placeholder to enable mints in multiple address trees. /// Currently set to 0. pub read_only_address_trees: [u8; 4], pub address_tree_pubkey: [u8; 32], diff --git a/program-libs/token-interface/src/instructions/mint_action/decompress_mint.rs b/program-libs/token-interface/src/instructions/mint_action/decompress_mint.rs index 2513b3143d..519748955c 100644 --- a/program-libs/token-interface/src/instructions/mint_action/decompress_mint.rs +++ b/program-libs/token-interface/src/instructions/mint_action/decompress_mint.rs @@ -2,10 +2,10 @@ use light_zero_copy::ZeroCopy; use crate::{AnchorDeserialize, AnchorSerialize}; -/// Action to decompress a compressed mint to a CMint Solana account. -/// Creates a CMint PDA that becomes the source of truth for the mint state. +/// Action to decompress a compressed mint to a Mint Solana account. +/// Creates a Mint PDA that becomes the source of truth for the mint state. /// -/// CMint is ALWAYS compressible - `rent_payment` must be >= 2. +/// Mint is ALWAYS compressible - `rent_payment` must be >= 2. /// rent_payment == 0 or 1 is rejected (epoch boundary edge case). #[repr(C)] #[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, ZeroCopy)] diff --git a/program-libs/token-interface/src/instructions/mint_action/instruction_data.rs b/program-libs/token-interface/src/instructions/mint_action/instruction_data.rs index 571c151be6..86311b79b6 100644 --- a/program-libs/token-interface/src/instructions/mint_action/instruction_data.rs +++ b/program-libs/token-interface/src/instructions/mint_action/instruction_data.rs @@ -3,16 +3,13 @@ use light_compressible::compression_info::CompressionInfo; use light_zero_copy::ZeroCopy; use super::{ - CompressAndCloseCMintAction, CpiContext, DecompressMintAction, MintToAction, + CompressAndCloseMintAction, CpiContext, DecompressMintAction, MintToAction, MintToCompressedAction, RemoveMetadataKeyAction, UpdateAuthority, UpdateMetadataAuthorityAction, UpdateMetadataFieldAction, }; use crate::{ instructions::extensions::{ExtensionInstructionData, ZExtensionInstructionData}, - state::{ - AdditionalMetadata, BaseMint, CompressedMint, CompressedMintMetadata, ExtensionStruct, - TokenMetadata, - }, + state::{AdditionalMetadata, BaseMint, ExtensionStruct, Mint, MintMetadata, TokenMetadata}, AnchorDeserialize, AnchorSerialize, TokenError, }; @@ -31,12 +28,12 @@ pub enum Action { UpdateMetadataField(UpdateMetadataFieldAction), UpdateMetadataAuthority(UpdateMetadataAuthorityAction), RemoveMetadataKey(RemoveMetadataKeyAction), - /// Decompress a compressed mint to a CMint Solana account. - /// Creates a CMint PDA that becomes the source of truth. + /// Decompress a compressed mint to a Mint Solana account. + /// Creates a Mint PDA that becomes the source of truth. DecompressMint(DecompressMintAction), - /// Compress and close a CMint Solana account. The compressed mint state is preserved. + /// Compress and close a Mint Solana account. The compressed mint state is preserved. /// Permissionless - anyone can call if is_compressible() returns true (rent expired). - CompressAndCloseCMint(CompressAndCloseCMintAction), + CompressAndCloseMint(CompressAndCloseMintAction), } #[repr(C)] @@ -56,39 +53,39 @@ pub struct MintActionCompressedInstructionData { pub actions: Vec, pub proof: Option, pub cpi_context: Option, - pub mint: Option, + pub mint: Option, } #[repr(C)] #[derive(Debug, Clone, AnchorSerialize, Default, AnchorDeserialize, ZeroCopy)] pub struct CreateMint { - /// Placeholder to enable cmints in multiple address trees. + /// Placeholder to enable mints in multiple address trees. /// Currently set to 0. pub read_only_address_trees: [u8; 4], - /// Placeholder to enable cmints in multiple address trees. + /// Placeholder to enable mints in multiple address trees. /// Currently set to 0. pub read_only_address_tree_root_indices: [u16; 4], } #[repr(C)] #[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize, ZeroCopy, PartialEq)] -pub struct CompressedMintWithContext { +pub struct MintWithContext { pub leaf_index: u32, pub prove_by_index: bool, pub root_index: u16, pub address: [u8; 32], - pub mint: Option, + pub mint: Option, } #[repr(C)] #[derive(Debug, PartialEq, Eq, Clone, AnchorSerialize, AnchorDeserialize, ZeroCopy)] -pub struct CompressedMintInstructionData { +pub struct MintInstructionData { /// Total supply of tokens. pub supply: u64, /// Number of base 10 digits to the right of the decimal place. pub decimals: u8, /// Light Protocol-specific metadata - pub metadata: CompressedMintMetadata, + pub metadata: MintMetadata, /// Optional authority used to mint new tokens. The mint authority may only /// be provided during mint creation. If no mint authority is present /// then the mint has a fixed supply and no further tokens may be @@ -100,10 +97,10 @@ pub struct CompressedMintInstructionData { pub extensions: Option>, } -impl TryFrom for CompressedMintInstructionData { +impl TryFrom for MintInstructionData { type Error = TokenError; - fn try_from(mint: CompressedMint) -> Result { + fn try_from(mint: Mint) -> Result { let extensions = match mint.extensions { Some(exts) if !exts.is_empty() => { let mut extension_list = Vec::with_capacity(exts.len()); @@ -141,12 +138,10 @@ impl TryFrom for CompressedMintInstructionData { } } -impl<'a> TryFrom<&ZCompressedMintInstructionData<'a>> for CompressedMint { +impl<'a> TryFrom<&ZMintInstructionData<'a>> for Mint { type Error = TokenError; - fn try_from( - instruction_data: &ZCompressedMintInstructionData<'a>, - ) -> Result { + fn try_from(instruction_data: &ZMintInstructionData<'a>) -> Result { let extensions = match &instruction_data.extensions { Some(exts) => { let converted_exts: Vec<_> = exts @@ -196,9 +191,9 @@ impl<'a> TryFrom<&ZCompressedMintInstructionData<'a>> for CompressedMint { is_initialized: true, // Always true for compressed mints freeze_authority: instruction_data.freeze_authority.map(|p| *p), }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: instruction_data.metadata.version, - cmint_decompressed: instruction_data.metadata.cmint_decompressed != 0, + mint_decompressed: instruction_data.metadata.mint_decompressed != 0, mint: instruction_data.metadata.mint, mint_signer: instruction_data.metadata.mint_signer, bump: instruction_data.metadata.bump, diff --git a/program-libs/token-interface/src/instructions/mint_action/mod.rs b/program-libs/token-interface/src/instructions/mint_action/mod.rs index 27585fd79d..3b38ae3b2e 100644 --- a/program-libs/token-interface/src/instructions/mint_action/mod.rs +++ b/program-libs/token-interface/src/instructions/mint_action/mod.rs @@ -1,5 +1,5 @@ mod builder; -mod compress_and_close_cmint; +mod compress_and_close_mint; mod cpi_context; mod decompress_mint; mod instruction_data; @@ -8,7 +8,7 @@ mod mint_to_compressed; mod update_metadata; mod update_mint; -pub use compress_and_close_cmint::*; +pub use compress_and_close_mint::*; pub use cpi_context::*; pub use decompress_mint::*; pub use instruction_data::*; diff --git a/program-libs/token-interface/src/state/mint/compressed_mint.rs b/program-libs/token-interface/src/state/mint/compressed_mint.rs index b499471856..719329bd9b 100644 --- a/program-libs/token-interface/src/state/mint/compressed_mint.rs +++ b/program-libs/token-interface/src/state/mint/compressed_mint.rs @@ -17,9 +17,9 @@ pub const ACCOUNT_TYPE_MINT: u8 = 1; #[repr(C)] #[derive(Debug, PartialEq, Eq, Clone, BorshSerialize, BorshDeserialize)] -pub struct CompressedMint { +pub struct Mint { pub base: BaseMint, - pub metadata: CompressedMintMetadata, + pub metadata: MintMetadata, /// Reserved bytes (16 bytes) for T22 layout compatibility. /// Positions `account_type` at offset 165: 82 (BaseMint) + 67 (metadata) + 16 (reserved) = 165. pub reserved: [u8; 16], @@ -30,11 +30,11 @@ pub struct CompressedMint { pub extensions: Option>, } -impl Default for CompressedMint { +impl Default for Mint { fn default() -> Self { Self { base: BaseMint::default(), - metadata: CompressedMintMetadata::default(), + metadata: MintMetadata::default(), reserved: [0u8; 16], account_type: ACCOUNT_TYPE_MINT, compression: CompressionInfo::default(), @@ -67,7 +67,7 @@ pub struct BaseMint { /// /// Total size: 67 bytes /// - version: 1 byte -/// - cmint_decompressed: 1 byte +/// - mint_decompressed: 1 byte /// - mint: 32 bytes /// - mint_signer: 32 bytes /// - bump: 1 byte @@ -75,12 +75,12 @@ pub struct BaseMint { #[derive( Debug, Default, PartialEq, Eq, Clone, AnchorDeserialize, AnchorSerialize, ZeroCopyMut, ZeroCopy, )] -pub struct CompressedMintMetadata { +pub struct MintMetadata { /// Version for upgradability pub version: u8, - /// Whether the compressed mint has been decompressed to a CMint Solana account. - /// When true, the CMint account is the source of truth. - pub cmint_decompressed: bool, + /// Whether the compressed mint has been decompressed to a Mint Solana account. + /// When true, the Mint account is the source of truth. + pub mint_decompressed: bool, /// PDA derived from mint_signer, used as seed for the compressed address pub mint: Pubkey, /// Signer pubkey used to derive the mint PDA @@ -89,7 +89,7 @@ pub struct CompressedMintMetadata { pub bump: u8, } -impl CompressedMintMetadata { +impl MintMetadata { /// Derives the compressed address from mint PDA, CMINT_ADDRESS_TREE and LIGHT_TOKEN_PROGRAM_ID pub fn compressed_address(&self) -> [u8; 32] { derive_address( @@ -100,7 +100,7 @@ impl CompressedMintMetadata { } } -impl ZCompressedMintMetadata<'_> { +impl ZMintMetadata<'_> { /// Derives the compressed address from mint PDA, CMINT_ADDRESS_TREE and LIGHT_TOKEN_PROGRAM_ID pub fn compressed_address(&self) -> [u8; 32] { derive_address( @@ -111,7 +111,7 @@ impl ZCompressedMintMetadata<'_> { } } -impl CompressedMint { +impl Mint { pub fn hash(&self) -> Result<[u8; 32], TokenError> { match self.metadata.version { 3 => Ok(Sha256BE::hash( @@ -123,49 +123,49 @@ impl CompressedMint { } } - /// Deserialize a CompressedMint from a CMint Solana account with validation. + /// Deserialize a Mint from a Solana account with validation. /// /// Checks: /// 1. Account is owned by the specified program /// 2. Account is initialized (BaseMint.is_initialized == true) /// - /// Note: CMint accounts follow SPL token mint pattern (no discriminator). + /// Note: Mint accounts follow SPL token mint pattern (no discriminator). /// Validation is done via owner check + PDA derivation (caller responsibility). pub fn from_account_info_checked(account_info: &AccountInfo) -> Result { // 1. Check program ownership if !account_info.is_owned_by(&LIGHT_TOKEN_PROGRAM_ID) { #[cfg(feature = "solana")] - msg!("CMint account has invalid owner"); - return Err(TokenError::InvalidCMintOwner); + msg!("Mint account has invalid owner"); + return Err(TokenError::InvalidMintOwner); } // 2. Borrow and deserialize account data let data = account_info .try_borrow_data() - .map_err(|_| TokenError::CMintBorrowFailed)?; + .map_err(|_| TokenError::MintBorrowFailed)?; let mint = - Self::try_from_slice(&data).map_err(|_| TokenError::CMintDeserializationFailed)?; + Self::try_from_slice(&data).map_err(|_| TokenError::MintDeserializationFailed)?; // 3. Check is_initialized if !mint.base.is_initialized { #[cfg(feature = "solana")] - msg!("CMint account is not initialized"); - return Err(TokenError::CMintNotInitialized); + msg!("Mint account is not initialized"); + return Err(TokenError::MintNotInitialized); } - if !mint.is_cmint_account() { + if !mint.is_mint_account() { #[cfg(feature = "solana")] - msg!("CMint account is not a CMint account"); + msg!("Mint account is not a Mint account"); return Err(TokenError::MintMismatch); } Ok(mint) } - /// Checks if account_type matches CMint discriminator value + /// Checks if account_type matches Mint discriminator value #[inline(always)] - pub fn is_cmint_account(&self) -> bool { + pub fn is_mint_account(&self) -> bool { self.account_type == ACCOUNT_TYPE_MINT } } diff --git a/program-libs/token-interface/src/state/mint/top_up.rs b/program-libs/token-interface/src/state/mint/top_up.rs index e054892249..a8d076bc7e 100644 --- a/program-libs/token-interface/src/state/mint/top_up.rs +++ b/program-libs/token-interface/src/state/mint/top_up.rs @@ -1,4 +1,4 @@ -//! Optimized top-up lamports calculation for CMint accounts. +//! Optimized top-up lamports calculation for Mint accounts. use light_compressible::compression_info::CompressionInfo; use light_program_profiler::profile; @@ -8,11 +8,11 @@ use pinocchio::account_info::AccountInfo; use super::compressed_mint::ACCOUNT_TYPE_MINT; -/// Minimum size for CMint with CompressionInfo. +/// Minimum size for Mint with CompressionInfo. /// 166 (offset to CompressionInfo) + 96 (CompressionInfo size) = 262 -pub const CMINT_MIN_SIZE_WITH_COMPRESSION: usize = COMPRESSION_INFO_OFFSET + COMPRESSION_INFO_SIZE; +pub const MINT_MIN_SIZE_WITH_COMPRESSION: usize = COMPRESSION_INFO_OFFSET + COMPRESSION_INFO_SIZE; -/// Offset to CompressionInfo in CMint. +/// Offset to CompressionInfo in Mint. /// 82 (BaseMint) + 67 (metadata) + 16 (reserved) + 1 (account_type) = 166 const COMPRESSION_INFO_OFFSET: usize = 166; @@ -22,17 +22,16 @@ const COMPRESSION_INFO_SIZE: usize = 96; /// Offset to account_type field. const ACCOUNT_TYPE_OFFSET: usize = 165; -/// Calculate top-up lamports directly from CMint account bytes. -/// Returns None if account is not a valid CMint. +/// Calculate top-up lamports directly from Mint account bytes. +/// Returns None if account is not a valid Mint. #[inline(always)] #[profile] -pub fn cmint_top_up_lamports_from_slice( +pub fn mint_top_up_lamports_from_slice( data: &[u8], current_lamports: u64, current_slot: u64, ) -> Option { - if data.len() < CMINT_MIN_SIZE_WITH_COMPRESSION - || data[ACCOUNT_TYPE_OFFSET] != ACCOUNT_TYPE_MINT + if data.len() < MINT_MIN_SIZE_WITH_COMPRESSION || data[ACCOUNT_TYPE_OFFSET] != ACCOUNT_TYPE_MINT { return None; } @@ -46,13 +45,13 @@ pub fn cmint_top_up_lamports_from_slice( .ok() } -/// Calculate top-up lamports from a CMint AccountInfo. +/// Calculate top-up lamports from a Mint AccountInfo. /// Verifies account owner is the Token program. Returns None if owner mismatch or invalid. /// Pass `current_slot` as 0 to fetch from Clock sysvar; non-zero values are used directly. #[cfg(target_os = "solana")] #[inline(always)] #[profile] -pub fn cmint_top_up_lamports_from_account_info( +pub fn mint_top_up_lamports_from_account_info( account_info: &AccountInfo, current_slot: &mut u64, ) -> Option { @@ -65,8 +64,7 @@ pub fn cmint_top_up_lamports_from_account_info( let data = account_info.try_borrow_data().ok()?; - if data.len() < CMINT_MIN_SIZE_WITH_COMPRESSION - || data[ACCOUNT_TYPE_OFFSET] != ACCOUNT_TYPE_MINT + if data.len() < MINT_MIN_SIZE_WITH_COMPRESSION || data[ACCOUNT_TYPE_OFFSET] != ACCOUNT_TYPE_MINT { return None; } diff --git a/program-libs/token-interface/src/state/mint/zero_copy.rs b/program-libs/token-interface/src/state/mint/zero_copy.rs index 4fe8b158b3..f62ae35d1e 100644 --- a/program-libs/token-interface/src/state/mint/zero_copy.rs +++ b/program-libs/token-interface/src/state/mint/zero_copy.rs @@ -10,27 +10,27 @@ use light_zero_copy::{ }; use spl_pod::solana_msg::msg; -use super::compressed_mint::{CompressedMintMetadata, ACCOUNT_TYPE_MINT}; +use super::compressed_mint::{MintMetadata, ACCOUNT_TYPE_MINT}; use crate::{ - instructions::mint_action::CompressedMintInstructionData, + instructions::mint_action::MintInstructionData, state::{ - CompressedMint, ExtensionStruct, ExtensionStructConfig, TokenDataVersion, ZExtensionStruct, + ExtensionStruct, ExtensionStructConfig, Mint, TokenDataVersion, ZExtensionStruct, ZExtensionStructMut, }, AnchorDeserialize, AnchorSerialize, TokenError, }; -/// Base size for CMint accounts (without extensions) -pub const BASE_MINT_ACCOUNT_SIZE: u64 = CompressedMintZeroCopyMeta::LEN as u64; +/// Base size for Mint accounts (without extensions) +pub const BASE_MINT_ACCOUNT_SIZE: u64 = MintZeroCopyMeta::LEN as u64; -/// Optimized CompressedMint zero copy struct. -/// Uses derive macros to generate ZCompressedMintZeroCopyMeta<'a> and ZCompressedMintZeroCopyMetaMut<'a>. +/// Optimized Mint zero copy struct. +/// Uses derive macros to generate ZMintZeroCopyMeta<'a> and ZMintZeroCopyMetaMut<'a>. #[derive( Debug, PartialEq, Eq, Clone, AnchorSerialize, AnchorDeserialize, ZeroCopy, ZeroCopyMut, )] #[repr(C)] #[aligned_sized] -struct CompressedMintZeroCopyMeta { +struct MintZeroCopyMeta { // BaseMint fields with flattened COptions (SPL format: 4 bytes discriminator + 32 bytes pubkey) mint_authority_option_prefix: u32, mint_authority: Pubkey, @@ -42,8 +42,8 @@ struct CompressedMintZeroCopyMeta { pub is_initialized: u8, freeze_authority_option_prefix: u32, freeze_authority: Pubkey, - // CompressedMintMetadata - pub metadata: CompressedMintMetadata, + // MintMetadata + pub metadata: MintMetadata, /// Reserved bytes for T22 layout compatibility (padding to reach byte 165) pub reserved: [u8; 16], /// Account type discriminator at byte 165 (1 = Mint, 2 = Account) @@ -54,42 +54,42 @@ struct CompressedMintZeroCopyMeta { has_extensions: bool, } -/// Zero-copy view of CompressedMint with base and optional extensions +/// Zero-copy view of Mint with base and optional extensions #[derive(Debug)] -pub struct ZCompressedMint<'a> { - pub base: ZCompressedMintZeroCopyMeta<'a>, +pub struct ZMint<'a> { + pub base: ZMintZeroCopyMeta<'a>, pub extensions: Option>>, } -/// Mutable zero-copy view of CompressedMint with base and optional extensions +/// Mutable zero-copy view of Mint with base and optional extensions #[derive(Debug)] -pub struct ZCompressedMintMut<'a> { - pub base: ZCompressedMintZeroCopyMetaMut<'a>, +pub struct ZMintMut<'a> { + pub base: ZMintZeroCopyMetaMut<'a>, pub extensions: Option>>, } -/// Configuration for creating a new CompressedMint via ZeroCopyNew +/// Configuration for creating a new Mint via ZeroCopyNew #[derive(Debug, Clone, PartialEq)] -pub struct CompressedMintConfig { +pub struct MintConfig { /// Extension configurations pub extensions: Option>, } -impl<'a> ZeroCopyNew<'a> for CompressedMint { - type ZeroCopyConfig = CompressedMintConfig; - type Output = ZCompressedMintMut<'a>; +impl<'a> ZeroCopyNew<'a> for Mint { + type ZeroCopyConfig = MintConfig; + type Output = ZMintMut<'a>; fn byte_len( config: &Self::ZeroCopyConfig, ) -> Result { // Use derived byte_len for meta struct - let meta_config = CompressedMintZeroCopyMetaConfig { + let meta_config = MintZeroCopyMetaConfig { metadata: (), compression: light_compressible::compression_info::CompressionInfoConfig { rent_config: (), }, }; - let mut size = CompressedMintZeroCopyMeta::byte_len(&meta_config)?; + let mut size = MintZeroCopyMeta::byte_len(&meta_config)?; // Add extension sizes if present if let Some(ref extensions) = config.extensions { @@ -113,14 +113,14 @@ impl<'a> ZeroCopyNew<'a> for CompressedMint { return Err(light_zero_copy::errors::ZeroCopyError::MemoryNotZeroed); } // Use derived new_zero_copy for meta struct - let meta_config = CompressedMintZeroCopyMetaConfig { + let meta_config = MintZeroCopyMetaConfig { metadata: (), compression: light_compressible::compression_info::CompressionInfoConfig { rent_config: (), }, }; let (mut base, remaining) = - >::new_zero_copy(bytes, meta_config)?; + >::new_zero_copy(bytes, meta_config)?; *base.account_type = ACCOUNT_TYPE_MINT; base.is_initialized = 1; @@ -133,7 +133,7 @@ impl<'a> ZeroCopyNew<'a> for CompressedMint { )?; Ok(( - ZCompressedMintMut { + ZMintMut { base, extensions: Some(extensions), }, @@ -141,7 +141,7 @@ impl<'a> ZeroCopyNew<'a> for CompressedMint { )) } else { Ok(( - ZCompressedMintMut { + ZMintMut { base, extensions: None, }, @@ -151,19 +151,19 @@ impl<'a> ZeroCopyNew<'a> for CompressedMint { } } -impl<'a> ZeroCopyAt<'a> for CompressedMint { - type ZeroCopyAt = ZCompressedMint<'a>; +impl<'a> ZeroCopyAt<'a> for Mint { + type ZeroCopyAt = ZMint<'a>; fn zero_copy_at( bytes: &'a [u8], ) -> Result<(Self::ZeroCopyAt, &'a [u8]), light_zero_copy::errors::ZeroCopyError> { - let (base, bytes) = >::zero_copy_at(bytes)?; + let (base, bytes) = >::zero_copy_at(bytes)?; // has_extensions already consumed the Option discriminator byte if base.has_extensions() { let (extensions, bytes) = as ZeroCopyAt<'a>>::zero_copy_at(bytes)?; Ok(( - ZCompressedMint { + ZMint { base, extensions: Some(extensions), }, @@ -171,7 +171,7 @@ impl<'a> ZeroCopyAt<'a> for CompressedMint { )) } else { Ok(( - ZCompressedMint { + ZMint { base, extensions: None, }, @@ -181,20 +181,19 @@ impl<'a> ZeroCopyAt<'a> for CompressedMint { } } -impl<'a> ZeroCopyAtMut<'a> for CompressedMint { - type ZeroCopyAtMut = ZCompressedMintMut<'a>; +impl<'a> ZeroCopyAtMut<'a> for Mint { + type ZeroCopyAtMut = ZMintMut<'a>; fn zero_copy_at_mut( bytes: &'a mut [u8], ) -> Result<(Self::ZeroCopyAtMut, &'a mut [u8]), light_zero_copy::errors::ZeroCopyError> { - let (base, bytes) = - >::zero_copy_at_mut(bytes)?; + let (base, bytes) = >::zero_copy_at_mut(bytes)?; // has_extensions already consumed the Option discriminator byte if base.has_extensions() { let (extensions, bytes) = as ZeroCopyAtMut<'a>>::zero_copy_at_mut(bytes)?; Ok(( - ZCompressedMintMut { + ZMintMut { base, extensions: Some(extensions), }, @@ -202,7 +201,7 @@ impl<'a> ZeroCopyAtMut<'a> for CompressedMint { )) } else { Ok(( - ZCompressedMintMut { + ZMintMut { base, extensions: None, }, @@ -213,27 +212,27 @@ impl<'a> ZeroCopyAtMut<'a> for CompressedMint { } // Deref implementations for field access -impl<'a> Deref for ZCompressedMint<'a> { - type Target = ZCompressedMintZeroCopyMeta<'a>; +impl<'a> Deref for ZMint<'a> { + type Target = ZMintZeroCopyMeta<'a>; fn deref(&self) -> &Self::Target { &self.base } } -impl<'a> Deref for ZCompressedMintMut<'a> { - type Target = ZCompressedMintZeroCopyMetaMut<'a>; +impl<'a> Deref for ZMintMut<'a> { + type Target = ZMintZeroCopyMetaMut<'a>; fn deref(&self) -> &Self::Target { &self.base } } -// Getters on ZCompressedMintZeroCopyMeta (immutable) -impl ZCompressedMintZeroCopyMeta<'_> { - /// Checks if account_type matches CMint discriminator value +// Getters on ZMintZeroCopyMeta (immutable) +impl ZMintZeroCopyMeta<'_> { + /// Checks if account_type matches Mint discriminator value #[inline(always)] - pub fn is_cmint_account(&self) -> bool { + pub fn is_mint_account(&self) -> bool { self.account_type == ACCOUNT_TYPE_MINT } @@ -262,11 +261,11 @@ impl ZCompressedMintZeroCopyMeta<'_> { } } -// Getters on ZCompressedMintZeroCopyMetaMut (mutable) -impl ZCompressedMintZeroCopyMetaMut<'_> { - /// Checks if account_type matches CMint discriminator value +// Getters on ZMintZeroCopyMetaMut (mutable) +impl ZMintZeroCopyMetaMut<'_> { + /// Checks if account_type matches Mint discriminator value #[inline(always)] - pub fn is_cmint_account(&self) -> bool { + pub fn is_mint_account(&self) -> bool { *self.account_type == ACCOUNT_TYPE_MINT } @@ -317,31 +316,31 @@ impl ZCompressedMintZeroCopyMetaMut<'_> { } } -// Checked methods on CompressedMint -impl CompressedMint { +// Checked methods on Mint +impl Mint { /// Zero-copy deserialization with initialization and account_type check. /// Returns an error if: /// - Account is not initialized (is_initialized == false) /// - Account type is not ACCOUNT_TYPE_MINT (byte 165 != 1) #[profile] - pub fn zero_copy_at_checked(bytes: &[u8]) -> Result<(ZCompressedMint<'_>, &[u8]), TokenError> { - // Check minimum size (use CMint-specific size, not Token size) + pub fn zero_copy_at_checked(bytes: &[u8]) -> Result<(ZMint<'_>, &[u8]), TokenError> { + // Check minimum size (use Mint-specific size, not Token size) if bytes.len() < BASE_MINT_ACCOUNT_SIZE as usize { return Err(TokenError::InvalidAccountData); } // Proceed with deserialization first - let (mint, remaining) = CompressedMint::zero_copy_at(bytes) - .map_err(|_| TokenError::CMintDeserializationFailed)?; + let (mint, remaining) = + Mint::zero_copy_at(bytes).map_err(|_| TokenError::MintDeserializationFailed)?; // Verify account_type using the method - if !mint.is_cmint_account() { + if !mint.is_mint_account() { return Err(TokenError::InvalidAccountType); } // Check is_initialized if !mint.is_initialized() { - return Err(TokenError::CMintNotInitialized); + return Err(TokenError::MintNotInitialized); } Ok((mint, remaining)) @@ -354,8 +353,8 @@ impl CompressedMint { #[profile] pub fn zero_copy_at_mut_checked( bytes: &mut [u8], - ) -> Result<(ZCompressedMintMut<'_>, &mut [u8]), TokenError> { - // Check minimum size (use CMint-specific size, not Token size) + ) -> Result<(ZMintMut<'_>, &mut [u8]), TokenError> { + // Check minimum size (use Mint-specific size, not Token size) if bytes.len() < BASE_MINT_ACCOUNT_SIZE as usize { msg!( "zero_copy_at_mut_checked bytes.len() < BASE_MINT_ACCOUNT_SIZE {}", @@ -364,13 +363,13 @@ impl CompressedMint { return Err(TokenError::InvalidAccountData); } - let (mint, remaining) = CompressedMint::zero_copy_at_mut(bytes) - .map_err(|_| TokenError::CMintDeserializationFailed)?; + let (mint, remaining) = + Mint::zero_copy_at_mut(bytes).map_err(|_| TokenError::MintDeserializationFailed)?; if !mint.is_initialized() { - return Err(TokenError::CMintNotInitialized); + return Err(TokenError::MintNotInitialized); } - if !mint.is_cmint_account() { + if !mint.is_mint_account() { return Err(TokenError::InvalidAccountType); } @@ -378,12 +377,12 @@ impl CompressedMint { } } -// Helper methods on ZCompressedMint -impl ZCompressedMint<'_> { - /// Checks if account_type matches CMint discriminator value +// Helper methods on ZMint +impl ZMint<'_> { + /// Checks if account_type matches Mint discriminator value #[inline(always)] - pub fn is_cmint_account(&self) -> bool { - self.base.is_cmint_account() + pub fn is_mint_account(&self) -> bool { + self.base.is_mint_account() } /// Checks if account is initialized @@ -393,12 +392,12 @@ impl ZCompressedMint<'_> { } } -// Helper methods on ZCompressedMintMut -impl ZCompressedMintMut<'_> { - /// Checks if account_type matches CMint discriminator value +// Helper methods on ZMintMut +impl ZMintMut<'_> { + /// Checks if account_type matches Mint discriminator value #[inline(always)] - pub fn is_cmint_account(&self) -> bool { - self.base.is_cmint_account() + pub fn is_mint_account(&self) -> bool { + self.base.is_mint_account() } /// Checks if account is initialized @@ -407,13 +406,13 @@ impl ZCompressedMintMut<'_> { self.base.is_initialized() } - /// Set all fields of the CompressedMint struct at once + /// Set all fields of the Mint struct at once #[inline] #[profile] pub fn set( &mut self, - ix_data: &>::ZeroCopyAt, - cmint_decompressed: bool, + ix_data: &>::ZeroCopyAt, + mint_decompressed: bool, ) -> Result<(), TokenError> { if ix_data.metadata.version != TokenDataVersion::ShaFlat as u8 { #[cfg(feature = "solana")] @@ -426,7 +425,7 @@ impl ZCompressedMintMut<'_> { // Set metadata fields from instruction data self.base.metadata.version = ix_data.metadata.version; self.base.metadata.mint = ix_data.metadata.mint; - self.base.metadata.cmint_decompressed = if cmint_decompressed { 1 } else { 0 }; + self.base.metadata.mint_decompressed = if mint_decompressed { 1 } else { 0 }; self.base.metadata.mint_signer = ix_data.metadata.mint_signer; self.base.metadata.bump = ix_data.metadata.bump; diff --git a/program-libs/token-interface/tests/compressed_mint.rs b/program-libs/token-interface/tests/compressed_mint.rs index 5d49264753..9cea668487 100644 --- a/program-libs/token-interface/tests/compressed_mint.rs +++ b/program-libs/token-interface/tests/compressed_mint.rs @@ -2,9 +2,8 @@ use borsh::{BorshDeserialize, BorshSerialize}; use light_compressed_account::Pubkey; use light_compressible::compression_info::CompressionInfo; use light_token_interface::state::{ - cmint_top_up_lamports_from_slice, extensions::{AdditionalMetadata, ExtensionStruct, TokenMetadata}, - BaseMint, CompressedMint, CompressedMintConfig, CompressedMintMetadata, ACCOUNT_TYPE_MINT, + mint_top_up_lamports_from_slice, BaseMint, Mint, MintConfig, MintMetadata, ACCOUNT_TYPE_MINT, }; use light_zero_copy::traits::{ZeroCopyAt, ZeroCopyNew}; use rand::{thread_rng, Rng}; @@ -47,8 +46,8 @@ fn generate_random_token_metadata(rng: &mut impl Rng, mint: Pubkey) -> TokenMeta } } -/// Generate a random CompressedMint for testing -fn generate_random_compressed_mint(rng: &mut impl Rng, with_extensions: bool) -> CompressedMint { +/// Generate a random Mint for testing +fn generate_random_compressed_mint(rng: &mut impl Rng, with_extensions: bool) -> Mint { let mint = Pubkey::from(rng.gen::<[u8; 32]>()); let extensions = if with_extensions { @@ -58,7 +57,7 @@ fn generate_random_compressed_mint(rng: &mut impl Rng, with_extensions: bool) -> None }; - CompressedMint { + Mint { base: BaseMint { mint_authority: if rng.gen_bool(0.7) { Some(Pubkey::from(rng.gen::<[u8; 32]>())) @@ -74,10 +73,10 @@ fn generate_random_compressed_mint(rng: &mut impl Rng, with_extensions: bool) -> None }, }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, mint, - cmint_decompressed: rng.gen_bool(0.5), + mint_decompressed: rng.gen_bool(0.5), mint_signer: rng.gen::<[u8; 32]>(), bump: rng.gen(), }, @@ -93,7 +92,7 @@ pub struct VecTestStruct { pub opt_vec: Option>, } -/// Test that CompressedMint borsh serialization and zero-copy representations are compatible +/// Test that Mint borsh serialization and zero-copy representations are compatible #[test] fn test_compressed_mint_borsh_zerocopy_compatibility() { let test = VecTestStruct { opt_vec: None }; @@ -108,8 +107,8 @@ fn test_compressed_mint_borsh_zerocopy_compatibility() { let original_mint = generate_random_compressed_mint(&mut rng, false); let borsh_bytes = original_mint.try_to_vec().unwrap(); println!("Iteration {}: Borsh size = {} bytes", i, borsh_bytes.len()); - let borsh_deserialized = CompressedMint::deserialize_reader(&mut borsh_bytes.as_slice()) - .unwrap_or_else(|_| panic!("Failed to deserialize CompressedMint at iteration {}", i)); + let borsh_deserialized = Mint::deserialize_reader(&mut borsh_bytes.as_slice()) + .unwrap_or_else(|_| panic!("Failed to deserialize Mint at iteration {}", i)); assert_eq!( original_mint, borsh_deserialized, "Borsh roundtrip failed at iteration {}", @@ -117,16 +116,11 @@ fn test_compressed_mint_borsh_zerocopy_compatibility() { ); // Test zero-copy serialization - let config = CompressedMintConfig { extensions: None }; - let byte_len = CompressedMint::byte_len(&config).unwrap(); + let config = MintConfig { extensions: None }; + let byte_len = Mint::byte_len(&config).unwrap(); let mut zero_copy_bytes = vec![0u8; byte_len]; - let (mut zc_mint, _) = CompressedMint::new_zero_copy(&mut zero_copy_bytes, config) - .unwrap_or_else(|_| { - panic!( - "Failed to create zero-copy CompressedMint at iteration {}", - i - ) - }); + let (mut zc_mint, _) = Mint::new_zero_copy(&mut zero_copy_bytes, config) + .unwrap_or_else(|_| panic!("Failed to create zero-copy Mint at iteration {}", i)); // Set the zero-copy fields to match original zc_mint @@ -144,7 +138,7 @@ fn test_compressed_mint_borsh_zerocopy_compatibility() { .set_freeze_authority(original_mint.base.freeze_authority); zc_mint.base.metadata.version = original_mint.metadata.version; zc_mint.base.metadata.mint = original_mint.metadata.mint; - zc_mint.base.metadata.cmint_decompressed = if original_mint.metadata.cmint_decompressed { + zc_mint.base.metadata.mint_decompressed = if original_mint.metadata.mint_decompressed { 1 } else { 0 @@ -185,13 +179,12 @@ fn test_compressed_mint_borsh_zerocopy_compatibility() { original_mint.compression.rent_config.max_top_up.into(); // Now deserialize the zero-copy bytes with borsh - let zc_as_borsh = CompressedMint::deserialize(&mut zero_copy_bytes.as_slice()) - .unwrap_or_else(|_| { - panic!( - "Failed to deserialize zero-copy bytes as borsh at iteration {}", - i - ) - }); + let zc_as_borsh = Mint::deserialize(&mut zero_copy_bytes.as_slice()).unwrap_or_else(|_| { + panic!( + "Failed to deserialize zero-copy bytes as borsh at iteration {}", + i + ) + }); assert_eq!( original_mint, zc_as_borsh, "Zero-copy to borsh conversion failed at iteration {}", @@ -199,9 +192,8 @@ fn test_compressed_mint_borsh_zerocopy_compatibility() { ); // Test zero-copy read - let (zc_read, _) = CompressedMint::zero_copy_at(&zero_copy_bytes).unwrap_or_else(|_| { - panic!("Failed to read zero-copy CompressedMint at iteration {}", i) - }); + let (zc_read, _) = Mint::zero_copy_at(&zero_copy_bytes) + .unwrap_or_else(|_| panic!("Failed to read zero-copy Mint at iteration {}", i)); // Verify fields match assert_eq!( @@ -238,19 +230,19 @@ fn test_compressed_mint_borsh_zerocopy_compatibility() { i ); assert_eq!( - original_mint.metadata.cmint_decompressed, - zc_read.base.metadata.cmint_decompressed != 0, + original_mint.metadata.mint_decompressed, + zc_read.base.metadata.mint_decompressed != 0, "Is decompressed mismatch at iteration {}", i ); } } -/// Test edge cases for CompressedMint serialization +/// Test edge cases for Mint serialization #[test] fn test_compressed_mint_edge_cases() { // Test with no authorities - let mint_no_auth = CompressedMint { + let mint_no_auth = Mint { base: BaseMint { mint_authority: None, supply: u64::MAX, @@ -258,10 +250,10 @@ fn test_compressed_mint_edge_cases() { is_initialized: true, freeze_authority: None, }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, mint: Pubkey::from([0xff; 32]), - cmint_decompressed: false, + mint_decompressed: false, mint_signer: [0u8; 32], bump: 0, }, @@ -275,15 +267,15 @@ fn test_compressed_mint_edge_cases() { let bytes = mint_no_auth.try_to_vec().unwrap(); println!("Borsh serialized size: {} bytes", bytes.len()); println!("All bytes: {:?}", &bytes); - let deserialized = CompressedMint::deserialize(&mut bytes.as_slice()).unwrap(); + let deserialized = Mint::deserialize(&mut bytes.as_slice()).unwrap(); assert_eq!(mint_no_auth, deserialized); // Zero-copy roundtrip - let config = CompressedMintConfig { extensions: None }; + let config = MintConfig { extensions: None }; - let byte_len = CompressedMint::byte_len(&config).unwrap(); + let byte_len = Mint::byte_len(&config).unwrap(); let mut zc_bytes = vec![0u8; byte_len]; - let (mut zc_mint, _) = CompressedMint::new_zero_copy(&mut zc_bytes, config).unwrap(); + let (mut zc_mint, _) = Mint::new_zero_copy(&mut zc_bytes, config).unwrap(); zc_mint .base @@ -296,7 +288,7 @@ fn test_compressed_mint_edge_cases() { .set_freeze_authority(mint_no_auth.base.freeze_authority); zc_mint.base.metadata.version = mint_no_auth.metadata.version; zc_mint.base.metadata.mint = mint_no_auth.metadata.mint; - zc_mint.base.metadata.cmint_decompressed = 0; + zc_mint.base.metadata.mint_decompressed = 0; zc_mint.base.metadata.mint_signer = mint_no_auth.metadata.mint_signer; zc_mint.base.metadata.bump = mint_no_auth.metadata.bump; // account_type is already set in new_zero_copy @@ -327,11 +319,11 @@ fn test_compressed_mint_edge_cases() { zc_mint.base.compression.rent_config.max_top_up = mint_no_auth.compression.rent_config.max_top_up.into(); - let zc_as_borsh = CompressedMint::deserialize(&mut zc_bytes.as_slice()).unwrap(); + let zc_as_borsh = Mint::deserialize(&mut zc_bytes.as_slice()).unwrap(); assert_eq!(mint_no_auth, zc_as_borsh); // Test with maximum values - let mint_max = CompressedMint { + let mint_max = Mint { base: BaseMint { mint_authority: Some(Pubkey::from([0xff; 32])), supply: u64::MAX, @@ -339,10 +331,10 @@ fn test_compressed_mint_edge_cases() { is_initialized: true, freeze_authority: Some(Pubkey::from([0xaa; 32])), }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 255, mint: Pubkey::from([0xbb; 32]), - cmint_decompressed: true, + mint_decompressed: true, mint_signer: [0xcc; 32], bump: 255, }, @@ -353,14 +345,14 @@ fn test_compressed_mint_edge_cases() { }; let bytes = mint_max.try_to_vec().unwrap(); - let deserialized = CompressedMint::deserialize(&mut bytes.as_slice()).unwrap(); + let deserialized = Mint::deserialize(&mut bytes.as_slice()).unwrap(); assert_eq!(mint_max, deserialized); } -/// Test that BaseMint within CompressedMint maintains SPL compatibility format +/// Test that BaseMint within Mint maintains SPL compatibility format #[test] fn test_base_mint_in_compressed_mint_spl_format() { - let mint = CompressedMint { + let mint = Mint { base: BaseMint { mint_authority: Some(Pubkey::from([1; 32])), supply: 1000000, @@ -368,10 +360,10 @@ fn test_base_mint_in_compressed_mint_spl_format() { is_initialized: true, freeze_authority: Some(Pubkey::from([2; 32])), }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, mint: Pubkey::from([3; 32]), - cmint_decompressed: false, + mint_decompressed: false, mint_signer: [4u8; 32], bump: 255, }, @@ -381,14 +373,14 @@ fn test_base_mint_in_compressed_mint_spl_format() { extensions: None, }; - // Serialize the whole CompressedMint + // Serialize the whole Mint let full_bytes = mint.try_to_vec().unwrap(); // The BaseMint portion should be at the beginning // and should be 82 bytes (SPL Mint size) assert!( full_bytes.len() >= 82, - "Serialized CompressedMint should be at least 82 bytes" + "Serialized Mint should be at least 82 bytes" ); // Extract just the BaseMint portion @@ -401,16 +393,15 @@ fn test_base_mint_in_compressed_mint_spl_format() { #[test] fn test_compressed_mint_new_zero_copy_fails_if_already_initialized() { - let config = CompressedMintConfig { extensions: None }; - let byte_len = CompressedMint::byte_len(&config).unwrap(); + let config = MintConfig { extensions: None }; + let byte_len = Mint::byte_len(&config).unwrap(); let mut buffer = vec![0u8; byte_len]; // First initialization should succeed - let _ = CompressedMint::new_zero_copy(&mut buffer, config.clone()) - .expect("First init should succeed"); + let _ = Mint::new_zero_copy(&mut buffer, config.clone()).expect("First init should succeed"); // Second initialization should fail because account is already initialized - let result = CompressedMint::new_zero_copy(&mut buffer, config); + let result = Mint::new_zero_copy(&mut buffer, config); assert!( result.is_err(), "new_zero_copy should fail if account is already initialized" @@ -421,40 +412,38 @@ fn test_compressed_mint_new_zero_copy_fails_if_already_initialized() { ); } -/// Test that cmint_top_up_lamports_from_slice produces identical results to full deserialization. +/// Test that mint_top_up_lamports_from_slice produces identical results to full deserialization. #[test] -fn test_cmint_top_up_lamports_matches_full_deserialization() { - // Create a CMint using zero-copy - let config = CompressedMintConfig { extensions: None }; - let byte_len = CompressedMint::byte_len(&config).unwrap(); +fn test_mint_top_up_lamports_matches_full_deserialization() { + // Create a Mint using zero-copy + let config = MintConfig { extensions: None }; + let byte_len = Mint::byte_len(&config).unwrap(); let mut buffer = vec![0u8; byte_len]; - let (mut cmint, _) = CompressedMint::new_zero_copy(&mut buffer, config).unwrap(); + let (mut mint, _) = Mint::new_zero_copy(&mut buffer, config).unwrap(); // Set known values in CompressionInfo - cmint.base.compression.lamports_per_write = 1000.into(); - cmint.base.compression.last_claimed_slot = 13500.into(); // Epoch 1 - cmint.base.compression.rent_exemption_paid = 50_000.into(); - cmint.base.compression.rent_config.base_rent = 128.into(); - cmint.base.compression.rent_config.compression_cost = 11000.into(); - cmint - .base + mint.base.compression.lamports_per_write = 1000.into(); + mint.base.compression.last_claimed_slot = 13500.into(); // Epoch 1 + mint.base.compression.rent_exemption_paid = 50_000.into(); + mint.base.compression.rent_config.base_rent = 128.into(); + mint.base.compression.rent_config.compression_cost = 11000.into(); + mint.base .compression .rent_config .lamports_per_byte_per_epoch = 1; - cmint.base.compression.rent_config.max_funded_epochs = 2; + mint.base.compression.rent_config.max_funded_epochs = 2; // Test parameters let current_slot = 27000u64; // Epoch 2 let current_lamports = 100_000u64; // Calculate using optimized function - let optimized_result = - cmint_top_up_lamports_from_slice(&buffer, current_lamports, current_slot) - .expect("Should return Some"); + let optimized_result = mint_top_up_lamports_from_slice(&buffer, current_lamports, current_slot) + .expect("Should return Some"); // Calculate using full deserialization - let (cmint_read, _) = CompressedMint::zero_copy_at(&buffer).unwrap(); - let full_deser_result = cmint_read + let (mint_read, _) = Mint::zero_copy_at(&buffer).unwrap(); + let full_deser_result = mint_read .base .compression .calculate_top_up_lamports(buffer.len() as u64, current_slot, current_lamports) diff --git a/program-libs/token-interface/tests/cross_deserialization.rs b/program-libs/token-interface/tests/cross_deserialization.rs index e572fdffd9..6d25d85444 100644 --- a/program-libs/token-interface/tests/cross_deserialization.rs +++ b/program-libs/token-interface/tests/cross_deserialization.rs @@ -1,4 +1,4 @@ -//! Cross-deserialization security tests for Token and CMint accounts. +//! Cross-deserialization security tests for Token and Mint accounts. //! Verifies that account_type discriminator at byte 165 prevents confusion. //! //! With the new extension-based design: @@ -10,14 +10,14 @@ use borsh::{BorshDeserialize, BorshSerialize}; use light_compressed_account::Pubkey; use light_compressible::{compression_info::CompressionInfo, rent::RentConfig}; use light_token_interface::state::{ - AccountState, BaseMint, CompressedMint, CompressedMintMetadata, CompressibleExtension, - ExtensionStruct, Token, ACCOUNT_TYPE_MINT, ACCOUNT_TYPE_TOKEN_ACCOUNT, + AccountState, BaseMint, CompressibleExtension, ExtensionStruct, Mint, MintMetadata, Token, + ACCOUNT_TYPE_MINT, ACCOUNT_TYPE_TOKEN_ACCOUNT, }; const ACCOUNT_TYPE_OFFSET: usize = 165; -fn create_test_cmint() -> CompressedMint { - CompressedMint { +fn create_test_mint() -> Mint { + Mint { base: BaseMint { mint_authority: Some(Pubkey::new_from_array([1; 32])), supply: 1000, @@ -25,10 +25,10 @@ fn create_test_cmint() -> CompressedMint { is_initialized: true, freeze_authority: None, }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, mint: Pubkey::new_from_array([2; 32]), - cmint_decompressed: false, + mint_decompressed: false, mint_signer: [5u8; 32], bump: 255, }, @@ -113,11 +113,11 @@ fn create_test_ctoken_simple() -> Token { #[test] fn test_account_type_byte_position() { - let cmint = create_test_cmint(); - let cmint_bytes = cmint.try_to_vec().unwrap(); + let mint = create_test_mint(); + let mint_bytes = mint.try_to_vec().unwrap(); assert_eq!( - cmint_bytes[ACCOUNT_TYPE_OFFSET], 1, - "CMint account_type should be 1" + mint_bytes[ACCOUNT_TYPE_OFFSET], 1, + "Mint account_type should be 1" ); // Token with extensions has account_type byte at position 165 @@ -142,43 +142,43 @@ fn test_ctoken_without_extensions_size() { } #[test] -fn test_cmint_bytes_fail_zero_copy_checked_as_ctoken() { - let cmint = create_test_cmint(); - let cmint_bytes = cmint.try_to_vec().unwrap(); +fn test_mint_bytes_fail_zero_copy_checked_as_ctoken() { + let mint = create_test_mint(); + let mint_bytes = mint.try_to_vec().unwrap(); - // Token zero_copy_at_checked verifies account_type == 2, should fail for CMint bytes - let result = Token::zero_copy_at_checked(&cmint_bytes); + // Token zero_copy_at_checked verifies account_type == 2, should fail for Mint bytes + let result = Token::zero_copy_at_checked(&mint_bytes); assert!( result.is_err(), - "CMint bytes should fail to parse as Token zero-copy checked" + "Mint bytes should fail to parse as Token zero-copy checked" ); } #[test] -fn test_ctoken_bytes_fail_zero_copy_checked_as_cmint() { +fn test_ctoken_bytes_fail_zero_copy_checked_as_mint() { let token = create_test_ctoken_with_extension(); let ctoken_bytes = token.try_to_vec().unwrap(); - // CompressedMint zero_copy_at_checked verifies account_type == 1, should fail for Token bytes - let result = CompressedMint::zero_copy_at_checked(&ctoken_bytes); + // Mint zero_copy_at_checked verifies account_type == 1, should fail for Token bytes + let result = Mint::zero_copy_at_checked(&ctoken_bytes); assert!( result.is_err(), - "Token bytes should fail to parse as CMint zero-copy checked" + "Token bytes should fail to parse as Mint zero-copy checked" ); } #[test] -fn test_ctoken_bytes_wrong_account_type_as_cmint() { +fn test_ctoken_bytes_wrong_account_type_as_mint() { let token = create_test_ctoken_with_extension(); let ctoken_bytes = token.try_to_vec().unwrap(); - // Deserialize as CMint - should succeed but have wrong account_type - let cmint = CompressedMint::try_from_slice(&ctoken_bytes); - match cmint { + // Deserialize as Mint - should succeed but have wrong account_type + let mint = Mint::try_from_slice(&ctoken_bytes); + match mint { Ok(mint) => { assert_ne!( mint.account_type, ACCOUNT_TYPE_MINT, - "Cross-deserialized CMint should have wrong account_type" + "Cross-deserialized Mint should have wrong account_type" ); } Err(_) => { @@ -188,25 +188,25 @@ fn test_ctoken_bytes_wrong_account_type_as_cmint() { } #[test] -fn test_cmint_bytes_borsh_as_ctoken() { - let cmint = create_test_cmint(); - let cmint_bytes = cmint.try_to_vec().unwrap(); +fn test_mint_bytes_borsh_as_ctoken() { + let mint = create_test_mint(); + let mint_bytes = mint.try_to_vec().unwrap(); - // Try to deserialize CMint bytes as Token - let result = Token::try_from_slice(&cmint_bytes); + // Try to deserialize Mint bytes as Token + let result = Token::try_from_slice(&mint_bytes); // Borsh deserialization is lenient, but checked deserialization should detect the wrong type match result { Ok(token) => { // Borsh is lenient and may succeed, but is_token_account() check should fail - // because CMint has account_type = ACCOUNT_TYPE_MINT (1), not ACCOUNT_TYPE_TOKEN_ACCOUNT (2) + // because Mint has account_type = ACCOUNT_TYPE_MINT (1), not ACCOUNT_TYPE_TOKEN_ACCOUNT (2) assert!( !token.is_token_account(), - "CMint bytes deserialized as Token should fail is_token_account() check" + "Mint bytes deserialized as Token should fail is_token_account() check" ); assert_eq!( token.account_type(), ACCOUNT_TYPE_MINT, - "CMint bytes should retain ACCOUNT_TYPE_MINT discriminator" + "Mint bytes should retain ACCOUNT_TYPE_MINT discriminator" ); } Err(_) => { diff --git a/program-libs/token-interface/tests/hash_tests.rs b/program-libs/token-interface/tests/hash_tests.rs index 5c4b4c92c3..6ad1a24ca0 100644 --- a/program-libs/token-interface/tests/hash_tests.rs +++ b/program-libs/token-interface/tests/hash_tests.rs @@ -1,11 +1,11 @@ // #[cfg(test)] // mod hash_tests { // use light_compressed_account::Pubkey; -// use light_token_interface::state::{BaseMint, CompressedMint, CompressedMintMetadata}; +// use light_token_interface::state::{BaseMint, Mint, MintMetadata}; // use rand::Rng; // /// Hash Collision Detection Tests -// /// Tests for CompressedMint::hash() following hash_collision_testing_guide.md: +// /// Tests for Mint::hash() following hash_collision_testing_guide.md: // /// // /// 1. test_hash_basic_functionality - Basic functionality and determinism // /// 2. test_hash_collision_detection - Systematic field-by-field collision testing @@ -25,7 +25,7 @@ // #[test] // fn test_hash_basic_functionality() { -// let mint = CompressedMint { +// let mint = Mint { // base: BaseMint { // mint_authority: Some(Pubkey::new_unique()), // supply: 1000000, @@ -33,10 +33,10 @@ // is_initialized: true, // freeze_authority: Some(Pubkey::new_unique()), // }, -// metadata: CompressedMintMetadata { +// metadata: MintMetadata { // version: 3, // mint: Pubkey::new_unique(), -// cmint_decompressed: false, +// mint_decompressed: false, // }, // extensions: None, // }; @@ -70,7 +70,7 @@ // let mut previous_hashes = Vec::new(); // // Base configuration - choose default state for each field -// let base = CompressedMint { +// let base = Mint { // base: BaseMint { // mint_authority: None, // supply: 0, @@ -78,10 +78,10 @@ // is_initialized: true, // freeze_authority: None, // }, -// metadata: CompressedMintMetadata { +// metadata: MintMetadata { // version: 3, // mint: Pubkey::new_from_array([1u8; 32]), -// cmint_decompressed: false, +// mint_decompressed: false, // }, // extensions: None, // }; @@ -109,9 +109,9 @@ // assert_to_previous_hashes(variant.hash().unwrap(), &mut previous_hashes); // } -// // Test cmint_decompressed boolean states +// // Test mint_decompressed boolean states // let mut variant = base.clone(); -// variant.metadata.cmint_decompressed = true; // Flip from false +// variant.metadata.mint_decompressed = true; // Flip from false // assert_to_previous_hashes(variant.hash().unwrap(), &mut previous_hashes); // // Test mint_authority Option states @@ -133,7 +133,7 @@ // let mut variant = base.clone(); // variant.base.supply = 5000; // variant.base.decimals = 9; -// variant.metadata.cmint_decompressed = true; +// variant.metadata.mint_decompressed = true; // variant.base.mint_authority = Some(Pubkey::new_from_array([12u8; 32])); // variant.base.freeze_authority = Some(Pubkey::new_from_array([13u8; 32])); // variant.extensions = Some(vec![]); @@ -145,7 +145,7 @@ // let mut previous_hashes = Vec::new(); // // All fields zero/None/false (minimal state) -// let all_minimal = CompressedMint { +// let all_minimal = Mint { // base: BaseMint { // mint_authority: None, // supply: 0, @@ -153,10 +153,10 @@ // is_initialized: true, // freeze_authority: None, // }, -// metadata: CompressedMintMetadata { +// metadata: MintMetadata { // version: 3, // mint: Pubkey::new_from_array([0u8; 32]), -// cmint_decompressed: false, +// mint_decompressed: false, // }, // extensions: None, // }; @@ -176,7 +176,7 @@ // assert_to_previous_hashes(variant.hash().unwrap(), &mut previous_hashes); // variant = all_minimal.clone(); -// variant.metadata.cmint_decompressed = true; // Only this field non-false +// variant.metadata.mint_decompressed = true; // Only this field non-false // assert_to_previous_hashes(variant.hash().unwrap(), &mut previous_hashes); // variant = all_minimal.clone(); @@ -196,7 +196,7 @@ // fn test_hash_boundary_values() { // let mut previous_hashes = Vec::new(); -// let base = CompressedMint { +// let base = Mint { // base: BaseMint { // mint_authority: Some(Pubkey::new_from_array([2u8; 32])), // supply: 100, @@ -204,10 +204,10 @@ // is_initialized: true, // freeze_authority: Some(Pubkey::new_from_array([3u8; 32])), // }, -// metadata: CompressedMintMetadata { +// metadata: MintMetadata { // version: 3, // mint: Pubkey::new_from_array([1u8; 32]), -// cmint_decompressed: false, +// mint_decompressed: false, // }, // extensions: None, // }; @@ -246,7 +246,7 @@ // let mut previous_hashes = Vec::new(); // let same_pubkey = Pubkey::new_from_array([42u8; 32]); -// let base = CompressedMint { +// let base = Mint { // base: BaseMint { // mint_authority: None, // supply: 1000, @@ -254,10 +254,10 @@ // is_initialized: true, // freeze_authority: None, // }, -// metadata: CompressedMintMetadata { +// metadata: MintMetadata { // version: 3, // mint: Pubkey::new_from_array([1u8; 32]), -// cmint_decompressed: false, +// mint_decompressed: false, // }, // extensions: None, // }; @@ -323,7 +323,7 @@ // fn test_hash_some_zero_vs_none() { // let pubkey_zero = Pubkey::new_from_array([0u8; 32]); -// let base = CompressedMint { +// let base = Mint { // base: BaseMint { // mint_authority: None, // supply: 1000, @@ -331,10 +331,10 @@ // is_initialized: true, // freeze_authority: None, // }, -// metadata: CompressedMintMetadata { +// metadata: MintMetadata { // version: 3, // mint: Pubkey::new_from_array([1u8; 32]), -// cmint_decompressed: false, +// mint_decompressed: false, // }, // extensions: None, // }; @@ -389,7 +389,7 @@ // let mut all_hashes = Vec::new(); // for iteration in 0..1000 { -// let mint = CompressedMint { +// let mint = Mint { // base: BaseMint { // mint_authority: if rng.gen_bool(0.7) { // Some(Pubkey::new_from_array(rng.gen::<[u8; 32]>())) @@ -405,10 +405,10 @@ // None // }, // }, -// metadata: CompressedMintMetadata { +// metadata: MintMetadata { // version: 3, // Always version 3 // mint: Pubkey::new_from_array(rng.gen::<[u8; 32]>()), -// cmint_decompressed: rng.gen_bool(0.5), +// mint_decompressed: rng.gen_bool(0.5), // }, // extensions: if rng.gen_bool(0.3) { // Some(vec![]) // Empty extensions for now diff --git a/program-libs/token-interface/tests/mint_borsh_zero_copy.rs b/program-libs/token-interface/tests/mint_borsh_zero_copy.rs index 111cce75ad..965c0ef9f9 100644 --- a/program-libs/token-interface/tests/mint_borsh_zero_copy.rs +++ b/program-libs/token-interface/tests/mint_borsh_zero_copy.rs @@ -1,4 +1,4 @@ -// Tests compatibility between Borsh and Zero-copy serialization for CompressedMint +// Tests compatibility between Borsh and Zero-copy serialization for Mint // Verifies that both implementations correctly serialize/deserialize their data // and maintain full struct equivalence including token metadata extension. @@ -7,11 +7,11 @@ use light_compressed_account::Pubkey; use light_compressible::compression_info::CompressionInfo; use light_token_interface::state::{ extensions::{AdditionalMetadata, ExtensionStruct, TokenMetadata}, - mint::{BaseMint, CompressedMint, CompressedMintMetadata, ACCOUNT_TYPE_MINT}, + mint::{BaseMint, Mint, MintMetadata, ACCOUNT_TYPE_MINT}, }; use light_zero_copy::traits::{ZeroCopyAt, ZeroCopyAtMut}; use rand::{thread_rng, Rng}; -use spl_token_2022::{solana_program::program_pack::Pack, state::Mint}; +use spl_token_2022::{solana_program::program_pack::Pack, state::Mint as SplMint}; /// Generate random token metadata extension fn generate_random_token_metadata(rng: &mut impl Rng) -> TokenMetadata { @@ -63,8 +63,8 @@ fn generate_random_token_metadata(rng: &mut impl Rng) -> TokenMetadata { } } -/// Generate random CompressedMint for testing -fn generate_random_mint() -> CompressedMint { +/// Generate random Mint for testing +fn generate_random_mint() -> Mint { let mut rng = thread_rng(); // 40% chance to include token metadata extension @@ -75,7 +75,7 @@ fn generate_random_mint() -> CompressedMint { None }; - CompressedMint { + Mint { base: BaseMint { mint_authority: if rng.gen_bool(0.7) { let mut bytes = [0u8; 32]; @@ -95,9 +95,9 @@ fn generate_random_mint() -> CompressedMint { decimals: rng.gen_range(0..=18), is_initialized: true, }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, - cmint_decompressed: rng.gen_bool(0.5), + mint_decompressed: rng.gen_bool(0.5), mint: { let mut bytes = [0u8; 32]; rng.fill(&mut bytes); @@ -144,18 +144,18 @@ fn reconstruct_extensions( } /// Compare Borsh-serialized mint with zero-copy deserialized versions -fn compare_mint_borsh_vs_zero_copy(original: &CompressedMint, borsh_bytes: &[u8]) { +fn compare_mint_borsh_vs_zero_copy(original: &Mint, borsh_bytes: &[u8]) { // Deserialize using Borsh - let borsh_mint = CompressedMint::try_from_slice(borsh_bytes).unwrap(); + let borsh_mint = Mint::try_from_slice(borsh_bytes).unwrap(); // Deserialize using zero-copy (read-only) - let (zc_mint, _) = CompressedMint::zero_copy_at(borsh_bytes).unwrap(); + let (zc_mint, _) = Mint::zero_copy_at(borsh_bytes).unwrap(); // Reconstruct extensions from zero-copy format let zc_extensions = reconstruct_extensions(&zc_mint.extensions); - // Construct a CompressedMint from zero-copy read-only data for comparison - let zc_reconstructed = CompressedMint { + // Construct a Mint from zero-copy read-only data for comparison + let zc_reconstructed = Mint { base: BaseMint { mint_authority: zc_mint.base.mint_authority().copied(), freeze_authority: zc_mint.base.freeze_authority().copied(), @@ -163,9 +163,9 @@ fn compare_mint_borsh_vs_zero_copy(original: &CompressedMint, borsh_bytes: &[u8] decimals: zc_mint.base.decimals, is_initialized: zc_mint.base.is_initialized != 0, }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: zc_mint.base.metadata.version, - cmint_decompressed: zc_mint.base.metadata.cmint_decompressed != 0, + mint_decompressed: zc_mint.base.metadata.mint_decompressed != 0, mint: zc_mint.base.metadata.mint, mint_signer: zc_mint.base.metadata.mint_signer, bump: zc_mint.base.metadata.bump, @@ -178,10 +178,10 @@ fn compare_mint_borsh_vs_zero_copy(original: &CompressedMint, borsh_bytes: &[u8] // Test zero-copy mutable deserialization let mut mutable_bytes = borsh_bytes.to_vec(); - let (zc_mint_mut, _) = CompressedMint::zero_copy_at_mut(&mut mutable_bytes).unwrap(); + let (zc_mint_mut, _) = Mint::zero_copy_at_mut(&mut mutable_bytes).unwrap(); // Reconstruct from mutable zero-copy data for comparison - let zc_mut_reconstructed = CompressedMint { + let zc_mut_reconstructed = Mint { base: BaseMint { mint_authority: zc_mint_mut.base.mint_authority().copied(), freeze_authority: zc_mint_mut.base.freeze_authority().copied(), @@ -189,9 +189,9 @@ fn compare_mint_borsh_vs_zero_copy(original: &CompressedMint, borsh_bytes: &[u8] decimals: zc_mint_mut.base.decimals, is_initialized: zc_mint_mut.base.is_initialized != 0, }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: zc_mint_mut.base.metadata.version, - cmint_decompressed: zc_mint_mut.base.metadata.cmint_decompressed != 0, + mint_decompressed: zc_mint_mut.base.metadata.mint_decompressed != 0, mint: zc_mint_mut.base.metadata.mint, mint_signer: zc_mint_mut.base.metadata.mint_signer, bump: zc_mint_mut.base.metadata.bump, @@ -211,7 +211,7 @@ fn compare_mint_borsh_vs_zero_copy(original: &CompressedMint, borsh_bytes: &[u8] // Test SPL mint pod deserialization on base mint only // Only use the first Mint::LEN bytes for SPL deserialization - let mint = Mint::unpack(&borsh_bytes[..Mint::LEN]).unwrap(); + let mint = SplMint::unpack(&borsh_bytes[..SplMint::LEN]).unwrap(); // Reconstruct BaseMint from SPL mint for comparison let spl_reconstructed_base = BaseMint { @@ -242,11 +242,11 @@ fn test_mint_borsh_zero_copy_compatibility() { } /// Generate mint with guaranteed TokenMetadata extension -fn generate_mint_with_extensions() -> CompressedMint { +fn generate_mint_with_extensions() -> Mint { let mut rng = thread_rng(); let token_metadata = generate_random_token_metadata(&mut rng); - CompressedMint { + Mint { base: BaseMint { mint_authority: Some(Pubkey::from(rng.gen::<[u8; 32]>())), freeze_authority: Some(Pubkey::from(rng.gen::<[u8; 32]>())), @@ -254,9 +254,9 @@ fn generate_mint_with_extensions() -> CompressedMint { decimals: rng.gen_range(0..=18), is_initialized: true, }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, - cmint_decompressed: rng.gen_bool(0.5), + mint_decompressed: rng.gen_bool(0.5), mint: Pubkey::from(rng.gen::<[u8; 32]>()), mint_signer: rng.gen::<[u8; 32]>(), bump: rng.gen(), @@ -282,7 +282,7 @@ fn test_mint_with_extensions_borsh_zero_copy_compatibility() { #[test] fn test_mint_extension_edge_cases() { // Test 1: Empty strings in TokenMetadata - let mint_empty_strings = CompressedMint { + let mint_empty_strings = Mint { base: BaseMint { mint_authority: Some(Pubkey::from([1u8; 32])), freeze_authority: None, @@ -290,9 +290,9 @@ fn test_mint_extension_edge_cases() { decimals: 9, is_initialized: true, }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, - cmint_decompressed: false, + mint_decompressed: false, mint: Pubkey::from([2u8; 32]), mint_signer: [0u8; 32], bump: 0, @@ -313,7 +313,7 @@ fn test_mint_extension_edge_cases() { compare_mint_borsh_vs_zero_copy(&mint_empty_strings, &borsh_bytes); // Test 2: Maximum reasonable lengths - let mint_max_lengths = CompressedMint { + let mint_max_lengths = Mint { base: BaseMint { mint_authority: Some(Pubkey::from([0xffu8; 32])), freeze_authority: Some(Pubkey::from([0xaau8; 32])), @@ -321,9 +321,9 @@ fn test_mint_extension_edge_cases() { decimals: 18, is_initialized: true, }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, - cmint_decompressed: true, + mint_decompressed: true, mint: Pubkey::from([0xbbu8; 32]), mint_signer: [0xddu8; 32], bump: 255, @@ -357,7 +357,7 @@ fn test_mint_extension_edge_cases() { compare_mint_borsh_vs_zero_copy(&mint_max_lengths, &borsh_bytes); // Test 3: Zero update authority (represents None) - let mint_zero_authority = CompressedMint { + let mint_zero_authority = Mint { base: BaseMint { mint_authority: None, freeze_authority: None, @@ -365,9 +365,9 @@ fn test_mint_extension_edge_cases() { decimals: 0, is_initialized: true, }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, - cmint_decompressed: false, + mint_decompressed: false, mint: Pubkey::from([4u8; 32]), mint_signer: [5u8; 32], bump: 255, @@ -388,7 +388,7 @@ fn test_mint_extension_edge_cases() { compare_mint_borsh_vs_zero_copy(&mint_zero_authority, &borsh_bytes); // Test 4: No extensions (explicit None) - let mint_no_extensions = CompressedMint { + let mint_no_extensions = Mint { base: BaseMint { mint_authority: Some(Pubkey::from([5u8; 32])), freeze_authority: Some(Pubkey::from([6u8; 32])), @@ -396,9 +396,9 @@ fn test_mint_extension_edge_cases() { decimals: 6, is_initialized: true, }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, - cmint_decompressed: true, + mint_decompressed: true, mint: Pubkey::from([7u8; 32]), mint_signer: [8u8; 32], bump: 255, diff --git a/program-libs/token-interface/tests/mint_compat.rs b/program-libs/token-interface/tests/mint_compat.rs index f954638631..7ef15e520c 100644 --- a/program-libs/token-interface/tests/mint_compat.rs +++ b/program-libs/token-interface/tests/mint_compat.rs @@ -1,4 +1,4 @@ -// Tests compatibility between Light Protocol BaseCompressedMint and SPL Mint +// Tests compatibility between Light Protocol BaseMint and SPL Mint // Verifies that both implementations correctly serialize/deserialize their data // and maintain logical equivalence of mint fields. @@ -6,7 +6,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use light_compressed_account::Pubkey; use light_token_interface::state::BaseMint; use rand::{thread_rng, Rng}; -use spl_token_2022::{solana_program::program_pack::Pack, state::Mint}; +use spl_token_2022::{solana_program::program_pack::Pack, state::Mint as SplMint}; /// Generate random test data for a mint fn generate_random_mint_data() -> (Option, Option, u64, u8, bool) { @@ -50,7 +50,7 @@ fn generate_random_mint_data() -> (Option, Option, u64, u8, bool /// Compare Light and SPL mint structures for logical equivalence /// Also tests that each format can serialize/deserialize its own data correctly -fn compare_mints(light: &BaseMint, spl: &Mint, iteration: usize) { +fn compare_mints(light: &BaseMint, spl: &SplMint, iteration: usize) { // Compare supply assert_eq!( light.supply, spl.supply, @@ -95,9 +95,9 @@ fn compare_mints(light: &BaseMint, spl: &Mint, iteration: usize) { ); // Test SPL serialization roundtrip - let mut spl_bytes = vec![0u8; Mint::LEN]; - Mint::pack(*spl, &mut spl_bytes).unwrap(); - let spl_deserialized = Mint::unpack(&spl_bytes).unwrap(); + let mut spl_bytes = vec![0u8; SplMint::LEN]; + SplMint::pack(*spl, &mut spl_bytes).unwrap(); + let spl_deserialized = SplMint::unpack(&spl_bytes).unwrap(); assert_eq!( spl, &spl_deserialized, "SPL mint roundtrip failed at iteration {}", @@ -113,9 +113,9 @@ fn compare_mints(light: &BaseMint, spl: &Mint, iteration: usize) { ); assert_eq!( spl_bytes.len(), - Mint::LEN, + SplMint::LEN, "SPL serialized size should be {} at iteration {}", - Mint::LEN, + SplMint::LEN, iteration ); assert_eq!( @@ -133,7 +133,7 @@ fn compare_mints(light: &BaseMint, spl: &Mint, iteration: usize) { ); } -/// Test that borsh serialization of BaseCompressedMint fields matches SPL Mint Pack format +/// Test that borsh serialization of BaseMint fields matches SPL Mint Pack format #[test] fn test_base_mint_borsh_pack_compatibility() { for i in 0..1000 { @@ -141,7 +141,7 @@ fn test_base_mint_borsh_pack_compatibility() { let (mint_authority, freeze_authority, supply, decimals, is_initialized) = generate_random_mint_data(); - // Create Light BaseCompressedMint + // Create Light BaseMint // Note: We generate a random mint pubkey for completeness let mut spl_mint_bytes = [0u8; 32]; thread_rng().fill(&mut spl_mint_bytes); @@ -156,7 +156,7 @@ fn test_base_mint_borsh_pack_compatibility() { }; // Create SPL Mint - let mint = Mint { + let mint = SplMint { mint_authority: mint_authority .map(|p| solana_pubkey::Pubkey::from(p.to_bytes())) .into(), @@ -186,7 +186,7 @@ fn test_mint_edge_cases() { freeze_authority: None, }; - let spl_no_auth = Mint { + let spl_no_auth = SplMint { mint_authority: None.into(), supply: 1_000_000, decimals: 6, @@ -206,7 +206,7 @@ fn test_mint_edge_cases() { freeze_authority: Some(Pubkey::from([254u8; 32])), }; - let spl_max = Mint { + let spl_max = SplMint { mint_authority: Some(solana_pubkey::Pubkey::from([255u8; 32])).into(), supply: u64::MAX, decimals: 9, @@ -225,7 +225,7 @@ fn test_mint_edge_cases() { freeze_authority: None, }; - let spl_zero = Mint { + let spl_zero = SplMint { mint_authority: Some(solana_pubkey::Pubkey::from([1u8; 32])).into(), supply: 0, decimals: 0, diff --git a/program-tests/compressed-token-test/tests/light_token/approve_revoke.rs b/program-tests/compressed-token-test/tests/light_token/approve_revoke.rs index b0c1ffc04e..fd001ef881 100644 --- a/program-tests/compressed-token-test/tests/light_token/approve_revoke.rs +++ b/program-tests/compressed-token-test/tests/light_token/approve_revoke.rs @@ -6,7 +6,7 @@ //! |--------------|---------|--------| //! | SPL compat | test_approve_success_cases | test_revoke_success_cases | //! | With SPL mint | test_approve_success_cases | test_revoke_success_cases | -//! | With CMint | test_approve_revoke_compressible | test_approve_revoke_compressible | +//! | With Mint | test_approve_revoke_compressible | test_approve_revoke_compressible | //! | Invalid ctoken (non-existent) | test_approve_fails | test_revoke_fails | //! | Invalid ctoken (wrong owner) | test_approve_fails | test_revoke_fails | //! | Invalid ctoken (spl account) | test_approve_fails | test_revoke_fails | @@ -336,7 +336,7 @@ async fn test_revoke_fails() { } // ============================================================================ -// Original Compressible Test (CMint scenario with extensions) +// Original Compressible Test (Mint scenario with extensions) // ============================================================================ use anchor_lang::AnchorDeserialize; diff --git a/program-tests/compressed-token-test/tests/light_token/burn.rs b/program-tests/compressed-token-test/tests/light_token/burn.rs index 94a82715fa..973bd4dac9 100644 --- a/program-tests/compressed-token-test/tests/light_token/burn.rs +++ b/program-tests/compressed-token-test/tests/light_token/burn.rs @@ -4,15 +4,15 @@ //! //! | Test Category | Test Name | //! |--------------|-----------| -//! | With CMint (partial burn) | test_burn_success_cases | -//! | With CMint (full balance) | test_burn_success_cases | +//! | With Mint (partial burn) | test_burn_success_cases | +//! | With Mint (full balance) | test_burn_success_cases | //! | Invalid mint (wrong mint) | test_burn_fails | //! | Invalid ctoken (non-existent) | test_burn_fails | //! | Invalid ctoken (wrong owner) | test_burn_fails | //! | Insufficient balance | test_burn_fails | //! | Wrong authority | test_burn_fails | //! -//! **Note**: Burn requires a real CMint account (owned by ctoken program) for supply tracking. +//! **Note**: Burn requires a real Mint account (owned by ctoken program) for supply tracking. //! This is different from approve/revoke which only modify the Light Token account. //! //! **Note**: Max top-up exceeded test requires compressible accounts with time warp. @@ -36,7 +36,7 @@ use super::shared::*; #[tokio::test] #[serial] async fn test_burn_success_cases() { - // Test 1: Basic burn with CMint (no top-up needed) + // Test 1: Basic burn with Mint (no top-up needed) { let mut ctx = setup_burn_test().await; let burn_amount = 50u64; @@ -44,7 +44,7 @@ async fn test_burn_success_cases() { // Burn 50 tokens let burn_ix = Burn { source: ctx.ctoken_account, - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, amount: burn_amount, authority: ctx.owner_keypair.pubkey(), max_top_up: None, @@ -62,7 +62,7 @@ async fn test_burn_success_cases() { .unwrap(); // Assert burn was successful using assert_ctoken_burn - assert_ctoken_burn(&mut ctx.rpc, ctx.ctoken_account, ctx.cmint_pda, burn_amount).await; + assert_ctoken_burn(&mut ctx.rpc, ctx.ctoken_account, ctx.mint_pda, burn_amount).await; println!("test_burn_success_cases: basic burn passed"); } @@ -75,7 +75,7 @@ async fn test_burn_success_cases() { // Burn all 100 tokens let burn_ix = Burn { source: ctx.ctoken_account, - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, amount: burn_amount, authority: ctx.owner_keypair.pubkey(), max_top_up: None, @@ -93,7 +93,7 @@ async fn test_burn_success_cases() { .unwrap(); // Assert burn was successful using assert_ctoken_burn - assert_ctoken_burn(&mut ctx.rpc, ctx.ctoken_account, ctx.cmint_pda, burn_amount).await; + assert_ctoken_burn(&mut ctx.rpc, ctx.ctoken_account, ctx.mint_pda, burn_amount).await; println!("test_burn_success_cases: burn full balance passed"); } @@ -114,18 +114,18 @@ mod error_codes { #[tokio::test] #[serial] async fn test_burn_fails() { - // Test 1: Invalid mint - wrong mint (different CMint) + // Test 1: Invalid mint - wrong mint (different Mint) { let mut ctx = setup_burn_test().await; - // Create a different CMint + // Create a different Mint let other_mint_seed = Keypair::new(); - let (other_cmint_pda, _) = find_mint_address(&other_mint_seed.pubkey()); + let (other_mint_pda, _) = find_mint_address(&other_mint_seed.pubkey()); // Try to burn with wrong mint let burn_ix = Burn { source: ctx.ctoken_account, - cmint: other_cmint_pda, // Wrong mint + mint: other_mint_pda, // Wrong mint amount: 50, authority: ctx.owner_keypair.pubkey(), max_top_up: None, @@ -142,7 +142,7 @@ async fn test_burn_fails() { ) .await; - // Non-existent CMint returns NotRentExempt (SPL Token code 0 -> 6153) + // Non-existent Mint returns NotRentExempt (SPL Token code 0 -> 6153) assert_rpc_error(result, 0, 6153).unwrap(); println!("test_burn_fails: wrong mint passed"); } @@ -155,7 +155,7 @@ async fn test_burn_fails() { let burn_ix = Burn { source: non_existent, - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, amount: 50, authority: ctx.owner_keypair.pubkey(), max_top_up: None, @@ -201,7 +201,7 @@ async fn test_burn_fails() { let burn_ix = Burn { source: wrong_owner_account.pubkey(), - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, amount: 50, authority: ctx.owner_keypair.pubkey(), max_top_up: None, @@ -231,7 +231,7 @@ async fn test_burn_fails() { // Try to burn more than balance (100 tokens) let burn_ix = Burn { source: ctx.ctoken_account, - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, amount: 200, // More than 100 balance authority: ctx.owner_keypair.pubkey(), max_top_up: None, @@ -266,7 +266,7 @@ async fn test_burn_fails() { let burn_ix = Burn { source: ctx.ctoken_account, - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, amount: 50, authority: wrong_authority.pubkey(), max_top_up: None, @@ -303,16 +303,16 @@ async fn test_burn_fails() { struct BurnTestContext { rpc: LightProgramTest, payer: Keypair, - cmint_pda: Pubkey, + mint_pda: Pubkey, ctoken_account: Pubkey, owner_keypair: Keypair, } -/// Setup: Create CMint + Light Token with 100 tokens +/// Setup: Create Mint + Light Token with 100 tokens /// /// Steps: /// 1. Init LightProgramTest -/// 2. Create compressed mint + CMint via mint_action_comprehensive +/// 2. Create compressed mint + Mint via mint_action_comprehensive /// 3. Create Light Token ATA /// 4. Mint 100 tokens async fn setup_burn_test() -> BurnTestContext { @@ -325,14 +325,14 @@ async fn setup_burn_test() -> BurnTestContext { let mint_authority = payer.insecure_clone(); let owner_keypair = Keypair::new(); - // Derive CMint PDA - let (cmint_pda, _) = find_mint_address(&mint_seed.pubkey()); + // Derive Mint PDA + let (mint_pda, _) = find_mint_address(&mint_seed.pubkey()); // Step 1: Create Light Token ATA for owner - let (ctoken_ata, _) = derive_token_ata(&owner_keypair.pubkey(), &cmint_pda); + let (ctoken_ata, _) = derive_token_ata(&owner_keypair.pubkey(), &mint_pda); let create_ata_ix = - CreateAssociatedTokenAccount::new(payer.pubkey(), owner_keypair.pubkey(), cmint_pda) + CreateAssociatedTokenAccount::new(payer.pubkey(), owner_keypair.pubkey(), mint_pda) .instruction() .unwrap(); @@ -340,13 +340,13 @@ async fn setup_burn_test() -> BurnTestContext { .await .unwrap(); - // Step 2: Create compressed mint + CMint (no recipients) + // Step 2: Create compressed mint + Mint (no recipients) light_token_client::actions::mint_action_comprehensive( &mut rpc, &mint_seed, &mint_authority, &payer, - Some(DecompressMintParams::default()), // Creates CMint + Some(DecompressMintParams::default()), // Creates Mint false, // Don't compress and close vec![], // No compressed recipients vec![], // No ctoken recipients @@ -366,7 +366,7 @@ async fn setup_burn_test() -> BurnTestContext { // Step 3: Mint 100 tokens to the Light Token account let mint_ix = MintTo { - cmint: cmint_pda, + mint: mint_pda, destination: ctoken_ata, amount: 100, authority: mint_authority.pubkey(), @@ -387,7 +387,7 @@ async fn setup_burn_test() -> BurnTestContext { BurnTestContext { rpc, payer, - cmint_pda, + mint_pda, ctoken_account: ctoken_ata, owner_keypair, } @@ -411,7 +411,7 @@ async fn test_burn_checked_success() { // Burn 50 tokens with correct decimals (8) let burn_ix = BurnChecked { source: ctx.ctoken_account, - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, amount: burn_amount, decimals: 8, // Correct decimals authority: ctx.owner_keypair.pubkey(), @@ -430,7 +430,7 @@ async fn test_burn_checked_success() { .unwrap(); // Assert burn was successful using assert_ctoken_burn - assert_ctoken_burn(&mut ctx.rpc, ctx.ctoken_account, ctx.cmint_pda, burn_amount).await; + assert_ctoken_burn(&mut ctx.rpc, ctx.ctoken_account, ctx.mint_pda, burn_amount).await; println!("test_burn_checked_success: passed"); } @@ -443,7 +443,7 @@ async fn test_burn_checked_wrong_decimals() { // Try to burn with wrong decimals (7 instead of 8) let burn_ix = BurnChecked { source: ctx.ctoken_account, - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, amount: 50, decimals: 7, // Wrong decimals authority: ctx.owner_keypair.pubkey(), diff --git a/program-tests/compressed-token-test/tests/light_token/spl_instruction_compat.rs b/program-tests/compressed-token-test/tests/light_token/spl_instruction_compat.rs index e786162273..5fe336f002 100644 --- a/program-tests/compressed-token-test/tests/light_token/spl_instruction_compat.rs +++ b/program-tests/compressed-token-test/tests/light_token/spl_instruction_compat.rs @@ -413,9 +413,9 @@ async fn test_spl_instruction_compatibility() { println!(" - All SPL token instructions are compatible with ctoken program"); } -/// Test SPL token instruction compatibility with ctoken program using decompressed cmint +/// Test SPL token instruction compatibility with ctoken program using decompressed mint /// -/// This test uses a real decompressed cmint to test instructions that require mint data: +/// This test uses a real decompressed mint to test instructions that require mint data: /// - transfer_checked, /// - mint_to, mint_to_checked (require mint authority) /// - burn, burn_checked (require token burning) @@ -423,7 +423,7 @@ async fn test_spl_instruction_compatibility() { #[tokio::test] #[serial] #[allow(deprecated)] -async fn test_spl_instruction_compatibility_with_cmint() { +async fn test_spl_instruction_compatibility_with_mint() { use light_program_test::ProgramTestConfig; use light_token_client::instructions::mint_action::DecompressMintParams; use light_token_sdk::compressed_token::create_compressed_mint::find_mint_address; @@ -439,19 +439,19 @@ async fn test_spl_instruction_compatibility_with_cmint() { let freeze_authority = Keypair::new(); let owner_keypair = Keypair::new(); - // Derive CMint PDA - let (cmint_pda, _) = find_mint_address(&mint_seed.pubkey()); + // Derive Mint PDA + let (mint_pda, _) = find_mint_address(&mint_seed.pubkey()); let decimals: u8 = 8; - println!("Creating decompressed cmint with freeze authority..."); + println!("Creating decompressed mint with freeze authority..."); - // Create compressed mint + CMint (decompressed mint) + // Create compressed mint + Mint (decompressed mint) light_token_client::actions::mint_action_comprehensive( &mut rpc, &mint_seed, &mint_authority, &payer, - Some(DecompressMintParams::default()), // Creates CMint + Some(DecompressMintParams::default()), // Creates Mint false, // Don't compress and close vec![], // No compressed recipients vec![], // No ctoken recipients @@ -469,7 +469,7 @@ async fn test_spl_instruction_compatibility_with_cmint() { .await .unwrap(); - println!("CMint created at: {}", cmint_pda); + println!("Mint created at: {}", mint_pda); // Create two non-compressible Light Token accounts (165 bytes) using SPL instruction format let account1_keypair = Keypair::new(); @@ -504,7 +504,7 @@ async fn test_spl_instruction_compatibility_with_cmint() { let mut init_ix = spl_token_2022::instruction::initialize_account3( &spl_token_2022::ID, &account1_keypair.pubkey(), - &cmint_pda, + &mint_pda, &owner_keypair.pubkey(), ) .unwrap(); @@ -546,7 +546,7 @@ async fn test_spl_instruction_compatibility_with_cmint() { let mut init_ix = spl_token_2022::instruction::initialize_account3( &spl_token_2022::ID, &account2_keypair.pubkey(), - &cmint_pda, + &mint_pda, &owner_keypair.pubkey(), ) .unwrap(); @@ -565,7 +565,7 @@ async fn test_spl_instruction_compatibility_with_cmint() { { let mut mint_to_ix = spl_token_2022::instruction::mint_to( &spl_token_2022::ID, - &cmint_pda, + &mint_pda, &account1_keypair.pubkey(), &mint_authority.pubkey(), &[], @@ -600,7 +600,7 @@ async fn test_spl_instruction_compatibility_with_cmint() { { let mut mint_to_checked_ix = spl_token_2022::instruction::mint_to_checked( &spl_token_2022::ID, - &cmint_pda, + &mint_pda, &account1_keypair.pubkey(), &mint_authority.pubkey(), &[], @@ -641,7 +641,7 @@ async fn test_spl_instruction_compatibility_with_cmint() { let mut transfer_checked_ix = spl_token_2022::instruction::transfer_checked( &spl_token_2022::ID, &account1_keypair.pubkey(), - &cmint_pda, + &mint_pda, &account2_keypair.pubkey(), &owner_keypair.pubkey(), &[], @@ -691,7 +691,7 @@ async fn test_spl_instruction_compatibility_with_cmint() { let mut freeze_ix = spl_token_2022::instruction::freeze_account( &spl_token_2022::ID, &account1_keypair.pubkey(), - &cmint_pda, + &mint_pda, &freeze_authority.pubkey(), &[], ) @@ -726,7 +726,7 @@ async fn test_spl_instruction_compatibility_with_cmint() { let mut thaw_ix = spl_token_2022::instruction::thaw_account( &spl_token_2022::ID, &account1_keypair.pubkey(), - &cmint_pda, + &mint_pda, &freeze_authority.pubkey(), &[], ) @@ -761,7 +761,7 @@ async fn test_spl_instruction_compatibility_with_cmint() { let mut burn_ix = spl_token_2022::instruction::burn( &spl_token_2022::ID, &account1_keypair.pubkey(), - &cmint_pda, + &mint_pda, &owner_keypair.pubkey(), &[], 100, @@ -796,7 +796,7 @@ async fn test_spl_instruction_compatibility_with_cmint() { let mut burn_checked_ix = spl_token_2022::instruction::burn_checked( &spl_token_2022::ID, &account1_keypair.pubkey(), - &cmint_pda, + &mint_pda, &owner_keypair.pubkey(), &[], 100, @@ -829,8 +829,8 @@ async fn test_spl_instruction_compatibility_with_cmint() { println!("burn_checked completed successfully"); } - println!("\nSPL instruction compatibility with CMint test passed!"); - println!(" - Created 2 non-compressible Light Token accounts with CMint"); + println!("\nSPL instruction compatibility with Mint test passed!"); + println!(" - Created 2 non-compressible Light Token accounts with Mint"); println!(" - mint_to: Minted 1000 tokens"); println!(" - mint_to_checked: Minted 500 tokens with decimals validation"); println!(" - transfer_checked: Transferred 500 tokens with decimals validation"); diff --git a/program-tests/compressed-token-test/tests/mint/burn.rs b/program-tests/compressed-token-test/tests/mint/burn.rs index 944c4072ee..f40d29667f 100644 --- a/program-tests/compressed-token-test/tests/mint/burn.rs +++ b/program-tests/compressed-token-test/tests/mint/burn.rs @@ -13,16 +13,16 @@ use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; struct BurnTestContext { rpc: LightProgramTest, payer: Keypair, - cmint_pda: Pubkey, + mint_pda: Pubkey, ctoken_account: Pubkey, owner_keypair: Keypair, } -/// Setup: Create CMint + Light Token with tokens minted +/// Setup: Create Mint + Light Token with tokens minted /// /// Steps: /// 1. Init LightProgramTest -/// 2. Create compressed mint + CMint via mint_action_comprehensive +/// 2. Create compressed mint + Mint via mint_action_comprehensive /// 3. Create Light Token ATA with compressible extension /// 4. Mint tokens to Light Token via mint_action_comprehensive async fn setup_burn_test(mint_amount: u64) -> BurnTestContext { @@ -36,14 +36,14 @@ async fn setup_burn_test(mint_amount: u64) -> BurnTestContext { let mint_authority = payer.insecure_clone(); let owner_keypair = Keypair::new(); - // Derive CMint PDA - let (cmint_pda, _) = find_mint_address(&mint_seed.pubkey()); + // Derive Mint PDA + let (mint_pda, _) = find_mint_address(&mint_seed.pubkey()); // Step 1: Create Light Token ATA for owner first (needed before minting) - let (ctoken_ata, _) = derive_token_ata(&owner_keypair.pubkey(), &cmint_pda); + let (ctoken_ata, _) = derive_token_ata(&owner_keypair.pubkey(), &mint_pda); let create_ata_ix = - CreateAssociatedTokenAccount::new(payer.pubkey(), owner_keypair.pubkey(), cmint_pda) + CreateAssociatedTokenAccount::new(payer.pubkey(), owner_keypair.pubkey(), mint_pda) .instruction() .unwrap(); @@ -51,13 +51,13 @@ async fn setup_burn_test(mint_amount: u64) -> BurnTestContext { .await .unwrap(); - // Step 2: Create compressed mint + CMint + mint tokens in one call + // Step 2: Create compressed mint + Mint + mint tokens in one call light_token_client::actions::mint_action_comprehensive( &mut rpc, &mint_seed, &mint_authority, &payer, - Some(DecompressMintParams::default()), // Creates CMint + Some(DecompressMintParams::default()), // Creates Mint false, // Don't compress and close vec![], // No compressed recipients vec![Recipient { @@ -81,7 +81,7 @@ async fn setup_burn_test(mint_amount: u64) -> BurnTestContext { BurnTestContext { rpc, payer, - cmint_pda, + mint_pda, ctoken_account: ctoken_ata, owner_keypair, } @@ -96,7 +96,7 @@ async fn test_ctoken_burn() { // First burn: 500 tokens (half) let burn_ix_1 = Burn { source: ctx.ctoken_account, - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, amount: 500, authority: ctx.owner_keypair.pubkey(), max_top_up: None, @@ -113,12 +113,12 @@ async fn test_ctoken_burn() { .await .unwrap(); - assert_ctoken_burn(&mut ctx.rpc, ctx.ctoken_account, ctx.cmint_pda, 500).await; + assert_ctoken_burn(&mut ctx.rpc, ctx.ctoken_account, ctx.mint_pda, 500).await; // Second burn: 500 tokens (remaining half) let burn_ix_2 = Burn { source: ctx.ctoken_account, - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, amount: 500, authority: ctx.owner_keypair.pubkey(), max_top_up: None, @@ -135,7 +135,7 @@ async fn test_ctoken_burn() { .await .unwrap(); - assert_ctoken_burn(&mut ctx.rpc, ctx.ctoken_account, ctx.cmint_pda, 500).await; + assert_ctoken_burn(&mut ctx.rpc, ctx.ctoken_account, ctx.mint_pda, 500).await; // Verify final balance is 0 use anchor_lang::prelude::borsh::BorshDeserialize; diff --git a/program-tests/compressed-token-test/tests/mint/cpi_context.rs b/program-tests/compressed-token-test/tests/mint/cpi_context.rs index 7293c34c0a..3c08b16e55 100644 --- a/program-tests/compressed-token-test/tests/mint/cpi_context.rs +++ b/program-tests/compressed-token-test/tests/mint/cpi_context.rs @@ -6,10 +6,10 @@ use light_program_test::{utils::assert::assert_rpc_error, LightProgramTest, Prog use light_test_utils::Rpc; use light_token_interface::{ instructions::mint_action::{ - CompressedMintInstructionData, CompressedMintWithContext, CpiContext, DecompressMintAction, - MintActionCompressedInstructionData, MintToAction, + CpiContext, DecompressMintAction, MintActionCompressedInstructionData, MintInstructionData, + MintToAction, MintWithContext, }, - state::CompressedMintMetadata, + state::MintMetadata, CMINT_ADDRESS_TREE, LIGHT_TOKEN_PROGRAM_ID, }; use light_token_sdk::compressed_token::{ @@ -29,7 +29,7 @@ use solana_sdk::{ struct TestSetup { rpc: LightProgramTest, - compressed_mint_inputs: CompressedMintWithContext, + compressed_mint_inputs: MintWithContext, payer: Keypair, mint_seed: Keypair, mint_authority: Keypair, @@ -72,17 +72,17 @@ async fn test_setup() -> TestSetup { // 3. Build compressed mint inputs let (_, bump) = find_mint_address(&mint_seed.pubkey()); - let compressed_mint_inputs = CompressedMintWithContext { + let compressed_mint_inputs = MintWithContext { leaf_index: 0, prove_by_index: false, root_index: 0, address: compressed_mint_address, - mint: Some(CompressedMintInstructionData { + mint: Some(MintInstructionData { supply: 0, decimals, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, - cmint_decompressed: false, + mint_decompressed: false, mint: spl_mint_pda.into(), mint_signer: mint_seed.pubkey().to_bytes(), bump, @@ -521,8 +521,8 @@ async fn test_write_to_cpi_context_decompressed_mint_fails() { } = test_setup().await; // Build instruction data with mint = None (simulates decompressed mint) - // This triggers cmint_decompressed = true in AccountsConfig - let mint_with_context = CompressedMintWithContext { + // This triggers mint_decompressed = true in AccountsConfig + let mint_with_context = MintWithContext { leaf_index: 0, prove_by_index: false, root_index: 0, diff --git a/program-tests/compressed-token-test/tests/mint/edge_cases.rs b/program-tests/compressed-token-test/tests/mint/edge_cases.rs index 34c7adcf7d..37008a658d 100644 --- a/program-tests/compressed-token-test/tests/mint/edge_cases.rs +++ b/program-tests/compressed-token-test/tests/mint/edge_cases.rs @@ -8,9 +8,7 @@ use light_token_client::{ actions::create_mint, instructions::mint_action::{MintActionType, MintToRecipient}, }; -use light_token_interface::state::{ - extensions::AdditionalMetadata, CompressedMint, TokenDataVersion, -}; +use light_token_interface::state::{extensions::AdditionalMetadata, Mint, TokenDataVersion}; use light_token_sdk::{ compressed_token::create_compressed_mint::{derive_mint_compressed_address, find_mint_address}, token::{CompressibleParams, CreateAssociatedTokenAccount}, @@ -244,7 +242,7 @@ async fn functional_all_in_one_instruction() { .value .unwrap(); - let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let pre_compressed_mint: Mint = BorshDeserialize::deserialize( &mut pre_compressed_mint_account.data.unwrap().data.as_slice(), ) .unwrap(); diff --git a/program-tests/compressed-token-test/tests/mint/failing.rs b/program-tests/compressed-token-test/tests/mint/failing.rs index a19b80c818..c798bf1cb8 100644 --- a/program-tests/compressed-token-test/tests/mint/failing.rs +++ b/program-tests/compressed-token-test/tests/mint/failing.rs @@ -10,7 +10,7 @@ use light_token_client::{ actions::create_mint, instructions::mint_action::{MintActionType, MintToRecipient}, }; -use light_token_interface::state::{extensions::AdditionalMetadata, CompressedMint}; +use light_token_interface::state::{extensions::AdditionalMetadata, Mint}; use light_token_sdk::{ compressed_token::create_compressed_mint::{derive_mint_compressed_address, find_mint_address}, token::{CompressibleParams, CreateAssociatedTokenAccount}, @@ -116,7 +116,7 @@ async fn functional_and_failing_tests() { // 2. FAIL - Create mint with duplicate metadata keys { let duplicate_mint_seed = Keypair::new(); - let result = create_mint( + let result = light_token_client::actions::create_mint( &mut rpc, &duplicate_mint_seed, // Use new mint seed 8, // decimals @@ -218,7 +218,7 @@ async fn functional_and_failing_tests() { .unwrap() .value .unwrap(); - let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let pre_compressed_mint: Mint = BorshDeserialize::deserialize( &mut pre_compressed_mint_account.data.unwrap().data.as_slice(), ) .unwrap(); @@ -296,7 +296,7 @@ async fn functional_and_failing_tests() { .unwrap() .value .unwrap(); - let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let pre_compressed_mint: Mint = BorshDeserialize::deserialize( &mut compressed_mint_account.data.unwrap().data.as_slice(), ) .unwrap(); @@ -367,7 +367,7 @@ async fn functional_and_failing_tests() { .unwrap() .value .unwrap(); - let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let pre_compressed_mint: Mint = BorshDeserialize::deserialize( &mut compressed_mint_account.data.unwrap().data.as_slice(), ) .unwrap(); @@ -419,7 +419,7 @@ async fn functional_and_failing_tests() { &invalid_mint_authority, // Invalid authority &payer, None, // decompress_mint - false, // compress_and_close_cmint + false, // compress_and_close_mint vec![], // No compressed recipients vec![ light_token_interface::instructions::mint_action::Recipient::new( @@ -450,7 +450,7 @@ async fn functional_and_failing_tests() { .unwrap() .value .unwrap(); - let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let pre_compressed_mint: Mint = BorshDeserialize::deserialize( &mut pre_compressed_mint_account.data.unwrap().data.as_slice(), ) .unwrap(); @@ -477,7 +477,7 @@ async fn functional_and_failing_tests() { &new_mint_authority, // Valid NEW authority after update &payer, None, // decompress_mint - false, // compress_and_close_cmint + false, // compress_and_close_mint vec![], // No compressed recipients vec![ light_token_interface::instructions::mint_action::Recipient::new( @@ -546,7 +546,7 @@ async fn functional_and_failing_tests() { .unwrap() .value .unwrap(); - let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let pre_compressed_mint: Mint = BorshDeserialize::deserialize( &mut pre_compressed_mint_account.data.unwrap().data.as_slice(), ) .unwrap(); @@ -627,7 +627,7 @@ async fn functional_and_failing_tests() { .unwrap() .value .unwrap(); - let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let pre_compressed_mint: Mint = BorshDeserialize::deserialize( &mut pre_compressed_mint_account.data.unwrap().data.as_slice(), ) .unwrap(); @@ -707,7 +707,7 @@ async fn functional_and_failing_tests() { .unwrap() .value .unwrap(); - let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let pre_compressed_mint: Mint = BorshDeserialize::deserialize( &mut pre_compressed_mint_account.data.unwrap().data.as_slice(), ) .unwrap(); @@ -760,7 +760,7 @@ async fn functional_and_failing_tests() { .unwrap() .value .unwrap(); - let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let pre_compressed_mint: Mint = BorshDeserialize::deserialize( &mut pre_compressed_mint_account.data.unwrap().data.as_slice(), ) .unwrap(); @@ -813,7 +813,7 @@ async fn test_mint_to_ctoken_max_top_up_exceeded() { use light_compressed_account::instruction_data::traits::LightInstructionData; use light_token_interface::{ instructions::mint_action::{ - CompressedMintWithContext, MintActionCompressedInstructionData, MintToAction, + MintActionCompressedInstructionData, MintToAction, MintWithContext, }, state::TokenDataVersion, LIGHT_TOKEN_PROGRAM_ID, @@ -843,7 +843,7 @@ async fn test_mint_to_ctoken_max_top_up_exceeded() { let (spl_mint_pda, _) = find_mint_address(&mint_seed.pubkey()); // 1. Create compressed mint - light_token_client::actions::create_mint( + create_mint( &mut rpc, &mint_seed, 8, // decimals @@ -894,7 +894,7 @@ async fn test_mint_to_ctoken_max_top_up_exceeded() { .value .unwrap(); - let compressed_mint: light_token_interface::state::CompressedMint = + let compressed_mint: light_token_interface::state::Mint = BorshDeserialize::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) .unwrap(); @@ -905,7 +905,7 @@ async fn test_mint_to_ctoken_max_top_up_exceeded() { .unwrap() .value; - let compressed_mint_inputs = CompressedMintWithContext { + let compressed_mint_inputs = MintWithContext { prove_by_index: rpc_proof_result.accounts[0].root_index.proof_by_index(), leaf_index: compressed_mint_account.leaf_index, root_index: rpc_proof_result.accounts[0] @@ -1017,11 +1017,11 @@ async fn test_create_mint_non_signer_mint_signer() { .unwrap(); } -/// Test that CompressAndCloseCMint must be the only action in the instruction. -/// Attempting to combine CompressAndCloseCMint with UpdateMintAuthority should fail. +/// Test that CompressAndCloseMint must be the only action in the instruction. +/// Attempting to combine CompressAndCloseMint with UpdateMintAuthority should fail. #[tokio::test] #[serial] -async fn test_compress_and_close_cmint_must_be_only_action() { +async fn test_compress_and_close_mint_must_be_only_action() { use light_compressible::rent::SLOTS_PER_EPOCH; use light_program_test::program_test::TestRpc; use light_token_client::instructions::mint_action::DecompressMintParams; @@ -1046,7 +1046,7 @@ async fn test_compress_and_close_cmint_must_be_only_action() { let compressed_mint_address = derive_mint_compressed_address(&mint_seed.pubkey(), &address_tree_pubkey); - // 1. Create compressed mint with CMint (decompressed) + // 1. Create compressed mint with Mint (decompressed) light_token_client::actions::mint_action_comprehensive( &mut rpc, &mint_seed, @@ -1073,7 +1073,7 @@ async fn test_compress_and_close_cmint_must_be_only_action() { // Warp to epoch 2 so that rent expires rpc.warp_to_slot(SLOTS_PER_EPOCH * 2).unwrap(); - // 2. Try to combine CompressAndCloseCMint with UpdateMintAuthority + // 2. Try to combine CompressAndCloseMint with UpdateMintAuthority let new_authority = Keypair::new(); let result = light_token_client::actions::mint_action( &mut rpc, @@ -1083,7 +1083,7 @@ async fn test_compress_and_close_cmint_must_be_only_action() { authority: mint_authority.pubkey(), payer: payer.pubkey(), actions: vec![ - MintActionType::CompressAndCloseCMint { idempotent: false }, + MintActionType::CompressAndCloseMint { idempotent: false }, MintActionType::UpdateMintAuthority { new_authority: Some(new_authority.pubkey()), }, @@ -1096,9 +1096,9 @@ async fn test_compress_and_close_cmint_must_be_only_action() { ) .await; - // Should fail with CompressAndCloseCMintMustBeOnlyAction (error code 6169) + // Should fail with CompressAndCloseMintMustBeOnlyAction (error code 6169) assert_rpc_error( - result, 0, 6169, // CompressAndCloseCMintMustBeOnlyAction + result, 0, 6169, // CompressAndCloseMintMustBeOnlyAction ) .unwrap(); } diff --git a/program-tests/compressed-token-test/tests/mint/functional.rs b/program-tests/compressed-token-test/tests/mint/functional.rs index bc9c129f28..4b441cc1d1 100644 --- a/program-tests/compressed-token-test/tests/mint/functional.rs +++ b/program-tests/compressed-token-test/tests/mint/functional.rs @@ -28,8 +28,8 @@ use light_token_interface::{ extensions::token_metadata::TokenMetadataInstructionData, mint_action::Recipient, }, state::{ - extensions::AdditionalMetadata, BaseMint, CompressedMint, CompressedMintMetadata, - TokenDataVersion, ACCOUNT_TYPE_MINT, + extensions::AdditionalMetadata, BaseMint, Mint, MintMetadata, TokenDataVersion, + ACCOUNT_TYPE_MINT, }, COMPRESSED_MINT_SEED, }; @@ -123,7 +123,7 @@ async fn test_create_compressed_mint() { .unwrap() .value .unwrap(); - let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let pre_compressed_mint: Mint = BorshDeserialize::deserialize( &mut pre_compressed_mint_account.data.unwrap().data.as_slice(), ) .unwrap(); @@ -585,8 +585,7 @@ async fn test_update_compressed_mint_authority() { .value .unwrap(); let compressed_mint = - CompressedMint::deserialize(&mut &compressed_mint_account.data.as_ref().unwrap().data[..]) - .unwrap(); + Mint::deserialize(&mut &compressed_mint_account.data.as_ref().unwrap().data[..]).unwrap(); println!("compressed_mint {:?}", compressed_mint); assert_eq!( compressed_mint.base.mint_authority.unwrap(), @@ -612,8 +611,7 @@ async fn test_update_compressed_mint_authority() { .value .unwrap(); let compressed_mint = - CompressedMint::deserialize(&mut &compressed_mint_account.data.as_ref().unwrap().data[..]) - .unwrap(); + Mint::deserialize(&mut &compressed_mint_account.data.as_ref().unwrap().data[..]).unwrap(); println!("compressed_mint {:?}", compressed_mint); assert_eq!( compressed_mint.base.freeze_authority.unwrap(), @@ -725,7 +723,7 @@ async fn test_ctoken_transfer() { &mint_authority, &payer, None, // decompress_mint - false, // compress_and_close_cmint + false, // compress_and_close_mint vec![], // no compressed recipients decompressed_recipients, // mint to decompressed recipients None, // no mint authority update @@ -1032,7 +1030,7 @@ async fn test_create_compressed_mint_with_token_metadata() { uri: b"https://example.com/token.json".to_vec(), additional_metadata: Some(additional_metadata.clone()), }; - light_token_client::actions::create_mint( + create_mint( &mut rpc, &mint_seed, decimals, @@ -1085,7 +1083,7 @@ async fn test_create_compressed_mint_with_token_metadata() { .unwrap() .value .unwrap(); - let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let pre_compressed_mint: Mint = BorshDeserialize::deserialize( &mut pre_compressed_mint_account.data.unwrap().data.as_slice(), ) .unwrap(); @@ -1177,7 +1175,7 @@ async fn test_mint_actions() { &mint_authority, &payer, None, // decompress_mint - false, // compress_and_close_cmint + false, // compress_and_close_mint recipients.clone(), // mint_to_recipients vec![], // mint_to_decompressed_recipients Some(new_mint_authority.pubkey()), // update_mint_authority @@ -1209,7 +1207,7 @@ async fn test_mint_actions() { // Create empty pre-states since everything was created from scratch let (_, mint_bump) = find_mint_address(&mint_seed.pubkey()); - let empty_pre_compressed_mint = CompressedMint { + let empty_pre_compressed_mint = Mint { base: BaseMint { mint_authority: Some(new_mint_authority.pubkey().into()), supply: 0, @@ -1217,10 +1215,10 @@ async fn test_mint_actions() { is_initialized: true, freeze_authority: Some(freeze_authority.pubkey().into()), // We didn't update freeze authority }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, // With metadata mint: spl_mint_pda.into(), - cmint_decompressed: false, // Becomes true after DecompressMint action + mint_decompressed: false, // Becomes true after DecompressMint action mint_signer: mint_seed.pubkey().to_bytes(), bump: mint_bump, }, @@ -1258,7 +1256,7 @@ async fn test_mint_actions() { .unwrap() .value .unwrap(); - let updated_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let updated_compressed_mint: Mint = BorshDeserialize::deserialize( &mut updated_compressed_mint_account .data .unwrap() @@ -1278,7 +1276,7 @@ async fn test_mint_actions() { "Supply should match minted amount" ); assert!( - !updated_compressed_mint.metadata.cmint_decompressed, + !updated_compressed_mint.metadata.mint_decompressed, "Mint should not be decompressed " ); @@ -1292,7 +1290,7 @@ async fn test_mint_actions() { .unwrap() .value .unwrap(); - let current_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let current_compressed_mint: Mint = BorshDeserialize::deserialize( &mut current_compressed_mint_account .data .unwrap() @@ -1332,7 +1330,7 @@ async fn test_mint_actions() { &new_mint_authority, // Current authority from first test (now the authority for this mint) &payer, None, // decompress_mint - false, // compress_and_close_cmint + false, // compress_and_close_mint additional_recipients.clone(), // mint_to_recipients vec![], // mint_to_decompressed_recipients Some(newer_mint_authority.pubkey()), // update_mint_authority to newer authority @@ -1369,7 +1367,7 @@ async fn test_mint_actions() { .unwrap() .value .unwrap(); - let final_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let final_compressed_mint: Mint = BorshDeserialize::deserialize( &mut final_compressed_mint_account.data.unwrap().data.as_slice(), ) .unwrap(); @@ -1386,15 +1384,15 @@ async fn test_mint_actions() { "Supply should include both mintings" ); assert!( - !final_compressed_mint.metadata.cmint_decompressed, + !final_compressed_mint.metadata.mint_decompressed, "Mint should remain compressed" ); } -/// Test creating compressed mint and CMint (decompressed) in same instruction +/// Test creating compressed mint and Mint (decompressed) in same instruction #[tokio::test] #[serial] -async fn test_create_compressed_mint_with_cmint() { +async fn test_create_compressed_mint_with_mint() { let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) .await .unwrap(); @@ -1415,7 +1413,7 @@ async fn test_create_compressed_mint_with_cmint() { let address_tree_pubkey = rpc.get_address_tree_v2().tree; let compressed_mint_address = derive_mint_compressed_address(&mint_seed.pubkey(), &address_tree_pubkey); - let (cmint_pda, _cmint_bump) = find_mint_address(&mint_seed.pubkey()); + let (mint_pda, _mint_bump) = find_mint_address(&mint_seed.pubkey()); // Create mint + decompress in single instruction let signature = light_token_client::actions::mint_action_comprehensive( @@ -1423,8 +1421,8 @@ async fn test_create_compressed_mint_with_cmint() { &mint_seed, &mint_authority, &payer, - Some(DecompressMintParams::default()), // decompress_mint = true (creates CMint) - false, // compress_and_close_cmint = false + Some(DecompressMintParams::default()), // decompress_mint = true (creates Mint) + false, // compress_and_close_mint = false vec![], // no compressed recipients vec![], // no decompressed recipients None, // no mint authority update @@ -1441,11 +1439,11 @@ async fn test_create_compressed_mint_with_cmint() { .await .unwrap(); - println!("Create mint + CMint signature: {}", signature); + println!("Create mint + Mint signature: {}", signature); // Build pre-state for DecompressMint assertion (state before DecompressMint was applied) - let (_, cmint_bump) = find_mint_address(&mint_seed.pubkey()); - let pre_decompress_mint = CompressedMint { + let (_, mint_bump) = find_mint_address(&mint_seed.pubkey()); + let pre_decompress_mint = Mint { base: BaseMint { mint_authority: Some(mint_authority.pubkey().into()), supply: 0, @@ -1453,12 +1451,12 @@ async fn test_create_compressed_mint_with_cmint() { is_initialized: true, freeze_authority: Some(freeze_authority.pubkey().into()), }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, - cmint_decompressed: false, // Before DecompressMint - mint: cmint_pda.to_bytes().into(), + mint_decompressed: false, // Before DecompressMint + mint: mint_pda.to_bytes().into(), mint_signer: mint_seed.pubkey().to_bytes(), - bump: cmint_bump, + bump: mint_bump, }, reserved: [0u8; 16], account_type: ACCOUNT_TYPE_MINT, @@ -1478,30 +1476,30 @@ async fn test_create_compressed_mint_with_cmint() { ) .await; - println!("Create compressed mint with CMint completed, now testing CompressAndCloseCMint..."); + println!("Create compressed mint with Mint completed, now testing CompressAndCloseMint..."); // === COMPRESS AND CLOSE CMINT === - // Warp to epoch 2 so that rent expires (CMint created with rent_payment: 2) + // Warp to epoch 2 so that rent expires (Mint created with rent_payment: 2) rpc.warp_to_slot(SLOTS_PER_EPOCH * 2).unwrap(); - // Fetch pre-close state from CMint BEFORE closing (CMint will be closed after transaction) - let cmint_account_data = rpc - .get_account(cmint_pda) + // Fetch pre-close state from Mint BEFORE closing (Mint will be closed after transaction) + let mint_account_data = rpc + .get_account(mint_pda) .await - .expect("Failed to get CMint account") - .expect("CMint account should exist before close"); - let pre_close_mint: CompressedMint = - BorshDeserialize::deserialize(&mut cmint_account_data.data.as_slice()) - .expect("Failed to deserialize CMint data"); + .expect("Failed to get Mint account") + .expect("Mint account should exist before close"); + let pre_close_mint: Mint = + BorshDeserialize::deserialize(&mut mint_account_data.data.as_slice()) + .expect("Failed to deserialize Mint data"); - // Compress and close CMint (permissionless when rent expired) + // Compress and close Mint (permissionless when rent expired) let close_signature = light_token_client::actions::mint_action_comprehensive( &mut rpc, &mint_seed, &mint_authority, &payer, None, // no decompress_mint - true, // compress_and_close_cmint = true + true, // compress_and_close_mint = true vec![], // no compressed recipients vec![], // no decompressed recipients None, // no mint authority update @@ -1511,25 +1509,25 @@ async fn test_create_compressed_mint_with_cmint() { .await .unwrap(); - println!("CompressAndCloseCMint signature: {}", close_signature); + println!("CompressAndCloseMint signature: {}", close_signature); - // Verify CompressAndCloseCMint action results using assert_mint_action + // Verify CompressAndCloseMint action results using assert_mint_action assert_mint_action( &mut rpc, compressed_mint_address, pre_close_mint, - vec![MintActionType::CompressAndCloseCMint { idempotent: false }], + vec![MintActionType::CompressAndCloseMint { idempotent: false }], ) .await; - println!("CompressAndCloseCMint test completed successfully!"); + println!("CompressAndCloseMint test completed successfully!"); } -/// Test idempotent behavior of CompressAndCloseCMint. -/// When CMint is already compressed, calling with idempotent=true should succeed silently. +/// Test idempotent behavior of CompressAndCloseMint. +/// When Mint is already compressed, calling with idempotent=true should succeed silently. #[tokio::test] #[serial] -async fn test_compress_and_close_cmint_idempotent() { +async fn test_compress_and_close_mint_idempotent() { use light_program_test::program_test::TestRpc; let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) @@ -1549,9 +1547,9 @@ async fn test_compress_and_close_cmint_idempotent() { let address_tree_pubkey = rpc.get_address_tree_v2().tree; let compressed_mint_address = derive_mint_compressed_address(&mint_seed.pubkey(), &address_tree_pubkey); - let (cmint_pda, _) = find_mint_address(&mint_seed.pubkey()); + let (mint_pda, _) = find_mint_address(&mint_seed.pubkey()); - // 1. Create compressed mint WITH CMint (decompress_mint = true) + // 1. Create compressed mint WITH Mint (decompress_mint = true) light_token_client::actions::mint_action_comprehensive( &mut rpc, &mint_seed, @@ -1578,14 +1576,14 @@ async fn test_compress_and_close_cmint_idempotent() { // Warp to epoch 2 so that rent expires rpc.warp_to_slot(SLOTS_PER_EPOCH * 2).unwrap(); - // 2. Compress and close CMint (first time - should succeed) + // 2. Compress and close Mint (first time - should succeed) light_token_client::actions::mint_action_comprehensive( &mut rpc, &mint_seed, &mint_authority, &payer, None, - true, // compress_and_close_cmint = true + true, // compress_and_close_mint = true vec![], vec![], None, @@ -1595,14 +1593,14 @@ async fn test_compress_and_close_cmint_idempotent() { .await .unwrap(); - // Verify CMint is closed - let cmint_after_close = rpc.get_account(cmint_pda).await.unwrap(); + // Verify Mint is closed + let mint_after_close = rpc.get_account(mint_pda).await.unwrap(); assert!( - cmint_after_close.is_none(), - "CMint should be closed after CompressAndCloseCMint" + mint_after_close.is_none(), + "Mint should be closed after CompressAndCloseMint" ); - // 3. Try CompressAndCloseCMint again with idempotent=true (should succeed silently) + // 3. Try CompressAndCloseMint again with idempotent=true (should succeed silently) // Use a very low compute budget (10k) to verify the CPI is being skipped. // If CPI was executed, this would fail due to insufficient compute units. use light_client::rpc::Rpc; @@ -1616,7 +1614,7 @@ async fn test_compress_and_close_cmint_idempotent() { mint_seed: mint_seed.pubkey(), authority: mint_authority.pubkey(), payer: payer.pubkey(), - actions: vec![MintActionType::CompressAndCloseCMint { idempotent: true }], + actions: vec![MintActionType::CompressAndCloseMint { idempotent: true }], new_mint: None, }, ) @@ -1635,21 +1633,21 @@ async fn test_compress_and_close_cmint_idempotent() { assert!( result.is_ok(), - "CompressAndCloseCMint with idempotent=true should succeed with only 10k compute units when CPI is skipped: {:?}", + "CompressAndCloseMint with idempotent=true should succeed with only 10k compute units when CPI is skipped: {:?}", result.err() ); - println!("CompressAndCloseCMint idempotent test completed successfully!"); + println!("CompressAndCloseMint idempotent test completed successfully!"); } -/// Test decompressing an existing compressed mint to CMint -/// 1. Create compressed mint without CMint +/// Test decompressing an existing compressed mint to Mint +/// 1. Create compressed mint without Mint /// 2. Mint tokens to recipients -/// 3. Decompress existing mint to CMint -/// 4. Verify CMint matches compressed mint state (including supply) +/// 3. Decompress existing mint to Mint +/// 4. Verify Mint matches compressed mint state (including supply) #[tokio::test] #[serial] -async fn test_decompress_existing_mint_to_cmint() { +async fn test_decompress_existing_mint_to_mint() { let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) .await .unwrap(); @@ -1672,9 +1670,9 @@ async fn test_decompress_existing_mint_to_cmint() { let address_tree_pubkey = rpc.get_address_tree_v2().tree; let compressed_mint_address = derive_mint_compressed_address(&mint_seed.pubkey(), &address_tree_pubkey); - let (cmint_pda, _cmint_bump) = find_mint_address(&mint_seed.pubkey()); + let (mint_pda, _mint_bump) = find_mint_address(&mint_seed.pubkey()); - // === STEP 1: Create compressed mint WITHOUT CMint === + // === STEP 1: Create compressed mint WITHOUT Mint === create_mint( &mut rpc, &mint_seed, @@ -1687,23 +1685,23 @@ async fn test_decompress_existing_mint_to_cmint() { .await .unwrap(); - // Verify CMint does NOT exist yet - let cmint_account = rpc.get_account(cmint_pda).await.unwrap(); - assert!(cmint_account.is_none(), "CMint should NOT exist yet"); + // Verify Mint does NOT exist yet + let mint_account = rpc.get_account(mint_pda).await.unwrap(); + assert!(mint_account.is_none(), "Mint should NOT exist yet"); - // Verify compressed mint exists with cmint_decompressed = false + // Verify compressed mint exists with mint_decompressed = false let compressed_mint_account = rpc .get_compressed_account(compressed_mint_address, None) .await .unwrap() .value .unwrap(); - let compressed_mint: CompressedMint = + let compressed_mint: Mint = BorshDeserialize::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) .unwrap(); assert!( - !compressed_mint.metadata.cmint_decompressed, - "cmint_decompressed should be false before DecompressMint" + !compressed_mint.metadata.mint_decompressed, + "mint_decompressed should be false before DecompressMint" ); // === STEP 2: Mint tokens to recipient === @@ -1729,7 +1727,7 @@ async fn test_decompress_existing_mint_to_cmint() { .unwrap() .value .unwrap(); - let compressed_mint_after_mint: CompressedMint = + let compressed_mint_after_mint: Mint = BorshDeserialize::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) .unwrap(); assert_eq!( @@ -1737,14 +1735,14 @@ async fn test_decompress_existing_mint_to_cmint() { "Supply should be updated after minting" ); - // === STEP 3: Decompress existing mint to CMint === + // === STEP 3: Decompress existing mint to Mint === let signature = light_token_client::actions::mint_action_comprehensive( &mut rpc, &mint_seed, &mint_authority, &payer, - Some(DecompressMintParams::default()), // decompress_mint = true (creates CMint) - false, // compress_and_close_cmint + Some(DecompressMintParams::default()), // decompress_mint = true (creates Mint) + false, // compress_and_close_mint vec![], // no new compressed recipients vec![], // no decompressed recipients None, // no mint authority update @@ -1754,7 +1752,7 @@ async fn test_decompress_existing_mint_to_cmint() { .await .unwrap(); - println!("Decompress existing mint to CMint signature: {}", signature); + println!("Decompress existing mint to Mint signature: {}", signature); // Verify DecompressMint action results using assert_mint_action assert_mint_action( @@ -1768,5 +1766,5 @@ async fn test_decompress_existing_mint_to_cmint() { ) .await; - println!("Decompress existing mint to CMint test completed successfully!"); + println!("Decompress existing mint to Mint test completed successfully!"); } diff --git a/program-tests/compressed-token-test/tests/mint/mint_to.rs b/program-tests/compressed-token-test/tests/mint/mint_to.rs index 31e593ba7f..987572eb9a 100644 --- a/program-tests/compressed-token-test/tests/mint/mint_to.rs +++ b/program-tests/compressed-token-test/tests/mint/mint_to.rs @@ -12,16 +12,16 @@ use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; struct MintToTestContext { rpc: LightProgramTest, payer: Keypair, - cmint_pda: Pubkey, + mint_pda: Pubkey, ctoken_account: Pubkey, mint_authority: Keypair, } -/// Setup: Create CMint + Light Token (without tokens) +/// Setup: Create Mint + Light Token (without tokens) /// /// Steps: /// 1. Init LightProgramTest -/// 2. Create compressed mint + CMint via mint_action_comprehensive (no recipients) +/// 2. Create compressed mint + Mint via mint_action_comprehensive (no recipients) /// 3. Create Light Token ATA with compressible extension async fn setup_mint_to_test() -> MintToTestContext { let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) @@ -34,14 +34,14 @@ async fn setup_mint_to_test() -> MintToTestContext { let mint_authority = payer.insecure_clone(); let owner_keypair = Keypair::new(); - // Derive CMint PDA - let (cmint_pda, _) = find_mint_address(&mint_seed.pubkey()); + // Derive Mint PDA + let (mint_pda, _) = find_mint_address(&mint_seed.pubkey()); // Step 1: Create Light Token ATA for owner first - let (ctoken_ata, _) = derive_token_ata(&owner_keypair.pubkey(), &cmint_pda); + let (ctoken_ata, _) = derive_token_ata(&owner_keypair.pubkey(), &mint_pda); let create_ata_ix = - CreateAssociatedTokenAccount::new(payer.pubkey(), owner_keypair.pubkey(), cmint_pda) + CreateAssociatedTokenAccount::new(payer.pubkey(), owner_keypair.pubkey(), mint_pda) .instruction() .unwrap(); @@ -49,13 +49,13 @@ async fn setup_mint_to_test() -> MintToTestContext { .await .unwrap(); - // Step 2: Create compressed mint + CMint (no recipients - we'll mint via MintTo) + // Step 2: Create compressed mint + Mint (no recipients - we'll mint via MintTo) light_token_client::actions::mint_action_comprehensive( &mut rpc, &mint_seed, &mint_authority, &payer, - Some(DecompressMintParams::default()), // Creates CMint + Some(DecompressMintParams::default()), // Creates Mint false, // Don't compress and close vec![], // No compressed recipients vec![], // No ctoken recipients - we'll mint separately @@ -76,7 +76,7 @@ async fn setup_mint_to_test() -> MintToTestContext { MintToTestContext { rpc, payer, - cmint_pda, + mint_pda, ctoken_account: ctoken_ata, mint_authority, } @@ -90,7 +90,7 @@ async fn test_ctoken_mint_to() { // First mint: 500 tokens let mint_ix_1 = MintTo { - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, destination: ctx.ctoken_account, amount: 500, authority: ctx.mint_authority.pubkey(), @@ -108,11 +108,11 @@ async fn test_ctoken_mint_to() { .await .unwrap(); - assert_ctoken_mint_to(&mut ctx.rpc, ctx.ctoken_account, ctx.cmint_pda, 500).await; + assert_ctoken_mint_to(&mut ctx.rpc, ctx.ctoken_account, ctx.mint_pda, 500).await; // Second mint: 500 tokens let mint_ix_2 = MintTo { - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, destination: ctx.ctoken_account, amount: 500, authority: ctx.mint_authority.pubkey(), @@ -130,7 +130,7 @@ async fn test_ctoken_mint_to() { .await .unwrap(); - assert_ctoken_mint_to(&mut ctx.rpc, ctx.ctoken_account, ctx.cmint_pda, 500).await; + assert_ctoken_mint_to(&mut ctx.rpc, ctx.ctoken_account, ctx.mint_pda, 500).await; // Verify final balance is 1000 use anchor_lang::prelude::borsh::BorshDeserialize; @@ -162,7 +162,7 @@ async fn test_ctoken_mint_to_checked_success() { // Mint 500 tokens with correct decimals (8) let mint_ix = MintToChecked { - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, destination: ctx.ctoken_account, amount: 500, decimals: 8, // Correct decimals @@ -204,7 +204,7 @@ async fn test_ctoken_mint_to_checked_wrong_decimals() { // Try to mint with wrong decimals (7 instead of 8) let mint_ix = MintToChecked { - cmint: ctx.cmint_pda, + mint: ctx.mint_pda, destination: ctx.ctoken_account, amount: 500, decimals: 7, // Wrong decimals diff --git a/program-tests/compressed-token-test/tests/mint/random.rs b/program-tests/compressed-token-test/tests/mint/random.rs index df81128674..9a0d4bc943 100644 --- a/program-tests/compressed-token-test/tests/mint/random.rs +++ b/program-tests/compressed-token-test/tests/mint/random.rs @@ -9,7 +9,7 @@ use light_token_client::{ actions::create_mint, instructions::mint_action::{MintActionType, MintToRecipient}, }; -use light_token_interface::state::{extensions::AdditionalMetadata, CompressedMint}; +use light_token_interface::state::{extensions::AdditionalMetadata, Mint}; use light_token_sdk::{ compressed_token::create_compressed_mint::{derive_mint_compressed_address, find_mint_address}, token::CreateAssociatedTokenAccount, @@ -340,7 +340,7 @@ async fn test_random_mint_action() { .value .unwrap(); - let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let pre_compressed_mint: Mint = BorshDeserialize::deserialize( &mut pre_compressed_mint_account.data.unwrap().data.as_slice(), ) .unwrap(); diff --git a/program-tests/compressed-token-test/tests/transfer2/compress_failing.rs b/program-tests/compressed-token-test/tests/transfer2/compress_failing.rs index 646704f64a..eda7457e4a 100644 --- a/program-tests/compressed-token-test/tests/transfer2/compress_failing.rs +++ b/program-tests/compressed-token-test/tests/transfer2/compress_failing.rs @@ -123,7 +123,7 @@ async fn setup_compression_test(token_amount: u64) -> Result Result<(), RpcError> { &mint_authority, &payer, None, // no decompress mint - false, // no close cmint + false, // no close mint vec![], // no compressed recipients decompressed_recipients, // mint to decompressed Light Token ATA None, diff --git a/program-tests/compressed-token-test/tests/transfer2/decompress_failing.rs b/program-tests/compressed-token-test/tests/transfer2/decompress_failing.rs index 902b09b57d..840313830a 100644 --- a/program-tests/compressed-token-test/tests/transfer2/decompress_failing.rs +++ b/program-tests/compressed-token-test/tests/transfer2/decompress_failing.rs @@ -124,7 +124,7 @@ async fn setup_decompression_test( &mint_authority, &payer, None, // no decompress mint - false, // compress_and_close_cmint + false, // compress_and_close_mint compressed_recipients, // mint compressed tokens to owner decompressed_recipients, // mint 1 token to decompressed Light Token ATA None, // no mint authority update diff --git a/program-tests/compressed-token-test/tests/transfer2/no_system_program_cpi_failing.rs b/program-tests/compressed-token-test/tests/transfer2/no_system_program_cpi_failing.rs index 4a7fe2ead4..5672c525d3 100644 --- a/program-tests/compressed-token-test/tests/transfer2/no_system_program_cpi_failing.rs +++ b/program-tests/compressed-token-test/tests/transfer2/no_system_program_cpi_failing.rs @@ -136,7 +136,7 @@ async fn setup_no_system_program_cpi_test( &mint_authority, &payer, None, // no decompress mint - false, // no close cmint + false, // no close mint vec![], // no compressed recipients decompressed_recipients, // mint to source Light Token ATA (empty if token_amount is 0) None, @@ -744,7 +744,7 @@ async fn test_too_many_mints() { &mint_authority, &context.payer, None, // no decompress mint - false, // no close cmint + false, // no close mint vec![], // no compressed recipients decompressed_recipients, // mint to source Light Token ATA None, diff --git a/program-tests/registry-test/tests/compressible.rs b/program-tests/registry-test/tests/compressible.rs index 0ef988013c..59a1b5af21 100644 --- a/program-tests/registry-test/tests/compressible.rs +++ b/program-tests/registry-test/tests/compressible.rs @@ -1171,14 +1171,14 @@ async fn assert_not_compressible( Ok(()) } -/// Helper function to assert that a compressible CMint account is NOT compressible (well-funded) -async fn assert_not_compressible_cmint( +/// Helper function to assert that a compressible Mint account is NOT compressible (well-funded) +async fn assert_not_compressible_mint( rpc: &mut R, account_pubkey: Pubkey, name: &str, ) -> Result<(), RpcError> { use borsh::BorshDeserialize; - use light_token_interface::state::CompressedMint; + use light_token_interface::state::Mint; let account = rpc .get_account(account_pubkey) @@ -1189,11 +1189,11 @@ async fn assert_not_compressible_cmint( .get_minimum_balance_for_rent_exemption(account.data.len()) .await?; - let cmint = CompressedMint::deserialize(&mut account.data.as_slice()) - .map_err(|e| RpcError::AssertRpcError(format!("Failed to deserialize CMint: {:?}", e)))?; + let mint = Mint::deserialize(&mut account.data.as_slice()) + .map_err(|e| RpcError::AssertRpcError(format!("Failed to deserialize Mint: {:?}", e)))?; - // CompressionInfo is embedded directly in cmint.compression - let compression_info = &cmint.compression; + // CompressionInfo is embedded directly in mint.compression + let compression_info = &mint.compression; let current_slot = rpc.get_slot().await?; // Check if account is compressible using AccountRentState @@ -1235,14 +1235,14 @@ async fn assert_not_compressible_cmint( /// Helper function to mint tokens to a Light Token account using MintTo instruction async fn mint_to_token( rpc: &mut R, - cmint: Pubkey, + mint: Pubkey, destination: Pubkey, amount: u64, mint_authority: &Keypair, payer: &Keypair, ) -> Result { let ix = MintTo { - cmint, + mint, destination, amount, authority: mint_authority.pubkey(), @@ -1264,12 +1264,12 @@ async fn test_compressible_account_infinite_funding() -> Result<(), RpcError> { .unwrap(); let payer = rpc.get_payer().insecure_clone(); - // Create a CMint with compressible config (will be tested alongside Light Token accounts) + // Create a Mint with compressible config (will be tested alongside Light Token accounts) let mint_seed = Keypair::new(); let mint_authority = payer.insecure_clone(); - let (cmint_pda, _) = find_mint_address(&mint_seed.pubkey()); + let (mint_pda, _) = find_mint_address(&mint_seed.pubkey()); - // Create CMint with write_top_up for infinite funding + // Create Mint with write_top_up for infinite funding mint_action_comprehensive( &mut rpc, &mint_seed, @@ -1296,8 +1296,8 @@ async fn test_compressible_account_infinite_funding() -> Result<(), RpcError> { .await .unwrap(); - // Use the CMint PDA as the mint for Light Token accounts - let mint = cmint_pda; + // Use the Mint PDA as the mint for Light Token accounts + let mint = mint_pda; // Create owner for both accounts let owner_keypair = Keypair::new(); @@ -1344,11 +1344,11 @@ async fn test_compressible_account_infinite_funding() -> Result<(), RpcError> { .await .unwrap(); - // Mint initial tokens to Account A via MintTo (this also writes to the CMint, triggering top-up) + // Mint initial tokens to Account A via MintTo (this also writes to the Mint, triggering top-up) let transfer_amount = 1_000_000u64; mint_to_token( &mut rpc, - cmint_pda, + mint_pda, account_a, transfer_amount, &mint_authority, @@ -1388,30 +1388,31 @@ async fn test_compressible_account_infinite_funding() -> Result<(), RpcError> { Ok(compression.last_claimed_slot) }; - let get_last_claimed_slot_cmint = |account_data: &[u8]| -> Result { + let get_last_claimed_slot_mint = |account_data: &[u8]| -> Result { use borsh::BorshDeserialize; - use light_token_interface::state::CompressedMint; - let cmint = CompressedMint::deserialize(&mut &account_data[..]).map_err(|e| { - RpcError::AssertRpcError(format!("Failed to deserialize CMint: {:?}", e)) + use light_token_interface::state::Mint; + let mint = Mint::deserialize(&mut &account_data[..]).map_err(|e| { + RpcError::AssertRpcError(format!("Failed to deserialize Mint: {:?}", e)) })?; - Ok(cmint.compression.last_claimed_slot) + Ok(mint.compression.last_claimed_slot) }; let initial_last_claimed_a = get_last_claimed_slot_token(&rpc.get_account(account_a).await?.unwrap().data)?; let initial_last_claimed_b = get_last_claimed_slot_token(&rpc.get_account(account_b).await?.unwrap().data)?; - let initial_last_claimed_cmint = - get_last_claimed_slot_cmint(&rpc.get_account(cmint_pda).await?.unwrap().data)?; - - // Get CMint size and rent config for final verification - let cmint_account = rpc.get_account(cmint_pda).await?.unwrap(); - let cmint_size = cmint_account.data.len() as u64; - let cmint_data = light_token_interface::state::CompressedMint::deserialize( - &mut cmint_account.data.as_slice(), - ) - .map_err(|e| RpcError::AssertRpcError(format!("Failed to deserialize CMint: {:?}", e)))?; - let cmint_rent_config = cmint_data.compression.rent_config; + let initial_last_claimed_mint = + get_last_claimed_slot_mint(&rpc.get_account(mint_pda).await?.unwrap().data)?; + + // Get Mint size and rent config for final verification + let mint_account = rpc.get_account(mint_pda).await?.unwrap(); + let mint_size = mint_account.data.len() as u64; + let mint_data = + light_token_interface::state::Mint::deserialize(&mut mint_account.data.as_slice()) + .map_err(|e| { + RpcError::AssertRpcError(format!("Failed to deserialize Mint: {:?}", e)) + })?; + let mint_rent_config = mint_data.compression.rent_config; println!("Initial slot: {}", initial_slot); println!( @@ -1423,8 +1424,8 @@ async fn test_compressible_account_infinite_funding() -> Result<(), RpcError> { initial_last_claimed_b ); println!( - "CMint initial last_claimed_slot: {}", - initial_last_claimed_cmint + "Mint initial last_claimed_slot: {}", + initial_last_claimed_mint ); // Main loop: 1000 iterations = 100 epochs * 10 iterations per epoch @@ -1459,71 +1460,73 @@ async fn test_compressible_account_infinite_funding() -> Result<(), RpcError> { assert_not_compressible(&mut rpc, source, source_name).await?; assert_not_compressible(&mut rpc, dest, dest_name).await?; - // Mint 0 tokens every 10 iterations (once per epoch) to trigger CMint write_top_up - // This keeps the CMint funded through its write_top_up mechanism - mint_to_token(&mut rpc, cmint_pda, dest, 0, &mint_authority, &payer).await?; + // Mint 0 tokens every 10 iterations (once per epoch) to trigger Mint write_top_up + // This keeps the Mint funded through its write_top_up mechanism + mint_to_token(&mut rpc, mint_pda, dest, 0, &mint_authority, &payer).await?; // Advance by 1/10 of an epoch (630 slots) let advance_slots = SLOTS_PER_EPOCH / 10; // 630 slots rpc.warp_slot_forward(advance_slots).await.unwrap(); - // Log progress and assert CMint every 100 iterations + // Log progress and assert Mint every 100 iterations if i % 100 == 0 && i > 0 { println!("Completed iteration {}/1000 (epoch {})", i, epoch); - // Assert CMint is still well-funded (write_top_up should keep it funded) - assert_not_compressible_cmint(&mut rpc, cmint_pda, "CMint").await?; + // Assert Mint is still well-funded (write_top_up should keep it funded) + assert_not_compressible_mint(&mut rpc, mint_pda, "Mint").await?; } } println!("Test completed successfully!"); - println!("All accounts (Light Token A, Light Token B, CMint) remained well-funded through 100 epochs"); + println!( + "All accounts (Light Token A, Light Token B, Mint) remained well-funded through 100 epochs" + ); // Final verification assert_not_compressible(&mut rpc, account_a, "Account A (final)").await?; assert_not_compressible(&mut rpc, account_b, "Account B (final)").await?; - assert_not_compressible_cmint(&mut rpc, cmint_pda, "CMint (final)").await?; + assert_not_compressible_mint(&mut rpc, mint_pda, "Mint (final)").await?; // Verify total rent claimed let final_rent_sponsor_balance = rpc.get_account(rent_sponsor).await?.unwrap().lamports; let total_rent_claimed = final_rent_sponsor_balance - initial_rent_sponsor_balance; - // Get final last_claimed_slot from all accounts (Light Token A, Light Token B, CMint) + // Get final last_claimed_slot from all accounts (Light Token A, Light Token B, Mint) let final_last_claimed_a = get_last_claimed_slot_token(&rpc.get_account(account_a).await?.unwrap().data)?; let final_last_claimed_b = get_last_claimed_slot_token(&rpc.get_account(account_b).await?.unwrap().data)?; - let final_last_claimed_cmint = - get_last_claimed_slot_cmint(&rpc.get_account(cmint_pda).await?.unwrap().data)?; + let final_last_claimed_mint = + get_last_claimed_slot_mint(&rpc.get_account(mint_pda).await?.unwrap().data)?; // Calculate exact number of completed epochs that were claimed for each account use light_compressible::rent::SLOTS_PER_EPOCH; let completed_epochs_a = (final_last_claimed_a - initial_last_claimed_a) / SLOTS_PER_EPOCH; let completed_epochs_b = (final_last_claimed_b - initial_last_claimed_b) / SLOTS_PER_EPOCH; - let completed_epochs_cmint = - (final_last_claimed_cmint - initial_last_claimed_cmint) / SLOTS_PER_EPOCH; + let completed_epochs_mint = + (final_last_claimed_mint - initial_last_claimed_mint) / SLOTS_PER_EPOCH; // Calculate exact expected rent using RentConfig's rent_curve_per_epoch let expected_rent_a = rent_config.get_rent(account_size, completed_epochs_a); let expected_rent_b = rent_config.get_rent(account_size, completed_epochs_b); - let expected_rent_cmint = cmint_rent_config.get_rent(cmint_size, completed_epochs_cmint); - let expected_total_rent = expected_rent_a + expected_rent_b + expected_rent_cmint; + let expected_rent_mint = mint_rent_config.get_rent(mint_size, completed_epochs_mint); + let expected_total_rent = expected_rent_a + expected_rent_b + expected_rent_mint; println!( - "Rent claimed: {} (A: {}, B: {}, CMint: {})", - total_rent_claimed, expected_rent_a, expected_rent_b, expected_rent_cmint + "Rent claimed: {} (A: {}, B: {}, Mint: {})", + total_rent_claimed, expected_rent_a, expected_rent_b, expected_rent_mint ); // Assert exact match assert_eq!( total_rent_claimed, expected_total_rent, - "Rent claimed should exactly match expected rent (Light Token A + Light Token B + CMint)" + "Rent claimed should exactly match expected rent (Light Token A + Light Token B + Mint)" ); Ok(()) } #[tokio::test] -async fn test_claim_from_cmint_account() -> Result<(), RpcError> { +async fn test_claim_from_mint_account() -> Result<(), RpcError> { let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) .await .unwrap(); @@ -1531,8 +1534,8 @@ async fn test_claim_from_cmint_account() -> Result<(), RpcError> { let mint_seed = Keypair::new(); let mint_authority = payer.insecure_clone(); - // Create compressed mint + decompress to CMint with rent prepaid - let (cmint_pda, _) = find_mint_address(&mint_seed.pubkey()); + // Create compressed mint + decompress to Mint with rent prepaid + let (mint_pda, _) = find_mint_address(&mint_seed.pubkey()); mint_action_comprehensive( &mut rpc, &mint_seed, @@ -1564,9 +1567,9 @@ async fn test_claim_from_cmint_account() -> Result<(), RpcError> { let target_slot = current_slot + 2 * SLOTS_PER_EPOCH; rpc.warp_to_slot(target_slot).unwrap(); - // Claim rent from CMint + // Claim rent from Mint let forester_keypair = rpc.test_accounts.protocol.forester.insecure_clone(); - claim_forester(&mut rpc, &[cmint_pda], &forester_keypair, &payer) + claim_forester(&mut rpc, &[mint_pda], &forester_keypair, &payer) .await .unwrap(); @@ -1574,7 +1577,7 @@ async fn test_claim_from_cmint_account() -> Result<(), RpcError> { let config = rpc.test_accounts.funding_pool_config; assert_claim( &mut rpc, - &[cmint_pda], + &[mint_pda], config.rent_sponsor_pda, config.compression_authority_pda, ) @@ -1584,7 +1587,7 @@ async fn test_claim_from_cmint_account() -> Result<(), RpcError> { } #[tokio::test] -async fn test_claim_mixed_token_and_cmint() -> Result<(), RpcError> { +async fn test_claim_mixed_token_and_mint() -> Result<(), RpcError> { let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) .await .unwrap(); @@ -1608,9 +1611,9 @@ async fn test_claim_mixed_token_and_cmint() -> Result<(), RpcError> { .await .unwrap(); - // Create CMint account with prepaid rent + // Create Mint account with prepaid rent let mint_seed = Keypair::new(); - let (cmint_pda, _) = find_mint_address(&mint_seed.pubkey()); + let (mint_pda, _) = find_mint_address(&mint_seed.pubkey()); mint_action_comprehensive( &mut rpc, &mint_seed, @@ -1646,7 +1649,7 @@ async fn test_claim_mixed_token_and_cmint() -> Result<(), RpcError> { let forester_keypair = rpc.test_accounts.protocol.forester.insecure_clone(); claim_forester( &mut rpc, - &[token_pubkey, cmint_pda], + &[token_pubkey, mint_pda], &forester_keypair, &payer, ) @@ -1657,7 +1660,7 @@ async fn test_claim_mixed_token_and_cmint() -> Result<(), RpcError> { let config = rpc.test_accounts.funding_pool_config; assert_claim( &mut rpc, - &[token_pubkey, cmint_pda], + &[token_pubkey, mint_pda], config.rent_sponsor_pda, config.compression_authority_pda, ) diff --git a/program-tests/utils/src/assert_claim.rs b/program-tests/utils/src/assert_claim.rs index b9330d5b51..5bdcb22ae7 100644 --- a/program-tests/utils/src/assert_claim.rs +++ b/program-tests/utils/src/assert_claim.rs @@ -1,8 +1,6 @@ use light_client::rpc::Rpc; use light_program_test::LightProgramTest; -use light_token_interface::state::{ - CompressedMint, Token, ACCOUNT_TYPE_MINT, ACCOUNT_TYPE_TOKEN_ACCOUNT, -}; +use light_token_interface::state::{Mint, Token, ACCOUNT_TYPE_MINT, ACCOUNT_TYPE_TOKEN_ACCOUNT}; use light_zero_copy::traits::{ZeroCopyAt, ZeroCopyAtMut}; use solana_sdk::{clock::Clock, pubkey::Pubkey}; @@ -63,9 +61,9 @@ fn extract_pre_compression_mut( } } ACCOUNT_TYPE_MINT => { - let (mut cmint, _) = CompressedMint::zero_copy_at_mut(data) - .unwrap_or_else(|e| panic!("Failed to parse cmint account {}: {:?}", pubkey, e)); - let compression = &mut cmint.base.compression; + let (mut mint, _) = Mint::zero_copy_at_mut(data) + .unwrap_or_else(|e| panic!("Failed to parse mint account {}: {:?}", pubkey, e)); + let compression = &mut mint.base.compression; let last_claimed_slot = u64::from(compression.last_claimed_slot); let compression_authority = Pubkey::from(compression.compression_authority); let rent_sponsor = Pubkey::from(compression.rent_sponsor); @@ -99,9 +97,9 @@ fn extract_post_compression(data: &[u8], pubkey: &Pubkey) -> u64 { u64::from(compressible.info.last_claimed_slot) } ACCOUNT_TYPE_MINT => { - let (cmint, _) = CompressedMint::zero_copy_at(data) - .unwrap_or_else(|e| panic!("Failed to parse cmint account {}: {:?}", pubkey, e)); - u64::from(cmint.base.compression.last_claimed_slot) + let (mint, _) = Mint::zero_copy_at(data) + .unwrap_or_else(|e| panic!("Failed to parse mint account {}: {:?}", pubkey, e)); + u64::from(mint.base.compression.last_claimed_slot) } _ => panic!("Unknown account type {} for {}", account_type, pubkey), } @@ -128,14 +126,14 @@ pub async fn assert_claim( // Must have > 165 bytes to include account_type discriminator assert!( pre_token_account.data.len() > 165, - "Account must have > 165 bytes for Light Token/CMint" + "Account must have > 165 bytes for Light Token/Mint" ); // Get account size and lamports before parsing (to avoid borrow conflicts) let account_size = pre_token_account.data.len() as u64; let account_lamports = pre_token_account.lamports; let current_slot = rpc.pre_context.as_ref().unwrap().get_sysvar::().slot; - // Extract compression info (handles both Light Token and CMint) + // Extract compression info (handles both Light Token and Mint) let pre_data = extract_pre_compression_mut( &mut pre_token_account.data, account_size, diff --git a/program-tests/utils/src/assert_ctoken_burn.rs b/program-tests/utils/src/assert_ctoken_burn.rs index 74b3dcab96..a45fd6d85c 100644 --- a/program-tests/utils/src/assert_ctoken_burn.rs +++ b/program-tests/utils/src/assert_ctoken_burn.rs @@ -2,7 +2,7 @@ use anchor_lang::prelude::borsh::BorshDeserialize; use light_client::rpc::Rpc; use light_compressible::compression_info::CompressionInfo; use light_program_test::LightProgramTest; -use light_token_interface::state::{extensions::ExtensionStruct, CompressedMint, Token}; +use light_token_interface::state::{extensions::ExtensionStruct, Mint, Token}; use solana_sdk::pubkey::Pubkey; /// Extract CompressionInfo from Light Token's Compressible extension @@ -23,27 +23,27 @@ fn get_ctoken_compression_info(ctoken: &Token) -> Option { /// # Arguments /// * `rpc` - RPC client to fetch account data (must be LightProgramTest) /// * `ctoken_account` - Source Light Token account pubkey -/// * `cmint_account` - CMint account pubkey +/// * `mint_account` - Mint account pubkey /// * `burn_amount` - Amount that was burned /// /// # Assertions /// * Light Token balance decreased by burn amount -/// * CMint supply decreased by burn amount +/// * Mint supply decreased by burn amount /// * Compressible extensions preserved (if present) /// * Lamport top-ups applied correctly (if compressible) pub async fn assert_ctoken_burn( rpc: &mut LightProgramTest, ctoken_account: Pubkey, - cmint_account: Pubkey, + mint_account: Pubkey, burn_amount: u64, ) { // Get pre-transaction state from cache let ctoken_before = rpc .get_pre_transaction_account(&ctoken_account) .expect("Light Token account should exist in pre-transaction context"); - let cmint_before = rpc - .get_pre_transaction_account(&cmint_account) - .expect("CMint account should exist in pre-transaction context"); + let mint_before = rpc + .get_pre_transaction_account(&mint_account) + .expect("Mint account should exist in pre-transaction context"); // Get post-transaction state let ctoken_after = rpc @@ -51,11 +51,11 @@ pub async fn assert_ctoken_burn( .await .expect("Failed to get Light Token account after transaction") .expect("Light Token account should exist after transaction"); - let cmint_after = rpc - .get_account(cmint_account) + let mint_after = rpc + .get_account(mint_account) .await - .expect("Failed to get CMint account after transaction") - .expect("CMint account should exist after transaction"); + .expect("Failed to get Mint account after transaction") + .expect("Mint account should exist after transaction"); // Parse accounts using Borsh let ctoken_parsed_before: Token = @@ -64,20 +64,18 @@ pub async fn assert_ctoken_burn( let ctoken_parsed_after: Token = BorshDeserialize::deserialize(&mut ctoken_after.data.as_slice()) .expect("Failed to deserialize Light Token after"); - let cmint_parsed_before: CompressedMint = - BorshDeserialize::deserialize(&mut cmint_before.data.as_slice()) - .expect("Failed to deserialize CMint before"); - let cmint_parsed_after: CompressedMint = - BorshDeserialize::deserialize(&mut cmint_after.data.as_slice()) - .expect("Failed to deserialize CMint after"); + let mint_parsed_before: Mint = BorshDeserialize::deserialize(&mut mint_before.data.as_slice()) + .expect("Failed to deserialize Mint before"); + let mint_parsed_after: Mint = BorshDeserialize::deserialize(&mut mint_after.data.as_slice()) + .expect("Failed to deserialize Mint after"); // Build expected Light Token state let mut expected_ctoken = ctoken_parsed_before.clone(); expected_ctoken.amount -= burn_amount; - // Build expected CMint state - let mut expected_cmint = cmint_parsed_before.clone(); - expected_cmint.base.supply -= burn_amount; + // Build expected Mint state + let mut expected_mint = mint_parsed_before.clone(); + expected_mint.base.supply -= burn_amount; // Assert full Light Token struct assert_eq!( @@ -86,10 +84,10 @@ pub async fn assert_ctoken_burn( burn_amount ); - // Assert full CMint struct + // Assert full Mint struct assert_eq!( - cmint_parsed_after, expected_cmint, - "CMint state mismatch after burn. burn_amount: {}", + mint_parsed_after, expected_mint, + "Mint state mismatch after burn. burn_amount: {}", burn_amount ); @@ -106,27 +104,23 @@ pub async fn assert_ctoken_burn( ) .await; - let expected_cmint_lamport_change = calculate_expected_lamport_change( + let expected_mint_lamport_change = calculate_expected_lamport_change( rpc, - &cmint_parsed_before.compression, - cmint_before.data.len(), + &mint_parsed_before.compression, + mint_before.data.len(), current_slot, - cmint_before.lamports, + mint_before.lamports, ) .await; let actual_ctoken_lamport_change = ctoken_after.lamports.saturating_sub(ctoken_before.lamports); - let actual_cmint_lamport_change = - cmint_after.lamports.saturating_sub(cmint_before.lamports); + let actual_mint_lamport_change = mint_after.lamports.saturating_sub(mint_before.lamports); // Assert lamport changes assert_eq!( - (actual_ctoken_lamport_change, actual_cmint_lamport_change), - ( - expected_ctoken_lamport_change, - expected_cmint_lamport_change - ), + (actual_ctoken_lamport_change, actual_mint_lamport_change), + (expected_ctoken_lamport_change, expected_mint_lamport_change), "Lamport changes mismatch after burn" ); } diff --git a/program-tests/utils/src/assert_ctoken_mint_to.rs b/program-tests/utils/src/assert_ctoken_mint_to.rs index 77ce472354..9e3e1abf91 100644 --- a/program-tests/utils/src/assert_ctoken_mint_to.rs +++ b/program-tests/utils/src/assert_ctoken_mint_to.rs @@ -2,7 +2,7 @@ use anchor_lang::prelude::borsh::BorshDeserialize; use light_client::rpc::Rpc; use light_compressible::compression_info::CompressionInfo; use light_program_test::LightProgramTest; -use light_token_interface::state::{extensions::ExtensionStruct, CompressedMint, Token}; +use light_token_interface::state::{extensions::ExtensionStruct, Mint, Token}; use solana_sdk::pubkey::Pubkey; /// Extract CompressionInfo from Light Token's Compressible extension @@ -23,27 +23,27 @@ fn get_ctoken_compression_info(ctoken: &Token) -> Option { /// # Arguments /// * `rpc` - RPC client to fetch account data (must be LightProgramTest) /// * `ctoken_account` - Destination Light Token account pubkey -/// * `cmint_account` - CMint account pubkey +/// * `mint_account` - Mint account pubkey /// * `mint_amount` - Amount that was minted /// /// # Assertions /// * Light Token balance increased by mint amount -/// * CMint supply increased by mint amount +/// * Mint supply increased by mint amount /// * Compressible extensions preserved (if present) /// * Lamport top-ups applied correctly (if compressible) pub async fn assert_ctoken_mint_to( rpc: &mut LightProgramTest, ctoken_account: Pubkey, - cmint_account: Pubkey, + mint_account: Pubkey, mint_amount: u64, ) { // Get pre-transaction state from cache let ctoken_before = rpc .get_pre_transaction_account(&ctoken_account) .expect("Light Token account should exist in pre-transaction context"); - let cmint_before = rpc - .get_pre_transaction_account(&cmint_account) - .expect("CMint account should exist in pre-transaction context"); + let mint_before = rpc + .get_pre_transaction_account(&mint_account) + .expect("Mint account should exist in pre-transaction context"); // Get post-transaction state let ctoken_after = rpc @@ -51,11 +51,11 @@ pub async fn assert_ctoken_mint_to( .await .expect("Failed to get Light Token account after transaction") .expect("Light Token account should exist after transaction"); - let cmint_after = rpc - .get_account(cmint_account) + let mint_after = rpc + .get_account(mint_account) .await - .expect("Failed to get CMint account after transaction") - .expect("CMint account should exist after transaction"); + .expect("Failed to get Mint account after transaction") + .expect("Mint account should exist after transaction"); // Parse accounts using Borsh let ctoken_parsed_before: Token = @@ -64,20 +64,18 @@ pub async fn assert_ctoken_mint_to( let ctoken_parsed_after: Token = BorshDeserialize::deserialize(&mut ctoken_after.data.as_slice()) .expect("Failed to deserialize Light Token after"); - let cmint_parsed_before: CompressedMint = - BorshDeserialize::deserialize(&mut cmint_before.data.as_slice()) - .expect("Failed to deserialize CMint before"); - let cmint_parsed_after: CompressedMint = - BorshDeserialize::deserialize(&mut cmint_after.data.as_slice()) - .expect("Failed to deserialize CMint after"); + let mint_parsed_before: Mint = BorshDeserialize::deserialize(&mut mint_before.data.as_slice()) + .expect("Failed to deserialize Mint before"); + let mint_parsed_after: Mint = BorshDeserialize::deserialize(&mut mint_after.data.as_slice()) + .expect("Failed to deserialize Mint after"); // Build expected Light Token state let mut expected_ctoken = ctoken_parsed_before.clone(); expected_ctoken.amount += mint_amount; - // Build expected CMint state - let mut expected_cmint = cmint_parsed_before.clone(); - expected_cmint.base.supply += mint_amount; + // Build expected Mint state + let mut expected_mint = mint_parsed_before.clone(); + expected_mint.base.supply += mint_amount; // Assert full Light Token struct assert_eq!( @@ -86,10 +84,10 @@ pub async fn assert_ctoken_mint_to( mint_amount ); - // Assert full CMint struct + // Assert full Mint struct assert_eq!( - cmint_parsed_after, expected_cmint, - "CMint state mismatch after mint_to. mint_amount: {}", + mint_parsed_after, expected_mint, + "Mint state mismatch after mint_to. mint_amount: {}", mint_amount ); @@ -106,27 +104,23 @@ pub async fn assert_ctoken_mint_to( ) .await; - let expected_cmint_lamport_change = calculate_expected_lamport_change( + let expected_mint_lamport_change = calculate_expected_lamport_change( rpc, - &cmint_parsed_before.compression, - cmint_before.data.len(), + &mint_parsed_before.compression, + mint_before.data.len(), current_slot, - cmint_before.lamports, + mint_before.lamports, ) .await; let actual_ctoken_lamport_change = ctoken_after.lamports.saturating_sub(ctoken_before.lamports); - let actual_cmint_lamport_change = - cmint_after.lamports.saturating_sub(cmint_before.lamports); + let actual_mint_lamport_change = mint_after.lamports.saturating_sub(mint_before.lamports); // Assert lamport changes assert_eq!( - (actual_ctoken_lamport_change, actual_cmint_lamport_change), - ( - expected_ctoken_lamport_change, - expected_cmint_lamport_change - ), + (actual_ctoken_lamport_change, actual_mint_lamport_change), + (expected_ctoken_lamport_change, expected_mint_lamport_change), "Lamport changes mismatch after mint_to" ); } diff --git a/program-tests/utils/src/assert_metadata.rs b/program-tests/utils/src/assert_metadata.rs index 6ad366dece..09654f25d6 100644 --- a/program-tests/utils/src/assert_metadata.rs +++ b/program-tests/utils/src/assert_metadata.rs @@ -6,7 +6,7 @@ use light_client::{ use light_hasher::{sha256::Sha256BE, Hasher, HasherError}; use light_token_interface::state::{ extensions::{AdditionalMetadata, ExtensionStruct, TokenMetadata}, - CompressedMint, + Mint, }; use solana_sdk::{pubkey::Pubkey, signature::Signature}; @@ -55,10 +55,10 @@ pub async fn assert_metadata_state( .expect("Compressed mint account not found"); assert_sha_account_hash(&compressed_mint_account).unwrap(); - // Deserialize the CompressedMint - let mint_data: CompressedMint = + // Deserialize the Mint + let mint_data: Mint = BorshDeserialize::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) - .expect("Failed to deserialize CompressedMint"); + .expect("Failed to deserialize Mint"); // Verify mint has extensions assert!( @@ -128,10 +128,10 @@ pub fn assert_sha_account_hash(account: &CompressedAccount) -> Result<(), Hasher pub async fn assert_mint_operation_result( rpc: &mut R, compressed_mint_address: [u8; 32], - mint_before: &CompressedMint, + mint_before: &Mint, expected_changes: F, ) where - F: FnOnce(&mut CompressedMint), + F: FnOnce(&mut Mint), { // Apply expected changes to the before state let mut expected_mint_after = mint_before.clone(); @@ -150,11 +150,11 @@ pub async fn assert_mint_operation_result( ); } -/// Get the complete CompressedMint state from account using borsh deserialization +/// Get the complete Mint state from account using borsh deserialization pub async fn get_actual_mint_state( rpc: &mut R, compressed_mint_address: [u8; 32], -) -> CompressedMint { +) -> Mint { let compressed_mint_account = rpc .indexer() .unwrap() @@ -172,7 +172,7 @@ pub async fn get_actual_mint_state( compressed_mint_account.data ); BorshDeserialize::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) - .expect("Failed to deserialize CompressedMint") + .expect("Failed to deserialize Mint") } /// Assert that an operation fails with the expected error code @@ -226,9 +226,9 @@ pub async fn assert_metadata_exists( ))) .expect("Compressed mint account not found"); - let mint_data: CompressedMint = + let mint_data: Mint = BorshDeserialize::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) - .expect("Failed to deserialize CompressedMint"); + .expect("Failed to deserialize Mint"); assert!( mint_data.extensions.is_some(), @@ -265,9 +265,9 @@ pub async fn assert_metadata_not_exists( ))) .expect("Compressed mint account not found"); - let mint_data: CompressedMint = + let mint_data: Mint = BorshDeserialize::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) - .expect("Failed to deserialize CompressedMint"); + .expect("Failed to deserialize Mint"); // Assert that either extensions is None or doesn't contain TokenMetadata if let Some(extensions) = mint_data.extensions { diff --git a/program-tests/utils/src/assert_mint_action.rs b/program-tests/utils/src/assert_mint_action.rs index 24843dec28..7b396714a4 100644 --- a/program-tests/utils/src/assert_mint_action.rs +++ b/program-tests/utils/src/assert_mint_action.rs @@ -6,9 +6,7 @@ use light_compressed_account::compressed_account::CompressedAccountData; use light_compressible::compression_info::CompressionInfo; use light_program_test::{LightProgramTest, Rpc}; use light_token_client::instructions::mint_action::MintActionType; -use light_token_interface::state::{ - extensions::AdditionalMetadata, CompressedMint, ExtensionStruct, Token, -}; +use light_token_interface::state::{extensions::AdditionalMetadata, ExtensionStruct, Mint, Token}; use solana_sdk::pubkey::Pubkey; /// Extract CompressionInfo from Light Token's Compressible extension @@ -37,7 +35,7 @@ fn get_ctoken_compression_info(ctoken: &Token) -> Option { pub async fn assert_mint_action( rpc: &mut LightProgramTest, compressed_mint_address: [u8; 32], - pre_compressed_mint: CompressedMint, + pre_compressed_mint: Mint, actions: Vec, ) { // Build expected state by applying actions to pre-state @@ -123,10 +121,10 @@ pub async fn assert_mint_action( } } MintActionType::DecompressMint { .. } => { - expected_mint.metadata.cmint_decompressed = true; + expected_mint.metadata.mint_decompressed = true; } - MintActionType::CompressAndCloseCMint { .. } => { - expected_mint.metadata.cmint_decompressed = false; + MintActionType::CompressAndCloseMint { .. } => { + expected_mint.metadata.mint_decompressed = false; // When compressed, the compression info should be default (zeroed) expected_mint.compression = light_compressible::compression_info::CompressionInfo::default(); @@ -134,43 +132,42 @@ pub async fn assert_mint_action( } } // Determine pre and post decompression states - let post_decompressed = expected_mint.metadata.cmint_decompressed; + let post_decompressed = expected_mint.metadata.mint_decompressed; - // Check for CompressAndCloseCMint action - let has_compress_and_close_cmint = actions + // Check for CompressAndCloseMint action + let has_compress_and_close_mint = actions .iter() - .any(|a| matches!(a, MintActionType::CompressAndCloseCMint { .. })); + .any(|a| matches!(a, MintActionType::CompressAndCloseMint { .. })); if post_decompressed { - // === CASE 1 & 2: CMint is source of truth after actions === + // === CASE 1 & 2: Mint is source of truth after actions === // (Either DecompressMint happened OR was already decompressed) - let cmint_pda = Pubkey::from(expected_mint.metadata.mint); + let mint_pda = Pubkey::from(expected_mint.metadata.mint); - let cmint_account = rpc - .get_account(cmint_pda) + let mint_account = rpc + .get_account(mint_pda) .await - .expect("Failed to fetch CMint account") - .expect("CMint PDA account should exist when decompressed"); + .expect("Failed to fetch Mint account") + .expect("Mint PDA account should exist when decompressed"); - let cmint: CompressedMint = - BorshDeserialize::deserialize(&mut cmint_account.data.as_slice()) - .expect("Failed to deserialize CMint account"); + let mint: Mint = BorshDeserialize::deserialize(&mut mint_account.data.as_slice()) + .expect("Failed to deserialize Mint account"); - // CMint base and metadata should match expected + // Mint base and metadata should match expected assert_eq!( - cmint.base, expected_mint.base, - "CMint base should match expected mint base" + mint.base, expected_mint.base, + "Mint base should match expected mint base" ); assert_eq!( - cmint.metadata, expected_mint.metadata, - "CMint metadata should match expected mint metadata" + mint.metadata, expected_mint.metadata, + "Mint metadata should match expected mint metadata" ); - // CMint compression info should be set (non-default) when decompressed + // Mint compression info should be set (non-default) when decompressed assert_ne!( - cmint.compression, + mint.compression, light_compressible::compression_info::CompressionInfo::default(), - "CMint compression info should be set when decompressed" + "Mint compression info should be set when decompressed" ); // Compressed account should have zero sentinel values @@ -185,11 +182,11 @@ pub async fn assert_mint_action( assert_eq!( *actual_mint_account.data.as_ref().unwrap(), CompressedAccountData::default(), - "Compressed mint should have zero sentinel values when CMint is source of truth" + "Compressed mint should have zero sentinel values when Mint is source of truth" ); } else { // === CASE 3 & 4: Compressed account is source of truth after actions === - // (Either CompressAndCloseCMint happened OR was never decompressed) + // (Either CompressAndCloseMint happened OR was never decompressed) let actual_mint_account = rpc .indexer() .unwrap() @@ -199,7 +196,7 @@ pub async fn assert_mint_action( .value .expect("Compressed mint account not found"); - let actual_mint: CompressedMint = + let actual_mint: Mint = BorshDeserialize::deserialize(&mut actual_mint_account.data.unwrap().data.as_slice()) .expect("Failed to deserialize compressed mint"); @@ -217,18 +214,18 @@ pub async fn assert_mint_action( ); } - // If CompressAndCloseCMint, verify CMint Solana account is closed - if has_compress_and_close_cmint { - let cmint_pda = Pubkey::from(pre_compressed_mint.metadata.mint); + // If CompressAndCloseMint, verify Mint Solana account is closed + if has_compress_and_close_mint { + let mint_pda = Pubkey::from(pre_compressed_mint.metadata.mint); - let cmint_account = rpc - .get_account(cmint_pda) + let mint_account = rpc + .get_account(mint_pda) .await - .expect("Failed to fetch CMint account"); + .expect("Failed to fetch Mint account"); assert!( - cmint_account.is_none(), - "CMint PDA account should not exist after CompressAndCloseCMint action" + mint_account.is_none(), + "Mint PDA account should not exist after CompressAndCloseMint action" ); } // Verify Light Token accounts for MintToCToken actions diff --git a/program-tests/utils/src/assert_mint_to_compressed.rs b/program-tests/utils/src/assert_mint_to_compressed.rs index 9e0514b3ab..1dac9a5eef 100644 --- a/program-tests/utils/src/assert_mint_to_compressed.rs +++ b/program-tests/utils/src/assert_mint_to_compressed.rs @@ -6,7 +6,7 @@ use light_client::{ }; use light_compressed_token::instructions::create_token_pool::find_token_pool_pda_with_index; use light_token_interface::{ - instructions::mint_action::Recipient, state::CompressedMint, LIGHT_TOKEN_PROGRAM_ID, + instructions::mint_action::Recipient, state::Mint, LIGHT_TOKEN_PROGRAM_ID, }; use light_token_sdk::compressed_token::create_compressed_mint::derive_mint_from_spl_mint; use solana_sdk::{program_pack::Pack, pubkey::Pubkey}; @@ -16,7 +16,7 @@ pub async fn assert_mint_to_compressed( spl_mint_pda: Pubkey, recipients: &[Recipient], pre_token_pool_account: Option, - pre_compressed_mint: CompressedMint, + pre_compressed_mint: Mint, pre_spl_mint: Option, ) -> Vec { // Derive compressed mint address from SPL mint PDA (same as instruction) @@ -86,7 +86,7 @@ pub async fn assert_mint_to_compressed( .value .expect("Compressed mint account not found"); - let actual_compressed_mint: CompressedMint = BorshDeserialize::deserialize( + let actual_compressed_mint: Mint = BorshDeserialize::deserialize( &mut updated_compressed_mint_account .data .unwrap() @@ -105,7 +105,7 @@ pub async fn assert_mint_to_compressed( ); // If mint is decompressed and pre_token_pool_account is provided, validate SPL mint and token pool - if actual_compressed_mint.metadata.cmint_decompressed { + if actual_compressed_mint.metadata.mint_decompressed { if let Some(pre_pool_account) = pre_token_pool_account { // Validate SPL mint supply let spl_mint_data = rpc @@ -165,7 +165,7 @@ pub async fn assert_mint_to_compressed_one( recipient: Pubkey, expected_amount: u64, pre_token_pool_account: Option, - pre_compressed_mint: CompressedMint, + pre_compressed_mint: Mint, pre_spl_mint: Option, ) -> light_client::indexer::CompressedTokenAccount { let recipients = vec![Recipient { diff --git a/program-tests/utils/src/mint_assert.rs b/program-tests/utils/src/mint_assert.rs index d2e7f2ce2f..115ff2141c 100644 --- a/program-tests/utils/src/mint_assert.rs +++ b/program-tests/utils/src/mint_assert.rs @@ -1,7 +1,7 @@ use anchor_lang::prelude::borsh::BorshDeserialize; use light_token_interface::{ instructions::extensions::TokenMetadataInstructionData, - state::{BaseMint, CompressedMint, CompressedMintMetadata, ExtensionStruct, ACCOUNT_TYPE_MINT}, + state::{BaseMint, ExtensionStruct, Mint, MintMetadata, ACCOUNT_TYPE_MINT}, }; use solana_sdk::pubkey::Pubkey; @@ -16,13 +16,13 @@ pub fn assert_compressed_mint_account( mint_authority: Pubkey, freeze_authority: Pubkey, metadata: Option, -) -> CompressedMint { +) -> Mint { // Derive mint_signer from spl_mint_pda by reversing the PDA derivation // We need to find the mint_signer and bump used to create spl_mint_pda // spl_mint_pda = PDA([COMPRESSED_MINT_SEED, mint_signer], program_id) // Since we can't reverse this, we extract it from the actual compressed mint data let compressed_account_data = compressed_mint_account.data.clone().unwrap(); - let actual_compressed_mint: CompressedMint = + let actual_compressed_mint: Mint = BorshDeserialize::deserialize(&mut compressed_account_data.data.as_slice()).unwrap(); let mint_signer = actual_compressed_mint.metadata.mint_signer; let bump = actual_compressed_mint.metadata.bump; @@ -44,7 +44,7 @@ pub fn assert_compressed_mint_account( }); // Create expected compressed mint for comparison - let expected_compressed_mint = CompressedMint { + let expected_compressed_mint = Mint { base: BaseMint { mint_authority: Some(mint_authority.into()), supply: 0, @@ -52,10 +52,10 @@ pub fn assert_compressed_mint_account( is_initialized: true, freeze_authority: Some(freeze_authority.into()), }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, mint: spl_mint_pda.into(), - cmint_decompressed: false, + mint_decompressed: false, mint_signer, bump, }, @@ -80,8 +80,8 @@ pub fn assert_compressed_mint_account( light_compressed_token::constants::COMPRESSED_MINT_DISCRIMINATOR ); - // Deserialize and verify the CompressedMint struct matches expected - let compressed_mint: CompressedMint = + // Deserialize and verify the Mint struct matches expected + let compressed_mint: Mint = BorshDeserialize::deserialize(&mut compressed_account_data.data.as_slice()).unwrap(); println!("Compressed Mint: {:?}", compressed_mint); assert_eq!(compressed_mint, expected_compressed_mint); diff --git a/programs/compressed-token/anchor/src/lib.rs b/programs/compressed-token/anchor/src/lib.rs index e1ef6c680f..b981c1e184 100644 --- a/programs/compressed-token/anchor/src/lib.rs +++ b/programs/compressed-token/anchor/src/lib.rs @@ -441,7 +441,7 @@ pub enum ErrorCode { #[msg("Duplicate mint index detected in inputs, outputs, or compressions")] DuplicateMint, // 6102 #[msg("Invalid compressed mint address derivation")] - MintActionInvalidCompressedMintAddress, // 6103 + MintActionInvalidMintAddress, // 6103 #[msg("Invalid CPI context for create mint operation")] MintActionInvalidCpiContextForCreateMint, // 6104 #[msg("Invalid address tree pubkey in CPI context")] diff --git a/programs/compressed-token/program/src/compressed_token/mint_action/accounts.rs b/programs/compressed-token/program/src/compressed_token/mint_action/accounts.rs index e0ad97d6d8..77c3d88f38 100644 --- a/programs/compressed-token/program/src/compressed_token/mint_action/accounts.rs +++ b/programs/compressed-token/program/src/compressed_token/mint_action/accounts.rs @@ -430,7 +430,7 @@ impl AccountsConfig { let has_compress_and_close_cmint_action = parsed_instruction_data .actions .iter() - .any(|action| matches!(action, ZAction::CompressAndCloseCMint(_))); + .any(|action| matches!(action, ZAction::CompressAndCloseMint(_))); // Validation: Cannot combine DecompressMint and CompressAndCloseCMint in the same instruction if has_decompress_mint_action && has_compress_and_close_cmint_action { diff --git a/programs/compressed-token/program/src/compressed_token/mint_action/actions/compress_and_close_cmint.rs b/programs/compressed-token/program/src/compressed_token/mint_action/actions/compress_and_close_cmint.rs index 3c2d8d330a..618050a52b 100644 --- a/programs/compressed-token/program/src/compressed_token/mint_action/actions/compress_and_close_cmint.rs +++ b/programs/compressed-token/program/src/compressed_token/mint_action/actions/compress_and_close_cmint.rs @@ -1,9 +1,7 @@ use anchor_compressed_token::ErrorCode; use anchor_lang::prelude::ProgramError; use light_program_profiler::profile; -use light_token_interface::{ - instructions::mint_action::ZCompressAndCloseCMintAction, state::CompressedMint, -}; +use light_token_interface::{instructions::mint_action::ZCompressAndCloseMintAction, state::Mint}; use pinocchio::{ pubkey::pubkey_eq, sysvars::{clock::Clock, Sysvar}, @@ -34,20 +32,20 @@ use crate::{ /// provided is_compressible() returns true. All lamports are returned to rent_sponsor. #[profile] pub fn process_compress_and_close_cmint_action( - action: &ZCompressAndCloseCMintAction, - compressed_mint: &mut CompressedMint, + action: &ZCompressAndCloseMintAction, + compressed_mint: &mut Mint, validated_accounts: &MintActionAccounts, ) -> Result<(), ProgramError> { // NOTE: CompressAndCloseCMint is permissionless - anyone can compress if is_compressible() returns true // All lamports returned to rent_sponsor // 1. Idempotent check - if CMint doesn't exist and idempotent is set, return early exit error to skip CPI - if action.is_idempotent() && !compressed_mint.metadata.cmint_decompressed { + if action.is_idempotent() && !compressed_mint.metadata.mint_decompressed { return Err(ErrorCode::IdempotentEarlyExit.into()); } // 2. Check CMint exists (is decompressed) - if !compressed_mint.metadata.cmint_decompressed { - msg!("CMint does not exist (cmint_decompressed = false)"); + if !compressed_mint.metadata.mint_decompressed { + msg!("CMint does not exist (mint_decompressed = false)"); return Err(ErrorCode::CMintNotDecompressed.into()); } @@ -107,8 +105,8 @@ pub fn process_compress_and_close_cmint_action( } cmint.resize(0).map_err(convert_program_error)?; } - // 8. Set cmint_decompressed = false - compressed_mint.metadata.cmint_decompressed = false; + // 8. Set mint_decompressed = false + compressed_mint.metadata.mint_decompressed = false; // 9. Zero out compression info - only relevant when account is decompressed // When compressed back to a compressed account, this info should be cleared diff --git a/programs/compressed-token/program/src/compressed_token/mint_action/actions/create_mint.rs b/programs/compressed-token/program/src/compressed_token/mint_action/actions/create_mint.rs index aaff1bf925..5105cd0b3e 100644 --- a/programs/compressed-token/program/src/compressed_token/mint_action/actions/create_mint.rs +++ b/programs/compressed-token/program/src/compressed_token/mint_action/actions/create_mint.rs @@ -76,7 +76,7 @@ pub fn process_create_mint_action( // (derived from mint PDA, CMINT_ADDRESS_TREE, and LIGHT_TOKEN_PROGRAM_ID) if address != mint.metadata.compressed_address() { msg!("Invalid compressed mint address derivation"); - return Err(ErrorCode::MintActionInvalidCompressedMintAddress.into()); + return Err(ErrorCode::MintActionInvalidMintAddress.into()); } } @@ -108,9 +108,9 @@ pub fn process_create_mint_action( return Err(ErrorCode::MintActionUnsupportedVersion.into()); } - // Validate cmint_decompressed is false for new mint creation - if mint.metadata.cmint_decompressed != 0 { - msg!("New mint must start without CMint decompressed"); + // Validate mint_decompressed is false for new mint creation + if mint.metadata.mint_decompressed != 0 { + msg!("mint.metadata.mint_decompressed must be false"); return Err(ErrorCode::MintActionInvalidCompressionState.into()); } diff --git a/programs/compressed-token/program/src/compressed_token/mint_action/actions/decompress_mint.rs b/programs/compressed-token/program/src/compressed_token/mint_action/actions/decompress_mint.rs index 288b1c6c43..9b0d4c98c1 100644 --- a/programs/compressed-token/program/src/compressed_token/mint_action/actions/decompress_mint.rs +++ b/programs/compressed-token/program/src/compressed_token/mint_action/actions/decompress_mint.rs @@ -4,7 +4,7 @@ use light_array_map::pubkey_eq; use light_compressible::compression_info::CompressionInfo; use light_program_profiler::profile; use light_token_interface::{ - instructions::mint_action::ZDecompressMintAction, state::CompressedMint, COMPRESSED_MINT_SEED, + instructions::mint_action::ZDecompressMintAction, state::Mint, COMPRESSED_MINT_SEED, }; use pinocchio::{ account_info::AccountInfo, @@ -41,12 +41,12 @@ use crate::{ #[profile] pub fn process_decompress_mint_action( action: &ZDecompressMintAction, - compressed_mint: &mut CompressedMint, + compressed_mint: &mut Mint, validated_accounts: &MintActionAccounts, fee_payer: &AccountInfo, ) -> Result<(), ProgramError> { // 1. Check not already decompressed - if compressed_mint.metadata.cmint_decompressed { + if compressed_mint.metadata.mint_decompressed { msg!("CMint account already exists"); return Err(ErrorCode::CMintAlreadyExists.into()); } @@ -183,8 +183,8 @@ pub fn process_decompress_mint_action( .invoke() .map_err(convert_program_error)?; - // 8. Set the cmint_decompressed flag - compressed_mint.metadata.cmint_decompressed = true; + // 8. Set the mint_decompressed flag + compressed_mint.metadata.mint_decompressed = true; Ok(()) } diff --git a/programs/compressed-token/program/src/compressed_token/mint_action/actions/mint_to.rs b/programs/compressed-token/program/src/compressed_token/mint_action/actions/mint_to.rs index cdf372d35a..997ac5e93f 100644 --- a/programs/compressed-token/program/src/compressed_token/mint_action/actions/mint_to.rs +++ b/programs/compressed-token/program/src/compressed_token/mint_action/actions/mint_to.rs @@ -4,8 +4,7 @@ use light_compressed_account::Pubkey; use light_program_profiler::profile; use light_sdk_pinocchio::instruction::ZOutputCompressedAccountWithPackedContextMut; use light_token_interface::{ - hash_cache::HashCache, instructions::mint_action::ZMintToCompressedAction, - state::CompressedMint, + hash_cache::HashCache, instructions::mint_action::ZMintToCompressedAction, state::Mint, }; use crate::{ @@ -24,7 +23,7 @@ use crate::{ #[profile] pub fn process_mint_to_compressed_action<'a>( action: &ZMintToCompressedAction, - compressed_mint: &mut CompressedMint, + compressed_mint: &mut Mint, validated_accounts: &MintActionAccounts, output_accounts_iter: &mut impl Iterator< Item = &'a mut ZOutputCompressedAccountWithPackedContextMut<'a>, diff --git a/programs/compressed-token/program/src/compressed_token/mint_action/actions/mint_to_ctoken.rs b/programs/compressed-token/program/src/compressed_token/mint_action/actions/mint_to_ctoken.rs index 60cc19acae..d1e24029e1 100644 --- a/programs/compressed-token/program/src/compressed_token/mint_action/actions/mint_to_ctoken.rs +++ b/programs/compressed-token/program/src/compressed_token/mint_action/actions/mint_to_ctoken.rs @@ -3,7 +3,7 @@ use anchor_lang::solana_program::program_error::ProgramError; use light_account_checks::packed_accounts::ProgramPackedAccounts; use light_compressed_account::Pubkey; use light_program_profiler::profile; -use light_token_interface::{instructions::mint_action::ZMintToAction, state::CompressedMint}; +use light_token_interface::{instructions::mint_action::ZMintToAction, state::Mint}; use pinocchio::account_info::AccountInfo; use crate::compressed_token::{ @@ -15,7 +15,7 @@ use crate::compressed_token::{ #[profile] pub fn process_mint_to_ctoken_action( action: &ZMintToAction, - compressed_mint: &mut CompressedMint, + compressed_mint: &mut Mint, validated_accounts: &MintActionAccounts, packed_accounts: &ProgramPackedAccounts<'_, AccountInfo>, mint: Pubkey, diff --git a/programs/compressed-token/program/src/compressed_token/mint_action/actions/process_actions.rs b/programs/compressed-token/program/src/compressed_token/mint_action/actions/process_actions.rs index 10a05ea675..7803aa539e 100644 --- a/programs/compressed-token/program/src/compressed_token/mint_action/actions/process_actions.rs +++ b/programs/compressed-token/program/src/compressed_token/mint_action/actions/process_actions.rs @@ -7,7 +7,7 @@ use light_program_profiler::profile; use light_token_interface::{ hash_cache::HashCache, instructions::mint_action::{ZAction, ZMintActionCompressedInstructionData}, - state::CompressedMint, + state::Mint, TokenError, }; use pinocchio::account_info::AccountInfo; @@ -45,7 +45,7 @@ pub fn process_actions<'a>( hash_cache: &mut HashCache, queue_indices: &QueueIndices, packed_accounts: &ProgramPackedAccounts<'_, AccountInfo>, - compressed_mint: &mut CompressedMint, + compressed_mint: &mut Mint, ) -> Result<(), ProgramError> { // Array to accumulate transfer amounts by account index let mut transfer_map = [0u64; MAX_PACKED_ACCOUNTS]; @@ -143,7 +143,7 @@ pub fn process_actions<'a>( fee_payer, )?; } - ZAction::CompressAndCloseCMint(action) => { + ZAction::CompressAndCloseMint(action) => { process_compress_and_close_cmint_action( action, compressed_mint, diff --git a/programs/compressed-token/program/src/compressed_token/mint_action/actions/update_metadata.rs b/programs/compressed-token/program/src/compressed_token/mint_action/actions/update_metadata.rs index 592319f676..e37a1d9c00 100644 --- a/programs/compressed-token/program/src/compressed_token/mint_action/actions/update_metadata.rs +++ b/programs/compressed-token/program/src/compressed_token/mint_action/actions/update_metadata.rs @@ -6,7 +6,7 @@ use light_token_interface::{ instructions::mint_action::{ ZRemoveMetadataKeyAction, ZUpdateMetadataAuthorityAction, ZUpdateMetadataFieldAction, }, - state::{CompressedMint, ExtensionStruct, TokenMetadata}, + state::{ExtensionStruct, Mint, TokenMetadata}, }; use spl_pod::solana_msg::msg; @@ -16,7 +16,7 @@ use crate::compressed_token::mint_action::check_authority; #[profile] #[track_caller] fn get_metadata_extension_mut<'a>( - compressed_mint: &'a mut CompressedMint, + compressed_mint: &'a mut Mint, extension_index: usize, operation_name: &str, signer: &pinocchio::pubkey::Pubkey, @@ -54,7 +54,7 @@ fn get_metadata_extension_mut<'a>( #[profile] pub fn process_update_metadata_field_action( action: &ZUpdateMetadataFieldAction, - compressed_mint: &mut CompressedMint, + compressed_mint: &mut Mint, signer: &pinocchio::pubkey::Pubkey, ) -> Result<(), ProgramError> { let metadata = get_metadata_extension_mut( @@ -103,7 +103,7 @@ pub fn process_update_metadata_field_action( #[profile] pub fn process_update_metadata_authority_action( action: &ZUpdateMetadataAuthorityAction, - compressed_mint: &mut CompressedMint, + compressed_mint: &mut Mint, signer: &pinocchio::pubkey::Pubkey, ) -> Result<(), ProgramError> { let metadata = get_metadata_extension_mut( @@ -126,7 +126,7 @@ pub fn process_update_metadata_authority_action( #[profile] pub fn process_remove_metadata_key_action( action: &ZRemoveMetadataKeyAction, - compressed_mint: &mut CompressedMint, + compressed_mint: &mut Mint, signer: &pinocchio::pubkey::Pubkey, ) -> Result<(), ProgramError> { let metadata = get_metadata_extension_mut( diff --git a/programs/compressed-token/program/src/compressed_token/mint_action/mint_input.rs b/programs/compressed-token/program/src/compressed_token/mint_action/mint_input.rs index 7b79d81884..c350ce74df 100644 --- a/programs/compressed-token/program/src/compressed_token/mint_action/mint_input.rs +++ b/programs/compressed-token/program/src/compressed_token/mint_action/mint_input.rs @@ -4,7 +4,7 @@ use light_compressed_account::instruction_data::with_readonly::ZInAccountMut; use light_hasher::{sha256::Sha256BE, Hasher}; use light_program_profiler::profile; use light_sdk::instruction::PackedMerkleContext; -use light_token_interface::state::CompressedMint; +use light_token_interface::state::Mint; use light_zero_copy::U16; use crate::{ @@ -25,7 +25,7 @@ pub fn create_input_compressed_mint_account( root_index: U16, merkle_context: PackedMerkleContext, accounts_config: &AccountsConfig, - compressed_mint: &CompressedMint, + compressed_mint: &Mint, ) -> Result<(), ProgramError> { // When CMint was decompressed (input state BEFORE actions), use zero values let (discriminator, input_data_hash) = if accounts_config.cmint_decompressed { diff --git a/programs/compressed-token/program/src/compressed_token/mint_action/mint_output.rs b/programs/compressed-token/program/src/compressed_token/mint_action/mint_output.rs index 8d4b17e122..272d2ea2e2 100644 --- a/programs/compressed-token/program/src/compressed_token/mint_action/mint_output.rs +++ b/programs/compressed-token/program/src/compressed_token/mint_action/mint_output.rs @@ -7,7 +7,7 @@ use light_hasher::{sha256::Sha256BE, Hasher}; use light_program_profiler::profile; use light_token_interface::{ hash_cache::HashCache, instructions::mint_action::ZMintActionCompressedInstructionData, - state::CompressedMint, + state::Mint, }; use pinocchio::sysvars::{clock::Clock, Sysvar}; use spl_pod::solana_msg::msg; @@ -31,7 +31,7 @@ pub fn process_output_compressed_account<'a>( output_compressed_accounts: &'a mut [ZOutputCompressedAccountWithPackedContextMut<'a>], hash_cache: &mut HashCache, queue_indices: &QueueIndices, - mut compressed_mint: CompressedMint, + mut compressed_mint: Mint, accounts_config: &AccountsConfig, ) -> Result<(), ProgramError> { let (mint_account, token_accounts) = split_mint_and_token_accounts(output_compressed_accounts); @@ -46,7 +46,7 @@ pub fn process_output_compressed_account<'a>( &mut compressed_mint, )?; - if compressed_mint.metadata.cmint_decompressed { + if compressed_mint.metadata.mint_decompressed { serialize_decompressed_mint(validated_accounts, accounts_config, &mut compressed_mint)?; } @@ -70,7 +70,7 @@ fn split_mint_and_token_accounts<'a>( fn serialize_compressed_mint<'a>( mint_account: &'a mut ZOutputCompressedAccountWithPackedContextMut<'a>, - compressed_mint: CompressedMint, + compressed_mint: Mint, queue_indices: &QueueIndices, ) -> Result<(), ProgramError> { let compressed_account_data = mint_account @@ -79,7 +79,7 @@ fn serialize_compressed_mint<'a>( .as_mut() .ok_or(ErrorCode::MintActionOutputSerializationFailed)?; - let (discriminator, data_hash) = if compressed_mint.metadata.cmint_decompressed { + let (discriminator, data_hash) = if compressed_mint.metadata.mint_decompressed { if !compressed_account_data.data.is_empty() { msg!( "Data allocation for output mint account is wrong: {} (expected) != {} ", @@ -128,7 +128,7 @@ fn serialize_compressed_mint<'a>( fn serialize_decompressed_mint( validated_accounts: &MintActionAccounts, accounts_config: &AccountsConfig, - compressed_mint: &mut CompressedMint, + compressed_mint: &mut Mint, ) -> Result<(), ProgramError> { let cmint_account = validated_accounts .get_cmint() diff --git a/programs/compressed-token/program/src/compressed_token/mint_action/processor.rs b/programs/compressed-token/program/src/compressed_token/mint_action/processor.rs index fe79915982..dd4cebc985 100644 --- a/programs/compressed-token/program/src/compressed_token/mint_action/processor.rs +++ b/programs/compressed-token/program/src/compressed_token/mint_action/processor.rs @@ -4,7 +4,7 @@ use light_compressed_account::instruction_data::with_readonly::InstructionDataIn use light_sdk::instruction::PackedMerkleContext; use light_token_interface::{ hash_cache::HashCache, instructions::mint_action::MintActionCompressedInstructionData, - state::CompressedMint, TokenError, + state::Mint, TokenError, }; use light_zero_copy::{traits::ZeroCopyAt, ZeroCopyNew}; use pinocchio::account_info::AccountInfo; @@ -51,7 +51,7 @@ pub fn process_mint_action( .mint .as_ref() .ok_or(ErrorCode::MintDataRequired)?; - CompressedMint::try_from(mint_data)? + Mint::try_from(mint_data)? } else if !accounts_config.cmint_decompressed { // Existing compressed mint with data in instruction // In case that cmint is not actually compressed proof verification will fail. @@ -59,13 +59,13 @@ pub fn process_mint_action( .mint .as_ref() .ok_or(ErrorCode::MintDataRequired)?; - CompressedMint::try_from(mint_data)? + Mint::try_from(mint_data)? } else { // CMint is decompressed - read from CMint account let cmint_account = validated_accounts .get_cmint() .ok_or(ErrorCode::MintActionMissingCMintAccount)?; - CompressedMint::from_account_info_checked(cmint_account)? + Mint::from_account_info_checked(cmint_account)? }; let (config, mut cpi_bytes, _) = diff --git a/programs/compressed-token/program/src/compressed_token/mint_action/zero_copy_config.rs b/programs/compressed-token/program/src/compressed_token/mint_action/zero_copy_config.rs index a7c1767bf1..7d2d803023 100644 --- a/programs/compressed-token/program/src/compressed_token/mint_action/zero_copy_config.rs +++ b/programs/compressed-token/program/src/compressed_token/mint_action/zero_copy_config.rs @@ -4,7 +4,7 @@ use light_compressed_account::instruction_data::with_readonly::InstructionDataIn use light_program_profiler::profile; use light_token_interface::{ instructions::mint_action::{ZAction, ZMintActionCompressedInstructionData}, - state::{CompressedMint, CompressedMintConfig}, + state::{Mint, MintConfig}, }; use spl_pod::solana_msg::msg; use tinyvec::ArrayVec; @@ -24,12 +24,12 @@ use crate::{ pub fn get_zero_copy_configs( parsed_instruction_data: &ZMintActionCompressedInstructionData<'_>, accounts_config: &AccountsConfig, - cmint: &CompressedMint, + cmint: &Mint, ) -> Result< ( InstructionDataInvokeCpiWithReadOnlyConfig, Vec, - CompressedMintConfig, + MintConfig, ), ProgramError, > { @@ -64,7 +64,7 @@ pub fn get_zero_copy_configs( } // Output mint config (always present) with final authority states - let output_mint_config = CompressedMintConfig { + let output_mint_config = MintConfig { extensions: if output_extensions_config.is_empty() { None } else { diff --git a/programs/compressed-token/program/src/compressible/claim.rs b/programs/compressed-token/program/src/compressible/claim.rs index eca3eb5321..d3588dfee2 100644 --- a/programs/compressed-token/program/src/compressible/claim.rs +++ b/programs/compressed-token/program/src/compressible/claim.rs @@ -4,7 +4,7 @@ use light_account_checks::{checks::check_owner, AccountInfoTrait, AccountIterato use light_compressible::{compression_info::ClaimAndUpdate, config::CompressibleConfig}; use light_program_profiler::profile; use light_token_interface::{ - state::{CompressedMint, Token, ACCOUNT_TYPE_MINT, ACCOUNT_TYPE_TOKEN_ACCOUNT}, + state::{Mint, Token, ACCOUNT_TYPE_MINT, ACCOUNT_TYPE_TOKEN_ACCOUNT}, TokenError, }; use pinocchio::{account_info::AccountInfo, sysvars::Sysvar}; @@ -146,7 +146,7 @@ fn validate_and_claim( } ACCOUNT_TYPE_MINT => { // CMint account - let (mut cmint, _) = CompressedMint::zero_copy_at_mut_checked(&mut account_data)?; + let (mut cmint, _) = Mint::zero_copy_at_mut_checked(&mut account_data)?; cmint .base .compression diff --git a/programs/compressed-token/program/src/shared/compressible_top_up.rs b/programs/compressed-token/program/src/shared/compressible_top_up.rs index bf0abc5e80..6797766f96 100644 --- a/programs/compressed-token/program/src/shared/compressible_top_up.rs +++ b/programs/compressed-token/program/src/shared/compressible_top_up.rs @@ -2,7 +2,7 @@ use anchor_lang::solana_program::program_error::ProgramError; use light_program_profiler::profile; #[cfg(target_os = "solana")] use light_token_interface::state::{ - cmint_top_up_lamports_from_account_info, top_up_lamports_from_account_info_unchecked, + mint_top_up_lamports_from_account_info, top_up_lamports_from_account_info_unchecked, }; use light_token_interface::TokenError; use pinocchio::{ @@ -50,7 +50,7 @@ pub fn calculate_and_execute_compressible_top_ups<'a>( // Calculate CMint top-up using optimized function (owner check inside) #[cfg(target_os = "solana")] - if let Some(amount) = cmint_top_up_lamports_from_account_info(cmint, &mut current_slot) { + if let Some(amount) = mint_top_up_lamports_from_account_info(cmint, &mut current_slot) { transfers[0].amount = amount; lamports_budget = lamports_budget.saturating_sub(amount); } diff --git a/programs/compressed-token/program/src/shared/cpi_bytes_size.rs b/programs/compressed-token/program/src/shared/cpi_bytes_size.rs index 17a4e6d717..303989c62e 100644 --- a/programs/compressed-token/program/src/shared/cpi_bytes_size.rs +++ b/programs/compressed-token/program/src/shared/cpi_bytes_size.rs @@ -10,7 +10,7 @@ use light_compressed_account::{ }, }; use light_program_profiler::profile; -use light_token_interface::state::CompressedMint; +use light_token_interface::state::Mint; use light_zero_copy::ZeroCopyNew; use pinocchio::program_error::ProgramError; use tinyvec::ArrayVec; @@ -21,8 +21,8 @@ const MAX_OUTPUT_ACCOUNTS: usize = 35; /// Calculate data length for a compressed mint account #[profile] #[inline(always)] -pub fn mint_data_len(config: &light_token_interface::state::CompressedMintConfig) -> u32 { - CompressedMint::byte_len(config).unwrap() as u32 +pub fn mint_data_len(config: &light_token_interface::state::MintConfig) -> u32 { + Mint::byte_len(config).unwrap() as u32 } /// Calculate data length for a compressed token account @@ -49,7 +49,7 @@ impl CpiConfigInput { pub fn mint_to_compressed( num_recipients: usize, has_proof: bool, - output_mint_config: &light_token_interface::state::CompressedMintConfig, + output_mint_config: &light_token_interface::state::MintConfig, ) -> Self { let mut outputs = ArrayVec::new(); @@ -73,7 +73,7 @@ impl CpiConfigInput { #[profile] pub fn update_mint( has_proof: bool, - output_mint_config: &light_token_interface::state::CompressedMintConfig, + output_mint_config: &light_token_interface::state::MintConfig, ) -> Self { let mut inputs = ArrayVec::new(); inputs.push(true); // Input mint has address diff --git a/programs/compressed-token/program/tests/allocation_test.rs b/programs/compressed-token/program/tests/allocation_test.rs index 2128c9bdb3..cc6554f7f0 100644 --- a/programs/compressed-token/program/tests/allocation_test.rs +++ b/programs/compressed-token/program/tests/allocation_test.rs @@ -4,15 +4,15 @@ use light_compressed_token::shared::cpi_bytes_size::{ allocate_invoke_with_read_only_cpi_bytes, cpi_bytes_config, CpiConfigInput, }; use light_token_interface::state::{ - extensions::TokenMetadataConfig, CompressedMint, CompressedMintConfig, ExtensionStructConfig, + extensions::TokenMetadataConfig, ExtensionStructConfig, Mint, MintConfig, }; use light_zero_copy::{traits::ZeroCopyAt, ZeroCopyNew}; #[test] fn test_extension_allocation_only() { // Test 1: No extensions - should work - let mint_config_no_ext = CompressedMintConfig { extensions: None }; - let expected_mint_size_no_ext = CompressedMint::byte_len(&mint_config_no_ext).unwrap(); + let mint_config_no_ext = MintConfig { extensions: None }; + let expected_mint_size_no_ext = Mint::byte_len(&mint_config_no_ext).unwrap(); let mut outputs_no_ext = tinyvec::ArrayVec::<[(bool, u32); 35]>::new(); outputs_no_ext.push((true, expected_mint_size_no_ext as u32)); // Mint account has address @@ -40,10 +40,10 @@ fn test_extension_allocation_only() { additional_metadata: vec![], // No additional metadata })]; - let mint_config_with_ext = CompressedMintConfig { + let mint_config_with_ext = MintConfig { extensions: Some(extensions_config.clone()), }; - let expected_mint_size_with_ext = CompressedMint::byte_len(&mint_config_with_ext).unwrap(); + let expected_mint_size_with_ext = Mint::byte_len(&mint_config_with_ext).unwrap(); let mut outputs_with_ext = tinyvec::ArrayVec::<[(bool, u32); 35]>::new(); outputs_with_ext.push((true, expected_mint_size_with_ext as u32)); // Mint account has address @@ -109,12 +109,12 @@ fn test_extension_allocation_only() { available_space, expected_mint_size_with_ext ); - // Test that we can create a CompressedMint with the allocated space (zero-copy compatibility) + // Test that we can create a Mint with the allocated space (zero-copy compatibility) let mint_test_data = vec![0u8; available_space]; - let test_mint_result = CompressedMint::zero_copy_at(&mint_test_data); + let test_mint_result = Mint::zero_copy_at(&mint_test_data); assert!( test_mint_result.is_ok(), - "Allocated space should be valid for zero-copy CompressedMint creation" + "Allocated space should be valid for zero-copy Mint creation" ); println!( @@ -149,11 +149,11 @@ fn test_progressive_extension_sizes() { additional_metadata: vec![], })]; - let mint_config = CompressedMintConfig { + let mint_config = MintConfig { extensions: Some(extensions_config), }; - let expected_mint_size = CompressedMint::byte_len(&mint_config).unwrap(); + let expected_mint_size = Mint::byte_len(&mint_config).unwrap(); println!("Expected mint size: {}", expected_mint_size); let mut outputs = tinyvec::ArrayVec::<[(bool, u32); 35]>::new(); @@ -210,10 +210,10 @@ fn test_progressive_extension_sizes() { name_len, symbol_len, uri_len, available_space, expected_mint_size ); - // Test zero-copy compatibility - verify allocated space can be used for CompressedMint + // Test zero-copy compatibility - verify allocated space can be used for Mint let mint_test_data = vec![0u8; available_space]; - let test_mint_result = CompressedMint::zero_copy_at(&mint_test_data); - assert!(test_mint_result.is_ok(), "Sizes name={}, symbol={}, uri={}: Allocated space should be valid for zero-copy CompressedMint", name_len, symbol_len, uri_len); + let test_mint_result = Mint::zero_copy_at(&mint_test_data); + assert!(test_mint_result.is_ok(), "Sizes name={}, symbol={}, uri={}: Allocated space should be valid for zero-copy Mint", name_len, symbol_len, uri_len); println!("✅ Success - Allocation verified for sizes: name={}, symbol={}, uri={} - {} bytes exactly allocated", name_len, symbol_len, uri_len, available_space); } else { diff --git a/programs/compressed-token/program/tests/exact_allocation_test.rs b/programs/compressed-token/program/tests/exact_allocation_test.rs index bd60eab799..6d2efd647b 100644 --- a/programs/compressed-token/program/tests/exact_allocation_test.rs +++ b/programs/compressed-token/program/tests/exact_allocation_test.rs @@ -5,7 +5,7 @@ use light_compressed_token::shared::cpi_bytes_size::{ }; use light_token_interface::state::{ extensions::{AdditionalMetadataConfig, TokenMetadataConfig}, - CompressedMint, CompressedMintConfig, ExtensionStructConfig, + ExtensionStructConfig, Mint, MintConfig, }; use light_zero_copy::{traits::ZeroCopyAt, ZeroCopyNew}; @@ -34,11 +34,11 @@ fn test_exact_allocation_assertion() { println!("Extension config: {:?}", extensions_config); // Step 1: Calculate expected mint size - let mint_config = CompressedMintConfig { + let mint_config = MintConfig { extensions: Some(extensions_config.clone()), }; - let expected_mint_size = CompressedMint::byte_len(&mint_config).unwrap(); + let expected_mint_size = Mint::byte_len(&mint_config).unwrap(); println!("Expected mint size: {} bytes", expected_mint_size); // Step 2: Calculate CPI allocation @@ -83,8 +83,8 @@ fn test_exact_allocation_assertion() { // Step 5: Calculate exact space needed let base_mint_size_no_ext = { - let no_ext_config = CompressedMintConfig { extensions: None }; - CompressedMint::byte_len(&no_ext_config).unwrap() + let no_ext_config = MintConfig { extensions: None }; + Mint::byte_len(&no_ext_config).unwrap() }; let extension_space_needed = expected_mint_size - base_mint_size_no_ext; @@ -194,10 +194,10 @@ fn test_exact_allocation_assertion() { ); println!("Allocated data space: {} bytes", available_data_space); - // The critical assertion: allocated space should exactly match CompressedMint::byte_len() + // The critical assertion: allocated space should exactly match Mint::byte_len() assert_eq!( available_data_space, expected_mint_size, - "Allocated bytes ({}) must exactly equal CompressedMint::byte_len() ({})", + "Allocated bytes ({}) must exactly equal Mint::byte_len() ({})", available_data_space, expected_mint_size ); @@ -225,19 +225,19 @@ fn test_exact_allocation_assertion() { available_space, expected_mint_size ); - // Test zero-copy compatibility - verify allocated space can be used for CompressedMint + // Test zero-copy compatibility - verify allocated space can be used for Mint let mint_test_data = vec![0u8; available_space]; - let test_mint_result = CompressedMint::zero_copy_at(&mint_test_data); + let test_mint_result = Mint::zero_copy_at(&mint_test_data); assert!( test_mint_result.is_ok(), - "Allocated space should be valid for zero-copy CompressedMint creation" + "Allocated space should be valid for zero-copy Mint creation" ); } else { panic!("Output account must have data space allocated"); } println!("✅ SUCCESS: Perfect allocation match!"); - println!(" allocated_bytes = CompressedMint::byte_len()"); + println!(" allocated_bytes = Mint::byte_len()"); println!(" {} = {}", available_data_space, expected_mint_size); // Note: The difference between our manual calculation and actual struct size @@ -285,11 +285,11 @@ fn test_allocation_with_various_metadata_sizes() { additional_metadata: additional_metadata_configs, })]; - let mint_config = CompressedMintConfig { + let mint_config = MintConfig { extensions: Some(extensions_config.clone()), }; - let expected_mint_size = CompressedMint::byte_len(&mint_config).unwrap(); + let expected_mint_size = Mint::byte_len(&mint_config).unwrap(); let mut outputs = tinyvec::ArrayVec::<[(bool, u32); 35]>::new(); outputs.push((true, expected_mint_size as u32)); // Mint account has address and uses calculated size @@ -359,12 +359,12 @@ fn test_allocation_with_various_metadata_sizes() { expected_mint_size ); - // Test zero-copy compatibility - verify allocated space can be used for CompressedMint + // Test zero-copy compatibility - verify allocated space can be used for Mint let mint_test_data = vec![0u8; allocated_space]; - let test_mint_result = CompressedMint::zero_copy_at(&mint_test_data); + let test_mint_result = Mint::zero_copy_at(&mint_test_data); assert!( test_mint_result.is_ok(), - "Test case {}: Allocated space should be valid for zero-copy CompressedMint", + "Test case {}: Allocated space should be valid for zero-copy Mint", i + 1 ); } else { diff --git a/programs/compressed-token/program/tests/mint.rs b/programs/compressed-token/program/tests/mint.rs index 6a1a720313..94e2de1d75 100644 --- a/programs/compressed-token/program/tests/mint.rs +++ b/programs/compressed-token/program/tests/mint.rs @@ -13,12 +13,11 @@ use light_compressed_token::{ use light_token_interface::{ instructions::{ extensions::{ExtensionInstructionData, TokenMetadataInstructionData}, - mint_action::{CompressedMintInstructionData, MintActionCompressedInstructionData}, + mint_action::{MintActionCompressedInstructionData, MintInstructionData}, }, state::{ - AdditionalMetadata, AdditionalMetadataConfig, BaseMint, CompressedMint, - CompressedMintMetadata, CompressionInfo, ExtensionStruct, TokenMetadata, ZCompressedMint, - ZExtensionStruct, ACCOUNT_TYPE_MINT, + AdditionalMetadata, AdditionalMetadataConfig, BaseMint, CompressionInfo, ExtensionStruct, + Mint, MintMetadata, TokenMetadata, ZExtensionStruct, ZMint, ACCOUNT_TYPE_MINT, }, CMINT_ADDRESS_TREE, COMPRESSED_MINT_SEED, LIGHT_TOKEN_PROGRAM_ID, }; @@ -61,7 +60,7 @@ fn test_rnd_create_compressed_mint_account() { // Generate random supplies let input_supply = rng.gen_range(0..=u64::MAX); let _output_supply = rng.gen_range(0..=u64::MAX); - let cmint_decompressed = rng.gen_bool(0.1); + let mint_decompressed = rng.gen_bool(0.1); // Generate random merkle context let merkle_tree_pubkey_index = rng.gen_range(0..=255u8); @@ -118,15 +117,15 @@ fn test_rnd_create_compressed_mint_account() { None }; - // Step 2: Create CompressedMintInstructionData using current API + // Step 2: Create MintInstructionData using current API // mint_signer and bump were derived at the start of the iteration - let mint_instruction_data = CompressedMintInstructionData { + let mint_instruction_data = MintInstructionData { supply: input_supply, decimals, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version, mint: mint_pda, - cmint_decompressed, + mint_decompressed, mint_signer: mint_signer.to_bytes(), bump, }, @@ -157,9 +156,9 @@ fn test_rnd_create_compressed_mint_account() { // Derive AccountsConfig from parsed instruction data (same as processor) let accounts_config = AccountsConfig::new(&parsed_instruction_data).unwrap(); - // Derive CompressedMint from instruction data (same as processor) + // Derive Mint from instruction data (same as processor) let mint_data = parsed_instruction_data.mint.as_ref().unwrap(); - let cmint = CompressedMint::try_from(mint_data).unwrap(); + let cmint = Mint::try_from(mint_data).unwrap(); let (config, mut cpi_bytes, output_mint_config) = get_zero_copy_configs(&parsed_instruction_data, &accounts_config, &cmint).unwrap(); @@ -204,8 +203,7 @@ fn test_rnd_create_compressed_mint_account() { println!("Borsh serialized {} bytes", borsh_bytes.len()); // Test 2: Deserialize with zero_copy_at - let (zc_mint, remaining) = - CompressedMintInstructionData::zero_copy_at(&borsh_bytes).unwrap(); + let (zc_mint, remaining) = MintInstructionData::zero_copy_at(&borsh_bytes).unwrap(); assert!(remaining.is_empty(), "Should consume all bytes"); // Test 3: Verify data matches between borsh and zero-copy @@ -217,8 +215,8 @@ fn test_rnd_create_compressed_mint_account() { assert_eq!(zc_mint.supply.get(), output_mint_data.supply); assert_eq!(zc_mint.decimals, output_mint_data.decimals); assert_eq!( - zc_mint.metadata.cmint_decompressed != 0, - output_mint_data.metadata.cmint_decompressed + zc_mint.metadata.mint_decompressed != 0, + output_mint_data.metadata.mint_decompressed ); if let (Some(zc_mint_auth), Some(orig_mint_auth)) = ( @@ -268,7 +266,7 @@ fn test_rnd_create_compressed_mint_account() { } // Test 5: Test the CPI allocation is correct - let expected_mint_size = CompressedMint::byte_len(&output_mint_config).unwrap(); + let expected_mint_size = Mint::byte_len(&output_mint_config).unwrap(); let output_account = &cpi_instruction_struct.output_compressed_accounts[0]; let compressed_account_data = output_account .compressed_account @@ -325,13 +323,13 @@ fn test_rnd_create_compressed_mint_account() { "Output account data must match expected mint size" ); - // Test that the allocated space is sufficient for a zero-copy CompressedMint creation + // Test that the allocated space is sufficient for a zero-copy Mint creation // (This verifies allocation correctness without requiring populated data) let test_mint_data = vec![0u8; account_data.data.len()]; - let test_result = CompressedMint::zero_copy_at(&test_mint_data); + let test_result = Mint::zero_copy_at(&test_mint_data); assert!( test_result.is_ok(), - "Allocated space should be valid for zero-copy CompressedMint creation" + "Allocated space should be valid for zero-copy Mint creation" ); // COMPLETE STRUCT ASSERTION: This verifies the entire CPI instruction structure is valid @@ -364,7 +362,7 @@ fn test_rnd_create_compressed_mint_account() { fn test_compressed_mint_borsh_zero_copy_compatibility() { use light_zero_copy::traits::ZeroCopyAt; - // Create CompressedMint with token metadata extension + // Create Mint with token metadata extension let token_metadata = TokenMetadata { update_authority: Pubkey::new_from_array([1; 32]), mint: Pubkey::new_from_array([2; 32]), @@ -374,7 +372,7 @@ fn test_compressed_mint_borsh_zero_copy_compatibility() { additional_metadata: vec![], }; - let compressed_mint = CompressedMint { + let compressed_mint = Mint { compression: CompressionInfo::default(), base: BaseMint { mint_authority: Some(Pubkey::new_from_array([4; 32])), @@ -383,10 +381,10 @@ fn test_compressed_mint_borsh_zero_copy_compatibility() { is_initialized: true, freeze_authority: None, }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3u8, mint: Pubkey::new_from_array([3; 32]), - cmint_decompressed: false, + mint_decompressed: false, mint_signer: [5; 32], bump: 255, }, @@ -399,8 +397,7 @@ fn test_compressed_mint_borsh_zero_copy_compatibility() { let borsh_bytes = borsh::to_vec(&compressed_mint).unwrap(); // Deserialize with zero_copy_at - let (zc_mint, remaining): (ZCompressedMint<'_>, &[u8]) = - CompressedMint::zero_copy_at(&borsh_bytes).unwrap(); + let (zc_mint, remaining): (ZMint<'_>, &[u8]) = Mint::zero_copy_at(&borsh_bytes).unwrap(); assert!(remaining.is_empty()); // COMPLETE STRUCT ASSERTION: Test borsh round-trip compatibility (UNIT_TESTING.md requirement) @@ -430,7 +427,7 @@ fn test_compressed_mint_borsh_zero_copy_compatibility() { } }; - let reconstructed_mint = CompressedMint { + let reconstructed_mint = Mint { compression, base: BaseMint { mint_authority: zc_mint.base.mint_authority().cloned(), @@ -439,10 +436,10 @@ fn test_compressed_mint_borsh_zero_copy_compatibility() { is_initialized: zc_mint.base.is_initialized != 0, freeze_authority: zc_mint.base.freeze_authority().cloned(), }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: zc_mint.base.metadata.version, mint: zc_mint.base.metadata.mint, - cmint_decompressed: zc_mint.base.metadata.cmint_decompressed != 0, + mint_decompressed: zc_mint.base.metadata.mint_decompressed != 0, mint_signer: zc_mint.base.metadata.mint_signer, bump: zc_mint.base.metadata.bump, }, diff --git a/programs/compressed-token/program/tests/mint_action.rs b/programs/compressed-token/program/tests/mint_action.rs index 1f62f31645..52ca50cc41 100644 --- a/programs/compressed-token/program/tests/mint_action.rs +++ b/programs/compressed-token/program/tests/mint_action.rs @@ -9,13 +9,13 @@ use light_token_interface::{ instructions::{ extensions::{token_metadata::TokenMetadataInstructionData, ExtensionInstructionData}, mint_action::{ - Action, CompressedMintInstructionData, CpiContext, CreateMint, - MintActionCompressedInstructionData, MintToAction, MintToCompressedAction, Recipient, + Action, CpiContext, CreateMint, MintActionCompressedInstructionData, + MintInstructionData, MintToAction, MintToCompressedAction, Recipient, RemoveMetadataKeyAction, UpdateAuthority, UpdateMetadataAuthorityAction, UpdateMetadataFieldAction, }, }, - state::CompressedMintMetadata, + state::MintMetadata, CMINT_ADDRESS_TREE, }; use light_zero_copy::traits::ZeroCopyAt; @@ -37,10 +37,10 @@ fn random_optional_pubkey(rng: &mut StdRng, probability: f64) -> Option } } -fn random_compressed_mint_metadata(rng: &mut StdRng) -> CompressedMintMetadata { - CompressedMintMetadata { +fn random_compressed_mint_metadata(rng: &mut StdRng) -> MintMetadata { + MintMetadata { version: rng.gen_range(1..=3) as u8, - cmint_decompressed: rng.gen_bool(0.5), + mint_decompressed: rng.gen_bool(0.5), mint: random_pubkey(rng), mint_signer: rng.gen::<[u8; 32]>(), bump: rng.gen(), @@ -162,7 +162,7 @@ fn generate_random_instruction_data( let mut mint_metadata = random_compressed_mint_metadata(rng); if let Some(spl_init) = force_spl_initialized { - mint_metadata.cmint_decompressed = spl_init && create_mint.is_none(); + mint_metadata.mint_decompressed = spl_init && create_mint.is_none(); } // Generate actions @@ -189,7 +189,7 @@ fn generate_random_instruction_data( } else { None }, - mint: Some(CompressedMintInstructionData { + mint: Some(MintInstructionData { supply: rng.gen_range(0..=1_000_000_000), decimals: rng.gen_range(0..=9), metadata: mint_metadata, @@ -222,8 +222,8 @@ fn compute_expected_config(data: &MintActionCompressedInstructionData) -> Accoun .iter() .any(|action| matches!(action, Action::MintToCompressed(_))); - // 4. cmint_decompressed - only based on metadata flag (matches AccountsConfig::new) - let cmint_decompressed = data.mint.as_ref().unwrap().metadata.cmint_decompressed; + // 4. mint_decompressed - only based on metadata flag (matches AccountsConfig::new) + let cmint_decompressed = data.mint.as_ref().unwrap().metadata.mint_decompressed; // 5. with_mint_signer let with_mint_signer = data.create_mint.is_some(); @@ -241,7 +241,7 @@ fn compute_expected_config(data: &MintActionCompressedInstructionData) -> Accoun let has_compress_and_close_cmint_action = data .actions .iter() - .any(|action| matches!(action, Action::CompressAndCloseCMint(_))); + .any(|action| matches!(action, Action::CompressAndCloseMint(_))); AccountsConfig { with_cpi_context, @@ -337,18 +337,18 @@ fn check_if_config_should_error(instruction_data: &MintActionCompressedInstructi .iter() .any(|action| matches!(action, Action::MintToCompressed(_))); - // cmint_decompressed is only from metadata flag (matches AccountsConfig::new) - let cmint_decompressed = instruction_data + // mint_decompressed is only from metadata flag (matches AccountsConfig::new) + let mint_decompressed = instruction_data .mint .as_ref() .unwrap() .metadata - .cmint_decompressed; + .mint_decompressed; // Error conditions matching AccountsConfig::new: // 1. has_mint_to_ctoken (MintToCToken actions not allowed) - // 2. cmint_decompressed && require_token_output_queue (mint decompressed + MintToCompressed not allowed) - has_mint_to_ctoken || (cmint_decompressed && require_token_output_queue) + // 2. mint_decompressed && require_token_output_queue (mint decompressed + MintToCompressed not allowed) + has_mint_to_ctoken || (mint_decompressed && require_token_output_queue) } else { false } diff --git a/sdk-libs/program-test/src/compressible.rs b/sdk-libs/program-test/src/compressible.rs index 6406f1b58b..a3c6180b94 100644 --- a/sdk-libs/program-test/src/compressible.rs +++ b/sdk-libs/program-test/src/compressible.rs @@ -18,9 +18,7 @@ use light_compressible::rent::SLOTS_PER_EPOCH; #[cfg(feature = "devenv")] use light_sdk::compressible::CompressibleConfig as CpdaCompressibleConfig; #[cfg(feature = "devenv")] -use light_token_interface::state::{ - CompressedMint, Token, ACCOUNT_TYPE_MINT, ACCOUNT_TYPE_TOKEN_ACCOUNT, -}; +use light_token_interface::state::{Mint, Token, ACCOUNT_TYPE_MINT, ACCOUNT_TYPE_TOKEN_ACCOUNT}; #[cfg(feature = "devenv")] use solana_pubkey::Pubkey; @@ -77,9 +75,9 @@ fn extract_compression_info(data: &[u8]) -> Option<(CompressionInfo, u8, bool)> Some((compression_info, account_type, compression_only)) } ACCOUNT_TYPE_MINT => { - let cmint = CompressedMint::deserialize(&mut &data[..]).ok()?; - // CMint accounts don't have compression_only, default to false - Some((cmint.compression, account_type, false)) + let mint = Mint::deserialize(&mut &data[..]).ok()?; + // Mint accounts don't have compression_only, default to false + Some((mint.compression, account_type, false)) } _ => None, } @@ -150,7 +148,7 @@ pub async fn claim_and_compress( let forester_keypair = rpc.test_accounts.protocol.forester.insecure_clone(); let payer = rpc.get_payer().insecure_clone(); - // Get all compressible token/mint accounts (both Token and CMint) + // Get all compressible token/mint accounts (both Token and Mint) let compressible_ctoken_accounts = rpc .context .get_program_accounts(&light_compressed_token::ID); @@ -236,7 +234,7 @@ pub async fn claim_and_compress( } Some(claimable_amount) if claimable_amount > 0 => { // Has rent to claim from completed epochs - // Both Token and CMint can be claimed + // Both Token and Mint can be claimed claim_accounts.push(*pubkey); } Some(_) => { @@ -429,13 +427,13 @@ async fn compress_cmint_forester( RpcError::CustomError(format!("CMint account {} not found", cmint_pubkey)) })?; - // Deserialize CMint to get compressed_address and rent_sponsor - let cmint: CompressedMint = + // Deserialize Mint to get compressed_address and rent_sponsor + let mint: Mint = BorshDeserialize::deserialize(&mut cmint_account.data.as_slice()) - .map_err(|e| RpcError::CustomError(format!("Failed to deserialize CMint: {:?}", e)))?; + .map_err(|e| RpcError::CustomError(format!("Failed to deserialize Mint: {:?}", e)))?; - let compressed_mint_address = cmint.metadata.compressed_address(); - let rent_sponsor = Pubkey::from(cmint.compression.rent_sponsor); + let compressed_mint_address = mint.metadata.compressed_address(); + let rent_sponsor = Pubkey::from(mint.compression.rent_sponsor); // Get the compressed mint account from indexer let compressed_mint_account = rpc diff --git a/sdk-libs/token-client/src/actions/mint_action.rs b/sdk-libs/token-client/src/actions/mint_action.rs index b13071d0e2..9f7d2c886f 100644 --- a/sdk-libs/token-client/src/actions/mint_action.rs +++ b/sdk-libs/token-client/src/actions/mint_action.rs @@ -70,10 +70,10 @@ pub async fn mint_action_comprehensive( mint_seed: &Keypair, authority: &Keypair, payer: &Keypair, - // Whether to decompress the mint to a CMint Solana account (with rent params) + // Whether to decompress the mint to a Mint Solana account (with rent params) decompress_mint: Option, - // Whether to compress and close the CMint Solana account - compress_and_close_cmint: bool, + // Whether to compress and close the Mint Solana account + compress_and_close_mint: bool, mint_to_recipients: Vec, mint_to_decompressed_recipients: Vec, update_mint_authority: Option, @@ -140,9 +140,9 @@ pub async fn mint_action_comprehensive( }); } - // Add CompressAndCloseCMint action if requested - if compress_and_close_cmint { - actions.push(MintActionType::CompressAndCloseCMint { idempotent: false }); + // Add CompressAndCloseMint action if requested + if compress_and_close_mint { + actions.push(MintActionType::CompressAndCloseMint { idempotent: false }); } // Determine if mint_signer is needed - matches onchain logic: diff --git a/sdk-libs/token-client/src/actions/update_compressed_mint.rs b/sdk-libs/token-client/src/actions/update_compressed_mint.rs index a6ee9b5d1d..48afb42748 100644 --- a/sdk-libs/token-client/src/actions/update_compressed_mint.rs +++ b/sdk-libs/token-client/src/actions/update_compressed_mint.rs @@ -2,7 +2,7 @@ use light_client::{ indexer::Indexer, rpc::{Rpc, RpcError}, }; -use light_token_types::CompressedMintAuthorityType; +use light_token_types::MintAuthorityType; use solana_keypair::Keypair; use solana_pubkey::Pubkey; use solana_signature::Signature; @@ -28,7 +28,7 @@ use crate::instructions::update_compressed_mint::update_compressed_mint_instruct #[allow(clippy::too_many_arguments)] pub async fn update_compressed_mint_authority( rpc: &mut R, - authority_type: CompressedMintAuthorityType, + authority_type: MintAuthorityType, current_authority: &Keypair, new_authority: Option, mint_authority: Option, @@ -74,7 +74,7 @@ pub async fn update_mint_authority( ) -> Result { update_compressed_mint_authority( rpc, - CompressedMintAuthorityType::MintTokens, + MintAuthorityType::MintTokens, current_mint_authority, new_mint_authority, Some(compressed_mint_merkle_tree), @@ -100,7 +100,7 @@ pub async fn update_freeze_authority( ) -> Result { update_compressed_mint_authority( rpc, - CompressedMintAuthorityType::FreezeAccount, + MintAuthorityType::FreezeAccount, current_freeze_authority, new_freeze_authority, Some(mint_authority), diff --git a/sdk-libs/token-client/src/instructions/create_mint.rs b/sdk-libs/token-client/src/instructions/create_mint.rs index 3993e4d02c..e7a58225f9 100644 --- a/sdk-libs/token-client/src/instructions/create_mint.rs +++ b/sdk-libs/token-client/src/instructions/create_mint.rs @@ -2,18 +2,18 @@ use light_client::{ indexer::Indexer, rpc::{Rpc, RpcError}, }; -use light_token_interface::instructions::extensions::{ - token_metadata::TokenMetadataInstructionData, ExtensionInstructionData, -}; -use light_token_sdk::token::{ - derive_mint_compressed_address, find_mint_address, CreateMint, CreateMintParams, -}; +use light_token_interface::instructions::extensions::token_metadata::TokenMetadataInstructionData; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey; use solana_signer::Signer; -/// Create a compressed mint instruction with automatic setup. +use crate::instructions::mint_action::{create_mint_action_instruction, MintActionParams, NewMint}; + +/// Create a compressed-only mint instruction (no decompression). +/// +/// This creates ONLY the compressed mint account, NOT the Mint Solana account. +/// Use DecompressMint action to create the Mint Solana account later if needed. /// /// # Arguments /// * `rpc` - RPC client with indexer capabilities @@ -35,55 +35,31 @@ pub async fn create_compressed_mint_instruction( payer: Pubkey, metadata: Option, ) -> Result { - // Get address tree and output queue from RPC + // Get address tree for deriving compressed mint address let address_tree_pubkey = rpc.get_address_tree_v2().tree; - - let output_queue = rpc.get_random_state_tree_info()?.queue; - - let compressed_mint_address = - derive_mint_compressed_address(&mint_seed.pubkey(), &address_tree_pubkey); - - // Create extensions if metadata is provided - let extensions = metadata.map(|meta| vec![ExtensionInstructionData::TokenMetadata(meta)]); - - // Get validity proof for address creation - let rpc_result = rpc - .get_validity_proof( - vec![], - vec![light_client::indexer::AddressWithTree { - address: compressed_mint_address, - tree: address_tree_pubkey, - }], - None, - ) - .await? - .value; - - let address_merkle_tree_root_index = rpc_result.addresses[0].root_index; - let (mint, bump) = find_mint_address(&mint_seed.pubkey()); - // Build params struct manually - let params = CreateMintParams { - decimals, - address_merkle_tree_root_index, - mint_authority, - proof: rpc_result.proof.0.unwrap(), - compression_address: compressed_mint_address, - mint, - bump, - freeze_authority, - extensions, - }; - - // Create instruction builder - let builder = CreateMint::new( - params, - mint_seed.pubkey(), - payer, - address_tree_pubkey, - output_queue, + let compressed_mint_address = light_token_sdk::token::derive_mint_compressed_address( + &mint_seed.pubkey(), + &address_tree_pubkey, ); - builder - .instruction() - .map_err(|e| RpcError::CustomError(format!("Token SDK error: {:?}", e))) + // Create compressed-only mint using MintAction with empty actions + create_mint_action_instruction( + rpc, + MintActionParams { + compressed_mint_address, + mint_seed: mint_seed.pubkey(), + authority: mint_authority, + payer, + actions: vec![], // No actions - just create compressed mint + new_mint: Some(NewMint { + decimals, + supply: 0, + mint_authority, + freeze_authority, + metadata, + version: 3, + }), + }, + ) + .await } diff --git a/sdk-libs/token-client/src/instructions/mint_action.rs b/sdk-libs/token-client/src/instructions/mint_action.rs index 606c18caac..376ae8dbe0 100644 --- a/sdk-libs/token-client/src/instructions/mint_action.rs +++ b/sdk-libs/token-client/src/instructions/mint_action.rs @@ -9,13 +9,13 @@ use light_token_interface::{ instructions::{ extensions::{token_metadata::TokenMetadataInstructionData, ExtensionInstructionData}, mint_action::{ - CompressAndCloseCMintAction, CompressedMintWithContext, DecompressMintAction, - MintActionCompressedInstructionData, MintToAction, MintToCompressedAction, Recipient, + CompressAndCloseMintAction, DecompressMintAction, MintActionCompressedInstructionData, + MintToAction, MintToCompressedAction, MintWithContext, Recipient, RemoveMetadataKeyAction, UpdateAuthority, UpdateMetadataAuthorityAction, UpdateMetadataFieldAction, }, }, - state::CompressedMint, + state::Mint, LIGHT_TOKEN_PROGRAM_ID, }; use light_token_sdk::compressed_token::{ @@ -66,18 +66,18 @@ pub enum MintActionType { key: Vec, idempotent: u8, }, - /// Decompress the compressed mint to a CMint Solana account. - /// CMint is always compressible - rent_payment must be >= 2. + /// Decompress the compressed mint to a Mint Solana account. + /// Mint is always compressible - rent_payment must be >= 2. DecompressMint { /// Rent payment in epochs (prepaid). Must be >= 2. rent_payment: u8, /// Lamports allocated for future write operations (top-up per write). write_top_up: u32, }, - /// Compress and close a CMint Solana account. The compressed mint state is preserved. + /// Compress and close a Mint Solana account. The compressed mint state is preserved. /// Permissionless - anyone can call if is_compressible() returns true (rent expired). - CompressAndCloseCMint { - /// If true, succeed silently when CMint doesn't exist + CompressAndCloseMint { + /// If true, succeed silently when Mint doesn't exist idempotent: bool, }, } @@ -119,11 +119,11 @@ pub async fn create_mint_action_instruction( .iter() .any(|a| matches!(a, MintActionType::DecompressMint { .. })); - // Check if CompressAndCloseCMint action is present - let has_compress_and_close_cmint = params + // Check if CompressAndCloseMint action is present + let has_compress_and_close_mint = params .actions .iter() - .any(|a| matches!(a, MintActionType::CompressAndCloseCMint { .. })); + .any(|a| matches!(a, MintActionType::CompressAndCloseMint { .. })); // Get address tree and output queue info let address_tree_pubkey = rpc.get_address_tree_v2().tree; @@ -149,27 +149,26 @@ pub async fn create_mint_action_instruction( })?; let (mint_pda, bump) = find_mint_address(¶ms.mint_seed); - let mint_data = - light_token_interface::instructions::mint_action::CompressedMintInstructionData { - supply: new_mint.supply, - decimals: new_mint.decimals, - metadata: light_token_interface::state::CompressedMintMetadata { - version: new_mint.version, - mint: mint_pda.to_bytes().into(), - // false for new mint - on-chain sets to true after DecompressMint - cmint_decompressed: false, - mint_signer: params.mint_seed.to_bytes(), - bump, - }, - mint_authority: Some(new_mint.mint_authority.to_bytes().into()), - freeze_authority: new_mint.freeze_authority.map(|auth| auth.to_bytes().into()), - extensions: new_mint - .metadata - .as_ref() - .map(|meta| vec![ExtensionInstructionData::TokenMetadata(meta.clone())]), - }; - - let compressed_mint_inputs = CompressedMintWithContext { + let mint_data = light_token_interface::instructions::mint_action::MintInstructionData { + supply: new_mint.supply, + decimals: new_mint.decimals, + metadata: light_token_interface::state::MintMetadata { + version: new_mint.version, + mint: mint_pda.to_bytes().into(), + // false for new mint - on-chain sets to true after DecompressMint + mint_decompressed: false, + mint_signer: params.mint_seed.to_bytes(), + bump, + }, + mint_authority: Some(new_mint.mint_authority.to_bytes().into()), + freeze_authority: new_mint.freeze_authority.map(|auth| auth.to_bytes().into()), + extensions: new_mint + .metadata + .as_ref() + .map(|meta| vec![ExtensionInstructionData::TokenMetadata(meta.clone())]), + }; + + let compressed_mint_inputs = MintWithContext { prove_by_index: false, // Use full proof for creation leaf_index: 0, // Not applicable for creation root_index: rpc_proof_result.addresses[0].root_index, @@ -193,8 +192,8 @@ pub async fn create_mint_action_instruction( params.compressed_mint_address )))?; - // Try to deserialize the compressed mint - may be None if CMint is source of truth - let compressed_mint: Option = compressed_mint_account + // Try to deserialize the compressed mint - may be None if Mint is source of truth + let compressed_mint: Option = compressed_mint_account .data .as_ref() .and_then(|d| BorshDeserialize::deserialize(&mut d.data.as_slice()).ok()); @@ -204,7 +203,7 @@ pub async fn create_mint_action_instruction( .await? .value; - let compressed_mint_inputs = CompressedMintWithContext { + let compressed_mint_inputs = MintWithContext { prove_by_index: rpc_proof_result.accounts[0].root_index.proof_by_index(), leaf_index: compressed_mint_account.leaf_index, root_index: rpc_proof_result.accounts[0] @@ -311,8 +310,8 @@ pub async fn create_mint_action_instruction( rent_payment, write_top_up, }), - MintActionType::CompressAndCloseCMint { idempotent } => instruction_data - .with_compress_and_close_cmint(CompressAndCloseCMintAction { + MintActionType::CompressAndCloseMint { idempotent } => instruction_data + .with_compress_and_close_mint(CompressAndCloseMintAction { idempotent: if idempotent { 1 } else { 0 }, }), }; @@ -347,9 +346,9 @@ pub async fn create_mint_action_instruction( config = config.with_token_accounts(ctoken_accounts); } - // Add compressible CMint accounts if DecompressMint or CompressAndCloseCMint action is present - if has_decompress_mint || has_compress_and_close_cmint { - let (cmint_pda, _) = find_mint_address(¶ms.mint_seed); + // Add compressible Mint accounts if DecompressMint or CompressAndCloseMint action is present + if has_decompress_mint || has_compress_and_close_mint { + let (mint_pda, _) = find_mint_address(¶ms.mint_seed); // Get config and rent_sponsor from v1 config PDA let config_address = CompressibleConfig::light_token_v1_config_pda(); let compressible_config: CompressibleConfig = rpc @@ -362,12 +361,12 @@ pub async fn create_mint_action_instruction( )) })?; config = config.with_compressible_mint( - cmint_pda, + mint_pda, config_address, compressible_config.rent_sponsor, ); // DecompressMint does NOT need mint_signer - it uses compressed_mint.metadata.mint_signer - // CompressAndCloseCMint does NOT need mint_signer - it verifies CMint via compressed_mint.metadata.mint + // CompressAndCloseMint does NOT need mint_signer - it verifies Mint via compressed_mint.metadata.mint } // Get account metas @@ -386,8 +385,8 @@ pub async fn create_mint_action_instruction( }) } -/// Parameters for decompressing a mint to a CMint Solana account. -/// CMint is always compressible. +/// Parameters for decompressing a mint to a Mint Solana account. +/// Mint is always compressible. #[derive(Debug, Clone)] pub struct DecompressMintParams { /// Rent payment in epochs (prepaid). Must be >= 2. @@ -412,7 +411,7 @@ pub async fn create_comprehensive_mint_action_instruction( mint_seed: &Keypair, authority: Pubkey, payer: Pubkey, - // Whether to decompress the mint to a CMint Solana account (with rent params) + // Whether to decompress the mint to a Mint Solana account (with rent params) decompress_mint: Option, mint_to_recipients: Vec<(Pubkey, u64)>, update_mint_authority: Option, diff --git a/sdk-libs/token-client/src/instructions/mint_to_compressed.rs b/sdk-libs/token-client/src/instructions/mint_to_compressed.rs index ea6bf7f507..35491ae755 100644 --- a/sdk-libs/token-client/src/instructions/mint_to_compressed.rs +++ b/sdk-libs/token-client/src/instructions/mint_to_compressed.rs @@ -4,8 +4,8 @@ use light_client::{ rpc::{Rpc, RpcError}, }; use light_token_interface::{ - instructions::mint_action::{CompressedMintWithContext, Recipient}, - state::{CompressedMint, TokenDataVersion}, + instructions::mint_action::{MintWithContext, Recipient}, + state::{Mint, TokenDataVersion}, }; use light_token_sdk::compressed_token::{ create_compressed_mint::derive_mint_from_spl_mint, @@ -39,8 +39,8 @@ pub async fn mint_to_compressed_instruction( compressed_mint_address )))?; - // Try to deserialize the compressed mint - may be None if CMint is source of truth - let compressed_mint: Option = compressed_mint_account + // Try to deserialize the compressed mint - may be None if Mint is source of truth + let compressed_mint: Option = compressed_mint_account .data .as_ref() .and_then(|d| BorshDeserialize::deserialize(&mut d.data.as_slice()).ok()); @@ -53,20 +53,20 @@ pub async fn mint_to_compressed_instruction( // Get state tree info for outputs let state_tree_info = rpc.get_random_state_tree_info()?; - // Check if CMint is decompressed (source of truth) - let cmint_decompressed = compressed_mint + // Check if Mint is decompressed (source of truth) + let mint_decompressed = compressed_mint .as_ref() - .map(|m| m.metadata.cmint_decompressed) - .unwrap_or(true); // If no data, assume CMint is source of truth + .map(|m| m.metadata.mint_decompressed) + .unwrap_or(true); // If no data, assume Mint is source of truth - if cmint_decompressed { - unimplemented!("SPL mint synchronization for decompressed CMint not yet implemented"); + if mint_decompressed { + unimplemented!("SPL mint synchronization for decompressed Mint not yet implemented"); } let decompressed_mint_config: Option> = None; let spl_interface_pda: Option = None; // Prepare compressed mint inputs - let compressed_mint_inputs = CompressedMintWithContext { + let compressed_mint_inputs = MintWithContext { prove_by_index: rpc_proof_result.accounts[0].root_index.proof_by_index(), leaf_index: compressed_mint_account.leaf_index, root_index: rpc_proof_result.accounts[0] @@ -87,7 +87,7 @@ pub async fn mint_to_compressed_instruction( payer, state_merkle_tree: compressed_mint_account.tree_info.tree, input_queue: compressed_mint_account.tree_info.queue, - output_queue_cmint: compressed_mint_account.tree_info.queue, + output_queue_mint: compressed_mint_account.tree_info.queue, output_queue_tokens: state_tree_info.queue, decompressed_mint_config, proof: rpc_proof_result.proof.into(), diff --git a/sdk-libs/token-client/src/instructions/update_compressed_mint.rs b/sdk-libs/token-client/src/instructions/update_compressed_mint.rs index 41abfc0d01..0b8f4e214a 100644 --- a/sdk-libs/token-client/src/instructions/update_compressed_mint.rs +++ b/sdk-libs/token-client/src/instructions/update_compressed_mint.rs @@ -4,13 +4,13 @@ use light_client::{ rpc::{Rpc, RpcError}, }; use light_token_interface::{ - instructions::mint_action::{CompressedMintInstructionData, CompressedMintWithContext}, - state::CompressedMint, + instructions::mint_action::{MintInstructionData, MintWithContext}, + state::Mint, }; use light_token_sdk::compressed_token::update_compressed_mint::{ - update_compressed_mint, UpdateCompressedMintInputs, + update_compressed_mint, UpdateMintInputs, }; -use light_token_types::CompressedMintAuthorityType; +use light_token_types::MintAuthorityType; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey; @@ -33,7 +33,7 @@ use solana_signer::Signer; #[allow(clippy::too_many_arguments)] pub async fn update_compressed_mint_instruction( rpc: &mut R, - authority_type: CompressedMintAuthorityType, + authority_type: MintAuthorityType, current_authority: &Keypair, new_authority: Option, mint_authority: Option, @@ -68,22 +68,22 @@ pub async fn update_compressed_mint_instruction( .ok_or_else(|| RpcError::CustomError("Compressed mint data not found".to_string()))?; // Deserialize the compressed mint - let compressed_mint: CompressedMint = + let compressed_mint: Mint = BorshDeserialize::deserialize(&mut compressed_mint_data.data.as_slice()).map_err(|e| { RpcError::CustomError(format!("Failed to deserialize compressed mint: {}", e)) })?; // Convert to instruction data format - let compressed_mint_instruction_data = - CompressedMintInstructionData::try_from(compressed_mint.clone()).map_err(|e| { - RpcError::CustomError(format!("Failed to convert compressed mint: {:?}", e)) - })?; + let compressed_mint_instruction_data = MintInstructionData::try_from(compressed_mint.clone()) + .map_err(|e| { + RpcError::CustomError(format!("Failed to convert compressed mint: {:?}", e)) + })?; // Get random state tree info for output queue let state_tree_info = rpc.get_random_state_tree_info()?; - // Create the CompressedMintWithContext - using similar pattern to mint_to_compressed - let compressed_mint_inputs = CompressedMintWithContext { + // Create the MintWithContext - using similar pattern to mint_to_compressed + let compressed_mint_inputs = MintWithContext { leaf_index: compressed_mint_leaf_index, prove_by_index: true, // Use index-based proof like mint_to_compressed root_index: 0, // Use 0 like mint_to_compressed @@ -92,7 +92,7 @@ pub async fn update_compressed_mint_instruction( }; // Create instruction using the existing SDK function - let inputs = UpdateCompressedMintInputs { + let inputs = UpdateMintInputs { compressed_mint_inputs, authority_type, new_authority, diff --git a/sdk-libs/token-sdk/Cargo.toml b/sdk-libs/token-sdk/Cargo.toml index 11b1f61435..5f54839537 100644 --- a/sdk-libs/token-sdk/Cargo.toml +++ b/sdk-libs/token-sdk/Cargo.toml @@ -43,7 +43,6 @@ solana-account-info = { workspace = true } solana-cpi = { workspace = true } solana-program-error = { workspace = true } arrayvec = { workspace = true } -spl-token-2022 = { workspace = true } spl-pod = { workspace = true } light-account-checks = { workspace = true, features = ["solana"] } light-sdk-types = { workspace = true, features = ["v2"] } diff --git a/sdk-libs/token-sdk/src/compressed_token/v2/create_compressed_mint/account_metas.rs b/sdk-libs/token-sdk/src/compressed_token/v2/create_compressed_mint/account_metas.rs index e03600533d..e86ae2f27c 100644 --- a/sdk-libs/token-sdk/src/compressed_token/v2/create_compressed_mint/account_metas.rs +++ b/sdk-libs/token-sdk/src/compressed_token/v2/create_compressed_mint/account_metas.rs @@ -5,15 +5,15 @@ use crate::utils::TokenDefaultAccounts; /// Account metadata configuration for create cMint instruction #[derive(Debug, Copy, Clone)] -pub struct CreateCompressedMintMetaConfig { +pub struct CreateMintMetaConfig { pub fee_payer: Option, pub mint_signer: Option, pub address_tree_pubkey: Pubkey, pub output_queue: Pubkey, } -impl CreateCompressedMintMetaConfig { - /// Create a new CreateCompressedMintMetaConfig for direct invocation +impl CreateMintMetaConfig { + /// Create a new CreateMintMetaConfig for direct invocation pub fn new( fee_payer: Pubkey, mint_signer: Pubkey, @@ -28,7 +28,7 @@ impl CreateCompressedMintMetaConfig { } } - /// Create a new CreateCompressedMintMetaConfig for client-side (CPI) usage + /// Create a new CreateMintMetaConfig for client-side (CPI) usage pub fn new_client( mint_seed: Pubkey, address_tree_pubkey: Pubkey, @@ -45,7 +45,7 @@ impl CreateCompressedMintMetaConfig { /// Get the standard account metas for a create cMint instruction pub fn get_create_compressed_mint_instruction_account_metas( - config: CreateCompressedMintMetaConfig, + config: CreateMintMetaConfig, ) -> Vec { let default_pubkeys = TokenDefaultAccounts::default(); @@ -122,13 +122,13 @@ pub fn get_create_compressed_mint_instruction_account_metas( } #[derive(Debug, Copy, Clone)] -pub struct CreateCompressedMintMetaConfigCpiWrite { +pub struct CreateMintMetaConfigCpiWrite { pub fee_payer: Pubkey, pub mint_signer: Pubkey, pub cpi_context: Pubkey, } pub fn get_create_compressed_mint_instruction_account_metas_cpi_write( - config: CreateCompressedMintMetaConfigCpiWrite, + config: CreateMintMetaConfigCpiWrite, ) -> [AccountMeta; 5] { let default_pubkeys = TokenDefaultAccounts::default(); [ diff --git a/sdk-libs/token-sdk/src/compressed_token/v2/create_compressed_mint/instruction.rs b/sdk-libs/token-sdk/src/compressed_token/v2/create_compressed_mint/instruction.rs index c497345534..5e8e254928 100644 --- a/sdk-libs/token-sdk/src/compressed_token/v2/create_compressed_mint/instruction.rs +++ b/sdk-libs/token-sdk/src/compressed_token/v2/create_compressed_mint/instruction.rs @@ -5,7 +5,7 @@ use light_token_interface::{ self, instructions::{ extensions::ExtensionInstructionData, - mint_action::{CompressedMintInstructionData, CpiContext}, + mint_action::{CpiContext, MintInstructionData}, }, COMPRESSED_MINT_SEED, }; @@ -24,7 +24,7 @@ use crate::{ /// Input struct for creating a compressed mint instruction #[derive(Debug, Clone, AnchorDeserialize, AnchorSerialize)] -pub struct CreateCompressedMintInputs { +pub struct CreateMintInputs { pub decimals: u8, pub mint_authority: Pubkey, pub freeze_authority: Option, @@ -40,18 +40,18 @@ pub struct CreateCompressedMintInputs { /// Creates a compressed mint instruction (wrapper around mint_action) pub fn create_compressed_mint_cpi( - input: CreateCompressedMintInputs, + input: CreateMintInputs, cpi_context: Option, cpi_context_pubkey: Option, ) -> Result { let (mint_pda, bump) = find_mint_address(&input.mint_signer); - let compressed_mint_instruction_data = CompressedMintInstructionData { + let compressed_mint_instruction_data = MintInstructionData { supply: 0, decimals: input.decimals, - metadata: light_token_interface::state::CompressedMintMetadata { + metadata: light_token_interface::state::MintMetadata { version: input.version, mint: mint_pda.to_bytes().into(), - cmint_decompressed: false, + mint_decompressed: false, mint_signer: input.mint_signer.to_bytes(), bump, }, @@ -103,7 +103,7 @@ pub fn create_compressed_mint_cpi( } #[derive(Debug, Clone, AnchorDeserialize, AnchorSerialize)] -pub struct CreateCompressedMintInputsCpiWrite { +pub struct CreateMintInputsCpiWrite { pub decimals: u8, pub mint_authority: Pubkey, pub freeze_authority: Option, @@ -117,9 +117,7 @@ pub struct CreateCompressedMintInputsCpiWrite { pub version: u8, } -pub fn create_compressed_mint_cpi_write( - input: CreateCompressedMintInputsCpiWrite, -) -> Result { +pub fn create_compressed_mint_cpi_write(input: CreateMintInputsCpiWrite) -> Result { if !input.cpi_context.first_set_context && !input.cpi_context.set_context { msg!( "Invalid CPI context first cpi set or set context must be true {:?}", @@ -129,13 +127,13 @@ pub fn create_compressed_mint_cpi_write( } let (mint_pda, bump) = find_mint_address(&input.mint_signer); - let compressed_mint_instruction_data = CompressedMintInstructionData { + let compressed_mint_instruction_data = MintInstructionData { supply: 0, decimals: input.decimals, - metadata: light_token_interface::state::CompressedMintMetadata { + metadata: light_token_interface::state::MintMetadata { version: input.version, mint: mint_pda.to_bytes().into(), - cmint_decompressed: false, + mint_decompressed: false, mint_signer: input.mint_signer.to_bytes(), bump, }, @@ -173,7 +171,7 @@ pub fn create_compressed_mint_cpi_write( } /// Creates a compressed mint instruction with automatic mint address derivation -pub fn create_compressed_mint(input: CreateCompressedMintInputs) -> Result { +pub fn create_compressed_mint(input: CreateMintInputs) -> Result { create_compressed_mint_cpi(input, None, None) } diff --git a/sdk-libs/token-sdk/src/compressed_token/v2/create_compressed_mint/mod.rs b/sdk-libs/token-sdk/src/compressed_token/v2/create_compressed_mint/mod.rs index 63fa502fe7..2c8c4a651f 100644 --- a/sdk-libs/token-sdk/src/compressed_token/v2/create_compressed_mint/mod.rs +++ b/sdk-libs/token-sdk/src/compressed_token/v2/create_compressed_mint/mod.rs @@ -2,10 +2,9 @@ pub mod account_metas; pub mod instruction; pub use account_metas::{ - get_create_compressed_mint_instruction_account_metas, CreateCompressedMintMetaConfig, + get_create_compressed_mint_instruction_account_metas, CreateMintMetaConfig, }; pub use instruction::{ create_compressed_mint, create_compressed_mint_cpi, create_compressed_mint_cpi_write, - derive_mint_compressed_address, derive_mint_from_spl_mint, find_mint_address, - CreateCompressedMintInputs, + derive_mint_compressed_address, derive_mint_from_spl_mint, find_mint_address, CreateMintInputs, }; diff --git a/sdk-libs/token-sdk/src/compressed_token/v2/mint_action/account_metas.rs b/sdk-libs/token-sdk/src/compressed_token/v2/mint_action/account_metas.rs index 9d900b5837..013af615d8 100644 --- a/sdk-libs/token-sdk/src/compressed_token/v2/mint_action/account_metas.rs +++ b/sdk-libs/token-sdk/src/compressed_token/v2/mint_action/account_metas.rs @@ -15,9 +15,9 @@ pub struct MintActionMetaConfig { pub tokens_out_queue: Option, // Output queue for new token accounts pub cpi_context: Option, pub token_accounts: Vec, // For mint_to_ctoken actions - pub cmint: Option, // CMint PDA account for DecompressMint action - pub compressible_config: Option, // CompressibleConfig account (when creating CMint) - pub rent_sponsor: Option, // Rent sponsor PDA (when creating CMint) + pub mint: Option, // Mint PDA account for DecompressMint action + pub compressible_config: Option, // CompressibleConfig account (when creating Mint) + pub rent_sponsor: Option, // Rent sponsor PDA (when creating Mint) pub mint_signer_must_sign: bool, // true for create_mint, false for decompress_mint } @@ -40,7 +40,7 @@ impl MintActionMetaConfig { tokens_out_queue: None, cpi_context: None, token_accounts: Vec::new(), - cmint: None, + mint: None, compressible_config: None, rent_sponsor: None, mint_signer_must_sign: true, @@ -65,7 +65,7 @@ impl MintActionMetaConfig { tokens_out_queue: None, cpi_context: None, token_accounts: Vec::new(), - cmint: None, + mint: None, compressible_config: None, rent_sponsor: None, mint_signer_must_sign: false, @@ -93,7 +93,7 @@ impl MintActionMetaConfig { tokens_out_queue: None, cpi_context: Some(cpi_context_pubkey), token_accounts: Vec::new(), - cmint: None, + mint: None, compressible_config: None, rent_sponsor: None, mint_signer_must_sign: false, @@ -110,8 +110,8 @@ impl MintActionMetaConfig { self } - pub fn with_mint(mut self, cmint: Pubkey) -> Self { - self.cmint = Some(cmint); + pub fn with_mint(mut self, mint: Pubkey) -> Self { + self.mint = Some(mint); self } @@ -123,15 +123,15 @@ impl MintActionMetaConfig { self } - /// Configure compressible CMint with config and rent sponsor. - /// CMint is always compressible - this sets all required accounts. + /// Configure compressible Mint with config and rent sponsor. + /// Mint is always compressible - this sets all required accounts. pub fn with_compressible_mint( mut self, - cmint: Pubkey, + mint: Pubkey, compressible_config: Pubkey, rent_sponsor: Pubkey, ) -> Self { - self.cmint = Some(cmint); + self.mint = Some(mint); self.compressible_config = Some(compressible_config); self.rent_sponsor = Some(rent_sponsor); self @@ -159,17 +159,17 @@ impl MintActionMetaConfig { metas.push(AccountMeta::new_readonly(self.authority, true)); - // CompressibleConfig account (when creating compressible CMint) + // CompressibleConfig account (when creating compressible Mint) if let Some(config) = self.compressible_config { metas.push(AccountMeta::new_readonly(config, false)); } - // CMint account is present when decompressing the mint (DecompressMint action) or syncing - if let Some(cmint) = self.cmint { - metas.push(AccountMeta::new(cmint, false)); + // Mint account is present when decompressing the mint (DecompressMint action) or syncing + if let Some(mint) = self.mint { + metas.push(AccountMeta::new(mint, false)); } - // Rent sponsor PDA (when creating compressible CMint) + // Rent sponsor PDA (when creating compressible Mint) if let Some(rent_sponsor) = self.rent_sponsor { metas.push(AccountMeta::new(rent_sponsor, false)); } diff --git a/sdk-libs/token-sdk/src/compressed_token/v2/mint_to_compressed/instruction.rs b/sdk-libs/token-sdk/src/compressed_token/v2/mint_to_compressed/instruction.rs index 18df3eca1c..5468dd6c24 100644 --- a/sdk-libs/token-sdk/src/compressed_token/v2/mint_to_compressed/instruction.rs +++ b/sdk-libs/token-sdk/src/compressed_token/v2/mint_to_compressed/instruction.rs @@ -1,7 +1,5 @@ use light_compressed_account::instruction_data::traits::LightInstructionData; -use light_token_interface::instructions::mint_action::{ - CompressedMintWithContext, CpiContext, Recipient, -}; +use light_token_interface::instructions::mint_action::{CpiContext, MintWithContext, Recipient}; pub use light_token_types::account_infos::mint_to_compressed::DecompressedMintConfig; use light_token_types::CompressedProof; use solana_instruction::Instruction; @@ -18,13 +16,13 @@ pub const MINT_TO_COMPRESSED_DISCRIMINATOR: u8 = 101; /// Input parameters for creating a mint_to_compressed instruction #[derive(Debug, Clone)] pub struct MintToCompressedInputs { - pub compressed_mint_inputs: CompressedMintWithContext, + pub compressed_mint_inputs: MintWithContext, pub recipients: Vec, pub mint_authority: Pubkey, pub payer: Pubkey, pub state_merkle_tree: Pubkey, pub input_queue: Pubkey, - pub output_queue_cmint: Pubkey, + pub output_queue_mint: Pubkey, pub output_queue_tokens: Pubkey, /// Required if the mint is decompressed pub decompressed_mint_config: Option>, @@ -47,7 +45,7 @@ pub fn create_mint_to_compressed_instruction( payer, state_merkle_tree, input_queue, - output_queue_cmint, + output_queue_mint, output_queue_tokens: _, decompressed_mint_config: _, proof, @@ -85,7 +83,7 @@ pub fn create_mint_to_compressed_instruction( mint_authority, state_merkle_tree, input_queue, - output_queue_cmint, + output_queue_mint, ) .with_mint_compressed_tokens() }; diff --git a/sdk-libs/token-sdk/src/compressed_token/v2/update_compressed_mint/account_metas.rs b/sdk-libs/token-sdk/src/compressed_token/v2/update_compressed_mint/account_metas.rs index 9ccab44773..bc12c50295 100644 --- a/sdk-libs/token-sdk/src/compressed_token/v2/update_compressed_mint/account_metas.rs +++ b/sdk-libs/token-sdk/src/compressed_token/v2/update_compressed_mint/account_metas.rs @@ -5,7 +5,7 @@ use crate::utils::TokenDefaultAccounts; /// Configuration for generating account metas for update compressed mint instruction #[derive(Debug, Clone)] -pub struct UpdateCompressedMintMetaConfig { +pub struct UpdateMintMetaConfig { pub fee_payer: Option, pub authority: Option, pub in_merkle_tree: Pubkey, @@ -16,7 +16,7 @@ pub struct UpdateCompressedMintMetaConfig { /// Generates account metas for the update compressed mint instruction pub fn get_update_compressed_mint_instruction_account_metas( - config: UpdateCompressedMintMetaConfig, + config: UpdateMintMetaConfig, ) -> Vec { let default_pubkeys = TokenDefaultAccounts::default(); diff --git a/sdk-libs/token-sdk/src/compressed_token/v2/update_compressed_mint/instruction.rs b/sdk-libs/token-sdk/src/compressed_token/v2/update_compressed_mint/instruction.rs index 6dd074f522..b330fa5b3e 100644 --- a/sdk-libs/token-sdk/src/compressed_token/v2/update_compressed_mint/instruction.rs +++ b/sdk-libs/token-sdk/src/compressed_token/v2/update_compressed_mint/instruction.rs @@ -3,9 +3,9 @@ use light_compressed_account::instruction_data::{ }; use light_token_interface::{ self, - instructions::mint_action::{CompressedMintWithContext, CpiContext}, + instructions::mint_action::{CpiContext, MintWithContext}, }; -use light_token_types::CompressedMintAuthorityType; +use light_token_types::MintAuthorityType; use solana_instruction::Instruction; use solana_pubkey::Pubkey; @@ -22,9 +22,9 @@ pub const UPDATE_COMPRESSED_MINT_DISCRIMINATOR: u8 = 105; /// Input struct for updating a compressed mint instruction #[derive(Debug, Clone, AnchorDeserialize, AnchorSerialize)] -pub struct UpdateCompressedMintInputs { - pub compressed_mint_inputs: CompressedMintWithContext, - pub authority_type: CompressedMintAuthorityType, +pub struct UpdateMintInputs { + pub compressed_mint_inputs: MintWithContext, + pub authority_type: MintAuthorityType, pub new_authority: Option, pub mint_authority: Option, // Current mint authority (needed when updating freeze authority) pub proof: Option, @@ -37,7 +37,7 @@ pub struct UpdateCompressedMintInputs { /// Creates an update compressed mint instruction with CPI context support (now uses mint_action) pub fn update_compressed_mint_cpi( - input: UpdateCompressedMintInputs, + input: UpdateMintInputs, cpi_context: Option, ) -> Result { let mut instruction_data = @@ -51,10 +51,10 @@ pub fn update_compressed_mint_cpi( }; instruction_data = match input.authority_type { - CompressedMintAuthorityType::MintTokens => { + MintAuthorityType::MintTokens => { instruction_data.with_update_mint_authority(update_authority) } - CompressedMintAuthorityType::FreezeAccount => { + MintAuthorityType::FreezeAccount => { instruction_data.with_update_freeze_authority(update_authority) } }; @@ -87,15 +87,15 @@ pub fn update_compressed_mint_cpi( } /// Creates an update compressed mint instruction without CPI context -pub fn update_compressed_mint(input: UpdateCompressedMintInputs) -> Result { +pub fn update_compressed_mint(input: UpdateMintInputs) -> Result { update_compressed_mint_cpi(input, None) } /// Input struct for creating an update compressed mint instruction with CPI context write #[derive(Debug, Clone)] -pub struct UpdateCompressedMintInputsCpiWrite { - pub compressed_mint_inputs: CompressedMintWithContext, - pub authority_type: CompressedMintAuthorityType, +pub struct UpdateMintInputsCpiWrite { + pub compressed_mint_inputs: MintWithContext, + pub authority_type: MintAuthorityType, pub new_authority: Option, pub payer: Pubkey, pub authority: Pubkey, @@ -105,7 +105,7 @@ pub struct UpdateCompressedMintInputsCpiWrite { /// Creates an update compressed mint instruction for CPI context writes (now uses mint_action) pub fn create_update_compressed_mint_cpi_write( - inputs: UpdateCompressedMintInputsCpiWrite, + inputs: UpdateMintInputsCpiWrite, ) -> Result { if !inputs.cpi_context.first_set_context && !inputs.cpi_context.set_context { return Err(TokenSdkError::InvalidCpiContext); @@ -122,10 +122,10 @@ pub fn create_update_compressed_mint_cpi_write( }; instruction_data = match inputs.authority_type { - CompressedMintAuthorityType::MintTokens => { + MintAuthorityType::MintTokens => { instruction_data.with_update_mint_authority(update_authority) } - CompressedMintAuthorityType::FreezeAccount => { + MintAuthorityType::FreezeAccount => { instruction_data.with_update_freeze_authority(update_authority) } }; diff --git a/sdk-libs/token-sdk/src/compressed_token/v2/update_compressed_mint/mod.rs b/sdk-libs/token-sdk/src/compressed_token/v2/update_compressed_mint/mod.rs index 8cb0fab1b9..be5a774a91 100644 --- a/sdk-libs/token-sdk/src/compressed_token/v2/update_compressed_mint/mod.rs +++ b/sdk-libs/token-sdk/src/compressed_token/v2/update_compressed_mint/mod.rs @@ -2,10 +2,9 @@ pub mod account_metas; pub mod instruction; pub use account_metas::{ - get_update_compressed_mint_instruction_account_metas, UpdateCompressedMintMetaConfig, + get_update_compressed_mint_instruction_account_metas, UpdateMintMetaConfig, }; pub use instruction::{ create_update_compressed_mint_cpi_write, update_compressed_mint, update_compressed_mint_cpi, - UpdateCompressedMintInputs, UpdateCompressedMintInputsCpiWrite, - UPDATE_COMPRESSED_MINT_DISCRIMINATOR, + UpdateMintInputs, UpdateMintInputsCpiWrite, UPDATE_COMPRESSED_MINT_DISCRIMINATOR, }; diff --git a/sdk-libs/token-sdk/src/error.rs b/sdk-libs/token-sdk/src/error.rs index e099fc81b7..39ad5a05b4 100644 --- a/sdk-libs/token-sdk/src/error.rs +++ b/sdk-libs/token-sdk/src/error.rs @@ -75,6 +75,8 @@ pub enum TokenSdkError { MissingCompressibleExtension, #[error("Invalid Token account data")] InvalidTokenAccount, + #[error("SPL token program mismatch between source and destination")] + SplTokenProgramMismatch, #[error(transparent)] CompressedTokenTypes(#[from] LightTokenSdkTypeError), #[error(transparent)] @@ -136,6 +138,7 @@ impl From for u32 { TokenSdkError::NoInputAccounts => 17030, TokenSdkError::MissingCompressibleExtension => 17031, TokenSdkError::InvalidTokenAccount => 17032, + TokenSdkError::SplTokenProgramMismatch => 17033, TokenSdkError::CompressedTokenTypes(e) => e.into(), TokenSdkError::TokenError(e) => e.into(), TokenSdkError::LightSdkTypesError(e) => e.into(), diff --git a/sdk-libs/token-sdk/src/token/burn.rs b/sdk-libs/token-sdk/src/token/burn.rs index 29621d4b4e..4961d6b855 100644 --- a/sdk-libs/token-sdk/src/token/burn.rs +++ b/sdk-libs/token-sdk/src/token/burn.rs @@ -10,11 +10,11 @@ use solana_pubkey::Pubkey; /// # use solana_pubkey::Pubkey; /// # use light_token_sdk::token::Burn; /// # let source = Pubkey::new_unique(); -/// # let cmint = Pubkey::new_unique(); +/// # let mint = Pubkey::new_unique(); /// # let authority = Pubkey::new_unique(); /// let instruction = Burn { /// source, -/// cmint, +/// mint, /// amount: 100, /// authority, /// max_top_up: None, @@ -24,8 +24,8 @@ use solana_pubkey::Pubkey; pub struct Burn { /// Light Token account to burn from pub source: Pubkey, - /// CMint account (supply tracking) - pub cmint: Pubkey, + /// Mint account (supply tracking) + pub mint: Pubkey, /// Amount of tokens to burn pub amount: u64, /// Owner of the Light Token account @@ -40,11 +40,11 @@ pub struct Burn { /// # use light_token_sdk::token::BurnCpi; /// # use solana_account_info::AccountInfo; /// # let source: AccountInfo = todo!(); -/// # let cmint: AccountInfo = todo!(); +/// # let mint: AccountInfo = todo!(); /// # let authority: AccountInfo = todo!(); /// BurnCpi { /// source, -/// cmint, +/// mint, /// amount: 100, /// authority, /// max_top_up: None, @@ -54,7 +54,7 @@ pub struct Burn { /// ``` pub struct BurnCpi<'info> { pub source: AccountInfo<'info>, - pub cmint: AccountInfo<'info>, + pub mint: AccountInfo<'info>, pub amount: u64, pub authority: AccountInfo<'info>, /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit) @@ -68,13 +68,13 @@ impl<'info> BurnCpi<'info> { pub fn invoke(self) -> Result<(), ProgramError> { let instruction = Burn::from(&self).instruction()?; - let account_infos = [self.source, self.cmint, self.authority]; + let account_infos = [self.source, self.mint, self.authority]; invoke(&instruction, &account_infos) } pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { let instruction = Burn::from(&self).instruction()?; - let account_infos = [self.source, self.cmint, self.authority]; + let account_infos = [self.source, self.mint, self.authority]; invoke_signed(&instruction, &account_infos, signer_seeds) } } @@ -83,7 +83,7 @@ impl<'info> From<&BurnCpi<'info>> for Burn { fn from(cpi: &BurnCpi<'info>) -> Self { Self { source: *cpi.source.key, - cmint: *cpi.cmint.key, + mint: *cpi.mint.key, amount: cpi.amount, authority: *cpi.authority.key, max_top_up: cpi.max_top_up, @@ -97,7 +97,7 @@ impl Burn { program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID), accounts: vec![ AccountMeta::new(self.source, false), - AccountMeta::new(self.cmint, false), + AccountMeta::new(self.mint, false), AccountMeta::new_readonly(self.authority, true), ], data: { diff --git a/sdk-libs/token-sdk/src/token/burn_checked.rs b/sdk-libs/token-sdk/src/token/burn_checked.rs index 41d7c0da60..dce7594250 100644 --- a/sdk-libs/token-sdk/src/token/burn_checked.rs +++ b/sdk-libs/token-sdk/src/token/burn_checked.rs @@ -10,11 +10,11 @@ use solana_pubkey::Pubkey; /// # use solana_pubkey::Pubkey; /// # use light_token_sdk::token::BurnChecked; /// # let source = Pubkey::new_unique(); -/// # let cmint = Pubkey::new_unique(); +/// # let mint = Pubkey::new_unique(); /// # let authority = Pubkey::new_unique(); /// let instruction = BurnChecked { /// source, -/// cmint, +/// mint, /// amount: 100, /// decimals: 8, /// authority, @@ -25,8 +25,8 @@ use solana_pubkey::Pubkey; pub struct BurnChecked { /// Light Token account to burn from pub source: Pubkey, - /// CMint account (supply tracking) - pub cmint: Pubkey, + /// Mint account (supply tracking) + pub mint: Pubkey, /// Amount of tokens to burn pub amount: u64, /// Expected token decimals @@ -43,11 +43,11 @@ pub struct BurnChecked { /// # use light_token_sdk::token::BurnCheckedCpi; /// # use solana_account_info::AccountInfo; /// # let source: AccountInfo = todo!(); -/// # let cmint: AccountInfo = todo!(); +/// # let mint: AccountInfo = todo!(); /// # let authority: AccountInfo = todo!(); /// BurnCheckedCpi { /// source, -/// cmint, +/// mint, /// amount: 100, /// decimals: 8, /// authority, @@ -58,7 +58,7 @@ pub struct BurnChecked { /// ``` pub struct BurnCheckedCpi<'info> { pub source: AccountInfo<'info>, - pub cmint: AccountInfo<'info>, + pub mint: AccountInfo<'info>, pub amount: u64, pub decimals: u8, pub authority: AccountInfo<'info>, @@ -73,13 +73,13 @@ impl<'info> BurnCheckedCpi<'info> { pub fn invoke(self) -> Result<(), ProgramError> { let instruction = BurnChecked::from(&self).instruction()?; - let account_infos = [self.source, self.cmint, self.authority]; + let account_infos = [self.source, self.mint, self.authority]; invoke(&instruction, &account_infos) } pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { let instruction = BurnChecked::from(&self).instruction()?; - let account_infos = [self.source, self.cmint, self.authority]; + let account_infos = [self.source, self.mint, self.authority]; invoke_signed(&instruction, &account_infos, signer_seeds) } } @@ -88,7 +88,7 @@ impl<'info> From<&BurnCheckedCpi<'info>> for BurnChecked { fn from(cpi: &BurnCheckedCpi<'info>) -> Self { Self { source: *cpi.source.key, - cmint: *cpi.cmint.key, + mint: *cpi.mint.key, amount: cpi.amount, decimals: cpi.decimals, authority: *cpi.authority.key, @@ -103,7 +103,7 @@ impl BurnChecked { program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID), accounts: vec![ AccountMeta::new(self.source, false), - AccountMeta::new(self.cmint, false), + AccountMeta::new(self.mint, false), AccountMeta::new_readonly(self.authority, true), ], data: { diff --git a/sdk-libs/token-sdk/src/token/create_mint.rs b/sdk-libs/token-sdk/src/token/create_mint.rs index 5b20117b90..847de6fb32 100644 --- a/sdk-libs/token-sdk/src/token/create_mint.rs +++ b/sdk-libs/token-sdk/src/token/create_mint.rs @@ -4,7 +4,7 @@ use light_compressed_account::instruction_data::{ use light_token_interface::{ instructions::{ extensions::ExtensionInstructionData, - mint_action::{CompressedMintInstructionData, CpiContext}, + mint_action::{CpiContext, DecompressMintAction, MintInstructionData}, }, COMPRESSED_MINT_SEED, }; @@ -14,9 +14,12 @@ use solana_instruction::Instruction; use solana_program_error::ProgramError; use solana_pubkey::Pubkey; +use super::{config_pda, rent_sponsor_pda}; use crate::{compressed_token::mint_action::MintActionMetaConfig, token::SystemAccountInfos}; -// TODO: modify so that it creates a decompressed mint, if you want a compressed mint use light_token_sdk::compressed_token::create_cmint -/// Parameters for creating a compressed mint. +/// Parameters for creating a mint. +/// +/// Creates both a compressed mint AND a decompressed Mint Solana account +/// in a single instruction. #[derive(Debug, Clone)] pub struct CreateMintParams { pub decimals: u8, @@ -28,9 +31,17 @@ pub struct CreateMintParams { pub bump: u8, pub freeze_authority: Option, pub extensions: Option>, + /// Rent payment in epochs for the Mint account (must be 0 or >= 2). + /// Default: 16 (~24 hours) + pub rent_payment: u8, + /// Lamports allocated for future write operations. + /// Default: 766 (~3 hours per write) + pub write_top_up: u32, } -/// # Create a compressed mint instruction: +/// Create a mint instruction that creates both a compressed mint AND a Mint Solana account. +/// +/// # Example /// ```rust,no_run /// # use solana_pubkey::Pubkey; /// use light_token_sdk::token::{ @@ -59,6 +70,8 @@ pub struct CreateMintParams { /// bump, /// freeze_authority: None, /// extensions: None, +/// rent_payment: 16, // ~24 hours rent +/// write_top_up: 766, // ~3 hours per write /// }; /// let instruction = CreateMint::new( /// params, @@ -108,13 +121,13 @@ impl CreateMint { } pub fn instruction(self) -> Result { - let compressed_mint_instruction_data = CompressedMintInstructionData { + let compressed_mint_instruction_data = MintInstructionData { supply: 0, decimals: self.params.decimals, - metadata: light_token_interface::state::CompressedMintMetadata { + metadata: light_token_interface::state::MintMetadata { version: 3, mint: self.params.mint.to_bytes().into(), - cmint_decompressed: false, + mint_decompressed: false, mint_signer: self.mint_seed_pubkey.to_bytes(), bump: self.params.bump, }, @@ -133,6 +146,12 @@ impl CreateMint { compressed_mint_instruction_data, ); + // Always add decompress action to create Mint Solana account + instruction_data = instruction_data.with_decompress_mint(DecompressMintAction { + rent_payment: self.params.rent_payment, + write_top_up: self.params.write_top_up, + }); + if let Some(ctx) = self.cpi_context { instruction_data = instruction_data.with_cpi_context(ctx); } @@ -143,7 +162,10 @@ impl CreateMint { self.mint_seed_pubkey, self.address_tree_pubkey, self.output_queue, - ); + ) + // Always include compressible accounts for Mint creation + .with_compressible_mint(self.params.mint, config_pda(), rent_sponsor_pda()); + if let Some(cpi_context_pubkey) = self.cpi_context_pubkey { meta_config.cpi_context = Some(cpi_context_pubkey); } @@ -163,10 +185,10 @@ impl CreateMint { } // ============================================================================ -// AccountInfos Struct: CreateCMintCpi (for CPI usage) +// AccountInfos Struct: CreateMintCpi (for CPI usage) // ============================================================================ -/// # Create a compressed mint via CPI: +/// # Create a mint via CPI: /// ```rust,no_run /// # use light_token_sdk::token::{CreateMintCpi, CreateMintParams, SystemAccountInfos}; /// # use solana_account_info::AccountInfo; @@ -175,6 +197,9 @@ impl CreateMint { /// # let payer: AccountInfo = todo!(); /// # let address_tree: AccountInfo = todo!(); /// # let output_queue: AccountInfo = todo!(); +/// # let compressible_config: AccountInfo = todo!(); +/// # let mint: AccountInfo = todo!(); +/// # let rent_sponsor: AccountInfo = todo!(); /// # let system_accounts: SystemAccountInfos = todo!(); /// # let params: CreateMintParams = todo!(); /// CreateMintCpi { @@ -183,6 +208,9 @@ impl CreateMint { /// payer, /// address_tree, /// output_queue, +/// compressible_config, +/// mint, +/// rent_sponsor, /// system_accounts, /// cpi_context: None, /// cpi_context_account: None, @@ -199,6 +227,12 @@ pub struct CreateMintCpi<'info> { pub payer: AccountInfo<'info>, pub address_tree: AccountInfo<'info>, pub output_queue: AccountInfo<'info>, + /// CompressibleConfig account (required for Mint creation) + pub compressible_config: AccountInfo<'info>, + /// Mint PDA account (writable, will be initialized) + pub mint: AccountInfo<'info>, + /// Rent sponsor PDA (required for Mint creation) + pub rent_sponsor: AccountInfo<'info>, pub system_accounts: SystemAccountInfos<'info>, pub cpi_context: Option, pub cpi_context_account: Option>, @@ -206,12 +240,16 @@ pub struct CreateMintCpi<'info> { } impl<'info> CreateMintCpi<'info> { + #[allow(clippy::too_many_arguments)] pub fn new( mint_seed: AccountInfo<'info>, authority: AccountInfo<'info>, payer: AccountInfo<'info>, address_tree: AccountInfo<'info>, output_queue: AccountInfo<'info>, + compressible_config: AccountInfo<'info>, + mint: AccountInfo<'info>, + rent_sponsor: AccountInfo<'info>, system_accounts: SystemAccountInfos<'info>, params: CreateMintParams, ) -> Self { @@ -221,6 +259,9 @@ impl<'info> CreateMintCpi<'info> { payer, address_tree, output_queue, + compressible_config, + mint, + rent_sponsor, system_accounts, cpi_context: None, cpi_context_account: None, @@ -235,50 +276,58 @@ impl<'info> CreateMintCpi<'info> { pub fn invoke(self) -> Result<(), ProgramError> { let instruction = self.instruction()?; - // Account order must match the instruction's account metas order (from get_mint_action_instruction_account_metas) + // Account order must match MintActionMetaConfig::to_account_metas() let mut account_infos = vec![ - self.system_accounts.light_system_program, // Index 0 - self.mint_seed, // Index 1 - self.authority, // Index 2 (authority) - self.payer, // Index 3 (fee_payer) + self.system_accounts.light_system_program, + self.mint_seed, + self.authority, + self.compressible_config, + self.mint, + self.rent_sponsor, + self.payer, self.system_accounts.cpi_authority_pda, self.system_accounts.registered_program_pda, self.system_accounts.account_compression_authority, self.system_accounts.account_compression_program, self.system_accounts.system_program, - self.output_queue, - self.address_tree, ]; if let Some(cpi_context_account) = self.cpi_context_account { account_infos.push(cpi_context_account); } + account_infos.push(self.output_queue); + account_infos.push(self.address_tree); + invoke(&instruction, &account_infos) } pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { let instruction = self.instruction()?; - // Account order must match the instruction's account metas order (from get_mint_action_instruction_account_metas) + // Account order must match MintActionMetaConfig::to_account_metas() let mut account_infos = vec![ - self.system_accounts.light_system_program, // Index 0 - self.mint_seed, // Index 1 - self.authority, // Index 2 (authority) - self.payer, // Index 3 (fee_payer) + self.system_accounts.light_system_program, + self.mint_seed, + self.authority, + self.compressible_config, + self.mint, + self.rent_sponsor, + self.payer, self.system_accounts.cpi_authority_pda, self.system_accounts.registered_program_pda, self.system_accounts.account_compression_authority, self.system_accounts.account_compression_program, self.system_accounts.system_program, - self.output_queue, - self.address_tree, ]; if let Some(cpi_context_account) = self.cpi_context_account { account_infos.push(cpi_context_account); } + account_infos.push(self.output_queue); + account_infos.push(self.address_tree); + invoke_signed(&instruction, &account_infos, signer_seeds) } } diff --git a/sdk-libs/token-sdk/src/token/decompress_mint.rs b/sdk-libs/token-sdk/src/token/decompress_mint.rs index bb48714d31..6d80b26c90 100644 --- a/sdk-libs/token-sdk/src/token/decompress_mint.rs +++ b/sdk-libs/token-sdk/src/token/decompress_mint.rs @@ -3,7 +3,7 @@ use light_compressed_account::instruction_data::{ }; use light_token_interface::instructions::mint_action::{ CompressedMintWithContext, CpiContext, DecompressMintAction, - MintActionCompressedInstructionData, + MintActionCompressedInstructionData, MintWithContext, }; use solana_account_info::AccountInfo; use solana_cpi::{invoke, invoke_signed}; @@ -14,10 +14,10 @@ use solana_pubkey::Pubkey; use super::{config_pda, rent_sponsor_pda, SystemAccountInfos}; use crate::compressed_token::mint_action::MintActionMetaConfig; -/// Decompress a compressed mint to a CMint Solana account. +/// Decompress a compressed mint to a Mint Solana account. /// -/// Creates an on-chain CMint PDA that becomes the source of truth. -/// The CMint is always compressible. +/// Creates an on-chain Mint PDA that becomes the source of truth. +/// The Mint is always compressible. /// /// # Example /// ```rust,ignore @@ -46,7 +46,7 @@ pub struct DecompressMint { /// Output queue for updated compressed mint pub output_queue: Pubkey, /// Compressed mint with context (from indexer) - pub compressed_mint_with_context: CompressedMintWithContext, + pub compressed_mint_with_context: MintWithContext, /// Validity proof for the compressed mint pub proof: ValidityProof, /// Rent payment in epochs (must be >= 2) @@ -57,13 +57,13 @@ pub struct DecompressMint { impl DecompressMint { pub fn instruction(self) -> Result { - // Get CMint PDA from compressed mint metadata + // Get Mint PDA from compressed mint metadata let mint_data = self .compressed_mint_with_context .mint .as_ref() .ok_or(ProgramError::InvalidInstructionData)?; - let cmint_pda = Pubkey::from(mint_data.metadata.mint.to_bytes()); + let mint_pda = Pubkey::from(mint_data.metadata.mint.to_bytes()); // Build DecompressMintAction let action = DecompressMintAction { @@ -78,7 +78,7 @@ impl DecompressMint { ) .with_decompress_mint(action); - // Build account metas with compressible CMint + // Build account metas with compressible Mint let meta_config = MintActionMetaConfig::new( self.payer, self.authority, @@ -86,7 +86,7 @@ impl DecompressMint { self.input_queue, self.output_queue, ) - .with_compressible_mint(cmint_pda, config_pda(), rent_sponsor_pda()); + .with_compressible_mint(mint_pda, config_pda(), rent_sponsor_pda()); let account_metas = meta_config.to_account_metas(); @@ -106,17 +106,17 @@ impl DecompressMint { // CPI Struct: DecompressMintCpi // ============================================================================ -/// Decompress a compressed mint to a CMint Solana account via CPI. +/// Decompress a compressed mint to a Mint Solana account via CPI. /// -/// Creates an on-chain CMint PDA that becomes the source of truth. -/// The CMint is always compressible. +/// Creates an on-chain Mint PDA that becomes the source of truth. +/// The Mint is always compressible. /// /// # Example /// ```rust,ignore /// DecompressMintCpi { /// authority: authority_account, /// payer: payer_account, -/// cmint: cmint_account, +/// mint: mint_account, /// compressible_config: config_account, /// rent_sponsor: rent_sponsor_account, /// state_tree: state_tree_account, @@ -135,8 +135,8 @@ pub struct DecompressMintCpi<'info> { pub authority: AccountInfo<'info>, /// Fee payer pub payer: AccountInfo<'info>, - /// CMint PDA account (writable) - pub cmint: AccountInfo<'info>, + /// Mint PDA account (writable) + pub mint: AccountInfo<'info>, /// CompressibleConfig account pub compressible_config: AccountInfo<'info>, /// Rent sponsor PDA account @@ -150,7 +150,7 @@ pub struct DecompressMintCpi<'info> { /// System accounts for Light Protocol pub system_accounts: SystemAccountInfos<'info>, /// Compressed mint with context (from indexer) - pub compressed_mint_with_context: CompressedMintWithContext, + pub compressed_mint_with_context: MintWithContext, /// Validity proof for the compressed mint pub proof: ValidityProof, /// Rent payment in epochs (must be >= 2) @@ -172,7 +172,7 @@ impl<'info> DecompressMintCpi<'info> { // 2. mint_signer (no sign for decompress) // 3. authority (signer) // 4. compressible_config - // 5. cmint + // 5. mint // 6. rent_sponsor // 7. fee_payer (signer) // 8. cpi_authority_pda @@ -198,7 +198,7 @@ impl<'info> DecompressMintCpi<'info> { self.system_accounts.light_system_program.clone(), self.authority.clone(), self.compressible_config.clone(), - self.cmint.clone(), + self.mint.clone(), self.rent_sponsor.clone(), self.payer.clone(), self.system_accounts.cpi_authority_pda.clone(), diff --git a/sdk-libs/token-sdk/src/token/mint_to.rs b/sdk-libs/token-sdk/src/token/mint_to.rs index 9359c3c07b..bff3e42c87 100644 --- a/sdk-libs/token-sdk/src/token/mint_to.rs +++ b/sdk-libs/token-sdk/src/token/mint_to.rs @@ -9,11 +9,11 @@ use solana_pubkey::Pubkey; /// ```rust /// # use solana_pubkey::Pubkey; /// # use light_token_sdk::token::MintTo; -/// # let cmint = Pubkey::new_unique(); +/// # let mint = Pubkey::new_unique(); /// # let destination = Pubkey::new_unique(); /// # let authority = Pubkey::new_unique(); /// let instruction = MintTo { -/// cmint, +/// mint, /// destination, /// amount: 100, /// authority, @@ -22,8 +22,8 @@ use solana_pubkey::Pubkey; /// # Ok::<(), solana_program_error::ProgramError>(()) /// ``` pub struct MintTo { - /// CMint account (supply tracking) - pub cmint: Pubkey, + /// Mint account (supply tracking) + pub mint: Pubkey, /// Destination Light Token account to mint to pub destination: Pubkey, /// Amount of tokens to mint @@ -39,12 +39,12 @@ pub struct MintTo { /// ```rust,no_run /// # use light_token_sdk::token::MintToCpi; /// # use solana_account_info::AccountInfo; -/// # let cmint: AccountInfo = todo!(); +/// # let mint: AccountInfo = todo!(); /// # let destination: AccountInfo = todo!(); /// # let authority: AccountInfo = todo!(); /// # let system_program: AccountInfo = todo!(); /// MintToCpi { -/// cmint, +/// mint, /// destination, /// amount: 100, /// authority, @@ -55,7 +55,7 @@ pub struct MintTo { /// # Ok::<(), solana_program_error::ProgramError>(()) /// ``` pub struct MintToCpi<'info> { - pub cmint: AccountInfo<'info>, + pub mint: AccountInfo<'info>, pub destination: AccountInfo<'info>, pub amount: u64, pub authority: AccountInfo<'info>, @@ -72,7 +72,7 @@ impl<'info> MintToCpi<'info> { pub fn invoke(self) -> Result<(), ProgramError> { let instruction = MintTo::from(&self).instruction()?; let account_infos = [ - self.cmint, + self.mint, self.destination, self.authority, self.system_program, @@ -83,7 +83,7 @@ impl<'info> MintToCpi<'info> { pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { let instruction = MintTo::from(&self).instruction()?; let account_infos = [ - self.cmint, + self.mint, self.destination, self.authority, self.system_program, @@ -95,7 +95,7 @@ impl<'info> MintToCpi<'info> { impl<'info> From<&MintToCpi<'info>> for MintTo { fn from(cpi: &MintToCpi<'info>) -> Self { Self { - cmint: *cpi.cmint.key, + mint: *cpi.mint.key, destination: *cpi.destination.key, amount: cpi.amount, authority: *cpi.authority.key, @@ -109,7 +109,7 @@ impl MintTo { Ok(Instruction { program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID), accounts: vec![ - AccountMeta::new(self.cmint, false), + AccountMeta::new(self.mint, false), AccountMeta::new(self.destination, false), AccountMeta::new(self.authority, true), AccountMeta::new_readonly(Pubkey::default(), false), // System program for lamport transfers diff --git a/sdk-libs/token-sdk/src/token/mint_to_checked.rs b/sdk-libs/token-sdk/src/token/mint_to_checked.rs index 44d2664ff9..662ff7185c 100644 --- a/sdk-libs/token-sdk/src/token/mint_to_checked.rs +++ b/sdk-libs/token-sdk/src/token/mint_to_checked.rs @@ -9,11 +9,11 @@ use solana_pubkey::Pubkey; /// ```rust /// # use solana_pubkey::Pubkey; /// # use light_token_sdk::token::MintToChecked; -/// # let cmint = Pubkey::new_unique(); +/// # let mint = Pubkey::new_unique(); /// # let destination = Pubkey::new_unique(); /// # let authority = Pubkey::new_unique(); /// let instruction = MintToChecked { -/// cmint, +/// mint, /// destination, /// amount: 100, /// decimals: 8, @@ -23,8 +23,8 @@ use solana_pubkey::Pubkey; /// # Ok::<(), solana_program_error::ProgramError>(()) /// ``` pub struct MintToChecked { - /// CMint account (supply tracking) - pub cmint: Pubkey, + /// Mint account (supply tracking) + pub mint: Pubkey, /// Destination Light Token account to mint to pub destination: Pubkey, /// Amount of tokens to mint @@ -42,11 +42,11 @@ pub struct MintToChecked { /// ```rust,no_run /// # use light_token_sdk::token::MintToCheckedCpi; /// # use solana_account_info::AccountInfo; -/// # let cmint: AccountInfo = todo!(); +/// # let mint: AccountInfo = todo!(); /// # let destination: AccountInfo = todo!(); /// # let authority: AccountInfo = todo!(); /// MintToCheckedCpi { -/// cmint, +/// mint, /// destination, /// amount: 100, /// decimals: 8, @@ -57,7 +57,7 @@ pub struct MintToChecked { /// # Ok::<(), solana_program_error::ProgramError>(()) /// ``` pub struct MintToCheckedCpi<'info> { - pub cmint: AccountInfo<'info>, + pub mint: AccountInfo<'info>, pub destination: AccountInfo<'info>, pub amount: u64, pub decimals: u8, @@ -73,13 +73,13 @@ impl<'info> MintToCheckedCpi<'info> { pub fn invoke(self) -> Result<(), ProgramError> { let instruction = MintToChecked::from(&self).instruction()?; - let account_infos = [self.cmint, self.destination, self.authority]; + let account_infos = [self.mint, self.destination, self.authority]; invoke(&instruction, &account_infos) } pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { let instruction = MintToChecked::from(&self).instruction()?; - let account_infos = [self.cmint, self.destination, self.authority]; + let account_infos = [self.mint, self.destination, self.authority]; invoke_signed(&instruction, &account_infos, signer_seeds) } } @@ -87,7 +87,7 @@ impl<'info> MintToCheckedCpi<'info> { impl<'info> From<&MintToCheckedCpi<'info>> for MintToChecked { fn from(cpi: &MintToCheckedCpi<'info>) -> Self { Self { - cmint: *cpi.cmint.key, + mint: *cpi.mint.key, destination: *cpi.destination.key, amount: cpi.amount, decimals: cpi.decimals, @@ -102,7 +102,7 @@ impl MintToChecked { Ok(Instruction { program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID), accounts: vec![ - AccountMeta::new(self.cmint, false), + AccountMeta::new(self.mint, false), AccountMeta::new(self.destination, false), AccountMeta::new_readonly(self.authority, true), ], diff --git a/sdk-libs/token-sdk/src/token/mod.rs b/sdk-libs/token-sdk/src/token/mod.rs index e016cad387..46360c12d7 100644 --- a/sdk-libs/token-sdk/src/token/mod.rs +++ b/sdk-libs/token-sdk/src/token/mod.rs @@ -132,7 +132,7 @@ use light_compressible::config::CompressibleConfig; pub use light_token_interface::{ instructions::{ extensions::{CompressToPubkey, ExtensionInstructionData}, - mint_action::CompressedMintWithContext, + mint_action::MintWithContext, }, state::{Token, TokenDataVersion}, }; @@ -146,7 +146,9 @@ pub use thaw::{Thaw, ThawCpi}; pub use transfer::*; pub use transfer_checked::*; pub use transfer_from_spl::{TransferFromSpl, TransferFromSplCpi}; -pub use transfer_interface::{SplInterface, TransferInterfaceCpi}; +pub use transfer_interface::{ + SplInterface, SplInterfaceCpi, TransferInterface, TransferInterfaceCpi, +}; pub use transfer_to_spl::{TransferToSpl, TransferToSplCpi}; /// System accounts required for CPI operations to Light Protocol. diff --git a/sdk-libs/token-sdk/src/token/transfer_interface.rs b/sdk-libs/token-sdk/src/token/transfer_interface.rs index 22717797e0..3bc732dcea 100644 --- a/sdk-libs/token-sdk/src/token/transfer_interface.rs +++ b/sdk-libs/token-sdk/src/token/transfer_interface.rs @@ -1,19 +1,247 @@ use solana_account_info::AccountInfo; +use solana_instruction::{AccountMeta, Instruction}; use solana_program_error::ProgramError; +use solana_pubkey::Pubkey; use super::{ - transfer::TransferCpi, transfer_from_spl::TransferFromSplCpi, transfer_to_spl::TransferToSplCpi, + transfer::Transfer, transfer_from_spl::TransferFromSpl, transfer_to_spl::TransferToSpl, }; -use crate::{error::TokenSdkError, utils::is_token_account}; +use crate::error::TokenSdkError; -/// Required accounts to interface between ctoken and SPL token accounts. -pub struct SplInterface<'info> { +/// Internal enum to classify transfer types based on account owners. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum TransferType { + /// ctoken -> ctoken + LightToLight, + /// ctoken -> SPL (decompress) + LightToSpl, + /// SPL -> ctoken (compress) + SplToLight, + /// SPL -> SPL (pass-through to SPL token program) + SplToSpl, +} + +/// Determine transfer type from account owners. +/// +/// Returns `Ok(TransferType)` if at least one account is a Light token account. +/// Returns `Err(UseRegularSplTransfer)` if both accounts are non-Light (SPL) accounts. +/// Returns `Err(CannotDetermineAccountType)` if an account owner is unrecognized. +fn determine_transfer_type( + source_owner: &Pubkey, + destination_owner: &Pubkey, +) -> Result { + use crate::utils::is_light_token_owner; + + let source_is_light = is_light_token_owner(source_owner) + .map_err(|_| ProgramError::Custom(TokenSdkError::CannotDetermineAccountType.into()))?; + let dest_is_light = is_light_token_owner(destination_owner) + .map_err(|_| ProgramError::Custom(TokenSdkError::CannotDetermineAccountType.into()))?; + + match (source_is_light, dest_is_light) { + (true, true) => Ok(TransferType::LightToLight), + (true, false) => Ok(TransferType::LightToSpl), + (false, true) => Ok(TransferType::SplToLight), + (false, false) => { + // Both are SPL - verify same token program + if source_owner == destination_owner { + Ok(TransferType::SplToSpl) + } else { + Err(ProgramError::Custom( + TokenSdkError::SplTokenProgramMismatch.into(), + )) + } + } + } +} + +/// Required accounts to interface between ctoken and SPL token accounts (Pubkey-based). +/// +/// Use this struct when building instructions outside of CPI context. +#[derive(Debug, Clone, Copy)] +pub struct SplInterface { + pub mint: Pubkey, + pub spl_token_program: Pubkey, + pub spl_interface_pda: Pubkey, + pub spl_interface_pda_bump: u8, +} + +impl<'info> From<&SplInterfaceCpi<'info>> for SplInterface { + fn from(spl: &SplInterfaceCpi<'info>) -> Self { + Self { + mint: *spl.mint.key, + spl_token_program: *spl.spl_token_program.key, + spl_interface_pda: *spl.spl_interface_pda.key, + spl_interface_pda_bump: spl.spl_interface_pda_bump, + } + } +} + +/// Required accounts to interface between ctoken and SPL token accounts (AccountInfo-based). +/// +/// Use this struct when building CPIs. +pub struct SplInterfaceCpi<'info> { pub mint: AccountInfo<'info>, pub spl_token_program: AccountInfo<'info>, pub spl_interface_pda: AccountInfo<'info>, pub spl_interface_pda_bump: u8, } +/// # Create a transfer interface instruction that auto-routes based on account types: +/// ```rust +/// # use solana_pubkey::Pubkey; +/// # use light_token_sdk::token::{TransferInterface, SplInterface, LIGHT_TOKEN_PROGRAM_ID}; +/// # let source = Pubkey::new_unique(); +/// # let destination = Pubkey::new_unique(); +/// # let authority = Pubkey::new_unique(); +/// # let payer = Pubkey::new_unique(); +/// // For ctoken -> ctoken transfer (source_owner and destination_owner are LIGHT_TOKEN_PROGRAM_ID) +/// let instruction = TransferInterface { +/// source, +/// destination, +/// amount: 100, +/// decimals: 9, +/// authority, +/// payer, +/// spl_interface: None, +/// max_top_up: None, +/// source_owner: LIGHT_TOKEN_PROGRAM_ID, +/// destination_owner: LIGHT_TOKEN_PROGRAM_ID, +/// }.instruction()?; +/// # Ok::<(), solana_program_error::ProgramError>(()) +/// ``` +pub struct TransferInterface { + pub source: Pubkey, + pub destination: Pubkey, + pub amount: u64, + pub decimals: u8, + pub authority: Pubkey, + pub payer: Pubkey, + pub spl_interface: Option, + /// Maximum lamports for rent and top-up combined (for ctoken->ctoken transfers) + pub max_top_up: Option, + /// Owner of the source account (used to determine transfer type) + pub source_owner: Pubkey, + /// Owner of the destination account (used to determine transfer type) + pub destination_owner: Pubkey, +} + +impl TransferInterface { + /// Build instruction based on detected transfer type + pub fn instruction(self) -> Result { + match determine_transfer_type(&self.source_owner, &self.destination_owner)? { + TransferType::LightToLight => Transfer { + source: self.source, + destination: self.destination, + amount: self.amount, + authority: self.authority, + max_top_up: self.max_top_up, + } + .instruction(), + + TransferType::LightToSpl => { + let spl = self.spl_interface.ok_or_else(|| { + ProgramError::Custom(TokenSdkError::SplInterfaceRequired.into()) + })?; + TransferToSpl { + source: self.source, + destination_spl_token_account: self.destination, + amount: self.amount, + authority: self.authority, + mint: spl.mint, + payer: self.payer, + spl_interface_pda: spl.spl_interface_pda, + spl_interface_pda_bump: spl.spl_interface_pda_bump, + decimals: self.decimals, + spl_token_program: spl.spl_token_program, + } + .instruction() + } + + TransferType::SplToLight => { + let spl = self.spl_interface.ok_or_else(|| { + ProgramError::Custom(TokenSdkError::SplInterfaceRequired.into()) + })?; + TransferFromSpl { + source_spl_token_account: self.source, + destination: self.destination, + amount: self.amount, + authority: self.authority, + mint: spl.mint, + payer: self.payer, + spl_interface_pda: spl.spl_interface_pda, + spl_interface_pda_bump: spl.spl_interface_pda_bump, + decimals: self.decimals, + spl_token_program: spl.spl_token_program, + } + .instruction() + } + + TransferType::SplToSpl => { + let spl = self.spl_interface.ok_or_else(|| { + ProgramError::Custom(TokenSdkError::SplInterfaceRequired.into()) + })?; + + // Build SPL transfer_checked instruction manually + // Discriminator 12 = TransferChecked + let mut data = vec![12u8]; + data.extend_from_slice(&self.amount.to_le_bytes()); + data.push(self.decimals); + + Ok(Instruction { + program_id: self.source_owner, // SPL token program + accounts: vec![ + AccountMeta::new(self.source, false), + AccountMeta::new_readonly(spl.mint, false), + AccountMeta::new(self.destination, false), + AccountMeta::new_readonly(self.authority, true), + ], + data, + }) + } + } + } +} + +impl<'info> From<&TransferInterfaceCpi<'info>> for TransferInterface { + fn from(cpi: &TransferInterfaceCpi<'info>) -> Self { + Self { + source: *cpi.source_account.key, + destination: *cpi.destination_account.key, + amount: cpi.amount, + decimals: cpi.decimals, + authority: *cpi.authority.key, + payer: *cpi.payer.key, + spl_interface: cpi.spl_interface.as_ref().map(SplInterface::from), + max_top_up: None, + source_owner: *cpi.source_account.owner, + destination_owner: *cpi.destination_account.owner, + } + } +} + +/// # Transfer interface via CPI (auto-detects account types): +/// ```rust,no_run +/// # use light_token_sdk::token::{TransferInterfaceCpi, SplInterfaceCpi}; +/// # use solana_account_info::AccountInfo; +/// # let source_account: AccountInfo = todo!(); +/// # let destination_account: AccountInfo = todo!(); +/// # let authority: AccountInfo = todo!(); +/// # let payer: AccountInfo = todo!(); +/// # let compressed_token_program_authority: AccountInfo = todo!(); +/// # let system_program: AccountInfo = todo!(); +/// TransferInterfaceCpi::new( +/// 100, // amount +/// 9, // decimals +/// source_account, +/// destination_account, +/// authority, +/// payer, +/// compressed_token_program_authority, +/// system_program, +/// ) +/// .invoke()?; +/// # Ok::<(), solana_program_error::ProgramError>(()) +/// ``` pub struct TransferInterfaceCpi<'info> { pub amount: u64, pub decimals: u8, @@ -22,7 +250,7 @@ pub struct TransferInterfaceCpi<'info> { pub authority: AccountInfo<'info>, pub payer: AccountInfo<'info>, pub compressed_token_program_authority: AccountInfo<'info>, - pub spl_interface: Option>, + pub spl_interface: Option>, /// System program - required for compressible account lamport top-ups pub system_program: AccountInfo<'info>, } @@ -86,7 +314,7 @@ impl<'info> TransferInterfaceCpi<'info> { ProgramError::Custom(TokenSdkError::MissingSplInterfacePdaBump.into()) })?; - self.spl_interface = Some(SplInterface { + self.spl_interface = Some(SplInterfaceCpi { mint, spl_token_program, spl_interface_pda, @@ -95,149 +323,148 @@ impl<'info> TransferInterfaceCpi<'info> { Ok(self) } + /// Build instruction from CPI context + pub fn instruction(&self) -> Result { + TransferInterface::from(self).instruction() + } + /// # Errors /// * `SplInterfaceRequired` - 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_light_token = is_token_account(&self.source_account) - .map_err(|_| ProgramError::Custom(TokenSdkError::CannotDetermineAccountType.into()))?; - let dest_is_light_token = is_token_account(&self.destination_account) - .map_err(|_| ProgramError::Custom(TokenSdkError::CannotDetermineAccountType.into()))?; - - match (source_is_light_token, dest_is_light_token) { - (true, true) => TransferCpi { - source: self.source_account.clone(), - destination: self.destination_account.clone(), - amount: self.amount, - authority: self.authority.clone(), - max_top_up: None, // No limit by default + use solana_cpi::invoke; + + let transfer_type = + determine_transfer_type(self.source_account.owner, self.destination_account.owner)?; + let instruction = self.instruction()?; + + match transfer_type { + TransferType::LightToLight => { + let account_infos = [ + self.source_account, + self.destination_account, + self.authority, + ]; + invoke(&instruction, &account_infos) } - .invoke(), - (true, false) => { + TransferType::LightToSpl => { let config = self.spl_interface.ok_or_else(|| { ProgramError::Custom(TokenSdkError::SplInterfaceRequired.into()) })?; - - TransferToSplCpi { - source: 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(), - spl_interface_pda: config.spl_interface_pda.clone(), - spl_interface_pda_bump: config.spl_interface_pda_bump, - decimals: self.decimals, - spl_token_program: config.spl_token_program.clone(), - compressed_token_program_authority: self - .compressed_token_program_authority - .clone(), - } - .invoke() + let account_infos = [ + self.compressed_token_program_authority, + self.payer, + config.mint, + self.source_account, + self.destination_account, + self.authority, + config.spl_interface_pda, + config.spl_token_program, + ]; + invoke(&instruction, &account_infos) } - (false, true) => { + TransferType::SplToLight => { let config = self.spl_interface.ok_or_else(|| { ProgramError::Custom(TokenSdkError::SplInterfaceRequired.into()) })?; - - TransferFromSplCpi { - source_spl_token_account: self.source_account.clone(), - destination: self.destination_account.clone(), - amount: self.amount, - authority: self.authority.clone(), - mint: config.mint.clone(), - payer: self.payer.clone(), - spl_interface_pda: config.spl_interface_pda.clone(), - spl_interface_pda_bump: config.spl_interface_pda_bump, - decimals: self.decimals, - spl_token_program: config.spl_token_program.clone(), - compressed_token_program_authority: self - .compressed_token_program_authority - .clone(), - system_program: self.system_program.clone(), - } - .invoke() + let account_infos = [ + self.compressed_token_program_authority, + self.payer, + config.mint, + self.destination_account, + self.authority, + self.source_account, + config.spl_interface_pda, + config.spl_token_program, + self.system_program, + ]; + invoke(&instruction, &account_infos) } - (false, false) => Err(ProgramError::Custom( - TokenSdkError::UseRegularSplTransfer.into(), - )), + TransferType::SplToSpl => { + let config = self.spl_interface.ok_or_else(|| { + ProgramError::Custom(TokenSdkError::SplInterfaceRequired.into()) + })?; + let account_infos = [ + self.source_account, + config.mint, + self.destination_account, + self.authority, + ]; + invoke(&instruction, &account_infos) + } } } /// # Errors /// * `SplInterfaceRequired` - 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_light_token = is_token_account(&self.source_account) - .map_err(|_| ProgramError::Custom(TokenSdkError::CannotDetermineAccountType.into()))?; - let dest_is_light_token = is_token_account(&self.destination_account) - .map_err(|_| ProgramError::Custom(TokenSdkError::CannotDetermineAccountType.into()))?; - - match (source_is_light_token, dest_is_light_token) { - (true, true) => TransferCpi { - source: self.source_account.clone(), - destination: self.destination_account.clone(), - amount: self.amount, - authority: self.authority.clone(), - max_top_up: None, // No limit by default + use solana_cpi::invoke_signed; + + let transfer_type = + determine_transfer_type(self.source_account.owner, self.destination_account.owner)?; + let instruction = self.instruction()?; + + match transfer_type { + TransferType::LightToLight => { + let account_infos = [ + self.source_account, + self.destination_account, + self.authority, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) } - .invoke_signed(signer_seeds), - (true, false) => { + TransferType::LightToSpl => { let config = self.spl_interface.ok_or_else(|| { ProgramError::Custom(TokenSdkError::SplInterfaceRequired.into()) })?; - - TransferToSplCpi { - source: 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(), - spl_interface_pda: config.spl_interface_pda.clone(), - spl_interface_pda_bump: config.spl_interface_pda_bump, - decimals: self.decimals, - spl_token_program: config.spl_token_program.clone(), - compressed_token_program_authority: self - .compressed_token_program_authority - .clone(), - } - .invoke_signed(signer_seeds) + let account_infos = [ + self.compressed_token_program_authority, + self.payer, + config.mint, + self.source_account, + self.destination_account, + self.authority, + config.spl_interface_pda, + config.spl_token_program, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) } - (false, true) => { + TransferType::SplToLight => { let config = self.spl_interface.ok_or_else(|| { ProgramError::Custom(TokenSdkError::SplInterfaceRequired.into()) })?; - - TransferFromSplCpi { - source_spl_token_account: self.source_account.clone(), - destination: self.destination_account.clone(), - amount: self.amount, - authority: self.authority.clone(), - mint: config.mint.clone(), - payer: self.payer.clone(), - spl_interface_pda: config.spl_interface_pda.clone(), - spl_interface_pda_bump: config.spl_interface_pda_bump, - decimals: self.decimals, - spl_token_program: config.spl_token_program.clone(), - compressed_token_program_authority: self - .compressed_token_program_authority - .clone(), - system_program: self.system_program.clone(), - } - .invoke_signed(signer_seeds) + let account_infos = [ + self.compressed_token_program_authority, + self.payer, + config.mint, + self.destination_account, + self.authority, + self.source_account, + config.spl_interface_pda, + config.spl_token_program, + self.system_program, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) } - (false, false) => Err(ProgramError::Custom( - TokenSdkError::UseRegularSplTransfer.into(), - )), + TransferType::SplToSpl => { + let config = self.spl_interface.ok_or_else(|| { + ProgramError::Custom(TokenSdkError::SplInterfaceRequired.into()) + })?; + let account_infos = [ + self.source_account, + config.mint, + self.destination_account, + self.authority, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } } } } diff --git a/sdk-libs/token-sdk/src/utils.rs b/sdk-libs/token-sdk/src/utils.rs index a43095f711..d72a28c00c 100644 --- a/sdk-libs/token-sdk/src/utils.rs +++ b/sdk-libs/token-sdk/src/utils.rs @@ -17,23 +17,37 @@ pub fn get_token_account_balance(token_account_info: &AccountInfo) -> Result Result { +/// Check if an account owner is a Light token program. +/// +/// Returns `Ok(true)` if owner is `LIGHT_TOKEN_PROGRAM_ID`. +/// Returns `Ok(false)` if owner is SPL Token or Token-2022. +/// Returns `Err` if owner is unrecognized. +pub fn is_light_token_owner(owner: &Pubkey) -> Result { let light_token_program_id = Pubkey::from(LIGHT_TOKEN_PROGRAM_ID); - if account_info.owner == &light_token_program_id { + if owner == &light_token_program_id { return Ok(true); } - let token_22 = spl_token_2022::ID; - let spl_token = Pubkey::from_str_const("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); + let spl_token = Pubkey::from(light_token_types::SPL_TOKEN_PROGRAM_ID); + let spl_token_2022 = Pubkey::from(light_token_types::SPL_TOKEN_2022_PROGRAM_ID); - if account_info.owner == &token_22 || account_info.owner == &spl_token { + if owner == &spl_token_2022 || owner == &spl_token { return Ok(false); } Err(TokenSdkError::CannotDetermineAccountType) } +/// Check if an account is a Light token account (by checking its owner). +/// +/// Returns `Ok(true)` if owner is `LIGHT_TOKEN_PROGRAM_ID`. +/// Returns `Ok(false)` if owner is SPL Token or Token-2022. +/// Returns `Err` if owner is unrecognized. +pub fn is_token_account(account_info: &AccountInfo) -> Result { + is_light_token_owner(account_info.owner) +} + pub const CLOSE_TOKEN_ACCOUNT_DISCRIMINATOR: u8 = 9; #[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)] diff --git a/sdk-libs/token-types/src/account_infos/create_compressed_mint.rs b/sdk-libs/token-types/src/account_infos/create_compressed_mint.rs index 12e4125005..f3f9afa2c3 100644 --- a/sdk-libs/token-types/src/account_infos/create_compressed_mint.rs +++ b/sdk-libs/token-types/src/account_infos/create_compressed_mint.rs @@ -3,7 +3,7 @@ use light_account_checks::AccountInfoTrait; use crate::error::{LightTokenSdkTypeError, Result}; #[repr(usize)] -pub enum CreateCompressedMintAccountInfosIndex { +pub enum CreateMintAccountInfosIndex { // Static non-CPI accounts first MintSigner = 0, LightSystemProgram = 1, @@ -21,12 +21,12 @@ pub enum CreateCompressedMintAccountInfosIndex { OutOutputQueue = 10, } -pub struct CreateCompressedMintAccountInfos<'a, T: AccountInfoTrait + Clone> { +pub struct CreateMintAccountInfos<'a, T: AccountInfoTrait + Clone> { fee_payer: &'a T, accounts: &'a [T], } -impl<'a, T: AccountInfoTrait + Clone> CreateCompressedMintAccountInfos<'a, T> { +impl<'a, T: AccountInfoTrait + Clone> CreateMintAccountInfos<'a, T> { // Idea new_with_fee_payer and new pub fn new(fee_payer: &'a T, accounts: &'a [T]) -> Self { Self { @@ -40,77 +40,77 @@ impl<'a, T: AccountInfoTrait + Clone> CreateCompressedMintAccountInfos<'a, T> { } pub fn mint_signer(&self) -> Result<&'a T> { - let index = CreateCompressedMintAccountInfosIndex::MintSigner as usize; + let index = CreateMintAccountInfosIndex::MintSigner as usize; self.accounts .get(index) .ok_or(LightTokenSdkTypeError::CpiAccountsIndexOutOfBounds(index)) } pub fn light_system_program(&self) -> Result<&'a T> { - let index = CreateCompressedMintAccountInfosIndex::LightSystemProgram as usize; + let index = CreateMintAccountInfosIndex::LightSystemProgram as usize; self.accounts .get(index) .ok_or(LightTokenSdkTypeError::CpiAccountsIndexOutOfBounds(index)) } pub fn cpi_authority_pda(&self) -> Result<&'a T> { - let index = CreateCompressedMintAccountInfosIndex::CpiAuthorityPda as usize; + let index = CreateMintAccountInfosIndex::CpiAuthorityPda as usize; self.accounts .get(index) .ok_or(LightTokenSdkTypeError::CpiAccountsIndexOutOfBounds(index)) } pub fn registered_program_pda(&self) -> Result<&'a T> { - let index = CreateCompressedMintAccountInfosIndex::RegisteredProgramPda as usize; + let index = CreateMintAccountInfosIndex::RegisteredProgramPda as usize; self.accounts .get(index) .ok_or(LightTokenSdkTypeError::CpiAccountsIndexOutOfBounds(index)) } pub fn noop_program(&self) -> Result<&'a T> { - let index = CreateCompressedMintAccountInfosIndex::NoopProgram as usize; + let index = CreateMintAccountInfosIndex::NoopProgram as usize; self.accounts .get(index) .ok_or(LightTokenSdkTypeError::CpiAccountsIndexOutOfBounds(index)) } pub fn account_compression_authority(&self) -> Result<&'a T> { - let index = CreateCompressedMintAccountInfosIndex::AccountCompressionAuthority as usize; + let index = CreateMintAccountInfosIndex::AccountCompressionAuthority as usize; self.accounts .get(index) .ok_or(LightTokenSdkTypeError::CpiAccountsIndexOutOfBounds(index)) } pub fn account_compression_program(&self) -> Result<&'a T> { - let index = CreateCompressedMintAccountInfosIndex::AccountCompressionProgram as usize; + let index = CreateMintAccountInfosIndex::AccountCompressionProgram as usize; self.accounts .get(index) .ok_or(LightTokenSdkTypeError::CpiAccountsIndexOutOfBounds(index)) } pub fn system_program(&self) -> Result<&'a T> { - let index = CreateCompressedMintAccountInfosIndex::SystemProgram as usize; + let index = CreateMintAccountInfosIndex::SystemProgram as usize; self.accounts .get(index) .ok_or(LightTokenSdkTypeError::CpiAccountsIndexOutOfBounds(index)) } pub fn self_program(&self) -> Result<&'a T> { - let index = CreateCompressedMintAccountInfosIndex::SelfProgram as usize; + let index = CreateMintAccountInfosIndex::SelfProgram as usize; self.accounts .get(index) .ok_or(LightTokenSdkTypeError::CpiAccountsIndexOutOfBounds(index)) } pub fn address_merkle_tree(&self) -> Result<&'a T> { - let index = CreateCompressedMintAccountInfosIndex::AddressMerkleTree as usize; + let index = CreateMintAccountInfosIndex::AddressMerkleTree as usize; self.accounts .get(index) .ok_or(LightTokenSdkTypeError::CpiAccountsIndexOutOfBounds(index)) } pub fn out_output_queue(&self) -> Result<&'a T> { - let index = CreateCompressedMintAccountInfosIndex::OutOutputQueue as usize; + let index = CreateMintAccountInfosIndex::OutOutputQueue as usize; self.accounts .get(index) .ok_or(LightTokenSdkTypeError::CpiAccountsIndexOutOfBounds(index)) diff --git a/sdk-libs/token-types/src/instruction/update_compressed_mint.rs b/sdk-libs/token-types/src/instruction/update_compressed_mint.rs index 5c0b7347da..626fb7c896 100644 --- a/sdk-libs/token-types/src/instruction/update_compressed_mint.rs +++ b/sdk-libs/token-types/src/instruction/update_compressed_mint.rs @@ -3,27 +3,27 @@ use crate::{AnchorDeserialize, AnchorSerialize}; /// Authority types for compressed mint updates #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, AnchorSerialize, AnchorDeserialize)] -pub enum CompressedMintAuthorityType { +pub enum MintAuthorityType { /// Authority to mint new tokens MintTokens = 0, /// Authority to freeze token accounts FreezeAccount = 1, } -impl TryFrom for CompressedMintAuthorityType { +impl TryFrom for MintAuthorityType { type Error = &'static str; fn try_from(value: u8) -> Result { match value { - 0 => Ok(CompressedMintAuthorityType::MintTokens), - 1 => Ok(CompressedMintAuthorityType::FreezeAccount), + 0 => Ok(MintAuthorityType::MintTokens), + 1 => Ok(MintAuthorityType::FreezeAccount), _ => Err("Invalid authority type"), } } } -impl From for u8 { - fn from(authority_type: CompressedMintAuthorityType) -> u8 { +impl From for u8 { + fn from(authority_type: MintAuthorityType) -> u8 { authority_type as u8 } } diff --git a/sdk-tests/csdk-anchor-full-derived-test/src/state.rs b/sdk-tests/csdk-anchor-full-derived-test/src/state.rs index 975d41282c..54ace5c653 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/src/state.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/src/state.rs @@ -1,6 +1,11 @@ use anchor_lang::prelude::*; -use light_sdk::{compressible::CompressionInfo, LightDiscriminator}; -use light_sdk_macros::RentFreeAccount; +use light_sdk::{ + compressible::CompressionInfo, + instruction::{PackedAddressTreeInfo, ValidityProof}, + LightDiscriminator, LightHasher, +}; +use light_sdk_macros::{Compressible, CompressiblePack, RentFreeAccount}; +use light_token_interface::instructions::mint_action::MintWithContext; #[derive(Default, Debug, InitSpace, RentFreeAccount)] #[account] @@ -37,3 +42,42 @@ pub struct PlaceholderRecord { pub placeholder_id: u64, pub counter: u32, } + +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug)] +pub struct AccountCreationData { + // Instruction data fields (accounts come from ctx.accounts.*) + pub owner: Pubkey, + pub category_id: u64, + pub user_name: String, + pub session_id: u64, + pub game_type: String, + pub placeholder_id: u64, + pub counter: u32, + pub mint_name: String, + pub mint_symbol: String, + pub mint_uri: String, + pub mint_decimals: u8, + pub mint_supply: u64, + pub mint_update_authority: Option, + pub mint_freeze_authority: Option, + pub additional_metadata: Option>, +} + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct TokenAccountInfo { + pub user: Pubkey, + pub mint: Pubkey, +} + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct CompressionParams { + pub proof: ValidityProof, + pub user_compressed_address: [u8; 32], + pub user_address_tree_info: PackedAddressTreeInfo, + pub user_output_state_tree_index: u8, + pub game_compressed_address: [u8; 32], + pub game_address_tree_info: PackedAddressTreeInfo, + pub game_output_state_tree_index: u8, + pub mint_bump: u8, + pub mint_with_context: MintWithContext, +} diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs index f43bb8afd2..b8c2cc4e43 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs @@ -9,7 +9,15 @@ use light_program_test::{ Indexer, ProgramTestConfig, Rpc, }; use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; +use light_token_interface::{ + instructions::mint_action::{MintInstructionData, MintWithContext}, + state::MintMetadata, +}; +use light_token_sdk::compressed_token::create_compressed_mint::{ + derive_mint_compressed_address, find_mint_address, +}; use light_token_sdk::token::find_mint_address as find_cmint_address; +use light_token_types::CPI_AUTHORITY_PDA; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey; diff --git a/sdk-tests/sdk-light-token-test/Cargo.toml b/sdk-tests/sdk-light-token-test/Cargo.toml index 0045b4f480..8febdd6b32 100644 --- a/sdk-tests/sdk-light-token-test/Cargo.toml +++ b/sdk-tests/sdk-light-token-test/Cargo.toml @@ -41,6 +41,7 @@ light-client = { workspace = true } light-compressible = { workspace = true } light-compressible-client = { workspace = true } light-compressed-account = { workspace = true } +light-token-client = { workspace = true } light-test-utils = { workspace = true, features = ["devenv"] } tokio = { version = "1.36.0", features = ["full"] } solana-sdk = "2.2" diff --git a/sdk-tests/sdk-light-token-test/src/burn.rs b/sdk-tests/sdk-light-token-test/src/burn.rs index ca55f300b0..24763da24d 100644 --- a/sdk-tests/sdk-light-token-test/src/burn.rs +++ b/sdk-tests/sdk-light-token-test/src/burn.rs @@ -14,7 +14,7 @@ pub struct BurnData { /// /// Account order: /// - accounts[0]: source (Light Token account, writable) -/// - accounts[1]: cmint (writable) +/// - accounts[1]: mint (writable) /// - accounts[2]: authority (owner, signer) /// - accounts[3]: light_token_program pub fn process_burn_invoke(accounts: &[AccountInfo], amount: u64) -> Result<(), ProgramError> { @@ -24,7 +24,7 @@ pub fn process_burn_invoke(accounts: &[AccountInfo], amount: u64) -> Result<(), BurnCpi { source: accounts[0].clone(), - cmint: accounts[1].clone(), + mint: accounts[1].clone(), amount, authority: accounts[2].clone(), max_top_up: None, @@ -38,7 +38,7 @@ pub fn process_burn_invoke(accounts: &[AccountInfo], amount: u64) -> Result<(), /// /// Account order: /// - accounts[0]: source (Light Token account, writable) -/// - accounts[1]: cmint (writable) +/// - accounts[1]: mint (writable) /// - accounts[2]: PDA authority (owner, program signs) /// - accounts[3]: light_token_program pub fn process_burn_invoke_signed( @@ -60,7 +60,7 @@ pub fn process_burn_invoke_signed( let signer_seeds: &[&[u8]] = &[TOKEN_ACCOUNT_SEED, &[bump]]; BurnCpi { source: accounts[0].clone(), - cmint: accounts[1].clone(), + mint: accounts[1].clone(), amount, authority: accounts[2].clone(), max_top_up: None, diff --git a/sdk-tests/sdk-light-token-test/src/create_cmint.rs b/sdk-tests/sdk-light-token-test/src/create_mint.rs similarity index 60% rename from sdk-tests/sdk-light-token-test/src/create_cmint.rs rename to sdk-tests/sdk-light-token-test/src/create_mint.rs index d2a305f491..c7ffe75a9d 100644 --- a/sdk-tests/sdk-light-token-test/src/create_cmint.rs +++ b/sdk-tests/sdk-light-token-test/src/create_mint.rs @@ -22,34 +22,39 @@ pub struct CreateCmintData { pub bump: u8, pub freeze_authority: Option, pub extensions: Option>, + pub rent_payment: u8, + pub write_top_up: u32, } /// Handler for creating a compressed mint (invoke) /// -/// Uses the CreateCMintCpi builder pattern. This demonstrates how to: +/// Uses the CreateMintCpi builder pattern. This demonstrates how to: /// 1. Build the CreateMintParams struct from instruction data -/// 2. Build the CreateCMintCpi with accounts +/// 2. Build the CreateMintCpi with accounts /// 3. Call invoke() which handles instruction building and CPI /// -/// Account order: +/// Account order (matches MintActionMetaConfig::to_account_metas()): /// - accounts[0]: compressed_token_program (for CPI) /// - accounts[1]: light_system_program /// - accounts[2]: mint_signer (signer) -/// - accounts[3]: payer (signer, also authority) -/// - accounts[4]: payer again (fee_payer in SDK) -/// - accounts[5]: cpi_authority_pda -/// - accounts[6]: registered_program_pda -/// - accounts[7]: account_compression_authority -/// - accounts[8]: account_compression_program -/// - accounts[9]: system_program -/// - accounts[10]: output_queue -/// - accounts[11]: address_tree -/// - accounts[12] (optional): cpi_context_account -pub fn process_create_cmint( +/// - accounts[3]: authority (signer) +/// - accounts[4]: compressible_config +/// - accounts[5]: mint (PDA, writable) +/// - accounts[6]: rent_sponsor (PDA, writable) +/// - accounts[7]: fee_payer (signer) +/// - accounts[8]: cpi_authority_pda +/// - accounts[9]: registered_program_pda +/// - accounts[10]: account_compression_authority +/// - accounts[11]: account_compression_program +/// - accounts[12]: system_program +/// - accounts[13]: output_queue +/// - accounts[14]: address_tree +/// - accounts[15] (optional): cpi_context_account +pub fn process_create_mint( accounts: &[AccountInfo], data: CreateCmintData, ) -> Result<(), ProgramError> { - if accounts.len() < 12 { + if accounts.len() < 15 { return Err(ProgramError::NotEnoughAccountKeys); } @@ -64,26 +69,30 @@ pub fn process_create_cmint( bump: data.bump, freeze_authority: data.freeze_authority, extensions: data.extensions, + rent_payment: data.rent_payment, + write_top_up: data.write_top_up, }; // Build system accounts struct let system_accounts = SystemAccountInfos { light_system_program: accounts[1].clone(), - cpi_authority_pda: accounts[5].clone(), - registered_program_pda: accounts[6].clone(), - account_compression_authority: accounts[7].clone(), - account_compression_program: accounts[8].clone(), - system_program: accounts[9].clone(), + cpi_authority_pda: accounts[8].clone(), + registered_program_pda: accounts[9].clone(), + account_compression_authority: accounts[10].clone(), + account_compression_program: accounts[11].clone(), + system_program: accounts[12].clone(), }; // Build the account infos struct - // In this case, payer == authority (accounts[3]) CreateMintCpi { mint_seed: accounts[2].clone(), authority: accounts[3].clone(), - payer: accounts[3].clone(), - address_tree: accounts[11].clone(), - output_queue: accounts[10].clone(), + payer: accounts[7].clone(), + address_tree: accounts[14].clone(), + output_queue: accounts[13].clone(), + compressible_config: accounts[4].clone(), + mint: accounts[5].clone(), + rent_sponsor: accounts[6].clone(), system_accounts, cpi_context: None, cpi_context_account: None, @@ -96,28 +105,31 @@ pub fn process_create_cmint( /// Handler for creating a compressed mint with PDA mint signer (invoke_signed) /// -/// Uses the CreateCMintCpi builder pattern with invoke_signed. +/// Uses the CreateMintCpi builder pattern with invoke_signed. /// The mint_signer is a PDA derived from this program. /// -/// Account order: +/// Account order (matches MintActionMetaConfig::to_account_metas()): /// - accounts[0]: compressed_token_program (for CPI) /// - accounts[1]: light_system_program /// - accounts[2]: mint_signer (PDA, not signer - program signs) -/// - accounts[3]: payer (signer, also authority) -/// - accounts[4]: payer again (fee_payer in SDK) -/// - accounts[5]: cpi_authority_pda -/// - accounts[6]: registered_program_pda -/// - accounts[7]: account_compression_authority -/// - accounts[8]: account_compression_program -/// - accounts[9]: system_program -/// - accounts[10]: output_queue -/// - accounts[11]: address_tree -/// - accounts[12] (optional): cpi_context_account -pub fn process_create_cmint_invoke_signed( +/// - accounts[3]: authority (signer) +/// - accounts[4]: compressible_config +/// - accounts[5]: mint (PDA, writable) +/// - accounts[6]: rent_sponsor (PDA, writable) +/// - accounts[7]: fee_payer (signer) +/// - accounts[8]: cpi_authority_pda +/// - accounts[9]: registered_program_pda +/// - accounts[10]: account_compression_authority +/// - accounts[11]: account_compression_program +/// - accounts[12]: system_program +/// - accounts[13]: output_queue +/// - accounts[14]: address_tree +/// - accounts[15] (optional): cpi_context_account +pub fn process_create_mint_invoke_signed( accounts: &[AccountInfo], data: CreateCmintData, ) -> Result<(), ProgramError> { - if accounts.len() < 12 { + if accounts.len() < 15 { return Err(ProgramError::NotEnoughAccountKeys); } @@ -140,26 +152,30 @@ pub fn process_create_cmint_invoke_signed( bump: data.bump, freeze_authority: data.freeze_authority, extensions: data.extensions, + rent_payment: data.rent_payment, + write_top_up: data.write_top_up, }; // Build system accounts struct let system_accounts = SystemAccountInfos { light_system_program: accounts[1].clone(), - cpi_authority_pda: accounts[5].clone(), - registered_program_pda: accounts[6].clone(), - account_compression_authority: accounts[7].clone(), - account_compression_program: accounts[8].clone(), - system_program: accounts[9].clone(), + cpi_authority_pda: accounts[8].clone(), + registered_program_pda: accounts[9].clone(), + account_compression_authority: accounts[10].clone(), + account_compression_program: accounts[11].clone(), + system_program: accounts[12].clone(), }; // Build the account infos struct - // In this case, payer == authority (accounts[3]) let account_infos = CreateMintCpi { mint_seed: accounts[2].clone(), authority: accounts[3].clone(), - payer: accounts[3].clone(), - address_tree: accounts[11].clone(), - output_queue: accounts[10].clone(), + payer: accounts[7].clone(), + address_tree: accounts[14].clone(), + output_queue: accounts[13].clone(), + compressible_config: accounts[4].clone(), + mint: accounts[5].clone(), + rent_sponsor: accounts[6].clone(), system_accounts, cpi_context: None, cpi_context_account: None, @@ -175,30 +191,33 @@ pub fn process_create_cmint_invoke_signed( /// Handler for creating a compressed mint with PDA mint signer AND PDA authority (invoke_signed) /// -/// Uses the SDK's CreateCMintCpi with separate authority and payer accounts. +/// Uses the SDK's CreateMintCpi with separate authority and payer accounts. /// Both mint_signer and authority are PDAs signed by this program. /// -/// Account order: +/// Account order (matches MintActionMetaConfig::to_account_metas()): /// - accounts[0]: compressed_token_program (for CPI) /// - accounts[1]: light_system_program /// - accounts[2]: mint_signer (PDA from MINT_SIGNER_SEED, not signer - program signs) /// - accounts[3]: authority (PDA from MINT_AUTHORITY_SEED, not signer - program signs) -/// - accounts[4]: fee_payer (signer) -/// - accounts[5]: cpi_authority_pda -/// - accounts[6]: registered_program_pda -/// - accounts[7]: account_compression_authority -/// - accounts[8]: account_compression_program -/// - accounts[9]: system_program -/// - accounts[10]: output_queue -/// - accounts[11]: address_tree -/// - accounts[12] (optional): cpi_context_account -pub fn process_create_cmint_with_pda_authority( +/// - accounts[4]: compressible_config +/// - accounts[5]: mint (PDA, writable) +/// - accounts[6]: rent_sponsor (PDA, writable) +/// - accounts[7]: fee_payer (signer) +/// - accounts[8]: cpi_authority_pda +/// - accounts[9]: registered_program_pda +/// - accounts[10]: account_compression_authority +/// - accounts[11]: account_compression_program +/// - accounts[12]: system_program +/// - accounts[13]: output_queue +/// - accounts[14]: address_tree +/// - accounts[15] (optional): cpi_context_account +pub fn process_create_mint_with_pda_authority( accounts: &[AccountInfo], data: CreateCmintData, ) -> Result<(), ProgramError> { use crate::MINT_AUTHORITY_SEED; - if accounts.len() < 12 { + if accounts.len() < 15 { return Err(ProgramError::NotEnoughAccountKeys); } @@ -230,25 +249,30 @@ pub fn process_create_cmint_with_pda_authority( bump: data.bump, freeze_authority: data.freeze_authority, extensions: data.extensions, + rent_payment: data.rent_payment, + write_top_up: data.write_top_up, }; // Build system accounts struct let system_accounts = SystemAccountInfos { light_system_program: accounts[1].clone(), - cpi_authority_pda: accounts[5].clone(), - registered_program_pda: accounts[6].clone(), - account_compression_authority: accounts[7].clone(), - account_compression_program: accounts[8].clone(), - system_program: accounts[9].clone(), + cpi_authority_pda: accounts[8].clone(), + registered_program_pda: accounts[9].clone(), + account_compression_authority: accounts[10].clone(), + account_compression_program: accounts[11].clone(), + system_program: accounts[12].clone(), }; // Build the account infos struct using SDK let account_infos = CreateMintCpi { mint_seed: accounts[2].clone(), authority: accounts[3].clone(), - payer: accounts[4].clone(), - address_tree: accounts[11].clone(), - output_queue: accounts[10].clone(), + payer: accounts[7].clone(), + address_tree: accounts[14].clone(), + output_queue: accounts[13].clone(), + compressible_config: accounts[4].clone(), + mint: accounts[5].clone(), + rent_sponsor: accounts[6].clone(), system_accounts, cpi_context: None, cpi_context_account: None, diff --git a/sdk-tests/sdk-light-token-test/src/ctoken_mint_to.rs b/sdk-tests/sdk-light-token-test/src/ctoken_mint_to.rs index 66bd3264f7..19dfaac507 100644 --- a/sdk-tests/sdk-light-token-test/src/ctoken_mint_to.rs +++ b/sdk-tests/sdk-light-token-test/src/ctoken_mint_to.rs @@ -24,7 +24,7 @@ pub fn process_mint_to_invoke(accounts: &[AccountInfo], amount: u64) -> Result<( } MintToCpi { - cmint: accounts[0].clone(), + mint: accounts[0].clone(), destination: accounts[1].clone(), amount, authority: accounts[2].clone(), @@ -62,7 +62,7 @@ pub fn process_mint_to_invoke_signed( let signer_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[bump]]; MintToCpi { - cmint: accounts[0].clone(), + mint: accounts[0].clone(), destination: accounts[1].clone(), amount, authority: accounts[2].clone(), diff --git a/sdk-tests/sdk-light-token-test/src/decompress_mint.rs b/sdk-tests/sdk-light-token-test/src/decompress_mint.rs index d91aa3aa58..8741e09beb 100644 --- a/sdk-tests/sdk-light-token-test/src/decompress_mint.rs +++ b/sdk-tests/sdk-light-token-test/src/decompress_mint.rs @@ -1,28 +1,28 @@ use borsh::{BorshDeserialize, BorshSerialize}; use light_token_sdk::{ - token::{CompressedMintWithContext, DecompressMintCpi, SystemAccountInfos}, + token::{DecompressMintCpi, MintWithContext, SystemAccountInfos}, ValidityProof, }; use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; use crate::{ID, MINT_AUTHORITY_SEED}; -/// Instruction data for DecompressCMint operations +/// Instruction data for DecompressMint operations #[derive(BorshSerialize, BorshDeserialize)] pub struct DecompressCmintData { - pub compressed_mint_with_context: CompressedMintWithContext, + pub compressed_mint_with_context: MintWithContext, pub proof: ValidityProof, pub rent_payment: u8, pub write_top_up: u32, } -/// Handler for decompressing CMint with PDA authority (invoke_signed) +/// Handler for decompressing Mint with PDA authority (invoke_signed) /// /// Account order: /// - accounts[0]: mint_seed (readonly) /// - accounts[1]: authority (PDA, readonly - program signs) /// - accounts[2]: payer (signer, writable) -/// - accounts[3]: cmint (writable) +/// - accounts[3]: mint (writable) /// - accounts[4]: compressible_config (readonly) /// - accounts[5]: rent_sponsor (writable) /// - accounts[6]: state_tree (writable) @@ -63,7 +63,7 @@ pub fn process_decompress_mint_invoke_signed( DecompressMintCpi { authority: accounts[0].clone(), payer: accounts[1].clone(), - cmint: accounts[2].clone(), + mint: accounts[2].clone(), compressible_config: accounts[3].clone(), rent_sponsor: accounts[4].clone(), state_tree: accounts[5].clone(), diff --git a/sdk-tests/sdk-light-token-test/src/lib.rs b/sdk-tests/sdk-light-token-test/src/lib.rs index 4fc437dc61..21179b2e09 100644 --- a/sdk-tests/sdk-light-token-test/src/lib.rs +++ b/sdk-tests/sdk-light-token-test/src/lib.rs @@ -4,7 +4,7 @@ mod approve; mod burn; mod close; mod create_ata; -mod create_cmint; +mod create_mint; mod create_token_account; mod ctoken_mint_to; mod decompress_mint; @@ -21,9 +21,9 @@ pub use approve::{process_approve_invoke, process_approve_invoke_signed, Approve pub use burn::{process_burn_invoke, process_burn_invoke_signed, BurnData}; pub use close::{process_close_account_invoke, process_close_account_invoke_signed}; pub use create_ata::{process_create_ata_invoke, process_create_ata_invoke_signed, CreateAtaData}; -pub use create_cmint::{ - process_create_cmint, process_create_cmint_invoke_signed, - process_create_cmint_with_pda_authority, CreateCmintData, MINT_SIGNER_SEED, +pub use create_mint::{ + process_create_mint, process_create_mint_invoke_signed, process_create_mint_with_pda_authority, + CreateCmintData, MINT_SIGNER_SEED, }; pub use create_token_account::{ process_create_token_account_invoke, process_create_token_account_invoke_signed, @@ -124,11 +124,11 @@ pub enum InstructionType { BurnInvoke = 29, /// Burn CTokens with PDA authority (invoke_signed) BurnInvokeSigned = 30, - /// Mint to Light Token from decompressed CMint (invoke) + /// Mint to Light Token from decompressed Mint (invoke) CTokenMintToInvoke = 31, - /// Mint to Light Token from decompressed CMint with PDA authority (invoke_signed) + /// Mint to Light Token from decompressed Mint with PDA authority (invoke_signed) CTokenMintToInvokeSigned = 32, - /// Decompress CMint with PDA authority (invoke_signed) + /// Decompress Mint with PDA authority (invoke_signed) DecompressCmintInvokeSigned = 33, /// Transfer cTokens with checked decimals (invoke) CTokenTransferCheckedInvoke = 34, @@ -202,7 +202,7 @@ pub fn process_instruction( InstructionType::CreateCmint => { let data = CreateCmintData::try_from_slice(&instruction_data[1..]) .map_err(|_| ProgramError::InvalidInstructionData)?; - process_create_cmint(accounts, data) + process_create_mint(accounts, data) } InstructionType::CreateTokenAccountInvoke => { let data = CreateTokenAccountData::try_from_slice(&instruction_data[1..]) @@ -239,12 +239,12 @@ pub fn process_instruction( InstructionType::CreateCmintInvokeSigned => { let data = CreateCmintData::try_from_slice(&instruction_data[1..]) .map_err(|_| ProgramError::InvalidInstructionData)?; - process_create_cmint_invoke_signed(accounts, data) + process_create_mint_invoke_signed(accounts, data) } InstructionType::CreateCmintWithPdaAuthority => { let data = CreateCmintData::try_from_slice(&instruction_data[1..]) .map_err(|_| ProgramError::InvalidInstructionData)?; - process_create_cmint_with_pda_authority(accounts, data) + process_create_mint_with_pda_authority(accounts, data) } InstructionType::SplToCtokenInvoke => { let data = TransferFromSplData::try_from_slice(&instruction_data[1..]) diff --git a/sdk-tests/sdk-light-token-test/src/transfer_checked.rs b/sdk-tests/sdk-light-token-test/src/transfer_checked.rs index 8e88c4355b..65c78fcd71 100644 --- a/sdk-tests/sdk-light-token-test/src/transfer_checked.rs +++ b/sdk-tests/sdk-light-token-test/src/transfer_checked.rs @@ -15,7 +15,7 @@ pub struct TransferCheckedData { /// /// Account order: /// - accounts[0]: source ctoken account -/// - accounts[1]: mint (SPL, T22, or decompressed CMint) +/// - accounts[1]: mint (SPL, T22, or decompressed Mint) /// - accounts[2]: destination ctoken account /// - accounts[3]: authority (signer) pub fn process_transfer_checked_invoke( @@ -44,7 +44,7 @@ pub fn process_transfer_checked_invoke( /// /// Account order: /// - accounts[0]: source ctoken account (PDA-owned) -/// - accounts[1]: mint (SPL, T22, or decompressed CMint) +/// - accounts[1]: mint (SPL, T22, or decompressed Mint) /// - accounts[2]: destination ctoken account /// - accounts[3]: authority (PDA) pub fn process_transfer_checked_invoke_signed( diff --git a/sdk-tests/sdk-light-token-test/tests/scenario_cmint.rs b/sdk-tests/sdk-light-token-test/tests/scenario_light_mint.rs similarity index 95% rename from sdk-tests/sdk-light-token-test/tests/scenario_cmint.rs rename to sdk-tests/sdk-light-token-test/tests/scenario_light_mint.rs index 731754e4db..9b460f1775 100644 --- a/sdk-tests/sdk-light-token-test/tests/scenario_cmint.rs +++ b/sdk-tests/sdk-light-token-test/tests/scenario_light_mint.rs @@ -21,7 +21,7 @@ use solana_sdk::{signature::Keypair, signer::Signer}; /// Test the complete cMint to cToken flow using direct SDK calls #[tokio::test] -async fn test_cmint_to_ctoken_scenario() { +async fn test_mint_to_ctoken_scenario() { // 1. Setup test environment let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) .await @@ -46,18 +46,17 @@ async fn test_cmint_to_ctoken_scenario() { let mint_amount2 = 5_000u64; let transfer_amount = 3_000u64; - let (mint, _compression_address, ata_pubkeys, _mint_seed) = - shared::setup_create_compressed_mint( - &mut rpc, - &payer, - payer.pubkey(), // mint_authority - 9, // decimals - vec![ - (mint_amount1, owner1.pubkey()), - (mint_amount2, owner2.pubkey()), - ], - ) - .await; + let (mint, _compression_address, ata_pubkeys, _mint_seed) = shared::setup_create_mint( + &mut rpc, + &payer, + payer.pubkey(), // mint_authority + 9, // decimals + vec![ + (mint_amount1, owner1.pubkey()), + (mint_amount2, owner2.pubkey()), + ], + ) + .await; let ctoken_ata1 = ata_pubkeys[0]; let ctoken_ata2 = ata_pubkeys[1]; diff --git a/sdk-tests/sdk-light-token-test/tests/scenario_cmint_compression_only.rs b/sdk-tests/sdk-light-token-test/tests/scenario_light_mint_compression_only.rs similarity index 98% rename from sdk-tests/sdk-light-token-test/tests/scenario_cmint_compression_only.rs rename to sdk-tests/sdk-light-token-test/tests/scenario_light_mint_compression_only.rs index c5a622eb5e..17cf7e2ba1 100644 --- a/sdk-tests/sdk-light-token-test/tests/scenario_cmint_compression_only.rs +++ b/sdk-tests/sdk-light-token-test/tests/scenario_light_mint_compression_only.rs @@ -23,7 +23,7 @@ use solana_sdk::{signature::Keypair, signer::Signer}; /// Test the complete cMint to cToken flow with compression_only: true #[tokio::test] -async fn test_cmint_to_ctoken_scenario_compression_only() { +async fn test_mint_to_ctoken_scenario_compression_only() { // 1. Setup test environment let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)) .await @@ -50,7 +50,7 @@ async fn test_cmint_to_ctoken_scenario_compression_only() { // Use compression_only: true for this test let (mint, _compression_address, ata_pubkeys) = - shared::setup_create_compressed_mint_with_compression_only( + shared::setup_create_mint_with_compression_only( &mut rpc, &payer, payer.pubkey(), // mint_authority diff --git a/sdk-tests/sdk-light-token-test/tests/shared.rs b/sdk-tests/sdk-light-token-test/tests/shared.rs index 38eb17b3c6..4d36af0f53 100644 --- a/sdk-tests/sdk-light-token-test/tests/shared.rs +++ b/sdk-tests/sdk-light-token-test/tests/shared.rs @@ -1,6 +1,5 @@ // Shared test utilities for sdk-light-token-test -use borsh::BorshDeserialize; use light_client::{indexer::Indexer, rpc::Rpc}; use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; @@ -9,7 +8,7 @@ use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; /// Note: This decompresses the mint first, then uses MintTo to mint to ctoken accounts. /// Returns (mint_pda, compression_address, ata_pubkeys, mint_seed_keypair) #[allow(unused)] -pub async fn setup_create_compressed_mint( +pub async fn setup_create_mint( rpc: &mut (impl Rpc + Indexer), payer: &Keypair, mint_authority: Pubkey, @@ -57,17 +56,19 @@ pub async fn setup_create_compressed_mint( bump, freeze_authority: None, extensions: None, + rent_payment: 16, + write_top_up: 766, }; // Create instruction directly using SDK - let create_cmint_builder = CreateMint::new( + let create_mint_builder = CreateMint::new( params, mint_seed.pubkey(), payer.pubkey(), address_tree.tree, output_queue, ); - let instruction = create_cmint_builder.instruction().unwrap(); + let instruction = create_mint_builder.instruction().unwrap(); // Send transaction rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_seed]) @@ -115,92 +116,30 @@ pub async fn setup_create_compressed_mint( .filter(|(_, (amount, _))| *amount > 0) .collect(); - if !recipients_with_amount.is_empty() { - // First, decompress the compressed mint to get a CMint account - // Then use MintTo to mint to ctoken accounts - use light_token_interface::{ - instructions::mint_action::CompressedMintWithContext, state::CompressedMint, - }; - use light_token_sdk::token::DecompressMint; - - let compressed_mint_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value - .expect("Compressed mint should exist"); - - let compressed_mint = CompressedMint::deserialize( - &mut compressed_mint_account - .data - .as_ref() - .unwrap() - .data - .as_slice(), - ) - .unwrap(); - - // Get validity proof for the decompress operation - let rpc_result = rpc - .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) - .await - .unwrap() - .value; - - let compressed_mint_with_context = CompressedMintWithContext { - address: compression_address, - leaf_index: compressed_mint_account.leaf_index, - prove_by_index: true, - root_index: rpc_result.accounts[0] - .root_index - .root_index() - .unwrap_or_default(), - mint: Some(compressed_mint.try_into().unwrap()), - }; - - let decompress_ix = DecompressMint { - payer: payer.pubkey(), + // Mint to each recipient using the decompressed Mint (CreateMint already decompresses) + for (idx, (amount, _)) in &recipients_with_amount { + let mint_instruction = MintTo { + mint, + destination: ata_pubkeys[*idx], + amount: *amount, authority: mint_authority, - state_tree: compressed_mint_account.tree_info.tree, - input_queue: compressed_mint_account.tree_info.queue, - output_queue, - compressed_mint_with_context, - proof: rpc_result.proof, - rent_payment: 16, // ~24 hours rent (16 epochs * 1.5h per epoch) - write_top_up: 766, // ~3 hours per write + max_top_up: None, } .instruction() .unwrap(); - rpc.create_and_send_transaction(&[decompress_ix], &payer.pubkey(), &[payer]) + rpc.create_and_send_transaction(&[mint_instruction], &payer.pubkey(), &[payer]) .await .unwrap(); - - // Now mint to each recipient using the decompressed CMint - for (idx, (amount, _)) in &recipients_with_amount { - let mint_instruction = MintTo { - cmint: mint, - destination: ata_pubkeys[*idx], - amount: *amount, - authority: mint_authority, - max_top_up: None, - } - .instruction() - .unwrap(); - - rpc.create_and_send_transaction(&[mint_instruction], &payer.pubkey(), &[payer]) - .await - .unwrap(); - } } (mint, compression_address, ata_pubkeys, mint_seed) } -/// Same as setup_create_compressed_mint but with optional freeze_authority +/// Same as setup_create_mint but with optional freeze_authority /// Returns (mint_pda, compression_address, ata_pubkeys) #[allow(unused)] -pub async fn setup_create_compressed_mint_with_freeze_authority( +pub async fn setup_create_mint_with_freeze_authority( rpc: &mut (impl Rpc + Indexer), payer: &Keypair, mint_authority: Pubkey, @@ -249,86 +188,25 @@ pub async fn setup_create_compressed_mint_with_freeze_authority( bump, freeze_authority, extensions: None, + rent_payment: 16, + write_top_up: 766, }; // Create instruction directly using SDK - let create_cmint_builder = CreateMint::new( + let create_mint_builder = CreateMint::new( params, mint_seed.pubkey(), payer.pubkey(), address_tree.tree, output_queue, ); - let instruction = create_cmint_builder.instruction().unwrap(); + let instruction = create_mint_builder.instruction().unwrap(); - // Send transaction + // Send transaction (CreateMint now creates both compressed mint AND Mint Solana account) rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_seed]) .await .unwrap(); - // Verify the compressed mint was created and get it for decompression - let compressed_mint_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value - .expect("Compressed mint should exist after setup"); - - // Decompress the mint to create an on-chain CMint account - // This is required for freeze/thaw operations which need to read the mint - { - use light_token_interface::{ - instructions::mint_action::CompressedMintWithContext, state::CompressedMint, - }; - use light_token_sdk::token::DecompressMint; - - let compressed_mint = CompressedMint::deserialize( - &mut compressed_mint_account - .data - .as_ref() - .unwrap() - .data - .as_slice(), - ) - .unwrap(); - - // Get validity proof for the decompress operation - let rpc_result = rpc - .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) - .await - .unwrap() - .value; - - let compressed_mint_with_context = CompressedMintWithContext { - address: compression_address, - leaf_index: compressed_mint_account.leaf_index, - prove_by_index: true, - root_index: rpc_result.accounts[0] - .root_index - .root_index() - .unwrap_or_default(), - mint: Some(compressed_mint.try_into().unwrap()), - }; - - let decompress_ix = DecompressMint { - payer: payer.pubkey(), - authority: mint_authority, - state_tree: compressed_mint_account.tree_info.tree, - input_queue: compressed_mint_account.tree_info.queue, - output_queue, - compressed_mint_with_context, - proof: rpc_result.proof, - rent_payment: 16, // ~24 hours rent (16 epochs * 1.5h per epoch) - write_top_up: 766, // ~3 hours per write - } - .instruction() - .unwrap(); - - rpc.create_and_send_transaction(&[decompress_ix], &payer.pubkey(), &[payer]) - .await - .unwrap(); - } - // If no recipients, return early if recipients.is_empty() { return (mint, compression_address, vec![]); @@ -361,7 +239,7 @@ pub async fn setup_create_compressed_mint_with_freeze_authority( if !recipients_with_amount.is_empty() { for (idx, (amount, _)) in &recipients_with_amount { let mint_instruction = MintTo { - cmint: mint, + mint, destination: ata_pubkeys[*idx], amount: *amount, authority: mint_authority, @@ -379,9 +257,9 @@ pub async fn setup_create_compressed_mint_with_freeze_authority( (mint, compression_address, ata_pubkeys) } -/// Same as setup_create_compressed_mint but with compression_only flag set +/// Same as setup_create_mint but with compression_only flag set #[allow(unused)] -pub async fn setup_create_compressed_mint_with_compression_only( +pub async fn setup_create_mint_with_compression_only( rpc: &mut (impl Rpc + Indexer), payer: &Keypair, mint_authority: Pubkey, @@ -430,17 +308,19 @@ pub async fn setup_create_compressed_mint_with_compression_only( bump, freeze_authority: None, extensions: None, + rent_payment: 16, + write_top_up: 766, }; // Create instruction directly using SDK - let create_cmint_builder = CreateMint::new( + let create_mint_builder = CreateMint::new( params, mint_seed.pubkey(), payer.pubkey(), address_tree.tree, output_queue, ); - let instruction = create_cmint_builder.instruction().unwrap(); + let instruction = create_mint_builder.instruction().unwrap(); // Send transaction rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_seed]) @@ -495,84 +375,95 @@ pub async fn setup_create_compressed_mint_with_compression_only( .filter(|(_, (amount, _))| *amount > 0) .collect(); - if !recipients_with_amount.is_empty() { - // First, decompress the compressed mint to get a CMint account - // Then use MintTo to mint to ctoken accounts - use light_token_interface::{ - instructions::mint_action::CompressedMintWithContext, state::CompressedMint, - }; - use light_token_sdk::token::DecompressMint; - - let compressed_mint_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value - .expect("Compressed mint should exist"); - - let compressed_mint = CompressedMint::deserialize( - &mut compressed_mint_account - .data - .as_ref() - .unwrap() - .data - .as_slice(), - ) - .unwrap(); - - // Get validity proof for the decompress operation - let rpc_result = rpc - .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) - .await - .unwrap() - .value; - - let compressed_mint_with_context = CompressedMintWithContext { - address: compression_address, - leaf_index: compressed_mint_account.leaf_index, - prove_by_index: true, - root_index: rpc_result.accounts[0] - .root_index - .root_index() - .unwrap_or_default(), - mint: Some(compressed_mint.try_into().unwrap()), - }; - - let decompress_ix = DecompressMint { - payer: payer.pubkey(), + // Mint to each recipient using the decompressed Mint (CreateMint already decompresses) + for (idx, (amount, _)) in &recipients_with_amount { + let mint_instruction = MintTo { + mint, + destination: ata_pubkeys[*idx], + amount: *amount, authority: mint_authority, - state_tree: compressed_mint_account.tree_info.tree, - input_queue: compressed_mint_account.tree_info.queue, - output_queue, - compressed_mint_with_context, - proof: rpc_result.proof, - rent_payment: 16, // ~24 hours rent (16 epochs * 1.5h per epoch) - write_top_up: 766, // ~3 hours per write + max_top_up: None, } .instruction() .unwrap(); - rpc.create_and_send_transaction(&[decompress_ix], &payer.pubkey(), &[payer]) + rpc.create_and_send_transaction(&[mint_instruction], &payer.pubkey(), &[payer]) .await .unwrap(); - - // Now mint to each recipient using the decompressed CMint - for (idx, (amount, _)) in &recipients_with_amount { - let mint_instruction = MintTo { - cmint: mint, - destination: ata_pubkeys[*idx], - amount: *amount, - authority: mint_authority, - max_top_up: None, - } - .instruction() - .unwrap(); - - rpc.create_and_send_transaction(&[mint_instruction], &payer.pubkey(), &[payer]) - .await - .unwrap(); - } } (mint, compression_address, ata_pubkeys) } + +/// Creates a compressed-only mint (no decompression) using light-token-client. +/// This creates ONLY the compressed mint account, NOT the Mint Solana account. +/// Use this to test the DecompressMint instruction. +/// Returns (mint_pda, compression_address, mint_seed_keypair) +#[allow(unused)] +pub async fn setup_create_compressed_only_mint( + rpc: &mut (impl Rpc + Indexer), + payer: &Keypair, + mint_authority: Pubkey, + decimals: u8, +) -> (Pubkey, [u8; 32], Keypair) { + use light_token_client::instructions::mint_action::{ + create_mint_action_instruction, MintActionParams, NewMint, + }; + use light_token_sdk::token::{derive_mint_compressed_address, find_mint_address}; + + let mint_seed = Keypair::new(); + let address_tree = rpc.get_address_tree_v2(); + + // Derive addresses + let compression_address = + derive_mint_compressed_address(&mint_seed.pubkey(), &address_tree.tree); + let (mint_pda, _bump) = find_mint_address(&mint_seed.pubkey()); + + // Create compressed-only mint using light-token-client + // By NOT including DecompressMint action, only the compressed mint is created + let create_ix = create_mint_action_instruction( + rpc, + MintActionParams { + compressed_mint_address: compression_address, + mint_seed: mint_seed.pubkey(), + authority: mint_authority, + payer: payer.pubkey(), + actions: vec![], // No actions - just create compressed mint + new_mint: Some(NewMint { + decimals, + supply: 0, + mint_authority, + freeze_authority: None, + metadata: None, + version: 3, + }), + }, + ) + .await + .unwrap(); + + // Send transaction - mint_seed must sign as mint_signer + rpc.create_and_send_transaction(&[create_ix], &payer.pubkey(), &[payer, &mint_seed]) + .await + .unwrap(); + + // Verify compressed mint was created + let compressed_account = rpc + .get_compressed_account(compression_address, None) + .await + .unwrap() + .value; + assert!( + compressed_account.is_some(), + "Compressed mint should exist after creation" + ); + + // Verify NO Mint Solana account exists + let mint_account = rpc.get_account(mint_pda).await.unwrap(); + assert!( + mint_account.is_none(), + "Mint Solana account should NOT exist for compressed-only mint" + ); + + (mint_pda, compression_address, mint_seed) +} diff --git a/sdk-tests/sdk-light-token-test/tests/test_approve_revoke.rs b/sdk-tests/sdk-light-token-test/tests/test_approve_revoke.rs index 296b4cd222..af47d5cac2 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_approve_revoke.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_approve_revoke.rs @@ -24,7 +24,7 @@ async fn test_approve_invoke() { let payer = rpc.get_payer().insecure_clone(); // Create a compressed mint with an ATA for the payer with 1000 tokens - let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = setup_create_compressed_mint( + let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = setup_create_mint( &mut rpc, &payer, payer.pubkey(), @@ -89,8 +89,7 @@ async fn test_approve_invoke_signed() { // Create a compressed mint with an ATA for the PDA owner with 1000 tokens let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = - setup_create_compressed_mint(&mut rpc, &payer, payer.pubkey(), 9, vec![(1000, pda_owner)]) - .await; + setup_create_mint(&mut rpc, &payer, payer.pubkey(), 9, vec![(1000, pda_owner)]).await; let ata = ata_pubkeys[0]; let delegate = Keypair::new(); @@ -144,7 +143,7 @@ async fn test_revoke_invoke() { let payer = rpc.get_payer().insecure_clone(); // Create a compressed mint with an ATA for the payer with 1000 tokens - let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = setup_create_compressed_mint( + let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = setup_create_mint( &mut rpc, &payer, payer.pubkey(), @@ -236,8 +235,7 @@ async fn test_revoke_invoke_signed() { // Create a compressed mint with an ATA for the PDA owner with 1000 tokens let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = - setup_create_compressed_mint(&mut rpc, &payer, payer.pubkey(), 9, vec![(1000, pda_owner)]) - .await; + setup_create_mint(&mut rpc, &payer, payer.pubkey(), 9, vec![(1000, pda_owner)]).await; let ata = ata_pubkeys[0]; let delegate = Keypair::new(); diff --git a/sdk-tests/sdk-light-token-test/tests/test_burn.rs b/sdk-tests/sdk-light-token-test/tests/test_burn.rs index fecab7c855..4c19906515 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_burn.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_burn.rs @@ -23,16 +23,15 @@ async fn test_burn_invoke() { let payer = rpc.get_payer().insecure_clone(); // Create a decompressed mint (required for burn) with an ATA for the payer with 1000 tokens - let (mint_pda, _compression_address, ata_pubkeys) = - setup_create_compressed_mint_with_freeze_authority( - &mut rpc, - &payer, - payer.pubkey(), - None, // No freeze authority needed for burn test - 9, - vec![(1000, payer.pubkey())], - ) - .await; + let (mint_pda, _compression_address, ata_pubkeys) = setup_create_mint_with_freeze_authority( + &mut rpc, + &payer, + payer.pubkey(), + None, // No freeze authority needed for burn test + 9, + vec![(1000, payer.pubkey())], + ) + .await; let ata = ata_pubkeys[0]; let burn_amount = 300u64; @@ -53,7 +52,7 @@ async fn test_burn_invoke() { program_id: ID, accounts: vec![ AccountMeta::new(ata, false), // source - AccountMeta::new(mint_pda, false), // cmint + AccountMeta::new(mint_pda, false), // mint AccountMeta::new_readonly(payer.pubkey(), true), // authority (signer) AccountMeta::new_readonly(light_token_program, false), // light_token_program ], @@ -89,16 +88,15 @@ async fn test_burn_invoke_signed() { let (pda_owner, _bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); // Create a decompressed mint with an ATA for the PDA owner with 1000 tokens - let (mint_pda, _compression_address, ata_pubkeys) = - setup_create_compressed_mint_with_freeze_authority( - &mut rpc, - &payer, - payer.pubkey(), - None, // No freeze authority needed for burn test - 9, - vec![(1000, pda_owner)], - ) - .await; + let (mint_pda, _compression_address, ata_pubkeys) = setup_create_mint_with_freeze_authority( + &mut rpc, + &payer, + payer.pubkey(), + None, // No freeze authority needed for burn test + 9, + vec![(1000, pda_owner)], + ) + .await; let ata = ata_pubkeys[0]; let burn_amount = 500u64; @@ -119,7 +117,7 @@ async fn test_burn_invoke_signed() { program_id: ID, accounts: vec![ AccountMeta::new(ata, false), // source - AccountMeta::new(mint_pda, false), // cmint + AccountMeta::new(mint_pda, false), // mint AccountMeta::new_readonly(pda_owner, false), // PDA authority (program signs) AccountMeta::new_readonly(light_token_program, false), // light_token_program ], diff --git a/sdk-tests/sdk-light-token-test/tests/test_close.rs b/sdk-tests/sdk-light-token-test/tests/test_close.rs index 89212270e7..eee94559c2 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_close.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_close.rs @@ -21,7 +21,7 @@ async fn test_close_invoke() { let payer = rpc.get_payer().insecure_clone(); // Create a compressed mint with an ATA for the payer - let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = setup_create_compressed_mint( + let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = setup_create_mint( &mut rpc, &payer, payer.pubkey(), @@ -78,7 +78,7 @@ async fn test_close_invoke_signed() { let (pda_owner, _bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); // Create a compressed mint with an ATA for the PDA owner - let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = setup_create_compressed_mint( + let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = setup_create_mint( &mut rpc, &payer, payer.pubkey(), diff --git a/sdk-tests/sdk-light-token-test/tests/test_create_ata.rs b/sdk-tests/sdk-light-token-test/tests/test_create_ata.rs index e1bf29154f..937b92ee15 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_create_ata.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_create_ata.rs @@ -7,7 +7,7 @@ use light_client::rpc::Rpc; use light_program_test::{LightProgramTest, ProgramTestConfig}; use light_token_sdk::token::LIGHT_TOKEN_PROGRAM_ID; use native_ctoken_examples::{CreateAtaData, ATA_SEED, ID}; -use shared::setup_create_compressed_mint; +use shared::setup_create_mint; use solana_sdk::{ instruction::{AccountMeta, Instruction}, pubkey::Pubkey, @@ -29,7 +29,7 @@ async fn test_create_ata_invoke() { // Create compressed mint first (using helper) let (mint_pda, _compression_address, _, _mint_seed) = - setup_create_compressed_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; + setup_create_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; // Derive the ATA address let owner = payer.pubkey(); @@ -103,7 +103,7 @@ async fn test_create_ata_invoke_signed() { // Create compressed mint first (using helper) let (mint_pda, _compression_address, _, _mint_seed) = - setup_create_compressed_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; + setup_create_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; // Derive the PDA that will act as payer/owner (using ATA_SEED) let (pda_owner, _pda_bump) = Pubkey::find_program_address(&[ATA_SEED], &ID); diff --git a/sdk-tests/sdk-light-token-test/tests/test_create_cmint.rs b/sdk-tests/sdk-light-token-test/tests/test_create_mint.rs similarity index 76% rename from sdk-tests/sdk-light-token-test/tests/test_create_cmint.rs rename to sdk-tests/sdk-light-token-test/tests/test_create_mint.rs index 828b936353..5d5be5628d 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_create_cmint.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_create_mint.rs @@ -1,4 +1,4 @@ -// Tests for CreateCMintCpi (CreateCmint instruction) +// Tests for CreateMintCpi (CreateCmint instruction) mod shared; @@ -11,7 +11,10 @@ use light_token_interface::{ }, state::AdditionalMetadata, }; -use light_token_sdk::compressed_token::mint_action::MintActionMetaConfig; +use light_token_sdk::{ + compressed_token::mint_action::MintActionMetaConfig, + token::{config_pda, rent_sponsor_pda}, +}; use native_ctoken_examples::{CreateCmintData, ID, MINT_SIGNER_SEED}; use solana_sdk::{ instruction::{AccountMeta, Instruction}, @@ -20,7 +23,7 @@ use solana_sdk::{ signer::Signer, }; -/// Test creating a compressed mint using CreateCMintCpi::invoke() +/// Test creating a compressed mint using CreateMintCpi::invoke() #[tokio::test] async fn test_create_compressed_mint() { let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( @@ -63,7 +66,7 @@ async fn test_create_compressed_mint() { .value; // Create instruction data for wrapper program with TokenMetadata extension - let create_cmint_data = CreateCmintData { + let create_mint_data = CreateCmintData { decimals, address_merkle_tree_root_index: rpc_result.addresses[0].root_index, mint_authority, @@ -90,8 +93,10 @@ async fn test_create_compressed_mint() { ]), }, )]), + rent_payment: 16, + write_top_up: 766, }; - let instruction_data = [vec![0u8], create_cmint_data.try_to_vec().unwrap()].concat(); + let instruction_data = [vec![0u8], create_mint_data.try_to_vec().unwrap()].concat(); // Add compressed token program as first account for CPI, then all SDK-generated accounts let mut wrapper_accounts = vec![AccountMeta::new_readonly( @@ -105,6 +110,7 @@ async fn test_create_compressed_mint() { address_tree.tree, output_queue, ) + .with_compressible_mint(mint_pda, config_pda(), rent_sponsor_pda()) .to_account_metas(); wrapper_accounts.extend(account_metas); @@ -118,16 +124,12 @@ async fn test_create_compressed_mint() { .await .unwrap(); - let compressed_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value; - - assert!(compressed_account.is_some(), "Compressed mint should exist"); + // Verify the Mint Solana account was created (CreateMint now decompresses automatically) + let mint_account = rpc.get_account(mint_pda).await.unwrap(); + assert!(mint_account.is_some(), "Mint Solana account should exist"); } -/// Test creating a compressed mint with PDA mint signer using CreateCMintCpi::invoke_signed() +/// Test creating a compressed mint with PDA mint signer using CreateMintCpi::invoke_signed() #[tokio::test] async fn test_create_compressed_mint_invoke_signed() { let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( @@ -172,7 +174,7 @@ async fn test_create_compressed_mint_invoke_signed() { .value; // Create instruction data for wrapper program - let create_cmint_data = CreateCmintData { + let create_mint_data = CreateCmintData { decimals, address_merkle_tree_root_index: rpc_result.addresses[0].root_index, mint_authority, @@ -182,27 +184,33 @@ async fn test_create_compressed_mint_invoke_signed() { bump: mint_bump, freeze_authority: None, extensions: None, + rent_payment: 16, + write_top_up: 766, }; // Discriminator 12 = CreateCmintInvokeSigned - let instruction_data = [vec![12u8], create_cmint_data.try_to_vec().unwrap()].concat(); + let instruction_data = [vec![12u8], create_mint_data.try_to_vec().unwrap()].concat(); // Build accounts manually since SDK marks mint_signer as signer, but we need it as non-signer // for invoke_signed (the wrapper program signs via CPI) + // Account order matches MintActionMetaConfig::to_account_metas() with mint_signer as non-signer let system_accounts = light_token_sdk::token::SystemAccounts::default(); let wrapper_accounts = vec![ - AccountMeta::new_readonly(compressed_token_program_id, false), - AccountMeta::new_readonly(system_accounts.light_system_program, false), + AccountMeta::new_readonly(compressed_token_program_id, false), // [0] compressed_token_program + AccountMeta::new_readonly(system_accounts.light_system_program, false), // [1] light_system_program // mint_signer NOT marked as signer - program will sign via invoke_signed - AccountMeta::new_readonly(mint_signer_pda, false), - AccountMeta::new_readonly(mint_authority, true), - AccountMeta::new(payer.pubkey(), true), - AccountMeta::new_readonly(system_accounts.cpi_authority_pda, false), - AccountMeta::new_readonly(system_accounts.registered_program_pda, false), - AccountMeta::new_readonly(system_accounts.account_compression_authority, false), - AccountMeta::new_readonly(system_accounts.account_compression_program, false), - AccountMeta::new_readonly(system_accounts.system_program, false), - AccountMeta::new(output_queue, false), - AccountMeta::new(address_tree.tree, false), + AccountMeta::new_readonly(mint_signer_pda, false), // [2] mint_signer (PDA) + AccountMeta::new_readonly(payer.pubkey(), true), // [3] authority (signer) + AccountMeta::new_readonly(config_pda(), false), // [4] compressible_config + AccountMeta::new(mint_pda, false), // [5] mint + AccountMeta::new(rent_sponsor_pda(), false), // [6] rent_sponsor + AccountMeta::new(payer.pubkey(), true), // [7] fee_payer (signer) + AccountMeta::new_readonly(system_accounts.cpi_authority_pda, false), // [8] + AccountMeta::new_readonly(system_accounts.registered_program_pda, false), // [9] + AccountMeta::new_readonly(system_accounts.account_compression_authority, false), // [10] + AccountMeta::new_readonly(system_accounts.account_compression_program, false), // [11] + AccountMeta::new_readonly(system_accounts.system_program, false), // [12] + AccountMeta::new(output_queue, false), // [13] + AccountMeta::new(address_tree.tree, false), // [14] ]; let instruction = Instruction { @@ -216,11 +224,7 @@ async fn test_create_compressed_mint_invoke_signed() { .await .unwrap(); - let compressed_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value; - - assert!(compressed_account.is_some(), "Compressed mint should exist"); + // Verify the Mint Solana account was created (CreateMint now decompresses automatically) + let mint_account = rpc.get_account(mint_pda).await.unwrap(); + assert!(mint_account.is_some(), "Mint Solana account should exist"); } diff --git a/sdk-tests/sdk-light-token-test/tests/test_create_token_account.rs b/sdk-tests/sdk-light-token-test/tests/test_create_token_account.rs index c27a5fd155..fd220462c3 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_create_token_account.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_create_token_account.rs @@ -7,7 +7,7 @@ use light_client::rpc::Rpc; use light_program_test::{LightProgramTest, ProgramTestConfig}; use light_token_sdk::token::LIGHT_TOKEN_PROGRAM_ID; use native_ctoken_examples::{CreateTokenAccountData, ID}; -use shared::setup_create_compressed_mint; +use shared::setup_create_mint; use solana_sdk::{ instruction::{AccountMeta, Instruction}, pubkey::Pubkey, @@ -30,7 +30,7 @@ async fn test_create_token_account_invoke() { // Create compressed mint first (using helper) let (mint_pda, _compression_address, _, _mint_seed) = - setup_create_compressed_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; + setup_create_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; // Create ctoken account via wrapper program let ctoken_account = Keypair::new(); @@ -103,7 +103,7 @@ async fn test_create_token_account_invoke_signed() { // Create compressed mint first (using helper) let (mint_pda, _compression_address, _, _mint_seed) = - setup_create_compressed_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; + setup_create_mint(&mut rpc, &payer, mint_authority, 9, vec![]).await; // Derive the PDA for the token account (same seeds as in the program) let token_account_seed: &[u8] = b"token_account"; diff --git a/sdk-tests/sdk-light-token-test/tests/test_ctoken_mint_to.rs b/sdk-tests/sdk-light-token-test/tests/test_ctoken_mint_to.rs index 064c664197..47fa876a75 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_ctoken_mint_to.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_ctoken_mint_to.rs @@ -23,16 +23,15 @@ async fn test_ctoken_mint_to_invoke() { let payer = rpc.get_payer().insecure_clone(); // Create a decompressed mint with an ATA for the payer with 0 tokens - let (mint_pda, _compression_address, ata_pubkeys) = - setup_create_compressed_mint_with_freeze_authority( - &mut rpc, - &payer, - payer.pubkey(), // mint authority is payer - None, - 9, - vec![(0, payer.pubkey())], // Start with 0 tokens - ) - .await; + let (mint_pda, _compression_address, ata_pubkeys) = setup_create_mint_with_freeze_authority( + &mut rpc, + &payer, + payer.pubkey(), // mint authority is payer + None, + 9, + vec![(0, payer.pubkey())], // Start with 0 tokens + ) + .await; let ata = ata_pubkeys[0]; let mint_amount = 500u64; @@ -53,7 +52,7 @@ async fn test_ctoken_mint_to_invoke() { let instruction = Instruction { program_id: ID, accounts: vec![ - AccountMeta::new(mint_pda, false), // cmint + AccountMeta::new(mint_pda, false), // mint AccountMeta::new(ata, false), // destination AccountMeta::new(payer.pubkey(), true), // authority (signer, writable for top-up) AccountMeta::new_readonly(system_program, false), // system_program @@ -83,21 +82,14 @@ async fn test_ctoken_mint_to_invoke() { /// Test minting to Light Token with PDA authority using CTokenMintToCpi::invoke_signed() /// /// This test: -/// 1. Creates a compressed mint with PDA authority via wrapper program (discriminator 14) -/// 2. Decompresses the mint (permissionless) -/// 3. Creates an ATA -/// 4. Mints tokens using PDA authority via invoke_signed +/// 1. Creates a compressed mint with PDA authority via wrapper program (auto-decompresses) +/// 2. Creates an ATA +/// 3. Mints tokens using PDA authority via invoke_signed #[tokio::test] async fn test_ctoken_mint_to_invoke_signed() { use light_client::indexer::Indexer; - use light_token_interface::{ - instructions::mint_action::CompressedMintWithContext, state::CompressedMint, - }; use light_token_sdk::token::CreateAssociatedTokenAccount; - use native_ctoken_examples::{ - CreateCmintData, DecompressCmintData, InstructionType as WrapperInstructionType, - MINT_SIGNER_SEED, - }; + use native_ctoken_examples::{CreateCmintData, MINT_SIGNER_SEED}; let config = ProgramTestConfig::new_v2(true, Some(vec![("native_ctoken_examples", ID)])); let mut rpc = LightProgramTest::new(config).await.unwrap(); @@ -137,8 +129,10 @@ async fn test_ctoken_mint_to_invoke_signed() { let compressed_token_program_id = Pubkey::new_from_array(light_token_interface::LIGHT_TOKEN_PROGRAM_ID); let default_pubkeys = light_token_sdk::utils::TokenDefaultAccounts::default(); + let compressible_config = light_token_sdk::token::config_pda(); + let rent_sponsor = light_token_sdk::token::rent_sponsor_pda(); - let create_cmint_data = CreateCmintData { + let create_mint_data = CreateCmintData { decimals, address_merkle_tree_root_index: rpc_result.addresses[0].root_index, mint_authority: pda_mint_authority, @@ -148,24 +142,45 @@ async fn test_ctoken_mint_to_invoke_signed() { bump: mint_bump, freeze_authority: None, extensions: None, + rent_payment: 16, + write_top_up: 766, }; // Discriminator 14 = CreateCmintWithPdaAuthority let wrapper_instruction_data = - [vec![14u8], create_cmint_data.try_to_vec().unwrap()].concat(); - + [vec![14u8], create_mint_data.try_to_vec().unwrap()].concat(); + + // Account order matches process_create_mint_with_pda_authority (MintActionMetaConfig): + // [0]: compressed_token_program + // [1]: light_system_program + // [2]: mint_signer (PDA) + // [3]: authority (PDA) + // [4]: compressible_config + // [5]: mint + // [6]: rent_sponsor + // [7]: fee_payer (signer) + // [8]: cpi_authority_pda + // [9]: registered_program_pda + // [10]: account_compression_authority + // [11]: account_compression_program + // [12]: system_program + // [13]: output_queue + // [14]: address_tree let wrapper_accounts = vec![ - AccountMeta::new_readonly(compressed_token_program_id, false), - AccountMeta::new_readonly(default_pubkeys.light_system_program, false), - AccountMeta::new_readonly(mint_signer_pda, false), - AccountMeta::new(pda_mint_authority, false), - AccountMeta::new(payer.pubkey(), true), - AccountMeta::new_readonly(default_pubkeys.cpi_authority_pda, false), - AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false), - AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false), - AccountMeta::new_readonly(default_pubkeys.account_compression_program, false), - AccountMeta::new_readonly(default_pubkeys.system_program, false), - AccountMeta::new(output_queue, false), - AccountMeta::new(address_tree.tree, false), + AccountMeta::new_readonly(compressed_token_program_id, false), // [0] + AccountMeta::new_readonly(default_pubkeys.light_system_program, false), // [1] + AccountMeta::new_readonly(mint_signer_pda, false), // [2] mint_signer PDA + AccountMeta::new_readonly(pda_mint_authority, false), // [3] authority PDA + AccountMeta::new_readonly(compressible_config, false), // [4] compressible_config + AccountMeta::new(mint_pda, false), // [5] mint + AccountMeta::new(rent_sponsor, false), // [6] rent_sponsor + AccountMeta::new(payer.pubkey(), true), // [7] fee_payer (signer) + AccountMeta::new_readonly(default_pubkeys.cpi_authority_pda, false), // [8] + AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false), // [9] + AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false), // [10] + AccountMeta::new_readonly(default_pubkeys.account_compression_program, false), // [11] + AccountMeta::new_readonly(default_pubkeys.system_program, false), // [12] + AccountMeta::new(output_queue, false), // [13] + AccountMeta::new(address_tree.tree, false), // [14] ]; let create_mint_ix = Instruction { @@ -179,108 +194,7 @@ async fn test_ctoken_mint_to_invoke_signed() { .unwrap(); } - // Step 2: Decompress the mint via wrapper program (PDA authority requires CPI) - { - let compressed_mint_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value - .expect("Compressed mint should exist"); - - let compressed_mint = CompressedMint::deserialize( - &mut compressed_mint_account - .data - .as_ref() - .unwrap() - .data - .as_slice(), - ) - .unwrap(); - - let rpc_result = rpc - .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) - .await - .unwrap() - .value; - - let compressed_mint_with_context = CompressedMintWithContext { - address: compression_address, - leaf_index: compressed_mint_account.leaf_index, - prove_by_index: true, - root_index: rpc_result.accounts[0] - .root_index - .root_index() - .unwrap_or_default(), - mint: Some(compressed_mint.try_into().unwrap()), - }; - - let default_pubkeys = light_token_sdk::utils::TokenDefaultAccounts::default(); - let compressible_config = light_token_sdk::token::config_pda(); - let rent_sponsor = light_token_sdk::token::rent_sponsor_pda(); - - let decompress_data = DecompressCmintData { - compressed_mint_with_context, - proof: rpc_result.proof, - rent_payment: 16, - write_top_up: 766, - }; - - // Discriminator 33 = DecompressCmintInvokeSigned - let wrapper_instruction_data = [ - vec![WrapperInstructionType::DecompressCmintInvokeSigned as u8], - decompress_data.try_to_vec().unwrap(), - ] - .concat(); - - // Account order matches process_decompress_mint_invoke_signed: - // 0: authority (PDA, readonly - program signs) - // 1: payer (signer, writable) - // 2: cmint (writable) - // 3: compressible_config (readonly) - // 4: rent_sponsor (writable) - // 5: state_tree (writable) - // 6: input_queue (writable) - // 7: output_queue (writable) - // 8: light_system_program (readonly) - // 9: cpi_authority_pda (readonly) - // 10: registered_program_pda (readonly) - // 11: account_compression_authority (readonly) - // 12: account_compression_program (readonly) - // 13: system_program (readonly) - // 14: light_token_program (readonly) - required for CPI - let light_token_program_id = - Pubkey::new_from_array(light_token_interface::LIGHT_TOKEN_PROGRAM_ID); - let wrapper_accounts = vec![ - AccountMeta::new_readonly(pda_mint_authority, false), - AccountMeta::new(payer.pubkey(), true), - AccountMeta::new(mint_pda, false), - AccountMeta::new_readonly(compressible_config, false), - AccountMeta::new(rent_sponsor, false), - AccountMeta::new(compressed_mint_account.tree_info.tree, false), - AccountMeta::new(compressed_mint_account.tree_info.queue, false), - AccountMeta::new(output_queue, false), - AccountMeta::new_readonly(default_pubkeys.light_system_program, false), - AccountMeta::new_readonly(default_pubkeys.cpi_authority_pda, false), - AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false), - AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false), - AccountMeta::new_readonly(default_pubkeys.account_compression_program, false), - AccountMeta::new_readonly(default_pubkeys.system_program, false), - AccountMeta::new_readonly(light_token_program_id, false), - ]; - - let decompress_ix = Instruction { - program_id: ID, - accounts: wrapper_accounts, - data: wrapper_instruction_data, - }; - - rpc.create_and_send_transaction(&[decompress_ix], &payer.pubkey(), &[&payer]) - .await - .unwrap(); - } - - // Step 3: Create ATA for payer + // Step 2: Create ATA for payer (CreateMint now auto-decompresses) let ata = { let (ata_address, _) = light_token_sdk::token::derive_token_ata(&payer.pubkey(), &mint_pda); let create_ata = @@ -300,7 +214,7 @@ async fn test_ctoken_mint_to_invoke_signed() { let ata_account_before = rpc.get_account(ata).await.unwrap().unwrap(); let ctoken_before = Token::deserialize(&mut &ata_account_before.data[..]).unwrap(); - // Step 4: Mint tokens using PDA authority via invoke_signed + // Step 3: Mint tokens using PDA authority via invoke_signed let mut instruction_data = vec![InstructionType::CTokenMintToInvokeSigned as u8]; let mint_data = MintToData { amount: mint_amount, @@ -312,7 +226,7 @@ async fn test_ctoken_mint_to_invoke_signed() { let instruction = Instruction { program_id: ID, accounts: vec![ - AccountMeta::new(mint_pda, false), // cmint + AccountMeta::new(mint_pda, false), // mint AccountMeta::new(ata, false), // destination AccountMeta::new(pda_mint_authority, false), // PDA authority (program signs, writable for top-up) AccountMeta::new_readonly(system_program, false), // system_program diff --git a/sdk-tests/sdk-light-token-test/tests/test_decompress_cmint.rs b/sdk-tests/sdk-light-token-test/tests/test_decompress_cmint.rs deleted file mode 100644 index a6bd5ef4ea..0000000000 --- a/sdk-tests/sdk-light-token-test/tests/test_decompress_cmint.rs +++ /dev/null @@ -1,728 +0,0 @@ -// Tests for DecompressMint SDK instruction - -mod shared; - -use borsh::BorshDeserialize; -use light_client::{indexer::Indexer, rpc::Rpc}; -use light_compressible::compression_info::CompressionInfo; -use light_program_test::{LightProgramTest, ProgramTestConfig}; -use light_token_interface::{ - instructions::mint_action::CompressedMintWithContext, state::CompressedMint, -}; -use light_token_sdk::token::{find_mint_address, DecompressMint}; -use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; - -/// Test decompressing a compressed mint to CMint account -#[tokio::test] -async fn test_decompress_mint() { - let config = ProgramTestConfig::new_v2(true, None); - let mut rpc = LightProgramTest::new(config).await.unwrap(); - let payer = rpc.get_payer().insecure_clone(); - - let mint_authority = payer.pubkey(); - let decimals = 9u8; - - // Create a compressed mint (returns mint_seed keypair) - let (mint_pda, compression_address, _, _mint_seed) = - shared::setup_create_compressed_mint(&mut rpc, &payer, mint_authority, decimals, vec![]) - .await; - - // Verify CMint account does NOT exist on-chain yet - let cmint_account_before = rpc.get_account(mint_pda).await.unwrap(); - assert!( - cmint_account_before.is_none(), - "CMint should not exist before decompression" - ); - - // Verify compressed mint exists - let compressed_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value - .expect("Compressed mint should exist"); - - // Get validity proof for decompression - let rpc_result = rpc - .get_validity_proof(vec![compressed_account.hash], vec![], None) - .await - .unwrap() - .value; - - // Deserialize the compressed mint to build context - let compressed_mint = - CompressedMint::deserialize(&mut compressed_account.data.as_ref().unwrap().data.as_slice()) - .unwrap(); - - let compressed_mint_with_context = CompressedMintWithContext { - address: compression_address, - leaf_index: compressed_account.leaf_index, - prove_by_index: true, - root_index: rpc_result.accounts[0] - .root_index - .root_index() - .unwrap_or_default(), - mint: Some(compressed_mint.clone().try_into().unwrap()), - }; - - let output_queue = rpc.get_random_state_tree_info().unwrap().queue; - - // Build and execute DecompressMint instruction - let decompress_ix = DecompressMint { - payer: payer.pubkey(), - authority: mint_authority, - state_tree: compressed_account.tree_info.tree, - input_queue: compressed_account.tree_info.queue, - output_queue, - compressed_mint_with_context, - proof: rpc_result.proof, - rent_payment: 16, - write_top_up: 766, - } - .instruction() - .unwrap(); - - rpc.create_and_send_transaction(&[decompress_ix], &payer.pubkey(), &[&payer]) - .await - .unwrap(); - - // Verify CMint account now exists on-chain - let cmint_account_after = rpc.get_account(mint_pda).await.unwrap(); - assert!( - cmint_account_after.is_some(), - "CMint should exist after decompression" - ); - - // Verify CMint state with single assert_eq - let cmint_account = cmint_account_after.unwrap(); - let cmint = CompressedMint::deserialize(&mut &cmint_account.data[..]).unwrap(); - - // Verify compression info is set (non-default) when decompressed - assert_ne!( - cmint.compression, - CompressionInfo::default(), - "CMint compression info should be set when decompressed" - ); - - // Build expected CMint from original compressed mint, updating fields changed by decompression - let mut expected_cmint = compressed_mint.clone(); - expected_cmint.metadata.cmint_decompressed = true; - expected_cmint.compression = cmint.compression; - - assert_eq!(cmint, expected_cmint, "CMint should match expected state"); -} - -/// Test decompressing a compressed mint with freeze_authority -#[tokio::test] -async fn test_decompress_mint_with_freeze_authority() { - let config = ProgramTestConfig::new_v2(true, None); - let mut rpc = LightProgramTest::new(config).await.unwrap(); - let payer = rpc.get_payer().insecure_clone(); - - let mint_authority = payer.pubkey(); - let freeze_authority = Keypair::new(); - let decimals = 6u8; - - // Create a compressed mint with freeze_authority - let (mint_pda, compression_address, _mint_seed) = - setup_create_compressed_mint_with_freeze_authority_only( - &mut rpc, - &payer, - mint_authority, - Some(freeze_authority.pubkey()), - decimals, - ) - .await; - - // Verify CMint account does NOT exist on-chain yet - let cmint_account_before = rpc.get_account(mint_pda).await.unwrap(); - assert!( - cmint_account_before.is_none(), - "CMint should not exist before decompression" - ); - - // Get compressed mint account - let compressed_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value - .expect("Compressed mint should exist"); - - // Get validity proof for decompression - let rpc_result = rpc - .get_validity_proof(vec![compressed_account.hash], vec![], None) - .await - .unwrap() - .value; - - // Deserialize the compressed mint - let compressed_mint = - CompressedMint::deserialize(&mut compressed_account.data.as_ref().unwrap().data.as_slice()) - .unwrap(); - - let compressed_mint_with_context = CompressedMintWithContext { - address: compression_address, - leaf_index: compressed_account.leaf_index, - prove_by_index: true, - root_index: rpc_result.accounts[0] - .root_index - .root_index() - .unwrap_or_default(), - mint: Some(compressed_mint.clone().try_into().unwrap()), - }; - - let output_queue = rpc.get_random_state_tree_info().unwrap().queue; - - // Build and execute DecompressMint instruction - let decompress_ix = DecompressMint { - payer: payer.pubkey(), - authority: mint_authority, - state_tree: compressed_account.tree_info.tree, - input_queue: compressed_account.tree_info.queue, - output_queue, - compressed_mint_with_context, - proof: rpc_result.proof, - rent_payment: 16, - write_top_up: 766, - } - .instruction() - .unwrap(); - - rpc.create_and_send_transaction(&[decompress_ix], &payer.pubkey(), &[&payer]) - .await - .unwrap(); - - // Verify CMint state with single assert_eq - let cmint_account = rpc - .get_account(mint_pda) - .await - .unwrap() - .expect("CMint should exist after decompression"); - let cmint = CompressedMint::deserialize(&mut &cmint_account.data[..]).unwrap(); - - // Verify compression info is set (non-default) when decompressed - assert_ne!( - cmint.compression, - CompressionInfo::default(), - "CMint compression info should be set when decompressed" - ); - - // Build expected CMint from original compressed mint, updating fields changed by decompression - let mut expected_cmint = compressed_mint.clone(); - expected_cmint.metadata.cmint_decompressed = true; - expected_cmint.compression = cmint.compression; - - assert_eq!(cmint, expected_cmint, "CMint should match expected state"); -} - -/// Helper function: Creates a compressed mint with optional freeze_authority -/// but does NOT decompress it (unlike setup_create_compressed_mint_with_freeze_authority) -/// Returns (mint_pda, compression_address, mint_seed_keypair) -async fn setup_create_compressed_mint_with_freeze_authority_only( - rpc: &mut (impl Rpc + Indexer), - payer: &Keypair, - mint_authority: Pubkey, - freeze_authority: Option, - decimals: u8, -) -> (Pubkey, [u8; 32], Keypair) { - use light_token_sdk::token::{CreateMint, CreateMintParams}; - - let mint_seed = Keypair::new(); - let address_tree = rpc.get_address_tree_v2(); - let output_queue = rpc.get_random_state_tree_info().unwrap().queue; - - // Derive compression address using SDK helpers - let compression_address = light_token_sdk::token::derive_mint_compressed_address( - &mint_seed.pubkey(), - &address_tree.tree, - ); - - let (mint, bump) = find_mint_address(&mint_seed.pubkey()); - - // Get validity proof for the address - let rpc_result = rpc - .get_validity_proof( - vec![], - vec![light_client::indexer::AddressWithTree { - address: compression_address, - tree: address_tree.tree, - }], - None, - ) - .await - .unwrap() - .value; - - // Build params for the SDK - let params = CreateMintParams { - decimals, - address_merkle_tree_root_index: rpc_result.addresses[0].root_index, - mint_authority, - proof: rpc_result.proof.0.unwrap(), - compression_address, - mint, - bump, - freeze_authority, - extensions: None, - }; - - // Create instruction directly using SDK - let create_cmint_builder = CreateMint::new( - params, - mint_seed.pubkey(), - payer.pubkey(), - address_tree.tree, - output_queue, - ); - let instruction = create_cmint_builder.instruction().unwrap(); - - // Send transaction - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_seed]) - .await - .unwrap(); - - // Verify the compressed mint was created - let compressed_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value; - - assert!( - compressed_account.is_some(), - "Compressed mint should exist after setup" - ); - - (mint, compression_address, mint_seed) -} - -/// Test decompressing a compressed mint with TokenMetadata extension -#[tokio::test] -async fn test_decompress_mint_with_token_metadata() { - use light_token_interface::instructions::extensions::{ - ExtensionInstructionData, TokenMetadataInstructionData, - }; - - let config = ProgramTestConfig::new_v2(true, None); - let mut rpc = LightProgramTest::new(config).await.unwrap(); - let payer = rpc.get_payer().insecure_clone(); - - let mint_authority = payer.pubkey(); - let update_authority = Keypair::new(); - let decimals = 9u8; - - // Create TokenMetadata extension - let token_metadata = TokenMetadataInstructionData { - update_authority: Some(update_authority.pubkey().to_bytes().into()), - name: b"Test Token".to_vec(), - symbol: b"TEST".to_vec(), - uri: b"https://example.com/token.json".to_vec(), - additional_metadata: None, - }; - let extensions = vec![ExtensionInstructionData::TokenMetadata(token_metadata)]; - - // Create a compressed mint with TokenMetadata extension - let (mint_pda, compression_address, _mint_seed) = setup_create_compressed_mint_with_extensions( - &mut rpc, - &payer, - mint_authority, - None, - decimals, - extensions, - ) - .await; - - // Verify CMint account does NOT exist on-chain yet - let cmint_account_before = rpc.get_account(mint_pda).await.unwrap(); - assert!( - cmint_account_before.is_none(), - "CMint should not exist before decompression" - ); - - // Get compressed mint account - let compressed_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value - .expect("Compressed mint should exist"); - - // Get validity proof for decompression - let rpc_result = rpc - .get_validity_proof(vec![compressed_account.hash], vec![], None) - .await - .unwrap() - .value; - - // Deserialize the compressed mint - let compressed_mint = - CompressedMint::deserialize(&mut compressed_account.data.as_ref().unwrap().data.as_slice()) - .unwrap(); - - let compressed_mint_with_context = CompressedMintWithContext { - address: compression_address, - leaf_index: compressed_account.leaf_index, - prove_by_index: true, - root_index: rpc_result.accounts[0] - .root_index - .root_index() - .unwrap_or_default(), - mint: Some(compressed_mint.clone().try_into().unwrap()), - }; - - let output_queue = rpc.get_random_state_tree_info().unwrap().queue; - - // Build and execute DecompressMint instruction - let decompress_ix = DecompressMint { - payer: payer.pubkey(), - authority: mint_authority, - state_tree: compressed_account.tree_info.tree, - input_queue: compressed_account.tree_info.queue, - output_queue, - compressed_mint_with_context, - proof: rpc_result.proof, - rent_payment: 16, - write_top_up: 766, - } - .instruction() - .unwrap(); - - rpc.create_and_send_transaction(&[decompress_ix], &payer.pubkey(), &[&payer]) - .await - .unwrap(); - - // Verify CMint state with single assert_eq - let cmint_account = rpc - .get_account(mint_pda) - .await - .unwrap() - .expect("CMint should exist after decompression"); - let cmint = CompressedMint::deserialize(&mut &cmint_account.data[..]).unwrap(); - - // Verify compression info is set (non-default) when decompressed - assert_ne!( - cmint.compression, - CompressionInfo::default(), - "CMint compression info should be set when decompressed" - ); - - // Verify TokenMetadata extension is preserved - assert!( - cmint.extensions.is_some(), - "CMint should have extensions with TokenMetadata" - ); - - // Build expected CMint from original compressed mint, updating fields changed by decompression - let mut expected_cmint = compressed_mint.clone(); - expected_cmint.metadata.cmint_decompressed = true; - expected_cmint.compression = cmint.compression; - // Extensions should preserve original TokenMetadata - - assert_eq!(cmint, expected_cmint, "CMint should match expected state"); -} - -/// Helper function: Creates a compressed mint with extensions -/// but does NOT decompress it -/// Returns (mint_pda, compression_address, mint_seed_keypair) -async fn setup_create_compressed_mint_with_extensions( - rpc: &mut (impl Rpc + Indexer), - payer: &Keypair, - mint_authority: Pubkey, - freeze_authority: Option, - decimals: u8, - extensions: Vec, -) -> (Pubkey, [u8; 32], Keypair) { - use light_token_sdk::token::{CreateMint, CreateMintParams}; - - let mint_seed = Keypair::new(); - let address_tree = rpc.get_address_tree_v2(); - let output_queue = rpc.get_random_state_tree_info().unwrap().queue; - - // Derive compression address using SDK helpers - let compression_address = light_token_sdk::token::derive_mint_compressed_address( - &mint_seed.pubkey(), - &address_tree.tree, - ); - - let (mint, bump) = find_mint_address(&mint_seed.pubkey()); - - // Get validity proof for the address - let rpc_result = rpc - .get_validity_proof( - vec![], - vec![light_client::indexer::AddressWithTree { - address: compression_address, - tree: address_tree.tree, - }], - None, - ) - .await - .unwrap() - .value; - - // Build params for the SDK - let params = CreateMintParams { - decimals, - address_merkle_tree_root_index: rpc_result.addresses[0].root_index, - mint_authority, - proof: rpc_result.proof.0.unwrap(), - compression_address, - mint, - bump, - freeze_authority, - extensions: Some(extensions), - }; - - // Create instruction directly using SDK - let create_cmint_builder = CreateMint::new( - params, - mint_seed.pubkey(), - payer.pubkey(), - address_tree.tree, - output_queue, - ); - let instruction = create_cmint_builder.instruction().unwrap(); - - // Send transaction - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer, &mint_seed]) - .await - .unwrap(); - - // Verify the compressed mint was created - let compressed_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value; - - assert!( - compressed_account.is_some(), - "Compressed mint should exist after setup" - ); - - (mint, compression_address, mint_seed) -} - -/// Test decompressing a compressed mint via CPI with PDA authority using invoke_signed -#[tokio::test] -async fn test_decompress_mint_cpi_invoke_signed() { - use borsh::BorshSerialize; - use native_ctoken_examples::{ - CreateCmintData, DecompressCmintData, InstructionType, ID, MINT_AUTHORITY_SEED, - MINT_SIGNER_SEED, - }; - use solana_sdk::instruction::{AccountMeta, Instruction}; - - let config = ProgramTestConfig::new_v2(true, Some(vec![("native_ctoken_examples", ID)])); - let mut rpc = LightProgramTest::new(config).await.unwrap(); - let payer = rpc.get_payer().insecure_clone(); - - // Derive the PDAs from our wrapper program - let (mint_signer_pda, _) = Pubkey::find_program_address(&[MINT_SIGNER_SEED], &ID); - let (pda_mint_authority, _) = Pubkey::find_program_address(&[MINT_AUTHORITY_SEED], &ID); - - let decimals = 9u8; - let address_tree = rpc.get_address_tree_v2(); - let output_queue = rpc.get_random_state_tree_info().unwrap().queue; - - // Derive compression address using the PDA mint_signer - let compression_address = light_token_sdk::token::derive_mint_compressed_address( - &mint_signer_pda, - &address_tree.tree, - ); - - let (mint_pda, mint_bump) = find_mint_address(&mint_signer_pda); - - // Step 1: Create compressed mint with PDA authority using wrapper program (discriminator 14) - { - let rpc_result = rpc - .get_validity_proof( - vec![], - vec![light_client::indexer::AddressWithTree { - address: compression_address, - tree: address_tree.tree, - }], - None, - ) - .await - .unwrap() - .value; - - let compressed_token_program_id = - Pubkey::new_from_array(light_token_interface::LIGHT_TOKEN_PROGRAM_ID); - let default_pubkeys = light_token_sdk::utils::TokenDefaultAccounts::default(); - - let create_cmint_data = CreateCmintData { - decimals, - address_merkle_tree_root_index: rpc_result.addresses[0].root_index, - mint_authority: pda_mint_authority, - proof: rpc_result.proof.0.unwrap(), - compression_address, - mint: mint_pda, - bump: mint_bump, - freeze_authority: None, - extensions: None, - }; - // Discriminator 14 = CreateCmintWithPdaAuthority - let wrapper_instruction_data = - [vec![14u8], create_cmint_data.try_to_vec().unwrap()].concat(); - - let wrapper_accounts = vec![ - AccountMeta::new_readonly(compressed_token_program_id, false), - AccountMeta::new_readonly(default_pubkeys.light_system_program, false), - AccountMeta::new_readonly(mint_signer_pda, false), - AccountMeta::new(pda_mint_authority, false), - AccountMeta::new(payer.pubkey(), true), - AccountMeta::new_readonly(default_pubkeys.cpi_authority_pda, false), - AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false), - AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false), - AccountMeta::new_readonly(default_pubkeys.account_compression_program, false), - AccountMeta::new_readonly(default_pubkeys.system_program, false), - AccountMeta::new(output_queue, false), - AccountMeta::new(address_tree.tree, false), - ]; - - let create_mint_ix = Instruction { - program_id: ID, - accounts: wrapper_accounts, - data: wrapper_instruction_data, - }; - - rpc.create_and_send_transaction(&[create_mint_ix], &payer.pubkey(), &[&payer]) - .await - .unwrap(); - } - - // Verify CMint account does NOT exist on-chain yet - let cmint_account_before = rpc.get_account(mint_pda).await.unwrap(); - assert!( - cmint_account_before.is_none(), - "CMint should not exist before decompression" - ); - - // Step 2: Decompress the mint via wrapper program (PDA authority requires CPI) - let compressed_mint = { - let compressed_mint_account = rpc - .get_compressed_account(compression_address, None) - .await - .unwrap() - .value - .expect("Compressed mint should exist"); - - let compressed_mint = CompressedMint::deserialize( - &mut compressed_mint_account - .data - .as_ref() - .unwrap() - .data - .as_slice(), - ) - .unwrap(); - - let rpc_result = rpc - .get_validity_proof(vec![compressed_mint_account.hash], vec![], None) - .await - .unwrap() - .value; - - let compressed_mint_with_context = CompressedMintWithContext { - address: compression_address, - leaf_index: compressed_mint_account.leaf_index, - prove_by_index: true, - root_index: rpc_result.accounts[0] - .root_index - .root_index() - .unwrap_or_default(), - mint: Some(compressed_mint.clone().try_into().unwrap()), - }; - - let default_pubkeys = light_token_sdk::utils::TokenDefaultAccounts::default(); - let compressible_config = light_token_sdk::token::config_pda(); - let rent_sponsor = light_token_sdk::token::rent_sponsor_pda(); - - let decompress_data = DecompressCmintData { - compressed_mint_with_context, - proof: rpc_result.proof, - rent_payment: 16, - write_top_up: 766, - }; - - // Discriminator 33 = DecompressCmintInvokeSigned - let wrapper_instruction_data = [ - vec![InstructionType::DecompressCmintInvokeSigned as u8], - decompress_data.try_to_vec().unwrap(), - ] - .concat(); - - // Account order matches process_decompress_mint_invoke_signed: - // 0: authority (PDA, readonly - program signs) - // 1: payer (signer, writable) - // 2: cmint (writable) - // 3: compressible_config (readonly) - // 4: rent_sponsor (writable) - // 5: state_tree (writable) - // 6: input_queue (writable) - // 7: output_queue (writable) - // 8: light_system_program (readonly) - // 9: cpi_authority_pda (readonly) - // 10: registered_program_pda (readonly) - // 11: account_compression_authority (readonly) - // 12: account_compression_program (readonly) - // 13: system_program (readonly) - // 14: light_token_program (readonly) - required for CPI - let light_token_program_id = - Pubkey::new_from_array(light_token_interface::LIGHT_TOKEN_PROGRAM_ID); - let wrapper_accounts = vec![ - AccountMeta::new_readonly(pda_mint_authority, false), - AccountMeta::new(payer.pubkey(), true), - AccountMeta::new(mint_pda, false), - AccountMeta::new_readonly(compressible_config, false), - AccountMeta::new(rent_sponsor, false), - AccountMeta::new(compressed_mint_account.tree_info.tree, false), - AccountMeta::new(compressed_mint_account.tree_info.queue, false), - AccountMeta::new(output_queue, false), - AccountMeta::new_readonly(default_pubkeys.light_system_program, false), - AccountMeta::new_readonly(default_pubkeys.cpi_authority_pda, false), - AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false), - AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false), - AccountMeta::new_readonly(default_pubkeys.account_compression_program, false), - AccountMeta::new_readonly(default_pubkeys.system_program, false), - AccountMeta::new_readonly(light_token_program_id, false), - ]; - - let decompress_ix = Instruction { - program_id: ID, - accounts: wrapper_accounts, - data: wrapper_instruction_data, - }; - - rpc.create_and_send_transaction(&[decompress_ix], &payer.pubkey(), &[&payer]) - .await - .unwrap(); - - compressed_mint - }; - - // Verify CMint state with single assert_eq - let cmint_account = rpc - .get_account(mint_pda) - .await - .unwrap() - .expect("CMint should exist after decompression"); - let cmint = CompressedMint::deserialize(&mut &cmint_account.data[..]).unwrap(); - - // Verify compression info is set (non-default) when decompressed - assert_ne!( - cmint.compression, - CompressionInfo::default(), - "CMint compression info should be set when decompressed" - ); - - // Build expected CMint from original compressed mint, updating fields changed by decompression - let mut expected_cmint = compressed_mint.clone(); - expected_cmint.metadata.cmint_decompressed = true; - expected_cmint.compression = cmint.compression; - - assert_eq!(cmint, expected_cmint, "CMint should match expected state"); -} diff --git a/sdk-tests/sdk-light-token-test/tests/test_decompress_mint.rs b/sdk-tests/sdk-light-token-test/tests/test_decompress_mint.rs new file mode 100644 index 0000000000..119bfdf667 --- /dev/null +++ b/sdk-tests/sdk-light-token-test/tests/test_decompress_mint.rs @@ -0,0 +1,216 @@ +// Tests for DecompressMint SDK instruction +// Flow: Create compressed-only mint -> DecompressMint + +mod shared; + +use borsh::BorshDeserialize; +use light_client::{indexer::Indexer, rpc::Rpc}; +use light_compressible::compression_info::CompressionInfo; +use light_program_test::{LightProgramTest, ProgramTestConfig}; +use light_token_client::instructions::mint_action::{ + create_mint_action_instruction, MintActionParams, MintActionType, +}; +use light_token_interface::state::Mint; +use light_token_sdk::token::derive_mint_compressed_address; +use solana_sdk::signer::Signer; + +/// Test decompressing a compressed-only mint +/// +/// Flow: +/// 1. Create a compressed-only mint (using setup_create_compressed_only_mint) +/// 2. Decompress it using DecompressMint +#[tokio::test] +async fn test_decompress_compressed_only_mint() { + let config = ProgramTestConfig::new_v2(true, None); + let mut rpc = LightProgramTest::new(config).await.unwrap(); + let payer = rpc.get_payer().insecure_clone(); + + let mint_authority = payer.pubkey(); + let decimals = 9u8; + + // Step 1: Create a compressed-only mint (no decompression) + let (mint_pda, compression_address, mint_seed) = + shared::setup_create_compressed_only_mint(&mut rpc, &payer, mint_authority, decimals).await; + + // Verify mint does NOT exist on-chain (compressed-only) + let mint_account = rpc.get_account(mint_pda).await.unwrap(); + assert!( + mint_account.is_none(), + "Mint should NOT exist after creating compressed-only mint" + ); + + // Verify compressed mint exists + let compressed_account = rpc + .get_compressed_account(compression_address, None) + .await + .unwrap() + .value; + assert!( + compressed_account.is_some(), + "Compressed mint should exist after creation" + ); + + let address_tree = rpc.get_address_tree_v2(); + let compressed_mint_address = + derive_mint_compressed_address(&mint_seed.pubkey(), &address_tree.tree); + + // Step 2: Decompress the mint + let decompress_ix = create_mint_action_instruction( + &mut rpc, + MintActionParams { + compressed_mint_address, + mint_seed: mint_seed.pubkey(), + authority: mint_authority, + payer: payer.pubkey(), + actions: vec![MintActionType::DecompressMint { + rent_payment: 16, + write_top_up: 766, + }], + new_mint: None, + }, + ) + .await + .unwrap(); + + rpc.create_and_send_transaction(&[decompress_ix], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Verify mint account now exists + let mint_account_after_decompress = rpc.get_account(mint_pda).await.unwrap(); + assert!( + mint_account_after_decompress.is_some(), + "Mint should exist after DecompressMint" + ); + + // Verify mint state + let mint_data = mint_account_after_decompress.unwrap(); + let mint = Mint::deserialize(&mut &mint_data.data[..]).unwrap(); + + // Verify basic mint properties + assert_eq!(mint.base.decimals, decimals, "Decimals should match"); + assert_eq!( + mint.base.mint_authority, + Some(mint_authority.to_bytes().into()), + "Mint authority should match" + ); + + // Verify mint_decompressed flag is set + assert!( + mint.metadata.mint_decompressed, + "Mint should be marked as decompressed" + ); + + // Verify compression info is set (non-default) + assert_ne!( + mint.compression, + CompressionInfo::default(), + "Mint compression info should be set when decompressed" + ); +} + +/// Test that CreateMint automatically decompresses the mint +#[tokio::test] +async fn test_create_mint_auto_decompresses() { + let config = ProgramTestConfig::new_v2(true, None); + let mut rpc = LightProgramTest::new(config).await.unwrap(); + let payer = rpc.get_payer().insecure_clone(); + + let mint_authority = payer.pubkey(); + let decimals = 9u8; + + // Create a compressed mint (now auto-decompresses) + let (mint_pda, compression_address, _, _mint_seed) = + shared::setup_create_mint(&mut rpc, &payer, mint_authority, decimals, vec![]).await; + + // Verify Mint account exists on-chain (auto-decompressed) + let mint_account = rpc.get_account(mint_pda).await.unwrap(); + assert!( + mint_account.is_some(), + "Mint should exist after CreateMint (auto-decompress)" + ); + + // Verify Mint state + let mint_data = mint_account.unwrap(); + let mint = Mint::deserialize(&mut &mint_data.data[..]).unwrap(); + + // Verify basic mint properties + assert_eq!(mint.base.decimals, decimals, "Decimals should match"); + assert_eq!( + mint.base.mint_authority, + Some(mint_authority.to_bytes().into()), + "Mint authority should match" + ); + assert_eq!(mint.base.supply, 0, "Initial supply should be 0"); + + // Verify mint_decompressed flag is set + assert!( + mint.metadata.mint_decompressed, + "Mint should be marked as decompressed" + ); + + // Verify compression info is set (non-default) + assert_ne!( + mint.compression, + CompressionInfo::default(), + "Mint compression info should be set when decompressed" + ); + + // Verify compressed mint still exists (both forms coexist) + let compressed_account = rpc + .get_compressed_account(compression_address, None) + .await + .unwrap() + .value; + assert!( + compressed_account.is_some(), + "Compressed mint should still exist after decompression" + ); +} + +/// Test CreateMint with freeze authority auto-decompresses +#[tokio::test] +async fn test_create_mint_with_freeze_authority_auto_decompresses() { + let config = ProgramTestConfig::new_v2(true, None); + let mut rpc = LightProgramTest::new(config).await.unwrap(); + let payer = rpc.get_payer().insecure_clone(); + + let mint_authority = payer.pubkey(); + let freeze_authority = Some(payer.pubkey()); + let decimals = 6u8; + + // Create a compressed mint with freeze_authority (now auto-decompresses) + let (mint_pda, _compression_address, _atas) = shared::setup_create_mint_with_freeze_authority( + &mut rpc, + &payer, + mint_authority, + freeze_authority, + decimals, + vec![], + ) + .await; + + // Verify Mint account exists on-chain (auto-decompressed) + let mint_account = rpc.get_account(mint_pda).await.unwrap(); + assert!( + mint_account.is_some(), + "Mint should exist after CreateMint (auto-decompress)" + ); + + // Verify Mint state + let mint_data = mint_account.unwrap(); + let mint = Mint::deserialize(&mut &mint_data.data[..]).unwrap(); + + // Verify freeze authority is set + assert_eq!( + mint.base.freeze_authority, + freeze_authority.map(|p| p.to_bytes().into()), + "Freeze authority should match" + ); + + // Verify mint_decompressed flag is set + assert!( + mint.metadata.mint_decompressed, + "Mint should be marked as decompressed" + ); +} diff --git a/sdk-tests/sdk-light-token-test/tests/test_freeze_thaw.rs b/sdk-tests/sdk-light-token-test/tests/test_freeze_thaw.rs index 86ae32e091..07c2a74539 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_freeze_thaw.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_freeze_thaw.rs @@ -26,16 +26,15 @@ async fn test_freeze_invoke() { let freeze_authority = Keypair::new(); // Create a compressed mint with freeze_authority and an ATA for the payer with 1000 tokens - let (mint_pda, _compression_address, ata_pubkeys) = - setup_create_compressed_mint_with_freeze_authority( - &mut rpc, - &payer, - payer.pubkey(), - Some(freeze_authority.pubkey()), - 9, - vec![(1000, payer.pubkey())], - ) - .await; + let (mint_pda, _compression_address, ata_pubkeys) = setup_create_mint_with_freeze_authority( + &mut rpc, + &payer, + payer.pubkey(), + Some(freeze_authority.pubkey()), + 9, + vec![(1000, payer.pubkey())], + ) + .await; let ata = ata_pubkeys[0]; @@ -94,16 +93,15 @@ async fn test_freeze_invoke_signed() { let (pda_freeze_authority, _bump) = Pubkey::find_program_address(&[FREEZE_AUTHORITY_SEED], &ID); // Create a compressed mint with PDA freeze_authority and an ATA for the payer with 1000 tokens - let (mint_pda, _compression_address, ata_pubkeys) = - setup_create_compressed_mint_with_freeze_authority( - &mut rpc, - &payer, - payer.pubkey(), - Some(pda_freeze_authority), - 9, - vec![(1000, payer.pubkey())], - ) - .await; + let (mint_pda, _compression_address, ata_pubkeys) = setup_create_mint_with_freeze_authority( + &mut rpc, + &payer, + payer.pubkey(), + Some(pda_freeze_authority), + 9, + vec![(1000, payer.pubkey())], + ) + .await; let ata = ata_pubkeys[0]; @@ -149,16 +147,15 @@ async fn test_thaw_invoke() { let light_token_program = Pubkey::from(LIGHT_TOKEN_PROGRAM_ID); // Create a compressed mint with freeze_authority and an ATA for the payer with 1000 tokens - let (mint_pda, _compression_address, ata_pubkeys) = - setup_create_compressed_mint_with_freeze_authority( - &mut rpc, - &payer, - payer.pubkey(), - Some(freeze_authority.pubkey()), - 9, - vec![(1000, payer.pubkey())], - ) - .await; + let (mint_pda, _compression_address, ata_pubkeys) = setup_create_mint_with_freeze_authority( + &mut rpc, + &payer, + payer.pubkey(), + Some(freeze_authority.pubkey()), + 9, + vec![(1000, payer.pubkey())], + ) + .await; let ata = ata_pubkeys[0]; @@ -236,16 +233,15 @@ async fn test_thaw_invoke_signed() { let light_token_program = Pubkey::from(LIGHT_TOKEN_PROGRAM_ID); // Create a compressed mint with PDA freeze_authority and an ATA for the payer with 1000 tokens - let (mint_pda, _compression_address, ata_pubkeys) = - setup_create_compressed_mint_with_freeze_authority( - &mut rpc, - &payer, - payer.pubkey(), - Some(pda_freeze_authority), - 9, - vec![(1000, payer.pubkey())], - ) - .await; + let (mint_pda, _compression_address, ata_pubkeys) = setup_create_mint_with_freeze_authority( + &mut rpc, + &payer, + payer.pubkey(), + Some(pda_freeze_authority), + 9, + vec![(1000, payer.pubkey())], + ) + .await; let ata = ata_pubkeys[0]; diff --git a/sdk-tests/sdk-light-token-test/tests/test_transfer.rs b/sdk-tests/sdk-light-token-test/tests/test_transfer.rs index 0e1a8e261d..88c9eb0f3b 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_transfer.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_transfer.rs @@ -24,7 +24,7 @@ async fn test_ctoken_transfer_invoke() { let source_owner = payer.pubkey(); let dest_owner = Pubkey::new_unique(); - let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = setup_create_compressed_mint( + let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = setup_create_mint( &mut rpc, &payer, payer.pubkey(), @@ -81,7 +81,7 @@ async fn test_ctoken_transfer_invoke_signed() { let (pda_owner, _bump) = Pubkey::find_program_address(&[TOKEN_ACCOUNT_SEED], &ID); let dest_owner = payer.pubkey(); - let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = setup_create_compressed_mint( + let (_mint_pda, _compression_address, ata_pubkeys, _mint_seed) = setup_create_mint( &mut rpc, &payer, payer.pubkey(), diff --git a/sdk-tests/sdk-light-token-test/tests/test_transfer_checked.rs b/sdk-tests/sdk-light-token-test/tests/test_transfer_checked.rs index fbbfedab88..147ffb880e 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_transfer_checked.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_transfer_checked.rs @@ -276,9 +276,9 @@ async fn test_ctoken_transfer_checked_t22_mint() { assert_eq!(dest_state.amount, 500); } -/// Test transfer_checked with decompressed CMint +/// Test transfer_checked with decompressed Mint #[tokio::test] -async fn test_ctoken_transfer_checked_cmint() { +async fn test_ctoken_transfer_checked_mint() { let config = ProgramTestConfig::new_v2(true, Some(vec![("native_ctoken_examples", ID)])); let mut rpc = LightProgramTest::new(config).await.unwrap(); let payer = rpc.get_payer().insecure_clone(); @@ -288,16 +288,15 @@ async fn test_ctoken_transfer_checked_cmint() { let dest_owner = Pubkey::new_unique(); // Create compressed mint and decompress it, then create ATAs with tokens - let (mint, _compression_address, ata_pubkeys) = - setup_create_compressed_mint_with_freeze_authority( - &mut rpc, - &payer, - payer.pubkey(), - None, // no freeze authority needed for transfer - decimals, - vec![(1000, source_owner), (0, dest_owner)], - ) - .await; + let (mint, _compression_address, ata_pubkeys) = setup_create_mint_with_freeze_authority( + &mut rpc, + &payer, + payer.pubkey(), + None, // no freeze authority needed for transfer + decimals, + vec![(1000, source_owner), (0, dest_owner)], + ) + .await; let source_ata = ata_pubkeys[0]; let dest_ata = ata_pubkeys[1]; diff --git a/sdk-tests/sdk-light-token-test/tests/test_transfer_interface.rs b/sdk-tests/sdk-light-token-test/tests/test_transfer_interface.rs index 78da61c252..489c656f81 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_transfer_interface.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_transfer_interface.rs @@ -843,3 +843,468 @@ async fn test_transfer_interface_ctoken_to_ctoken_invoke_signed() { println!("TransferInterface Light Token->Light Token invoke_signed test passed"); } + +// ============================================================================= +// SPL-TO-SPL TESTS +// ============================================================================= + +/// Test TransferInterface: SPL -> SPL (invoke) +#[tokio::test] +async fn test_transfer_interface_spl_to_spl_invoke() { + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( + false, + Some(vec![("native_ctoken_examples", ID)]), + )) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + let sender = Keypair::new(); + light_test_utils::airdrop_lamports(&mut rpc, &sender.pubkey(), 1_000_000_000) + .await + .unwrap(); + + // Create SPL mint and token accounts + let mint = create_mint_helper(&mut rpc, &payer).await; + let amount = 10000u64; + let transfer_amount = 5000u64; + + // Create source SPL token account + let source_spl_keypair = Keypair::new(); + create_token_2022_account(&mut rpc, &mint, &source_spl_keypair, &sender, false) + .await + .unwrap(); + mint_spl_tokens( + &mut rpc, + &mint, + &source_spl_keypair.pubkey(), + &payer.pubkey(), + &payer, + amount, + false, + ) + .await + .unwrap(); + + // Create destination SPL token account + let recipient = Keypair::new(); + light_test_utils::airdrop_lamports(&mut rpc, &recipient.pubkey(), 1_000_000_000) + .await + .unwrap(); + let dest_spl_keypair = Keypair::new(); + create_token_2022_account(&mut rpc, &mint, &dest_spl_keypair, &recipient, false) + .await + .unwrap(); + + // Get SPL interface PDA (not actually used for SPL->SPL, but needed by wrapper) + let (spl_interface_pda, spl_interface_pda_bump) = + find_spl_interface_pda_with_index(&mint, 0, false); + let compressed_token_program_id = + Pubkey::new_from_array(light_token_interface::LIGHT_TOKEN_PROGRAM_ID); + let cpi_authority_pda = Pubkey::new_from_array(CPI_AUTHORITY_PDA); + + // Build wrapper instruction for SPL->SPL transfer + let data = TransferInterfaceData { + amount: transfer_amount, + spl_interface_pda_bump: Some(spl_interface_pda_bump), + decimals: CREATE_MINT_HELPER_DECIMALS, + }; + // Discriminator 19 = TransferInterfaceInvoke + let wrapper_instruction_data = [vec![19u8], data.try_to_vec().unwrap()].concat(); + + let wrapper_accounts = vec![ + AccountMeta::new_readonly(compressed_token_program_id, false), + AccountMeta::new(source_spl_keypair.pubkey(), false), // source (SPL) + AccountMeta::new(dest_spl_keypair.pubkey(), false), // destination (SPL) + AccountMeta::new_readonly(sender.pubkey(), true), // authority (signer) + AccountMeta::new(payer.pubkey(), true), // payer + AccountMeta::new_readonly(cpi_authority_pda, false), // compressed_token_program_authority + AccountMeta::new_readonly(solana_sdk::system_program::ID, false), // system_program + AccountMeta::new_readonly(mint, false), // mint (for SPL transfer_checked) + AccountMeta::new(spl_interface_pda, false), // spl_interface_pda (passed but not used) + AccountMeta::new_readonly(anchor_spl::token::ID, false), // spl_token_program + ]; + + let instruction = Instruction { + program_id: ID, + accounts: wrapper_accounts, + data: wrapper_instruction_data, + }; + + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer, &sender]) + .await + .unwrap(); + + // Verify balances + use spl_token_2022::pod::PodAccount; + let source_account_data = rpc + .get_account(source_spl_keypair.pubkey()) + .await + .unwrap() + .unwrap(); + let source_account = + spl_pod::bytemuck::pod_from_bytes::(&source_account_data.data).unwrap(); + assert_eq!(u64::from(source_account.amount), amount - transfer_amount); + + let dest_account_data = rpc + .get_account(dest_spl_keypair.pubkey()) + .await + .unwrap() + .unwrap(); + let dest_account = + spl_pod::bytemuck::pod_from_bytes::(&dest_account_data.data).unwrap(); + assert_eq!(u64::from(dest_account.amount), transfer_amount); + + println!("TransferInterface SPL->SPL invoke test passed"); +} + +/// Test TransferInterface: SPL -> SPL with PDA authority (invoke_signed) +#[tokio::test] +async fn test_transfer_interface_spl_to_spl_invoke_signed() { + use anchor_spl::associated_token::{ + get_associated_token_address, spl_associated_token_account, + }; + + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( + false, + Some(vec![("native_ctoken_examples", ID)]), + )) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + + // Derive PDA authority + let (authority_pda, _) = + Pubkey::find_program_address(&[TRANSFER_INTERFACE_AUTHORITY_SEED], &ID); + + let mint = create_mint_helper(&mut rpc, &payer).await; + let amount = 10000u64; + let transfer_amount = 5000u64; + + // Create SPL ATA owned by PDA + let source_spl_ata = get_associated_token_address(&authority_pda, &mint); + let create_ata_ix = spl_associated_token_account::instruction::create_associated_token_account( + &payer.pubkey(), + &authority_pda, + &mint, + &anchor_spl::token::ID, + ); + rpc.create_and_send_transaction(&[create_ata_ix], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Mint tokens to PDA's ATA + mint_spl_tokens( + &mut rpc, + &mint, + &source_spl_ata, + &payer.pubkey(), + &payer, + amount, + false, + ) + .await + .unwrap(); + + // Create destination SPL token account + let recipient = Keypair::new(); + light_test_utils::airdrop_lamports(&mut rpc, &recipient.pubkey(), 1_000_000_000) + .await + .unwrap(); + let dest_spl_keypair = Keypair::new(); + create_token_2022_account(&mut rpc, &mint, &dest_spl_keypair, &recipient, false) + .await + .unwrap(); + + let (spl_interface_pda, spl_interface_pda_bump) = + find_spl_interface_pda_with_index(&mint, 0, false); + let compressed_token_program_id = + Pubkey::new_from_array(light_token_interface::LIGHT_TOKEN_PROGRAM_ID); + let cpi_authority_pda = Pubkey::new_from_array(CPI_AUTHORITY_PDA); + + let data = TransferInterfaceData { + amount: transfer_amount, + spl_interface_pda_bump: Some(spl_interface_pda_bump), + decimals: CREATE_MINT_HELPER_DECIMALS, + }; + // Discriminator 20 = TransferInterfaceInvokeSigned + let wrapper_instruction_data = [vec![20u8], data.try_to_vec().unwrap()].concat(); + + let wrapper_accounts = vec![ + AccountMeta::new_readonly(compressed_token_program_id, false), + AccountMeta::new(source_spl_ata, false), // source (SPL owned by PDA) + AccountMeta::new(dest_spl_keypair.pubkey(), false), // destination (SPL) + AccountMeta::new_readonly(authority_pda, false), // authority (PDA, not signer) + AccountMeta::new(payer.pubkey(), true), // payer + AccountMeta::new_readonly(cpi_authority_pda, false), // compressed_token_program_authority + AccountMeta::new_readonly(solana_sdk::system_program::ID, false), // system_program + AccountMeta::new_readonly(mint, false), + AccountMeta::new(spl_interface_pda, false), + AccountMeta::new_readonly(anchor_spl::token::ID, false), + ]; + + let instruction = Instruction { + program_id: ID, + accounts: wrapper_accounts, + data: wrapper_instruction_data, + }; + + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Verify balances + use spl_token_2022::pod::PodAccount; + let source_account_data = rpc.get_account(source_spl_ata).await.unwrap().unwrap(); + let source_account = + spl_pod::bytemuck::pod_from_bytes::(&source_account_data.data).unwrap(); + assert_eq!(u64::from(source_account.amount), amount - transfer_amount); + + let dest_account_data = rpc + .get_account(dest_spl_keypair.pubkey()) + .await + .unwrap() + .unwrap(); + let dest_account = + spl_pod::bytemuck::pod_from_bytes::(&dest_account_data.data).unwrap(); + assert_eq!(u64::from(dest_account.amount), transfer_amount); + + println!("TransferInterface SPL->SPL invoke_signed test passed"); +} + +// ============================================================================= +// TOKEN-2022 TO TOKEN-2022 TESTS +// ============================================================================= + +/// Test TransferInterface: T22 -> T22 (invoke) +#[tokio::test] +async fn test_transfer_interface_t22_to_t22_invoke() { + use light_test_utils::spl::create_mint_22_helper; + + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( + false, + Some(vec![("native_ctoken_examples", ID)]), + )) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + let sender = Keypair::new(); + light_test_utils::airdrop_lamports(&mut rpc, &sender.pubkey(), 1_000_000_000) + .await + .unwrap(); + + // Create T22 mint and token accounts + let mint = create_mint_22_helper(&mut rpc, &payer).await; + let amount = 10000u64; + let transfer_amount = 5000u64; + + // Create source T22 token account + let source_t22_keypair = Keypair::new(); + create_token_2022_account(&mut rpc, &mint, &source_t22_keypair, &sender, true) + .await + .unwrap(); + mint_spl_tokens( + &mut rpc, + &mint, + &source_t22_keypair.pubkey(), + &payer.pubkey(), + &payer, + amount, + true, + ) + .await + .unwrap(); + + // Create destination T22 token account + let recipient = Keypair::new(); + light_test_utils::airdrop_lamports(&mut rpc, &recipient.pubkey(), 1_000_000_000) + .await + .unwrap(); + let dest_t22_keypair = Keypair::new(); + create_token_2022_account(&mut rpc, &mint, &dest_t22_keypair, &recipient, true) + .await + .unwrap(); + + // Get SPL interface PDA for T22 + let (spl_interface_pda, spl_interface_pda_bump) = + find_spl_interface_pda_with_index(&mint, 0, true); + let compressed_token_program_id = + Pubkey::new_from_array(light_token_interface::LIGHT_TOKEN_PROGRAM_ID); + let cpi_authority_pda = Pubkey::new_from_array(CPI_AUTHORITY_PDA); + + // Build wrapper instruction for T22->T22 transfer + let data = TransferInterfaceData { + amount: transfer_amount, + spl_interface_pda_bump: Some(spl_interface_pda_bump), + decimals: CREATE_MINT_HELPER_DECIMALS, + }; + // Discriminator 19 = TransferInterfaceInvoke + let wrapper_instruction_data = [vec![19u8], data.try_to_vec().unwrap()].concat(); + + let wrapper_accounts = vec![ + AccountMeta::new_readonly(compressed_token_program_id, false), + AccountMeta::new(source_t22_keypair.pubkey(), false), // source (T22) + AccountMeta::new(dest_t22_keypair.pubkey(), false), // destination (T22) + AccountMeta::new_readonly(sender.pubkey(), true), // authority (signer) + AccountMeta::new(payer.pubkey(), true), // payer + AccountMeta::new_readonly(cpi_authority_pda, false), // compressed_token_program_authority + AccountMeta::new_readonly(solana_sdk::system_program::ID, false), // system_program + AccountMeta::new_readonly(mint, false), // mint (for T22 transfer_checked) + AccountMeta::new(spl_interface_pda, false), // spl_interface_pda (passed but not used) + AccountMeta::new_readonly(anchor_spl::token_2022::ID, false), // T22 token program + ]; + + let instruction = Instruction { + program_id: ID, + accounts: wrapper_accounts, + data: wrapper_instruction_data, + }; + + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer, &sender]) + .await + .unwrap(); + + // Verify balances using T22 state unpacking (handles extensions) + use spl_token_2022::{extension::StateWithExtensions, state::Account as T22Account}; + + let source_account_data = rpc + .get_account(source_t22_keypair.pubkey()) + .await + .unwrap() + .unwrap(); + let source_state = + StateWithExtensions::::unpack(&source_account_data.data).unwrap(); + assert_eq!(source_state.base.amount, amount - transfer_amount); + + let dest_account_data = rpc + .get_account(dest_t22_keypair.pubkey()) + .await + .unwrap() + .unwrap(); + let dest_state = StateWithExtensions::::unpack(&dest_account_data.data).unwrap(); + assert_eq!(dest_state.base.amount, transfer_amount); + + println!("TransferInterface T22->T22 invoke test passed"); +} + +/// Test TransferInterface: T22 -> T22 with PDA authority (invoke_signed) +#[tokio::test] +async fn test_transfer_interface_t22_to_t22_invoke_signed() { + use anchor_spl::associated_token::{ + get_associated_token_address_with_program_id, spl_associated_token_account, + }; + use light_test_utils::spl::create_mint_22_helper; + + let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2( + false, + Some(vec![("native_ctoken_examples", ID)]), + )) + .await + .unwrap(); + + let payer = rpc.get_payer().insecure_clone(); + + // Derive PDA authority + let (authority_pda, _) = + Pubkey::find_program_address(&[TRANSFER_INTERFACE_AUTHORITY_SEED], &ID); + + let mint = create_mint_22_helper(&mut rpc, &payer).await; + let amount = 10000u64; + let transfer_amount = 5000u64; + + // Create T22 ATA owned by PDA + let source_t22_ata = get_associated_token_address_with_program_id( + &authority_pda, + &mint, + &anchor_spl::token_2022::ID, + ); + let create_ata_ix = spl_associated_token_account::instruction::create_associated_token_account( + &payer.pubkey(), + &authority_pda, + &mint, + &anchor_spl::token_2022::ID, + ); + rpc.create_and_send_transaction(&[create_ata_ix], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Mint tokens to PDA's ATA + mint_spl_tokens( + &mut rpc, + &mint, + &source_t22_ata, + &payer.pubkey(), + &payer, + amount, + true, + ) + .await + .unwrap(); + + // Create destination T22 token account + let recipient = Keypair::new(); + light_test_utils::airdrop_lamports(&mut rpc, &recipient.pubkey(), 1_000_000_000) + .await + .unwrap(); + let dest_t22_keypair = Keypair::new(); + create_token_2022_account(&mut rpc, &mint, &dest_t22_keypair, &recipient, true) + .await + .unwrap(); + + let (spl_interface_pda, spl_interface_pda_bump) = + find_spl_interface_pda_with_index(&mint, 0, true); + let compressed_token_program_id = + Pubkey::new_from_array(light_token_interface::LIGHT_TOKEN_PROGRAM_ID); + let cpi_authority_pda = Pubkey::new_from_array(CPI_AUTHORITY_PDA); + + let data = TransferInterfaceData { + amount: transfer_amount, + spl_interface_pda_bump: Some(spl_interface_pda_bump), + decimals: CREATE_MINT_HELPER_DECIMALS, + }; + // Discriminator 20 = TransferInterfaceInvokeSigned + let wrapper_instruction_data = [vec![20u8], data.try_to_vec().unwrap()].concat(); + + let wrapper_accounts = vec![ + AccountMeta::new_readonly(compressed_token_program_id, false), + AccountMeta::new(source_t22_ata, false), // source (T22 owned by PDA) + AccountMeta::new(dest_t22_keypair.pubkey(), false), // destination (T22) + AccountMeta::new_readonly(authority_pda, false), // authority (PDA, not signer) + AccountMeta::new(payer.pubkey(), true), // payer + AccountMeta::new_readonly(cpi_authority_pda, false), // compressed_token_program_authority + AccountMeta::new_readonly(solana_sdk::system_program::ID, false), // system_program + AccountMeta::new_readonly(mint, false), + AccountMeta::new(spl_interface_pda, false), + AccountMeta::new_readonly(anchor_spl::token_2022::ID, false), // T22 token program + ]; + + let instruction = Instruction { + program_id: ID, + accounts: wrapper_accounts, + data: wrapper_instruction_data, + }; + + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer]) + .await + .unwrap(); + + // Verify balances using T22 state unpacking (handles extensions) + use spl_token_2022::{extension::StateWithExtensions, state::Account as T22Account}; + + let source_account_data = rpc.get_account(source_t22_ata).await.unwrap().unwrap(); + let source_state = + StateWithExtensions::::unpack(&source_account_data.data).unwrap(); + assert_eq!(source_state.base.amount, amount - transfer_amount); + + let dest_account_data = rpc + .get_account(dest_t22_keypair.pubkey()) + .await + .unwrap() + .unwrap(); + let dest_state = StateWithExtensions::::unpack(&dest_account_data.data).unwrap(); + assert_eq!(dest_state.base.amount, transfer_amount); + + println!("TransferInterface T22->T22 invoke_signed test passed"); +} diff --git a/sdk-tests/sdk-token-test/src/mint_compressed_tokens_cpi_write.rs b/sdk-tests/sdk-token-test/src/mint_compressed_tokens_cpi_write.rs index 1872e830ab..e4d5eec534 100644 --- a/sdk-tests/sdk-token-test/src/mint_compressed_tokens_cpi_write.rs +++ b/sdk-tests/sdk-token-test/src/mint_compressed_tokens_cpi_write.rs @@ -1,7 +1,6 @@ use anchor_lang::{prelude::*, solana_program::program::invoke}; use light_token_interface::instructions::mint_action::{ - CompressedMintWithContext, MintActionCompressedInstructionData, MintToCompressedAction, - Recipient, + MintActionCompressedInstructionData, MintToCompressedAction, MintWithContext, Recipient, }; use light_token_sdk::compressed_token::{ ctoken_instruction::CTokenInstruction, mint_action::MintActionCpiWriteAccounts, @@ -12,7 +11,7 @@ use crate::Generic; #[derive(AnchorSerialize, AnchorDeserialize, Clone)] pub struct MintCompressedTokensCpiWriteParams { - pub compressed_mint_with_context: CompressedMintWithContext, + pub compressed_mint_with_context: MintWithContext, pub recipients: Vec, pub cpi_context: light_token_interface::instructions::mint_action::CpiContext, pub cpi_context_pubkey: Pubkey, diff --git a/sdk-tests/sdk-token-test/src/pda_ctoken/processor.rs b/sdk-tests/sdk-token-test/src/pda_ctoken/processor.rs index f22dd0c2ae..4181a3240d 100644 --- a/sdk-tests/sdk-token-test/src/pda_ctoken/processor.rs +++ b/sdk-tests/sdk-token-test/src/pda_ctoken/processor.rs @@ -1,5 +1,5 @@ use anchor_lang::prelude::*; -use light_token_interface::instructions::mint_action::{CompressedMintWithContext, Recipient}; +use light_token_interface::instructions::mint_action::{MintWithContext, Recipient}; use light_token_sdk::ValidityProof; use super::{ @@ -7,7 +7,7 @@ use super::{ }; #[derive(Debug, Clone, AnchorDeserialize, AnchorSerialize)] pub struct ChainedCtokenInstructionData { - pub compressed_mint_with_context: CompressedMintWithContext, + pub compressed_mint_with_context: MintWithContext, pub token_recipients: Vec, pub final_mint_authority: Option, pub pda_creation: PdaCreationData, diff --git a/sdk-tests/sdk-token-test/tests/ctoken_pda.rs b/sdk-tests/sdk-token-test/tests/ctoken_pda.rs index b7e1c70b4b..74e997c337 100644 --- a/sdk-tests/sdk-token-test/tests/ctoken_pda.rs +++ b/sdk-tests/sdk-token-test/tests/ctoken_pda.rs @@ -6,9 +6,9 @@ use light_sdk::instruction::{PackedAccounts, SystemAccountMetaConfig}; use light_token_interface::{ instructions::{ extensions::token_metadata::TokenMetadataInstructionData, - mint_action::{CompressedMintInstructionData, CompressedMintWithContext, Recipient}, + mint_action::{MintInstructionData, MintWithContext, Recipient}, }, - state::{extensions::AdditionalMetadata, CompressedMintMetadata}, + state::{extensions::AdditionalMetadata, MintMetadata}, LIGHT_TOKEN_PROGRAM_ID, }; use light_token_sdk::compressed_token::create_compressed_mint::{ @@ -91,7 +91,7 @@ async fn test_ctoken_pda() { println!("🧪 Verifying chained CPI results..."); // 1. Verify compressed mint was created and mint authority was revoked - let compressed_mint = light_token_interface::state::CompressedMint::deserialize( + let compressed_mint = light_token_interface::state::Mint::deserialize( &mut &mint_account.data.as_ref().unwrap().data[..], ) .unwrap(); @@ -190,18 +190,18 @@ pub async fn create_mint( let pda_amount = 100u64; // Create consolidated instruction data using new optimized structure - let compressed_mint_with_context = CompressedMintWithContext { + let compressed_mint_with_context = MintWithContext { leaf_index: 0, prove_by_index: false, root_index: rpc_result.addresses[0].root_index, address: compressed_mint_address, - mint: Some(CompressedMintInstructionData { + mint: Some(MintInstructionData { supply: 0, decimals, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, mint: mint.into(), - cmint_decompressed: false, + mint_decompressed: false, mint_signer: mint_seed.pubkey().to_bytes(), bump: mint_bump, }, diff --git a/sdk-tests/sdk-token-test/tests/decompress_full_cpi.rs b/sdk-tests/sdk-token-test/tests/decompress_full_cpi.rs index 57dcea0617..b40056d2eb 100644 --- a/sdk-tests/sdk-token-test/tests/decompress_full_cpi.rs +++ b/sdk-tests/sdk-token-test/tests/decompress_full_cpi.rs @@ -8,7 +8,7 @@ use light_program_test::{Indexer, LightProgramTest, ProgramTestConfig, Rpc}; use light_sdk::instruction::PackedAccounts; use light_test_utils::airdrop_lamports; use light_token_client::{actions::mint_action_comprehensive, instructions::mint_action::NewMint}; -use light_token_interface::instructions::mint_action::{CompressedMintWithContext, Recipient}; +use light_token_interface::instructions::mint_action::{MintWithContext, Recipient}; use light_token_sdk::compressed_token::{ create_compressed_mint::find_mint_address, decompress_full::DecompressFullAccounts, }; @@ -117,7 +117,7 @@ async fn setup_decompress_full_test(num_inputs: usize) -> (LightProgramTest, Tes &payer, &payer, None, // decompress_mint - false, // compress_and_close_cmint + false, // compress_and_close_mint compressed_recipients, Vec::new(), None, @@ -387,12 +387,11 @@ async fn test_decompress_full_cpi_with_context() { .unwrap() .value; - use light_token_interface::state::CompressedMint; + use light_token_interface::state::Mint; let compressed_mint = - CompressedMint::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()) - .unwrap(); + Mint::deserialize(&mut compressed_mint_account.data.unwrap().data.as_slice()).unwrap(); - let compressed_mint_with_context = CompressedMintWithContext { + let compressed_mint_with_context = MintWithContext { prove_by_index: true, leaf_index: compressed_mint_account.leaf_index, root_index: 0, diff --git a/sdk-tests/sdk-token-test/tests/pda_ctoken.rs b/sdk-tests/sdk-token-test/tests/pda_ctoken.rs index 9c40aabaa0..7709a019b2 100644 --- a/sdk-tests/sdk-token-test/tests/pda_ctoken.rs +++ b/sdk-tests/sdk-token-test/tests/pda_ctoken.rs @@ -9,9 +9,9 @@ use light_sdk::instruction::{PackedAccounts, SystemAccountMetaConfig}; use light_token_interface::{ instructions::{ extensions::token_metadata::TokenMetadataInstructionData, - mint_action::{CompressedMintInstructionData, CompressedMintWithContext, Recipient}, + mint_action::{MintInstructionData, MintWithContext, Recipient}, }, - state::{extensions::AdditionalMetadata, CompressedMintMetadata}, + state::{extensions::AdditionalMetadata, MintMetadata}, LIGHT_TOKEN_PROGRAM_ID, }; use light_token_sdk::{ @@ -95,7 +95,7 @@ async fn test_pda_ctoken() { println!("🧪 Verifying chained CPI results..."); // 1. Verify compressed mint was created and mint authority was revoked - let compressed_mint = light_token_interface::state::CompressedMint::deserialize( + let compressed_mint = light_token_interface::state::Mint::deserialize( &mut &mint_account.data.as_ref().unwrap().data[..], ) .unwrap(); @@ -261,18 +261,18 @@ pub async fn create_mint( let pda_amount = 100u64; // Create consolidated instruction data using new optimized structure - let compressed_mint_with_context = CompressedMintWithContext { + let compressed_mint_with_context = MintWithContext { leaf_index: 0, prove_by_index: false, root_index: rpc_result.addresses[0].root_index, address: compressed_mint_address, - mint: Some(CompressedMintInstructionData { + mint: Some(MintInstructionData { supply: 0, decimals, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, mint: mint.into(), - cmint_decompressed: false, + mint_decompressed: false, mint_signer: mint_seed.pubkey().to_bytes(), bump: mint_bump, }, diff --git a/sdk-tests/sdk-token-test/tests/test_4_transfer2.rs b/sdk-tests/sdk-token-test/tests/test_4_transfer2.rs index c0970dfe4d..f160b1fe87 100644 --- a/sdk-tests/sdk-token-test/tests/test_4_transfer2.rs +++ b/sdk-tests/sdk-token-test/tests/test_4_transfer2.rs @@ -7,15 +7,15 @@ use light_sdk::{ use light_test_utils::RpcError; use light_token_interface::{ instructions::{ - mint_action::{CompressedMintWithContext, Recipient}, + mint_action::{MintWithContext, Recipient}, transfer2::MultiInputTokenDataWithContext, }, - state::{BaseMint, CompressedMintMetadata, ACCOUNT_TYPE_MINT}, + state::{BaseMint, MintMetadata, ACCOUNT_TYPE_MINT}, COMPRESSED_MINT_SEED, }; use light_token_sdk::{ compressed_token::{ - create_compressed_mint::{create_compressed_mint, CreateCompressedMintInputs}, + create_compressed_mint::{create_compressed_mint, CreateMintInputs}, mint_to_compressed::{create_mint_to_compressed_instruction, MintToCompressedInputs}, }, token::CreateAssociatedTokenAccount, @@ -185,7 +185,7 @@ async fn create_compressed_mint_helper( .value; // Create compressed mint - let instruction = create_compressed_mint(CreateCompressedMintInputs { + let instruction = create_compressed_mint(CreateMintInputs { version: 3, decimals, mint_authority, @@ -231,11 +231,11 @@ async fn mint_compressed_tokens( // Extract mint_signer and bump from the actual compressed mint account use borsh::BorshDeserialize; let compressed_account_data = compressed_mint_account.data.clone().unwrap(); - let actual_compressed_mint: light_token_interface::state::CompressedMint = + let actual_compressed_mint: light_token_interface::state::Mint = BorshDeserialize::deserialize(&mut compressed_account_data.data.as_slice()).unwrap(); // Create expected compressed mint for the input using actual mint_signer and bump - let expected_compressed_mint = light_token_interface::state::CompressedMint { + let expected_compressed_mint = light_token_interface::state::Mint { base: BaseMint { mint_authority: Some(payer.pubkey().into()), supply: 0, @@ -243,10 +243,10 @@ async fn mint_compressed_tokens( is_initialized: true, freeze_authority: None, }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, mint: mint_pda.into(), - cmint_decompressed: false, + mint_decompressed: false, mint_signer: actual_compressed_mint.metadata.mint_signer, bump: actual_compressed_mint.metadata.bump, }, @@ -259,7 +259,7 @@ async fn mint_compressed_tokens( let mint_to_instruction = create_mint_to_compressed_instruction( MintToCompressedInputs { cpi_context_pubkey: None, - compressed_mint_inputs: CompressedMintWithContext { + compressed_mint_inputs: MintWithContext { prove_by_index: true, leaf_index: compressed_mint_account.leaf_index, root_index: 0, @@ -275,7 +275,7 @@ async fn mint_compressed_tokens( payer: payer.pubkey(), state_merkle_tree: compressed_mint_account.tree_info.tree, input_queue: compressed_mint_account.tree_info.queue, - output_queue_cmint: compressed_mint_account.tree_info.queue, + output_queue_mint: compressed_mint_account.tree_info.queue, output_queue_tokens: output_queue, decompressed_mint_config: None, token_account_version: 2, diff --git a/sdk-tests/sdk-token-test/tests/test_compress_full_and_close.rs b/sdk-tests/sdk-token-test/tests/test_compress_full_and_close.rs index 4a0bbc753b..780479e05a 100644 --- a/sdk-tests/sdk-token-test/tests/test_compress_full_and_close.rs +++ b/sdk-tests/sdk-token-test/tests/test_compress_full_and_close.rs @@ -6,15 +6,13 @@ use light_program_test::{Indexer, LightProgramTest, ProgramTestConfig, Rpc}; use light_sdk::instruction::{PackedAccounts, SystemAccountMetaConfig}; use light_token_client::instructions::transfer2::create_decompress_instruction; use light_token_interface::{ - instructions::mint_action::{CompressedMintWithContext, Recipient}, - state::{ - BaseMint, CompressedMint, CompressedMintMetadata, TokenDataVersion, ACCOUNT_TYPE_MINT, - }, + instructions::mint_action::{MintWithContext, Recipient}, + state::{BaseMint, Mint, MintMetadata, TokenDataVersion, ACCOUNT_TYPE_MINT}, COMPRESSED_MINT_SEED, LIGHT_TOKEN_PROGRAM_ID, }; use light_token_sdk::{ compressed_token::{ - create_compressed_mint::{create_compressed_mint, CreateCompressedMintInputs}, + create_compressed_mint::{create_compressed_mint, CreateMintInputs}, mint_to_compressed::{create_mint_to_compressed_instruction, MintToCompressedInputs}, }, token::{ @@ -80,7 +78,7 @@ async fn test_compress_full_and_close() { let address_merkle_tree_root_index = rpc_result.addresses[0].root_index; - let instruction = create_compressed_mint(CreateCompressedMintInputs { + let instruction = create_compressed_mint(CreateMintInputs { version: 3, decimals, mint_authority, @@ -120,7 +118,7 @@ async fn test_compress_full_and_close() { .ok_or("Compressed mint account not found") .unwrap(); - let expected_compressed_mint = CompressedMint { + let expected_compressed_mint = Mint { base: BaseMint { mint_authority: Some(mint_authority.into()), supply: 0, @@ -128,10 +126,10 @@ async fn test_compress_full_and_close() { is_initialized: true, freeze_authority: Some(freeze_authority.into()), }, - metadata: CompressedMintMetadata { + metadata: MintMetadata { version: 3, mint: mint_pda.into(), - cmint_decompressed: false, + mint_decompressed: false, mint_signer: mint_signer.pubkey().to_bytes(), bump: mint_bump, }, @@ -141,7 +139,7 @@ async fn test_compress_full_and_close() { extensions: None, }; - let compressed_mint_inputs = CompressedMintWithContext { + let compressed_mint_inputs = MintWithContext { prove_by_index: true, leaf_index: compressed_mint_account.leaf_index, root_index: 0, @@ -159,7 +157,7 @@ async fn test_compress_full_and_close() { payer: payer.pubkey(), state_merkle_tree: compressed_mint_account.tree_info.tree, input_queue: compressed_mint_account.tree_info.queue, - output_queue_cmint: compressed_mint_account.tree_info.queue, + output_queue_mint: compressed_mint_account.tree_info.queue, output_queue_tokens: compressed_mint_account.tree_info.queue, decompressed_mint_config: None, token_account_version: 2, From 341fcedd6ffaee535073e692eb84660e059b249b Mon Sep 17 00:00:00 2001 From: ananas Date: Fri, 16 Jan 2026 23:55:47 +0000 Subject: [PATCH 2/2] cleanup --- .../src/decompress_mint.rs | 32 +++++++++---------- sdk-libs/macros/src/finalize/codegen.rs | 12 +++---- sdk-libs/program-test/src/compressible.rs | 14 ++++---- .../src/program_test/light_program_test.rs | 4 +-- .../token-sdk/src/token/decompress_mint.rs | 7 ++-- .../csdk-anchor-full-derived-test/src/lib.rs | 4 +-- .../src/state.rs | 4 +-- .../tests/basic_test.rs | 8 ----- 8 files changed, 37 insertions(+), 48 deletions(-) diff --git a/sdk-libs/compressible-client/src/decompress_mint.rs b/sdk-libs/compressible-client/src/decompress_mint.rs index 42bc878653..a843b82e29 100644 --- a/sdk-libs/compressible-client/src/decompress_mint.rs +++ b/sdk-libs/compressible-client/src/decompress_mint.rs @@ -16,8 +16,8 @@ use borsh::BorshDeserialize; use light_client::indexer::{CompressedAccount, Indexer, IndexerError, ValidityProofWithContext}; use light_compressed_account::instruction_data::compressed_proof::ValidityProof; use light_token_interface::{ - instructions::mint_action::{CompressedMintInstructionData, CompressedMintWithContext}, - state::CompressedMint, + instructions::mint_action::{MintInstructionData, MintWithContext}, + state::Mint, CMINT_ADDRESS_TREE, }; use light_token_sdk::{ @@ -58,7 +58,7 @@ pub enum MintState { /// CMint is compressed - needs decompression. Cold { compressed: CompressedAccount, - mint_data: CompressedMint, + mint_data: Mint, }, /// CMint doesn't exist (neither on-chain nor compressed). None, @@ -114,7 +114,7 @@ impl MintInterface { } /// Returns the compressed account and mint data if cold. - pub fn compressed(&self) -> Option<(&CompressedAccount, &CompressedMint)> { + pub fn compressed(&self) -> Option<(&CompressedAccount, &Mint)> { match &self.state { MintState::Cold { compressed, @@ -159,7 +159,7 @@ pub fn build_decompress_mint( }; // Check if already decompressed flag is set - return empty vec (idempotent) - if mint_data.metadata.cmint_decompressed { + if mint_data.metadata.mint_decompressed { return Ok(vec![]); } @@ -177,11 +177,11 @@ pub fn build_decompress_mint( .map(|next| next.queue) .unwrap_or(input_queue); - // Build CompressedMintWithContext - let mint_instruction_data = CompressedMintInstructionData::try_from(mint_data.clone()) + // Build MintWithContext + let mint_instruction_data = MintInstructionData::try_from(mint_data.clone()) .map_err(|_| DecompressMintError::MissingMintData)?; - let compressed_mint_with_context = CompressedMintWithContext { + let compressed_mint_with_context = MintWithContext { leaf_index: account_info.leaf_index as u32, prove_by_index: account_info.root_index.proof_by_index(), root_index: account_info.root_index.root_index().unwrap_or_default(), @@ -234,7 +234,7 @@ pub async fn decompress_mint( // Check decompressed flag before fetching proof if let Some((_, mint_data)) = mint.compressed() { - if mint_data.metadata.cmint_decompressed { + if mint_data.metadata.mint_decompressed { return Ok(vec![]); } } @@ -323,11 +323,11 @@ pub async fn decompress_mint_idempotent( }; // 4. Parse mint data from compressed account - let mint_data = CompressedMint::try_from_slice(&data.data) - .map_err(|_| DecompressMintError::MissingMintData)?; + let mint_data = + Mint::try_from_slice(&data.data).map_err(|_| DecompressMintError::MissingMintData)?; // 5. Check if already decompressed flag is set - return empty vec (idempotent) - if mint_data.metadata.cmint_decompressed { + if mint_data.metadata.mint_decompressed { return Ok(vec![]); } @@ -348,13 +348,13 @@ pub async fn decompress_mint_idempotent( .map(|next| next.queue) .unwrap_or(input_queue); - // 7. Build CompressedMintWithContext + // 7. Build MintWithContext // NOTE: prove_by_index and leaf_index come from account_info (the proof), not compressed_account // The query may have stale values, but the proof is authoritative. - let mint_instruction_data = CompressedMintInstructionData::try_from(mint_data) + let mint_instruction_data = MintInstructionData::try_from(mint_data) .map_err(|_| DecompressMintError::MissingMintData)?; - let compressed_mint_with_context = CompressedMintWithContext { + let compressed_mint_with_context = MintWithContext { leaf_index: account_info.leaf_index as u32, prove_by_index: account_info.root_index.proof_by_index(), root_index: account_info.root_index.root_index().unwrap_or_default(), @@ -387,7 +387,7 @@ pub fn create_mint_interface( signer: Pubkey, address_tree: Pubkey, onchain_account: Option, - compressed: Option<(CompressedAccount, CompressedMint)>, + compressed: Option<(CompressedAccount, Mint)>, ) -> MintInterface { let (cmint, _) = find_mint_address(&signer); let compressed_address = derive_mint_compressed_address(&signer, &address_tree); diff --git a/sdk-libs/macros/src/finalize/codegen.rs b/sdk-libs/macros/src/finalize/codegen.rs index 5cc3a93f86..0e8059c3c6 100644 --- a/sdk-libs/macros/src/finalize/codegen.rs +++ b/sdk-libs/macros/src/finalize/codegen.rs @@ -288,13 +288,13 @@ fn generate_pre_init_pdas_and_mints( let __freeze_authority: Option = #freeze_authority_tokens; // Build compressed mint instruction data - let compressed_mint_data = light_token_interface::instructions::mint_action::CompressedMintInstructionData { + let compressed_mint_data = light_token_interface::instructions::mint_action::MintInstructionData { supply: 0, decimals: #decimals, - metadata: light_token_interface::state::CompressedMintMetadata { + metadata: light_token_interface::state::MintMetadata { version: 3, mint: mint_pda.to_bytes().into(), - cmint_decompressed: false, + mint_decompressed: false, mint_signer: mint_signer_key.to_bytes(), bump: _cmint_bump, }, @@ -453,13 +453,13 @@ fn generate_pre_init_mints_only( let __freeze_authority: Option = #freeze_authority_tokens; // Build compressed mint instruction data - let compressed_mint_data = light_token_interface::instructions::mint_action::CompressedMintInstructionData { + let compressed_mint_data = light_token_interface::instructions::mint_action::MintInstructionData { supply: 0, decimals: #decimals, - metadata: light_token_interface::state::CompressedMintMetadata { + metadata: light_token_interface::state::MintMetadata { version: 3, mint: mint_pda.to_bytes().into(), - cmint_decompressed: false, + mint_decompressed: false, mint_signer: mint_signer_key.to_bytes(), bump: _cmint_bump, }, diff --git a/sdk-libs/program-test/src/compressible.rs b/sdk-libs/program-test/src/compressible.rs index a3c6180b94..a51f16df00 100644 --- a/sdk-libs/program-test/src/compressible.rs +++ b/sdk-libs/program-test/src/compressible.rs @@ -414,8 +414,7 @@ async fn compress_cmint_forester( use light_compressible::config::CompressibleConfig; use light_token_interface::{ instructions::mint_action::{ - CompressAndCloseCMintAction, CompressedMintWithContext, - MintActionCompressedInstructionData, + CompressAndCloseMintAction, MintActionCompressedInstructionData, MintWithContext, }, LIGHT_TOKEN_PROGRAM_ID, }; @@ -428,9 +427,8 @@ async fn compress_cmint_forester( })?; // Deserialize Mint to get compressed_address and rent_sponsor - let mint: Mint = - BorshDeserialize::deserialize(&mut cmint_account.data.as_slice()) - .map_err(|e| RpcError::CustomError(format!("Failed to deserialize Mint: {:?}", e)))?; + let mint: Mint = BorshDeserialize::deserialize(&mut cmint_account.data.as_slice()) + .map_err(|e| RpcError::CustomError(format!("Failed to deserialize Mint: {:?}", e)))?; let compressed_mint_address = mint.metadata.compressed_address(); let rent_sponsor = Pubkey::from(mint.compression.rent_sponsor); @@ -455,7 +453,7 @@ async fn compress_cmint_forester( // IMPORTANT: Set mint to None when CMint is decompressed // This tells on-chain code to read mint data from CMint Solana account // (not from instruction data which would have stale compression_info) - let compressed_mint_inputs = CompressedMintWithContext { + let compressed_mint_inputs = MintWithContext { prove_by_index: rpc_proof_result.accounts[0].root_index.proof_by_index(), leaf_index: compressed_mint_account.leaf_index, root_index: rpc_proof_result.accounts[0] @@ -466,12 +464,12 @@ async fn compress_cmint_forester( mint: None, // CMint is decompressed, data lives in CMint account }; - // Build instruction data with CompressAndCloseCMint action + // Build instruction data with CompressAndCloseMint action let instruction_data = MintActionCompressedInstructionData::new( compressed_mint_inputs, rpc_proof_result.proof.into(), ) - .with_compress_and_close_cmint(CompressAndCloseCMintAction { idempotent: 1 }); + .with_compress_and_close_mint(CompressAndCloseMintAction { idempotent: 1 }); // Get state tree info let state_tree_info = rpc_proof_result.accounts[0].tree_info; diff --git a/sdk-libs/program-test/src/program_test/light_program_test.rs b/sdk-libs/program-test/src/program_test/light_program_test.rs index 3591ec8a77..47919e08d3 100644 --- a/sdk-libs/program-test/src/program_test/light_program_test.rs +++ b/sdk-libs/program-test/src/program_test/light_program_test.rs @@ -647,7 +647,7 @@ impl LightProgramTest { use borsh::BorshDeserialize; use light_client::indexer::Indexer; use light_compressible_client::{MintInterface, MintState}; - use light_token_interface::{state::CompressedMint, CMINT_ADDRESS_TREE}; + use light_token_interface::{state::Mint, CMINT_ADDRESS_TREE}; use light_token_sdk::{ compressed_token::create_compressed_mint::derive_mint_compressed_address, token::find_mint_address, @@ -677,7 +677,7 @@ impl LightProgramTest { // Parse mint data if available if let Some(data) = compressed.data.as_ref() { if !data.data.is_empty() { - if let Ok(mint_data) = CompressedMint::try_from_slice(&data.data) { + if let Ok(mint_data) = Mint::try_from_slice(&data.data) { return Ok(MintInterface { cmint, signer: *signer, diff --git a/sdk-libs/token-sdk/src/token/decompress_mint.rs b/sdk-libs/token-sdk/src/token/decompress_mint.rs index 6d80b26c90..0541358783 100644 --- a/sdk-libs/token-sdk/src/token/decompress_mint.rs +++ b/sdk-libs/token-sdk/src/token/decompress_mint.rs @@ -2,8 +2,7 @@ use light_compressed_account::instruction_data::{ compressed_proof::ValidityProof, traits::LightInstructionData, }; use light_token_interface::instructions::mint_action::{ - CompressedMintWithContext, CpiContext, DecompressMintAction, - MintActionCompressedInstructionData, MintWithContext, + CpiContext, DecompressMintAction, MintActionCompressedInstructionData, MintWithContext, }; use solana_account_info::AccountInfo; use solana_cpi::{invoke, invoke_signed}; @@ -250,7 +249,7 @@ pub struct DecompressCMintWithCpiContext { /// Output queue for updated compressed mint pub output_queue: Pubkey, /// Compressed mint with context (from indexer) - pub compressed_mint_with_context: CompressedMintWithContext, + pub compressed_mint_with_context: MintWithContext, /// Validity proof for the compressed mint pub proof: ValidityProof, /// Rent payment in epochs (must be >= 2) @@ -341,7 +340,7 @@ pub struct DecompressCMintCpiWithContext<'info> { /// This is separate from system_accounts.cpi_authority_pda which is the calling program's authority pub ctoken_cpi_authority: AccountInfo<'info>, /// Compressed mint with context (from indexer) - pub compressed_mint_with_context: CompressedMintWithContext, + pub compressed_mint_with_context: MintWithContext, /// Validity proof for the compressed mint pub proof: ValidityProof, /// Rent payment in epochs (must be >= 2) diff --git a/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs b/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs index 9cbd0f3849..8afc849d3f 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs @@ -107,7 +107,7 @@ pub mod csdk_anchor_full_derived_test { if params.vault_mint_amount > 0 { CTokenMintToCpi { - cmint: ctx.accounts.cmint.to_account_info(), + mint: ctx.accounts.cmint.to_account_info(), destination: ctx.accounts.vault.to_account_info(), amount: params.vault_mint_amount, authority: ctx.accounts.mint_authority.to_account_info(), @@ -119,7 +119,7 @@ pub mod csdk_anchor_full_derived_test { if params.user_ata_mint_amount > 0 { CTokenMintToCpi { - cmint: ctx.accounts.cmint.to_account_info(), + mint: ctx.accounts.cmint.to_account_info(), destination: ctx.accounts.user_ata.to_account_info(), amount: params.user_ata_mint_amount, authority: ctx.accounts.mint_authority.to_account_info(), diff --git a/sdk-tests/csdk-anchor-full-derived-test/src/state.rs b/sdk-tests/csdk-anchor-full-derived-test/src/state.rs index 54ace5c653..cca63029fd 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/src/state.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/src/state.rs @@ -2,9 +2,9 @@ use anchor_lang::prelude::*; use light_sdk::{ compressible::CompressionInfo, instruction::{PackedAddressTreeInfo, ValidityProof}, - LightDiscriminator, LightHasher, + LightDiscriminator, }; -use light_sdk_macros::{Compressible, CompressiblePack, RentFreeAccount}; +use light_sdk_macros::RentFreeAccount; use light_token_interface::instructions::mint_action::MintWithContext; #[derive(Default, Debug, InitSpace, RentFreeAccount)] diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs index b8c2cc4e43..f43bb8afd2 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs @@ -9,15 +9,7 @@ use light_program_test::{ Indexer, ProgramTestConfig, Rpc, }; use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; -use light_token_interface::{ - instructions::mint_action::{MintInstructionData, MintWithContext}, - state::MintMetadata, -}; -use light_token_sdk::compressed_token::create_compressed_mint::{ - derive_mint_compressed_address, find_mint_address, -}; use light_token_sdk::token::find_mint_address as find_cmint_address; -use light_token_types::CPI_AUTHORITY_PDA; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey;