Skip to content

Conversation

@ananas-block
Copy link
Contributor

@ananas-block ananas-block commented Jan 6, 2026

Summary

Refactors compressed token program structure and adds validation for restricted T22 extensions.

Changes

Structure

  • Reorganize into src/ctoken/ (decompressed account ops) and src/compressed_token/ (Merkle tree ops)
  • Restructure documentation under docs/

Token Extensions

  • Add validation: mints with restricted extensions require compressible token accounts
  • Add MAX_COMPRESSIONS constant (32) with descriptive error
  • Refactor and test check_extensions for transfer2

Code Quality

  • Unify top-up calculations with CalculateTopUp trait
  • Simplify ctoken account access with direct indexing
  • Validate CompressibleConfig is ACTIVE during account parsing
  • Remove dead code: CreateSplMint, no_output_compressed_accounts
  • Refactor mint_output into focused helper functions

Fixes

  • Error conversions for pinocchio token operations
  • Stackframe issues
  • Create ctoken account validation

Summary by CodeRabbit

  • New Features

    • Expanded compressed-token and compressible workflows: new create-token and ATA flows, compressible account handling, and broader ctoken operation surface (mint, transfer, approve, burn, close, freeze/thaw).
  • Bug Fixes

    • Unified and clarified error mappings; tightened mint/extension validation; optional payer/top‑up handling and safer rent/top‑up processing.
  • Behavior Changes

    • Removed the CreateSplMint action; reduced action set and adjusted signer/validation rules.
  • Documentation

    • Large reorganization and expansion of instruction and program docs with improved navigation.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 6, 2026

📝 Walkthrough

Walkthrough

Added a public CalculateTopUp trait and macro impls for compression info types; introduced centralized compressible top‑up helpers that accept an optional payer and delegate top‑up calculation to the new trait; removed CreateSplMintAction and related action variant; reorganized modules (compressed_token, ctoken, compressible), moved many files, consolidated error-to-ProgramError converters, and adjusted many call sites and tests.

Changes

Cohort / File(s) Summary
Trait Addition & Impl (program-libs)
program-libs/compressible/src/compression_info.rs
Added pub trait CalculateTopUp, macro_rules! impl_calculate_top_up, and impls for CompressionInfo, ZCompressionInfo<'_>, ZCompressionInfoMut<'_> delegating to existing calculate_top_up_lamports.
Compressible top‑up helpers (shared)
programs/compressed-token/program/src/shared/compressible_top_up.rs
Made payer optional (Option<&AccountInfo>), added generic process_compression_top_up<T: CalculateTopUp> helper, and refactored top‑up flow to delegate calculation and rent/clock initialization to the helper.
Create/config parsing moved & reworked
programs/compressed-token/program/src/create_token_account.rs (deleted), programs/compressed-token/program/src/shared/config_account.rs
Removed legacy create_token_account module; added parse_config_account and next_config_account helpers in shared/config_account.rs.
CToken API & flows (ctoken module)
programs/compressed-token/program/src/ctoken/*, programs/compressed-token/program/src/shared/initialize_ctoken_account.rs
New ctoken module surface; added process_create_token_account and create_compressible_account, is_valid_mint; changed CTokenInitConfig (removed mint field) and adjusted ATA/create flows and rent/seed handling.
Removed CreateSplMintAction & Action variant
program-libs/ctoken-interface/src/instructions/mint_action/create_spl_mint.rs, program-libs/ctoken-interface/src/instructions/mint_action/instruction_data.rs, program-libs/ctoken-interface/src/instructions/mint_action/mod.rs
Deleted CreateSplMintAction type, removed Action::CreateSplMint enum variant and re-export.
Mint action rework / mint_output removal
programs/compressed-token/program/src/compressed_token/mint_action/*, programs/compressed-token/program/src/mint_action/mint_output.rs (deleted)
Added compressed_token::mint_action module, renamed/updated many mint-action files, simplified MintActionAccounts constructor/validate, renamed cmint_is_source_of_truthcmint_is_decompressed, and removed process_output_compressed_account orchestration.
Module reorganizations & path updates
programs/compressed-token/program/src/compressed_token/mod.rs, many programs/compressed-token/program/src/** files and tests
Added compressed_token and transfer2 modules; widespread import/path updates, direct-index account access in hot paths, many comment/doc tweaks, and test path updates.
MAX_COMPRESSIONS / compression arrays
programs/compressed-token/program/src/lib.rs, programs/compressed-token/program/src/compressed_token/transfer2/*, programs/compressed-token/program/src/compressed_token/transfer2/token_inputs.rs, programs/compressed-token/program/src/compressed_token/transfer2/compression/*
Introduced MAX_COMPRESSIONS and replaced fixed 32-array types with [...; MAX_COMPRESSIONS]; added runtime guard and adjusted related signatures.
Removed public process_compression_top_up export
programs/compressed-token/program/src/compressed_token/transfer2/compression/ctoken/compress_or_decompress_ctokens.rs, .../mod.rs
Deleted process_compression_top_up function and narrowed public re-export to compress_or_decompress_ctokens. Call sites pass &mut None where applicable.
Centralized error conversion
programs/compressed-token/program/src/shared/convert_program_error.rs, programs/compressed-token/program/src/shared/mod.rs
Added convert_token_error, convert_pinocchio_token_error, and internal SPL-code mapping; re-exported converters from shared. Replaced many ProgramError::Custom(...) mappings with converters.
Error enum expansions
programs/compressed-token/anchor/src/lib.rs
Added many SPL-compatible ErrorCode variants and inline offset comments.
CToken approve/burn/mint_to top‑up adjustments
programs/compressed-token/program/src/ctoken/approve_revoke.rs, .../burn.rs, .../mint_to.rs
Switched to centralized error converters, adjusted payer handling to Option<&AccountInfo>, added missing-payer checks, and updated account indexing patterns.
Tests & new test suites
programs/compressed-token/program/tests/*
Updated imports to new module layout; added check_extensions.rs, mint_validation.rs; removed CreateSplMint tests/usages.
Docs & CLAUDE.md / INSTRUCTIONS reorganized
programs/compressed-token/program/docs/*, programs/compressed-token/program/CLAUDE.md
Large documentation reorganization to grouped hierarchies, updated file path references and instruction docs to match code moves; no behavioral changes in docs-only edits.

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
participant Program as Compressed-Token Program
participant Shared as shared::compressible_top_up
participant Info as light_compressible::CompressionInfo
participant Sysvars as Rent/Clock Sysvars
participant Payer as Optional Payer Account

Note right of Program: instruction (mint_to/approve/ctoken flow)
Program->>Shared: calculate_and_execute_compressible_top_ups(ctoken, cmint, payer_opt, max_top_up)
Shared->>Info: process_compression_top_up<T:CalculateTopUp>(compression, account_info, current_slot, transfer_amount, lamports_budget, rent_opt)
alt Rent/Clock not initialized
Shared->>Sysvars: load Rent & Clock
Sysvars-->>Shared: rent_info, current_slot
end
Shared->>Info: compression.calculate_top_up_lamports(num_bytes, current_slot, current_lamports, rent_exemption)
Info-->>Shared: top_up_amount
alt top_up_amount > 0
Shared->>Payer: request lamports transfer (if payer present)
Payer-->>Shared: lamports moved / error
end
Shared-->>Program: Result<(), ProgramError>

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • sergeytimoshin
  • SwenSchaeferjohann

Poem

A trait to tally lamports due,
Helpers call and paths move through.
Modules shifted, errors tamed,
Top‑ups neat and code renamed.
Cheers to layers clean and new! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'chore: ctoken extensions cleanup' is vague and does not accurately reflect the substantial architectural refactoring performed in this PR. Consider a more descriptive title such as: 'refactor: reorganize ctoken and compressed_token modules with extension validation and top-up trait'. This better captures the major structural changes, new validation logic, and trait introductions.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 70.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jorrit/chore-ctoken-extensions-cleanup

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13d992b and 4f2c674.

⛔ Files ignored due to path filters (2)
  • js/compressed-token/tests/e2e/unwrap.test.ts is excluded by none and included by none
  • js/compressed-token/tests/unit/layout-mint-action.test.ts is excluded by none and included by none
📒 Files selected for processing (1)
  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
🧰 Additional context used
📓 Path-based instructions (2)
programs/compressed-token/program/src/**/*.rs

📄 CodeRabbit inference engine (programs/compressed-token/program/CLAUDE.md)

programs/compressed-token/program/src/**/*.rs: Owner validation logic must be implemented using utilities from src/shared/owner_validation.rs
Safe lamport transfer operations must use helpers from src/shared/transfer_lamports.rs
Rent top-up calculations for compressible accounts must use utilities from src/shared/compressible_top_up.rs
Token data handling must use utilities from src/shared/token_input.rs and src/shared/token_output.rs
Extension handling must use validation and processing utilities from src/extensions/ module, including check_mint_extensions.rs, token_metadata.rs, and processor.rs
PDA account creation and validation must use utilities from src/shared/create_pda_account.rs
Token account initialization with extensions must use utilities from src/shared/initialize_ctoken_account.rs

Files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
programs/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

programs/**/*.rs: Programs must depend only on program-libs and external crates (except devenv feature)
Programs must not depend on other programs without devenv feature

