Skip to content

refactor: separate compressed tokens from light-token#2199

Merged
ananas-block merged 5 commits intomainfrom
jorrit/refactor-separate-light-compressed-token-sdks
Jan 23, 2026
Merged

refactor: separate compressed tokens from light-token#2199
ananas-block merged 5 commits intomainfrom
jorrit/refactor-separate-light-compressed-token-sdks

Conversation

@ananas-block
Copy link
Contributor

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

  1. New SDK Crate: Created light-compressed-token-sdk - a new low-level SDK extracted from light-token-sdk for compressed token operations
  2. Module Migration: Moved compressed token implementation modules (compressed_token/, utils.rs, constants.rs, spl_interface.rs) from light-token-sdk to light-compressed-token-sdk
  3. Workspace Updates: Added sdk-libs/compressed-token-sdk to workspace members in root Cargo.toml and defined dependency in [workspace.dependencies]
  4. Token SDK Refactoring:
    - light-token-sdk now depends on and re-exports modules from light-compressed-token-sdk
    - Removed low-level compressed token code from light-token-sdk
  5. Error Handling Restructuring:
    - TokenSdkError moved to light-compressed-token-sdk (covers errors 17001-17033)
    - New LightTokenError in light-token-sdk for high-level token operations (errors 17500-17510)
    - light-token-sdk re-exports TokenSdkError from light-compressed-token-sdk
  6. Type Compatibility Layer: Created compat.rs in light-compressed-token-sdk with Solana-friendly types (TokenData, AccountState) and compatibility conversions
  7. Feature Propagation: Updated feature flags in light-token-sdk to pass through to light-compressed-token-sdk (v1, anchor-discriminator, anchor, idl-build, profiling)
  8. Documentation Update: Updated docstring in spl_interface.rs to reference light-compressed-token-sdk instead of light-token
  9. Import Update: Fixed import in decompress_full.rs to use ValidityProof from light_compressed_account instead of re-export

Summary by CodeRabbit

  • New Features

    • Introduced a new compressed-token SDK crate with public token types, constants, utilities, and default account configs.
    • Added SPL interface helpers and PDA creation utilities.
    • Exposed helpers for token balance retrieval and program-owner detection.
  • Refactor

    • Consolidated and re-exported token constants and utilities across packages for a single canonical source.
    • Updated public error/result types and aligned APIs to the new SDK surface.
  • Chores

    • Adjusted imports across tests and clients to use the new crate.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 22, 2026

📝 Walkthrough

Walkthrough

Adds a new light-compressed-token-sdk crate (types, errors, constants, SPL PDA helpers, utilities) and migrates many compressed-token-related types, imports, and features across token-sdk, token-client, client, program-test, and forester to depend on and re-export from the new SDK.

Changes

