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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ groth16-solana = { version = "0.2.0" }
bytemuck = { version = "1.19.0" }
arrayvec = "0.7"
tinyvec = "1.10.0"
pinocchio-token-program = { git= "https://github.com/Lightprotocol/token", rev="1bf7a9e525e753c3eb7bbf9971a26efbc23e5c73" }
pinocchio-token-program = { git= "https://github.com/Lightprotocol/token", rev="0c55d18" }
# Math and crypto
num-bigint = "0.4.6"
tabled = "0.20"
Expand Down
9 changes: 0 additions & 9 deletions js/compressed-token/src/v3/layout/layout-mint-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ export const UpdateAuthorityLayout = struct([
option(publicKey(), 'newAuthority'),
]);

export const CreateSplMintActionLayout = struct([u8('mintBump')]);

export const MintToCTokenActionLayout = struct([
u8('accountIndex'),
u64('amount'),
Expand Down Expand Up @@ -74,7 +72,6 @@ export const ActionLayout = rustEnum([
MintToCompressedActionLayout.replicate('mintToCompressed'),
UpdateAuthorityLayout.replicate('updateMintAuthority'),
UpdateAuthorityLayout.replicate('updateFreezeAuthority'),
CreateSplMintActionLayout.replicate('createSplMint'),
MintToCTokenActionLayout.replicate('mintToCToken'),
UpdateMetadataFieldActionLayout.replicate('updateMetadataField'),
UpdateMetadataAuthorityActionLayout.replicate('updateMetadataAuthority'),
Expand Down Expand Up @@ -176,7 +173,6 @@ const ActionLayoutV1 = rustEnum([
MintToCompressedActionLayout.replicate('mintToCompressed'),
UpdateAuthorityLayout.replicate('updateMintAuthority'),
UpdateAuthorityLayout.replicate('updateFreezeAuthority'),
CreateSplMintActionLayout.replicate('createSplMint'),
MintToCTokenActionLayout.replicate('mintToCToken'),
UpdateMetadataFieldActionLayout.replicate('updateMetadataField'),
UpdateMetadataAuthorityActionLayout.replicate('updateMetadataAuthority'),
Expand Down Expand Up @@ -233,10 +229,6 @@ export interface UpdateAuthority {
newAuthority: PublicKey | null;
}

export interface CreateSplMintAction {
mintBump: number;
}

export interface MintToCTokenAction {
accountIndex: number;
amount: bigint;
Expand Down Expand Up @@ -274,7 +266,6 @@ export type Action =
| { mintToCompressed: MintToCompressedAction }
| { updateMintAuthority: UpdateAuthority }
| { updateFreezeAuthority: UpdateAuthority }
| { createSplMint: CreateSplMintAction }
| { mintToCToken: MintToCTokenAction }
| { updateMetadataField: UpdateMetadataFieldAction }
| { updateMetadataAuthority: UpdateMetadataAuthorityAction }
Expand Down
2 changes: 2 additions & 0 deletions js/compressed-token/tests/e2e/unwrap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ describe('createUnwrapInstruction', () => {
mint,
BigInt(1000),
tokenPoolInfo!,
TEST_TOKEN_DECIMALS,
);

expect(ix).toBeDefined();
Expand Down Expand Up @@ -125,6 +126,7 @@ describe('createUnwrapInstruction', () => {
mint,
BigInt(500),
tokenPoolInfo!,
TEST_TOKEN_DECIMALS,
feePayer.publicKey,
);

Expand Down
56 changes: 0 additions & 56 deletions js/compressed-token/tests/unit/layout-mint-action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,62 +203,6 @@ describe('layout-mint-action', () => {
expect('updateMintAuthority' in decoded.actions[0]).toBe(true);
});

it('should encode and decode with createSplMint action', () => {
const mint = Keypair.generate().publicKey;

const createSplMintAction: Action = {
createSplMint: {
mintBump: 254,
},
};

const data: MintActionCompressedInstructionData = {
leafIndex: 0,
proveByIndex: false,
rootIndex: 0,
compressedAddress: Array(32).fill(0),
tokenPoolBump: 255,
tokenPoolIndex: 0,
maxTopUp: 0,
createMint: {
readOnlyAddressTrees: [1, 2, 3, 4],
readOnlyAddressTreeRootIndices: [10, 20, 30, 40],
},
actions: [createSplMintAction],
proof: {
a: Array(32).fill(1),
b: Array(64).fill(2),
c: Array(32).fill(3),
},
cpiContext: null,
mint: {
supply: 0n,
decimals: 9,
metadata: {
version: 1,
cmintDecompressed: false,
mint,
},
mintAuthority: mint,
freezeAuthority: null,
extensions: null,
},
};

const encoded = encodeMintActionInstructionData(data);
const decoded = decodeMintActionInstructionData(encoded);

expect(decoded.actions.length).toBe(1);
expect('createSplMint' in decoded.actions[0]).toBe(true);

const action = decoded.actions[0] as {
createSplMint: { mintBump: number };
};
expect(action.createSplMint.mintBump).toBe(254);
expect(decoded.createMint).not.toBe(null);
expect(decoded.proof).not.toBe(null);
});

it('should encode and decode with multiple actions', () => {
const mint = Keypair.generate().publicKey;
const recipient = Keypair.generate().publicKey;
Expand Down
40 changes: 40 additions & 0 deletions program-libs/compressible/src/compression_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ use crate::{
AnchorDeserialize, AnchorSerialize,
};

/// Trait for types that can calculate top-up lamports for compressible accounts.
pub trait CalculateTopUp {
fn calculate_top_up_lamports(
&self,
num_bytes: u64,
current_slot: u64,
current_lamports: u64,
rent_exemption_lamports: u64,
) -> Result<u64, CompressibleError>;
}

/// Compressible extension for ctoken accounts.
#[derive(
Debug,
Expand Down Expand Up @@ -129,6 +140,35 @@ impl_is_compressible!(CompressionInfo);
impl_is_compressible!(ZCompressionInfo<'_>);
impl_is_compressible!(ZCompressionInfoMut<'_>);

// Implement CalculateTopUp trait for all compressible extension types
macro_rules! impl_calculate_top_up {
($struct_name:ty) => {
impl CalculateTopUp for $struct_name {
#[inline(always)]
fn calculate_top_up_lamports(
&self,
num_bytes: u64,
current_slot: u64,
current_lamports: u64,
rent_exemption_lamports: u64,
) -> Result<u64, CompressibleError> {
// Delegate to the inherent method
Self::calculate_top_up_lamports(
self,
num_bytes,
current_slot,
current_lamports,
rent_exemption_lamports,
)
}
}
};
}

impl_calculate_top_up!(CompressionInfo);
impl_calculate_top_up!(ZCompressionInfo<'_>);
impl_calculate_top_up!(ZCompressionInfoMut<'_>);

// Unified macro to implement get_last_funded_epoch for all extension types
macro_rules! impl_get_last_paid_epoch {
($struct_name:ty) => {
Expand Down
4 changes: 4 additions & 0 deletions program-libs/ctoken-interface/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ pub enum CTokenError {
"Decompress has withheld_transfer_fee but destination lacks TransferFeeAccount extension"
)]
DecompressWithheldFeeWithoutExtension,

#[error("Missing required payer account")]
MissingPayer,
}

impl From<CTokenError> for u32 {
Expand Down Expand Up @@ -253,6 +256,7 @@ impl From<CTokenError> for u32 {
CTokenError::MintMismatch => 18058,
CTokenError::DecompressDelegatedAmountWithoutDelegate => 18059,
CTokenError::DecompressWithheldFeeWithoutExtension => 18060,
CTokenError::MissingPayer => 18061,
CTokenError::HasherError(e) => u32::from(e),
CTokenError::ZeroCopyError(e) => u32::from(e),
CTokenError::CompressedAccountError(e) => u32::from(e),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ pub struct CompressibleExtensionInstructionData {
/// Rent payment in epochs.
/// Paid once at initialization.
pub rent_payment: u8,
/// Placeholder for future use. If true, the compressed token account cannot be transferred,
/// only decompressed. Currently unused - always set to 0.
/// If non-zero, the compressed token account cannot be transferred, only decompressed.
/// Required for mints with restricted extensions (Pausable, PermanentDelegate, TransferFee, TransferHook).
/// Must be set for compressible ATAs.
pub compression_only: u8,
pub write_top_up: u32,
pub compress_to_account_pubkey: Option<CompressToPubkey>,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use light_compressible::compression_info::CompressionInfo;
use light_zero_copy::ZeroCopy;

use super::{
CompressAndCloseCMintAction, CpiContext, CreateSplMintAction, DecompressMintAction,
MintToCTokenAction, MintToCompressedAction, RemoveMetadataKeyAction, UpdateAuthority,
CompressAndCloseCMintAction, CpiContext, DecompressMintAction, MintToCTokenAction,
MintToCompressedAction, RemoveMetadataKeyAction, UpdateAuthority,
UpdateMetadataAuthorityAction, UpdateMetadataFieldAction,
};
use crate::{
Expand All @@ -25,11 +25,6 @@ pub enum Action {
UpdateMintAuthority(UpdateAuthority),
/// Update freeze authority of a compressed mint account.
UpdateFreezeAuthority(UpdateAuthority),
/// Create an spl mint for a cmint.
/// - existing supply is minted to a token pool account.
/// - mint and freeze authority are a ctoken pda.
/// - is an spl-token-2022 mint account.
CreateSplMint(CreateSplMintAction),
/// Mint ctokens from a cmint to a ctoken solana account
/// (tokens are not compressed but not spl tokens).
MintToCToken(MintToCTokenAction),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
mod builder;
mod compress_and_close_cmint;
mod cpi_context;
mod create_spl_mint;
mod decompress_mint;
mod instruction_data;
mod mint_to_compressed;
Expand All @@ -11,7 +10,6 @@ mod update_mint;

pub use compress_and_close_cmint::*;
pub use cpi_context::*;
pub use create_spl_mint::*;
pub use decompress_mint::*;
pub use instruction_data::*;
pub use mint_to_compressed::*;
Expand Down
4 changes: 2 additions & 2 deletions program-libs/ctoken-interface/src/state/ctoken/size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use crate::{

/// Calculates the size of a ctoken account based on which extensions are present.
///
/// Note: Compression info is now embedded in the base struct (CTokenZeroCopyMeta),
/// so there's no separate compressible extension parameter.
/// Note: Compressible extension is required if the T22 mint has restricted extensions
/// (Pausable, PermanentDelegate, TransferFee, TransferHook).
Comment on lines +10 to +11
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Include DefaultAccountState in the list of restricted extensions.

Your update correctly documents that the compressible extension is required for mints with restricted extensions, but the list still omits DefaultAccountState. The DefaultAccountStateExtension sets a default state for all new token accounts created for a mint. The validation logic (per the prior review) enforces this across five restricted extensions: Pausable, PermanentDelegate, TransferFeeConfig, DefaultAccountState, and TransferHook. The comment should match the implementation.

🔎 Proposed fix
/// Note: Compressible extension is required if the T22 mint has restricted extensions
-/// (Pausable, PermanentDelegate, TransferFee, TransferHook).
+/// (Pausable, PermanentDelegate, DefaultAccountState, TransferFee, TransferHook).
🤖 Prompt for AI Agents
In @program-libs/ctoken-interface/src/state/ctoken/size.rs around lines 10 - 11,
Update the comment in size.rs to include DefaultAccountState in the list of
restricted extensions: change the sentence that lists Pausable,
PermanentDelegate, TransferFee, TransferHook to list Pausable,
PermanentDelegate, TransferFeeConfig, DefaultAccountState, and TransferHook so
the documentation matches the validation logic that enforces these five
restricted extensions (e.g., DefaultAccountStateExtension and the compressible
requirement).

///
/// # Arguments
/// * `extensions` - Optional slice of extension configs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ async fn test_approve_fails() {
100,
None,
"non_existent_account",
6000, // Pinocchio token program error - account doesn't exist
6153, // NotRentExempt (SPL Token code 0 -> ErrorCode::NotRentExempt)
)
.await;
}
Expand Down Expand Up @@ -253,7 +253,7 @@ async fn test_revoke_fails() {
&owner,
None,
"non_existent_account",
6000, // Pinocchio token program error - account doesn't exist
6153, // NotRentExempt (SPL Token code 0 -> ErrorCode::NotRentExempt)
)
.await;
}
Expand Down
22 changes: 11 additions & 11 deletions program-tests/compressed-token-test/tests/ctoken/burn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ async fn test_burn_success_cases() {
// Burn Failure Cases
// ============================================================================

/// Error codes used in burn validation
/// Error codes used in burn validation (mapped to ErrorCode enum variants)
mod error_codes {
/// Insufficient funds to complete the operation (SPL Token code 1)
pub const INSUFFICIENT_FUNDS: u32 = 1;
/// Authority doesn't match token account owner (SPL Token code 4)
pub const OWNER_MISMATCH: u32 = 4;
/// Insufficient funds to complete the operation (SplInsufficientFunds = 6154)
pub const INSUFFICIENT_FUNDS: u32 = 6154;
/// Authority doesn't match token account owner (OwnerMismatch = 6075)
pub const OWNER_MISMATCH: u32 = 6075;
}

#[tokio::test]
Expand Down Expand Up @@ -142,8 +142,8 @@ async fn test_burn_fails() {
)
.await;

// Non-existent CMint returns GenericError (code 0)
assert_rpc_error(result, 0, 0).unwrap();
// Non-existent CMint returns NotRentExempt (SPL Token code 0 -> 6153)
assert_rpc_error(result, 0, 6153).unwrap();
println!("test_burn_fails: wrong mint passed");
}

Expand Down Expand Up @@ -172,8 +172,8 @@ async fn test_burn_fails() {
)
.await;

// Non-existent CToken account returns GenericError (code 0)
assert_rpc_error(result, 0, 0).unwrap();
// Non-existent CToken account returns NotRentExempt (SPL Token code 0 -> 6153)
assert_rpc_error(result, 0, 6153).unwrap();
println!("test_burn_fails: non-existent account passed");
}

Expand Down Expand Up @@ -399,8 +399,8 @@ async fn setup_burn_test() -> BurnTestContext {

use light_ctoken_sdk::ctoken::BurnCTokenChecked;

/// MintDecimalsMismatch error code (SPL Token code 18)
const MINT_DECIMALS_MISMATCH: u32 = 18;
/// MintDecimalsMismatch error code (SplMintDecimalsMismatch = 6166)
const MINT_DECIMALS_MISMATCH: u32 = 6166;

#[tokio::test]
#[serial]
Expand Down
Loading