Files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
🧠 Learnings (34)
📓 Common learnings
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/program-libs/ctoken-interface/src/state/ctoken/**/*.rs : CToken account layout must match SPL token account layout, with a custom extension named Compressible for rent management
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: When working with ctoken accounts that have the compressible extension (rent management), you must read and comply with documentation in `program-libs/compressible/docs/` including RENT.md, CONFIG_ACCOUNT.md, and SOLANA_RENT.md
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-tests/sdk-ctoken-test/README.md:0-0
Timestamp: 2025-12-07T03:17:28.803Z
Learning: Use compressible token account extensions that allow accounts to be compressed back into compressed state with rent payment mechanisms
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: The program is a compressed token implementation similar to spl-token that supports both compressed token accounts (TokenData) and decompressed ctoken solana accounts (CompressedToken) with compatible account layouts
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/program-libs/ctoken-interface/src/state/mint/**/*.rs : Compressed mint accounts (cmints) must support exactly one extension: TokenMetadata
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/src/**/*.rs : Token account initialization with extensions must use utilities from `src/shared/initialize_ctoken_account.rs`
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/src/transfer2/**/*.rs : Transfer2 instruction (discriminator: 101) must support Compress, Decompress, and CompressAndClose operations with multi-mint support and sum checks
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/docs/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:27.323Z
Learning: Applies to programs/compressed-token/program/docs/**/T22_VS_CTOKEN_COMPARISON.md : Token-2022 vs ctoken extension behavior comparison should be documented in a dedicated `T22_VS_CTOKEN_COMPARISON.md` file
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-tests/sdk-ctoken-test/README.md:0-0
Timestamp: 2025-12-07T03:17:28.803Z
Learning: Implement all 8 compressed token instructions: create_cmint, mint_to_ctoken, create_token_account_invoke, create_token_account_invoke_signed, create_ata_invoke, create_ata_invoke_signed, transfer_interface_invoke, and transfer_interface_invoke_signed
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/docs/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:27.323Z
Learning: Applies to programs/compressed-token/program/docs/**/EXTENSIONS.md : Token-2022 extension validation should be documented in a dedicated `EXTENSIONS.md` file
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/src/**/*.rs : Rent top-up calculations for compressible accounts must use utilities from `src/shared/compressible_top_up.rs`
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-libs/macros/src/compressible/README.md:0-0
Timestamp: 2025-11-24T17:55:17.323Z
Learning: Applies to sdk-libs/macros/src/compressible/**/decompress_context.rs : Decompression trait implementation (`DecompressContext`) with account accessors, PDA/token separation logic, and token processing delegation should be in `decompress_context.rs`
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-libs/macros/src/compressible/README.md:0-0
Timestamp: 2025-11-24T17:55:17.323Z
Learning: Applies to sdk-libs/macros/src/compressible/**/traits.rs : Core trait implementations (`HasCompressionInfo`, `CompressAs`, `Compressible`) should be defined in `traits.rs`
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-libs/macros/src/compressible/README.md:0-0
Timestamp: 2025-11-24T17:55:17.323Z
Learning: Applies to sdk-libs/macros/src/compressible/**/instructions.rs : Compress/decompress instruction handlers and context struct generation should be implemented in `instructions.rs`, with compress using PDA-only and decompress supporting full PDA + ctoken
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-libs/macros/src/compressible/README.md:0-0
Timestamp: 2025-11-24T17:55:17.323Z
Learning: Applies to sdk-libs/macros/src/compressible/**/pack_unpack.rs : Pubkey compression logic and `PackedXxx` struct generation with Pack/Unpack trait implementations should be in `pack_unpack.rs`
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/README.md:0-0
Timestamp: 2025-11-24T17:54:38.537Z
Learning: Implement compressed token program interfaces for third-party token creation and usage on Solana using ZK Compression
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/src/transfer2/**/*.rs : Transfer2 instruction (discriminator: 101) must support Compress, Decompress, and CompressAndClose operations with multi-mint support and sum checks

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/src/**/*.rs : Rent top-up calculations for compressible accounts must use utilities from `src/shared/compressible_top_up.rs`

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/src/transfer2/compression/**/*.rs : Rent authority can only compress accounts when `is_compressible()` returns true

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-11-24T18:02:15.670Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/registry/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:02:15.670Z
Learning: Applies to programs/registry/src/account_compression_cpi/mod.rs : Export new wrapper modules in `account_compression_cpi/mod.rs` using `pub mod new_operation;` and `pub use new_operation::*;`, then import in `lib.rs`

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-11-24T17:55:17.323Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-libs/macros/src/compressible/README.md:0-0
Timestamp: 2025-11-24T17:55:17.323Z
Learning: Applies to sdk-libs/macros/src/compressible/**/decompress_context.rs : Decompression trait implementation (`DecompressContext`) with account accessors, PDA/token separation logic, and token processing delegation should be in `decompress_context.rs`

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-11-24T17:55:17.323Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-libs/macros/src/compressible/README.md:0-0
Timestamp: 2025-11-24T17:55:17.323Z
Learning: Applies to sdk-libs/macros/src/compressible/**/instructions.rs : Compress/decompress instruction handlers and context struct generation should be implemented in `instructions.rs`, with compress using PDA-only and decompress supporting full PDA + ctoken

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/program-libs/ctoken-interface/src/state/mint/**/*.rs : Compressed mint accounts (cmints) must support exactly one extension: TokenMetadata

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-11-24T17:55:17.323Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-libs/macros/src/compressible/README.md:0-0
Timestamp: 2025-11-24T17:55:17.323Z
Learning: Applies to sdk-libs/macros/src/compressible/**/pack_unpack.rs : Pubkey compression logic and `PackedXxx` struct generation with Pack/Unpack trait implementations should be in `pack_unpack.rs`

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-11-24T17:55:17.323Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-libs/macros/src/compressible/README.md:0-0
Timestamp: 2025-11-24T17:55:17.323Z
Learning: Applies to sdk-libs/macros/src/compressible/**/traits.rs : Core trait implementations (`HasCompressionInfo`, `CompressAs`, `Compressible`) should be defined in `traits.rs`

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/program-libs/ctoken-interface/src/state/ctoken/**/*.rs : CToken account layout must match SPL token account layout, with a custom extension named Compressible for rent management

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-12-06T00:49:21.983Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/compressible/CLAUDE.md:0-0
Timestamp: 2025-12-06T00:49:21.983Z
Learning: Applies to program-libs/compressible/src/error.rs : Error types must use numeric codes in the 19xxx range for CToken-specific errors

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-12-07T18:09:57.240Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/compressible/docs/CONFIG_ACCOUNT.md:0-0
Timestamp: 2025-12-07T18:09:57.240Z
Learning: Applies to program-libs/compressible/docs/**/*.rs : CompressibleConfig account size must be exactly 256 bytes including the 8-byte discriminator to maintain compatibility with Light Protocol Solana programs

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-11-24T18:00:36.663Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/compressible/docs/RENT.md:0-0
Timestamp: 2025-11-24T18:00:36.663Z
Learning: Applies to program-libs/compressible/docs/**/*rent*.rs : Implement compression cost and incentive values as constants: COMPRESSION_COST = 10,000 lamports, COMPRESSION_INCENTIVE = 1,000 lamports.

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-11-24T18:00:21.501Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/compressible/docs/ERRORS.md:0-0
Timestamp: 2025-11-24T18:00:21.501Z
Learning: Applies to program-libs/compressible/docs/program-libs/compressible/src/**/*.rs : InvalidState (Error Code 19002): For account creation, ensure CompressibleConfig state is `Active` (1). For other operations (claim, withdraw, compress & close), ensure config state is not `Inactive` (0). Validate config state using `config.validate_active()` for creation and `config.validate_not_inactive()` for other operations.

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-11-24T17:58:35.079Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/batched-merkle-tree/docs/UPDATE_FROM_OUTPUT_QUEUE.md:0-0
Timestamp: 2025-11-24T17:58:35.079Z
Learning: Applies to program-libs/batched-merkle-tree/docs/src/merkle_tree.rs : Verify tree capacity constraint before tree update: `tree.next_index + zkp_batch_size <= tree_capacity`; error with `TreeIsFull` (14310) if violated

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/src/mint_action/**/*.rs : MintAction instruction (discriminator: 103) must support 9 action types: CreateCompressedMint, MintTo, UpdateMintAuthority, UpdateFreezeAuthority, CreateSplMint, MintToCToken, UpdateMetadataField, UpdateMetadataAuthority, RemoveMetadataKey

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-11-24T18:00:36.663Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/compressible/docs/RENT.md:0-0
Timestamp: 2025-11-24T18:00:36.663Z
Learning: Applies to program-libs/compressible/docs/**/*rent*.rs : RentConfig struct must be exactly 8 bytes and contain: base_rent (u16), compression_cost (u16), lamports_per_byte_per_epoch (u8), and 3 bytes of padding.

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: All account structures (TokenData, CToken, CompressedMint, extensions) must be defined in the `program-libs/ctoken-interface/` crate (light-ctoken-interface) rather than in program logic to enable SDK imports without pulling in program dependencies

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/src/transfer/default.rs : CTokenTransfer instruction (discriminator: 3) must support transfers between decompressed accounts only

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/src/**/*.rs : Token account initialization with extensions must use utilities from `src/shared/initialize_ctoken_account.rs`

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: The program is a compressed token implementation similar to spl-token that supports both compressed token accounts (TokenData) and decompressed ctoken solana accounts (CompressedToken) with compatible account layouts

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/{programs/compressed-token/anchor/src/lib.rs,program-libs/ctoken-interface/src/error.rs} : Custom error codes must be defined in `programs/compressed-token/anchor/src/lib.rs` in the `anchor_compressed_token::ErrorCode` enum, and CToken-specific errors in `program-libs/ctoken-interface/src/error.rs` in the `CTokenError` enum

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/src/**/*.rs : Safe lamport transfer operations must use helpers from `src/shared/transfer_lamports.rs`

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-11-24T18:02:15.670Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/registry/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:02:15.670Z
Learning: Applies to programs/registry/src/account_compression_cpi/*.rs : Create wrapper instruction module at `src/account_compression_cpi/new_operation.rs` with `NewOperationContext` struct defining required accounts

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-11-24T17:56:50.011Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/batched-merkle-tree/docs/INITIALIZE_STATE_TREE.md:0-0
Timestamp: 2025-11-24T17:56:50.011Z
Learning: Applies to program-libs/batched-merkle-tree/docs/src/initialize_state_tree.rs : Return error `AccountError::InvalidAccountSize` (error code 12006) when account data length doesn't match calculated size requirements

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-12-07T03:17:28.803Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-tests/sdk-ctoken-test/README.md:0-0
Timestamp: 2025-12-07T03:17:28.803Z
Learning: Implement all 8 compressed token instructions: create_cmint, mint_to_ctoken, create_token_account_invoke, create_token_account_invoke_signed, create_ata_invoke, create_ata_invoke_signed, transfer_interface_invoke, and transfer_interface_invoke_signed

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Compressed token accounts must support compression to spl tokens and decompression back, with spl tokens serving as decompressed representations

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-12-07T03:17:28.803Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-tests/sdk-ctoken-test/README.md:0-0
Timestamp: 2025-12-07T03:17:28.803Z
Learning: Use compressible token account extensions that allow accounts to be compressed back into compressed state with rent payment mechanisms

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-11-24T17:54:38.537Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/README.md:0-0
Timestamp: 2025-11-24T17:54:38.537Z
Learning: Implement compressed token program interfaces for third-party token creation and usage on Solana using ZK Compression

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: When working with ctoken accounts that have the compressible extension (rent management), you must read and comply with documentation in `program-libs/compressible/docs/` including RENT.md, CONFIG_ACCOUNT.md, and SOLANA_RENT.md

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:27.323Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/docs/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:27.323Z
Learning: Applies to programs/compressed-token/program/docs/**/RESTRICTED_T22_EXTENSIONS.md : Restricted SPL Token-2022 extension behavior should be documented in a dedicated `RESTRICTED_T22_EXTENSIONS.md` file

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2026-01-05T19:53:20.702Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-05T19:53:20.702Z
Learning: Applies to programs/compressed-token/program/src/**/*.rs : Extension handling must use validation and processing utilities from `src/extensions/` module, including check_mint_extensions.rs, token_metadata.rs, and processor.rs

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
📚 Learning: 2025-12-06T00:49:21.983Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/compressible/CLAUDE.md:0-0
Timestamp: 2025-12-06T00:49:21.983Z
Learning: Applies to program-libs/compressible/src/config.rs : Implement PDA derivation methods (`derive_pda`, `derive_v1_config_pda`) on CompressibleConfig for account discovery

Applied to files:

  • programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: Test program-libs-fast
  • GitHub Check: Test batched-merkle-tree-simulate
  • GitHub Check: Test program-libs-slow
  • GitHub Check: cli-v1
  • GitHub Check: Forester e2e test
  • GitHub Check: stateless-js-v1
  • GitHub Check: stateless-js-v2
  • GitHub Check: programs (account-compression-and-registry, ["cargo-test-sbf -p account-compression-test", "cargo...
  • GitHub Check: programs (system-cpi-test-v2-functional-account-infos, ["cargo-test-sbf -p system-cpi-v2-test -- ...
  • GitHub Check: programs (light-system-program-address, ["cargo-test-sbf -p system-test -- test_with_address", "c...
  • GitHub Check: programs (system-cpi-test, ["cargo-test-sbf -p system-cpi-test", "cargo test -p light-system-prog...
  • GitHub Check: programs (system-cpi-test-v2-functional-read-only, ["cargo-test-sbf -p system-cpi-v2-test -- func...
  • GitHub Check: programs (compressed-token-and-e2e, ["cargo test -p light-compressed-token", "cargo-test-sbf -p c...
🔇 Additional comments (3)
programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs (3)

50-50: Good use of named constant in function signature.

Replacing the hardcoded 32 with MAX_COMPRESSIONS improves maintainability and makes the constraint explicit.


53-60: Critical off-by-one error has been correctly fixed.

The validation now properly uses > instead of >=, allowing exactly MAX_COMPRESSIONS (32) compressions as intended. The error message "maximum {MAX_COMPRESSIONS} allowed" now aligns with the implementation.


126-129: Comment accurately describes the restriction flag propagation.

The updated comment correctly explains that the is_restricted flag is propagated to enable proper handling of SPL Token-2022 accounts with restricted extensions. This aligns with the requirement that restricted T22 extensions require compressible token accounts.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ananas-block ananas-block force-pushed the jorrit/chore-ctoken-extensions-cleanup branch from 8ecc95a to 34f5c48 Compare January 6, 2026 00:33
@ananas-block ananas-block marked this pull request as draft January 6, 2026 00:42
@ananas-block ananas-block marked this pull request as ready for review January 7, 2026 01:14
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
programs/compressed-token/program/tests/multi_sum_check.rs (1)

1-20: Test location should be moved to program-tests.

Per the coding guidelines, integration tests for programs must be located in program-tests, not in the programs crate's tests/ directory. The tests/ directory tests are compiled as integration tests by Rust convention, and this comprehensive multi-mint sum check test would be better organized in the dedicated program-tests workspace.

This is a structural improvement worth addressing as part of a broader test organization refactoring, though it's outside the scope of this PR's module reorganization.

programs/compressed-token/program/src/shared/owner_validation.rs (1)

80-81: Pre-existing discrepancy: docstring mentions "account delegate" but implementation doesn't check it.

The docstring on line 81 states the function "Allows owner, account delegate, or permanent delegate" but the implementation only checks owner (line 98) and permanent delegate (lines 103-108). The error comment on line 111 also references "account delegate" despite no such check existing.

This is a pre-existing issue (not introduced by this PR), but worth noting for cleanup:

  1. Either add the missing account delegate check if that functionality is intended, or
  2. Update the docstring and error comment to reflect actual behavior
🔎 If account delegate check is not needed, update comments
-/// Verify and update token account authority using zero-copy compressed token format.
-/// Allows owner, account delegate, or permanent delegate (from mint) to authorize compression operations.
+/// Verify token account authority using zero-copy compressed token format.
+/// Allows owner or permanent delegate (from mint) to authorize compression operations.
 #[profile]
 pub fn check_ctoken_owner(

And on line 111:

-    // Authority is neither owner, account delegate, nor permanent delegate
+    // Authority is neither owner nor permanent delegate
     Err(ErrorCode::OwnerMismatch.into())

Also applies to: 111-112

sdk-libs/token-client/src/actions/mint_action.rs (1)

52-57: Consider adding validation that mint_signer matches the mint seed.

While the on-chain program will reject invalid signers, adding client-side validation would improve the developer experience by catching errors earlier rather than failing during transaction execution.

🔎 Proposed validation
 // Add mint signer if needed for create_mint or DecompressMint
 if let Some(signer) = mint_signer {
+    // Validate mint_signer matches mint_seed
+    if signer.pubkey() != params.mint_seed {
+        return Err(RpcError::CustomError(
+            "Mint signer pubkey does not match params mint_seed".to_string(),
+        ));
+    }
+
     if !signers.iter().any(|s| s.pubkey() == signer.pubkey()) {
         signers.push(signer);
     }
 }
programs/compressed-token/program/src/compressed_token/mint_action/actions/update_metadata.rs (1)

78-98: TODO: Adding new metadata keys is not yet implemented.

The commented-out code (lines 88-94) and the error return (line 95) indicate that adding new keys to additional_metadata is intentionally disabled. The error message could be more specific - currently returns MintActionUnsupportedOperation which is generic.

Consider filing an issue to track SPL Token-2022 compatibility for adding new metadata keys, or updating the error variant to something like MintActionAddingMetadataKeysNotSupported for clarity.

Would you like me to open an issue to track implementing the "add new metadata key" functionality for SPL Token-2022 compatibility?

programs/compressed-token/program/src/compressed_token/transfer2/accounts.rs (1)

140-156: Tree account extraction relies on magic bytes heuristic.

The extract_tree_accounts function uses a hardcoded byte pattern [9, 44, 54, 236, 34, 245, 23, 131] (line 151) to identify accounts owned by the account compression program. While the comment explains this is a heuristic, consider adding a constant with documentation explaining these are the first 8 bytes of the account compression program ID for better maintainability.

🔎 Suggested improvement
/// First 8 bytes of the account compression program ID
/// Used as a heuristic to identify tree/queue accounts
const ACCOUNT_COMPRESSION_PROGRAM_PREFIX: [u8; 8] = [9, 44, 54, 236, 34, 245, 23, 131];

Then use ACCOUNT_COMPRESSION_PROGRAM_PREFIX in the comparison.

programs/compressed-token/program/src/ctoken/approve_revoke.rs (1)

107-149: Clarify the lamports_budget parameter usage.

The function passes &mut 0 for lamports_budget to process_compression_top_up, which based on the signature in compressible_top_up.rs would be modified by the function. However, you're passing a literal 0 which creates a temporary. This works because you're checking max_top_up separately, but the intent is unclear.

Looking at the process_compression_top_up function, it uses lamports_budget.saturating_sub(*transfer_amount). With &mut 0, this would underflow-protect to 0, but the caller then ignores the budget check entirely and only uses max_top_up.

Consider either:

  1. Adding a comment explaining why the budget parameter is ignored here
  2. Or using the budget parameter for the max_top_up check instead of duplicating logic
programs/compressed-token/program/src/compressed_token/transfer2/token_inputs.rs (1)

15-30: Add bounds validation for compression_index before array access.

At lines 55-60, compression_index (a u8 from user-provided TLV data) is cast to usize and used directly to index compression_to_input[idx] without bounds validation. Since MAX_COMPRESSIONS = 32 but u8 ranges from 0-255, malicious input with compression_index >= 32 will cause an out-of-bounds panic instead of a proper error.

Add a bounds check:

let idx = co.compression_index as usize;
if idx >= MAX_COMPRESSIONS {
    return Err(ProgramError::InvalidInstructionData);
}
🤖 Fix all issues with AI agents
In @programs/compressed-token/anchor/src/lib.rs:
- Around line 249-550: Several ErrorCode enum variants lack #[msg(...)]
attributes which makes runtime logs less descriptive; add concise #[msg("...")]
attributes to the missing variants (for example: InvalidDelegateIndex,
TokenPoolPdaUndefined, NoInputTokenAccountsProvided, NoInputsProvided,
InvalidTokenPoolPda, TooManyMintToRecipients, MintIsNone, InvalidExtensionType,
etc.) within the ErrorCode enum so each variant has a human-readable message;
ensure messages are short, unique, and placed immediately above the
corresponding variant while preserving the existing inline numeric comments
(e.g., // 6020) for easy cross-checking.

In @programs/compressed-token/program/CLAUDE.md:
- Around line 118-139: Update the fenced code block that currently starts with
``` to include a language specifier (e.g., change ``` to ```text) so the
directory tree is rendered as plain text; edit the Markdown block containing the
src/ tree (the triple-backtick fenced block) to begin with ```text to satisfy
the linter.

In @programs/compressed-token/program/docs/CLAUDE.md:
- Around line 38-39: Insert a single blank line immediately before the "##
Navigation Tips" heading in the Markdown so the heading is separated from the
preceding paragraph/element; simply add one empty line above the "## Navigation
Tips" line to satisfy Markdown rendering best practices.

In @programs/compressed-token/program/docs/compressed_token/MINT_ACTION.md:
- Around line 73-74: The markdown list has inconsistent indentation: the line "-
(signer) - required if create_mint is Some or DecompressMint action present"
currently has leading spaces; remove the extra spaces so it is a top-level list
item (no leading spaces) and ensure the following line "- PDA seed for SPL mint
creation (seeds from compressed mint randomness)" matches the same top-level
indentation; update the two list items in MINT_ACTION.md to use consistent
zero-space top-level hyphen indentation.

In @programs/compressed-token/program/docs/ctoken/CREATE.md:
- Around line 89-91: Reword the awkward phrase in section "4.3.1" so it no
longer uses the redundant adverb; change "Validate compression_only is only set
for mints with restricted extensions" to something like "Validate
compression_only is set exclusively for mints with restricted extensions"
(preserving the rule and the ErrorCode::CompressionOnlyNotAllowed semantics for
cases where compression_only != 0 and the mint lacks restricted extensions).

In @programs/compressed-token/program/docs/ctoken/MINT_TO.md:
- Line 16: The documented line range in MINT_TO.md incorrectly points to lines
10-47; update the reference to accurately cover the instruction data parsing in
programs/compressed-token/program/src/ctoken/mint_to.rs (which spans
approximately lines 12-49 and includes the full max_top_up match), either by
changing the range to "lines 12-49" or removing explicit line numbers entirely
and referencing only the file path and relevant symbols (e.g., the instruction
parsing block and the max_top_up match) to make the doc resilient to refactors.

In @programs/compressed-token/program/docs/ctoken/TRANSFER.md:
- Line 5: Update the path reference in TRANSFER.md to use an absolute
repository-root path by prepending the missing prefix so it reads
programs/compressed-token/program/src/ctoken/transfer/default.rs (replace the
current relative/missing-prefix path with this exact absolute path string).

In @programs/compressed-token/program/docs/EXTENSIONS.md:
- Line 139: The documentation currently points to the call site for
validate_permanent_delegate() instead of its definition; update the
EXTENSIONS.md entry to reference the actual definition of
validate_permanent_delegate() (not its usage inside validate_sender()) so the
doc links to the function implementation rather than the call site.

In @programs/compressed-token/program/docs/INSTRUCTIONS.md:
- Around line 84-106: The markdown has linter failures: MD022 (missing blank
line after headings) and MD029 (ordered list numbering style). Fix by inserting
a single blank line after each section heading (e.g., "## Compressed Token
Operations (`compressed_token/`)", "## CToken Operations (`ctoken/`)", "##
Compressible Operations (`compressible/`)", "## Token Pool Operations (root)")
so there is an empty line before the following list, and normalize all ordered
lists to the "1." repeated style (restart numbering at 1 for each section)
instead of continuing global numbering; these changes will satisfy MD022 and
MD029 without altering content.

In
@programs/compressed-token/program/src/compressed_token/mint_action/mint_output.rs:
- Around line 140-168: The top-up failure paths conflate two different errors
into CMintTopUpCalculationFailed; update error handling to distinguish failures
from get_rent_exemption_lamports and from
compressed_mint.compression.calculate_top_up_lamports by mapping each to a
distinct error variant (e.g., CMintRentExemptionCalculationFailed for the rent
lookup and keep CMintTopUpCalculationFailed for the compression calculation) or
by including the underlying error detail when converting; change the map_err on
get_rent_exemption_lamports and on calculate_top_up_lamports accordingly and
adjust the error enum and any callers to handle the new error variant/name
(referencing get_rent_exemption_lamports,
compressed_mint.compression.calculate_top_up_lamports, and
CMintTopUpCalculationFailed).

In
@programs/compressed-token/program/src/compressed_token/transfer2/compression/ctoken/compress_or_decompress_ctokens.rs:
- Around line 79-80: The call sites passing &mut None into
process_compression_top_up should cache the Rent sysvar and pass the cached
Option to avoid repeated Rent::get() calls; create a mutable Option<Rent> (e.g.,
let mut cached_rent: Option<Rent> = None) in compress_or_decompress_ctokens.rs
before performing multiple compression operations, then pass &mut cached_rent to
each process_compression_top_up invocation (the rent parameter in
compressible_top_up.rs) so the sysvar is fetched only once and reused across
subsequent calls.

In @programs/compressed-token/program/src/ctoken/create.rs:
- Around line 92-108: The non-compressible branch currently assumes the
token_account already exists and is owned by the CToken program but doesn't
validate ownership; before calling initialize_ctoken_account(...) add a call to
the shared owner validation utility (use check_token_program_owner(...) from
src/shared/owner_validation.rs) to verify token_account is owned by the CToken
program when compressible_init_data is None, then proceed to call
initialize_ctoken_account with the same CTokenInitConfig; also update the
comment to reflect that unlike SPL initialize_account3, this path expects a
pre-existing CToken-owned account rather than a System-owned uninitialized
account.

In @programs/compressed-token/program/src/lib.rs:
- Line 129: Import process_transfer2 is isolated; move the use
crate::compressed_token::transfer2::processor::process_transfer2 declaration
into the existing block of crate:: imports (the group that contains other use
crate::... statements such as the ones near the top of the file) so all crate::
imports are consolidated and consistently ordered with related symbols like
compressed_token modules.

In @programs/compressed-token/program/src/shared/initialize_ctoken_account.rs:
- Around line 304-313: The code reads decimals via let decimals =
mint_data.get(44) before verifying the mint; move the read so it only happens
after the validity check to avoid accessing mint_data when the mint is invalid.
Specifically, in the function that calls is_valid_mint(owner, &mint_data)?
remove the early let decimals = mint_data.get(44) and instead call
mint_data.get(44) (and handle the Option) immediately before calling
compressible_ext.set_decimals(...), keeping the is_valid_mint(...) check and the
compressible_ext.set_decimals(...) call intact.

In @programs/compressed-token/program/tests/check_extensions.rs:
- Around line 143-152: create_mock_spl_token_mint uses numeric "magic" offsets
(45 and 44) for is_initialized and decimals; replace these with named constants
(e.g., SPL_MINT_OFFSET_IS_INITIALIZED, SPL_MINT_OFFSET_DECIMALS) defined near
the function or module-level and/or add a brief comment linking to the SPL Token
mint layout spec; update create_mock_spl_token_mint to use those constants
instead of raw numbers so the intent is clear and easier to maintain.

In @programs/compressed-token/program/tests/mint_validation.rs:
- Around line 322-357: The test currently executes each case twice: once in the
verbose loop that calls is_valid_mint for printing and then again by calling
run_test_case which asserts; to fix, consolidate to a single loop by either: (a)
change run_test_case to return a boolean/Result indicating success and call
run_test_case from the existing verbose loop to print and accumulate
passed/failed counts and then assert/fail once based on that return, or (b)
remove the final for tc in &test_cases loop and instead call run_test_case
inside the verbose loop (so the assertion runs as you print); update references
to test_cases, is_valid_mint, run_test_case and Expected accordingly so each
case is evaluated and asserted exactly once.

- Add CalculateTopUp trait for generic top-up calculations
- Implement trait for CompressionInfo, ZCompressionInfo, ZCompressionInfoMut
- Unify process_compression_top_up to use shared implementation
- Remove duplicate function from compress_or_decompress_ctokens.rs
- Fix extension ordering in assert_create_token_account (insert at index 0)
- Replace redundant .get().ok_or() with direct indexing after length validation
- Add SAFETY comments documenting the length invariants
- Make payer optional - only required when top-up transfer is needed
- Remove redundant process_extensions function in transfer/default.rs
- Use CTokenError::MissingPayer consistently when payer is required
- Remove CreateSplMint enum variant and CreateSplMintAction struct (never activated)
- Remove create_spl_mint.rs file from ctoken-interface
- Add MAX_COMPRESSIONS constant (32) with meaningful error message
- Remove dead no_output_compressed_accounts field from Transfer2Config
- Update documentation (MINT_ACTION.md, CLAUDE.md, lib.rs)
- Update JS layout to remove CreateSplMint
- Refactor check_extensions tests for better coverage
- Add config state validation (active required) during account parsing
- Change compressible_config field to store parsed CompressibleConfig
- Remove redundant config parsing in decompress_mint action
- Refactor mint_output into serialize_decompressed_mint and serialize_compressed_mint helpers
- Use cmint_decompressed flag directly after process_actions
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
sdk-libs/token-client/src/actions/mint_action.rs (1)

167-167: Consider removing or converting debug output.

The println! statement may be leftover debug code. Consider removing it or converting to proper logging if needed for SDK debugging.

programs/compressed-token/program/src/compressed_token/transfer2/change_account.rs (2)

26-26: Remove debug logging statement.

This debug log statement appears to be leftover debugging code that should be removed before merging. Debug logs in production code create noise and can expose sensitive information.

🔎 Proposed fix
-    anchor_lang::solana_program::log::msg!("inputs {:?}", inputs);

1-1: Remove the unused change_account.rs file entirely.

This file is not declared in the transfer2 module tree (mod.rs), making it unreachable dead code. The "unused" marker is accurate—the functions assign_change_account and process_change_lamports are never called from anywhere in the active codebase. Per YAGNI principles, dead code should be removed rather than maintained.

programs/compressed-token/program/docs/EXTENSIONS.md (1)

250-277: Replace line number references with function names for maintainability.

The documentation accurately describes the validation logic in compress_and_close.rs and decompression flow, but references specific line ranges (e.g., "lines 168-277", "lines 181-188") that have drifted from the actual implementation. The validate_compressed_token_account function spans lines 80-290, and individual validation checks no longer align with documented line numbers.

Since code reorganization has already shifted these line anchors, document by function name instead: reference validate_compressed_token_account() directly and describe what each validation block does (owner checks, amount validation, CompressedOnly extension validation, etc.) rather than relying on line number stability.

programs/compressed-token/program/src/ctoken/freeze_thaw.rs (1)

12-17: Improved error handling consistency.

Replacing manual ProgramError::Custom(...) construction with .map_err(convert_pinocchio_token_error) centralizes the error conversion logic in a shared utility, making the codebase more maintainable.

programs/compressed-token/program/docs/compressed_token/TRANSFER2.md (1)

344-373: Fix TooManyCompressionTransfers documentation: Maximum is 32 transfers, not 40.

The error code documentation in lines 344-373 has been verified against the ErrorCode enum in programs/compressed-token/anchor/src/lib.rs. All 30 error codes and variant names are correct, and the renumbering you noted (e.g., InputsOutOfOrder to 6038, CompressAndCloseDuplicateOutput to 6106) matches the actual enum definition.

However, one discrepancy exists: The TooManyCompressionTransfers error (6095) currently states "Maximum 40 transfers allowed per instruction" in the documentation, but the enum defines it as "Maximum 32 transfers allowed per instruction". Update the documentation to reflect the correct limit of 32.

🤖 Fix all issues with AI agents
In @program-libs/ctoken-interface/src/instructions/extensions/compressible.rs:
- Around line 21-24: The docstring for the compressible extension's field
compression_only is ambiguous about what is prevented; update the comment on the
public struct field compression_only in compressible.rs (the line with "pub
compression_only: u8") to replace "prevents transfers (only decompression
allowed)" with a clearer phrase such as "prevents compressed transfers" or
"restricts to decompression-only mode" so it accurately states that transfers
still occur but compressed outputs are disallowed.

In @program-libs/ctoken-interface/src/state/ctoken/size.rs:
- Around line 10-11: Update the comment in size.rs to list DefaultAccountState
among the restricted extensions so it matches the implementation and docs;
specifically, edit the note that currently lists Pausable, PermanentDelegate,
TransferFee, TransferHook to include DefaultAccountState (reflecting the five
extensions validated in check_mint_extensions.rs and enforced in
initialize_ctoken_account.rs) so the comment aligns with EXTENSIONS.md and the
actual logic.

In @programs/compressed-token/anchor/src/lib.rs:
- Line 291: The enum variant InvalidDelegateIndex is missing an Anchor error
message attribute; add a #[msg("Invalid delegate index")] attribute above
InvalidDelegateIndex and similarly add appropriate #[msg("...")] attributes for
the other variants noted (lines ~291-308, 311-312) so every enum variant has an
explicit Anchor error message; use concise, human-readable messages that mirror
the variant names to keep error reporting consistent with other variants.

In @programs/compressed-token/program/CLAUDE.md:
- Line 141: Add a blank line immediately after each second-level markdown
heading so headings are separated from the following content; specifically
insert a blank line after the "## Compressed Token Operations
(`compressed_token/`)" heading and likewise after the other two flagged "##"
headings in this file to satisfy markdown heading spacing rules.
- Around line 118-139: The fenced code block in CLAUDE.md that shows the src/
directory tree is missing a language specifier; update the opening fence from
``` to ```text (or ```plaintext) so static analysis recognizes it as plain text
(i.e., change the code block header in the directory-listing block to ```text).

In @programs/compressed-token/program/docs/CLAUDE.md:
- Around line 38-42: Add a blank line immediately before the "## Navigation
Tips" heading in CLAUDE.md so the heading is surrounded by blank lines (fixing
MD022); specifically, insert an empty line after the previous line that ends
with "THAW_ACCOUNT.md" so the "## Navigation Tips" heading is separated by a
blank line above it.

In @programs/compressed-token/program/docs/compressed_token/MINT_ACTION.md:
- Around line 72-74: The list under "mint_signer" has inconsistent indentation;
remove the extra leading spaces before the sub-item so the bullet is a direct
child of the list (change the indented "- (signer) - required if create_mint is
Some or DecompressMint action present" to a standard list item aligned with
"mint_signer" bullet), ensuring the "(signer) - required..." line is not
prefixed with additional spaces.

In @programs/compressed-token/program/docs/ctoken/CREATE.md:
- Around line 89-92: Edit the validation description in section "4.3.1" to
remove the duplicated adverb; replace the phrase "Validate compression_only is
only set only for mints with restricted extensions" with a corrected version
such as "Validate compression_only is set only for mints with restricted
extensions" (or "Validate compression_only is only set for mints with restricted
extensions") so the sentence contains a single "only".
- Line 157: Add a brief explanatory paragraph where CREATE.md currently says
"the compression_only marker is part of the Compressible extension" (around the
lines referencing ATAs at 157 and 205-207) that clarifies why Associated Token
Accounts (ATAs) must have compression_only: explain that ATAs are deterministic
PDAs derived from [owner, program_id, mint], and marking them compression_only
prevents those PDAs from being used for unrestricted token transfers for mints
with restricted extensions (Pausable, PermanentDelegate, TransferFee,
TransferHook), ensuring such tokens can only move through controlled
compress/decompress flows rather than ordinary transfer paths.
- Around line 106-120: The documentation must state that mint validation (done
by the is_valid_mint helper used in initialize_ctoken_account) is conditional —
only performed when mint account data is present/initialized; update CREATE.md
to explicitly say "If mint data is present/initialized, validate mint owner and
structure" and list the exact error mappings: ProgramError::IncorrectProgramId
when owner is not SPL Token / Token-2022 / CToken program, and
ProgramError::InvalidAccountData when the mint structure is invalid; ensure this
note appears alongside the existing steps describing mint validation and the
compressible/non-compressible branching.

In @programs/compressed-token/program/docs/EXTENSIONS.md:
- Line 139: Update the outdated line-range for the validate_permanent_delegate()
reference in EXTENSIONS.md: change the existing
`programs/compressed-token/program/src/ctoken/transfer/shared.rs:164-179` entry
to `programs/compressed-token/program/src/ctoken/transfer/shared.rs:195-210` so
the documented location matches the actual definition of
validate_permanent_delegate().

In @programs/compressed-token/program/docs/INSTRUCTIONS.md:
- Around line 84-106: The document lacks blank lines before several section
headings which reduces readability; insert a single blank line immediately
before each of the headings "Compressed Token Operations (`compressed_token/`)",
"CToken Operations (`ctoken/`)", "Compressible Operations (`compressible/`)",
and "Token Pool Operations (root)" so there is visual separation between logical
groups while keeping the existing numbering and content unchanged.

In
@programs/compressed-token/program/src/compressed_token/mint_action/zero_copy_config.rs:
- Around line 88-89: The inline comment above the call to
accounts_config.cmint_is_decompressed() still refers to "source of truth" and
should be updated to match the renamed variable; change the comment to something
like "CMint is decompressed and not closing" (or an equivalent that reflects
decompressed state) so it consistently documents the meaning of
cmint_is_decompressed().

In @programs/compressed-token/program/src/compressed_token/mod.rs:
- Around line 1-2: Add convenient re-exports for commonly used types/functions
from the submodules so consumers can import directly from compressed_token;
specifically re-export MintActionAccounts and check_authority from the
mint_action module (symbols: MintActionAccounts, check_authority) and re-export
compress_or_decompress_ctokens and CTokenCompressionInputs from
transfer2::compression (symbols: compress_or_decompress_ctokens,
CTokenCompressionInputs); keep the existing pub mod declarations and add these
pub use lines in compressed_token/mod.rs to simplify external import paths.

In
@programs/compressed-token/program/src/compressed_token/transfer2/check_extensions.rs:
- Line 118: Fix the minor grammar in the comment "- Decompress: no check it
restores existing state" by adding appropriate punctuation; replace it with
either "- Decompress: no check - it restores existing state" or "- Decompress:
no check; it restores existing state" so the intent is clear.

In
@programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs:
- Line 50: The current validation using compressions.len() >= MAX_COMPRESSIONS
incorrectly prevents using exactly MAX_COMPRESSIONS items; locate the length
check around the compressions validation (where compression_to_input:
&[Option<u8>; MAX_COMPRESSIONS] and MAX_COMPRESSIONS are referenced) and change
the condition to compressions.len() > MAX_COMPRESSIONS so that 0 through
MAX_COMPRESSIONS compressions are allowed, keeping the error message and
constant as-is.

In @programs/compressed-token/program/src/shared/initialize_ctoken_account.rs:
- Around line 304-314: The code reads decimals via mint_data.get(44) and then
unconditionally calls compressible_ext.set_decimals(decimals.copied()), which
can pass None if the byte is missing; update the logic in the block that calls
mint_data.get(44) to defensively handle the Option: explicitly match or if-let
on decimals from mint_data.get(44) and either return an error (e.g.,
ProgramError::InvalidAccountData) or otherwise handle the missing byte before
calling compressible_ext.set_decimals; reference the existing is_valid_mint
check but do not rely on it — ensure mint_data.get(44) is Some(u8) and call
set_decimals with that value only.

└── lib.rs # Entry point and instruction dispatch
```

## Compressed Token Operations (`compressed_token/`)
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add blank lines around headings for markdown compliance.

Static analysis flagged missing blank lines below headings at lines 141, 155, and 170. Add a blank line after each ## heading for consistent markdown formatting.

Also applies to: 155-155, 170-170

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

141-141: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
In @programs/compressed-token/program/CLAUDE.md at line 141, Add a blank line
immediately after each second-level markdown heading so headings are separated
from the following content; specifically insert a blank line after the "##
Compressed Token Operations (`compressed_token/`)" heading and likewise after
the other two flagged "##" headings in this file to satisfy markdown heading
spacing rules.

Comment on lines +1 to +2
pub mod mint_action;
pub mod transfer2;
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Module structure is correct.

The module declarations follow Rust conventions and align with the PR's goal of organizing compressed token instructions under compressed_token/. The public visibility allows these submodules to be accessed from outside the crate.

Optional: Consider adding re-exports for commonly used items to simplify import paths:

pub mod mint_action;
pub mod transfer2;

// Re-export commonly used items for convenience
pub use mint_action::{MintActionAccounts, check_authority};
pub use transfer2::compression::{compress_or_decompress_ctokens, CTokenCompressionInputs};

This would allow users to write use compressed_token::MintActionAccounts instead of use compressed_token::mint_action::accounts::MintActionAccounts, though the current structure is perfectly valid.

🤖 Prompt for AI Agents
In @programs/compressed-token/program/src/compressed_token/mod.rs around lines 1
- 2, Add convenient re-exports for commonly used types/functions from the
submodules so consumers can import directly from compressed_token; specifically
re-export MintActionAccounts and check_authority from the mint_action module
(symbols: MintActionAccounts, check_authority) and re-export
compress_or_decompress_ctokens and CTokenCompressionInputs from
transfer2::compression (symbols: compress_or_decompress_ctokens,
CTokenCompressionInputs); keep the existing pub mod declarations and add these
pub use lines in compressed_token/mod.rs to simplify external import paths.

Comment on lines +17 to +20
check_owner(
&pubkey!("Lighton6oQpVkeewmo2mcPTQQp7kYHr4fWpAgJyEmDX").to_bytes(),
config_account,
)?;
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider extracting the hardcoded program ID to a named constant.

The Light Registry program ID is hardcoded inline. For maintainability and clarity, consider defining this as a named constant (e.g., LIGHT_REGISTRY_PROGRAM_ID) either in this file or imported from a shared location. This makes auditing ownership checks easier and prevents typos if reused elsewhere.

// Example: define at module level or import from a constants module
const LIGHT_REGISTRY_PROGRAM_ID: [u8; 32] = pubkey!("Lighton6oQpVkeewmo2mcPTQQp7kYHr4fWpAgJyEmDX").to_bytes();

@ananas-block ananas-block force-pushed the jorrit/chore-ctoken-extensions-cleanup branch from c642fcb to 13d992b Compare January 7, 2026 01:51
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (12)
sdk-libs/token-client/src/actions/mint_action.rs (1)

167-167: Remove debug println before merge.

Line 167 contains a leftover debug statement that should be removed. SDK client code should not emit unstructured output, and this could expose parameter details during execution.

🔎 Proposed fix
     };
-    println!("params {:?}", params);
     mint_action(rpc, params, authority, payer, mint_signer).await
 }
programs/compressed-token/program/src/compressed_token/transfer2/change_account.rs (2)

26-26: Remove debug logging of transaction inputs.

This debug log exposes the entire inputs structure in transaction logs, which can leak sensitive transaction data including amounts, account indices, and user patterns. Debug logging should be removed or placed behind a development-only feature flag.

🔎 Proposed fix
-    anchor_lang::solana_program::log::msg!("inputs {:?}", inputs);

70-85: Fix the inverted is_compress flag logic in lamport change handling.

The current logic sets is_compress = 0 when input > output and is_compress = 1 when output > input. This is backwards. The system processor (programs/system/src/processor/sol_compression.rs) shows that is_compress = true triggers compress_lamports() and is_compress = false triggers decompress_lamports(). Additionally, the sum check logic (programs/system/src/processor/sum_check.rs) adds lamports to the output sum when is_compress = true (compression) and subtracts when is_compress = false (decompression).

When input lamports exceed output lamports, the excess becomes a change account—a compression operation that should set is_compress = 1. When output exceeds input, a decompression is needed to cover the deficit—which should set is_compress = 0. Swap these values.

programs/compressed-token/program/docs/ctoken/THAW_ACCOUNT.md (1)

45-54: Line 64 contains an incorrect error code mapping.

The documentation states ProgramError::InvalidAccountData (error code: 4), but error code 4 actually maps to ErrorCode::OwnerMismatch—which is already correctly documented on line 60.

Error code 4 cannot appear twice with different variant names. Verify the actual error that should be at line 64: Is this meant to be a different error code (perhaps from pinocchio's generic ProgramError variants), or should this line be removed as a duplicate?

The core implementation is sound—process_thaw_account exists in pinocchio-token-program, convert_pinocchio_token_error correctly maps SPL Token error codes 0-18 to named ErrorCode variants, and codes 3, 13, and 16 all map correctly to their documented counterparts.

programs/compressed-token/program/docs/ctoken/BURN_CHECKED.md (1)

74-96: Correct the CMint deserialization method name on line 74.

The documentation references CompressedMint::zero_copy_at but the actual implementation uses CompressedMint::zero_copy_at_checked. This matters because zero_copy_at_checked adds critical validation (size checking, account type verification, and initialization status) that zero_copy_at alone does not perform. Update line 74 to reference the correct method name.

The remaining helper functions are documented accurately:

  • Line 82: CToken::zero_copy_at_checked()
  • Line 83: token.get_compressible_extension()
  • Line 95: multi_transfer_lamports(payer, &transfers)
  • Lines 77, 86: compression.calculate_top_up_lamports(data_len, current_slot, lamports, rent_exemption)
programs/compressed-token/program/src/compressed_token/transfer2/accounts.rs (1)

140-155: Tree account extraction logic is clear, though relies on magic bytes.

The documentation accurately describes the heuristic: checking if account owner's first 8 bytes match the account-compression program. The magic bytes [9, 44, 54, 236, 34, 245, 23, 131] are the first 8 bytes of the account-compression program ID.

Consider extracting the magic bytes to a named constant for clarity:

🔎 Optional improvement
+const ACCOUNT_COMPRESSION_PROGRAM_PREFIX: [u8; 8] = [9, 44, 54, 236, 34, 245, 23, 131];
+
 pub fn extract_tree_accounts<'info>(
     packed_accounts: &'info ProgramPackedAccounts<'info, AccountInfo>,
 ) -> Vec<&'info Pubkey> {
     let mut tree_accounts = Vec::with_capacity(8);
     for account_info in packed_accounts.accounts {
-        if account_info.owner()[0..8] == [9, 44, 54, 236, 34, 245, 23, 131] {
+        if account_info.owner()[0..8] == ACCOUNT_COMPRESSION_PROGRAM_PREFIX {
             tree_accounts.push(account_info.key());
         }
     }
     tree_accounts
 }
programs/compressed-token/program/src/shared/compressible_top_up.rs (1)

84-90: Dual early-exit conditions are correct but could use a brief comment.

Line 84-86 exits if current_slot == 0 (no compressible accounts processed), and lines 88-90 exit if both transfer amounts are zero. These are distinct conditions:

  • current_slot == 0 means neither account had compression info to process
  • Both amounts zero means compression info existed but no top-up was needed

The logic is sound; a brief inline comment would help future readers understand why both checks exist.

programs/compressed-token/program/docs/compressible/WITHDRAW_FUNDING_POOL.md (1)

1-87: Add working code examples demonstrating instruction construction and account deserialization.

The documentation comprehensively describes the WithdrawFundingPool instruction logic but lacks working code examples required by the coding guidelines. Include:

  • Example showing how to construct the 8-byte instruction data (amount as u64 little-endian)
  • Example deserializing CompressibleConfig with error handling
  • Example validating PDA derivation seeds
  • Example executing the system_instruction::transfer with PDA signer seeds

These examples should include inline comments explaining key validation steps (e.g., authority matching, config state validation).

programs/compressed-token/program/docs/ctoken/CLOSE.md (1)

1-156: Add code examples and clarify optional account handling.

This documentation is comprehensive but lacks working code examples required by coding guidelines. Additionally, the rent_sponsor account description could be clearer.

Suggested improvements:

  1. Add code examples showing:

    • Parsing CToken account with zero-copy validation
    • Extracting and validating compressible extension
    • Calculating lamport distribution for compressible accounts
    • Executing lamport transfers to rent_sponsor and destination
  2. Clarify account descriptions: Line 46 states rent_sponsor is "(optional, required for compressible accounts)". Consider restructuring as: "4. rent_sponsor (conditionally required)" with clear logic: "Required if and only if the CToken account has a Compressible extension (line 56)."

  3. Brief inline comment at lines 125-127 explaining why unsafe owner assignment is necessary to prevent account revival attacks.

programs/compressed-token/program/docs/ctoken/BURN.md (1)

1-134: Add code examples and document the 165-byte magic number.

The BURN instruction documentation is comprehensive but requires code examples per coding guidelines and needs clarification on a magic number.

Issues and improvements:

  1. Missing code examples showing:

    • Constructing both 8-byte (legacy) and 10-byte (with max_top_up) instruction data
    • Deserializing CMint and CToken accounts with zero-copy
    • Accessing compression_info from CMint and Compressible extension from CToken
    • Calculating top-up using the CalculateTopUp trait's calculate_top_up_lamports method
    • Executing multi_transfer_lamports with budget validation
  2. Document the 165-byte constant (line 77): Add an explanatory comment that 165 bytes is the standard SPL Token account data size per the SPL Token specification. This makes the skip logic clearer: accounts with no extensions match the standard size exactly and don't require top-up extension access.

  3. Clarify the lazy-load pattern (lines 72-82): The logic that lazily loads Clock/Rent sysvars only when needed could benefit from inline pseudo-code showing when current_slot==0 triggers sysvar access, to improve readability of the budget validation logic.

programs/compressed-token/program/src/compressed_token/transfer2/token_inputs.rs (1)

55-60: Potential out-of-bounds panic if compression_index >= MAX_COMPRESSIONS.

The compression_index is a u8 (max 255), but the array is sized to MAX_COMPRESSIONS. If a malicious or malformed instruction provides compression_index >= MAX_COMPRESSIONS, the direct array access at line 57 will panic.

Consider adding an explicit bounds check before accessing the array:

🔎 Proposed fix
             if let ZExtensionInstructionData::CompressedOnly(co) = ext {
                 let idx = co.compression_index as usize;
+                if idx >= MAX_COMPRESSIONS {
+                    return Err(CTokenError::CompressionIndexOutOfBounds.into());
+                }
                 // Check uniqueness - error if compression_index already used
                 if compression_to_input[idx].is_some() {
                     return Err(CTokenError::DuplicateCompressionIndex.into());
                 }
                 compression_to_input[idx] = Some(i as u8);
             }
programs/compressed-token/program/src/lib.rs (1)

221-221: Address the TODO: Add discriminator conflict test.

The TODO comment flags a missing test for discriminator conflicts between InstructionType values (SPL Token 3-18 + custom 100+) and anchor instruction discriminators. This test is important to prevent runtime dispatch errors.

Would you like me to generate a test implementation or open a tracking issue for this?

🤖 Fix all issues with AI agents
In @program-libs/ctoken-interface/src/state/ctoken/size.rs:
- Around line 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).

In @programs/compressed-token/program/CLAUDE.md:
- Around line 209-257: The docs lack a note for unknown SPL Token error codes
>18; update the section describing convert_pinocchio_token_error and
convert_token_error to state that any unmapped token error code (greater than
18) falls back to the +6900 offset mapping (i.e., map unknown token error code n
to ProgramError::Custom((6900 + n) as u32)), and also mention that
convert_program_error continues to apply its +6000 offset for system/internal
errors so readers understand the distinct fallback behavior.

In @programs/compressed-token/program/docs/CLAUDE.md:
- Line 11: Add a single blank line immediately before the heading line that
reads "**`INSTRUCTIONS.md`** - Full instruction reference and discriminator
table" so the heading is separated from the preceding content; edit the Markdown
around that heading to insert one newline above it to comply with Markdown
rendering best practices.

In @programs/compressed-token/program/docs/compressed_token/MINT_ACTION.md:
- Around line 73-74: The two list items "(signer) - required if create_mint is
Some or DecompressMint action present" and "PDA seed for SPL mint creation
(seeds from compressed mint randomness)" in MINT_ACTION.md are indented with 3
spaces causing markdown lint failures; remove the leading spaces so each
top-level unordered list item starts at column 0 with the dash directly at the
line start, ensuring consistent top-level list formatting and rerun markdown
lint to confirm.

In @programs/compressed-token/program/docs/compressed_token/TRANSFER2.md:
- Line 348: The docs and code disagree on the transfer limit: lib.rs
(ErrorCode::TooManyCompressionTransfers) reports "Maximum 32 transfers allowed
per instruction" while TRANSFER2.md says 40; update TRANSFER2.md so the
ErrorCode::TooManyCompressionTransfers entry reads "Maximum 32 transfers allowed
per instruction" to match lib.rs (or if the intended limit is 40, instead update
the error string in lib.rs at the ErrorCode::TooManyCompressionTransfers message
to 40); ensure you reference ErrorCode::TooManyCompressionTransfers and the
error message in lib.rs when making the change and then search the repo for
other occurrences to keep the limit consistent.

In @programs/compressed-token/program/docs/ctoken/CREATE.md:
- Around line 89-91: The sentence "4.3.1. Validate compression_only is only set
for mints with restricted extensions" is redundant; change the phrasing to
remove the duplicate "only" (e.g., "Validate compression_only is set only for
mints with restricted extensions" or "Validate compression_only is set for mints
with restricted extensions") while keeping the conditional and error description
(compression_only != 0 and mint has no restricted extensions -> Error:
`ErrorCode::CompressionOnlyNotAllowed`) intact; update the heading text in
CREATE.md where that rule is defined (the "4.3.1" validation line).
- Around line 122-141: The AccountError codes in the CREATE.md errors block are
wrong; update the four AccountError entries to the correct 20000-range values:
change AccountError::AccountOwnedByWrongProgram to error code 20001,
AccountError::AccountNotMutable to 20002, AccountError::InvalidSigner to 20009,
and AccountError::NotEnoughAccountKeys to 20014 (these values come from
program-libs/account-checks/src/error.rs); modify the three corresponding lines
that currently show 12007, 12008, 12015, and 12020 to the new codes so the
documentation matches the real error codes.

In
@programs/compressed-token/program/src/compressed_token/mint_action/mint_output.rs:
- Around line 156-160: Replace the use of ProgramError::NotEnoughAccountKeys
when extracting the fee payer from
validated_accounts.executing.map(...).ok_or(...) with the more descriptive
CTokenError::MissingPayer (converted to ProgramError as needed), i.e., return
Err(CTokenError::MissingPayer.into()) instead of
Err(ProgramError::NotEnoughAccountKeys); apply the same change to the other
identical occurrence that extracts the payer from
validated_accounts.executing.system.fee_payer so both places consistently use
CTokenError::MissingPayer.
- Around line 176-178: The code redundantly calls Rent::get() to compute
required_lamports after a previous call to get_rent_exemption_lamports; instead
reuse the earlier rent value or the computed rent_exemption to derive the new
minimum balance to avoid a second sysvar fetch. Modify the mint output logic
(around get_rent_exemption_lamports, Rent::get(), rent_exemption, required_size,
and required_lamports) so you either store the Rent instance once and use it for
both minimum_balance calls or compute the delta using the previously computed
rent_exemption and sizes to produce required_lamports without a second
Rent::get() call.

In
@programs/compressed-token/program/src/compressed_token/transfer2/check_extensions.rs:
- Line 118: Fix the grammar in the inline comment that reads "- Decompress: no
check it restores existing state" by adding punctuation for clarity (e.g., "-
Decompress: no check - it restores existing state" or "- Decompress: no check;
it restores existing state") so the intent is unambiguous in
check_extensions.rs.

In
@programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs:
- Around line 53-60: The length check is off-by-one: change the condition in the
block that currently uses compressions.len() >= MAX_COMPRESSIONS to
compressions.len() > MAX_COMPRESSIONS so exactly MAX_COMPRESSIONS entries are
allowed; keep the msg! and Err(ErrorCode::TooManyCompressionTransfers.into())
but use the corrected comparison to match the "maximum {MAX_COMPRESSIONS}
allowed" message.

In @programs/compressed-token/program/src/ctoken/mod.rs:
- Around line 10-21: The re-export for the close module uses a nested path
(close::processor::process_close_token_account) while sibling modules export
from their module root (e.g., burn::process_ctoken_burn); either re-export the
symbol at the close module root (add pub use
processor::process_close_token_account in close/mod.rs so you can import as
close::process_close_token_account) or change this file to consistently import
the processor symbol from close::process_close_token_account, and if the nested
organization is intentional add a brief comment in close/mod.rs explaining why
the processor submodule must be exposed directly.

In @programs/compressed-token/program/tests/check_authority.rs:
- Line 3: This test file is an integration test (it imports the public symbol
check_authority from the external crate), so move the test source out of the
program's internal tests and into the top-level integration test suite: relocate
the test file to the program-tests directory as a top-level integration test
file, remove the original from the program's tests, and update the test to be a
standalone integration test (no internal mod) that imports check_authority via
use light_compressed_token::compressed_token::mint_action::check_authority;
ensure the workspace/Cargo configuration already exposes the program crate to
integration tests (no internal test-only hacks needed).

In @programs/compressed-token/program/tests/compress_and_close.rs:
- Around line 7-9: The test file is in the wrong location; move this integration
test from programs/compressed-token/program/tests/ into the project's
program-tests integration test directory, update any module paths if needed, and
run the test suite from the top-level workspace so Cargo picks it up;
specifically, relocate the file that imports
light_compressed_token::compressed_token::transfer2::{accounts::Transfer2Accounts,
compression::ctoken::close_for_compress_and_close} into program-tests (keeping
the same imports and test code), ensure the workspace Cargo.toml includes the
new test target or that program-tests is configured as an integration test
crate, and remove the old file to comply with architectural guidelines.

Comment on lines +10 to +11
/// Note: Compressible extension is required if the T22 mint has restricted extensions
/// (Pausable, PermanentDelegate, TransferFee, TransferHook).
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).

Comment on lines +209 to +257
### Error Conversion Functions (`shared/convert_program_error.rs`)

Two functions exist for converting pinocchio errors to anchor ProgramError:

| Function | Use Case | Error Mapping |
|----------|----------|---------------|
| `convert_pinocchio_token_error` | SPL Token operations via pinocchio_token_program processors | Maps SPL Token error codes (0-18) to named ErrorCode variants |
| `convert_token_error` | Functions returning TokenError directly (e.g., unpack_amount_and_decimals) | Maps SPL Token error codes (0-18) to named ErrorCode variants |
| `convert_program_error` | System program, data access, lamport transfers | Adds +6000 offset to raw error code |

**When to use each:**

```rust
// SPL Token operations - use convert_pinocchio_token_error
process_transfer(accounts, data).map_err(convert_pinocchio_token_error)?;
process_burn(accounts, data).map_err(convert_pinocchio_token_error)?;
process_mint_to(accounts, data).map_err(convert_pinocchio_token_error)?;

// System/internal operations - use convert_program_error
transfer_lamports_via_cpi(...).map_err(convert_program_error)?;
account.try_borrow_mut_data().map_err(convert_program_error)?;

// ErrorCode variants - use ProgramError::from directly
sum_check_multi_mint(...).map_err(ProgramError::from)?;
validate_mint_uniqueness(...).map_err(ProgramError::from)?;
```

**SPL Token Error Code Mapping:**
| SPL Code | ErrorCode Variant | Description |
|----------|-------------------|-------------|
| 0 | NotRentExempt | Lamport balance below rent-exempt threshold |
| 1 | InsufficientFunds | Insufficient funds for the operation |
| 2 | InvalidMint | Invalid mint account |
| 3 | MintMismatch | Account not associated with this Mint |
| 4 | OwnerMismatch | Owner does not match |
| 5 | FixedSupply | Token supply is fixed |
| 6 | AlreadyInUse | Account already in use |
| 7-8 | InvalidNumberOf*Signers | Signer count mismatch |
| 9 | UninitializedState | State is uninitialized |
| 10 | NativeNotSupported | Native tokens not supported |
| 11 | NonNativeHasBalance | Non-native account has balance |
| 12 | InvalidInstruction | Invalid instruction |
| 13 | InvalidState | State is invalid |
| 14 | Overflow | Operation overflowed |
| 15 | AuthorityTypeNotSupported | Authority type not supported |
| 16 | MintHasNoFreezeAuthority | Mint cannot freeze |
| 17 | AccountFrozen | Account is frozen |
| 18 | MintDecimalsMismatch | Decimals mismatch |

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Find convert_program_error.rs file
echo "=== Locating convert_program_error.rs ==="
find . -name "convert_program_error.rs" -type f

# Also check for alternative naming patterns
echo -e "\n=== Searching for error conversion implementations ==="
find programs/compressed-token/program -name "*.rs" | grep -E "(convert|error)" | head -20

Repository: Lightprotocol/light-protocol

Length of output: 435


🏁 Script executed:

#!/bin/bash

# Get file size to determine reading strategy
if [ -f "programs/compressed-token/program/src/shared/convert_program_error.rs" ]; then
  echo "=== File size ==="
  wc -l programs/compressed-token/program/src/shared/convert_program_error.rs
  
  echo -e "\n=== Full file content ==="
  cat -n programs/compressed-token/program/src/shared/convert_program_error.rs
fi

Repository: Lightprotocol/light-protocol

Length of output: 3046


🏁 Script executed:

#!/bin/bash

# Search for SPL Token error definitions in pinocchio or other dependencies
echo "=== Searching for TokenError enum definitions ==="
rg -n "enum TokenError" --type rust

echo -e "\n=== Checking pinocchio_token_program for error codes ==="
rg -n "TokenError::" --type rust -A 2 | head -50

Repository: Lightprotocol/light-protocol

Length of output: 4744


🏁 Script executed:

#!/bin/bash

# Check usage of convert_pinocchio_token_error and convert_token_error in codebase
echo "=== Usages of convert_pinocchio_token_error ==="
rg -n "convert_pinocchio_token_error" --type rust | head -20

echo -e "\n=== Usages of convert_token_error ==="
rg -n "convert_token_error" --type rust | head -20

echo -e "\n=== Usages of convert_program_error ==="
rg -n "convert_program_error" --type rust | head -20

Repository: Lightprotocol/light-protocol

Length of output: 6365


🏁 Script executed:

#!/bin/bash

# Check the actual error code handling for unknown codes
echo "=== Checking pinocchio_token_program error values ==="
rg -n "pinocchio_token_program" programs/compressed-token/program/src/shared/convert_program_error.rs -B 2 -A 2

# Also check for any comments about error offset handling
echo -e "\n=== Verifying convert_spl_token_error_code behavior ==="
sed -n '29,55p' programs/compressed-token/program/src/shared/convert_program_error.rs

Repository: Lightprotocol/light-protocol

Length of output: 1849


Document fallback behavior for SPL Token error codes above 18.

The error conversion documentation accurately reflects the implementation. All three functions work as documented:

  • convert_pinocchio_token_error correctly maps SPL Token codes 0-18 to ErrorCode variants for token operations
  • convert_token_error correctly handles TokenError conversions
  • convert_program_error correctly adds +6000 offset for system program and data access operations
  • The SPL Token error code mapping table (codes 0-18) matches the implementation precisely

Add one clarification: unknown SPL Token error codes above 18 fall back to a +6900 offset strategy (though this shouldn't occur in standard SPL Token operations, which only define errors 0-18).

🤖 Prompt for AI Agents
In @programs/compressed-token/program/CLAUDE.md around lines 209 - 257, The docs
lack a note for unknown SPL Token error codes >18; update the section describing
convert_pinocchio_token_error and convert_token_error to state that any unmapped
token error code (greater than 18) falls back to the +6900 offset mapping (i.e.,
map unknown token error code n to ProgramError::Custom((6900 + n) as u32)), and
also mention that convert_program_error continues to apply its +6000 offset for
system/internal errors so readers understand the distinct fallback behavior.

- **`../CLAUDE.md`** (parent) - Main entry point with summary and instruction index
- **`ACCOUNTS.md`** - Complete account layouts and data structures
- **`EXTENSIONS.md`** - Token-2022 extension validation across ctoken instructions
- **`INSTRUCTIONS.md`** - Full instruction reference and discriminator table
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

Add blank line before heading.

Markdown best practice requires a blank line before headings for proper rendering.

🔎 Proposed fix
 - **`EXTENSIONS.md`** - Token-2022 extension validation across ctoken instructions
+
 - **`INSTRUCTIONS.md`** - Full instruction reference and discriminator table
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

11-11: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
In @programs/compressed-token/program/docs/CLAUDE.md at line 11, Add a single
blank line immediately before the heading line that reads "**`INSTRUCTIONS.md`**
- Full instruction reference and discriminator table" so the heading is
separated from the preceding content; edit the Markdown around that heading to
insert one newline above it to comply with Markdown rendering best practices.

Comment on lines +73 to 74
- (signer) - required if create_mint is Some or DecompressMint action present
- PDA seed for SPL mint creation (seeds from compressed mint randomness)
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

Fix markdown list indentation.

The list item on line 73 has 3 spaces of indentation where 0 is expected for a top-level unordered list item. This causes markdown linting failures.

🔎 Proposed fix
 2. mint_signer
-   - (signer) - required if create_mint is Some or DecompressMint action present
-   - PDA seed for SPL mint creation (seeds from compressed mint randomness)
+- (signer) - required if create_mint is Some or DecompressMint action present
+- PDA seed for SPL mint creation (seeds from compressed mint randomness)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- (signer) - required if create_mint is Some or DecompressMint action present
- PDA seed for SPL mint creation (seeds from compressed mint randomness)
2. mint_signer
- (signer) - required if create_mint is Some or DecompressMint action present
- PDA seed for SPL mint creation (seeds from compressed mint randomness)
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

73-73: Unordered list indentation
Expected: 0; Actual: 3

(MD007, ul-indent)


74-74: Unordered list indentation
Expected: 0; Actual: 3

(MD007, ul-indent)

🤖 Prompt for AI Agents
In @programs/compressed-token/program/docs/compressed_token/MINT_ACTION.md
around lines 73 - 74, The two list items "(signer) - required if create_mint is
Some or DecompressMint action present" and "PDA seed for SPL mint creation
(seeds from compressed mint randomness)" in MINT_ACTION.md are indented with 3
spaces causing markdown lint failures; remove the leading spaces so each
top-level unordered list item starts at column 0 with the dash directly at the
line start, ensuring consistent top-level list formatting and rerun markdown
lint to confirm.

- `ErrorCode::DuplicateMint` (error code: 6102) - Duplicate mint index detected in inputs, outputs, or compressions (same mint referenced by multiple indices or same index used multiple times)
- `ErrorCode::ComputeOutputSumFailed` (error code: 6002) - Output mint not in inputs or compressions
- `ErrorCode::TooManyCompressionTransfers` (error code: 6106) - Too many compression transfers. Maximum 40 transfers allowed per instruction
- `ErrorCode::TooManyCompressionTransfers` (error code: 6095) - Too many compression transfers. Maximum 40 transfers allowed per instruction
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

Documentation says "Maximum 40 transfers" but code says "Maximum 32 transfers".

The error message in lib.rs line 428-429 states "Too many compression transfers. Maximum 32 transfers allowed per instruction", but this documentation says "Maximum 40 transfers allowed per instruction". This discrepancy will confuse developers debugging transfer limits.

🔎 Proposed fix
-- `ErrorCode::TooManyCompressionTransfers` (error code: 6095) - Too many compression transfers. Maximum 40 transfers allowed per instruction
+- `ErrorCode::TooManyCompressionTransfers` (error code: 6095) - Too many compression transfers. Maximum 32 transfers allowed per instruction
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- `ErrorCode::TooManyCompressionTransfers` (error code: 6095) - Too many compression transfers. Maximum 40 transfers allowed per instruction
- `ErrorCode::TooManyCompressionTransfers` (error code: 6095) - Too many compression transfers. Maximum 32 transfers allowed per instruction
🤖 Prompt for AI Agents
In @programs/compressed-token/program/docs/compressed_token/TRANSFER2.md at line
348, The docs and code disagree on the transfer limit: lib.rs
(ErrorCode::TooManyCompressionTransfers) reports "Maximum 32 transfers allowed
per instruction" while TRANSFER2.md says 40; update TRANSFER2.md so the
ErrorCode::TooManyCompressionTransfers entry reads "Maximum 32 transfers allowed
per instruction" to match lib.rs (or if the intended limit is 40, instead update
the error string in lib.rs at the ErrorCode::TooManyCompressionTransfers message
to 40); ensure you reference ErrorCode::TooManyCompressionTransfers and the
error message in lib.rs when making the change and then search the repo for
other occurrences to keep the limit consistent.

Comment on lines +10 to +21
pub use approve_revoke::{
process_ctoken_approve, process_ctoken_approve_checked, process_ctoken_revoke,
};
pub use burn::{process_ctoken_burn, process_ctoken_burn_checked};
pub use close::processor::process_close_token_account;
pub use create::process_create_token_account;
pub use create_ata::{
process_create_associated_token_account, process_create_associated_token_account_idempotent,
};
pub use freeze_thaw::{process_ctoken_freeze_account, process_ctoken_thaw_account};
pub use mint_to::{process_ctoken_mint_to, process_ctoken_mint_to_checked};
pub use transfer::{process_ctoken_transfer, process_ctoken_transfer_checked};
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Minor structural inconsistency in close module re-export.

Line 14 uses a nested path close::processor::process_close_token_account while all other modules export directly from the module root (e.g., burn::process_ctoken_burn).

If the close module has additional internal organization that necessitates this pattern, consider documenting why. Otherwise, consider flattening the export to match the sibling modules for consistency.

🤖 Prompt for AI Agents
In @programs/compressed-token/program/src/ctoken/mod.rs around lines 10 - 21,
The re-export for the close module uses a nested path
(close::processor::process_close_token_account) while sibling modules export
from their module root (e.g., burn::process_ctoken_burn); either re-export the
symbol at the close module root (add pub use
processor::process_close_token_account in close/mod.rs so you can import as
close::process_close_token_account) or change this file to consistently import
the processor symbol from close::process_close_token_account, and if the nested
organization is intentional add a brief comment in close/mod.rs explaining why
the processor submodule must be exposed directly.

Comment on lines +322 to +357
println!(
"\nRunning {} test cases for is_valid_mint:\n",
test_cases.len()
);

let mut passed = 0;
let mut failed = 0;

for tc in &test_cases {
print!(" {:60} ... ", tc.description);
let data = tc.data.to_bytes();
let result = is_valid_mint(tc.owner.pubkey(), &data);

let success = match tc.expected {
Expected::Valid => result.as_ref().map(|v| *v).unwrap_or(false),
Expected::Invalid => result.as_ref().map(|v| !*v).unwrap_or(false),
Expected::IncorrectProgramId => {
result.as_ref().err() == Some(&ProgramError::IncorrectProgramId)
}
};

if success {
println!("ok");
passed += 1;
} else {
println!("FAILED (got {:?})", result);
failed += 1;
}
}

println!("\nResults: {} passed, {} failed\n", passed, failed);

// Now run assertions to fail the test if any failed
for tc in &test_cases {
run_test_case(tc);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider removing duplicate test execution.

As noted in the previous review, each test case is executed twice: once in the verbose loop (lines 330-350) for printing, and again in the assertion loop (lines 355-357). This is redundant and could be simplified by calling run_test_case within the verbose loop.

@ananas-block ananas-block force-pushed the jorrit/chore-ctoken-extensions-cleanup branch from 0f634cd to 8c2e9f1 Compare January 7, 2026 15:57
Change validation from >= to > to fix off-by-one error that prevented
using exactly MAX_COMPRESSIONS compression operations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants