-
Notifications
You must be signed in to change notification settings - Fork 87
chore: ctoken extensions cleanup #2161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughAdded 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
Sequence Diagram(s)mermaid Note right of Program: instruction (mint_to/approve/ctoken flow) Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (2)programs/compressed-token/program/src/**/*.rs📄 CodeRabbit inference engine (programs/compressed-token/program/CLAUDE.md)
Files:
programs/**/*.rs📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (34)📓 Common learnings📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2025-11-24T18:02:15.670ZApplied to files:
📚 Learning: 2025-11-24T17:55:17.323ZApplied to files:
📚 Learning: 2025-11-24T17:55:17.323ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2025-11-24T17:55:17.323ZApplied to files:
📚 Learning: 2025-11-24T17:55:17.323ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2025-12-06T00:49:21.983ZApplied to files:
📚 Learning: 2025-12-07T18:09:57.240ZApplied to files:
📚 Learning: 2025-11-24T18:00:36.663ZApplied to files:
📚 Learning: 2025-11-24T18:00:21.501ZApplied to files:
📚 Learning: 2025-11-24T17:58:35.079ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2025-11-24T18:00:36.663ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2025-11-24T18:02:15.670ZApplied to files:
📚 Learning: 2025-11-24T17:56:50.011ZApplied to files:
📚 Learning: 2025-12-07T03:17:28.803ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2025-12-07T03:17:28.803ZApplied to files:
📚 Learning: 2025-11-24T17:54:38.537ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2026-01-05T19:53:27.323ZApplied to files:
📚 Learning: 2026-01-05T19:53:20.702ZApplied to files:
📚 Learning: 2025-12-06T00:49:21.983ZApplied to files:
⏰ 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)
🔇 Additional comments (3)
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. Comment |
8ecc95a to
34f5c48
Compare
There was a problem hiding this 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 theprogramscrate'stests/directory. Thetests/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:
- Either add the missing account delegate check if that functionality is intended, or
- 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_metadatais intentionally disabled. The error message could be more specific - currently returnsMintActionUnsupportedOperationwhich 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
MintActionAddingMetadataKeysNotSupportedfor 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_accountsfunction 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_PREFIXin the comparison.programs/compressed-token/program/src/ctoken/approve_revoke.rs (1)
107-149: Clarify the lamports_budget parameter usage.The function passes
&mut 0forlamports_budgettoprocess_compression_top_up, which based on the signature incompressible_top_up.rswould be modified by the function. However, you're passing a literal0which creates a temporary. This works because you're checkingmax_top_upseparately, but the intent is unclear.Looking at the
process_compression_top_upfunction, it useslamports_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 usesmax_top_up.Consider either:
- Adding a comment explaining why the budget parameter is ignored here
- 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 forcompression_indexbefore array access.At lines 55-60,
compression_index(au8from user-provided TLV data) is cast tousizeand used directly to indexcompression_to_input[idx]without bounds validation. SinceMAX_COMPRESSIONS = 32butu8ranges from 0-255, malicious input withcompression_index >= 32will 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.
.../program/src/compressed_token/transfer2/compression/ctoken/compress_or_decompress_ctokens.rs
Show resolved
Hide resolved
programs/compressed-token/program/src/shared/initialize_ctoken_account.rs
Outdated
Show resolved
Hide resolved
… compressed_token
- 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
There was a problem hiding this 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 unusedchange_account.rsfile entirely.This file is not declared in the
transfer2module tree (mod.rs), making it unreachable dead code. The "unused" marker is accurate—the functionsassign_change_accountandprocess_change_lamportsare 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.rsand decompression flow, but references specific line ranges (e.g., "lines 168-277", "lines 181-188") that have drifted from the actual implementation. Thevalidate_compressed_token_accountfunction 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
ErrorCodeenum inprograms/compressed-token/anchor/src/lib.rs. All 30 error codes and variant names are correct, and the renumbering you noted (e.g.,InputsOutOfOrderto 6038,CompressAndCloseDuplicateOutputto 6106) matches the actual enum definition.However, one discrepancy exists: The
TooManyCompressionTransferserror (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.
program-libs/ctoken-interface/src/instructions/extensions/compressible.rs
Show resolved
Hide resolved
| └── lib.rs # Entry point and instruction dispatch | ||
| ``` | ||
|
|
||
| ## Compressed Token Operations (`compressed_token/`) |
There was a problem hiding this comment.
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.
| pub mod mint_action; | ||
| pub mod transfer2; |
There was a problem hiding this comment.
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.
programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
Show resolved
Hide resolved
| check_owner( | ||
| &pubkey!("Lighton6oQpVkeewmo2mcPTQQp7kYHr4fWpAgJyEmDX").to_bytes(), | ||
| config_account, | ||
| )?; |
There was a problem hiding this comment.
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();
programs/compressed-token/program/src/shared/initialize_ctoken_account.rs
Outdated
Show resolved
Hide resolved
c642fcb to
13d992b
Compare
There was a problem hiding this 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
inputsstructure 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 invertedis_compressflag logic in lamport change handling.The current logic sets
is_compress = 0wheninput > outputandis_compress = 1whenoutput > input. This is backwards. The system processor (programs/system/src/processor/sol_compression.rs) shows thatis_compress = truetriggerscompress_lamports()andis_compress = falsetriggersdecompress_lamports(). Additionally, the sum check logic (programs/system/src/processor/sum_check.rs) adds lamports to the output sum whenis_compress = true(compression) and subtracts whenis_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 setis_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 toErrorCode::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_accountexists in pinocchio-token-program,convert_pinocchio_token_errorcorrectly 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_atbut the actual implementation usesCompressedMint::zero_copy_at_checked. This matters becausezero_copy_at_checkedadds critical validation (size checking, account type verification, and initialization status) thatzero_copy_atalone 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 == 0means 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:
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
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)."
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:
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
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.
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 ifcompression_index >= MAX_COMPRESSIONS.The
compression_indexis au8(max 255), but the array is sized toMAX_COMPRESSIONS. If a malicious or malformed instruction providescompression_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.
| /// Note: Compressible extension is required if the T22 mint has restricted extensions | ||
| /// (Pausable, PermanentDelegate, TransferFee, TransferHook). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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).
| ### 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 | | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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 -20Repository: 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
fiRepository: 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 -50Repository: 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 -20Repository: 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.rsRepository: 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_errorcorrectly maps SPL Token codes 0-18 to ErrorCode variants for token operationsconvert_token_errorcorrectly handles TokenError conversionsconvert_program_errorcorrectly 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| - (signer) - required if create_mint is Some or DecompressMint action present | ||
| - PDA seed for SPL mint creation (seeds from compressed mint randomness) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| - (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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| - `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.
programs/compressed-token/program/src/compressed_token/transfer2/compression/mod.rs
Outdated
Show resolved
Hide resolved
| 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}; |
There was a problem hiding this comment.
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.
| 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); | ||
| } |
There was a problem hiding this comment.
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.
0f634cd to
8c2e9f1
Compare
Change validation from >= to > to fix off-by-one error that prevented using exactly MAX_COMPRESSIONS compression operations.
Summary
Refactors compressed token program structure and adds validation for restricted T22 extensions.
Changes
Structure
src/ctoken/(decompressed account ops) andsrc/compressed_token/(Merkle tree ops)docs/Token Extensions
MAX_COMPRESSIONSconstant (32) with descriptive errorcheck_extensionsfor transfer2Code Quality
CalculateTopUptraitCreateSplMint,no_output_compressed_accountsmint_outputinto focused helper functionsFixes
Summary by CodeRabbit
New Features
Bug Fixes
Behavior Changes
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.