Skip to content

refactor: sdk, rename CompressedMint -> Mint#2172

Merged
SwenSchaeferjohann merged 2 commits intomainfrom
jorrit/chore-fix-token-sdk
Jan 17, 2026
Merged

refactor: sdk, rename CompressedMint -> Mint#2172
SwenSchaeferjohann merged 2 commits intomainfrom
jorrit/chore-fix-token-sdk

Conversation

@ananas-block
Copy link
Contributor

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

Changes

Type Renames:

  1. CompressedMint -> Mint
  2. CompressedMintMetadata -> MintMetadata
  3. cmint_decompressed -> mint_decompressed

Error Renames:
4. InvalidCMintOwner -> InvalidMintOwner
5. CMintNotInitialized -> MintNotInitialized
6. CMintBorrowFailed -> MintBorrowFailed
7. CMintDeserializationFailed -> MintDeserializationFailed

File Renames:
8. compress_and_close_cmint.rs -> compress_and_close_mint.rs
9. create_cmint.rs -> create_mint.rs
10. decompress_cmint.rs -> decompress_mint.rs

Bug Fix:
11. create_compressed_mint_instruction now uses MintAction with empty actions instead of CreateMint builder (which always added DecompressMint)

Summary by CodeRabbit

  • Breaking Changes
    • Public types, functions, enums and fields renamed from compressed/CMint variants to Mint equivalents; update callers to new names and signatures.
  • New Features
    • Transfer interface with automatic routing between Light and SPL flows.
    • Ownership-detection helper for token account types.
    • Create-mint API expanded with rent and write-top-up controls.
  • Bug Fixes / Errors
    • Added SPL token program mismatch error for clearer diagnostics.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 16, 2026

📝 Walkthrough

Walkthrough

Mass renaming of "CMint"/"CompressedMint" to "Mint" across token-interface, token-sdk, token-client, programs, SDKs, and tests; error variants, instruction payloads/builders, zero-copy state, CPI/account-meta builders, and transfer routing utilities updated to the new Mint-centric names and shapes.

Changes

Cohort / File(s) Summary
Core token-interface errors
program-libs/token-interface/src/error.rs
Renamed CMint error variants → Mint equivalents; updated messages and u32 mappings (18044–18047).
Mint instruction data & builders
program-libs/token-interface/src/instructions/mint_action/{builder.rs,instruction_data.rs,...}
CompressedMint* → Mint* renames; builders now accept MintWithContext / MintInstructionData; action variant CompressAndCloseMint.
Compress & close action module
program-libs/token-interface/src/instructions/mint_action/compress_and_close_mint.rs, .../mod.rs, .../cpi_context.rs, .../decompress_mint.rs
CMint → Mint renames for structs, Z-action types, docs; one doc-only fix in cpi_context.
State: Mint (full & zero-copy)
program-libs/token-interface/src/state/mint/{compressed_mint.rs,zero_copy.rs,top_up.rs}
CompressedMintMint; metadata type/field renames (CompressedMintMetadataMintMetadata, cmint_decompressedmint_decompressed); zero-copy types ZCompressedMint* → ZMint*; helpers/top-up renamed.
Token-interface tests
program-libs/token-interface/tests/*
Tests updated to use Mint/MintMetadata/ZMint and renamed fields; zero-copy/Borsh paths adjusted.
Program mint-action logic
programs/compressed-token/program/src/compressed_token/mint_action/**
All action handlers/processors updated to Mint types, mint_decompressed; ZAction variants renamed; call-site signature updates.
Program IO / serialization / config
programs/compressed-token/program/src/compressed_token/{mint_input.rs,mint_output.rs,zero_copy_config.rs,accounts.rs}
Serialization, output, and zero-copy config functions now operate on Mint/MintConfig; field renames propagated.
Program tests & compressible integration
programs/compressed-token/program/tests/*, programs/compressed-token/program/src/compressible/*, programs/compressed-token/program/src/shared/*
Tests and claim/compress flows updated to Mint naming; top-up helper name swapped to mint_top_up_*.
SDK: token-client instruction/actions
sdk-libs/token-client/src/{instructions,actions}/*
CompressedMintWithContextMintWithContext, CompressedMintInstructionDataMintInstructionData, action flags/params and builders switched to Mint variants.
SDK: token-sdk public APIs
sdk-libs/token-sdk/src/token/*, sdk-libs/token-sdk/src/compressed_token/v2/*
Many public structs/fields renamed (cmintmint), Create/Update compressed-mint APIs renamed → CreateMint/UpdateMint inputs/meta types; CreateMint flow expanded (rent_payment, write_top_up); Decompress/Create CPI structs updated.
Token-sdk transfer & utils
sdk-libs/token-sdk/src/token/transfer_interface.rs, sdk-libs/token-sdk/src/utils.rs
New TransferInterface / TransferInterfaceCpi routing logic (Light ↔ SPL paths) added; new helper is_light_token_owner and is_token_account now delegates.
Token-types & account infos
sdk-libs/token-types/src/*
CreateCompressedMintAccountInfos*CreateMintAccountInfos* rename and helpers; CompressedMintAuthorityTypeMintAuthorityType.
Token-sdk tests, program-test & compressible client
sdk-libs/*, sdk-libs/program-test/*, sdk-libs/compressible-client/*
Public/surface imports updated to Mint variants; instruction builders and client helpers updated to MintWithContext/MintInstructionData; tests adjusted.
Misc / deps
sdk-libs/token-sdk/Cargo.toml, various docs
Removed spl-token-2022 dependency entry; many inline doc/comment terminologies changed CMint→Mint.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client as Client
participant TransferInt as TransferInterface
participant LightProg as LightProgram
participant SplProg as SPLTokenProgram

Client->>TransferInt: request transfer(src,dst,amount,opts)
TransferInt->>TransferInt: determine_transfer_type(src_owner,dst_owner)
alt LightToLight
    TransferInt->>LightProg: build Light transfer instruction
    LightProg-->>Client: return instruction/result
else LightToSpl
    TransferInt->>SplProg: build SPL CPI (TransferChecked)
    SplProg-->>Client: return CPI result
else SplToLight
    TransferInt->>SplProg: build SPL CPI for source
    SplProg-->>TransferInt: CPI result
    TransferInt->>LightProg: build Light-side instruction
    LightProg-->>Client: final result
else SplToSpl
    TransferInt->>SplProg: build SPL TransferChecked (direct)
    SplProg-->>Client: return result
end

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

ai-review

Suggested reviewers

  • sergeytimoshin

Poem

🌱 From CMint to Mint the names align,
Types and errors march in tidy line.
Builders, tests, and zero-copy wake,
Transfers route the paths they take,
A refactor trimmed and feeling fine.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'refactor: sdk, rename CompressedMint -> Mint' accurately reflects the main change across the codebase: a systematic renaming of the CompressedMint type to Mint throughout the SDK.
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-fix-token-sdk

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-fix-token-sdk branch from 52096b0 to b8fbff0 Compare January 16, 2026 16:34
@ananas-block ananas-block marked this pull request as ready for review January 16, 2026 16:34
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: 10

Caution

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

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

164-164: Remove debug println! before merging.

This debug statement shouldn't ship in SDK code. Consider either removing it entirely or, if the logging is valuable for debugging, converting it to a proper tracing/logging call at an appropriate level (e.g., tracing::debug!).

Proposed fix
-    println!("params {:?}", params);

Or if debug logging is desired:

-    println!("params {:?}", params);
+    tracing::debug!(?params, "mint_action_comprehensive params");
sdk-libs/token-sdk/src/token/transfer_interface.rs (1)

265-292: Consider simplifying with_spl_interface signature.

All four parameters are Option<T>, but the function returns an error if any is None. This creates an odd contract where the caller must provide Some for all four anyway.

If the intent is to require all fields when using SPL interface, consider taking concrete types instead:

♻️ Proposed signature simplification
     pub fn with_spl_interface(
         mut self,
-        mint: Option<AccountInfo<'info>>,
-        spl_token_program: Option<AccountInfo<'info>>,
-        spl_interface_pda: Option<AccountInfo<'info>>,
-        spl_interface_pda_bump: Option<u8>,
-    ) -> Result<Self, ProgramError> {
-        let mint =
-            mint.ok_or_else(|| ProgramError::Custom(TokenSdkError::MissingMintAccount.into()))?;
-
-        let spl_token_program = spl_token_program
-            .ok_or_else(|| ProgramError::Custom(TokenSdkError::MissingSplTokenProgram.into()))?;
-
-        let spl_interface_pda = spl_interface_pda
-            .ok_or_else(|| ProgramError::Custom(TokenSdkError::MissingSplInterfacePda.into()))?;
-
-        let spl_interface_pda_bump = spl_interface_pda_bump.ok_or_else(|| {
-            ProgramError::Custom(TokenSdkError::MissingSplInterfacePdaBump.into())
-        })?;
-
+        mint: AccountInfo<'info>,
+        spl_token_program: AccountInfo<'info>,
+        spl_interface_pda: AccountInfo<'info>,
+        spl_interface_pda_bump: u8,
+    ) -> Self {
         self.spl_interface = Some(SplInterfaceCpi {
             mint,
             spl_token_program,
             spl_interface_pda,
             spl_interface_pda_bump,
         });
-        Ok(self)
+        self
     }

If there's a specific reason for the Option parameters (e.g., framework compatibility where accounts may come as Options), please disregard.

sdk-libs/photon-api/src/models/_get_compressed_mint_token_holders_post_request.rs (1)

13-42: Filename inconsistency in auto-generated OpenAPI model code.

The struct GetMintTokenHoldersPostRequest has been renamed, but the file remains named _get_compressed_mint_token_holders_post_request.rs, which creates a mismatch. The module declaration in mod.rs still references _get_compressed_mint_token_holders_post_request, keeping the "compressed" qualifier.

For consistency with the codebase pattern (where filenames reflect struct names in snake_case), consider updating the OpenAPI spec or generator configuration to produce _get_mint_token_holders_post_request.rs and update the corresponding module declarations. This applies if the API spec itself has been updated to remove "compressed" from the operation name.

sdk-libs/photon-api/src/apis/default_api.rs (1)

753-791: Function name should be renamed to match the types and endpoint it uses.

The function is named get_compressed_mint_token_holders_post, but the types and endpoint omit "compressed":

  • Types: GetMintTokenHoldersPostRequest, GetMintTokenHoldersPost200Response, GetMintTokenHoldersPostError
  • Endpoint: /getMintTokenHolders

Other similar functions in this file follow a consistent pattern where the function name matches the type naming—for example, get_compressed_account_post has types like GetCompressedAccountPostRequest. Rename the function to get_mint_token_holders_post to align with the generated types and endpoint. This requires updating one call site in sdk-libs/client/src/indexer/photon_indexer.rs.

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

429-433: Action variant renamed but tracking field still uses cmint naming.

The ZAction::CompressAndCloseMint variant was correctly updated, but the field storing this detection result is still named has_compress_and_close_cmint_action (line 360). For full consistency with the rename, consider also renaming the field to has_compress_and_close_mint_action.

This also applies to other cmint-prefixed fields in AccountsConfig:

  • cmint_decompressed (line 350) → mint_decompressed
  • Methods like cmint_output_decompressed() (line 368) → mint_output_decompressed()
  • needs_cmint_account() (line 383) → needs_mint_account()

If this is intentional to limit scope, that's fine—just noting for future cleanup.

programs/compressed-token/program/src/compressed_token/mint_action/actions/process_actions.rs (1)

146-151: Address naming inconsistency between variant and implementation.

The variant was renamed to ZAction::CompressAndCloseMint but the function, module, and field names still use the old cmint suffix pattern. Update these for consistency:

  • Module: compress_and_close_cmintcompress_and_close_mint
  • Function: process_compress_and_close_cmint_actionprocess_compress_and_close_mint_action
  • Field: has_compress_and_close_cmint_actionhas_compress_and_close_mint_action

Note: the term "CMint" (compressed mint account) will still appear in comments and documentation, which is correct—this refactoring affects only the action handler naming.

sdk-libs/token-sdk/src/compressed_token/v2/update_compressed_mint/account_metas.rs (2)

8-35: Enforce required authority in the config.
The comment says authority is always required; leaving it optional lets callers build invalid metas. Consider making it non-optional (or fail fast when missing).

♻️ Proposed fix (make authority mandatory)
 pub struct UpdateMintMetaConfig {
     pub fee_payer: Option<Pubkey>,
-    pub authority: Option<Pubkey>,
+    pub authority: Pubkey,
     pub in_merkle_tree: Pubkey,
     pub in_output_queue: Pubkey,
     pub out_output_queue: Pubkey,
     pub with_cpi_context: bool,
 }
@@
-    if let Some(authority) = config.authority {
-        metas.push(AccountMeta::new_readonly(authority, true));
-    }
+    metas.push(AccountMeta::new_readonly(config.authority, true));

37-86: with_cpi_context returns incomplete metas.
When true, the function skips required accounts (queues/tree/PDAs), yielding an invalid instruction. Either populate CPI-context accounts or explicitly guard/reject this mode until supported.

programs/compressed-token/program/src/compressed_token/mint_action/actions/compress_and_close_cmint.rs (1)

16-32: Documentation still uses "CMint" terminology while code uses "Mint".

The docstring references "CMint" throughout (lines 16, 20, 21, 22, 24, 27, 31, 32), but the code now uses Mint type and mint_decompressed field. Consider updating the documentation for consistency:

  • Line 16: "CMint Solana account" → "Mint Solana account"
  • Line 20: "CMint doesn't exist" → "Mint doesn't exist"
  • Lines 22-27: Update step descriptions to use "Mint" terminology
  • Line 31: "compress and close a CMint" → "compress and close a Mint"

Alternatively, if "CMint" is intentionally kept for the concept of a "compressible mint account on-chain", document this distinction clearly.

program-libs/token-interface/tests/compressed_mint.rs (1)

49-107: Consider renaming the helper to reflect Mint terminology.

The helper now returns Mint, so a renamed function would avoid confusion in future test edits.

♻️ Suggested rename
-/// Generate a random Mint for testing
-fn generate_random_compressed_mint(rng: &mut impl Rng, with_extensions: bool) -> Mint {
+/// Generate a random Mint for testing
+fn generate_random_mint(rng: &mut impl Rng, with_extensions: bool) -> Mint {
@@
-        let original_mint = generate_random_compressed_mint(&mut rng, false);
+        let original_mint = generate_random_mint(&mut rng, false);
🤖 Fix all issues with AI agents
In `@program-libs/token-interface/src/state/mint/top_up.rs`:
- Around line 11-17: Introduce Mint-prefixed constants and keep the existing
CMINT_* names as deprecated aliases: create new public constants named
MINT_MIN_SIZE_WITH_COMPRESSION and MINT_COMPRESSION_INFO_OFFSET (and any other
CMINT_* constants referenced around the other occurrences) initialized to the
same values currently used, then change the existing CMINT_* declarations to be
deprecated aliases that simply equal the new MINT_* constants (use
#[deprecated(note = "...")] on the CMINT_* declarations). Reference the symbols
CMINT_MIN_SIZE_WITH_COMPRESSION and COMPRESSION_INFO_OFFSET (and the other
CMINT_* symbols mentioned) when adding the new MINT_* constants and making the
old CMINT_* consts forward to them.

In `@program-libs/token-interface/tests/hash_tests.rs`:
- Around line 1-10: Uncomment the entire test module hash_tests and restore the
unit tests exercising Mint::hash(), ensuring signatures and imports match
current types (use light_token_interface::state::{Mint, MintMetadata, BaseMint}
and light_compressed_account::Pubkey); run the tests and fix any compile errors
by updating uses of Option vs zero semantics, authority handling, or renamed
methods/fields referenced in tests (e.g., Mint::hash, Mint::new,
MintMetadata::default), and ensure the randomized 1k stress test compiles and
asserts deterministic, non-zero, and collision-free outputs for varied authority
combinations and boundary values.

In
`@programs/compressed-token/program/src/compressed_token/mint_action/actions/create_mint.rs`:
- Around line 111-115: The error message referencing the old "CMint" name is
inconsistent with the renamed field mint.metadata.mint_decompressed; update the
message in create_mint.rs (where you validate mint.metadata.mint_decompressed !=
0) to use a consistent phrase such as "New mint must not be decompressed" or
"New mint must start without decompressed mint" so the log matches the renamed
field and intent.

In `@sdk-libs/token-client/src/instructions/create_mint.rs`:
- Around line 38-64: You derive the compressed mint address with
rpc.get_address_tree_v2() and then call create_mint_action_instruction which
itself fetches the address tree, risking a mismatch if the tree rotates; pass
the same tree (address_tree_pubkey) into the instruction builder (e.g., add a
tree parameter to create_mint_action_instruction or MintActionParams) or move
the derive_mint_compressed_address call into the mint_action.rs path so there is
a single source of truth (referencing get_address_tree_v2,
derive_mint_compressed_address, create_mint_action_instruction,
MintActionParams). Also verify the on-chain MintAction accepts an empty actions
Vec for a create-only flow; if it does not, populate MintActionParams.actions
with the explicit create action instead of vec![] to ensure the create-only
behavior is encoded.

In
`@sdk-libs/token-sdk/src/compressed_token/v2/mint_to_compressed/instruction.rs`:
- Around line 18-19: Rename the field compressed_mint_inputs on struct
MintToCompressedInputs to reflect the renamed type MintWithContext (suggest
mint_inputs or mint_with_context) to keep the API consistent; update all places
that pattern-match/destructure MintToCompressedInputs and any usages that
reference compressed_mint_inputs to the new field name, and keep or add a short
comment if you intentionally retain the old name for backwards compatibility.

In `@sdk-libs/token-sdk/src/token/create_mint.rs`:
- Around line 34-39: Add a client-side validation that returns an error if
rent_payment < 2 before constructing the on-chain instruction: inside the
builder/function that creates the mint instruction (e.g., the method that
assembles the CreateMint/CreateMintInstruction or build() function in
create_mint.rs), check the struct field rent_payment and early-return a
descriptive error (or Result::Err) when rent_payment is less than 2 so callers
fail fast instead of relying on on-chain rejection.
- Around line 279-301: Add explicit key validations inside
TryFrom<&CreateMintCpi> so callers cannot pass mismatched accounts: check that
params.mint == mint.key, compressible_config.key == config_pda(), and
rent_sponsor.key == rent_sponsor_pda() (these mirror what
CreateMint::instruction() hardcodes), and return an Err on mismatch; keep
existing check of params.mint_authority == authority. This ensures the CPI
account ordering used in invoke()/invoke_signed() (mint_seed, payer,
address_tree, etc.) will fail fast on bad inputs.

In `@sdk-libs/token-sdk/src/token/transfer_interface.rs`:
- Around line 173-188: The From<&TransferInterfaceCpi> for TransferInterface
implementation currently sets max_top_up: None because TransferInterfaceCpi
lacks that field; either document this intentional omission or (if CPI callers
should control rent top-up) add a max_top_up: Option<u16> field to the
TransferInterfaceCpi struct and propagate it in the conversion: update the
TransferInterfaceCpi definition to include max_top_up and modify the impl
From<&TransferInterfaceCpi> for TransferInterface to map max_top_up:
cpi.max_top_up (preserving Option semantics), ensuring any constructors/parsers
of TransferInterfaceCpi are also updated to accept/forward this field.
- Around line 302-316: The code currently calls determine_transfer_type twice
(once in TransferInterface::invoke and again inside
TransferInterface::instruction/TransferInterface::from), so modify the API to
compute the transfer type once and reuse it: in TransferInterface::invoke call
determine_transfer_type(self.source_account.owner,
self.destination_account.owner) and then call a new/internal method like
instruction_with_type(&self, transfer_type: TransferType) (or change instruction
to accept a TransferType parameter) so the instruction builder uses the
pre-computed transfer_type instead of calling determine_transfer_type again;
update TransferInterface::instruction() usages accordingly (or keep it for
backward compatibility) and ensure TransferInterface::invoke uses the new
instruction_with_type when building the CPI instruction before calling
solana_cpi::invoke.

In `@sdk-libs/token-sdk/src/utils.rs`:
- Around line 32-35: The code compares owner against a hard-coded SPL Token
program pubkey created via Pubkey::from_str_const; replace that hard-coded value
with the canonical constant spl_token::ID (and remove the temporary spl_token
variable) so the if-check becomes a comparison against spl_token::ID and
spl_token_2022::ID (i.e., use spl_token::ID and token_22/spl_token_2022::ID in
the owner equality checks).

Comment on lines +38 to +64
// Get address tree for deriving compressed mint address
let address_tree_pubkey = rpc.get_address_tree_v2().tree;

let output_queue = rpc.get_random_state_tree_info()?.queue;

let compressed_mint_address =
derive_mint_compressed_address(&mint_seed.pubkey(), &address_tree_pubkey);

// Create extensions if metadata is provided
let extensions = metadata.map(|meta| vec![ExtensionInstructionData::TokenMetadata(meta)]);

// Get validity proof for address creation
let rpc_result = rpc
.get_validity_proof(
vec![],
vec![light_client::indexer::AddressWithTree {
address: compressed_mint_address,
tree: address_tree_pubkey,
}],
None,
)
.await?
.value;

let address_merkle_tree_root_index = rpc_result.addresses[0].root_index;
let (mint, bump) = find_mint_address(&mint_seed.pubkey());
// Build params struct manually
let params = CreateMintParams {
decimals,
address_merkle_tree_root_index,
mint_authority,
proof: rpc_result.proof.0.unwrap(),
compression_address: compressed_mint_address,
mint,
bump,
freeze_authority,
extensions,
};

// Create instruction builder
let builder = CreateMint::new(
params,
mint_seed.pubkey(),
payer,
address_tree_pubkey,
output_queue,
let compressed_mint_address = light_token_sdk::token::derive_mint_compressed_address(
&mint_seed.pubkey(),
&address_tree_pubkey,
);

builder
.instruction()
.map_err(|e| RpcError::CustomError(format!("Token SDK error: {:?}", e)))
// Create compressed-only mint using MintAction with empty actions
create_mint_action_instruction(
rpc,
MintActionParams {
compressed_mint_address,
mint_seed: mint_seed.pubkey(),
authority: mint_authority,
payer,
actions: vec![], // No actions - just create compressed mint
new_mint: Some(NewMint {
decimals,
supply: 0,
mint_authority,
freeze_authority,
metadata,
version: 3,
}),
},
)
.await
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

Verify create-only MintAction behavior and address-tree consistency.
You derive the compressed address via rpc.get_address_tree_v2, but create_mint_action_instruction fetches the address tree internally (sdk-libs/token-client/src/instructions/mint_action.rs, Lines 108-385). If the tree can rotate between calls, the derived address could drift from the config used in the instruction. Consider plumbing the tree into the instruction builder (or deriving there) to keep a single source of truth. Also confirm that an empty actions vec is accepted for “create-only” in the on-chain MintAction flow; if not, include the explicit create action.

🤖 Prompt for AI Agents
In `@sdk-libs/token-client/src/instructions/create_mint.rs` around lines 38 - 64,
You derive the compressed mint address with rpc.get_address_tree_v2() and then
call create_mint_action_instruction which itself fetches the address tree,
risking a mismatch if the tree rotates; pass the same tree (address_tree_pubkey)
into the instruction builder (e.g., add a tree parameter to
create_mint_action_instruction or MintActionParams) or move the
derive_mint_compressed_address call into the mint_action.rs path so there is a
single source of truth (referencing get_address_tree_v2,
derive_mint_compressed_address, create_mint_action_instruction,
MintActionParams). Also verify the on-chain MintAction accepts an empty actions
Vec for a create-only flow; if it does not, populate MintActionParams.actions
with the explicit create action instead of vec![] to ensure the create-only
behavior is encoded.

Comment on lines 18 to +19
pub struct MintToCompressedInputs {
pub compressed_mint_inputs: CompressedMintWithContext,
pub compressed_mint_inputs: MintWithContext,
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 renaming the field to match the type rename.

The type was renamed from CompressedMintWithContextMintWithContext, but the field is still called compressed_mint_inputs. This creates an inconsistency where the field name implies "compressed" but the type no longer carries that prefix.

For API consistency, consider renaming to mint_inputs or mint_with_context. If you're intentionally preserving the field name to minimize breaking changes for consumers, that's a reasonable tradeoff—just worth documenting.

♻️ Optional rename for consistency
 pub struct MintToCompressedInputs {
-    pub compressed_mint_inputs: MintWithContext,
+    pub mint_inputs: MintWithContext,

And update the destructuring at line 42:

     let MintToCompressedInputs {
-        compressed_mint_inputs,
+        mint_inputs,

And the usage at line 64:

-            compressed_mint_inputs.clone(),
+            mint_inputs.clone(),
🤖 Prompt for AI Agents
In `@sdk-libs/token-sdk/src/compressed_token/v2/mint_to_compressed/instruction.rs`
around lines 18 - 19, Rename the field compressed_mint_inputs on struct
MintToCompressedInputs to reflect the renamed type MintWithContext (suggest
mint_inputs or mint_with_context) to keep the API consistent; update all places
that pattern-match/destructure MintToCompressedInputs and any usages that
reference compressed_mint_inputs to the new field name, and keep or add a short
comment if you intentionally retain the old name for backwards compatibility.

Comment on lines +279 to +301
// Account order must match MintActionMetaConfig::to_account_metas()
let mut account_infos = vec![
self.system_accounts.light_system_program, // Index 0
self.mint_seed, // Index 1
self.authority, // Index 2 (authority)
self.payer, // Index 3 (fee_payer)
self.system_accounts.light_system_program,
self.mint_seed,
self.authority,
self.compressible_config,
self.mint,
self.rent_sponsor,
self.payer,
self.system_accounts.cpi_authority_pda,
self.system_accounts.registered_program_pda,
self.system_accounts.account_compression_authority,
self.system_accounts.account_compression_program,
self.system_accounts.system_program,
self.output_queue,
self.address_tree,
];

if let Some(cpi_context_account) = self.cpi_context_account {
account_infos.push(cpi_context_account);
}

account_infos.push(self.output_queue);
account_infos.push(self.address_tree);

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 the create_mint.rs file and explore the sdk-libs structure
find . -type f -name "create_mint.rs" | head -20

Repository: Lightprotocol/light-protocol

Length of output: 368


🏁 Script executed:

#!/bin/bash
# List the structure of sdk-libs/token-sdk/src/token/
ls -la sdk-libs/token-sdk/src/token/ 2>/dev/null || echo "Directory not found, checking sdk-libs structure..."
find sdk-libs -type d -name token-sdk 2>/dev/null | head -5

Repository: Lightprotocol/light-protocol

Length of output: 1700


🏁 Script executed:

#!/bin/bash
# Search for MintActionMetaConfig to understand account ordering
rg -n "MintActionMetaConfig" --type=rs | head -30

Repository: Lightprotocol/light-protocol

Length of output: 98


🏁 Script executed:

#!/bin/bash
# Search for MintActionMetaConfig with correct syntax
rg -n "MintActionMetaConfig" --type rust | head -40

Repository: Lightprotocol/light-protocol

Length of output: 5197


🏁 Script executed:

#!/bin/bash
# Read the create_mint.rs file to see the full context
wc -l sdk-libs/token-sdk/src/token/create_mint.rs

Repository: Lightprotocol/light-protocol

Length of output: 119


🏁 Script executed:

#!/bin/bash
# Read lines around 279-330 to see the CPI account ordering
sed -n '270,335p' sdk-libs/token-sdk/src/token/create_mint.rs

Repository: Lightprotocol/light-protocol

Length of output: 2363


🏁 Script executed:

#!/bin/bash
# Read the MintActionMetaConfig implementation to see to_account_metas()
cat -n sdk-libs/token-sdk/src/compressed_token/v2/mint_action/account_metas.rs | head -150

Repository: Lightprotocol/light-protocol

Length of output: 6074


🏁 Script executed:

#!/bin/bash
# Continue reading to find the to_account_metas() method
sed -n '100,250p' sdk-libs/token-sdk/src/compressed_token/v2/mint_action/account_metas.rs

Repository: Lightprotocol/light-protocol

Length of output: 4943


🏁 Script executed:

#!/bin/bash
# Search for TryFrom implementation for CreateMintCpi
sed -n '330,393p' sdk-libs/token-sdk/src/token/create_mint.rs

Repository: Lightprotocol/light-protocol

Length of output: 2460


🏁 Script executed:

#!/bin/bash
# Read the CreateMintCpi struct definition to understand the accounts
sed -n '1,150p' sdk-libs/token-sdk/src/token/create_mint.rs

Repository: Lightprotocol/light-protocol

Length of output: 5295


🏁 Script executed:

#!/bin/bash
# Search for where CreateMintCpi is used or constructed to understand how mint_seed relates to mint_signer
rg -n "mint_seed|CreateMintCpi" sdk-libs/token-sdk/src/token/create_mint.rs | head -30

Repository: Lightprotocol/light-protocol

Length of output: 1560


🏁 Script executed:

#!/bin/bash
# Check the CreateMintCpi struct definition fully to understand all accounts
sed -n '222,275p' sdk-libs/token-sdk/src/token/create_mint.rs

Repository: Lightprotocol/light-protocol

Length of output: 1890


🏁 Script executed:

#!/bin/bash
# Check for dependencies in sdk-libs/token-sdk/Cargo.toml
head -50 sdk-libs/token-sdk/Cargo.toml

Repository: Lightprotocol/light-protocol

Length of output: 1753


🏁 Script executed:

#!/bin/bash
# Let's verify the account order by examining how to_account_metas builds the list for a create_mint scenario
# and compare it explicitly with the invoke() method
sed -n '142,230p' sdk-libs/token-sdk/src/compressed_token/v2/mint_action/account_metas.rs | cat -n

Repository: Lightprotocol/light-protocol

Length of output: 3732


🏁 Script executed:

#!/bin/bash
# Search for examples of CreateMintCpi usage to see if there are potential mismatches
rg -n "CreateMintCpi\|CreateMintCpi::new" sdk-libs/ sdk-tests/ -A 5 | head -60

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Check what config_pda() and rent_sponsor_pda() functions do
rg -n "fn config_pda|fn rent_sponsor_pda" sdk-libs/token-sdk/src/ -A 5

Repository: Lightprotocol/light-protocol

Length of output: 787


🏁 Script executed:

#!/bin/bash
# Look at actual test usage of CreateMintCpi to see how it's constructed
rg -n "CreateMintCpi::new\|CreateMintCpi {" sdk-tests/ -B 3 -A 10 | head -80

Repository: Lightprotocol/light-protocol

Length of output: 205


🏁 Script executed:

#!/bin/bash
# Check if there are any instruction building examples showing the actual account flow
rg -n "new_create_mint\|CreateMint::new" sdk-libs/token-client/ -B 2 -A 8 | head -80

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Verify that mint_seed and mint are indeed the same derived address by checking the find_mint_address usage
rg -n "find_mint_address" sdk-libs/token-sdk/src/ -B 1 -A 2

Repository: Lightprotocol/light-protocol

Length of output: 4094


🏁 Script executed:

#!/bin/bash
# Create a comparison analysis of the two account orderings
python3 << 'EOF'
# Account order from to_account_metas() for create_mint scenario
to_account_metas_order = [
    "light_system_program",      # 1
    "mint_signer",               # 2 (from new_create_mint)
    "authority",                 # 3
    "compressible_config",       # 4 (optional, set for create_mint)
    "mint",                      # 5 (optional, set for create_mint)
    "rent_sponsor",              # 6 (optional, set for create_mint)
    "fee_payer",                 # 7
    "cpi_authority_pda",         # 8
    "registered_program_pda",    # 9
    "account_compression_authority",  # 10
    "account_compression_program",    # 11
    "system_program",            # 12
    "cpi_context",               # 13 (optional)
    "output_queue",              # 14
    "tree_pubkey",               # 15
]

# Account order from invoke() method (lines 279-301)
invoke_order = [
    "light_system_program",      # 1
    "mint_seed",                 # 2  <-- POTENTIAL ISSUE: labeled as mint_seed, but should be mint_signer
    "authority",                 # 3
    "compressible_config",       # 4
    "mint",                      # 5
    "rent_sponsor",              # 6
    "payer",                     # 7
    "cpi_authority_pda",         # 8
    "registered_program_pda",    # 9
    "account_compression_authority",  # 10
    "account_compression_program",    # 11
    "system_program",            # 12
    "cpi_context_account",       # 13 (optional)
    "output_queue",              # 14
    "address_tree",              # 15
]

print("Position Comparison:")
for i, (to_meta, invoke) in enumerate(zip(to_account_metas_order, invoke_order), 1):
    marker = " <-- MATCH" if to_meta.split('_')[0] == invoke.split('_')[0] or \
             (to_meta == "tree_pubkey" and invoke == "address_tree") or \
             (to_meta == "fee_payer" and invoke == "payer") or \
             (to_meta == "mint_signer" and invoke == "mint_seed") else " <-- SEMANTIC MATCH (renamed)"
    print(f"{i:2}. to_account_metas: {to_meta:40} | invoke: {invoke:40}{marker}")

print("\n✓ Account order MATCHES semantically")
print("✓ mint_seed is the account used as mint_signer")
print("✓ payer is the fee_payer")
print("✓ address_tree is the tree_pubkey")
EOF

Repository: Lightprotocol/light-protocol

Length of output: 158


🏁 Script executed:

#!/bin/bash
# Now let's check if there are any validations already in place for the CPI accounts
sed -n '335,365p' sdk-libs/token-sdk/src/token/create_mint.rs

Repository: Lightprotocol/light-protocol

Length of output: 1344


🏁 Script executed:

#!/bin/bash
# Verify account order by examining CreateMint::instruction() which builds the metadata
sed -n '145,180p' sdk-libs/token-sdk/src/token/create_mint.rs

Repository: Lightprotocol/light-protocol

Length of output: 1425


🏁 Script executed:

#!/bin/bash
# Check if params.mint is validated anywhere in CreateMint
rg -n "params\.mint" sdk-libs/token-sdk/src/token/create_mint.rs

Repository: Lightprotocol/light-protocol

Length of output: 595


🏁 Script executed:

#!/bin/bash
# See how mint_seed_pubkey relates to params.mint in the instruction building
sed -n '125,170p' sdk-libs/token-sdk/src/token/create_mint.rs

Repository: Lightprotocol/light-protocol

Length of output: 1976


Account ordering is correct; add missing key validations to TryFrom.

The CPI account order in invoke() and invoke_signed() correctly matches MintActionMetaConfig::to_account_metas() (mint_seed acts as mint_signer, payer as fee_payer, address_tree as tree_pubkey). However, TryFrom<&CreateMintCpi> only validates params.mint_authority against the authority account. Since CreateMint::instruction() hardcodes config_pda(), rent_sponsor_pda(), and uses params.mint from parameters, add guards to ensure callers pass matching accounts:

  • Validate params.mint == mint.key (caller's mint account must match params)
  • Validate compressible_config.key == config_pda()
  • Validate rent_sponsor.key == rent_sponsor_pda()

Without these checks, mismatched accounts fail silently during CPI invocation.

Proposed validation addition
fn try_from(account_infos: &CreateMintCpi<'info>) -> Result<Self, Self::Error> {
    if account_infos.params.mint_authority != *account_infos.authority.key {
        solana_msg::msg!(
            "CreateMintCpi: params.mint_authority ({}) does not match authority account ({})",
            account_infos.params.mint_authority,
            account_infos.authority.key
        );
        return Err(ProgramError::InvalidAccountData);
    }
+   if account_infos.params.mint != *account_infos.mint.key {
+       solana_msg::msg!(
+           "CreateMintCpi: params.mint ({}) does not match mint account ({})",
+           account_infos.params.mint,
+           account_infos.mint.key
+       );
+       return Err(ProgramError::InvalidAccountData);
+   }
+   if *account_infos.compressible_config.key != config_pda() {
+       solana_msg::msg!(
+           "CreateMintCpi: compressible_config ({}) must be config_pda ({})",
+           account_infos.compressible_config.key,
+           &config_pda()
+       );
+       return Err(ProgramError::InvalidAccountData);
+   }
+   if *account_infos.rent_sponsor.key != rent_sponsor_pda() {
+       solana_msg::msg!(
+           "CreateMintCpi: rent_sponsor ({}) must be rent_sponsor_pda ({})",
+           account_infos.rent_sponsor.key,
+           &rent_sponsor_pda()
+       );
+       return Err(ProgramError::InvalidAccountData);
+   }
    Ok(Self {
        mint_seed_pubkey: *account_infos.mint_seed.key,
        payer: *account_infos.payer.key,
        address_tree_pubkey: *account_infos.address_tree.key,
        output_queue: *account_infos.output_queue.key,
        cpi_context: account_infos.cpi_context.clone(),
        cpi_context_pubkey: account_infos
            .cpi_context_account
            .as_ref()
            .map(|acc| *acc.key),
        params: account_infos.params.clone(),
    })
}

Also applies to: 308-330

🤖 Prompt for AI Agents
In `@sdk-libs/token-sdk/src/token/create_mint.rs` around lines 279 - 301, Add
explicit key validations inside TryFrom<&CreateMintCpi> so callers cannot pass
mismatched accounts: check that params.mint == mint.key, compressible_config.key
== config_pda(), and rent_sponsor.key == rent_sponsor_pda() (these mirror what
CreateMint::instruction() hardcodes), and return an Err on mismatch; keep
existing check of params.mint_authority == authority. This ensures the CPI
account ordering used in invoke()/invoke_signed() (mint_seed, payer,
address_tree, etc.) will fail fast on bad inputs.

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: 2

🤖 Fix all issues with AI agents
In `@sdk-libs/token-sdk/src/token/transfer_interface.rs`:
- Around line 24-28: The doc comments for the transfer-type determination are
out of sync: update the function-level and related docstrings that currently
claim SPL→SPL returns UseRegularSplTransfer to instead describe the current
behavior (SPL→SPL returns TransferType::SplToSpl or an
Err(SplTokenProgramMismatch) when token program ids differ, and
Err(CannotDetermineAccountType) for unrecognized owners). Update all instances
mentioning UseRegularSplTransfer (including the other two occurrences) to
reference TransferType::SplToSpl and
SplTokenProgramMismatch/CannotDetermineAccountType as appropriate so the docs
match the implementation in the determine-transfer logic.
- Around line 184-199: Replace the manual byte-level encoding of a
TransferChecked instruction with the official SPL Token instruction builder:
call the appropriate transfer_checked function (e.g.
spl_token::instruction::transfer_checked or the token-2022 equivalent) instead
of constructing data = vec![12u8] + amount bytes + decimals; pass self.amount,
self.decimals, spl.mint, self.source, self.destination, self.authority and the
correct token program id (instead of self.source_owner) to that builder so it
returns an Instruction with the correct program_id and AccountMeta list; also
verify you import and use the token-2022 crate/version when building
instructions for token-2022 compatibility.
♻️ Duplicate comments (2)
sdk-libs/token-sdk/src/token/transfer_interface.rs (2)

205-218: Expose max_top_up for CPI callers (still hardcoded to None).

CPI users cannot set a rent/top-up cap. Either document this as intentional or plumb the field through TransferInterfaceCpi.


337-340: Avoid recomputing transfer type in invoke/invoke_signed.

You compute transfer_type and then call instruction(), which recomputes it again via TransferInterface::from(self). Consider reusing the computed value.

Also applies to: 407-410

📜 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 2a87259 and ff14aea.

⛔ Files ignored due to path filters (1)
  • sdk-tests/sdk-light-token-test/tests/test_transfer_interface.rs is excluded by none and included by none
📒 Files selected for processing (2)
  • sdk-libs/token-sdk/src/error.rs
  • sdk-libs/token-sdk/src/token/transfer_interface.rs
🧰 Additional context used
📓 Path-based instructions (1)
sdk-libs/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

sdk-libs/**/*.rs: SDK libs must depend only on program-libs, light-prover-client, and external crates
SDK libs must not depend on programs without devenv feature

Files:

  • sdk-libs/token-sdk/src/error.rs
  • sdk-libs/token-sdk/src/token/transfer_interface.rs
🧠 Learnings (43)
📓 Common learnings
Learnt from: CR
Repo: Lightprotocol/program-examples PR: 0
File: airdrop-implementations/simple-claim/README.md:0-0
Timestamp: 2026-01-10T19:24:56.367Z
Learning: Applies to airdrop-implementations/simple-claim/**/*.rs : Tokens must be minted as compressed tokens to PDAs derived from `[claimant, mint, unlock_slot, bump]`
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:46.444Z
Learning: Applies to programs/compressed-token/program/src/compressed_token/mint_action/**/*.rs : MintAction instruction must support exactly 10 action types: CreateCompressedMint, MintTo, UpdateMintAuthority, UpdateFreezeAuthority, MintToCToken, UpdateMetadataField, UpdateMetadataAuthority, RemoveMetadataKey, DecompressMint, and CompressAndCloseCMint
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/docs/CLAUDE.md:0-0
Timestamp: 2026-01-12T14:27:56.326Z
Learning: Applies to programs/compressed-token/program/docs/compressed_token/{TRANSFER2,MINT_ACTION,FREEZE,THAW,CREATE_TOKEN_POOL,ADD_TOKEN_POOL}.md : Compressed token operations documentation should be organized in a compressed_token/ subdirectory with separate files for TRANSFER2.md (batch transfer with compress/decompress), MINT_ACTION.md (mint operations), FREEZE.md (freeze operations), THAW.md (thaw operations), CREATE_TOKEN_POOL.md (initial token pool creation), and ADD_TOKEN_POOL.md (additional token pools)
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/account-checks/docs/PACKED_ACCOUNTS.md:0-0
Timestamp: 2025-11-24T17:59:54.233Z
Learning: Applies to program-libs/account-checks/docs/program-libs/account-checks/src/**/*.rs : Provide descriptive names in ProgramPackedAccounts error messages (e.g., 'token_mint' instead of 'account')
Learnt from: CR
Repo: Lightprotocol/program-examples PR: 0
File: create-and-update/README.md:0-0
Timestamp: 2026-01-10T19:25:28.052Z
Learning: Applies to create-and-update/**/*.rs : Use `LightAccount::new_mut()` to update existing compressed accounts
Learnt from: CR
Repo: Lightprotocol/distributor PR: 0
File: programs/merkle-distributor/CLAUDE.md:0-0
Timestamp: 2026-01-11T01:05:19.378Z
Learning: Applies to programs/merkle-distributor/**/instructions/{new_claim,claim_locked}.rs : Use Light SDK v2 CPI via `LightSystemProgramCpi::new_cpi` for compressed account interactions
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:46.444Z
Learning: Applies to programs/compressed-token/program/src/compressed_token/transfer2/**/*.rs : Transfer2 instruction must perform sum checks when supporting multi-mint Compress, Decompress, and CompressAndClose operations
Learnt from: CR
Repo: Lightprotocol/program-examples PR: 0
File: account-comparison/CLAUDE.md:0-0
Timestamp: 2026-01-10T19:26:01.412Z
Learning: Applies to account-comparison/programs/**/src/**/*.rs : Use `LightAccount::new_init` for compressed account creation and `LightAccount::new_mut` for updates in Light Protocol implementations
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-tests/sdk-light-token-test/README.md:0-0
Timestamp: 2026-01-14T00:05:32.217Z
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-12T14:27:56.326Z
Learning: Applies to programs/compressed-token/program/docs/ctoken/{CREATE,CLOSE,TRANSFER,TRANSFER_CHECKED,APPROVE,REVOKE,MINT_TO,MINT_TO_CHECKED,BURN,BURN_CHECKED,FREEZE_ACCOUNT,THAW_ACCOUNT}.md : CToken account operations documentation should be organized in a ctoken/ subdirectory with files for account creation, closure, transfers, approval delegation, minting, burning, and freeze/thaw operations
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-tests/sdk-light-token-test/README.md:0-0
Timestamp: 2026-01-14T00:05:32.217Z
Learning: Applies to sdk-tests/sdk-light-token-test/**/*.rs : Use the builder pattern from `light-token-sdk::ctoken` module for CPI operations instead of manual instruction building
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/account-checks/docs/ACCOUNT_ITERATOR.md:0-0
Timestamp: 2025-11-24T17:59:13.714Z
Learning: Applies to program-libs/account-checks/docs/**/*.rs : Use descriptive account names when calling AccountIterator methods (e.g., `iter.next_account("token_mint")` instead of `iter.next_account("account_3")`)
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-tests/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:28.451Z
Learning: Compressed token tests should include core token tests (light_token, v1, mint, transfer2) and batched tree tests
📚 Learning: 2026-01-14T00:06:46.444Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:46.444Z
Learning: Applies to programs/compressed-token/program/**/*.rs : When handling errors from functions returning TokenError directly (e.g., unpack_amount_and_decimals), use `convert_token_error` to map SPL Token error codes (0-18) to named ErrorCode variants

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-14T00:06:46.444Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:46.444Z
Learning: Applies to programs/compressed-token/program/**/*.rs : When converting SPL Token operation errors from pinocchio token program processors, use `convert_pinocchio_token_error` to map error codes (0-18) to named ErrorCode variants

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-14T00:06:46.444Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:46.444Z
Learning: Applies to programs/compressed-token/program/**/*.rs : When converting errors from system program operations, data access operations, or lamport transfers, use `convert_program_error` which adds a +6000 offset to raw error codes

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
📚 Learning: 2025-11-24T17:59:46.693Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/account-checks/docs/ERRORS.md:0-0
Timestamp: 2025-11-24T17:59:46.693Z
Learning: Applies to program-libs/account-checks/docs/program-libs/account-checks/src/error.rs : All AccountError variants must convert automatically to `ProgramError::Custom(u32)` for both solana-program and pinocchio SDKs using From trait implementations

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-14T00:06:11.915Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/compressible/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:11.915Z
Learning: Applies to program-libs/compressible/src/error.rs : Error types in error.rs must use numeric codes in the 19xxx range for CToken-specific errors

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
📚 Learning: 2026-01-11T01:05:19.378Z
Learnt from: CR
Repo: Lightprotocol/distributor PR: 0
File: programs/merkle-distributor/CLAUDE.md:0-0
Timestamp: 2026-01-11T01:05:19.378Z
Learning: Applies to programs/merkle-distributor/**/error.rs : Define error codes as an `ErrorCode` enum in error.rs with proper variants

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
📚 Learning: 2026-01-14T00:06:00.109Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/account-checks/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:00.109Z
Learning: Applies to program-libs/account-checks/**/*.rs : Define error codes in the range 12006-12021 with automatic ProgramError conversion for both solana-program and pinocchio SDKs

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
📚 Learning: 2026-01-14T00:06:11.915Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/compressible/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:11.915Z
Learning: Applies to program-libs/compressible/src/error.rs : Implement error type conversions (ProgramError) for Anchor, Pinocchio, and Solana program compatibility

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2025-11-24T17:56:00.229Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/batched-merkle-tree/docs/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:56:00.229Z
Learning: Applies to program-libs/batched-merkle-tree/docs/**/*.rs : Error handling must use BatchedMerkleTreeError enum from errors.rs with u32 error codes in the 14301-14312 range

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
📚 Learning: 2026-01-10T19:26:10.578Z
Learnt from: CR
Repo: Lightprotocol/program-examples PR: 0
File: airdrop-implementations/simple-claim/CLAUDE.md:0-0
Timestamp: 2026-01-10T19:26:10.578Z
Learning: Applies to airdrop-implementations/simple-claim/program/src/error.rs : Define ClaimError enum with three variants: MissingRequiredSignature, TokensLocked, and InvalidPDA

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-10T19:26:10.578Z
Learnt from: CR
Repo: Lightprotocol/program-examples PR: 0
File: airdrop-implementations/simple-claim/CLAUDE.md:0-0
Timestamp: 2026-01-10T19:26:10.578Z
Learning: Applies to airdrop-implementations/simple-claim/program/src/processor.rs : Invoke light_ctoken_sdk::decompress via CPI to convert compressed tokens to SPL tokens in the Claim instruction processor

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2025-11-24T17:59:46.693Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/account-checks/docs/ERRORS.md:0-0
Timestamp: 2025-11-24T17:59:46.693Z
Learning: Applies to program-libs/account-checks/docs/program-libs/account-checks/src/error.rs : Map Pinocchio standard ProgramError variants (InvalidArgument, InvalidInstructionData, InvalidAccountData, AccountDataTooSmall, InsufficientFunds, IncorrectProgramId, MissingRequiredSignature, AccountAlreadyInitialized, UninitializedAccount, NotEnoughAccountKeys, AccountBorrowFailed) to AccountError::PinocchioProgramError with codes 1-11

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
📚 Learning: 2026-01-14T00:06:46.444Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:46.444Z
Learning: Applies to programs/compressed-token/program/src/ctoken/*.rs : CToken operations must validate that the CompressibleConfig state is ACTIVE only for CreateTokenAccount, CreateAssociatedTokenAccount, and CreateAssociatedTokenAccountIdempotent instructions

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
📚 Learning: 2026-01-14T00:06:11.915Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/compressible/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:11.915Z
Learning: Applies to program-libs/compressible/src/error.rs : Propagate HasherError from light-hasher library (7xxx error codes) in error handling

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
📚 Learning: 2025-11-24T17:59:46.693Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/account-checks/docs/ERRORS.md:0-0
Timestamp: 2025-11-24T17:59:46.693Z
Learning: Applies to program-libs/account-checks/docs/program-libs/account-checks/src/error.rs : Use error code range 20000-20015 for AccountError variants

Applied to files:

  • sdk-libs/token-sdk/src/error.rs
📚 Learning: 2026-01-14T00:07:21.335Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: DOCS.md:0-0
Timestamp: 2026-01-14T00:07:21.335Z
Learning: For Solana standard ProgramError variants, document with their actual codes: InvalidInstructionData = 3, InvalidAccountData = 4, InsufficientFunds = 6, MissingRequiredSignature = 8, NotEnoughAccountKeys = 11, InvalidSeeds = 14

Applied to files:

  • sdk-libs/token-sdk/src/error.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:

  • sdk-libs/token-sdk/src/error.rs
📚 Learning: 2026-01-14T00:05:32.217Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-tests/sdk-light-token-test/README.md:0-0
Timestamp: 2026-01-14T00:05:32.217Z
Learning: Applies to sdk-tests/sdk-light-token-test/**/*.rs : Use the builder pattern from `light-token-sdk::ctoken` module for CPI operations instead of manual instruction building

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-11T01:05:19.378Z
Learnt from: CR
Repo: Lightprotocol/distributor PR: 0
File: programs/merkle-distributor/CLAUDE.md:0-0
Timestamp: 2026-01-11T01:05:19.378Z
Learning: Applies to programs/merkle-distributor/**/instructions/{new_claim,claim_locked}.rs : Use Light SDK v2 CPI via `LightSystemProgramCpi::new_cpi` for compressed account interactions

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-14T00:06:46.444Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:46.444Z
Learning: Applies to programs/compressed-token/program/src/**/*.rs : When transferring lamports programmatically, use safe transfer helpers from `shared/transfer_lamports.rs`

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-14T00:06:46.444Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:46.444Z
Learning: Data structures must be isolated in the `light-token-interface` crate (program-libs/token-interface/) so SDKs and clients can import types without pulling in program dependencies

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-14T00:06:46.444Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:46.444Z
Learning: Applies to programs/compressed-token/program/src/compressed_token/transfer2/**/*.rs : Transfer2 instruction must perform sum checks when supporting multi-mint Compress, Decompress, and CompressAndClose operations

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-14T00:05:32.217Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-tests/sdk-light-token-test/README.md:0-0
Timestamp: 2026-01-14T00:05:32.217Z
Learning: Applies to sdk-tests/sdk-light-token-test/**/*.rs : Document each instruction implementation with clear comments explaining the builder pattern usage and account structure

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2025-11-24T17:59:36.701Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/account-checks/docs/DISCRIMINATOR.md:0-0
Timestamp: 2025-11-24T17:59:36.701Z
Learning: Applies to program-libs/account-checks/docs/**/account-checks/**/*.rs : Implement the Discriminator trait for account types, providing 8-byte LIGHT_DISCRIMINATOR constant and LIGHT_DISCRIMINATOR_SLICE reference in Rust account structures

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-15T02:04:37.090Z
Learnt from: CR
Repo: Lightprotocol/program-examples PR: 0
File: zk/zk-id/README.md:0-0
Timestamp: 2026-01-15T02:04:37.090Z
Learning: Applies to zk/zk-id/src/lib.rs : Use CpiAccounts::new() from light_sdk::cpi::v2 for account parameter handling

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-14T00:05:32.217Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-tests/sdk-light-token-test/README.md:0-0
Timestamp: 2026-01-14T00:05:32.217Z
Learning: Applies to sdk-tests/sdk-light-token-test/**/*.rs : Use the `invoke()` method for regular CPI calls where the program acts as authority in compressed token operations

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2025-11-24T17:59:23.357Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/account-checks/docs/CLAUDE.md:0-0
Timestamp: 2025-11-24T17:59:23.357Z
Learning: Applies to program-libs/account-checks/docs/src/**/*.rs : Use 8-byte discriminators for account type identification and implement the Discriminator trait for account identification

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-14T00:06:46.444Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: programs/compressed-token/program/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:46.444Z
Learning: Applies to programs/compressed-token/program/src/**/*.rs : When calculating rent top-ups for compressible accounts, use utilities from `shared/compressible_top_up.rs`

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.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:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-14T00:05:32.217Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-tests/sdk-light-token-test/README.md:0-0
Timestamp: 2026-01-14T00:05:32.217Z
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:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2025-11-24T17:58:50.237Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/account-checks/docs/ACCOUNT_CHECKS.md:0-0
Timestamp: 2025-11-24T17:58:50.237Z
Learning: Applies to program-libs/account-checks/docs/program-libs/account-checks/src/checks.rs : Use generic `AccountInfoTrait` parameter for all account validation functions to enable compatibility with both Solana and Pinocchio runtimes

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2025-11-24T17:59:03.485Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/account-checks/docs/ACCOUNT_INFO_TRAIT.md:0-0
Timestamp: 2025-11-24T17:59:03.485Z
Learning: Applies to program-libs/account-checks/docs/program-libs/account-checks/src/account_info/{solana,pinocchio}.rs : Implement AccountInfoTrait with SDK-specific Pubkey types: `solana_pubkey::Pubkey` for solana-program feature and `[u8; 32]` raw bytes for pinocchio feature

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-10T19:26:10.578Z
Learnt from: CR
Repo: Lightprotocol/program-examples PR: 0
File: airdrop-implementations/simple-claim/CLAUDE.md:0-0
Timestamp: 2026-01-10T19:26:10.578Z
Learning: Applies to airdrop-implementations/simple-claim/program/src/instruction.rs : Define ClaimAccounts structure with 16 account fields in order: claimant, fee_payer, associated_airdrop_pda, ctoken_cpi_authority_pda, light_system_program, registered_program_pda, noop_program, account_compression_authority, account_compression_program, ctoken_program, spl_interface_pda, decompress_destination, token_program, system_program, state_tree, and queue

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-14T00:06:11.915Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/compressible/CLAUDE.md:0-0
Timestamp: 2026-01-14T00:06:11.915Z
Learning: Applies to program-libs/compressible/src/config.rs : CompressibleConfig account structure must support multiple serialization features (Anchor, Pinocchio, Borsh) for program compatibility

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-14T00:05:47.820Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/compressible/docs/CONFIG_ACCOUNT.md:0-0
Timestamp: 2026-01-14T00:05:47.820Z
Learning: Applies to program-libs/compressible/docs/**/*.rs : CompressibleConfig account state layout must follow the defined structure with version (u16), state (u8), bump (u8), update_authority (Pubkey), withdrawal_authority (Pubkey), rent_sponsor (Pubkey), compression_authority (Pubkey), rent_sponsor_bump (u8), compression_authority_bump (u8), rent_config (RentConfig), address_space ([Pubkey; 4]), and _place_holder ([u8; 32]) fields

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2025-11-24T17:59:03.485Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/account-checks/docs/ACCOUNT_INFO_TRAIT.md:0-0
Timestamp: 2025-11-24T17:59:03.485Z
Learning: Applies to program-libs/account-checks/docs/program-libs/account-checks/src/account_info/test_account_info.rs : Create a test-only AccountInfo mock implementation without external dependencies for unit testing AccountInfoTrait implementations

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2025-11-24T17:59:03.485Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/account-checks/docs/ACCOUNT_INFO_TRAIT.md:0-0
Timestamp: 2025-11-24T17:59:03.485Z
Learning: Applies to program-libs/account-checks/docs/program-libs/account-checks/src/account_info/*.rs : AccountInfoTrait implementations must return `AccountError` from all fallible operations: `BorrowAccountDataFailed` (12009) for borrow failures, `InvalidSeeds` (12016) for PDA creation, and `FailedBorrowRentSysvar` (12014) for rent access

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2025-11-24T17:58:50.237Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: program-libs/account-checks/docs/ACCOUNT_CHECKS.md:0-0
Timestamp: 2025-11-24T17:58:50.237Z
Learning: Applies to program-libs/account-checks/docs/program-libs/account-checks/src/checks.rs : Implement `check_pda_seeds_with_bump` function to verify PDA with known bump seed using `create_program_address`, returning `InvalidSeeds` (20010) error on mismatch

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-10T19:26:10.578Z
Learnt from: CR
Repo: Lightprotocol/program-examples PR: 0
File: airdrop-implementations/simple-claim/CLAUDE.md:0-0
Timestamp: 2026-01-10T19:26:10.578Z
Learning: Applies to airdrop-implementations/simple-claim/program/src/processor.rs : Airdrop PDA must be derived using seeds: [claimant.to_bytes(), mint.to_bytes(), unlock_slot.to_le_bytes(), bump] via Pubkey::create_program_address

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-14T00:05:32.217Z
Learnt from: CR
Repo: Lightprotocol/light-protocol PR: 0
File: sdk-tests/sdk-light-token-test/README.md:0-0
Timestamp: 2026-01-14T00:05:32.217Z
Learning: Applies to sdk-tests/sdk-light-token-test/**/*.rs : Use the `invoke_signed()` method for PDA-signed CPI calls in compressed token operations

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
📚 Learning: 2026-01-10T19:26:38.625Z
Learnt from: CR
Repo: Lightprotocol/program-examples PR: 0
File: create-and-update/CLAUDE.md:0-0
Timestamp: 2026-01-10T19:26:38.625Z
Learning: Applies to create-and-update/**/programs/**/src/**/*.rs : Derive `LIGHT_CPI_SIGNER` using the `derive_light_cpi_signer!` macro from program ID for CPI authorization

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.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 : Implement `process_new_operation()` function in wrapper module to handle PDA signer setup, account mapping, and CPI execution