Cohort / File(s) Summary
New compressed-token SDK crate
sdk-libs/compressed-token-sdk/Cargo.toml, src/lib.rs, src/compat.rs, src/constants.rs, src/error.rs, src/spl_interface.rs, src/utils.rs
Introduces new crate with compat TokenData/AccountState (hashing + conversions), TokenSdkError result type, program ID constants, SPL interface PDA builders (SplInterfacePda/CreateSplInterfacePda), and utility helpers (get_token_account_balance, is_light_token_owner, TokenDefaultAccounts).
Token SDK error and result changes
sdk-libs/token-sdk/src/error.rs
Replaces exported TokenSdkError with new LightTokenError and LightTokenResult<T>; provides conversions to ProgramError/u32 and shifts code ranges into 17500+; retains compatibility re-export of original TokenSdkError from compressed-token-sdk.
Token SDK → compressed-token-sdk migration & re-exports
sdk-libs/token-sdk/src/lib.rs, src/constants.rs, src/utils.rs, src/pack.rs, src/spl_interface.rs, src/compressible/mod.rs
Removes/redirects local compressed_token implementations to re-export and reuse types and helpers from light-compressed-token-sdk (TokenData, AccountState, constants, TokenDefaultAccounts, spl_interface). Adjusts public surface via consolidated re-exports and adds small helpers (e.g., get_spl_interface_pda_and_bump, associated-token helpers).
Instruction APIs and signatures
sdk-libs/token-sdk/src/instruction/*.rs, notably create_associated_token_account.rs and related ATA2 variants
Many instruction builders' return types changed to LightTokenResult<Instruction>; import paths updated to light_compressed_token_sdk for compressed-token components; CPI wrapper signatures largely unchanged.
Decompress / transfer2 / mint action imports
sdk-libs/token-sdk/src/{decompress.rs,decompress_mint.rs,create_mint.rs,create_mints.rs}, sdk-libs/token-sdk/src/instruction/{transfer_to_spl.rs,transfer_from_spl.rs}, sdk-libs/token-sdk/src/compressible/decompress_runtime.rs
Switches internal imports to use compressed-token SDK implementations (decompress_full, transfer2, mint_action types) instead of crate-local ones.
Downstream import updates
sdk-libs/client/src/*, sdk-libs/token-client/src/*, sdk-libs/program-test/src/*, forester/src/*, forester/tests/*, sdk-libs/token-client/tests/*
~15+ files updated to import compressed_token, spl_interface, mint_action, transfer2 types from light_compressed_token_sdk instead of light_token or local paths.
Workspace dependency & features
sdk-libs/compressed-token-sdk/Cargo.toml, sdk-libs/token-sdk/Cargo.toml, sdk-libs/client/Cargo.toml, sdk-libs/program-test/Cargo.toml, sdk-libs/token-client/Cargo.toml, forester/Cargo.toml
Adds light-compressed-token-sdk as a workspace dependency and wires its feature flags into token-sdk features (v1, anchor-discriminator, anchor, idl-build, cpi-context, profile-*).
Minor import fix
sdk-libs/compressed-token-sdk/src/compressed_token/v2/decompress_full.rs
Resolves ValidityProof from light_compressed_account::instruction_data::compressed_proof::ValidityProof (import path adjustment only).

Sequence Diagram(s)

(Skipped — changes are primarily crate/surface refactors and import/typedef migrations; control flow is not newly introduced.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

ai-review

Suggested reviewers

  • sergeytimoshin
  • SwenSchaeferjohann

Poem

A crate was born to hold the key,
compressed tokens now sit neatly,
Constants, errors, PDAs in line,
imports changed — the code feels fine,
small steps to tidy, one commit at a time.

🚥 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: separate compressed tokens from light-token' directly and concisely describes the main objective: extraction of compressed token functionality into a new dedicated SDK crate.
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/refactor-separate-light-compressed-token-sdks

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.

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

Caution

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

⚠️ Outside diff range comments (2)
sdk-libs/token-sdk/src/error.rs (1)

6-62: Avoid ambiguous Result alias now that LightTokenResult exists.

Re-exporting Result (backed by TokenSdkError) alongside LightTokenResult makes it easy for callers to pick the wrong error domain. Consider renaming the re-export to an explicit alias (e.g., TokenSdkResult) or deprecating it.

Proposed change
-pub use light_compressed_token_sdk::error::{Result, TokenSdkError};
+pub use light_compressed_token_sdk::error::{Result as TokenSdkResult, TokenSdkError};
sdk-libs/token-sdk/src/instruction/create_associated_token_account.rs (1)

155-203: Consider a dedicated error for missing compressible config.

When COMPRESSIBLE is true and the config tuple is absent, returning InvalidAccountData conflates caller misuse with on-chain data errors. A dedicated variant (e.g., MissingCompressibleConfig) would make failures clearer to downstream callers.

🤖 Fix all issues with AI agents
In `@sdk-libs/compressed-token-sdk/src/compat.rs`:
- Around line 115-123: The current impl
From<light_token_interface::state::TokenData> for TokenData silently coerces
unknown account states to AccountState::Initialized; change this to a fallible
conversion by implementing TryFrom<light_token_interface::state::TokenData> for
TokenData (or changing the conversion to return Result<TokenData,
ProgramError>), call AccountState::try_from(data.state).map_err(|_|
ProgramError::InvalidAccountData) and propagate that error instead of unwrap_or,
and update any callers of the old From conversion to handle the Result (or
?-propagate the error) so invalid state bytes cause
ProgramError::InvalidAccountData rather than defaulting to Initialized.

In `@sdk-libs/compressed-token-sdk/src/error.rs`:
- Around line 93-104: The anchor-gated impl currently uses an unqualified
ProgramError which may resolve to solana_program_error::ProgramError; update the
impl From<TokenSdkError> for anchor builds to explicitly reference Anchor's
ProgramError (e.g., use anchor_lang::prelude::ProgramError::Custom or alias
anchor_lang::prelude::ProgramError to a local name) so the conversion
unambiguously constructs Anchor's error variant from TokenSdkError; adjust the
anchor-gated block around the impl to use that fully-qualified/aliased type
instead of the bare ProgramError.

@ananas-block ananas-block force-pushed the jorrit/refactor-separate-light-compressed-token-sdks branch from 49770eb to 179b060 Compare January 22, 2026 02:31
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: 3

Caution

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

⚠️ Outside diff range comments (5)
sdk-libs/compressed-token-sdk/src/compressed_token/v2/decompress_full.rs (1)

259-275: Minor: Redundant TokenDefaultAccounts::default() calls.

Both constructors call TokenDefaultAccounts::default() twice. While not a bug, you could cache the result for slightly cleaner code:

let defaults = TokenDefaultAccounts::default();
Self {
    compressed_token_program: defaults.compressed_token_program,
    cpi_authority_pda: defaults.cpi_authority_pda,
    // ...
}

This is a nitpick—feel free to leave as-is if the compiler optimizes it away or if you prefer the current explicit style.

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

108-108: Mixed import paths: light_token vs light_compressed_token_sdk.

This line still uses light_token::instruction::derive_token_ata while the file's other imports have migrated to light_compressed_token_sdk. While this works because light-token-sdk re-exports from light-compressed-token-sdk, it creates inconsistency within the same file.

Consider whether derive_token_ata (alias for derive_associated_token_account) should also be imported from the compressed-token SDK path for consistency, or if this is intentionally kept at the higher-level SDK since it deals with Light Token accounts rather than compressed tokens specifically.

sdk-libs/token-sdk/src/pack.rs (1)

42-54: Note: TokenDataWithMerkleContext duplicated between crates.

This struct and its hash() method are defined both here and in light_compressed_token_sdk::compat. While the implementations are identical, consider whether token-sdk should re-export TokenDataWithMerkleContext from compressed-token-sdk as well, to maintain a single definition.

Not blocking—this could be addressed in a follow-up cleanup.

sdk-libs/token-sdk/src/error.rs (1)

14-40: Duplicate error variants with different codes will complicate debugging.

All 11 LightTokenError variants have identically-named counterparts in TokenSdkError (from the relevant code snippet):

Variant LightTokenError TokenSdkError
SplInterfaceRequired 17500 17020
InvalidAccountData 17509 17013
SerializationError 17510 17002
... ... ...

When debugging, seeing error code 17500 vs 17020 for the same semantic issue (SplInterfaceRequired) is confusing. Consider either:

  1. Distinct naming - prefix high-level variants (e.g., HighLevelSplInterfaceRequired) to clarify origin
  2. Error wrapping - wrap TokenSdkError instead of duplicating variants:
pub enum LightTokenError {
    #[error(transparent)]
    CompressedToken(#[from] TokenSdkError),
    // ... only truly new high-level errors
}
  1. Remove duplicates - if the high-level SDK always surfaces the underlying error, just re-export without duplication
sdk-libs/token-sdk/src/instruction/create_associated_token_account.rs (1)

101-117: Naming collision with semantic mismatch between instruction builders and CPI wrappers.

The same function names have different semantics:

Function Name Signature (Lines 102-117) Signature (Lines 408-493)
create_associated_token_account Creates basic ATA Creates compressible ATA via CPI
create_associated_token_account_idempotent Creates basic ATA Creates compressible ATA via CPI

While Rust allows this due to different generic parameters, calling create_associated_token_account could silently create the wrong account type depending on context. This is a footgun for API consumers.

Consider renaming the CPI wrappers to be explicit:

Suggested rename for clarity
-pub fn create_associated_token_account<'info>(
+pub fn create_compressible_associated_token_account_cpi<'info>(
     payer: AccountInfo<'info>,
     // ...
 ) -> std::result::Result<(), solana_program_error::ProgramError> {

-pub fn create_associated_token_account_idempotent<'info>(
+pub fn create_compressible_associated_token_account_idempotent_cpi<'info>(
     payer: AccountInfo<'info>,
     // ...
 ) -> std::result::Result<(), solana_program_error::ProgramError> {

Also applies to: 406-493

🤖 Fix all issues with AI agents
In `@sdk-libs/compressed-token-sdk/src/compat.rs`:
- Around line 83-94: The hash() method on TokenDataWithMerkleContext currently
returns HasherError::EmptyInput when the
compressed_account.compressed_account.data.discriminator is present but not
equal to the sha-flat marker ([0,0,0,0,0,0,0,4]); change this to return a more
descriptive error by adding an enum variant to HasherError (e.g.,
UnsupportedVersion or InvalidDiscriminator) and return that variant in the match
arm that currently yields Err(HasherError::EmptyInput) while leaving the
successful path calling token_data.hash_sha_flat() unchanged.

In `@sdk-libs/token-client/src/instructions/mint_to_compressed.rs`:
- Around line 66-67: Replace the verbose fully-qualified type for
spl_interface_pda with a top-level import: add use
light_compressed_token_sdk::spl_interface::SplInterfacePda; at the top of the
file and change the declaration let spl_interface_pda:
Option<light_compressed_token_sdk::spl_interface::SplInterfacePda> = None; to
use the imported symbol (Option<SplInterfacePda>) so the variable
spl_interface_pda references SplInterfacePda directly.

In `@sdk-libs/token-sdk/src/compressible/decompress_runtime.rs`:
- Around line 56-58: The file repeatedly uses the long path
light_compressed_token_sdk::compressed_token::decompress_full::...; add a
single-use import for the items used (e.g., DecompressFullIndices and
decompress_full_token_accounts_with_indices) near the top with the other use
statements, then replace fully-qualified occurrences of
light_compressed_token_sdk::compressed_token::decompress_full::DecompressFullIndices
and
light_compressed_token_sdk::compressed_token::decompress_full::decompress_full_token_accounts_with_indices
with the short names DecompressFullIndices and
decompress_full_token_accounts_with_indices throughout the file (locations
around where token_decompress_indices is created and the calls at the other
reported ranges).
♻️ Duplicate comments (2)
sdk-libs/compressed-token-sdk/src/compat.rs (1)

115-126: Silent coercion of unknown account states persists.

The From<light_token_interface::state::TokenData> impl at line 122 uses unwrap_or(AccountState::Initialized) which silently masks corrupted or unknown state values. This was flagged in a previous review—consider implementing TryFrom instead to propagate ProgramError::InvalidAccountData for invalid state bytes.

sdk-libs/compressed-token-sdk/src/error.rs (1)

93-98: Disambiguate ProgramError in anchor builds.

The anchor-gated impl references ProgramError::Custom which resolves to solana_program_error::ProgramError from the module-level import. For clarity and to avoid potential divergence, prefer fully qualifying as anchor_lang::prelude::ProgramError::Custom(e.into()).

@ananas-block ananas-block force-pushed the jorrit/refactor-separate-light-compressed-token-sdks branch from 07e5189 to 4618eb7 Compare January 22, 2026 04:28
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: 8

Caution

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

⚠️ Outside diff range comments (1)
sdk-libs/program-test/Cargo.toml (1)

36-37: Remove redundant version specifications on workspace dependencies.

When workspace = true is set, Cargo retrieves the version from the workspace root definition—the explicit version here is ignored and triggers CI lint warnings. In the workspace root, light-registry and light-compressed-token are already configured with version = "2.0.0", so these specifications are unnecessary.

Suggested fix
-light-registry = { workspace = true, version = "2.0.0", features = ["cpi"], optional = true }
-light-compressed-token = { workspace = true, version = "2.0.0", features = ["cpi"], optional = true }
+light-registry = { workspace = true, features = ["cpi"], optional = true }
+light-compressed-token = { workspace = true, features = ["cpi"], optional = true }
🤖 Fix all issues with AI agents
In `@sdk-libs/compressed-token-sdk/src/spl_interface.rs`:
- Around line 55-62: The public CreateSplInterfacePda struct allows inconsistent
manual construction (e.g., index>0 with existing_spl_interface_pda=None) which
will panic on the unwrap in instruction(); fix by making CreateSplInterfacePda's
fields private and expose safe constructors (e.g., new_with_existing(...) and
new_without_existing(...)) that enforce the invariant, or alternatively change
instruction() to return Result<Instruction, TokenSdkError> and replace the
unwrap() with a propagated error (or an expect() with a clear message) so
inconsistent state is handled without panicking; update any call sites that
construct the struct directly to use the new constructors or handle the Result
from instruction().

In `@sdk-libs/compressed-token-sdk/src/utils.rs`:
- Around line 73-77: The listed imports (REGISTERED_PROGRAM_PDA,
ACCOUNT_COMPRESSION_AUTHORITY_PDA, ACCOUNT_COMPRESSION_PROGRAM_ID,
CPI_AUTHORITY_PDA, LIGHT_SYSTEM_PROGRAM_ID, NOOP_PROGRAM_ID, and
LIGHT_COMPRESSED_TOKEN_PROGRAM_ID) are declared mid-file; move their use
statements into the file's main import block alongside the existing top imports
(join them with the imports at lines 3-11) and remove the duplicate import block
currently at lines 73-77 so all external dependencies are declared together at
the top of the file.

In `@sdk-libs/token-client/Cargo.toml`:
- Line 17: The token-client crate currently depends on another SDK crate
light-compressed-token-sdk (line with light-compressed-token-sdk = { workspace =
true }); remove that dependency and move the shared types/utilities it consumes
into a program-libs crate (create or reuse light-token-interface) so
token-client only imports from program-libs. Concretely: extract the exported
types, PDA helpers, instruction constructors, and any shared utilities from
light-compressed-token-sdk into light-token-interface, update token-client's
Cargo.toml to remove light-compressed-token-sdk and add a dependency on
light-token-interface, then update all use/import paths in token-client to
reference the new light-token-interface symbols (types, functions, modules). Run
cargo build/tests to ensure no remaining references to
light-compressed-token-sdk and adjust visibility/feature flags in
light-token-interface so all former usages compile.

In `@sdk-libs/token-sdk/Cargo.toml`:
- Around line 9-16: The cpi-context feature in Cargo.toml currently only enables
"light-sdk/cpi-context" but does not propagate to "light-compressed-token-sdk";
update the cpi-context feature entry so it also includes
"light-compressed-token-sdk/cpi-context" (mirroring how features like anchor and
idl-build propagate) so that enabling cpi-context on this crate automatically
enables the corresponding feature in the light-compressed-token-sdk crate.

In `@sdk-libs/token-sdk/src/compressible/mod.rs`:
- Around line 5-16: The struct AccountInfoToCompress currently derives
AnchorSerialize/AnchorDeserialize (aliased to Borsh) but contains
solana_account_info::AccountInfo which is not serializable; remove the derives
from AccountInfoToCompress (drop #[derive(Debug, Clone, AnchorSerialize,
AnchorDeserialize)]) and keep it as a plain runtime container, or alternatively
extract any fields that must be serialized into a separate serializable struct
and use that for (de)serialization; update references to use the
non-serializable AccountInfoToCompress where runtime-only data is needed and the
new serializable struct where persistence/IPC is required.

In `@sdk-libs/token-sdk/src/constants.rs`:
- Around line 6-12: The local redefinition of LIGHT_TOKEN_PROGRAM_ID conflicts
with the glob re-export from light_compressed_token_sdk::constants::* (which
already defines LIGHT_TOKEN_PROGRAM_ID), causing a duplicate binding; either
remove the local LIGHT_TOKEN_PROGRAM_ID constant so both SDKs share the upstream
value, or replace the glob re-export with an explicit import list that omits
LIGHT_TOKEN_PROGRAM_ID (export other constants from
light_compressed_token_sdk::constants::* explicitly) so the local constant
remains a deliberate override—adjust the use statement and/or delete the local
pub const LIGHT_TOKEN_PROGRAM_ID accordingly.

In `@sdk-libs/token-sdk/src/instruction/create_mints.rs`:
- Around line 14-17: The file create_mints.rs imports MintActionMetaConfig,
MintActionMetaConfigCpiWrite, and
get_mint_action_instruction_account_metas_cpi_write from
light_compressed_token_sdk which violates sdk-libs dependency rules; move the
definitions of MintActionMetaConfig and MintActionMetaConfigCpiWrite and the
function get_mint_action_instruction_account_metas_cpi_write into the
light-token-interface program-lib, then update create_mints.rs to import those
symbols from light-token-interface instead of light_compressed_token_sdk and add
light-token-interface as the dependency in Cargo.toml for this crate so the SDK
only depends on program-libs.

In `@sdk-libs/token-sdk/src/instruction/mod.rs`:
- Around line 215-224: Re-export compression_authority_pda alongside the other
PDA helpers by adding compression_authority_pda into the public re-export block
(the pub use crate::{ constants::{ ... } , ... } statement) next to config_pda
and rent_sponsor_pda so the module facade consistently exposes the PDA helper;
update the constants re-export list to include compression_authority_pda and run
a build/check to ensure the symbol is in scope.
♻️ Duplicate comments (3)
sdk-libs/compressed-token-sdk/src/compat.rs (2)

115-126: Silent coercion of unknown account states masks data corruption.

This was flagged in a previous review and remains unaddressed. The unwrap_or(AccountState::Initialized) at line 122 will silently accept invalid state values, potentially allowing operations on corrupted accounts to proceed when they should fail.

The recommended fix is to change this to a fallible TryFrom conversion that propagates ProgramError::InvalidAccountData.


83-94: Hash method error semantics could be clearer.

This was noted in a previous review. Returning HasherError::EmptyInput when the discriminator doesn't match [0,0,0,0,0,0,0,4] is semantically imprecise — the input isn't empty, it's an unsupported version. A dedicated error variant would better communicate the failure reason.

sdk-libs/compressed-token-sdk/src/error.rs (1)

93-98: Ambiguous ProgramError reference in anchor-gated impl.

This was flagged in a previous review. In the anchor-gated impl, ProgramError::Custom resolves to solana_program_error::ProgramError (from the import at line 7), not Anchor's ProgramError. While they're currently the same type, explicitly using anchor_lang::prelude::ProgramError::Custom would be clearer and more robust against future divergence.

Comment on lines +55 to +62
pub struct CreateSplInterfacePda {
pub fee_payer: Pubkey,
pub mint: Pubkey,
pub token_program: Pubkey,
pub spl_interface_pda: Pubkey,
pub existing_spl_interface_pda: Option<Pubkey>,
pub index: u8,
}
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

Potential panic if struct is constructed manually with inconsistent state.

The struct fields are public, which means a consumer could bypass the constructors and create an instance with index > 0 but existing_spl_interface_pda = None. This would cause an unwrap() panic at line 123.

Consider either:

  1. Making fields private and exposing only through constructors, or
  2. Using expect() with a descriptive message, or
  3. Returning Result<Instruction, TokenSdkError> from instruction()
🔧 Suggested defensive fix
     pub fn instruction(self) -> Instruction {
         let cpi_authority = Pubkey::from(CPI_AUTHORITY);

         if self.index == 0 {
             // CreateTokenPool instruction
             Instruction {
                 program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID),
                 accounts: vec![
                     AccountMeta::new(self.fee_payer, true),
                     AccountMeta::new(self.spl_interface_pda, false),
                     AccountMeta::new_readonly(Pubkey::default(), false), // system_program
                     AccountMeta::new(self.mint, false),
                     AccountMeta::new_readonly(self.token_program, false),
                     AccountMeta::new_readonly(cpi_authority, false),
                 ],
                 data: CREATE_TOKEN_POOL.to_vec(),
             }
         } else {
             // AddTokenPool instruction
             let mut data = ADD_TOKEN_POOL.to_vec();
             data.push(self.index);
             Instruction {
                 program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID),
                 accounts: vec![
                     AccountMeta::new(self.fee_payer, true),
                     AccountMeta::new(self.spl_interface_pda, false),
-                    AccountMeta::new_readonly(self.existing_spl_interface_pda.unwrap(), false),
+                    AccountMeta::new_readonly(
+                        self.existing_spl_interface_pda
+                            .expect("existing_spl_interface_pda required when index > 0"),
+                        false,
+                    ),
                     AccountMeta::new_readonly(Pubkey::default(), false), // system_program
                     AccountMeta::new(self.mint, false),
                     AccountMeta::new_readonly(self.token_program, false),
                     AccountMeta::new_readonly(cpi_authority, false),
                 ],
                 data,
             }
         }
     }

Also applies to: 120-131

🤖 Prompt for AI Agents
In `@sdk-libs/compressed-token-sdk/src/spl_interface.rs` around lines 55 - 62, The
public CreateSplInterfacePda struct allows inconsistent manual construction
(e.g., index>0 with existing_spl_interface_pda=None) which will panic on the
unwrap in instruction(); fix by making CreateSplInterfacePda's fields private
and expose safe constructors (e.g., new_with_existing(...) and
new_without_existing(...)) that enforce the invariant, or alternatively change
instruction() to return Result<Instruction, TokenSdkError> and replace the
unwrap() with a propagated error (or an expect() with a clear message) so
inconsistent state is handled without panicking; update any call sites that
construct the struct directly to use the new constructors or handle the Result
from instruction().

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

🤖 Fix all issues with AI agents
In `@sdk-libs/compressed-token-sdk/src/utils.rs`:
- Around line 68-72: The struct AccountInfoToCompress currently derives
AnchorSerialize and AnchorDeserialize but contains AccountInfo<'info>, which
doesn't implement those traits; either remove the
AnchorSerialize/AnchorDeserialize derives from AccountInfoToCompress to fix the
compile error, or if you need wire serialization change the field to an
Account<'info, T> (a concrete account type) and then keep the serialization
derives; locate AccountInfoToCompress in utils.rs and apply one of these two
changes accordingly.
- Around line 93-105: The default impl for TokenDefaultAccounts uses
Pubkey::default() for the system_program field; replace that with the explicit
Solana system program constant to clarify intent by setting system_program to
solana_program::system_program::ID in the TokenDefaultAccounts::default()
implementation (update imports if necessary to reference
solana_program::system_program::ID).

In `@sdk-libs/token-client/src/actions/mint_action.rs`:
- Around line 5-7: The new direct import of create_compressed_mint (and its
helpers derive_mint_compressed_address and find_mint_address) from
light_compressed_token_sdk violates the sdk-libs dependency policy; update the
code to consume these helpers via an approved program-lib re-export (add or use
a function in the program-lib that wraps/re-exports
derive_mint_compressed_address and find_mint_address) or, if unavoidable, add an
explicit documented exception to the repository policy documenting why
light_compressed_token_sdk must be imported directly and who approved it; ensure
references in mint_action.rs are changed to call the program-lib wrapper (or
annotated exception) rather than importing from light_compressed_token_sdk
directly.

In `@sdk-libs/token-sdk/Cargo.toml`:
- Around line 10-16: The cpi-context feature in this Cargo.toml currently lists
"light-sdk/cpi-context" but does not propagate that feature to the dependent
crate "light-compressed-token-sdk", so cfg(feature="cpi-context") in
light-compressed-token-sdk remains inactive; update the cpi-context feature
array to include "light-compressed-token-sdk/cpi-context" (i.e., add the symbol
"light-compressed-token-sdk/cpi-context" to the cpi-context feature in this
file) and make the corresponding identical change for the other affected feature
blocks/ crates noted (the same cpi-context feature entries referenced on the
other lines) so consumers enabling cpi-context on the top-level crate
transitively enable it in light-compressed-token-sdk.

In `@sdk-libs/token-sdk/src/compressible/mod.rs`:
- Around line 5-16: The AccountInfoToCompress struct currently derives
AnchorSerialize/AnchorDeserialize which is invalid because AccountInfo cannot be
serialized; remove the #[derive(...)] (AnchorSerialize, AnchorDeserialize) from
the AccountInfoToCompress struct to make it a runtime-only container, and if you
actually need a serializable payload create a separate POD wrapper (e.g.,
SerializableAccountInfo) that extracts owned fields like key, is_signer,
is_writable, owner and implement/derive AnchorSerialize/AnchorDeserialize on
that wrapper instead; locate the struct AccountInfoToCompress and remove the
serialize/deserialize derives or replace its usage where a serializable type is
required with the new wrapper.

In `@sdk-libs/token-sdk/src/constants.rs`:
- Around line 6-13: The file currently re-exports everything from
light_compressed_token_sdk::constants::* while also defining a local constant
LIGHT_TOKEN_PROGRAM_ID, causing a duplicate symbol; remove the local duplicate
definition or stop wildcard re-exporting and instead explicitly re-export only
the needed constants from light_compressed_token_sdk::constants (or rename the
local constant) so that LIGHT_TOKEN_PROGRAM_ID is defined once; locate the
conflict around the pub use light_compressed_token_sdk::constants::* and the
local LIGHT_TOKEN_PROGRAM_ID constant in constants.rs and apply the chosen fix.

In `@sdk-libs/token-sdk/src/instruction/transfer_from_spl.rs`:
- Around line 2-7: This file imports transfer2 types from
light_compressed_token_sdk (create_transfer2_instruction,
Transfer2AccountsMetaConfig, Transfer2Config, Transfer2Inputs, CTokenAccount2)
which violates sdk-libs dependency rules; fix by moving the shared
transfer2/CTokenAccount2 type definitions into a program-lib interface crate
(e.g., light-token-interface) or into an existing program-lib, update this
crate's Cargo.toml to depend on that program-lib instead of
light_compressed_token_sdk, and replace the imports in transfer_from_spl.rs to
reference the new interface crate's module paths (ensure function/type names
remain create_transfer2_instruction, Transfer2AccountsMetaConfig,
Transfer2Config, Transfer2Inputs, CTokenAccount2).

In `@sdk-libs/token-sdk/src/utils.rs`:
- Around line 3-10: Remove the duplicate import of LIGHT_TOKEN_PROGRAM_ID from
light_sdk_types and consolidate on the single source from crate::constants
(currently imported as PROGRAM_ID); delete the `use
light_sdk_types::LIGHT_TOKEN_PROGRAM_ID;` line and update any usage of
LIGHT_TOKEN_PROGRAM_ID in this module (e.g., the occurrence referenced around
where PROGRAM_ID is used) to use the existing PROGRAM_ID symbol so there’s only
one canonical import.
♻️ Duplicate comments (7)
sdk-libs/compressed-token-sdk/src/error.rs (1)

93-98: Disambiguate Anchor ProgramError construction.
In the anchor-gated impl, ProgramError::Custom resolves to the imported solana_program_error::ProgramError. Prefer the fully qualified Anchor type to avoid ambiguity.

🧭 Proposed tweak
#[cfg(feature = "anchor")]
impl From<TokenSdkError> for anchor_lang::prelude::ProgramError {
    fn from(e: TokenSdkError) -> Self {
-        ProgramError::Custom(e.into())
+        anchor_lang::prelude::ProgramError::Custom(e.into())
    }
}
In anchor_lang, is prelude::ProgramError a type alias to solana_program_error::ProgramError? If not, is ProgramError::Custom the correct constructor for Anchor’s ProgramError conversions?
sdk-libs/client/src/interface/load_accounts.rs (1)

6-11: Same sdk-libs dependency layering concern.

This introduces the same sdk-lib → sdk-lib dependency noted earlier. Please resolve consistently across sdk-libs. As per coding guidelines, ...

sdk-libs/token-sdk/src/instruction/decompress_mint.rs (1)

4-16: Same sdk-libs dependency layering concern.

This is the same sdk-lib → sdk-lib dependency introduced elsewhere and should be resolved uniformly. As per coding guidelines, ...

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

5-8: Same sdk-libs dependency layering concern.

This is the same sdk-lib → sdk-lib dependency introduced elsewhere and should be resolved consistently. As per coding guidelines, ...

sdk-libs/token-sdk/src/instruction/transfer_interface.rs (1)

1-1: Same sdk-libs dependency layering concern.

This introduces the same sdk-lib → sdk-lib dependency noted earlier. As per coding guidelines, ...

sdk-libs/compressed-token-sdk/src/compat.rs (2)

115-126: Silent coercion of invalid account state remains unaddressed.

Line 122 still uses unwrap_or(AccountState::Initialized) which masks corrupted or invalid state bytes. This was flagged in a prior review. If data.state contains an unexpected value (e.g., 2 or 255), you silently produce a valid TokenData with Initialized state instead of surfacing the error.

Consider switching to TryFrom to propagate errors:

🔧 Suggested change
-impl From<light_token_interface::state::TokenData> for TokenData {
-    fn from(data: light_token_interface::state::TokenData) -> Self {
-        Self {
+impl TryFrom<light_token_interface::state::TokenData> for TokenData {
+    type Error = ProgramError;
+
+    fn try_from(data: light_token_interface::state::TokenData) -> Result<Self, Self::Error> {
+        Ok(Self {
             mint: Pubkey::new_from_array(data.mint.to_bytes()),
             owner: Pubkey::new_from_array(data.owner.to_bytes()),
             amount: data.amount,
             delegate: data.delegate.map(|d| Pubkey::new_from_array(d.to_bytes())),
-            state: AccountState::try_from(data.state).unwrap_or(AccountState::Initialized),
+            state: AccountState::try_from(data.state)?,
             tlv: data.tlv,
-        }
+        })
     }
 }

83-94: Consider a more descriptive error variant for unsupported discriminators.

Both the missing-data case (line 92) and the wrong-discriminator case (line 89) return HasherError::EmptyInput. While this works, it conflates two distinct failure modes. A variant like UnsupportedVersion or InvalidDiscriminator would communicate the actual issue more clearly to callers debugging hash failures.

This is a minor clarity improvement—not blocking.

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

🤖 Fix all issues with AI agents
In `@sdk-libs/token-client/src/instructions/mint_to_compressed.rs`:
- Around line 6-14: This import from light_compressed_token_sdk violates
sdk-libs -> program-libs-only boundaries; remove direct dependency and instead
have a program-lib re-export the needed symbols (e.g., re-export
derive_mint_from_spl_mint, create_mint_to_compressed_instruction,
DecompressedMintConfig, MintToCompressedInputs, SplInterfacePda) from a shared
interface crate (suggested name: light-token-interface), update this file's use
statements to import those re-exports, and update Cargo.toml to depend on that
program-lib rather than light_compressed_token_sdk; alternatively, if a
documented exception is required, add an explicit rationale in the repo policy
and a TODO comment referencing the exception while keeping current imports until
the re-export is implemented.

In `@sdk-libs/token-sdk/Cargo.toml`:
- Around line 11-16: The Cargo feature "v1" in token-sdk is a dangling stub that
references light-compressed-token-sdk/v1 but has no implementation in downstream
crates; either remove the v1 feature and its propagation from
token-sdk/Cargo.toml to avoid exposing a non-functional public API, or implement
full v1 support by adding a v1 feature to light-token-types and
light-token-interface (e.g., v1 = []), updating token-sdk's v1 to propagate to
those crates (and to light-compressed-token-sdk/v1), and then audit light-sdk
and light-sdk-types (currently pinned to ["v2"]) to decide if they should be
made version-switchable or left pinned to v2.
- Line 33: The Cargo.toml currently adds an SDK-to-SDK dependency on
light-compressed-token-sdk which violates the SDK layering rule; remove that
dependency and either (A) move the shared constants, error types, SPL interface
and utilities currently consumed by light-token into a program-lib (for example
extend or add them to light-token-interface) and update light-token and this
crate to depend on the program-lib instead, or (B) if this dependency is
intentional, add an explicit documented exception to the repository policy
(CLAUDE.md) and justify it in this crate's README/Cargo manifest comments;
reference the crate name light-compressed-token-sdk, the consumer crate
light-token, and the program-lib candidate light-token-interface when making the
change.

In `@sdk-libs/token-sdk/src/compressible/decompress_runtime.rs`:
- Around line 3-5: The import in decompress_runtime.rs pulls
DecompressFullIndices and decompress_full_token_accounts_with_indices from
light_compressed_token_sdk which creates an sdk-libs-to-sdk-libs dependency;
move the definitions of DecompressFullIndices and
decompress_full_token_accounts_with_indices into program-libs/token-interface so
both light-compressed-token-sdk and light-token-sdk can depend on them, then
update decompress_runtime.rs to import these symbols from the new
program-libs/token-interface crate (replace the current use of
light_compressed_token_sdk::compressed_token::decompress_full with the
corresponding module path in program-libs/token-interface) and remove the old
cross-sdklib dependency.
♻️ Duplicate comments (1)
sdk-libs/compressed-token-sdk/src/utils.rs (1)

93-104: Prefer explicit System Program ID over Pubkey::default() for clarity.
This is the same readability nit flagged earlier; still applies here.

♻️ Suggested tweak
+use solana_program::system_program::ID as SYSTEM_PROGRAM_ID;
 ...
 impl Default for TokenDefaultAccounts {
     fn default() -> Self {
         Self {
             light_system_program: Pubkey::from(LIGHT_SYSTEM_PROGRAM_ID),
             registered_program_pda: Pubkey::from(REGISTERED_PROGRAM_PDA),
             noop_program: Pubkey::from(NOOP_PROGRAM_ID),
             account_compression_authority: Pubkey::from(ACCOUNT_COMPRESSION_AUTHORITY_PDA),
             account_compression_program: Pubkey::from(ACCOUNT_COMPRESSION_PROGRAM_ID),
             self_program: Pubkey::from(LIGHT_COMPRESSED_TOKEN_PROGRAM_ID),
             cpi_authority_pda: Pubkey::from(CPI_AUTHORITY_PDA),
-            system_program: Pubkey::default(),
+            system_program: SYSTEM_PROGRAM_ID,
             compressed_token_program: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID),
         }
     }
 }

Comment on lines +6 to 14
use light_compressed_token_sdk::{
compressed_token::{
create_compressed_mint::derive_mint_from_spl_mint,
mint_to_compressed::{
create_mint_to_compressed_instruction, DecompressedMintConfig, MintToCompressedInputs,
},
},
spl_interface::SplInterfacePda,
};
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 | 🟠 Major

Validate SDK layer boundaries for the new light_compressed_token_sdk dependency.

This file is under sdk-libs/, but the new import pulls in another sdk-lib crate (light_compressed_token_sdk). If the boundary rule still applies, consider re-exporting the needed types/functions via a program-lib (e.g., light-token-interface) or documenting an explicit exception for sdk-lib→sdk-lib dependencies. As per coding guidelines, sdk-libs should depend only on program-libs, light-prover-client, and external crates.

Also applies to: 69-69

🤖 Prompt for AI Agents
In `@sdk-libs/token-client/src/instructions/mint_to_compressed.rs` around lines 6
- 14, This import from light_compressed_token_sdk violates sdk-libs ->
program-libs-only boundaries; remove direct dependency and instead have a
program-lib re-export the needed symbols (e.g., re-export
derive_mint_from_spl_mint, create_mint_to_compressed_instruction,
DecompressedMintConfig, MintToCompressedInputs, SplInterfacePda) from a shared
interface crate (suggested name: light-token-interface), update this file's use
statements to import those re-exports, and update Cargo.toml to depend on that
program-lib rather than light_compressed_token_sdk; alternatively, if a
documented exception is required, add an explicit rationale in the repo policy
and a TODO comment referencing the exception while keeping current imports until
the re-export is implemented.

Comment on lines +11 to +16
v1 = ["light-compressed-token-sdk/v1"]
anchor-discriminator = ["light-sdk/anchor-discriminator", "light-compressed-token-sdk/anchor-discriminator"]
anchor = ["anchor-lang", "light-token-types/anchor", "light-token-interface/anchor", "light-compressed-token-sdk/anchor", "dep:light-sdk-macros"]
# idl-build feature enables IDL generation for client-side tooling and SDK generation
idl-build = ["anchor", "anchor-lang/idl-build", "light-sdk/idl-build"]
cpi-context = ["light-sdk/cpi-context"]
idl-build = ["anchor", "anchor-lang/idl-build", "light-sdk/idl-build", "light-compressed-token-sdk/idl-build"]
cpi-context = ["light-sdk/cpi-context", "light-compressed-token-sdk/cpi-context"]
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
# First, locate and inspect the token-sdk Cargo.toml
echo "=== sdk-libs/token-sdk/Cargo.toml ==="
cat -n sdk-libs/token-sdk/Cargo.toml | head -50

echo -e "\n=== Check for v1 features in related crates ==="
# Find all Cargo.toml files in sdk-libs and program-libs that mention token or sdk
find sdk-libs program-libs -name "Cargo.toml" -type f | sort | while read f; do
  # Only check if file contains "token" or "sdk" or "interface" in path or contains v1 feature
  if echo "$f" | grep -qE "(token|sdk|interface|compressed)" || rg -q "^\s*v1\s*=" "$f" 2>/dev/null; then
    echo -e "\n--- $f ---"
    rg -A 2 "^\s*\[features\]" "$f" | head -20
  fi
done

Repository: Lightprotocol/light-protocol

Length of output: 200


🏁 Script executed:

#!/bin/bash
# Check light-compressed-token-sdk specifically
echo "=== Checking light-compressed-token-sdk in program-libs ==="
find program-libs -path "*compressed-token*" -name "Cargo.toml" -type f | while read f; do
  echo -e "\n--- $f ---"
  cat -n "$f" | head -60
done

Repository: Lightprotocol/light-protocol

Length of output: 131


🏁 Script executed:

#!/bin/bash
# Look for CLAUDE.md files that provide guidance on feature handling
echo "=== Checking for CLAUDE.md guidance ==="
find sdk-libs program-libs -name "CLAUDE.md" -type f | while read f; do
  echo -e "\n--- $f ---"
  cat "$f" 2>/dev/null | grep -i -E "feature|v1|v2|layout" | head -10
done

Repository: Lightprotocol/light-protocol

Length of output: 1265


🏁 Script executed:

#!/bin/bash
# Read the actual token-sdk Cargo.toml file
echo "=== sdk-libs/token-sdk/Cargo.toml (full file) ==="
cat sdk-libs/token-sdk/Cargo.toml

echo -e "\n=== Search for all v1 feature definitions ==="
rg -l "v1" --type toml sdk-libs program-libs | while read f; do
  echo -e "\n--- $f ---"
  rg -B 2 -A 2 "^\s*v1\s*=" "$f"
done

Repository: Lightprotocol/light-protocol

Length of output: 3669


🏁 Script executed:

#!/bin/bash
# Search for light-compressed-token-sdk location
echo "=== Finding light-compressed-token-sdk ==="
find . -name "light-compressed-token-sdk" -type d 2>/dev/null | head -5

echo -e "\n=== Search all Cargo.toml for compressed-token-sdk references ==="
rg "light-compressed-token-sdk" --type toml sdk-libs program-libs -A 1 -B 1

Repository: Lightprotocol/light-protocol

Length of output: 2546


🏁 Script executed:

#!/bin/bash
# Check feature propagation patterns in light-sdk and related crates
echo "=== light-sdk Cargo.toml feature block ==="
rg -A 20 "^\[features\]" sdk-libs/light-sdk/Cargo.toml 2>/dev/null || echo "Not found in sdk-libs/light-sdk"

echo -e "\n=== light-token-types Cargo.toml ==="
find sdk-libs -name "light-token-types" -o -name "token-types" | while read d; do
  [ -f "$d/Cargo.toml" ] && cat "$d/Cargo.toml" | head -40
done

echo -e "\n=== light-token-interface (program-libs) ==="
find program-libs -name "*token-interface*" -type d | while read d; do
  [ -f "$d/Cargo.toml" ] && cat "$d/Cargo.toml" | head -50
done

Repository: Lightprotocol/light-protocol

Length of output: 2782


🏁 Script executed:

#!/bin/bash
# Search for any documentation about v1 vs v2 in codebase
echo "=== Looking for v1/v2 documentation ==="
find . -name "README.md" -o -name "CLAUDE.md" | xargs grep -l "v1\|v2\|layout\|account" 2>/dev/null | head -10 | while read f; do
  echo -e "\n--- $f ---"
  grep -i -E "v1|v2|layout|account.*v" "$f" | head -5
done

echo -e "\n=== Check if compressed-token-sdk v1 feature is used anywhere ==="
rg "compressed-token-sdk.*v1" --type toml sdk-libs program-libs

Repository: Lightprotocol/light-protocol

Length of output: 4784


Remove the incomplete v1 feature stub or implement it fully across the token stack.

The v1 feature in token-sdk/Cargo.toml maps to light-compressed-token-sdk/v1, but that feature is defined as empty (v1 = []). Since light-token-types and light-token-interface have no v1 feature definitions, you cannot actually switch account layouts by enabling v1. Additionally, light-sdk and light-sdk-types are hard-pinned to ["v2"] in dependencies, making any hypothetical v1 functionality moot.

Either remove the v1 feature declaration and its propagation to unclutter the public API, or if V1 account layout support is needed, implement it by:

  1. Adding v1 = [] to light-token-types and light-token-interface
  2. Having token-sdk's v1 propagate to these crates
  3. Reviewing whether light-sdk should also be version-switchable (currently locked to v2)
🤖 Prompt for AI Agents
In `@sdk-libs/token-sdk/Cargo.toml` around lines 11 - 16, The Cargo feature "v1"
in token-sdk is a dangling stub that references light-compressed-token-sdk/v1
but has no implementation in downstream crates; either remove the v1 feature and
its propagation from token-sdk/Cargo.toml to avoid exposing a non-functional
public API, or implement full v1 support by adding a v1 feature to
light-token-types and light-token-interface (e.g., v1 = []), updating
token-sdk's v1 to propagate to those crates (and to
light-compressed-token-sdk/v1), and then audit light-sdk and light-sdk-types
(currently pinned to ["v2"]) to decide if they should be made version-switchable
or left pinned to v2.


[dependencies]
# Light Protocol dependencies
light-compressed-token-sdk = { workspace = true }
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check the actual Cargo.toml file
cat -n sdk-libs/token-sdk/Cargo.toml | head -50

Repository: Lightprotocol/light-protocol

Length of output: 2498


🏁 Script executed:

# Search for light-compressed-token-sdk to understand what it is and where it's located
find . -name "Cargo.toml" -type f | xargs grep -l "light-compressed-token-sdk" | head -20

Repository: Lightprotocol/light-protocol

Length of output: 551


🏁 Script executed:

# Look for CLAUDE.md files in token-sdk and related areas
find sdk-libs -name "CLAUDE.md" -o -name "README.md" | xargs cat 2>/dev/null | head -200

Repository: Lightprotocol/light-protocol

Length of output: 7035


🏁 Script executed:

# Check if light-compressed-token-sdk is a program-lib or sdk-lib
find . -path "*/light-compressed-token-sdk/Cargo.toml" -o -path "*/compressed-token-sdk/Cargo.toml" | head -10

Repository: Lightprotocol/light-protocol

Length of output: 114


🏁 Script executed:

# Look for CLAUDE.md or docs in token-sdk
ls -la sdk-libs/token-sdk/ | grep -E "CLAUDE|README"

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Check the structure and dependencies of light-compressed-token-sdk
cat -n sdk-libs/compressed-token-sdk/Cargo.toml | head -60

Repository: Lightprotocol/light-protocol

Length of output: 2571


🏁 Script executed:

# Look for any CLAUDE.md files in sdk-libs that document SDK dependencies
find sdk-libs -name "CLAUDE.md" -type f

Repository: Lightprotocol/light-protocol

Length of output: 174


🏁 Script executed:

# Check what's in light-token-interface (the shared interface crate mentioned in learnings)
cat -n program-libs/token-interface/Cargo.toml | head -50

Repository: Lightprotocol/light-protocol

Length of output: 2185


🏁 Script executed:

# Look at the actual imports/usage in token-sdk to understand the relationship
head -100 sdk-libs/token-sdk/src/lib.rs

Repository: Lightprotocol/light-protocol

Length of output: 4170


🏁 Script executed:

# Check if there are other SDK-to-SDK dependencies in the codebase
cd sdk-libs && for f in */Cargo.toml; do echo "=== $f ==="; grep -A 20 "\[dependencies\]" "$f" | grep -E "light-.*-sdk|light-.*-client" | grep -v "^--$"; done

Repository: Lightprotocol/light-protocol

Length of output: 705


🏁 Script executed:

# Look for documentation or comments about SDK layering in CLAUDE.md files
find . -name "CLAUDE.md" -type f | xargs grep -l "SDK\|dependencies\|layering" 2>/dev/null | head -10

Repository: Lightprotocol/light-protocol

Length of output: 217


🏁 Script executed:

# Check the specific CLAUDE.md files that might document SDK rules
cat program-libs/token-interface/CLAUDE.md 2>/dev/null || echo "No CLAUDE.md in token-interface"

Repository: Lightprotocol/light-protocol

Length of output: 103


🏁 Script executed:

# Check if there's any documentation at sdk-libs level about SDK dependencies
ls -la sdk-libs/ | head -30

Repository: Lightprotocol/light-protocol

Length of output: 1059


🏁 Script executed:

# Look at what light-token re-exports from light-compressed-token-sdk
grep -n "pub use.*light_compressed_token_sdk\|pub.*from.*light_compressed_token_sdk" sdk-libs/token-sdk/src/*.rs 2>/dev/null || grep -rn "light_compressed_token_sdk::" sdk-libs/token-sdk/src/ | head -20

Repository: Lightprotocol/light-protocol

Length of output: 540


🏁 Script executed:

# Check the root CLAUDE.md to see if there's any documentation about SDK dependencies
cat CLAUDE.md | grep -A 20 -B 5 "SDK\|dependencies" | head -80

Repository: Lightprotocol/light-protocol

Length of output: 4599


🏁 Script executed:

# Check program tests CLAUDE.md for any notes on SDK-to-SDK dependencies
cat program-tests/CLAUDE.md | head -100

Repository: Lightprotocol/light-protocol

Length of output: 2980


🏁 Script executed:

# Verify if light-compressed-token-sdk is correctly located as an SDK lib
ls -la sdk-libs/compressed-token-sdk/src/ | head -20

Repository: Lightprotocol/light-protocol

Length of output: 608


SDK layering violation: light-compressed-token-sdk dependency in SDK crate.

Per the repository's documented policy (root CLAUDE.md), SDK libs must depend only on program-libs, light-prover-client, and external crates. The dependency on light-compressed-token-sdk violates this rule. Since light-token re-exports core functionality from it (constants, error types, SPL interface, utilities), either:

  1. Move the shared logic to a program-lib (e.g., extend light-token-interface), or
  2. Document an intentional exception to the layering policy.
🤖 Prompt for AI Agents
In `@sdk-libs/token-sdk/Cargo.toml` at line 33, The Cargo.toml currently adds an
SDK-to-SDK dependency on light-compressed-token-sdk which violates the SDK
layering rule; remove that dependency and either (A) move the shared constants,
error types, SPL interface and utilities currently consumed by light-token into
a program-lib (for example extend or add them to light-token-interface) and
update light-token and this crate to depend on the program-lib instead, or (B)
if this dependency is intentional, add an explicit documented exception to the
repository policy (CLAUDE.md) and justify it in this crate's README/Cargo
manifest comments; reference the crate name light-compressed-token-sdk, the
consumer crate light-token, and the program-lib candidate light-token-interface
when making the change.

Comment on lines +3 to +5
use light_compressed_token_sdk::compressed_token::decompress_full::{
decompress_full_token_accounts_with_indices, DecompressFullIndices,
};
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find light_compressed_token_sdk crate location and examine its Cargo.toml
fd -t f "Cargo.toml" | xargs grep -l "name = \"light-compressed-token-sdk\""

Repository: Lightprotocol/light-protocol

Length of output: 112


🏁 Script executed:

#!/bin/bash
# Check the token-sdk Cargo.toml to see the dependency declaration
cat -n sdk-libs/token-sdk/Cargo.toml | head -50

Repository: Lightprotocol/light-protocol

Length of output: 2498


🏁 Script executed:

#!/bin/bash
# Search for all references to light_compressed_token_sdk in the codebase to understand its structure
rg "light.compressed.token.sdk|light-compressed-token-sdk" --type toml | head -20

Repository: Lightprotocol/light-protocol

Length of output: 1420


🏁 Script executed:

#!/bin/bash
# Check CLAUDE.md files for sdk-libs layering policies
fd -t f "CLAUDE.md" | xargs grep -l "sdk-libs" | head -10

Repository: Lightprotocol/light-protocol

Length of output: 283


🏁 Script executed:

#!/bin/bash
# Examine the actual dependency structure - check what light-token-interface contains
ls -la sdk-libs/token-interface/ 2>/dev/null || echo "Directory not found"
fd "token-interface" -t d sdk-libs/

Repository: Lightprotocol/light-protocol

Length of output: 91


🏁 Script executed:

#!/bin/bash
# Check if decompress_full functions are truly implementation-specific or should be in light-token-interface
rg "decompress_full" sdk-libs/ --type rust | head -20

Repository: Lightprotocol/light-protocol

Length of output: 1609


🏁 Script executed:

#!/bin/bash
# Examine light-compressed-token-sdk dependencies to understand its own layering
cat sdk-libs/compressed-token-sdk/Cargo.toml | head -50

Repository: Lightprotocol/light-protocol

Length of output: 1881


🏁 Script executed:

#!/bin/bash
# Check the main CLAUDE.md and sdk-libs related CLAUDE.md files for layering policies
cat CLAUDE.md | grep -A 10 -B 2 "sdk-libs"

Repository: Lightprotocol/light-protocol

Length of output: 3304


🏁 Script executed:

#!/bin/bash
# Look at sdk-libs specific documentation
cat sdk-libs/macros/CLAUDE.md 2>/dev/null | head -30

Repository: Lightprotocol/light-protocol

Length of output: 1231


🏁 Script executed:

#!/bin/bash
# Check if there's documented exception for light-compressed-token-sdk as a "core" library
rg "compressed-token-sdk|layering|architecture" CLAUDE.md --context 3

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Check what light-token-interface actually exports
ls -la sdk-libs/ | grep -E "token|interface"

Repository: Lightprotocol/light-protocol

Length of output: 327


🏁 Script executed:

#!/bin/bash
# Verify if light-compressed-token-sdk is classified as program-libs or sdk-libs by its location
find . -name "Cargo.toml" -path "*/sdk-libs/*" -o -name "Cargo.toml" -path "*/program-libs/*" | grep -E "(compressed-token|token-interface)" | head -10

Repository: Lightprotocol/light-protocol

Length of output: 156


🏁 Script executed:

#!/bin/bash
# Check if there's any exception documented in CLAUDE.md for sdk-libs → sdk-libs dependencies
rg "light-compressed-token-sdk|sdk-libs.*sdk-libs|exception|allow" CLAUDE.md programs/compressed-token/program/CLAUDE.md sdk-libs/macros/CLAUDE.md --context 2

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Verify light-token-interface location to understand if it's in program-libs
fd -t f "Cargo.toml" | xargs grep -l "name = \"light-token-interface\""

Repository: Lightprotocol/light-protocol

Length of output: 111


🏁 Script executed:

#!/bin/bash
# Check the actual directory structure to confirm locations
find . -maxdepth 3 -type d -name "*token-interface*" -o -type d -name "*compressed-token*" 2>/dev/null | head -20

Repository: Lightprotocol/light-protocol

Length of output: 222


🏁 Script executed:

#!/bin/bash
# Check what's exported from light-compressed-token-sdk and whether it's truly SDK-level code
head -100 sdk-libs/compressed-token-sdk/src/lib.rs

Repository: Lightprotocol/light-protocol

Length of output: 1383


🏁 Script executed:

#!/bin/bash
# Check if light-token-interface already has similar decompress functions or if this is truly sdk-level
rg "decompress" program-libs/token-interface/src/ --type rust | head -20

Repository: Lightprotocol/light-protocol

Length of output: 2965


🏁 Script executed:

#!/bin/bash
# Understand the dependency graph - does light-compressed-token-sdk depend only on program-libs?
cat sdk-libs/compressed-token-sdk/Cargo.toml | grep -A 30 "\[dependencies\]"

Repository: Lightprotocol/light-protocol

Length of output: 1167


🏁 Script executed:

#!/bin/bash
# Look at what DecompressFullIndices and decompress_full_token_accounts_with_indices actually are
cat sdk-libs/compressed-token-sdk/src/compressed_token/v2/decompress_full.rs | head -80

Repository: Lightprotocol/light-protocol

Length of output: 3585


🏁 Script executed:

#!/bin/bash
# Verify the import in decompress_runtime.rs is indeed from sdk-libs/compressed-token-sdk
cat -n sdk-libs/token-sdk/src/compressible/decompress_runtime.rs | head -30

Repository: Lightprotocol/light-protocol

Length of output: 1638


🏁 Script executed:

#!/bin/bash
# Check if any other sdk-libs crates have similar sdk-libs → sdk-libs dependencies
rg "light-.*-sdk" sdk-libs/*/Cargo.toml | grep "dependencies" -A 20 | head -40

Repository: Lightprotocol/light-protocol

Length of output: 54


Move DecompressFullIndices and decompress_full_token_accounts_with_indices to program-libs/token-interface/ to fix sdk-libs dependency layering.

light-token-sdk importing from light-compressed-token-sdk violates the architectural policy. SDK libs must depend only on program-libs, light-prover-client, and external crates—not on other sdk-libs. These decompress functions are core data structures needed across the SDK layer and belong in program-libs/token-interface/ alongside other compressed token abstractions, allowing both light-compressed-token-sdk and light-token-sdk to depend on them without creating sdk-libs-to-sdk-libs coupling.

🤖 Prompt for AI Agents
In `@sdk-libs/token-sdk/src/compressible/decompress_runtime.rs` around lines 3 -
5, The import in decompress_runtime.rs pulls DecompressFullIndices and
decompress_full_token_accounts_with_indices from light_compressed_token_sdk
which creates an sdk-libs-to-sdk-libs dependency; move the definitions of
DecompressFullIndices and decompress_full_token_accounts_with_indices into
program-libs/token-interface so both light-compressed-token-sdk and
light-token-sdk can depend on them, then update decompress_runtime.rs to import
these symbols from the new program-libs/token-interface crate (replace the
current use of light_compressed_token_sdk::compressed_token::decompress_full
with the corresponding module path in program-libs/token-interface) and remove
the old cross-sdklib dependency.

@ananas-block ananas-block force-pushed the jorrit/refactor-separate-light-compressed-token-sdks branch from 67b207c to a7bc104 Compare January 22, 2026 20:26
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: 7

Caution

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

⚠️ Outside diff range comments (2)
sdk-libs/token-sdk/tests/mint_action_cpi_accounts_tests.rs (2)

27-36: Acceptable test helper, but note potential for data race.

The static mut COUNTER pattern works for generating unique keys within individual tests. Since Rust's test harness runs tests in parallel by default, there's technically a data race here. In practice, collisions are unlikely to cause test failures since each test only needs internal uniqueness.

If you ever see flaky tests, consider using AtomicU8 instead:

♻️ Thread-safe alternative (optional)
+use std::sync::atomic::{AtomicU8, Ordering};
+
+static COUNTER: AtomicU8 = AtomicU8::new(0);
+
 fn pubkey_unique() -> [u8; 32] {
-    static mut COUNTER: u8 = 0;
     let mut key = [0u8; 32];
-    unsafe {
-        COUNTER = COUNTER.wrapping_add(1);
-        key[0] = COUNTER;
-    }
+    key[0] = COUNTER.fetch_add(1, Ordering::Relaxed);
     key
 }

504-505: Duplicate assertion — appears to be a copy-paste artifact.

This assert!(result.is_err()); is repeated twice. The same pattern occurs in several other test functions (lines 571-572, 643-644, 715-716, 781-782, 862-863).

🧹 Remove duplicate assertions
     let result = MintActionCpiAccounts::<AccountInfo>::try_from_account_infos(&accounts);
     assert!(result.is_err());
-    assert!(result.is_err());
 }

Apply the same fix to the other affected tests.

🤖 Fix all issues with AI agents
In `@sdk-libs/compressed-token-sdk/Cargo.toml`:
- Around line 9-16: The feature "v1" in Cargo.toml is currently an empty/no-op
feature (v1 = []); confirm intended behavior and either wire it to toggle the
appropriate feature flags (e.g., map "v1" to the legacy layout or API features
used elsewhere) or remove it and add a clear comment documenting it as a
compatibility placeholder; locate the "v1" entry alongside other features like
"anchor-discriminator", "anchor", and "idl-build" and update Cargo.toml
accordingly to either reference the intended dependency features or add a
comment explaining that v1 is intentionally a no-op for compatibility.
- Around line 31-38: This introduces an SDK layering violation by making
light-sdk and light-sdk-types depend directly on each other (see light-sdk and
light-sdk-types in the Cargo.toml diff) — either confirm+document this exception
or move the shared types into a program-lib; specifically, if the dependency is
intentional, add a clear comment/README entry explaining the exception and
rationale where light-sdk and light-sdk-types are defined, otherwise migrate the
shared type definitions from light-sdk-types into a program-lib (e.g., alongside
light-token-interface) so SDKs can depend on program-libs only and update the
Cargo.toml to remove the SDK->SDK dependency.

In `@sdk-libs/compressed-token-sdk/src/utils.rs`:
- Around line 3-5: The code imports LIGHT_TOKEN_PROGRAM_ID from light_sdk_types
but the project should use its own source of truth; replace any import/usage of
light_sdk_types::LIGHT_TOKEN_PROGRAM_ID with
crate::constants::LIGHT_TOKEN_PROGRAM_ID (or
light_sdk::constants::LIGHT_TOKEN_PROGRAM_ID if that is the crate path used
elsewhere) so that all references (the use statement and any places referring to
LIGHT_TOKEN_PROGRAM_ID) reference the local constant instead of light_sdk_types.

In `@sdk-libs/program-test/Cargo.toml`:
- Line 24: The Cargo.toml entry adding light-compressed-token-sdk to
light-program-test violates the layering rule; remove the direct dependency on
light-compressed-token-sdk from the light-program-test Cargo.toml and instead
import the shared types from the program-libs package: move the needed types
into the light-token-interface crate (under program-libs), update their public
API there, then change light-program-test to depend on program-libs (or
light-token-interface) and update all uses/imports to reference the moved types;
ensure any tests or code referencing symbols from light-compressed-token-sdk
(e.g., token type names) are updated to the new module paths and run cargo
build/tests to verify no remaining cross-SDK imports.

In `@sdk-libs/program-test/src/forester/compress_and_close_forester.rs`:
- Line 5: The current use statement imports CompressAndCloseAccounts from
another SDK crate
(light_compressed_token_sdk::compressed_token::CompressAndCloseAccounts), which
creates an SDK-to-SDK dependency; move the CompressAndCloseAccounts type into a
shared program-lib (e.g., light-token-interface or light-compressed-account) and
update consumers to import from that program-lib instead of
light_compressed_token_sdk. Specifically, define the struct in the chosen
program-lib crate, export it from a clear module, then replace the existing use
line in compress_and_close_forester.rs to import the type from the new
program-lib module (and update any paths/names to match the new location).

In `@sdk-libs/token-sdk/src/constants.rs`:
- Around line 25-44: Add unit tests that assert the hard-coded PDAs
COMPRESSIBLE_CONFIG_V1 and RENT_SPONSOR_V1 match the PDAs derived by
CompressibleConfig::light_token_v1_config_pda() and
CompressibleConfig::light_token_v1_rent_sponsor_pda() respectively; implement
these tests in the crate's tests module (or #[cfg(test)] mod) so they run in CI,
failing if the constants drift from the derived values, and include similar
coverage for compression_authority_pda() vs
CompressibleConfig::light_token_v1_compression_authority_pda() to fully lock the
three PDAs.

In `@sdk-libs/token-sdk/src/instruction/mod.rs`:
- Around line 200-203: The implementation of Default for SystemAccounts pulls
TokenDefaultAccounts from light_compressed_token_sdk, causing an SDK-libs
layering breach; fix by removing that dependency: either move
TokenDefaultAccounts into an allowed crate (e.g., program-libs/shared) and
import it there, or re-implement the needed default values inside
token-sdk::instruction::SystemAccounts (or a token-sdk-local
TokenDefaultAccounts) so token-sdk no longer imports light_compressed_token_sdk;
if you intend to keep the cross-crate dependency, add an explicit documented
exception and update the dependency guideline docs accordingly. Ensure
references to SystemAccounts and TokenDefaultAccounts are updated to the chosen
location so the Default impl compiles without importing
light_compressed_token_sdk.
♻️ Duplicate comments (20)
sdk-libs/client/src/interface/create_accounts_proof.rs (1)

4-6: SDK layering violation: sdk-libs shouldn’t depend on another sdk-libs crate.
Importing derive_mint_compressed_address from light_compressed_token_sdk (Line 5) introduces an sdk-libs → sdk-libs dependency. Move this helper into a program-lib (e.g., light-token-interface) or explicitly document an exception if light-compressed-token-sdk is meant to be a new layering tier. As per coding guidelines, sdk-libs must depend on program-libs, light-prover-client, and external crates only.

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

6-8: SDK layering violation: sdk-libs → sdk-libs dependency.
Line 6 imports from light_compressed_token_sdk, which is another sdk-libs crate. Please move the shared API into a program-lib (e.g., light-token-interface) or document an explicit exception if this new crate is intended to sit below all SDKs. As per coding guidelines, sdk-libs must depend on program-libs, light-prover-client, and external crates only.

sdk-libs/token-sdk/src/instruction/create_mints.rs (1)

14-17: SDK layering violation remains for MintActionMetaConfig types.
Line 14–17 still import MintAction meta config types from light_compressed_token_sdk, which is another sdk-libs crate. These types should live in light-token-interface (program-lib) or an approved lower-level crate, or the policy should explicitly allow this new dependency tier. As per coding guidelines, sdk-libs must depend on program-libs, light-prover-client, and external crates only.

sdk-libs/token-sdk/src/instruction/decompress_mint.rs (1)

1-5: SDK layering violation: MintActionMetaConfig import.
Line 4 pulls MintActionMetaConfig from light_compressed_token_sdk, which breaks the sdk-libs dependency rule. Please move this type to a program-lib (e.g., light-token-interface) or document an explicit exception for the new SDK layer. As per coding guidelines, sdk-libs must depend on program-libs, light-prover-client, and external crates only.

sdk-libs/compressed-token-sdk/src/compat.rs (1)

115-123: Don’t silently coerce unknown account states.
This still defaults invalid state bytes to Initialized, masking corrupted data. Prefer a fallible conversion that returns ProgramError::InvalidAccountData.

sdk-libs/compressed-token-sdk/src/spl_interface.rs (1)

55-123: Potential panic on existing_spl_interface_pda.unwrap() with public fields.
Since the struct is publicly constructible, a caller can set index > 0 without providing existing_spl_interface_pda, leading to a panic. Consider making fields private with safe constructors, or return a Result from instruction() and validate the invariant.

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

5-7: Route compressed‑mint helpers through program‑libs, not another SDK.

Directly importing light_compressed_token_sdk in sdk‑libs source violates the dependency boundary; use a program‑libs wrapper/re‑export instead. As per coding guidelines, ...

sdk-libs/token-client/Cargo.toml (1)

17-17: SDK‑to‑SDK dependency breaks sdk‑libs policy.

light-token-client should not depend on another SDK crate; move shared utilities into a program‑libs crate (e.g., light-token-interface) and depend on that instead. Based on learnings, ...

sdk-libs/token-sdk/src/instruction/transfer_from_spl.rs (1)

2-7: Dependency layering concern already flagged.

This import from light_compressed_token_sdk (another sdk-lib) into light-token-sdk has been identified in a previous review as potentially violating the sdk-libs dependency constraints. As per coding guidelines, sdk-libs should depend only on program-libs, light-prover-client, and external crates.

sdk-libs/token-sdk/src/instruction/transfer_to_spl.rs (1)

2-7: Same dependency layering concern as transfer_from_spl.rs.

This file exhibits the identical pattern—importing transfer2 types from light_compressed_token_sdk (an sdk-lib) into light-token-sdk (another sdk-lib). The architectural concern raised for transfer_from_spl.rs applies equally here. As per coding guidelines, sdk-libs should only depend on program-libs, light-prover-client, and external crates.

sdk-libs/program-test/src/compressible.rs (1)

392-392: Consistent with the dependency layering concern in token-sdk.

This import pulls MintActionMetaConfig from light_compressed_token_sdk into light-program-test, which is another sdk-lib. If the team decides to restructure dependencies per the coding guidelines (moving shared types to a program-lib), this import path would need updating accordingly.

sdk-libs/client/src/interface/load_accounts.rs (1)

6-11: Same dependency layering pattern as token-sdk modules.

The light-client crate (an sdk-lib) now imports transfer2 types from light_compressed_token_sdk (another sdk-lib). This follows the same architectural pattern flagged in the token-sdk files. If shared types are relocated to a program-lib to satisfy the dependency constraints, this import path would need corresponding updates.

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

4-4: Same dependency layering pattern for MintActionMetaConfig.

This import from light_compressed_token_sdk into light-token-sdk follows the same pattern discussed in other files. The concern about sdk-libs depending on other sdk-libs applies here as well.

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

6-14: Import paths correctly updated to use the new SDK surface.

The migration from light_token::compressed_token to light_compressed_token_sdk::compressed_token aligns with the PR's refactoring goals. The SplInterfacePda is now properly imported at the top level, addressing the previous nitpick.

Note: The sdk-lib → sdk-lib dependency boundary concern was previously flagged and remains relevant for architectural documentation.

sdk-libs/token-sdk/src/instruction/transfer_interface.rs (1)

1-1: Avoid sdk-libs → sdk-libs dependency for ownership helper.

light-token-sdk now depends on light-compressed-token-sdk via is_light_token_owner, which violates the sdk-libs layering rule. Please move/shared this helper into a program-libs crate (e.g., program-libs/token-interface) or keep a local helper in each SDK to preserve dependency direction. As per coding guidelines, sdk-libs must only depend on program-libs, light-prover-client, and external crates.

sdk-libs/token-sdk/src/compressible/decompress_runtime.rs (1)

3-5: SDK layering violation remains at import boundary.

Pulling DecompressFullIndices and decompress_full_token_accounts_with_indices from another sdk-libs crate keeps the forbidden dependency direction. Consider relocating these shared primitives into a program-libs crate so both SDKs can depend on it. As per coding guidelines, sdk-libs must only depend on program-libs, light-prover-client, and external crates.

sdk-libs/token-sdk/src/spl_interface.rs (1)

6-7: Re-export introduces forbidden sdk-libs coupling.

pub use light_compressed_token_sdk::spl_interface::* makes light-token-sdk depend on another sdk-libs crate. Please move shared SPL interface types/utilities into a program-libs crate (e.g., program-libs/token-interface) and re-export from both SDKs. As per coding guidelines, sdk-libs must only depend on program-libs, light-prover-client, and external crates.

sdk-libs/token-sdk/Cargo.toml (2)

9-16: v1 feature still appears to be a no-op.
It only enables light-compressed-token-sdk/v1, which is empty; consider wiring it through or documenting it as a compatibility placeholder.


31-34: SDK-to-SDK dependency still conflicts with the layering policy.
Line 33 adds a dependency on light-compressed-token-sdk; if SDKs must only depend on program-libs/external crates, please document the exception or relocate shared types to a program-lib. As per coding guidelines, SDK-libs must depend on program-libs, light-prover-client and external crates only.

sdk-libs/compressed-token-sdk/src/utils.rs (1)

79-105: Prefer an explicit System Program ID over Pubkey::default().
Using a named constant makes intent clearer and avoids relying on implicit all-zero semantics.

♻️ Suggested tweak
-use solana_pubkey::Pubkey;
+use solana_pubkey::{pubkey, Pubkey};
+
+const SYSTEM_PROGRAM_ID: Pubkey = pubkey!("11111111111111111111111111111111");
...
-            system_program: Pubkey::default(),
+            system_program: SYSTEM_PROGRAM_ID,

Comment on lines +9 to +16
[features]
default = ["anchor-discriminator"]
v1 = []
anchor-discriminator = ["light-sdk/anchor-discriminator"]
anchor = ["anchor-lang", "light-token-types/anchor", "light-token-interface/anchor"]
# idl-build feature enables IDL generation for client-side tooling and SDK generation
idl-build = ["anchor", "anchor-lang/idl-build", "light-sdk/idl-build"]
cpi-context = ["light-sdk/cpi-context"]
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

Confirm whether v1 is intended to be a no-op feature.
Line 11 defines v1 = [], which doesn’t toggle any dependency features; if it should switch layouts/APIs, wire it through or document it as a compatibility placeholder.

🤖 Prompt for AI Agents
In `@sdk-libs/compressed-token-sdk/Cargo.toml` around lines 9 - 16, The feature
"v1" in Cargo.toml is currently an empty/no-op feature (v1 = []); confirm
intended behavior and either wire it to toggle the appropriate feature flags
(e.g., map "v1" to the legacy layout or API features used elsewhere) or remove
it and add a clear comment documenting it as a compatibility placeholder; locate
the "v1" entry alongside other features like "anchor-discriminator", "anchor",
and "idl-build" and update Cargo.toml accordingly to either reference the
intended dependency features or add a comment explaining that v1 is
intentionally a no-op for compatibility.

Comment on lines +31 to +38
light-token-types = { workspace = true }
light-compressed-account = { workspace = true, features = ["std", "solana"] }
light-token-interface = { workspace = true }
light-sdk = { workspace = true, features = ["v2"] }
light-sdk-types = { workspace = true, features = ["v2"] }
light-account-checks = { workspace = true, features = ["solana"] }
light-zero-copy = { workspace = true }
light-program-profiler = { workspace = true }
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find where light-sdk and light-sdk-types are located in the repo
fd -t f "Cargo.toml" | xargs grep -l 'name = "light-sdk"' | head -5

Repository: Lightprotocol/light-protocol

Length of output: 95


🏁 Script executed:

# Also check the structure of the repo to understand layering
git ls-files | grep -E "(sdk-libs|program-libs)" | head -20

Repository: Lightprotocol/light-protocol

Length of output: 1067


🏁 Script executed:

# Check the CLAUDE.md files mentioned in learnings
find . -name "CLAUDE.md" -type f | head -10

Repository: Lightprotocol/light-protocol

Length of output: 452


🏁 Script executed:

# Find light-sdk-types location
fd -t f "Cargo.toml" | xargs grep -l 'name = "light-sdk-types"'

Repository: Lightprotocol/light-protocol

Length of output: 101


🏁 Script executed:

# Read the main CLAUDE.md to understand SDK dependency policies
cat ./CLAUDE.md

Repository: Lightprotocol/light-protocol

Length of output: 11426


🏁 Script executed:

# Check the sdk-libs/sdk/Cargo.toml to see what light-sdk depends on
cat sdk-libs/sdk/Cargo.toml

Repository: Lightprotocol/light-protocol

Length of output: 2627


🏁 Script executed:

# Check if there are CLAUDE.md files in sdk-libs
find sdk-libs -name "CLAUDE.md" -type f

Repository: Lightprotocol/light-protocol

Length of output: 174


🏁 Script executed:

# Check light-sdk-types Cargo.toml dependencies
cat sdk-libs/sdk-types/Cargo.toml

Repository: Lightprotocol/light-protocol

Length of output: 1116


🏁 Script executed:

# Check what compressed-token-sdk is and its structure
head -50 sdk-libs/compressed-token-sdk/Cargo.toml

Repository: Lightprotocol/light-protocol

Length of output: 1881


Confirm SDK layering violation: light-sdk and light-sdk-types are SDK-lib dependencies, which violates the policy requiring SDK-libs to depend on program-libs, light-prover-client, and external crates only.

Lines 34–35 introduce SDK-to-SDK dependencies. Per the documented policy and the learning that "data structures must be isolated in program-libs (like light-token-interface) so SDKs can import types without pulling in program dependencies," either confirm this exception is intentional and document it, or migrate light-sdk-types to program-libs/ where shared SDK types belong.

🤖 Prompt for AI Agents
In `@sdk-libs/compressed-token-sdk/Cargo.toml` around lines 31 - 38, This
introduces an SDK layering violation by making light-sdk and light-sdk-types
depend directly on each other (see light-sdk and light-sdk-types in the
Cargo.toml diff) — either confirm+document this exception or move the shared
types into a program-lib; specifically, if the dependency is intentional, add a
clear comment/README entry explaining the exception and rationale where
light-sdk and light-sdk-types are defined, otherwise migrate the shared type
definitions from light-sdk-types into a program-lib (e.g., alongside
light-token-interface) so SDKs can depend on program-libs only and update the
Cargo.toml to remove the SDK->SDK dependency.

Comment on lines +3 to +5
use light_sdk::constants::REGISTERED_PROGRAM_PDA;
use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID;
use light_token_interface::{
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

Prefer this crate’s LIGHT_TOKEN_PROGRAM_ID constant as the source of truth.
Using crate::constants::LIGHT_TOKEN_PROGRAM_ID avoids potential drift if light_sdk_types changes.

♻️ Suggested tweak
-use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID;
+use crate::constants::LIGHT_TOKEN_PROGRAM_ID;
🤖 Prompt for AI Agents
In `@sdk-libs/compressed-token-sdk/src/utils.rs` around lines 3 - 5, The code
imports LIGHT_TOKEN_PROGRAM_ID from light_sdk_types but the project should use
its own source of truth; replace any import/usage of
light_sdk_types::LIGHT_TOKEN_PROGRAM_ID with
crate::constants::LIGHT_TOKEN_PROGRAM_ID (or
light_sdk::constants::LIGHT_TOKEN_PROGRAM_ID if that is the crate path used
elsewhere) so that all references (the use statement and any places referring to
LIGHT_TOKEN_PROGRAM_ID) reference the local constant instead of light_sdk_types.

light-token-interface = { workspace = true }
light-compressible = { workspace = true }
light-token = { workspace = true }
light-compressed-token-sdk = { workspace = true }
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find the crates mentioned in the review
fd -t d "program-test|light-compressed-token-sdk" --full-path "*sdk-libs*"

Repository: Lightprotocol/light-protocol

Length of output: 169


🏁 Script executed:

# Check the structure under sdk-libs
ls -la sdk-libs/ 2>/dev/null | head -20

Repository: Lightprotocol/light-protocol

Length of output: 1118


🏁 Script executed:

# Read the Cargo.toml file in question
cat -n sdk-libs/program-test/Cargo.toml

Repository: Lightprotocol/light-protocol

Length of output: 3387


🏁 Script executed:

# Check if light-compressed-token-sdk exists and where
fd "light-compressed-token-sdk" -t d

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Verify the overall directory structure
tree -L 2 sdk-libs/ 2>/dev/null | head -50

Repository: Lightprotocol/light-protocol

Length of output: 868


🏁 Script executed:

# Check if compressed-token-sdk in sdk-libs is the light-compressed-token-sdk
cat sdk-libs/compressed-token-sdk/Cargo.toml | head -5

Repository: Lightprotocol/light-protocol

Length of output: 246


🏁 Script executed:

# Look for where light-compressed-token-sdk might be defined (check workspace)
cat Cargo.toml | grep -A 5 "light-compressed-token-sdk"

Repository: Lightprotocol/light-protocol

Length of output: 410


🏁 Script executed:

# Check what light-program-test actually imports from light-compressed-token-sdk
rg "light.compressed.token.sdk|from.*compressed.*token.*sdk" sdk-libs/program-test/src/ -A 2

Repository: Lightprotocol/light-protocol

Length of output: 809


🏁 Script executed:

# Verify the structure - is light-token-interface in program-libs?
fd "light-token-interface" -t d

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Check the CLAUDE.md constraints
cat CLAUDE.md 2>/dev/null | grep -A 10 "SDK-libs"

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Check what's in program-libs
ls -la program-libs/ 2>/dev/null | head -20

Repository: Lightprotocol/light-protocol

Length of output: 1269


🏁 Script executed:

# Check what light-compressed-token-sdk exports and its dependencies
cat sdk-libs/compressed-token-sdk/Cargo.toml

Repository: Lightprotocol/light-protocol

Length of output: 2266


🏁 Script executed:

# Check light-token-interface location and purpose
cat program-libs/token-interface/Cargo.toml | head -30

Repository: Lightprotocol/light-protocol

Length of output: 1085


🏁 Script executed:

# Verify if CompressAndCloseAccounts or MintActionMetaConfig types exist in light-token-interface
rg "CompressAndCloseAccounts|MintActionMetaConfig" program-libs/token-interface/src/ -l

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Check the purpose and context of light-program-test - is it a test utility?
cat sdk-libs/program-test/Cargo.toml | grep -A 5 "^\[lib\]\|^\[description\]"

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Look at CLAUDE.md rules about program-libs and sdk-libs more carefully
cat CLAUDE.md | grep -B 5 -A 15 "SDK-libs must depend"

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Check if light-compressed-token-sdk is documented as an exception or special case
cat sdk-libs/compressed-token-sdk/Cargo.toml -A 20 | head -40

Repository: Lightprotocol/light-protocol

Length of output: 1619


🏁 Script executed:

# Check what types/modules are actually used from light-compressed-token-sdk
rg "light_compressed_token_sdk::" sdk-libs/program-test/src/ | cut -d: -f2 | sort -u

Repository: Lightprotocol/light-protocol

Length of output: 137


🏁 Script executed:

# Check if those types exist in light-token-interface
rg "struct|enum" program-libs/token-interface/src/ | grep -i "mint\|compress" | head -20

Repository: Lightprotocol/light-protocol

Length of output: 2127


🏁 Script executed:

# Read the README for light-program-test to understand its role
cat sdk-libs/program-test/README.md 2>/dev/null || echo "No README found"

Repository: Lightprotocol/light-protocol

Length of output: 87


🏁 Script executed:

# Check CLAUDE.md more carefully for full context on SDK-libs rules
head -100 CLAUDE.md

Repository: Lightprotocol/light-protocol

Length of output: 5665


🏁 Script executed:

# See if there are any other SDK-to-SDK dependencies in sdk-libs crates
rg "light-(token|client|event|sdk|compressed)" sdk-libs/*/Cargo.toml | grep -v "workspace = true" | grep -v "optional = true" | grep -v "#"

Repository: Lightprotocol/light-protocol

Length of output: 4595


SDK-to-SDK dependencies violate the architecture boundary.

light-program-test in sdk-libs must depend only on program-libs, light-prover-client, and external crates. The direct dependency on light-compressed-token-sdk (another sdk-libs crate) breaches this rule.

Move the required types to light-token-interface (program-libs) so light-program-test can import them from the program-libs layer instead.

🤖 Prompt for AI Agents
In `@sdk-libs/program-test/Cargo.toml` at line 24, The Cargo.toml entry adding
light-compressed-token-sdk to light-program-test violates the layering rule;
remove the direct dependency on light-compressed-token-sdk from the
light-program-test Cargo.toml and instead import the shared types from the
program-libs package: move the needed types into the light-token-interface crate
(under program-libs), update their public API there, then change
light-program-test to depend on program-libs (or light-token-interface) and
update all uses/imports to reference the moved types; ensure any tests or code
referencing symbols from light-compressed-token-sdk (e.g., token type names) are
updated to the new module paths and run cargo build/tests to verify no remaining
cross-SDK imports.

indexer::Indexer,
rpc::{Rpc, RpcError},
};
use light_compressed_token_sdk::compressed_token::CompressAndCloseAccounts as CTokenCompressAndCloseAccounts;
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's find and examine the Cargo.toml for sdk-libs/program-test
find sdk-libs/program-test -name "Cargo.toml" -type f

Repository: Lightprotocol/light-protocol

Length of output: 104


🏁 Script executed:

# Let's also check the file in question
cat -n sdk-libs/program-test/src/forester/compress_and_close_forester.rs | head -20

Repository: Lightprotocol/light-protocol

Length of output: 816


🏁 Script executed:

# Search for light_compressed_token_sdk definition to understand its nature
find . -name "light_compressed_token_sdk" -o -name "*compressed_token_sdk*" | grep -v target | head -20

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

# Check if light_compressed_token_sdk is in program-libs or sdk-libs
fd "Cargo.toml" | xargs grep -l "name = \"light_compressed_token_sdk\"" | head -5

Repository: Lightprotocol/light-protocol

Length of output: 54


🏁 Script executed:

cat sdk-libs/program-test/Cargo.toml

Repository: Lightprotocol/light-protocol

Length of output: 2904


🏁 Script executed:

# Search more broadly for compressed_token related crates
rg "name = \"light_compressed_token" --type toml

Repository: Lightprotocol/light-protocol

Length of output: 148


🏁 Script executed:

# Check if it's in sdk-libs or program-libs
fd "Cargo.toml" sdk-libs sdk-libs program-libs | xargs grep -l "light_compressed_token"

Repository: Lightprotocol/light-protocol

Length of output: 56


🏁 Script executed:

# Search for light-compressed-token-sdk crate definition
find . -name "Cargo.toml" -type f ! -path "*/target/*" | xargs grep -l "^name = \"light-compressed-token-sdk\"" | head -5

Repository: Lightprotocol/light-protocol

Length of output: 114


🏁 Script executed:

# Also search with different naming patterns
find . -name "Cargo.toml" -type f ! -path "*/target/*" | xargs grep -l "light.compressed.token.sdk" | head -10

Repository: Lightprotocol/light-protocol

Length of output: 447


🏁 Script executed:

# Check directory structure
ls -la sdk-libs/ | grep -i compress
ls -la program-libs/ | grep -i compress

Repository: Lightprotocol/light-protocol

Length of output: 274


🏁 Script executed:

cat sdk-libs/compressed-token-sdk/Cargo.toml

Repository: Lightprotocol/light-protocol

Length of output: 2266


🏁 Script executed:

# Check what CompressAndCloseAccounts is and where it comes from
rg "struct CompressAndCloseAccounts|type CompressAndCloseAccounts" sdk-libs/compressed-token-sdk/src/ -A 3

Repository: Lightprotocol/light-protocol

Length of output: 527


SDK‑to‑SDK import violates sdk‑libs boundary.

light_compressed_token_sdk is itself an sdk-lib crate. SDK libraries must depend only on program-libs, light-prover-client, and external crates. Types like CompressAndCloseAccounts should be defined in a program-libs crate (such as light-token-interface or light-compressed-account) so both SDK libraries can import from the shared program-lib layer without creating SDK-to-SDK dependencies. Move the struct to the appropriate program-lib and update the import accordingly.

🤖 Prompt for AI Agents
In `@sdk-libs/program-test/src/forester/compress_and_close_forester.rs` at line 5,
The current use statement imports CompressAndCloseAccounts from another SDK
crate (light_compressed_token_sdk::compressed_token::CompressAndCloseAccounts),
which creates an SDK-to-SDK dependency; move the CompressAndCloseAccounts type
into a shared program-lib (e.g., light-token-interface or
light-compressed-account) and update consumers to import from that program-lib
instead of light_compressed_token_sdk. Specifically, define the struct in the
chosen program-lib crate, export it from a clear module, then replace the
existing use line in compress_and_close_forester.rs to import the type from the
new program-lib module (and update any paths/names to match the new location).

Comment on lines +25 to +44
/// Default compressible config PDA (V1)
pub const COMPRESSIBLE_CONFIG_V1: Pubkey = pubkey!("ACXg8a7VaqecBWrSbdu73W4Pg9gsqXJ3EXAqkHyhvVXg");

/// Default rent sponsor PDA (V1)
pub const RENT_SPONSOR_V1: Pubkey = pubkey!("r18WwUxfG8kQ69bQPAB2jV6zGNKy3GosFGctjQoV4ti");

/// Returns the default compressible config PDA.
pub fn config_pda() -> Pubkey {
CompressibleConfig::light_token_v1_config_pda()
}

/// Returns the default rent sponsor PDA.
pub fn rent_sponsor_pda() -> Pubkey {
CompressibleConfig::light_token_v1_rent_sponsor_pda()
}

/// Returns the compression authority PDA.
pub fn compression_authority_pda() -> Pubkey {
CompressibleConfig::light_token_v1_compression_authority_pda()
}
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

Guard against drift between hard-coded PDAs and derived PDAs.
Line 26 and Line 29 hard-code PDAs while Line 32/37 derive them via CompressibleConfig; a small test helps ensure they never diverge.

✅ Suggested test to lock PDA constants
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn pda_constants_match_derivations() {
+        assert_eq!(COMPRESSIBLE_CONFIG_V1, CompressibleConfig::light_token_v1_config_pda());
+        assert_eq!(RENT_SPONSOR_V1, CompressibleConfig::light_token_v1_rent_sponsor_pda());
+    }
+}
🤖 Prompt for AI Agents
In `@sdk-libs/token-sdk/src/constants.rs` around lines 25 - 44, Add unit tests
that assert the hard-coded PDAs COMPRESSIBLE_CONFIG_V1 and RENT_SPONSOR_V1 match
the PDAs derived by CompressibleConfig::light_token_v1_config_pda() and
CompressibleConfig::light_token_v1_rent_sponsor_pda() respectively; implement
these tests in the crate's tests module (or #[cfg(test)] mod) so they run in CI,
failing if the constants drift from the derived values, and include similar
coverage for compression_authority_pda() vs
CompressibleConfig::light_token_v1_compression_authority_pda() to fully lock the
three PDAs.

Comment on lines 200 to 203
impl Default for SystemAccounts {
fn default() -> Self {
use crate::utils::TokenDefaultAccounts;
use light_compressed_token_sdk::utils::TokenDefaultAccounts;
let defaults = TokenDefaultAccounts::default();
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 | 🟠 Major

SDK-libs dependency layering breach.
token-sdk now depends on light_compressed_token_sdk via TokenDefaultAccounts, which conflicts with the guideline that SDK-libs should depend only on program-libs, light-prover-client, or external crates. Please either move TokenDefaultAccounts to an allowed crate (e.g., program-libs/shared), or document an explicit exception and update the guideline.

As per coding guidelines, ensure SDK-libs dependency direction stays compliant.

🤖 Prompt for AI Agents
In `@sdk-libs/token-sdk/src/instruction/mod.rs` around lines 200 - 203, The
implementation of Default for SystemAccounts pulls TokenDefaultAccounts from
light_compressed_token_sdk, causing an SDK-libs layering breach; fix by removing
that dependency: either move TokenDefaultAccounts into an allowed crate (e.g.,
program-libs/shared) and import it there, or re-implement the needed default
values inside token-sdk::instruction::SystemAccounts (or a token-sdk-local
TokenDefaultAccounts) so token-sdk no longer imports light_compressed_token_sdk;
if you intend to keep the cross-crate dependency, add an explicit documented
exception and update the dependency guideline docs accordingly. Ensure
references to SystemAccounts and TokenDefaultAccounts are updated to the chosen
location so the Default impl compiles without importing
light_compressed_token_sdk.

The cpi-context feature was missing from light-compressed-token-sdk,
causing the DecompressFullAccounts implementation to not pass the
cpi_context to SystemAccountMetaConfig. This resulted in
test_decompress_full_cpi_with_context failing with error 20001
(AccountOwnedByWrongProgram).
@ananas-block ananas-block force-pushed the jorrit/refactor-separate-light-compressed-token-sdks branch from a7bc104 to 07d0794 Compare January 23, 2026 15:15
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: 3

Caution

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

⚠️ Outside diff range comments (2)
sdk-libs/token-sdk/src/instruction/decompress_mint.rs (1)

198-215: Fix the account order documentation—mint_signer is not included for decompress operations.

The comment on lines 172-187 incorrectly documents position 2 as "mint_signer (no sign for decompress)". For decompress, MintActionMetaConfig::new() sets mint_signer: None, so to_account_metas() skips it entirely. The actual first account in the list should be authority at position 2. Update the comment to accurately reflect the decompress-specific account order, or add a note explaining that mint_signer is conditional and omitted here.

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

1-21: Remove sdk-lib cross-dependency: move shared types to a program-lib.

SDK-libs must only depend on program-libs, light-prover-client, and external crates. light-token currently imports from light-compressed-token-sdk (both are sdk-libs), which violates this boundary. Move MintActionMetaConfig and related types to a program-lib like light-token-interface so both sdk-libs can import them without creating cross-dependencies.

🤖 Fix all issues with AI agents
In `@sdk-libs/compressed-token-sdk/src/spl_interface.rs`:
- Around line 97-132: The AccountMeta entries for the system program in the
instruction() method use Pubkey::default() which is incorrect; replace those
with the actual System Program ID constant (11111111111111111111111111111111) by
adding a SYSTEM_PROGRAM_ID constant and using Pubkey::from(SYSTEM_PROGRAM_ID)
(or the equivalent Sysvar/const) in both the CreateTokenPool and AddTokenPool
branches where AccountMeta::new_readonly(Pubkey::default(), false) currently
appears (within instruction()) so the system program account is correct when
building the Instruction.

In `@sdk-libs/token-client/src/actions/transfer2/spl_to_ctoken.rs`:
- Around line 5-8: This file introduces a forbidden SDK‑to‑SDK dependency by
importing SPL_TOKEN_PROGRAM_ID and find_spl_interface_pda from
light_compressed_token_sdk; remove that direct import and instead consume those
SPL interface helpers from the program‑lib layer (or from a program‑lib
reexport). Update the use statements in spl_to_ctoken.rs to import
SPL_TOKEN_PROGRAM_ID and find_spl_interface_pda from the program‑lib
crate/module that re-exports them (and keep using TransferFromSpl from
light_token), and if needed move or re-export the helper functions/constants
into program‑lib so sdk‑libs no longer depend on another sdk‑lib.

In `@sdk-libs/token-client/src/instructions/update_compressed_mint.rs`:
- Around line 6-8: The current import of
light_compressed_token_sdk::compressed_token::update_compressed_mint (symbols
update_compressed_mint and UpdateMintInputs) violates sdk‑libs layering; move
the shared instruction helpers (the update_compressed_mint function and the
UpdateMintInputs type) into the program‑lib (for example light-token-interface)
or add an approved re‑export in that program‑lib, then update this file to
import update_compressed_mint and UpdateMintInputs from the program‑lib crate
instead of light_compressed_token_sdk to restore proper layering.
♻️ Duplicate comments (19)
sdk-libs/compressed-token-sdk/Cargo.toml (1)

34-35: SDK layering violation: light-sdk and light-sdk-types are sdk-lib dependencies.

Per the coding guidelines, SDK-libs must depend on program-libs, light-prover-client, and external crates only. Both light-sdk and light-sdk-types reside in sdk-libs/, creating an SDK-to-SDK dependency chain. This propagates to all crates that depend on light-compressed-token-sdk.

Either document this as an intentional exception with clear rationale, or migrate the shared types/utilities that light-compressed-token-sdk needs from these crates into program-libs/ (e.g., light-token-interface).

sdk-libs/program-test/Cargo.toml (1)

24-24: SDK-to-SDK dependency violates the layering policy.

light-program-test (sdk-lib) depending on light-compressed-token-sdk (sdk-lib) breaches the architecture rule. Either move the required types to program-libs/ or document this as an intentional exception.

sdk-libs/token-client/Cargo.toml (1)

17-17: SDK-to-SDK dependency violates the layering policy.

light-token-client (sdk-lib) depending on light-compressed-token-sdk (sdk-lib) breaches the architecture rule. The required types and utilities should live in program-libs/light-token-interface where both SDKs can depend on them without creating sdk-libs-to-sdk-libs coupling.

sdk-libs/token-sdk/src/compressible/decompress_runtime.rs (1)

3-5: SDK-to-SDK import violates layering policy.

light-token-sdk (sdk-lib) importing from light-compressed-token-sdk (sdk-lib) creates an sdk-libs-to-sdk-libs dependency. Per the architecture policy, DecompressFullIndices and decompress_full_token_accounts_with_indices should be moved to program-libs/light-token-interface so both SDKs can depend on them without cross-SDK coupling.

sdk-libs/token-sdk/Cargo.toml (2)

11-11: The v1 feature remains a stub mapping to an empty feature.

Looking at the PR context, light-compressed-token-sdk/v1 is defined as v1 = [] (empty). This means enabling v1 on light-token has no effect. Either remove this feature declaration to avoid exposing a non-functional API, or implement V1 layout support across the token stack if needed.


31-33: SDK-to-SDK dependency is intentional for this refactor.

Per coding guidelines, SDK-libs should depend on program-libs and external crates. However, this PR explicitly extracts light-compressed-token-sdk as a lower-level SDK from light-token-sdk. The dependency direction (token-sdkcompressed-token-sdk) establishes a clear layering where light-token is the higher-level facade. This architectural decision should be documented in a CLAUDE.md or README for clarity.

sdk-libs/token-sdk/src/constants.rs (1)

25-44: Consider adding a test to guard against drift between constants and derived PDAs.

The hard-coded COMPRESSIBLE_CONFIG_V1 and RENT_SPONSOR_V1 constants should always match their derived counterparts. A simple unit test would catch any future divergence:

✅ Suggested test
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn pda_constants_match_derivations() {
        assert_eq!(COMPRESSIBLE_CONFIG_V1, config_pda());
        assert_eq!(RENT_SPONSOR_V1, rent_sponsor_pda());
    }
}
sdk-libs/compressed-token-sdk/src/spl_interface.rs (1)

55-62: Public fields allow bypassing constructor invariants, risking a panic.

The struct has public fields, allowing consumers to construct it manually with index > 0 but existing_spl_interface_pda = None. This causes an unwrap() panic at line 123. Consider making fields private or using expect() with a descriptive message.

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

5-7: SDK‑to‑SDK dependency still breaches sdk‑libs boundary.

This is the same dependency‑layering concern previously raised for this file; sdk‑libs should not depend on other sdk‑libs. Please route via a program‑lib re‑export or document an approved exception. As per coding guidelines, ...

sdk-libs/program-test/src/forester/compress_and_close_forester.rs (1)

5-5: SDK‑to‑SDK dependency still breaches sdk‑libs boundary.

Same issue previously raised: sdk‑libs should not depend on other sdk‑libs. Route via a program‑lib type or re‑export. As per coding guidelines, ...

sdk-libs/token-sdk/src/instruction/transfer_from_spl.rs (1)

2-7: SDK‑to‑SDK dependency still breaches sdk‑libs boundary.

This repeats the earlier dependency‑layering concern. Move these shared transfer2 types into a program‑lib interface and import from there. As per coding guidelines, ...

sdk-libs/token-sdk/src/instruction/create_mints.rs (1)

14-17: Dependency policy concern already flagged.

This import from light_compressed_token_sdk (another sdk-lib crate) was previously identified as potentially violating the sdk-libs dependency policy. Per the coding guidelines, sdk-libs should depend only on program-libs, light-prover-client, and external crates.

However, given the PR's stated objective is to extract light-compressed-token-sdk and have light-token-sdk depend on and re-export from it, this appears to be an intentional architectural decision. If this layering is approved, consider documenting this exception in CLAUDE.md or the crate's README.

sdk-libs/token-sdk/src/instruction/transfer_to_spl.rs (1)

1-7: Duplicate: sdk-lib dependency boundary concern.

Same issue as noted in sdk-libs/token-sdk/src/instruction/create_mint.rs. As per coding guidelines, ...

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

6-14: Duplicate: sdk-lib dependency boundary concern.

This matches the prior review feedback on sdk-lib → sdk-lib dependencies for this file.

Also applies to: 69-69

sdk-libs/token-sdk/src/instruction/transfer_interface.rs (1)

1-10: Duplicate: sdk-lib dependency boundary concern.

Same dependency-boundary issue as noted earlier for sdk-libs. As per coding guidelines, ...

sdk-libs/token-sdk/src/instruction/mod.rs (1)

200-203: SDK-libs dependency layering still applies here.
Defaulting through TokenDefaultAccounts from light_compressed_token_sdk preserves the sdk-libs → sdk-libs dependency concern; consider relocating defaults or documenting an exception. As per coding guidelines, SDK-libs should depend only on program-libs/external crates.

#!/bin/bash
# Confirm usage sites and dependency entry points
rg -n "TokenDefaultAccounts" sdk-libs/token-sdk/src -g '*.rs'
rg -n "light-compressed-token-sdk|light_compressed_token_sdk" sdk-libs/token-sdk/Cargo.toml
sdk-libs/compressed-token-sdk/src/compat.rs (2)

115-123: Avoid defaulting unknown state to Initialized.
A fallible conversion prevents silently treating corrupted state bytes as initialized.

#!/bin/bash
# Find conversions from light_token_interface::state::TokenData
rg -n "light_token_interface::state::TokenData" -g '*.rs'
rg -n "TokenData::from\\(" -g '*.rs'

83-94: Use a discriminator-specific error instead of EmptyInput.
Returning EmptyInput when a discriminator is present but unsupported obscures the root cause; a dedicated error variant would be clearer.

#!/bin/bash
# Locate HasherError definition and current variants
rg -n "enum HasherError" -g '*.rs'
rg -n "HasherError::" -g '*.rs' sdk-libs
sdk-libs/compressed-token-sdk/src/utils.rs (1)

3-16: Avoid sdk‑libs → sdk‑libs dependencies here.

light_sdk::constants::REGISTERED_PROGRAM_PDA and light_sdk_types::LIGHT_TOKEN_PROGRAM_ID are SDK‑libs; the guideline requires sdk‑libs to depend only on program‑libs/external crates. Please move/re‑export these constants into this crate or a program‑lib and import from there. As per coding guidelines, ...

@ananas-block ananas-block merged commit 77ef809 into main Jan 23, 2026
34 of 35 checks passed
@ananas-block ananas-block deleted the jorrit/refactor-separate-light-compressed-token-sdks branch January 23, 2026 17:22
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