Applied to files:

  • sdk-libs/token-sdk/src/token/transfer_interface.rs
🧬 Code graph analysis (1)
sdk-libs/token-sdk/src/token/transfer_interface.rs (1)
sdk-libs/token-sdk/src/utils.rs (1)
  • is_light_token_owner (25-40)
⏰ 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). (20)
  • GitHub Check: stateless-js-v2
  • GitHub Check: Test program-libs-fast
  • GitHub Check: Test program-libs-slow
  • GitHub Check: Test batched-merkle-tree-simulate
  • GitHub Check: lint
  • GitHub Check: programs (compressed-token-and-e2e, ["cargo test -p light-compressed-token", "cargo-test-sbf -p c...
  • 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 (system-cpi-test-v2-functional-read-only, ["cargo-test-sbf -p system-cpi-v2-test -- func...
  • GitHub Check: programs (compressed-token-batched-tree, ["cargo-test-sbf -p compressed-token-test -- test_transf...
  • GitHub Check: system-programs (native, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v1-native-t...
  • GitHub Check: programs (system-cpi-test, ["cargo-test-sbf -p system-cpi-test", "cargo test -p light-system-prog...
  • GitHub Check: programs (light-system-program-address, ["cargo-test-sbf -p system-test -- test_with_address", "c...
  • GitHub Check: cli-v1
  • GitHub Check: stateless-js-v1
  • GitHub Check: system-programs (token test, ["cargo-test-sbf -p sdk-token-test"])
  • GitHub Check: Forester e2e test
  • GitHub Check: system-programs (anchor & pinocchio, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -p sdk...
  • GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client light-token...
  • GitHub Check: cli-v2
🔇 Additional comments (3)
sdk-libs/token-sdk/src/error.rs (1)

78-79: Looks good — new error variant is consistently wired.

Enum variant, display message, and numeric mapping are aligned and follow the existing error-code pattern.

Also applies to: 141-141

sdk-libs/token-sdk/src/token/transfer_interface.rs (2)

57-77: SplInterface CPI → Pubkey mapping looks solid.

Clean conversion and keeps CPI lifetime details out of the public interface.


292-324: CPI builder argument validation is clear.

with_spl_interface enforces required accounts and fails fast with precise errors.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@ananas-block ananas-block force-pushed the jorrit/chore-fix-token-sdk branch from ff14aea to 21a7d1f Compare January 16, 2026 20:14
revert photon api renamings

cleanup

feat: transfer interface spl -> spl , t22 -> t22 transfer support
@ananas-block ananas-block force-pushed the jorrit/chore-fix-token-sdk branch from 21a7d1f to 341fced Compare January 16, 2026 23:56
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: 5

Caution

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

⚠️ Outside diff range comments (21)
sdk-libs/token-client/src/instructions/update_compressed_mint.rs (2)

85-92: Don’t default missing compressed mint address to zero.
A zero address can silently build an invalid proof or target the wrong account. Treat a missing address as an error.

🐛 Proposed fix
-    let compressed_mint_inputs = MintWithContext {
+    let compressed_mint_address = compressed_mint_account
+        .address
+        .ok_or_else(|| RpcError::CustomError("Compressed mint address missing".to_string()))?;
+
+    let compressed_mint_inputs = MintWithContext {
         leaf_index: compressed_mint_leaf_index,
         prove_by_index: true, // Use index-based proof like mint_to_compressed
         root_index: 0,        // Use 0 like mint_to_compressed
-        address: compressed_mint_account.address.unwrap_or([0u8; 32]),
+        address: compressed_mint_address,
         mint: Some(compressed_mint_instruction_data),
     };

95-105: Fix output queue selection to keep updated mint in its source tree.
The comment "Use same queue for output" is misleading. The code currently uses state_tree_info.queue (a random state tree), but the updated mint should stay in its source tree's output queue for consistency with mint_to_compressed behavior, where mint objects always go to compressed_mint_account.tree_info.queue.

Suggested fix
-    out_output_queue: state_tree_info.queue, // Use same queue for output
+    out_output_queue: compressed_mint_account.tree_info.queue,
sdk-libs/program-test/src/compressible.rs (1)

33-80: Size-based account type detection will fail for Token-2022 Mint accounts.

The 165-byte offset cannot reliably distinguish Token from Mint accounts because Token-2022 supports variable-length accounts with TLV extensions. Mint accounts are typically 82 bytes but can exceed 165 bytes with extensions, causing misclassification or silent failure. The try-parse approach (Token first, then Mint) is sound and avoids this pitfall.

Alternatively, if spl_token_2022::extension::StateWithExtensions is available in your dependencies, that's the canonical path: StateWithExtensions::<Mint>::unpack(data)? and StateWithExtensions::<Account>::unpack(data)? handle all extension cases safely.

🔧 Suggested refactor (try-based approach)
-fn determine_account_type(data: &[u8]) -> Option<u8> {
-    const ACCOUNT_TYPE_OFFSET: usize = 165;
-
-    match data.len().cmp(&ACCOUNT_TYPE_OFFSET) {
-        std::cmp::Ordering::Less => None,
-        std::cmp::Ordering::Equal => Some(ACCOUNT_TYPE_TOKEN_ACCOUNT), // 165 bytes = Token
-        std::cmp::Ordering::Greater => Some(data[ACCOUNT_TYPE_OFFSET]),
-    }
-}
-
 #[cfg(feature = "devenv")]
 fn extract_compression_info(data: &[u8]) -> Option<(CompressionInfo, u8, bool)> {
     use light_zero_copy::traits::ZeroCopyAt;
 
-    let account_type = determine_account_type(data)?;
-
-    match account_type {
-        ACCOUNT_TYPE_TOKEN_ACCOUNT => {
-            let (ctoken, _) = Token::zero_copy_at(data).ok()?;
-            let ext = ctoken.get_compressible_extension()?;
-            // ... extract compression info ...
-            Some((compression_info, account_type, compression_only))
-        }
-        ACCOUNT_TYPE_MINT => {
-            let mint = Mint::deserialize(&mut &data[..]).ok()?;
-            Some((mint.compression, account_type, false))
-        }
-        _ => None,
-    }
+    if let Ok((ctoken, _)) = Token::zero_copy_at(data) {
+        let ext = ctoken.get_compressible_extension()?;
+        // ... extract compression info ...
+        return Some((compression_info, ACCOUNT_TYPE_TOKEN_ACCOUNT, compression_only));
+    }
+
+    let mint = Mint::deserialize(&mut &data[..]).ok()?;
+    Some((mint.compression, ACCOUNT_TYPE_MINT, false))
 }
programs/compressed-token/program/src/compressible/claim.rs (1)

147-154: Rename cmint to mint for clarity after the type rename.

This reduces cognitive load now that the type is Mint.

♻️ Suggested rename
-            let (mut cmint, _) = Mint::zero_copy_at_mut_checked(&mut account_data)?;
-            cmint
+            let (mut mint, _) = Mint::zero_copy_at_mut_checked(&mut account_data)?;
+            mint
                 .base
                 .compression
                 .claim_and_update(claim_and_update)
programs/compressed-token/program/src/compressed_token/mint_action/actions/update_metadata.rs (1)

15-51: Type updated to &mut Mint; consider renaming parameter.

The function correctly accepts &mut Mint now. The parameter name compressed_mint is slightly inconsistent with the new Mint type—consider renaming to just mint for consistency, though this is a minor nitpick since it's an internal function.

♻️ Optional parameter rename
 fn get_metadata_extension_mut<'a>(
-    compressed_mint: &'a mut Mint,
+    mint: &'a mut Mint,
     extension_index: usize,
     operation_name: &str,
     signer: &pinocchio::pubkey::Pubkey,
 ) -> Result<&'a mut TokenMetadata, ProgramError> {
-    let extensions = compressed_mint.extensions.as_mut().ok_or_else(|| {
+    let extensions = mint.extensions.as_mut().ok_or_else(|| {
programs/compressed-token/program/src/shared/compressible_top_up.rs (1)

18-56: Consider updating doc comments and parameter names to Mint terminology.

While the import and call site were updated, the documentation and parameter names still reference the old "CMint" terminology:

  • Line 18-24: Doc comments mention "CMint" and "CToken"
  • Line 30: Parameter named cmint
  • Line 51: Comment says "CMint top-up"

For consistency with the PR's renaming effort, consider updating these as well.

♻️ Suggested terminology updates
-/// Calculate and execute top-up transfers for compressible CMint and CToken accounts.
-/// CMint always has compression info. CToken requires Compressible extension or errors.
+/// Calculate and execute top-up transfers for compressible Mint and CToken accounts.
+/// Mint always has compression info. CToken requires Compressible extension or errors.
 ///
 /// # Arguments
-/// * `cmint` - The CMint account (may or may not have Compressible extension)
+/// * `mint` - The Mint account (always has compression info)
 /// * `ctoken` - The CToken account (may or may not have Compressible extension)
 /// * `payer` - The fee payer for top-ups
 /// * `max_top_up` - Maximum lamports for top-ups combined (0 = no limit)
 #[inline(always)]
 #[profile]
 #[allow(unused)]
 pub fn calculate_and_execute_compressible_top_ups<'a>(
-    cmint: &'a AccountInfo,
+    mint: &'a AccountInfo,
     ctoken: &'a AccountInfo,
@@
     let mut transfers = [
         Transfer {
-            account: cmint,
+            account: mint,
             amount: 0,
         },
@@
-    // Calculate CMint top-up using optimized function (owner check inside)
+    // Calculate Mint top-up using optimized function (owner check inside)
     #[cfg(target_os = "solana")]
-    if let Some(amount) = mint_top_up_lamports_from_account_info(cmint, &mut current_slot) {
+    if let Some(amount) = mint_top_up_lamports_from_account_info(mint, &mut current_slot) {
programs/compressed-token/program/src/compressed_token/mint_action/mint_output.rs (1)

26-26: Minor: Comment still references "CMint account".

The comment on line 26 says "writes mint data to CMint account" — consider updating to "writes mint data to decompressed Mint account" or similar for consistency with the broader rename.

📝 Suggested comment update
 /// Processes the output compressed mint account.
-/// When decompressed, writes mint data to CMint account (compressed account is empty).
+/// When decompressed, writes mint data to decompressed Mint account (compressed account is empty).
 #[profile]
sdk-libs/compressible-client/src/decompress_mint.rs (3)

1-13: Module documentation still uses outdated "CMint" terminology.

The doc comments reference "CMint" throughout (lines 1, 4, etc.), but this PR renames CompressedMint to Mint. For consistency, these should be updated:

  • Line 1: "CMint accounts" → "compressed mint accounts" or just "Mint accounts"
  • Line 4: "CMint accounts (mints created via..." → "mint accounts..."
  • Similar references in lines 52, 56, 58, 63, 67

This is a minor documentation issue but worth addressing to avoid confusion for future readers.


74-85: Struct field cmint should be renamed to mint for consistency.

The MintInterface struct still uses cmint as a field name (line 76), while the PR's intent is to move away from the "cmint" nomenclature. Similarly, comments reference "CMint PDA pubkey" on line 75.

♻️ Suggested rename
 pub struct MintInterface {
-    /// The CMint PDA pubkey.
-    pub cmint: Pubkey,
-    /// The mint signer pubkey (used to derive CMint).
+    /// The Mint PDA pubkey.
+    pub mint: Pubkey,
+    /// The mint signer pubkey (used to derive Mint).
     pub signer: Pubkey,

320-332: Duplicate step number in comments.

Lines 329-332 have step "5. Check if already decompressed..." but then line 334 also says "5. Get validity proof". The second should be step 6.

♻️ Fix step numbering
     // 5. Check if already decompressed flag is set - return empty vec (idempotent)
     if mint_data.metadata.mint_decompressed {
         return Ok(vec![]);
     }

-    // 5. Get validity proof
+    // 6. Get validity proof
     let proof_result = indexer
sdk-libs/program-test/src/program_test/light_program_test.rs (1)

628-634: Doc comments still reference outdated "CMint" terminology.

The documentation on lines 632-634 still uses "CMint" while the code has been updated to use "Mint". For consistency with the rename effort, consider updating these comments.

Suggested fix
     /// Fetches MintInterface for a mint signer pubkey.
     ///
     /// Checks on-chain first, then compressed state.
     /// Returns `MintInterface` with state:
-    /// - `Hot` if CMint exists on-chain
-    /// - `Cold` if CMint is compressed (needs decompression)
-    /// - `None` if CMint doesn't exist
+    /// - `Hot` if Mint exists on-chain
+    /// - `Cold` if Mint is compressed (needs decompression)
+    /// - `None` if Mint doesn't exist
programs/compressed-token/program/src/compressed_token/mint_action/zero_copy_config.rs (1)

36-42: Comment still references "CMint" terminology.

Line 37 mentions "Get extensions from instruction data or CMint account" — this should be updated to "Mint" for consistency with the rename.

     // Generate output config based on final state after all actions
-    // Get extensions from instruction data or CMint account
+    // Get extensions from instruction data or Mint account
programs/compressed-token/program/src/compressed_token/mint_action/actions/decompress_mint.rs (2)

25-36: Comments still reference outdated "CMint" terminology.

The docstring mentions "CMint Solana account" (line 25) and "Set cmint_decompressed flag" (line 36), but the code now uses Mint types and mint_decompressed field. Update for consistency:

-/// Processes the DecompressMint action by creating a CMint Solana account
+/// Processes the DecompressMint action by creating a Mint Solana account
 /// from a compressed mint.
 ///
 /// ## Process Steps
 /// ...
-/// 8. **Flag Update**: Set cmint_decompressed flag
+/// 8. **Flag Update**: Set mint_decompressed flag

122-126: Comment and error message reference "CMint" terminology.

-    // 6b. Verify CMint account matches compressed_mint.metadata.mint
-    if !pubkey_eq(cmint.key(), &compressed_mint.metadata.mint.to_bytes()) {
-        msg!("CMint account does not match compressed_mint.metadata.mint");
+    // 6b. Verify Mint account matches compressed_mint.metadata.mint
+    if !pubkey_eq(mint_account.key(), &compressed_mint.metadata.mint.to_bytes()) {
+        msg!("Mint account does not match compressed_mint.metadata.mint");
         return Err(ErrorCode::InvalidCMintAccount.into());
programs/compressed-token/program/src/compressed_token/mint_action/actions/process_actions.rs (1)

146-152: Enum variant renamed but function call uses old name.

The match arm correctly uses ZAction::CompressAndCloseMint (line 146), but the function being called is still process_compress_and_close_cmint_action (line 147). If the function was renamed as part of this PR (consistent with the file rename mentioned in PR description), this call should be updated:

             ZAction::CompressAndCloseMint(action) => {
-                process_compress_and_close_cmint_action(
+                process_compress_and_close_mint_action(
                     action,
                     compressed_mint,
                     validated_accounts,
                 )?;
             }
programs/compressed-token/program/src/compressed_token/mint_action/actions/compress_and_close_cmint.rs (1)

16-32: Doc comments still reference "CMint" terminology.

The function documentation extensively references "CMint" (e.g., "CMint doesn't exist", "CMint account matches", "CMint Solana account") while the code now uses Mint types. Consider updating the doc comments for consistency with the Mint terminology rename. For example:

  • Line 16: "CMint Solana account" → "Mint Solana account"
  • Line 20: "CMint doesn't exist" → "Mint doesn't exist"
  • Line 22: "CMint account" → "Mint account"
  • Line 27: "CMint" → "Mint" throughout

This isn't a functional issue but would improve documentation consistency.

program-libs/token-interface/tests/compressed_mint.rs (1)

49-108: Optional: rename helper to match Mint terminology.

The helper now returns Mint; renaming reduces legacy “compressed” naming.

♻️ Suggested rename
-/// Generate a random Mint for testing
-fn generate_random_compressed_mint(rng: &mut impl Rng, with_extensions: bool) -> Mint {
+/// Generate a random Mint for testing
+fn generate_random_mint(rng: &mut impl Rng, with_extensions: bool) -> Mint {
@@
-        let original_mint = generate_random_compressed_mint(&mut rng, false);
+        let original_mint = generate_random_mint(&mut rng, false);
programs/compressed-token/program/tests/mint_action.rs (1)

225-252: Optional: rename local cmint_decompressed to mint_decompressed.

Keeps variable naming aligned with the Mint rename while leaving the struct field name intact.

♻️ Suggested rename
-    let cmint_decompressed = data.mint.as_ref().unwrap().metadata.mint_decompressed;
+    let mint_decompressed = data.mint.as_ref().unwrap().metadata.mint_decompressed;
@@
-        cmint_decompressed,
+        cmint_decompressed: mint_decompressed,
sdk-libs/token-sdk/src/token/decompress_mint.rs (3)

237-267: Inconsistent naming: DecompressCMintWithCpiContext still uses "CMint".

This struct name still contains "CMint" while the rest of the file uses "Mint" terminology. The comment on line 239 also says "CMint PDA". For consistency with the PR's renaming objective, consider renaming to DecompressMintWithCpiContext.

Similarly, the local variable cmint_pda on line 272 and the method call find_mint_address could be reviewed for consistency.

♻️ Suggested rename
-pub struct DecompressCMintWithCpiContext {
-    /// Mint seed pubkey (used to derive CMint PDA)
+pub struct DecompressMintWithCpiContext {
+    /// Mint seed pubkey (used to derive Mint PDA)
     pub mint_seed_pubkey: Pubkey,

316-352: Inconsistent naming: DecompressCMintCpiWithContext and cmint field.

This CPI struct retains "CMint" in the name and has a field pub cmint: AccountInfo<'info> (line 324). For consistency:

  • Rename struct to DecompressMintCpiWithContext
  • Rename field cmint to mint
  • Update the doc comment on line 323
♻️ Suggested rename
-pub struct DecompressCMintCpiWithContext<'info> {
+pub struct DecompressMintCpiWithContext<'info> {
     /// Mint seed account (used to derive CMint PDA, does not sign)
     pub mint_seed: AccountInfo<'info>,
     /// Mint authority (must sign)
     pub authority: AccountInfo<'info>,
     /// Fee payer
     pub payer: AccountInfo<'info>,
-    /// CMint PDA account (writable)
-    pub cmint: AccountInfo<'info>,
+    /// Mint PDA account (writable)
+    pub mint: AccountInfo<'info>,

387-407: build_account_infos still references self.cmint.

Line 393 uses self.cmint.clone() which would need to change if the field is renamed to mint.

🤖 Fix all issues with AI agents
In
`@programs/compressed-token/program/src/compressed_token/mint_action/mint_input.rs`:
- Around line 30-31: Update the stale comment in mint_input.rs that reads "When
CMint was decompressed (input state BEFORE actions), use zero values" to use the
new terminology "Mint" instead of "CMint"; locate the conditional using
accounts_config.cmint_decompressed and revise the comment above the let
(discriminator, input_data_hash) = if accounts_config.cmint_decompressed { ... }
accordingly so the comment matches the rename.

In
`@programs/compressed-token/program/src/compressed_token/mint_action/processor.rs`:
- Around line 54-68: Comments and variable names still use "cmint/CMint" while
the type calls now use Mint (Mint::try_from, Mint::from_account_info_checked);
align terminology by renaming comments and identifiers for clarity: update
comments that mention "CMint is decompressed" to "decompressed mint" (or
similar), rename the boolean accounts_config.cmint_decompressed to
accounts_config.mint_decompressed (or accounts_config.decompressed_mint), rename
validated_accounts.get_cmint() to get_mint_account (or
get_decompressed_mint_account) and cmint_account to mint_account, and adjust any
related error names/messages (e.g., ErrorCode::MintActionMissingCMintAccount) so
names consistently reference "mint" rather than "cmint" across this block and
associated definitions.

In
`@programs/compressed-token/program/src/compressed_token/mint_action/zero_copy_config.rs`:
- Line 27: Rename the parameter named "cmint" to "mint" in the function
signature where it is declared as "&Mint" and update every usage of the old
identifier "cmint" within the same file to "mint" (including the places that
currently reference cmint on the other lines referenced in the review). Ensure
the parameter name change preserves borrows/mutability (e.g., &Mint stays &Mint)
and update any pattern matches or method calls that used "cmint" to use "mint"
so identifiers are consistent with the renamed type.

In `@sdk-libs/token-client/src/instructions/update_compressed_mint.rs`:
- Around line 71-80: The code unnecessarily clones compressed_mint before
calling MintInstructionData::try_from; change the call to consume the owned
compressed_mint (pass compressed_mint directly to MintInstructionData::try_from)
instead of compressed_mint.clone(), ensuring no further use of compressed_mint
after that point (or update subsequent code to use the resulting
MintInstructionData); locate this in the block where BorshDeserialize produces
compressed_mint and replace the clone usage so the value is moved into
MintInstructionData::try_from.

In `@sdk-libs/token-sdk/src/token/transfer_interface.rs`:
- Around line 404-468: The code calls determine_transfer_type twice (once
explicitly and again indirectly when building the instruction), so change
invoke_signed to compute transfer_type once and reuse it when building the
instruction: call determine_transfer_type(self.source_account.owner,
self.destination_account.owner) and store the result in transfer_type, then
generate the instruction using a new or modified method that accepts the
already-determined TransferType (e.g., add an instruction_for(&self,
transfer_type: TransferType) or change instruction(&self) to accept
transfer_type) and use transfer_type in the match arms
(TransferType::LightToLight, LightToSpl, SplToLight, SplToSpl) to avoid the
duplicate call.
♻️ Duplicate comments (8)
sdk-libs/token-sdk/src/compressed_token/v2/mint_to_compressed/instruction.rs (1)

16-34: Field name still carries “compressed” after type rename.
Line 19 keeps compressed_mint_inputs even though the type is now MintWithContext. Consider renaming for API consistency (same as earlier feedback).

sdk-libs/token-client/src/instructions/create_mint.rs (1)

38-63: Verify address-tree source and empty-actions validity in MintAction.
If create_mint_action_instruction also fetches the address tree, the derived compressed_mint_address can drift on tree rotation; also confirm the on-chain MintAction path accepts an empty actions vec for create-only. Consider plumbing the tree into the builder or deriving there to keep a single source of truth.

#!/bin/bash
# Inspect MintAction builder for address-tree fetching and actions validation.
fd -a 'mint_action.rs' sdk-libs -x rg -n "create_mint_action_instruction|get_address_tree_v2|actions|MintActionParams|NewMint" {}
# Inspect on-chain MintAction handling for empty actions / create-only behavior.
rg -n "MintAction|actions|CreateCompressedMint|DecompressMint|NewMint" \
  programs/compressed-token/program/src/compressed_token/mint_action -g '*.rs'
program-libs/token-interface/src/state/mint/top_up.rs (1)

11-13: Constant renamed; backward compatibility consideration already noted.

The constant rename from CMINT_MIN_SIZE_WITH_COMPRESSION to MINT_MIN_SIZE_WITH_COMPRESSION is consistent with the PR's goals. A previous review already suggested adding a deprecated alias for backward compatibility.

program-libs/token-interface/tests/hash_tests.rs (1)

1-10: Hash collision tests remain commented out - this is a security coverage gap.

The entire test module (451 lines) testing Mint::hash() is commented out. While the type renames within the comments (Mint, MintMetadata, mint_decompressed) are correct, the tests themselves provide zero coverage because they're disabled.

These tests verify critical security properties:

  • Hash determinism
  • No collisions across field variations
  • Authority confusion prevention (None vs Some(zero_pubkey))
  • Boundary value handling

Hash collision bugs in compressed account hashing could allow attackers to forge valid proofs. The implementation exists and appears functional based on the learnings, but without active tests there's no CI verification.

Recommendation: Uncomment these tests and resolve any compilation issues. The type names have been updated correctly in the comments, so the tests should be close to compiling with the new Mint/MintMetadata types.

sdk-libs/token-sdk/src/token/create_mint.rs (2)

34-39: Still missing client‑side guard for rent_payment (0 or ≥2).

Docs promise a constraint, but callers can still pass invalid values without an early error.


335-358: Missing CPI key validations still outstanding.

TryFrom still only checks mint_authority; add guards for params.mint, compressible_config, and rent_sponsor to prevent mismatched CPI accounts.

sdk-libs/token-sdk/src/token/transfer_interface.rs (2)

205-220: max_top_up: None in CPI conversion - intentional limitation.

As noted in a previous review, the From<&TransferInterfaceCpi> implementation hardcodes max_top_up: None because TransferInterfaceCpi lacks this field. If CPI callers should have this control for LightToLight transfers, consider adding the field. Otherwise, document this limitation.


334-398: invoke() method - transfer type computed twice.

Looking at the implementation:

  1. Line 337-338: determine_transfer_type called explicitly
  2. Line 339: self.instruction() internally calls determine_transfer_type again via TransferInterface::from(self).instruction() (line 328 → 131)

This results in redundant computation. The previous review comment was marked as "addressed" but the code still shows the double call pattern.

♻️ Suggested optimization

Compute transfer_type once and pass it through, or refactor instruction() to accept an optional pre-computed type:

     pub fn invoke(self) -> Result<(), ProgramError> {
         use solana_cpi::invoke;

         let transfer_type =
             determine_transfer_type(self.source_account.owner, self.destination_account.owner)?;
-        let instruction = self.instruction()?;
+        let transfer_interface = TransferInterface::from(&self);
+        // Use transfer_type directly in a match that builds the instruction
+        // to avoid re-computing it inside instruction()

Comment on lines 30 to 31
// When CMint was decompressed (input state BEFORE actions), use zero values
let (discriminator, input_data_hash) = if accounts_config.cmint_decompressed {
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

Stale terminology in comment: "CMint" should be "Mint".

The comment on line 30 still references "CMint" which is inconsistent with the broader rename effort in this PR.

Suggested fix
-    // When CMint was decompressed (input state BEFORE actions), use zero values
+    // When Mint was decompressed (input state BEFORE actions), use zero values
📝 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
// When CMint was decompressed (input state BEFORE actions), use zero values
let (discriminator, input_data_hash) = if accounts_config.cmint_decompressed {
// When Mint was decompressed (input state BEFORE actions), use zero values
let (discriminator, input_data_hash) = if accounts_config.cmint_decompressed {
🤖 Prompt for AI Agents
In
`@programs/compressed-token/program/src/compressed_token/mint_action/mint_input.rs`
around lines 30 - 31, Update the stale comment in mint_input.rs that reads "When
CMint was decompressed (input state BEFORE actions), use zero values" to use the
new terminology "Mint" instead of "CMint"; locate the conditional using
accounts_config.cmint_decompressed and revise the comment above the let
(discriminator, input_data_hash) = if accounts_config.cmint_decompressed { ... }
accordingly so the comment matches the rename.

parsed_instruction_data: &ZMintActionCompressedInstructionData<'_>,
accounts_config: &AccountsConfig,
cmint: &CompressedMint,
cmint: &Mint,
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 renaming parameter cmint to mint for consistency.

Throughout this PR, the type has been renamed from CompressedMint to Mint. The parameter name here still uses the abbreviated old convention. For consistency with the broader rename, consider:

-    cmint: &Mint,
+    mint: &Mint,

This would require updating references on lines 40 and 88 as well.

🤖 Prompt for AI Agents
In
`@programs/compressed-token/program/src/compressed_token/mint_action/zero_copy_config.rs`
at line 27, Rename the parameter named "cmint" to "mint" in the function
signature where it is declared as "&Mint" and update every usage of the old
identifier "cmint" within the same file to "mint" (including the places that
currently reference cmint on the other lines referenced in the review). Ensure
the parameter name change preserves borrows/mutability (e.g., &Mint stays &Mint)
and update any pattern matches or method calls that used "cmint" to use "mint"
so identifiers are consistent with the renamed type.

Comment on lines +71 to +80
let compressed_mint: Mint =
BorshDeserialize::deserialize(&mut compressed_mint_data.data.as_slice()).map_err(|e| {
RpcError::CustomError(format!("Failed to deserialize compressed mint: {}", e))
})?;

// Convert to instruction data format
let compressed_mint_instruction_data =
CompressedMintInstructionData::try_from(compressed_mint.clone()).map_err(|e| {
RpcError::CustomError(format!("Failed to convert compressed mint: {:?}", e))
})?;
let compressed_mint_instruction_data = MintInstructionData::try_from(compressed_mint.clone())
.map_err(|e| {
RpcError::CustomError(format!("Failed to convert compressed mint: {:?}", e))
})?;
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

Avoid the extra clone when converting MintInstructionData.
compressed_mint isn’t used after conversion, so you can move it to avoid an unnecessary allocation.

♻️ Proposed diff
-    let compressed_mint_instruction_data = MintInstructionData::try_from(compressed_mint.clone())
+    let compressed_mint_instruction_data = MintInstructionData::try_from(compressed_mint)
         .map_err(|e| {
         RpcError::CustomError(format!("Failed to convert compressed mint: {:?}", e))
     })?;
📝 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
let compressed_mint: Mint =
BorshDeserialize::deserialize(&mut compressed_mint_data.data.as_slice()).map_err(|e| {
RpcError::CustomError(format!("Failed to deserialize compressed mint: {}", e))
})?;
// Convert to instruction data format
let compressed_mint_instruction_data =
CompressedMintInstructionData::try_from(compressed_mint.clone()).map_err(|e| {
RpcError::CustomError(format!("Failed to convert compressed mint: {:?}", e))
})?;
let compressed_mint_instruction_data = MintInstructionData::try_from(compressed_mint.clone())
.map_err(|e| {
RpcError::CustomError(format!("Failed to convert compressed mint: {:?}", e))
})?;
let compressed_mint: Mint =
BorshDeserialize::deserialize(&mut compressed_mint_data.data.as_slice()).map_err(|e| {
RpcError::CustomError(format!("Failed to deserialize compressed mint: {}", e))
})?;
// Convert to instruction data format
let compressed_mint_instruction_data = MintInstructionData::try_from(compressed_mint)
.map_err(|e| {
RpcError::CustomError(format!("Failed to convert compressed mint: {:?}", e))
})?;
🤖 Prompt for AI Agents
In `@sdk-libs/token-client/src/instructions/update_compressed_mint.rs` around
lines 71 - 80, The code unnecessarily clones compressed_mint before calling
MintInstructionData::try_from; change the call to consume the owned
compressed_mint (pass compressed_mint directly to MintInstructionData::try_from)
instead of compressed_mint.clone(), ensuring no further use of compressed_mint
after that point (or update subsequent code to use the resulting
MintInstructionData); locate this in the block where BorshDeserialize produces
compressed_mint and replace the clone usage so the value is moved into
MintInstructionData::try_from.

Comment on lines 404 to 468
pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> {
let source_is_light_token = is_token_account(&self.source_account)
.map_err(|_| ProgramError::Custom(TokenSdkError::CannotDetermineAccountType.into()))?;
let dest_is_light_token = is_token_account(&self.destination_account)
.map_err(|_| ProgramError::Custom(TokenSdkError::CannotDetermineAccountType.into()))?;

match (source_is_light_token, dest_is_light_token) {
(true, true) => TransferCpi {
source: self.source_account.clone(),
destination: self.destination_account.clone(),
amount: self.amount,
authority: self.authority.clone(),
max_top_up: None, // No limit by default
use solana_cpi::invoke_signed;

let transfer_type =
determine_transfer_type(self.source_account.owner, self.destination_account.owner)?;
let instruction = self.instruction()?;

match transfer_type {
TransferType::LightToLight => {
let account_infos = [
self.source_account,
self.destination_account,
self.authority,
];
invoke_signed(&instruction, &account_infos, signer_seeds)
}
.invoke_signed(signer_seeds),

(true, false) => {
TransferType::LightToSpl => {
let config = self.spl_interface.ok_or_else(|| {
ProgramError::Custom(TokenSdkError::SplInterfaceRequired.into())
})?;

TransferToSplCpi {
source: self.source_account.clone(),
destination_spl_token_account: self.destination_account.clone(),
amount: self.amount,
authority: self.authority.clone(),
mint: config.mint.clone(),
payer: self.payer.clone(),
spl_interface_pda: config.spl_interface_pda.clone(),
spl_interface_pda_bump: config.spl_interface_pda_bump,
decimals: self.decimals,
spl_token_program: config.spl_token_program.clone(),
compressed_token_program_authority: self
.compressed_token_program_authority
.clone(),
}
.invoke_signed(signer_seeds)
let account_infos = [
self.compressed_token_program_authority,
self.payer,
config.mint,
self.source_account,
self.destination_account,
self.authority,
config.spl_interface_pda,
config.spl_token_program,
];
invoke_signed(&instruction, &account_infos, signer_seeds)
}

(false, true) => {
TransferType::SplToLight => {
let config = self.spl_interface.ok_or_else(|| {
ProgramError::Custom(TokenSdkError::SplInterfaceRequired.into())
})?;

TransferFromSplCpi {
source_spl_token_account: self.source_account.clone(),
destination: self.destination_account.clone(),
amount: self.amount,
authority: self.authority.clone(),
mint: config.mint.clone(),
payer: self.payer.clone(),
spl_interface_pda: config.spl_interface_pda.clone(),
spl_interface_pda_bump: config.spl_interface_pda_bump,
decimals: self.decimals,
spl_token_program: config.spl_token_program.clone(),
compressed_token_program_authority: self
.compressed_token_program_authority
.clone(),
system_program: self.system_program.clone(),
}
.invoke_signed(signer_seeds)
let account_infos = [
self.compressed_token_program_authority,
self.payer,
config.mint,
self.destination_account,
self.authority,
self.source_account,
config.spl_interface_pda,
config.spl_token_program,
self.system_program,
];
invoke_signed(&instruction, &account_infos, signer_seeds)
}

(false, false) => Err(ProgramError::Custom(
TokenSdkError::UseRegularSplTransfer.into(),
)),
TransferType::SplToSpl => {
let config = self.spl_interface.ok_or_else(|| {
ProgramError::Custom(TokenSdkError::SplInterfaceRequired.into())
})?;
let account_infos = [
self.source_account,
config.mint,
self.destination_account,
self.authority,
];
invoke_signed(&instruction, &account_infos, signer_seeds)
}
}
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

invoke_signed() has the same double-call pattern.

Same issue as invoke() - determine_transfer_type is called twice (lines 407-408 and implicitly in line 409).

🤖 Prompt for AI Agents
In `@sdk-libs/token-sdk/src/token/transfer_interface.rs` around lines 404 - 468,
The code calls determine_transfer_type twice (once explicitly and again
indirectly when building the instruction), so change invoke_signed to compute
transfer_type once and reuse it when building the instruction: call
determine_transfer_type(self.source_account.owner,
self.destination_account.owner) and store the result in transfer_type, then
generate the instruction using a new or modified method that accepts the
already-determined TransferType (e.g., add an instruction_for(&self,
transfer_type: TransferType) or change instruction(&self) to accept
transfer_type) and use transfer_type in the match arms
(TransferType::LightToLight, LightToSpl, SplToLight, SplToSpl) to avoid the
duplicate call.

@SwenSchaeferjohann SwenSchaeferjohann merged commit f576f70 into main Jan 17, 2026
32 checks passed
@SwenSchaeferjohann SwenSchaeferjohann deleted the jorrit/chore-fix-token-sdk branch January 17, 2026 00:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants