Skip to content

Conversation

@SwenSchaeferjohann
Copy link
Contributor

@SwenSchaeferjohann SwenSchaeferjohann commented May 13, 2025

  • codec still has breaking changes to v1

Summary by CodeRabbit

  • New Features

    • Added support for multiple token pools per mint with new instructions for approve, revoke, and delegate compressed token accounts.
    • Introduced batch compress and multi-pool decompression capabilities.
    • Added delegated transfer and decompression functions with corresponding tests.
    • Expanded utilities for dynamic selection of state tree and token pool info.
  • Improvements

    • Refined core APIs to accept structured state tree and token pool info, improving flexibility and error handling.
    • Enhanced compute budget management with dynamic limits based on operation size.
    • Improved test coverage with new end-to-end suites and verbose reporting.
    • Updated documentation and migration guides for new protocol versions.
  • Bug Fixes

    • Fixed state tree and token pool handling in actions and tests for consistency.
    • Corrected naming conventions and type definitions across the codebase.
  • Refactor

    • Deprecated legacy types and functions in favor of explicit, robust structures.
    • Reorganized exports and internal modules for clarity and maintainability.
    • Added feature flags and versioning to support multiple protocol versions.
    • Simplified transaction construction and parameter passing in key functions.
  • Chores

    • Updated dependencies and configuration files.
    • Removed unused imports and cleaned up code style.
    • Enhanced build and test scripts with new options and improved control.

@SwenSchaeferjohann SwenSchaeferjohann changed the title chore: merge JS v2 onto v1 chore: js-v2 refactored onto 0.21.0 May 13, 2025
@SwenSchaeferjohann SwenSchaeferjohann changed the title chore: js-v2 refactored onto 0.21.0 chore: rebase v2 support onto 0.21.0 May 13, 2025
@SwenSchaeferjohann SwenSchaeferjohann marked this pull request as ready for review May 13, 2025 01:15
@SwenSchaeferjohann SwenSchaeferjohann force-pushed the v2-only branch 5 times, most recently from 9c08675 to 7e607c0 Compare June 10, 2025 15:54
* stateless.js add treeinfos

* wip

* add getTokenPoolInfos

* wip

* wip

* wip - storageoptions

* wip

* wip

* wip

* wip - known bug in rpc-interop.test.ts if using random trees

* debugged test-rpc in ctoken

* all ctoken tests working

* rm logs

* clean

* ctxs to infos, removed redundant getMintProgramId calls

* rm deadcode, storageoptions

* fix cli getMindProgramId use

* fix tokenpool

* fix test

stateless.js add treeinfos

wip

add getTokenPoolInfos

wip

wip

wip - storageoptions

wip

wip

wip

wip - known bug in rpc-interop.test.ts if using random trees

debugged test-rpc in ctoken

all ctoken tests working

rm logs

clean

ctxs to infos, removed redundant getMintProgramId calls

rm deadcode, storageoptions

fix cli getMindProgramId use

fix tokenpool

fix test

update CHANGELOG.md files

update changelog

update CHANGELOG.md

export get-token-pool-infos.ts

wip

clean

wip

refactor merklecontext

refactor StateTreeInfo

wip

wip

debug test-rpc getCompressedTokenAccountsByOwner

fix unit test

wip

wip

wip

debug .readUIntLE

wip

wip

wip

fix buffer conversion at test-rpc decode

wip

tests working

compressedProof -> validityProof

update changelog

wip

refactor: do not allow output trees for decompress, transfer

fmt

wip

rename getCachedStateTreeInfos

upd changelog

use with getCachedStateTreeInfos

getStateTreeInfos

wip

wip

delegate test

wip

added transfer-delegated.test.ts

added transfer-delegated test cases. all green

add tests for decompress-delegated

fix

rm logs from program

update changelog

update err msg

fix state-tree-luts

wip

getActiveStateTreeInfos -> getAllStateTreeInfos

wip

fix sigs for nullifyStateTree

wip

wip

cleanup

add getCachedActiveStateTreeInfos mock

revert to compressedProof

add tokenpools tests

update comment

update CHANGELOG.md

update changelog

0.21.0

update changelog

fix

fix event parsing post rebase

stateless js refactor

dedupe types

link to computebudgetprogram

wip

fix DX for instructions: add docstrings to call signatures

wip

dont break mergeTokenAccounts

wip

wip

add v2 trees to test-rpc

v1 mergeable

test-rpc tests working

rpc-interop tests working with v2

all stateless.js tests working with v2

compressed-token tests working

rebase to main

fixup cargo lock

wip
SwenSchaeferjohann and others added 4 commits June 10, 2025 12:54
chore: backport breaking api changes to v1 js SDKs (#1661)

* stateless.js add treeinfos

* wip

* add getTokenPoolInfos

* wip

* wip

* wip - storageoptions

* wip

* wip

* wip

* wip - known bug in rpc-interop.test.ts if using random trees

* debugged test-rpc in ctoken

* all ctoken tests working

* rm logs

* clean

* ctxs to infos, removed redundant getMintProgramId calls

* rm deadcode, storageoptions

* fix cli getMindProgramId use

* fix tokenpool

* fix test

stateless.js add treeinfos

wip

add getTokenPoolInfos

wip

wip

wip - storageoptions

wip

wip

wip

wip - known bug in rpc-interop.test.ts if using random trees

debugged test-rpc in ctoken

all ctoken tests working

rm logs

clean

ctxs to infos, removed redundant getMintProgramId calls

rm deadcode, storageoptions

fix cli getMindProgramId use

fix tokenpool

fix test

update CHANGELOG.md files

update changelog

update CHANGELOG.md

export get-token-pool-infos.ts

wip

clean

wip

refactor merklecontext

refactor StateTreeInfo

wip

wip

debug test-rpc getCompressedTokenAccountsByOwner

fix unit test

wip

wip

wip

debug .readUIntLE

wip

wip

wip

fix buffer conversion at test-rpc decode

wip

tests working

compressedProof -> validityProof

update changelog

wip

refactor: do not allow output trees for decompress, transfer

fmt

wip

rename getCachedStateTreeInfos

upd changelog

use with getCachedStateTreeInfos

getStateTreeInfos

wip

wip

delegate test

wip

added transfer-delegated.test.ts

added transfer-delegated test cases. all green

add tests for decompress-delegated

fix

rm logs from program

update changelog

update err msg

fix state-tree-luts

wip

getActiveStateTreeInfos -> getAllStateTreeInfos

wip

fix sigs for nullifyStateTree

wip

wip

cleanup

add getCachedActiveStateTreeInfos mock

revert to compressedProof

add tokenpools tests

update comment

update CHANGELOG.md

update changelog

0.21.0

update changelog

fix

fix event parsing post rebase

stateless js refactor

dedupe types

link to computebudgetprogram

wip

fix DX for instructions: add docstrings to call signatures

wip

dont break mergeTokenAccounts

wip

wip

add v2 trees to test-rpc

v1 mergeable

test-rpc tests working

rpc-interop tests working with v2

all stateless.js tests working with v2

compressed-token tests working

rebase to main

fixup cargo lock

wip

chore: backport breaking api changes to v1 js SDKs (#1661)

* stateless.js add treeinfos

* wip

* add getTokenPoolInfos

* wip

* wip

* wip - storageoptions

* wip

* wip

* wip

* wip - known bug in rpc-interop.test.ts if using random trees

* debugged test-rpc in ctoken

* all ctoken tests working

* rm logs

* clean

* ctxs to infos, removed redundant getMintProgramId calls

* rm deadcode, storageoptions

* fix cli getMindProgramId use

* fix tokenpool

* fix test

stateless.js add treeinfos

wip

add getTokenPoolInfos

wip

wip

wip - storageoptions

wip

wip

wip

wip - known bug in rpc-interop.test.ts if using random trees

debugged test-rpc in ctoken

all ctoken tests working

rm logs

clean

ctxs to infos, removed redundant getMintProgramId calls

rm deadcode, storageoptions

fix cli getMindProgramId use

fix tokenpool

fix test

update CHANGELOG.md files

update changelog

update CHANGELOG.md

export get-token-pool-infos.ts

wip

clean

wip

refactor merklecontext

refactor StateTreeInfo

wip

wip

debug test-rpc getCompressedTokenAccountsByOwner

fix unit test

wip

wip

wip

debug .readUIntLE

wip

wip

wip

fix buffer conversion at test-rpc decode

wip

tests working

compressedProof -> validityProof

update changelog

wip

refactor: do not allow output trees for decompress, transfer

fmt

wip

rename getCachedStateTreeInfos

upd changelog

use with getCachedStateTreeInfos

getStateTreeInfos

wip

wip

delegate test

wip

added transfer-delegated.test.ts

added transfer-delegated test cases. all green

add tests for decompress-delegated

fix

rm logs from program

update changelog

update err msg

fix state-tree-luts

wip

getActiveStateTreeInfos -> getAllStateTreeInfos

wip

fix sigs for nullifyStateTree

wip

wip

cleanup

add getCachedActiveStateTreeInfos mock

revert to compressedProof

add tokenpools tests

update comment

update CHANGELOG.md

update changelog

0.21.0

update changelog

fix

fix event parsing post rebase

stateless js refactor

dedupe types

link to computebudgetprogram

wip

fix DX for instructions: add docstrings to call signatures

wip

dont break mergeTokenAccounts

wip

wip

add v2 trees to test-rpc

v1 mergeable

test-rpc tests working

rpc-interop tests working with v2

all stateless.js tests working with v2

compressed-token tests working

rebase to main

fixup cargo lock

wip

wip

rename statetreeinfo -> treeinfo

wip

wip

wip

cli test works

wip

wip

wip

wip

wip

wip

wip

wip

wip

wip

wip

wip

rm logs

fix lint

clean

upd comment
* stateless.js add treeinfos

* wip

* add getTokenPoolInfos

* wip

* wip

* wip - storageoptions

* wip

* wip

* wip

* wip - known bug in rpc-interop.test.ts if using random trees

* debugged test-rpc in ctoken

* all ctoken tests working

* rm logs

* clean

* ctxs to infos, removed redundant getMintProgramId calls

* rm deadcode, storageoptions

* fix cli getMindProgramId use

* fix tokenpool

* fix test

stateless.js add treeinfos

wip

add getTokenPoolInfos

wip

wip

wip - storageoptions

wip

wip

wip

wip - known bug in rpc-interop.test.ts if using random trees

debugged test-rpc in ctoken

all ctoken tests working

rm logs

clean

ctxs to infos, removed redundant getMintProgramId calls

rm deadcode, storageoptions

fix cli getMindProgramId use

fix tokenpool

fix test

update CHANGELOG.md files

update changelog

update CHANGELOG.md

export get-token-pool-infos.ts

wip

clean

wip

refactor merklecontext

refactor StateTreeInfo

wip

wip

debug test-rpc getCompressedTokenAccountsByOwner

fix unit test

wip

wip

wip

debug .readUIntLE

wip

wip

wip

fix buffer conversion at test-rpc decode

wip

tests working

compressedProof -> validityProof

update changelog

wip

refactor: do not allow output trees for decompress, transfer

fmt

wip

rename getCachedStateTreeInfos

upd changelog

use with getCachedStateTreeInfos

getStateTreeInfos

wip

wip

delegate test

wip

added transfer-delegated.test.ts

added transfer-delegated test cases. all green

add tests for decompress-delegated

fix

rm logs from program

update changelog

update err msg

fix state-tree-luts

wip

getActiveStateTreeInfos -> getAllStateTreeInfos

wip

fix sigs for nullifyStateTree

wip

wip

cleanup

add getCachedActiveStateTreeInfos mock

revert to compressedProof

add tokenpools tests

update comment

update CHANGELOG.md

update changelog

0.21.0

update changelog

fix

fix event parsing post rebase

stateless js refactor

dedupe types

link to computebudgetprogram

wip

fix DX for instructions: add docstrings to call signatures

wip

dont break mergeTokenAccounts

wip

wip

add v2 trees to test-rpc

v1 mergeable

test-rpc tests working

rpc-interop tests working with v2

all stateless.js tests working with v2

compressed-token tests working

rebase to main

fixup cargo lock

wip

chore: backport breaking api changes to v1 js SDKs (#1661)

* stateless.js add treeinfos

* wip

* add getTokenPoolInfos

* wip

* wip

* wip - storageoptions

* wip

* wip

* wip

* wip - known bug in rpc-interop.test.ts if using random trees

* debugged test-rpc in ctoken

* all ctoken tests working

* rm logs

* clean

* ctxs to infos, removed redundant getMintProgramId calls

* rm deadcode, storageoptions

* fix cli getMindProgramId use

* fix tokenpool

* fix test

stateless.js add treeinfos

wip

add getTokenPoolInfos

wip

wip

wip - storageoptions

wip

wip

wip

wip - known bug in rpc-interop.test.ts if using random trees

debugged test-rpc in ctoken

all ctoken tests working

rm logs

clean

ctxs to infos, removed redundant getMintProgramId calls

rm deadcode, storageoptions

fix cli getMindProgramId use

fix tokenpool

fix test

update CHANGELOG.md files

update changelog

update CHANGELOG.md

export get-token-pool-infos.ts

wip

clean

wip

refactor merklecontext

refactor StateTreeInfo

wip

wip

debug test-rpc getCompressedTokenAccountsByOwner

fix unit test

wip

wip

wip

debug .readUIntLE

wip

wip

wip

fix buffer conversion at test-rpc decode

wip

tests working

compressedProof -> validityProof

update changelog

wip

refactor: do not allow output trees for decompress, transfer

fmt

wip

rename getCachedStateTreeInfos

upd changelog

use with getCachedStateTreeInfos

getStateTreeInfos

wip

wip

delegate test

wip

added transfer-delegated.test.ts

added transfer-delegated test cases. all green

add tests for decompress-delegated

fix

rm logs from program

update changelog

update err msg

fix state-tree-luts

wip

getActiveStateTreeInfos -> getAllStateTreeInfos

wip

fix sigs for nullifyStateTree

wip

wip

cleanup

add getCachedActiveStateTreeInfos mock

revert to compressedProof

add tokenpools tests

update comment

update CHANGELOG.md

update changelog

0.21.0

update changelog

fix

fix event parsing post rebase

stateless js refactor

dedupe types

link to computebudgetprogram

wip

fix DX for instructions: add docstrings to call signatures

wip

dont break mergeTokenAccounts

wip

wip

add v2 trees to test-rpc

v1 mergeable

test-rpc tests working

rpc-interop tests working with v2

all stateless.js tests working with v2

compressed-token tests working

rebase to main

fixup cargo lock

wip

wip

rename statetreeinfo -> treeinfo

wip

wip

wip

cli test works

wip

wip

wip

wip

wip

wip

wip

wip

wip

wip

wip

wip

rm logs

fix lint

clean

upd comment

wip

stateless.js tests working

ctoken tests working
inputCompressedTokenAccounts,
);

const CHANGE_INDEX = featureFlags.isV2() ? 1 : 0; // TODO: find better solution.
Copy link
Contributor

@ananas-block ananas-block Jun 11, 2025

Choose a reason for hiding this comment

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

what's the idea here?

are we always passing a tree and a queue in remaining accounts and are just switching the indices based on the version?

Copy link
Contributor

Choose a reason for hiding this comment

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

doesn't inputCompressedTokenAccounts include a TreeInfo that has a tree version?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

featureFlags is for switching our sdk to v2 (endpoints, new instructions,..) so it's actually misused here. switched to treeInfo.treeType in all places where it should

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 11, 2025

Walkthrough

This update introduces substantial changes across the compressed token and stateless.js libraries, focusing on protocol scalability, multi-pool support, and enhanced state tree and proof management. Major refactors include new and updated types, batch and delegation instructions, migration to dynamic state tree/token pool selection, and V2 API support. Test suites and utilities are updated accordingly.

Changes

File(s) / Group Change Summary
Cargo.toml Added serde and serde_json dependencies for serialization.
cli/src/commands/compress-spl/index.ts, cli/src/commands/decompress-spl/index.ts Updated method names for token program ID retrieval and simplified compress/decompress calls.
cli/src/commands/init/index.ts Removed unused execSync import.
cli/src/commands/start-prover/index.ts Added explicit undefined fallback for redisUrl assignment.
examples/anchor/counter/tests/test.rs Swapped field order in CompressedAccountMeta struct initialization.
examples/browser/nextjs/src/app/page.tsx Replaced static state tree usage with dynamic selection from RPC; updated compress/transfer calls.
js/compressed-token/CHANGELOG.md, js/stateless.js/CHANGELOG.md Documented breaking changes, migration guides, and new features for v0.21.0+.
js/compressed-token/package.json, js/stateless.js/package.json Bumped versions, updated/added test scripts for new features and coverage.
js/compressed-token/rollup.config.js Changed terser config to keep console statements.
js/compressed-token/src/actions/*.ts Refactored actions to use new state tree/token pool info types, added delegation/batch support, updated signatures, and compute budgets.
js/compressed-token/src/constants.ts Added new discriminator constants for batch, approve, revoke, and add token pool.
js/compressed-token/src/idl.ts Added new instruction data types for approve/revoke, updated packed merkle context fields.
js/compressed-token/src/index.ts, js/compressed-token/src/actions/index.ts Reordered and expanded exports for new actions and utilities.
js/compressed-token/src/instructions/index.ts Removed re-export of pack-compressed-token-accounts.
js/compressed-token/src/layout.ts Added batch/approve/revoke layouts, updated encoding/decoding, and account meta utilities.
js/compressed-token/src/program.ts Major refactor: multi-pool support, batch compress, delegation, updated method signatures, new utility functions, improved error handling.
js/compressed-token/src/types.ts Updated/added types for batch, delegation, proof, and context; removed/modified legacy types.
js/compressed-token/src/utils/*.ts Added/updated utilities for token pool info, input account selection, validation, and packing.
js/compressed-token/tests/e2e/*.test.ts Updated all tests to use dynamic state tree/token pool info, added new tests for delegation, multi-pool, and batch features.
js/compressed-token/tsconfig.json Clarified typeRoots path.
js/compressed-token/vitest.config.ts Enabled verbose test reporting.
js/stateless.js/src/actions/*.ts Updated compress, decompress, transfer, and createAccount signatures and logic for new tree info types and V2 compatibility.
js/stateless.js/src/constants.ts Introduced versioning, updated constants, added V2 support, and renamed fields.
js/stateless.js/src/index.ts, js/stateless.js/src/programs/index.ts, ... Reorganized/removed/added exports to reflect new structure and modularization.
js/stateless.js/src/instruction/pack-compressed-accounts.ts Deleted; replaced by new packing utilities in system/pack.ts.
js/stateless.js/src/programs/system/{index.ts, layout.ts, pack.ts, program.ts} Added/updated for new packing, layout, and program logic; refactored for V2 and new types.
js/stateless.js/src/programs/system/idl.ts Removed queueIndex, renamed fields, simplified context struct.
js/stateless.js/src/programs/system/select-compressed-accounts.ts Added function to select minimal compressed SOL accounts for transfer.
js/stateless.js/src/rpc-interface.ts, js/stateless.js/src/rpc.ts Added V2 result types, updated methods for V2 compatibility, improved caching and concurrency, refactored proof handling.
js/stateless.js/src/state/{BN254.ts, bn.ts, compressed-account.ts, index.ts, types.ts} Refactored/added types, legacy support, new BN utility, updated tree info structures, and improved documentation.
js/stateless.js/src/test-helpers/test-rpc/*.ts Updated test RPC helpers for dynamic tree info, V2 support, and improved validation.
js/stateless.js/src/utils/*.ts Reorganized/replaced utilities, improved validation, updated for new types and V2.
js/stateless.js/tests/e2e/.test.ts, js/stateless.js/tests/unit/.test.ts Updated all tests for new tree info selection, type changes, and V2 compatibility; added new tests for selection utilities.
js/stateless.js/vitest.config.ts Reduced timeouts, set verbose reporter.

Sequence Diagram(s)

Compressed Token Minting and Compression (New Flow)

sequenceDiagram
    participant User
    participant RPC
    participant TokenPoolSelector
    participant StateTreeSelector
    participant CompressedTokenProgram

    User->>RPC: getStateTreeInfos()
    RPC-->>User: [TreeInfo...]
    User->>StateTreeSelector: selectStateTreeInfo([TreeInfo...])
    StateTreeSelector-->>User: stateTreeInfo

    User->>RPC: getTokenPoolInfos(mint)
    RPC-->>User: [TokenPoolInfo...]
    User->>TokenPoolSelector: selectTokenPoolInfo([TokenPoolInfo...])
    TokenPoolSelector-->>User: tokenPoolInfo

    User->>CompressedTokenProgram: mintTo({stateTreeInfo, tokenPoolInfo, ...})
    CompressedTokenProgram-->>User: TransactionInstruction
    User->>RPC: sendAndConfirmTx(TransactionInstruction)
    RPC-->>User: TransactionSignature
Loading

Delegated Transfer and Decompression

sequenceDiagram
    participant Owner
    participant Delegate
    participant RPC
    participant CompressedTokenProgram

    Owner->>CompressedTokenProgram: approve({owner, delegate, amount, ...})
    CompressedTokenProgram-->>Owner: TransactionInstruction
    Owner->>RPC: sendAndConfirmTx(TransactionInstruction)
    RPC-->>Owner: TransactionSignature

    Delegate->>CompressedTokenProgram: transferDelegated({delegate, toAddress, amount, ...})
    CompressedTokenProgram-->>Delegate: TransactionInstruction
    Delegate->>RPC: sendAndConfirmTx(TransactionInstruction)
    RPC-->>Delegate: TransactionSignature

    Delegate->>CompressedTokenProgram: decompressDelegated({delegate, toAddress, amount, ...})
    CompressedTokenProgram-->>Delegate: TransactionInstruction
    Delegate->>RPC: sendAndConfirmTx(TransactionInstruction)
    RPC-->>Delegate: TransactionSignature
Loading

Poem

A rabbit hopped through fields of code,
Where pools and trees in clusters grow’d.
With batch and delegate, tokens flew—
New proofs and layouts, shiny and new!
Now every test runs, verbose and bright,
As stateless hops to greater height.
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🔭 Outside diff range comments (14)
js/stateless.js/tests/e2e/transfer.test.ts (1)

55-57: 🛠️ Refactor suggestion

bn(-transferAmount) may throw – construct negative BN via .neg() instead.

Many BN helpers accept only unsigned input. Creating a negative number directly can fail on some runtimes.

-            assert(
-                postSenderBalance.sub(preSenderBalance).eq(bn(-transferAmount)),
+            const expectedDelta = bn(transferAmount).neg(); // safer
+            assert(
+                postSenderBalance.sub(preSenderBalance).eq(expectedDelta),
cli/src/commands/compress-spl/index.ts (2)

55-66: ⚠️ Potential issue

Guard against undefined tokenProgramId.

getMintProgramId returns Promise<PublicKey | undefined>.
If the mint was initialized with the default SPL‐Token program the call will resolve to undefined; passing this straight into getAssociatedTokenAddressSync will throw because that function expects a defined PublicKey, not undefined.

       const payer = defaultSolanaWalletKeypair();
-      const tokenProgramId = await CompressedTokenProgram.getMintProgramId(
+      const tokenProgramId = await CompressedTokenProgram.getMintProgramId(
         mintPublicKey,
         rpc(),
       );
+
+      if (!tokenProgramId) {
+        throw new Error(
+          `Unable to determine token program for mint ${mintPublicKey.toBase58()}`,
+        );
+      }

48-87: 🛠️ Refactor suggestion

Spinner never stops on error.

loader.start() is called but loader.stop() is only executed on the happy path.
On failure the process exits via this.error, leaving the TTY spinner running and
corrupting subsequent output.

       console.log("compress-spl successful");
     } catch (error) {
-      console.log("compress-spl failed", txId);
+      loader.stop(true);
+      console.log("compress-spl failed", txId);
       this.error(`Failed to compress-spl!\n${error}`);
     }
cli/src/commands/decompress-spl/index.ts (2)

60-69: ⚠️ Potential issue

Incorrect argument position in getOrCreateAssociatedTokenAccount.

tokenProgramId must be the 8-th parameter (programId), not the 9-th
(associatedTokenProgramId).
Passing it in the wrong slot breaks accounts created under custom programs.

-        undefined,
-        undefined,
-        undefined,
-        tokenProgramId,
+        undefined,  // allowOwnerOffCurve
+        undefined,  // commitment
+        undefined,  // confirmOptions
+        tokenProgramId, // <-- programId (8th argument)

48-90: 🛠️ Refactor suggestion

Stop loader on failure (same issue as compress command).

       console.log("decompress-spl successful");
     } catch (error) {
-      console.log("decompress-spl failed", txId);
+      loader.stop(true);
+      console.log("decompress-spl failed", txId);
       this.error(`Failed to decompress-spl!\n${error}`);
     }
js/stateless.js/src/actions/decompress.ts (1)

32-41: ⚠️ Potential issue

Guard against empty input set before proceeding

rpc.getCompressedAccountsByOwner can legitimately return an empty list (e.g. a brand-new payer).
Invoking sumUpLamports and rpc.getValidityProof with an empty array will either throw or produce a confusing “Not enough compressed lamports” error downstream. Bail out early with a clearer message.

+    if (userCompressedAccountsWithMerkleContext.length === 0) {
+        throw new Error('Payer owns no compressed accounts – nothing to decompress');
+    }
js/stateless.js/tests/unit/utils/conversion.test.ts (2)

59-63: 🛠️ Refactor suggestion

Out-of-date field name queue_index will break once decoder is upgraded

The new layout renamed queue_indexqueuePubkeyIndex. Keeping the old
expectation will cause this test to fail after the corresponding implementation lands.

-            tree_index: 1,
-            queue_index: 1,
+            treeIndex: 1,
+            queuePubkeyIndex: 1,

71-78: 🛠️ Refactor suggestion

Synchronise nullifier context field names with latest IDL

Similarly, queue_index should be queuePubkeyIndex and prove_by_index is now proveByIndex (camel-cased). Update to stay aligned with production structs.

js/compressed-token/tests/e2e/merge-token-accounts.test.ts (1)

60-60: ⚠️ Potential issue

it.only will cripple the entire Vitest suite

Leaving .only in committed code means all other tests are skipped, masking regressions in unrelated areas.

-    it.only('should merge all token accounts', async () => {
+    it('should merge all token accounts', async () => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 60-60: Don't focus the test.

The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.

(lint/suspicious/noFocusedTests)

js/stateless.js/src/utils/parse-validity-proof.ts (1)

64-78: ⚠️ Potential issue

Incorrect BN constructor usage – second arg should be endian string, not 32

Passing 32 as the base for a Uint8Array input is undefined behaviour in bn.js.
Use the endian parameter instead:

-const aYElement = bn(proofA.slice(32, 64), 32, 'be');
+const aYElement = bn(proofA.slice(32, 64), undefined, 'be');
...
-    bn(bYElement.slice(0, 32), 32, 'be'),
-    bn(bYElement.slice(32, 64), 32, 'be'),
+    bn(bYElement.slice(0, 32), undefined, 'be'),
+    bn(bYElement.slice(32, 64), undefined, 'be'),
...
-const proofCIsPositive = yElementIsPositiveG1(bn(cYElement, 32, 'be'));
+const proofCIsPositive = yElementIsPositiveG1(
+    bn(cYElement, undefined, 'be'),
+);

Without this fix the values are parsed as base-32 numbers, silently producing wrong proofs.

🧰 Tools
🪛 Biome (1.9.4)

[error] 68-68: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

js/stateless.js/src/actions/transfer.ts (2)

80-83: ⚠️ Potential issue

getValidityProof call uses legacy signature – will reject on V2 clusters.

API v2 expects {hash, tree, queue} objects (getValidityProofV0). Passing bare hashes will lead to Invalid parameters RPC errors.

-    const proof = await rpc.getValidityProof(
-        inputAccounts.map(account => bn(account.hash)),
-    );
+    const proof = await rpc.getValidityProofV0(
+        inputAccounts.map(a => ({
+            hash: bn(a.hash),
+            tree: a.treeInfo.tree,
+            queue: a.treeInfo.queue,
+        })),
+    );

93-99: ⚠️ Potential issue

Owner is never added to additionalSigners – tx will fail when owner ≠ payer.

-    const signedTx = buildAndSignTx(
-        [ComputeBudgetProgram.setComputeUnitLimit({ units: 350_000 }), ix],
-        payer,
-        blockhash,
-    );
+    const additionalSigners =
+        owner.publicKey.equals(payer.publicKey) ? [] : [owner];
+
+    const signedTx = buildAndSignTx(
+        [ComputeBudgetProgram.setComputeUnitLimit({ units: 350_000 }), ix],
+        payer,
+        blockhash,
+        additionalSigners,
+    );
js/compressed-token/tests/e2e/compress-spl-token-account.test.ts (1)

359-367: ⚠️ Potential issue

mintTo call misses tokenPoolInfo – will throw after API upgrade

All other mintTo invocations were updated to pass tokenPoolInfo.
The Token-2022 branch still calls:

await mintTo(..., bn(1000), stateTreeInfo);

tokenPoolInfo is now mandatory for V2. Add the argument to keep the test green.

-            bn(1000),
-            stateTreeInfo,
+            bn(1000),
+            stateTreeInfo,
+            tokenPoolInfoT22,
js/compressed-token/src/layout.ts (1)

49-64: ⚠️ Potential issue

Breaking change: Field renaming in InputTokenDataWithContextLayout

The renaming of nullifierQueuePubkeyIndex to queuePubkeyIndex and changing queueIndex from a field to a boolean proveByIndex are breaking changes that could affect existing code using these layouts.

Ensure that all code depending on these field names is updated accordingly, and document this breaking change in the migration guide or changelog.

♻️ Duplicate comments (2)
cli/src/commands/decompress-spl/index.ts (1)

55-59: Same undefined guard needed as in compress-spl.

The code path is identical; protect against an unresolved token-program id.

js/compressed-token/src/types.ts (1)

76-81: Remove leftover development comments

🧹 Nitpick comments (55)
js/compressed-token/vitest.config.ts (1)

14-15: Consider keeping the default reporter alongside verbose.

Replacing the default reporter means the final pass/fail summary banner disappears ‒ useful in CI logs.
If you only need the detailed per-test output, ignore this. Otherwise, consider:

-        reporters: ['verbose'],
+        reporters: ['default', 'verbose'],
js/stateless.js/tests/e2e/transfer.test.ts (1)

21-22: Optional: use await Promise.all to speed up compress + airdrop.

compress is awaited serially; parallelising funding steps (if more accounts appear later) can shave test time.
Not critical, just a potential test-suite speed-up.

cli/src/commands/start-prover/index.ts (1)

63-66: Prefer the null-coalescing (??) operator for clearer intent

|| will treat an empty string passed via --redisUrl="" as falsy and silently fall back to the env-var or undefined, which may surprise users.
Using ?? preserves the “explicit empty means empty” contract and makes the || undefined suffix redundant.

-const redisUrl = flags["redisUrl"] || process.env.REDIS_URL || undefined;
+const redisUrl = flags["redisUrl"] ?? process.env.REDIS_URL;
examples/anchor/counter/tests/test.rs (1)

202-206: Field order is now inconsistent with sibling helpers

increment_counter builds CompressedAccountMeta with address first, whereas decrement_counter, reset_counter, etc. still use tree_info first.
Although Rust struct literals ignore order, keeping a consistent layout across helpers avoids cognitive friction and reduces merge-conflict noise.

Consider reverting to the common order or updating the others for uniformity.

js/compressed-token/rollup.config.js (1)

48-55: Leaving drop_console: false in production bundles may bloat size and leak internals

If the intention is only to aid local debugging, gate this option on the build target (e.g. keep logs for dev builds, strip for prod).
A quick toggle:

-        terser({
-            compress: {
-                drop_console: false,
+        terser({
+            compress: {
+                drop_console: env !== 'browser', // strip in node/browser-prod

or expose an explicit --debug flag in your build script.

js/compressed-token/src/utils/index.ts (1)

1-4: Public API surface widened – double-check versioning

Re-exporting these helpers is handy, but it also freezes their current signatures in the public contract. Make sure semver expectations are clear (e.g. bump minor version) and add docs so downstream consumers know they’re supported.

js/compressed-token/src/utils/validation.ts (1)

11-21: Empty input array is silently accepted.

every() on an empty array returns true, so checkMint([]) currently succeeds.
For symmetry with validateSameOwner (which guards against this) add an explicit
length check:

 export function checkMint(
     compressedTokenAccounts: ParsedTokenAccount[],
     mint: PublicKey,
 ): boolean {
-    if (
+    if (compressedTokenAccounts.length === 0) {
+        throw new Error('No accounts provided for mint validation');
+    }
+
+    if (
         !compressedTokenAccounts.every(account =>
             account.parsed.mint.equals(mint),
         )
     ) {
js/stateless.js/package.json (1)

101-101: Suggest extracting composite script.
The test:e2e:all command is very long and hard to maintain. Consider moving it into a dedicated shell script or leveraging a task runner for better readability.

js/stateless.js/src/test-helpers/test-rpc/get-parsed-events.ts (1)

254-261: Add error handling around deserialization.
If deserializeAppendNullifyCreateAddressInputsIndexer throws, it could abort the entire parsing loop. Wrap this call in a try/catch to log failures without dropping all events.

js/compressed-token/tests/e2e/mint-to.test.ts (2)

104-115: Remove unused txId (or assert on it) to satisfy linters

txId is assigned but never read. Either assert its truthiness or drop the variable.

-        const txId = await mintTo(
+        await mintTo(

160-170: tx variable is computed but never used

Same issue as above – consider asserting on tx or omitting the assignment.

-        const tx = await mintTo(
+        await mintTo(
js/stateless.js/src/state/bn.ts (1)

3-12: Avoid forwarding base / endian when cloning an existing BN

Passing base to new BN(existingBn, base) is undocumented and may throw in
future bn.js versions. Strip the optional params when the argument is already
a BN instance.

-    if (number instanceof Uint8Array && !(number instanceof Buffer)) {
+    if (number instanceof Uint8Array && !(number instanceof Buffer)) {
         return new BN(Buffer.from(number), base, endian);
     }
-    return new BN(number, base, endian);
+    return number instanceof BN
+        ? new BN(number)
+        : new BN(number, base, endian);
js/compressed-token/src/actions/index.ts (1)

1-15: Alphabetise exports to make merge conflicts rarer

Minor, but keeping exports sorted reduces churn when adding new actions.

No code change required if team disagrees.

js/stateless.js/src/state/BN254.ts (1)

46-49: Avoid the extra memory copy when converting BufferUint8Array

Buffer is already a subclass of Uint8Array; the cast is only required for the TS type-checker.
new Uint8Array(bn254Buffer) allocates a fresh copy every call, which is wasteful in a hot path such as encoding.

-    return bs58.encode(new Uint8Array(bn254Buffer));
+    // Cast without copying – zero-alloc on Node ≥ 12
+    return bs58.encode(
+        bn254Buffer as unknown as Uint8Array /* Buffer → Uint8Array cast */
+    );
js/compressed-token/src/constants.ts (1)

14-16: Discriminators look good – consider adding dev-docs

The new discriminators follow the existing 8-byte pattern and are unique.
For long-term maintainability, consider a short comment indicating which on-chain instruction each constant maps to (mirrors the IDL names).

Also applies to: 24-32

js/compressed-token/tests/e2e/merge-token-accounts.test.ts (1)

3-11: Remove unused import to keep linter happy

defaultTestStateTreeAccounts is no longer referenced after moving to dynamic stateTreeInfo selection.

-    defaultTestStateTreeAccounts,
js/compressed-token/tests/e2e/approve-and-mint-to.test.ts (2)

18-21: Remove unused import to avoid linter noise

defaultTestStateTreeAccounts is still imported but never referenced after the refactor to dynamic tree-selection.

-import {
-    Rpc,
-    bn,
-    buildAndSignTx,
-    dedupeSigner,
-    newAccountWithLamports,
-    sendAndConfirmTx,
-    getTestRpc,
-    defaultTestStateTreeAccounts,
-    TreeInfo,
-    selectStateTreeInfo,
-} from '@lightprotocol/stateless.js';
+import {
+    Rpc,
+    bn,
+    buildAndSignTx,
+    dedupeSigner,
+    newAccountWithLamports,
+    sendAndConfirmTx,
+    getTestRpc,
+    TreeInfo,
+    selectStateTreeInfo,
+} from '@lightprotocol/stateless.js';

Tidying this keeps CI/lint pipelines green.


113-127: Variable shadowing may confuse future maintainers

Inside the second it block a new const payer shadows the outer-scope payer.
Although legal, it is easy to overlook and can lead to subtle bugs if the inner variable is later moved/used outside the block.

Consider renaming (e.g. localPayer) to make the scoping explicit.

js/compressed-token/tests/e2e/rpc-token-interop.test.ts (1)

9-12: Drop the now-unused static tree import

defaultTestStateTreeAccounts is not referenced after the v2 migration.
Same pattern as in the previous test file – trim the import list.

js/stateless.js/tests/unit/utils/tree-info.test.ts (1)

88-116: Test loops run 10k–100k iterations – they add seconds to CI

Randomised selection can be validated with far fewer iterations (e.g. 500).
Reducing the loops keeps the suite fast without sacrificing confidence.

js/stateless.js/src/utils/parse-validity-proof.ts (1)

98-103: Minor – small performance win

bn(...) already returns a Buffer when given a hex string; converting straight to be bytes avoids an interim BN:

-const bN = bn(hexStr.startsWith('0x') ? hexStr.substring(2) : hexStr, 'hex');
-return new Uint8Array(bN.toArray('be', 32));
+return new Uint8Array(
+    bn(
+        hexStr.startsWith('0x') ? hexStr.substring(2) : hexStr,
+        'hex',
+    ).toArray('be', 32),
+);
js/compressed-token/src/actions/create-token-program-lookup-table.ts (2)

43-43: Explicitly type txIds for stronger compile-time guarantees

Declare the variable with its intended type to avoid accidental misuse and make downstream intent clear.

-    const txIds = [];
+    const txIds: TransactionSignature[] = [];

45-60: Sequential per-instruction round-trips are slow & blockhash calls are redundant

Every iteration fetches a fresh blockhash and awaits confirmation before moving on.
For large lookup tables this becomes noticeably slower and places unnecessary load on the RPC.

  1. Reuse one recent blockhash – a single hash is typically valid for ~150 blocks; signing several transactions with the same hash is fine.
  2. Fire transactions in parallelPromise.all(instructions.map(...)) reduces wall-clock time and RPC latency.
  3. Aggregate errors – if one tx fails you can still continue and return partial success (or bubble all errors).

Illustrative refactor:

-const txIds = [];
-for (const instruction of instructions) {
-    const blockhashCtx = await rpc.getLatestBlockhash();
-    const signedTx = buildAndSignTx([instruction], payer, blockhashCtx.blockhash, additionalSigners);
-    const txId = await sendAndConfirmTx(rpc, signedTx, { commitment: 'finalized' }, blockhashCtx);
-    txIds.push(txId);
-}
+const { blockhash, lastValidBlockHeight } = await rpc.getLatestBlockhash();
+const txIds = await Promise.all(
+    instructions.map(async ix => {
+        const signed = buildAndSignTx([ix], payer, blockhash, additionalSigners);
+        return sendAndConfirmTx(
+            rpc,
+            signed,
+            { commitment: 'finalized' },
+            { blockhash, lastValidBlockHeight },
+        );
+    }),
+);

This keeps behaviour identical while greatly improving throughput.

js/stateless.js/tests/unit/instruction/pack-compressed-accounts.test.ts (2)

10-13: Remove dead variables to keep the test tidy

accA, accB, and accC are no longer referenced after the refactor. Extra variables may trip linters or confuse future readers.

-    const accA: any = { treeInfo: { tree: treeA } };
-    const accB: any = { treeInfo: { tree: treeB } };
-    const accC: any = { treeInfo: { tree: treeC } };

14-17: Update description to match the new function contract

padOutputStateMerkleTrees no longer inspects “input state”.
A clearer spec title avoids cognitive dissonance.

-it('should use the 0th state tree of input state if no output state trees are provided', () => {
+it('should pad with the provided tree when only a single tree is given', () => {
js/compressed-token/src/actions/approve.ts (2)

17-20: Drop unused import

selectMinCompressedTokenAccountsForTransfer is imported but never used.

-import {
-    selectMinCompressedTokenAccountsForTransfer,
-    selectTokenAccountsForApprove,
-} from '../utils';
+import { selectTokenAccountsForApprove } from '../utils';

39-45: Avoid mutating the amount parameter

Shadowing the input with a different type can lead to subtle bugs and weakens type safety.

-    amount = bn(amount);
+    const amountBn = bn(amount);

Remember to pass amountBn to subsequent calls.

js/compressed-token/src/actions/transfer-delegated.ts (1)

36-42: Prefer immutable conversion for amount

Same mutability issue as in approve.ts.

-    amount = bn(amount);
+    const amountBn = bn(amount);

Use amountBn downstream.

js/compressed-token/src/actions/merge-token-accounts.ts (2)

51-57: Repeated slice calls waste cycles and risk off-by-one errors

You already cap to 8 accounts; compute that once and iterate on the trimmed array.

-    for (
-        let i = 0;
-        i < compressedTokenAccounts.items.slice(0, 8).length;
-        i += 4
-    ) {
-        const batch = compressedTokenAccounts.items.slice(i, i + 4);
+    const capped = compressedTokenAccounts.items.slice(0, 8);
+    for (let i = 0; i < capped.length; i += 4) {
+        const batch = capped.slice(i, i + 4);

This improves readability and eliminates redundant allocations.


48-49: 1 000 000 compute units may exceed the current network cap

Recent main-net limits cap at 800 000 units. Setting a higher value results in a failed simulation. Consider reducing to 500 000 or adding a try/catch that falls back to a lower limit.

js/stateless.js/tests/e2e/compress.test.ts (2)

69-76: Avoid randomness in tests – inject a deterministic TreeInfo.

selectStateTreeInfo chooses a random active tree which can cause non-deterministic test failures when several trees are present. Prefer selecting a fixed element (e.g. first sorted entry) or mocking the selector for the test run.


95-111: Assertion relies on an exact error-string – brittle test.

Matching the complete message 'Neither input accounts nor outputStateTreeInfo are available' will break the test on any wording tweak.
Compare against the error class/type or use toThrow() with a partial / regex instead.

js/stateless.js/src/actions/compress.ts (1)

41-44: Fetching state-tree infos inside every call adds unnecessary RPC latency.

Consider injecting outputStateTreeInfo from the caller (tests already do this) or caching the result in the action to avoid an extra round-trip per compression.

js/compressed-token/tests/e2e/decompress.test.ts (1)

1-13: defaultTestStateTreeAccounts import is now unused.

Tree selection is performed via selectStateTreeInfo; remove the unused symbol to keep the test lint-clean.

-import {
-    ParsedTokenAccount,
-    Rpc,
-    bn,
-    defaultTestStateTreeAccounts,
-    newAccountWithLamports,
-    getTestRpc,
-    selectStateTreeInfo,
-    TreeInfo,
-} from '@lightprotocol/stateless.js';
+import {
+    ParsedTokenAccount,
+    Rpc,
+    bn,
+    newAccountWithLamports,
+    getTestRpc,
+    selectStateTreeInfo,
+    TreeInfo,
+} from '@lightprotocol/stateless.js';
js/compressed-token/tests/e2e/multi-pool.test.ts (1)

134-143: Random pool / tree selection makes the test flaky

selectStateTreeInfo and selectTokenPoolInfo intentionally return a random active entry.
Because subsequent assertions expect fixed indices & counts, the test can pass or fail depending on RNG state and concurrent test order.

Seed the selection deterministically, or pick by explicit index:

-        const info = selectStateTreeInfo(stateTreeInfos);
+        const info = stateTreeInfos.find(i => i.treeType === expectedType)!;

Same applies to tokenPoolInfo.

js/compressed-token/tests/e2e/delegate.test.ts (1)

122-134: Single global stateTreeInfo reused across tests

stateTreeInfo is captured once in beforeAll, but state-tree rotation can occur between tests (especially when compression/decompression is happening in the same suite).
Stale tree info will cause sporadic Proof verification failed errors.

Fetch a fresh tree snapshot in beforeEach or just-in-time before each action.

js/stateless.js/tests/e2e/rpc-multi-trees.test.ts (1)

41-46: Consider completing the TODO for V2 multi-tree testing.

The code correctly handles V1/V2 differences, but there's a TODO comment about adding specific tests for multiple V2 trees. This might be important for comprehensive test coverage.

Would you like me to help implement the test for multiple V2 trees or create an issue to track this task?

js/compressed-token/tests/e2e/decompress-delegated.test.ts (1)

51-56: Consider using bigint for balance calculations.

While the current BN implementation works, consider migrating to native bigint for better performance and cleaner syntax, especially since you're already using modern JavaScript features.

js/compressed-token/package.json (1)

103-103: Consider refactoring the long test:e2e:all script.

The test:e2e:all script has become quite long and difficult to maintain. Consider refactoring it to use a shell script or breaking it into smaller logical groups.

Example approach:

"test:e2e:all": "pnpm test:e2e:core && pnpm test:e2e:delegation && pnpm test:e2e:compression",
"test:e2e:core": "pnpm test-validator && vitest run tests/e2e/create-mint.test.ts && vitest run tests/e2e/mint-to.test.ts && vitest run tests/e2e/transfer.test.ts",
"test:e2e:delegation": "pnpm test-validator && vitest run tests/e2e/delegate.test.ts && vitest run tests/e2e/transfer-delegated.test.ts && vitest run tests/e2e/decompress-delegated.test.ts",
"test:e2e:compression": "pnpm test-validator-skip-prover && vitest run tests/e2e/compress.test.ts && vitest run tests/e2e/compress-spl-token-account.test.ts && vitest run tests/e2e/decompress.test.ts"
js/compressed-token/src/utils/pack-compressed-token-accounts.ts (1)

39-159: Well-structured refactoring with good error handling.

The refactoring properly implements the new TreeInfo-based approach with excellent defensive programming:

  • Clear mutual exclusivity validation
  • Proper feature flag gating for V2 trees
  • Logical precedence handling for nextTreeInfo

Consider extracting the tree resolution logic into a helper function for better testability:

+function resolveActiveTreeOrQueue(treeInfo: TreeInfo): PublicKey {
+    const activeTreeInfo = treeInfo.nextTreeInfo || treeInfo;
+    
+    if (activeTreeInfo.treeType === TreeType.StateV2) {
+        if (featureFlags.isV2()) {
+            return activeTreeInfo.queue;
+        }
+        throw new Error('V2 trees are not supported yet');
+    }
+    
+    return activeTreeInfo.tree;
+}

     // Use next tree if available, otherwise fall back to current tree.
     // `nextTreeInfo` always takes precedence.
-    const activeTreeInfo = treeInfo.nextTreeInfo || treeInfo;
-    let activeTreeOrQueue = activeTreeInfo.tree;
-
-    if (activeTreeInfo.treeType === TreeType.StateV2) {
-        if (featureFlags.isV2()) {
-            activeTreeOrQueue = activeTreeInfo.queue;
-        } else throw new Error('V2 trees are not supported yet');
-    }
+    const activeTreeOrQueue = resolveActiveTreeOrQueue(treeInfo);
js/stateless.js/CHANGELOG.md (1)

65-68: Typo in sample code – wrong variable name passed to selectStateTreeInfo

info is not defined in the snippet; the earlier line stores the array in infos. Readers copy-pasting this will hit a ReferenceError.

-const selectedInfo = selectStateTreeInfo(info);
+const selectedInfo = selectStateTreeInfo(infos);
js/compressed-token/tests/e2e/transfer.test.ts (2)

147-156: Remove noisy console.log in test suite

console.log('txid transfer ', txid); will spam CI logs and makes failures harder to read. The txid is rarely useful outside debugging.

-        console.log('txid transfer ', txid);

174-182: transfer helper already prints a txid – duplicate debug output

Same log duplication as above; please drop it for cleaner output.

-        console.log('txid transfer 2 ', txid2);
js/stateless.js/tests/unit/state/compressed-account.test.ts (1)

55-63: hash generation uses numbers > 255 – consider Uint8Array

new Array(32).fill(1) creates an array of JS numbers, not bytes; anything above 255 would silently truncate when converted to BN/Buffer.
Using a Uint8Array(32).fill(1) is safer and documents intent.

No diff provided – applies to multiple occurrences.

js/compressed-token/tests/e2e/compress.test.ts (1)

211-223: Inefficient assertion loop – body ignores loop index

for (let i = 0; i < recipients.length; i++) { … } repeatedly executes the same assertion with identical data, adding ~15 × runtime.

Recommend removing the loop and calling assertCompress once, or passing the current recipient slice to validate per-iteration.

js/stateless.js/tests/e2e/rpc-interop.test.ts (3)

23-41: Debug helpers pollute global test output

log / logIndexed emit to stdout on every run. Wrap them behind an env flag or remove to keep CI logs concise.


123-133: Duplicate iteration over treeInfos

You loop twice to compare .tree and .queue; combine for clarity:

validityProof.treeInfos.forEach((elem, idx) => {
    assert.isTrue(elem.tree.equals(validityProofTest.treeInfos[idx].tree));
    assert.isTrue(elem.queue.equals(validityProofTest.treeInfos[idx].queue));
});

682-688: Dead code – results read but never used

allAccountsTestRpc and allAccountsRpc are fetched then discarded. Remove to avoid lint warnings and unnecessary RPC round-trips.

js/compressed-token/CHANGELOG.md (1)

85-85: Add comma for improved readability.

-When building a transaction you can now pick a random treeInfo via `selectStateTreeInfo(infos)`.
+When building a transaction, you can now pick a random treeInfo via `selectStateTreeInfo(infos)`.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~85-~85: Possible missing comma found.
Context: ...ll active state trees. When building a transaction you can now pick a random treeInfo via ...

(AI_HYDRA_LEO_MISSING_COMMA)

js/compressed-token/src/utils/get-token-pool-infos.ts (1)

42-44: Consider making the pool count configurable

The function is hardcoded to fetch exactly 5 token pools. Consider extracting this as a constant or making it a parameter for future flexibility.

+const MAX_TOKEN_POOLS = 5;
+
 export async function getTokenPoolInfos(
     rpc: Rpc,
     mint: PublicKey,
     commitment?: Commitment,
 ): Promise<TokenPoolInfo[]> {
-    const addressesAndBumps = Array.from({ length: 5 }, (_, i) =>
+    const addressesAndBumps = Array.from({ length: MAX_TOKEN_POOLS }, (_, i) =>
         CompressedTokenProgram.deriveTokenPoolPdaWithIndex(mint, i),
     );
js/stateless.js/src/state/compressed-account.ts (1)

226-236: Use bn() helper instead of new BN() for consistency.

The codebase is migrating to use the bn() helper function, but these deprecated functions still use new BN(0).

Apply this diff to maintain consistency:

-    lamports: lamports ?? new BN(0),
+    lamports: lamports ?? bn(0),

Also applies to: 240-253

js/stateless.js/src/rpc.ts (1)

633-636: Add deprecation notice to guide users.

The deprecated method should inform users about the replacement.

-    async getCachedActiveStateTreeInfos() {}
+    async getCachedActiveStateTreeInfos() {
+        console.warn('getCachedActiveStateTreeInfos is deprecated. Use getStateTreeInfos() instead.');
+        return this.getStateTreeInfos();
+    }
js/compressed-token/src/layout.ts (1)

534-547: Technical debt: Optional proof support

The comment indicates that approve and revoke instructions don't support optional proofs yet, requiring the use of an empty proof placeholder. This is a limitation that should be addressed in future updates.

Would you like me to create an issue to track adding optional proof support for approve and revoke instructions?

js/compressed-token/src/program.ts (1)

650-661: Consider making the token pool limit configurable

The findTokenPoolIndexAndBump method hardcodes a limit of 5 indices (0-4). Consider making this limit configurable or at least define it as a constant for easier maintenance.

+    static readonly MAX_TOKEN_POOLS_PER_MINT = 5;
+
     static findTokenPoolIndexAndBump(
         poolPda: PublicKey,
         mint: PublicKey,
     ): [number, number] {
-        for (let index = 0; index < 5; index++) {
+        for (let index = 0; index < CompressedTokenProgram.MAX_TOKEN_POOLS_PER_MINT; index++) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 655-655: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

js/stateless.js/src/utils/get-state-tree-infos.ts (1)

197-209: Consider adding documentation for the nullified tree linking logic

The logic for linking nullified trees to their next available tree is complex. While the implementation appears correct, it would benefit from additional inline comments explaining:

  • Why PublicKey.default is used as a placeholder
  • The relationship between nullified trees and their next tree assignments
  • The assumption that there will always be an available tree to assign
        /// for each context, check if the tree is in the nullifyLookupTable
+       // Link nullified trees to the next available active tree
+       // Trees marked with PublicKey.default placeholder need assignment
        for (const context of contexts) {
            if (context.nextTreeInfo?.tree.equals(PublicKey.default)) {
+               // Find an active tree (one without nextTreeInfo) to link to
                const nextAvailableTreeInfo = contexts.find(
                    ctx => !ctx.nextTreeInfo,
                );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d9c398f and 808071c.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (106)
  • Cargo.toml (1 hunks)
  • cli/src/commands/compress-spl/index.ts (1 hunks)
  • cli/src/commands/decompress-spl/index.ts (1 hunks)
  • cli/src/commands/init/index.ts (1 hunks)
  • cli/src/commands/start-prover/index.ts (1 hunks)
  • examples/anchor/counter/tests/test.rs (1 hunks)
  • examples/browser/nextjs/src/app/page.tsx (3 hunks)
  • js/compressed-token/CHANGELOG.md (2 hunks)
  • js/compressed-token/package.json (2 hunks)
  • js/compressed-token/rollup.config.js (1 hunks)
  • js/compressed-token/src/actions/approve-and-mint-to.ts (2 hunks)
  • js/compressed-token/src/actions/approve.ts (1 hunks)
  • js/compressed-token/src/actions/compress-spl-token-account.ts (2 hunks)
  • js/compressed-token/src/actions/compress.ts (3 hunks)
  • js/compressed-token/src/actions/create-mint.ts (2 hunks)
  • js/compressed-token/src/actions/create-token-pool.ts (4 hunks)
  • js/compressed-token/src/actions/create-token-program-lookup-table.ts (2 hunks)
  • js/compressed-token/src/actions/decompress-delegated.ts (1 hunks)
  • js/compressed-token/src/actions/decompress.ts (3 hunks)
  • js/compressed-token/src/actions/index.ts (1 hunks)
  • js/compressed-token/src/actions/merge-token-accounts.ts (4 hunks)
  • js/compressed-token/src/actions/mint-to.ts (2 hunks)
  • js/compressed-token/src/actions/revoke.ts (1 hunks)
  • js/compressed-token/src/actions/transfer-delegated.ts (1 hunks)
  • js/compressed-token/src/actions/transfer.ts (3 hunks)
  • js/compressed-token/src/constants.ts (2 hunks)
  • js/compressed-token/src/idl.ts (4 hunks)
  • js/compressed-token/src/index.ts (1 hunks)
  • js/compressed-token/src/instructions/index.ts (0 hunks)
  • js/compressed-token/src/layout.ts (9 hunks)
  • js/compressed-token/src/program.ts (26 hunks)
  • js/compressed-token/src/types.ts (5 hunks)
  • js/compressed-token/src/utils/get-token-pool-infos.ts (1 hunks)
  • js/compressed-token/src/utils/index.ts (1 hunks)
  • js/compressed-token/src/utils/pack-compressed-token-accounts.ts (5 hunks)
  • js/compressed-token/src/utils/select-input-accounts.ts (1 hunks)
  • js/compressed-token/src/utils/validation.ts (1 hunks)
  • js/compressed-token/tests/e2e/approve-and-mint-to.test.ts (5 hunks)
  • js/compressed-token/tests/e2e/compress-spl-token-account.test.ts (15 hunks)
  • js/compressed-token/tests/e2e/compress.test.ts (14 hunks)
  • js/compressed-token/tests/e2e/create-token-pool.test.ts (8 hunks)
  • js/compressed-token/tests/e2e/decompress-delegated.test.ts (1 hunks)
  • js/compressed-token/tests/e2e/decompress.test.ts (6 hunks)
  • js/compressed-token/tests/e2e/delegate.test.ts (1 hunks)
  • js/compressed-token/tests/e2e/layout.test.ts (14 hunks)
  • js/compressed-token/tests/e2e/merge-token-accounts.test.ts (7 hunks)
  • js/compressed-token/tests/e2e/mint-to.test.ts (10 hunks)
  • js/compressed-token/tests/e2e/multi-pool.test.ts (1 hunks)
  • js/compressed-token/tests/e2e/rpc-multi-trees.test.ts (6 hunks)
  • js/compressed-token/tests/e2e/rpc-token-interop.test.ts (5 hunks)
  • js/compressed-token/tests/e2e/select-accounts.test.ts (35 hunks)
  • js/compressed-token/tests/e2e/transfer-delegated.test.ts (1 hunks)
  • js/compressed-token/tests/e2e/transfer.test.ts (13 hunks)
  • js/compressed-token/tsconfig.json (1 hunks)
  • js/compressed-token/vitest.config.ts (1 hunks)
  • js/stateless.js/CHANGELOG.md (2 hunks)
  • js/stateless.js/package.json (2 hunks)
  • js/stateless.js/src/actions/compress.ts (1 hunks)
  • js/stateless.js/src/actions/create-account.ts (5 hunks)
  • js/stateless.js/src/actions/decompress.ts (1 hunks)
  • js/stateless.js/src/actions/index.ts (1 hunks)
  • js/stateless.js/src/actions/transfer.ts (4 hunks)
  • js/stateless.js/src/constants.ts (10 hunks)
  • js/stateless.js/src/index.ts (1 hunks)
  • js/stateless.js/src/instruction/index.ts (0 hunks)
  • js/stateless.js/src/instruction/pack-compressed-accounts.ts (0 hunks)
  • js/stateless.js/src/programs/index.ts (0 hunks)
  • js/stateless.js/src/programs/system/idl.ts (2 hunks)
  • js/stateless.js/src/programs/system/index.ts (1 hunks)
  • js/stateless.js/src/programs/system/layout.ts (7 hunks)
  • js/stateless.js/src/programs/system/pack.ts (1 hunks)
  • js/stateless.js/src/programs/system/program.ts (13 hunks)
  • js/stateless.js/src/programs/system/select-compressed-accounts.ts (1 hunks)
  • js/stateless.js/src/rpc-interface.ts (17 hunks)
  • js/stateless.js/src/rpc.ts (32 hunks)
  • js/stateless.js/src/state/BN254.ts (1 hunks)
  • js/stateless.js/src/state/bn.ts (1 hunks)
  • js/stateless.js/src/state/compressed-account.ts (1 hunks)
  • js/stateless.js/src/state/index.ts (1 hunks)
  • js/stateless.js/src/state/types.ts (1 hunks)
  • js/stateless.js/src/test-helpers/test-rpc/get-compressed-accounts.ts (2 hunks)
  • js/stateless.js/src/test-helpers/test-rpc/get-compressed-token-accounts.ts (7 hunks)
  • js/stateless.js/src/test-helpers/test-rpc/get-parsed-events.ts (2 hunks)
  • js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts (12 hunks)
  • js/stateless.js/src/utils/address.ts (1 hunks)
  • js/stateless.js/src/utils/calculate-compute-unit-price.ts (1 hunks)
  • js/stateless.js/src/utils/conversion.ts (3 hunks)
  • js/stateless.js/src/utils/get-state-tree-infos.ts (1 hunks)
  • js/stateless.js/src/utils/index.ts (1 hunks)
  • js/stateless.js/src/utils/parse-validity-proof.ts (5 hunks)
  • js/stateless.js/src/utils/send-and-confirm.ts (0 hunks)
  • js/stateless.js/src/utils/state-tree-lookup-table.ts (5 hunks)
  • js/stateless.js/src/utils/validation.ts (2 hunks)
  • js/stateless.js/tests/e2e/compress.test.ts (10 hunks)
  • js/stateless.js/tests/e2e/layout.test.ts (3 hunks)
  • js/stateless.js/tests/e2e/rpc-interop.test.ts (15 hunks)
  • js/stateless.js/tests/e2e/rpc-multi-trees.test.ts (8 hunks)
  • js/stateless.js/tests/e2e/test-rpc.test.ts (4 hunks)
  • js/stateless.js/tests/e2e/testnet.test.ts (2 hunks)
  • js/stateless.js/tests/e2e/transfer.test.ts (2 hunks)
  • js/stateless.js/tests/unit/instruction/pack-compressed-accounts.test.ts (1 hunks)
  • js/stateless.js/tests/unit/state/bn254.test.ts (1 hunks)
  • js/stateless.js/tests/unit/state/compressed-account.test.ts (4 hunks)
  • js/stateless.js/tests/unit/utils/conversion.test.ts (3 hunks)
  • js/stateless.js/tests/unit/utils/tree-info.test.ts (1 hunks)
  • js/stateless.js/vitest.config.ts (1 hunks)
💤 Files with no reviewable changes (5)
  • js/stateless.js/src/programs/index.ts
  • js/stateless.js/src/utils/send-and-confirm.ts
  • js/compressed-token/src/instructions/index.ts
  • js/stateless.js/src/instruction/index.ts
  • js/stateless.js/src/instruction/pack-compressed-accounts.ts
🧰 Additional context used
🧬 Code Graph Analysis (35)
js/stateless.js/tests/e2e/transfer.test.ts (1)
js/stateless.js/src/actions/compress.ts (1)
  • compress (31-63)
js/compressed-token/src/utils/validation.ts (1)
js/stateless.js/src/rpc-interface.ts (1)
  • ParsedTokenAccount (236-239)
cli/src/commands/compress-spl/index.ts (1)
js/compressed-token/src/program.ts (1)
  • CompressedTokenProgram (599-1646)
js/stateless.js/src/utils/validation.ts (1)
js/stateless.js/src/state/types.ts (1)
  • CompressedAccountLegacy (146-163)
js/compressed-token/tests/e2e/decompress.test.ts (5)
js/stateless.js/src/state/types.ts (1)
  • TreeInfo (77-99)
js/compressed-token/src/utils/get-token-pool-infos.ts (4)
  • TokenPoolInfo (104-141)
  • getTokenPoolInfos (37-93)
  • selectTokenPoolInfo (173-187)
  • selectTokenPoolInfosForDecompression (201-229)
js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts (1)
  • getTestRpc (91-110)
js/stateless.js/src/test-helpers/test-utils.ts (1)
  • newAccountWithLamports (69-83)
js/stateless.js/src/utils/get-state-tree-infos.ts (1)
  • selectStateTreeInfo (84-110)
js/stateless.js/src/utils/parse-validity-proof.ts (3)
js/stateless.js/src/state/types.ts (1)
  • ValidityProof (397-410)
js/stateless.js/src/state/bn.ts (1)
  • bn (3-12)
js/stateless.js/src/constants.ts (1)
  • FIELD_SIZE (29-31)
js/stateless.js/tests/unit/instruction/pack-compressed-accounts.test.ts (1)
js/stateless.js/src/programs/system/pack.ts (1)
  • padOutputStateMerkleTrees (52-63)
js/compressed-token/tests/e2e/merge-token-accounts.test.ts (3)
js/stateless.js/src/state/types.ts (1)
  • TreeInfo (77-99)
js/stateless.js/src/utils/get-state-tree-infos.ts (1)
  • selectStateTreeInfo (84-110)
js/compressed-token/src/actions/merge-token-accounts.ts (1)
  • mergeTokenAccounts (29-86)
js/stateless.js/src/test-helpers/test-rpc/get-compressed-accounts.ts (3)
js/stateless.js/src/utils/get-state-tree-infos.ts (1)
  • getStateTreeInfoByPubkey (8-13)
js/stateless.js/src/state/compressed-account.ts (3)
  • MerkleContext (80-97)
  • CompressedAccountWithMerkleContext (14-17)
  • createCompressedAccountWithMerkleContextLegacy (240-253)
js/stateless.js/src/state/bn.ts (1)
  • bn (3-12)
js/compressed-token/src/actions/create-token-program-lookup-table.ts (1)
js/stateless.js/src/utils/send-and-confirm.ts (2)
  • buildAndSignTx (123-144)
  • sendAndConfirmTx (51-62)
js/compressed-token/src/actions/revoke.ts (4)
js/stateless.js/src/rpc-interface.ts (1)
  • ParsedTokenAccount (236-239)
js/compressed-token/src/program.ts (1)
  • CompressedTokenProgram (599-1646)
js/stateless.js/src/utils/dedupe-signer.ts (1)
  • dedupeSigner (4-11)
js/stateless.js/src/utils/send-and-confirm.ts (2)
  • buildAndSignTx (123-144)
  • sendAndConfirmTx (51-62)
js/stateless.js/tests/e2e/layout.test.ts (2)
js/compressed-token/src/idl.ts (1)
  • IDL (1833-3669)
js/stateless.js/src/constants.ts (1)
  • COMPRESSED_TOKEN_PROGRAM_ID (198-200)
js/stateless.js/tests/e2e/compress.test.ts (7)
js/stateless.js/src/state/types.ts (1)
  • TreeInfo (77-99)
js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts (1)
  • TestRpc (120-950)
js/stateless.js/src/test-helpers/test-utils.ts (1)
  • newAccountWithLamports (69-83)
js/stateless.js/src/utils/get-state-tree-infos.ts (1)
  • selectStateTreeInfo (84-110)
js/stateless.js/src/actions/create-account.ts (1)
  • createAccountWithLamports (113-175)
js/stateless.js/src/actions/compress.ts (1)
  • compress (31-63)
js/stateless.js/src/actions/decompress.ts (1)
  • decompress (25-71)
js/compressed-token/src/utils/pack-compressed-token-accounts.ts (4)
js/stateless.js/src/state/types.ts (2)
  • TreeInfo (77-99)
  • InputTokenDataWithContext (415-440)
js/stateless.js/src/rpc-interface.ts (1)
  • ParsedTokenAccount (236-239)
js/stateless.js/src/programs/system/pack.ts (2)
  • getIndexOrAdd (22-34)
  • padOutputStateMerkleTrees (52-63)
js/stateless.js/src/constants.ts (1)
  • featureFlags (16-19)
js/stateless.js/src/actions/transfer.ts (1)
js/stateless.js/src/state/bn.ts (1)
  • bn (3-12)
js/stateless.js/src/actions/compress.ts (4)
js/stateless.js/src/programs/system/program.ts (2)
  • compress (421-476)
  • LightSystemProgram (171-537)
js/stateless.js/src/rpc.ts (1)
  • Rpc (614-1938)
js/stateless.js/src/state/types.ts (1)
  • TreeInfo (77-99)
js/stateless.js/src/utils/get-state-tree-infos.ts (1)
  • selectStateTreeInfo (84-110)
js/compressed-token/tests/e2e/approve-and-mint-to.test.ts (5)
js/compressed-token/src/utils/get-token-pool-infos.ts (3)
  • TokenPoolInfo (104-141)
  • selectTokenPoolInfo (173-187)
  • getTokenPoolInfos (37-93)
js/stateless.js/src/state/types.ts (1)
  • TreeInfo (77-99)
js/stateless.js/src/utils/get-state-tree-infos.ts (1)
  • selectStateTreeInfo (84-110)
js/compressed-token/src/program.ts (1)
  • approveAndMintTo (937-972)
js/compressed-token/src/actions/approve-and-mint-to.ts (1)
  • approveAndMintTo (44-100)
js/compressed-token/src/actions/merge-token-accounts.ts (1)
js/stateless.js/src/utils/send-and-confirm.ts (1)
  • sendAndConfirmTx (51-62)
js/stateless.js/src/programs/system/layout.ts (2)
js/stateless.js/src/constants.ts (1)
  • INVOKE_DISCRIMINATOR (38-40)
js/stateless.js/src/state/bn.ts (1)
  • bn (3-12)
js/compressed-token/src/actions/mint-to.ts (6)
js/compressed-token/src/program.ts (2)
  • mintTo (867-920)
  • CompressedTokenProgram (599-1646)
js/stateless.js/src/state/types.ts (1)
  • TreeInfo (77-99)
js/compressed-token/src/utils/get-token-pool-infos.ts (3)
  • TokenPoolInfo (104-141)
  • selectTokenPoolInfo (173-187)
  • getTokenPoolInfos (37-93)
js/stateless.js/src/utils/get-state-tree-infos.ts (1)
  • selectStateTreeInfo (84-110)
js/stateless.js/src/utils/dedupe-signer.ts (1)
  • dedupeSigner (4-11)
js/stateless.js/src/utils/send-and-confirm.ts (1)
  • sendAndConfirmTx (51-62)
js/stateless.js/tests/unit/state/compressed-account.test.ts (2)
js/stateless.js/src/state/compressed-account.ts (3)
  • createCompressedAccountLegacy (226-236)
  • createMerkleContextLegacy (258-268)
  • createCompressedAccountWithMerkleContextLegacy (240-253)
js/stateless.js/src/state/bn.ts (1)
  • bn (3-12)
js/compressed-token/src/utils/select-input-accounts.ts (1)
js/stateless.js/src/rpc-interface.ts (1)
  • ParsedTokenAccount (236-239)
js/stateless.js/tests/e2e/rpc-multi-trees.test.ts (8)
js/stateless.js/src/state/types.ts (1)
  • TreeInfo (77-99)
js/stateless.js/src/rpc.ts (1)
  • createRpc (232-265)
js/stateless.js/src/utils/get-state-tree-infos.ts (1)
  • selectStateTreeInfo (84-110)
js/stateless.js/src/constants.ts (1)
  • featureFlags (16-19)
js/stateless.js/src/test-helpers/test-utils.ts (1)
  • newAccountWithLamports (69-83)
js/stateless.js/src/actions/compress.ts (1)
  • compress (31-63)
js/stateless.js/src/actions/transfer.ts (1)
  • transfer (30-102)
js/stateless.js/src/actions/create-account.ts (1)
  • createAccountWithLamports (113-175)
js/stateless.js/src/utils/conversion.ts (1)
js/stateless.js/src/state/types.ts (2)
  • CompressedAccountLegacy (146-163)
  • PackedMerkleContextLegacy (120-137)
js/compressed-token/tests/e2e/compress-spl-token-account.test.ts (4)
js/stateless.js/src/state/types.ts (1)
  • TreeInfo (77-99)
js/compressed-token/src/utils/get-token-pool-infos.ts (3)
  • TokenPoolInfo (104-141)
  • selectTokenPoolInfo (173-187)
  • getTokenPoolInfos (37-93)
js/stateless.js/src/utils/get-state-tree-infos.ts (1)
  • selectStateTreeInfo (84-110)
js/stateless.js/src/state/bn.ts (1)
  • bn (3-12)
js/compressed-token/src/types.ts (2)
js/stateless.js/src/state/types.ts (4)
  • PackedMerkleContextLegacy (120-137)
  • ValidityProof (397-410)
  • InputTokenDataWithContext (415-440)
  • CompressedCpiContext (353-368)
js/compressed-token/src/utils/get-token-pool-infos.ts (1)
  • TokenPoolInfo (104-141)
js/stateless.js/src/constants.ts (1)
js/stateless.js/src/state/types.ts (1)
  • TreeInfo (77-99)
js/stateless.js/src/state/compressed-account.ts (2)
js/stateless.js/src/state/types.ts (4)
  • CompressedAccountData (199-212)
  • TreeInfo (77-99)
  • CompressedAccountLegacy (146-163)
  • PackedMerkleContextLegacy (120-137)
js/stateless.js/src/state/BN254.ts (1)
  • BN254 (12-12)
js/stateless.js/src/programs/system/program.ts (5)
js/stateless.js/src/utils/address.ts (1)
  • NewAddressParams (43-62)
js/stateless.js/src/state/types.ts (3)
  • ValidityProof (397-410)
  • TreeInfo (77-99)
  • CompressedAccountLegacy (146-163)
js/stateless.js/src/state/bn.ts (1)
  • bn (3-12)
js/stateless.js/src/utils/validation.ts (2)
  • validateSufficientBalance (8-12)
  • validateSameOwner (14-28)
js/stateless.js/src/state/compressed-account.ts (2)
  • createCompressedAccountLegacy (226-236)
  • CompressedAccountWithMerkleContext (14-17)
js/stateless.js/src/state/types.ts (3)
js/stateless.js/src/state/compressed-account.ts (1)
  • PackedCompressedAccountWithMerkleContext (204-221)
js/stateless.js/src/utils/address.ts (1)
  • NewAddressParamsPacked (64-83)
js/compressed-token/src/types.ts (2)
  • InputTokenDataWithContext (53-60)
  • TokenData (139-165)
js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts (9)
js/stateless.js/src/constants.ts (2)
  • defaultTestStateTreeAccounts (178-186)
  • localTestActiveStateTreeInfo (135-161)
js/stateless.js/src/rpc-interface.ts (4)
  • CompressionApiInterface (756-861)
  • CompressedTransaction (120-142)
  • WithContext (241-248)
  • ValidityProofWithContext (161-190)
js/stateless.js/src/state/types.ts (2)
  • TreeInfo (77-99)
  • PublicTransactionEvent (231-272)
js/stateless.js/src/state/BN254.ts (1)
  • BN254 (12-12)
js/stateless.js/src/state/compressed-account.ts (1)
  • MerkleContextWithMerkleProof (102-115)
js/stateless.js/src/test-helpers/test-rpc/get-parsed-events.ts (1)
  • getParsedEvents (37-151)
js/stateless.js/src/utils/get-state-tree-infos.ts (1)
  • getStateTreeInfoByPubkey (8-13)
js/stateless.js/src/state/bn.ts (1)
  • bn (3-12)
js/stateless.js/src/rpc.ts (4)
  • convertMerkleProofsWithContextToHex (425-443)
  • proverRequest (337-391)
  • MerkleContextWithNewAddressProof (403-413)
  • convertNonInclusionMerkleProofInputsToHex (445-471)
js/compressed-token/src/layout.ts (3)
js/compressed-token/src/constants.ts (6)
  • MINT_TO_DISCRIMINATOR (11-13)
  • BATCH_COMPRESS_DISCRIMINATOR (14-16)
  • COMPRESS_SPL_TOKEN_ACCOUNT_DISCRIMINATOR (20-22)
  • TRANSFER_DISCRIMINATOR (17-19)
  • APPROVE_DISCRIMINATOR (24-26)
  • REVOKE_DISCRIMINATOR (27-29)
js/compressed-token/src/types.ts (5)
  • MintToInstructionData (82-86)
  • BatchCompressInstructionData (67-74)
  • CompressSplTokenAccountInstructionData (87-91)
  • CompressedTokenInstructionDataApprove (167-177)
  • CompressedTokenInstructionDataRevoke (179-185)
js/stateless.js/src/state/types.ts (1)
  • ValidityProof (397-410)
js/stateless.js/src/rpc-interface.ts (3)
js/stateless.js/src/state/BN254.ts (1)
  • BN254 (12-12)
js/stateless.js/src/state/types.ts (3)
  • TreeInfo (77-99)
  • AddressTreeInfo (107-115)
  • ValidityProof (397-410)
js/stateless.js/src/state/bn.ts (1)
  • bn (3-12)
js/stateless.js/src/rpc.ts (8)
js/stateless.js/src/constants.ts (5)
  • versionedEndpoint (26-27)
  • featureFlags (16-19)
  • localTestActiveStateTreeInfo (135-161)
  • defaultStateTreeLookupTables (99-123)
  • defaultTestStateTreeAccounts (178-186)
js/stateless.js/src/rpc-interface.ts (17)
  • jsonRpcResultAndContext (362-371)
  • CompressionApiInterface (756-861)
  • CompressedAccountResultV2 (408-425)
  • CompressedAccountResult (390-406)
  • MerkleProofResultV2 (549-557)
  • MerkleProofResult (537-544)
  • MultipleCompressedAccountsResultV2 (461-463)
  • MultipleCompressedAccountsResult (454-456)
  • CompressedAccountsByOwnerResultV2 (476-479)
  • CompressedAccountsByOwnerResult (468-471)
  • jsonRpcResult (338-349)
  • CompressedTransactionResultV2 (736-754)
  • CompressedTransactionResult (713-731)
  • ValidityProofWithContext (161-190)
  • WithContext (241-248)
  • ValidityProofResultV2 (619-623)
  • ValidityProofResult (594-603)
js/stateless.js/src/utils/get-state-tree-infos.ts (3)
  • getStateTreeInfoByPubkey (8-13)
  • getAllStateTreeInfos (120-213)
  • getTreeInfoByPubkey (15-35)
js/stateless.js/src/state/compressed-account.ts (4)
  • CompressedAccountWithMerkleContext (14-17)
  • createCompressedAccountWithMerkleContextLegacy (240-253)
  • createMerkleContextLegacy (258-268)
  • MerkleContextWithMerkleProof (102-115)
js/stateless.js/src/state/types.ts (4)
  • ValidityProof (397-410)
  • AddressTreeInfo (107-115)
  • TokenData (445-471)
  • TreeInfo (77-99)
js/stateless.js/src/state/bn.ts (1)
  • bn (3-12)
js/stateless.js/src/state/BN254.ts (2)
  • BN254 (12-12)
  • encodeBN254toBase58 (43-49)
js/stateless.js/src/utils/validation.ts (1)
  • validateNumbersForProof (31-50)
js/compressed-token/src/program.ts (9)
js/stateless.js/src/state/types.ts (3)
  • TreeInfo (77-99)
  • ValidityProof (397-410)
  • CompressedProof (373-386)
js/compressed-token/src/utils/get-token-pool-infos.ts (2)
  • TokenPoolInfo (104-141)
  • checkTokenPoolInfo (13-27)
js/compressed-token/src/types.ts (6)
  • TokenTransferOutputData (11-28)
  • DelegatedTransfer (62-65)
  • CompressedTokenInstructionDataTransfer (99-137)
  • BatchCompressInstructionData (67-74)
  • CompressedTokenInstructionDataApprove (167-177)
  • CompressedTokenInstructionDataRevoke (179-185)
js/compressed-token/src/layout.ts (10)
  • createTokenPoolAccountsLayout (268-287)
  • addTokenPoolAccountsLayout (289-310)
  • encodeBatchCompressInstructionData (137-152)
  • mintToAccountsLayout (312-365)
  • encodeTransferInstructionData (193-213)
  • transferAccountsLayout (367-424)
  • encodeApproveInstructionData (549-574)
  • approveAccountsLayout (426-462)
  • encodeRevokeInstructionData (588-613)
  • revokeAccountsLayout (464-464)
js/stateless.js/src/constants.ts (4)
  • defaultStaticAccountsStruct (79-89)
  • featureFlags (16-19)
  • noopProgram (58-58)
  • accountCompressionProgram (60-61)
js/stateless.js/src/utils/conversion.ts (1)
  • toArray (28-29)
js/stateless.js/src/programs/system/program.ts (2)
  • createTransferOutputState (198-223)
  • createDecompressOutputState (225-249)
js/compressed-token/src/utils/pack-compressed-token-accounts.ts (1)
  • packCompressedTokenAccounts (39-159)
js/compressed-token/src/utils/validation.ts (1)
  • checkMint (11-24)
🪛 LanguageTool
js/compressed-token/CHANGELOG.md

[uncategorized] ~64-~64: Loose punctuation mark.
Context: ...Instruction Changes: - compress, mintTo, approveAndMintTo, `compress...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~66-~66: Loose punctuation mark.
Context: ...ore TokenPoolInfos. - decompress, transfer now do not allow state tree ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~66-~66: This verb does not appear to agree with the subject. Consider using a different form.
Context: ...s. - decompress, transfer now do not allow state tree overrides. - Ac...

(AI_EN_LECTOR_REPLACEMENT_VERB_AGREEMENT)


[uncategorized] ~78-~78: Loose punctuation mark.
Context: ...ransfer support. - addTokenPools: you can now register additional token p...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~85-~85: Possible missing comma found.
Context: ...ll active state trees. When building a transaction you can now pick a random treeInfo via ...

(AI_HYDRA_LEO_MISSING_COMMA)

🪛 Biome (1.9.4)
js/compressed-token/src/program.ts

[error] 655-655: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 688-688: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 779-779: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 828-828: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 832-832: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 840-840: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 845-845: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 904-904: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1103-1103: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1180-1180: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1201-1201: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1207-1207: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1218-1218: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1264-1264: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1266-1266: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1275-1275: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1357-1357: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1363-1363: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1405-1405: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1562-1562: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1568-1568: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1575-1575: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1628-1628: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1634-1634: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1641-1641: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (113)
Cargo.toml (1)

115-117: Workspace-level serialization dependencies look correct.
Adding serde = "1.0" with the derive feature and serde_json = "1.0" aligns with the new (de)serialization requirements across your Rust crates and complements the JS/TS updates.

cli/src/commands/init/index.ts (1)

1-4: Approve removal of unused execSync import.
The unused execSync import was removed without affecting any functionality; executeCommand is now the sole mechanism for running subprocesses.

js/stateless.js/tests/unit/state/bn254.test.ts (1)

2-2: Approve consolidated import from state module.
Importing createBN254 and encodeBN254toBase58 from the unified state entrypoint aligns with the recent refactors and maintains test consistency.

js/stateless.js/src/utils/address.ts (2)

1-1: Approve removal of unused AccountMeta import.
The unused AccountMeta import has been cleaned up, reducing dead code.


4-4: Approve updated import path for getIndexOrAdd.
Switching to ../programs/system/pack matches the new code organization for packing utilities.

js/stateless.js/src/utils/calculate-compute-unit-price.ts (1)

5-5: Approve JSDoc hyperlink enhancement.
Linking directly to the Solana Web3.js reference improves documentation clarity and developer navigation.

js/compressed-token/tsconfig.json (1)

16-16: Approve explicit relative typeRoots path.
Using "./types" ensures the custom types directory is correctly resolved alongside @types.

js/stateless.js/src/state/index.ts (1)

2-3: Verify there is no naming collision between bn re-export and existing BN helpers.

export * from './BN254' already re-exports a BN class. Re-exporting a helper called bn is fine, but double-check:

  1. ./bn does not also export a top-level identifier called BN that would shadow the previous one.
  2. Down-stream imports such as import { bn, BN } … still resolve unambiguously.

A quick grep in the codebase for export .*BN.* from './bn' will confirm.

js/stateless.js/src/actions/index.ts (1)

4-5: API surface changed – ensure no consumers relied on ./common.

Switching the export from ./common to ../utils/dedupe-signer drops any symbols that were previously re-exported from ./common.
If external packages imported them through @lightprotocol/stateless.js/actions, this is a breaking change.

Confirm that:

  1. All removed re-exports are now available elsewhere.
  2. Version bump (minor/major) reflects this potential break.

If nothing else was exported from ./common, feel free to ignore.

js/stateless.js/vitest.config.ts (1)

7-10: Tightened timeouts – watch for flaky CI failures.

Reducing testTimeout to 30 s and adding a 20 s hookTimeout is fine, but some e2e blockchain interactions (blockhash fetch, confirmation) occasionally exceed that, especially under load.

If flakiness appears, bump these values or conditionally increase them in CI via env vars.

js/stateless.js/tests/e2e/testnet.test.ts (2)

5-6: Imported surface trimmed correctly – 👍

The unused helpers were removed and tree-info utilities are no longer required. Import list is tidy.


21-22:

✅ Verification successful

Ensure all compress call-sites were updated to the new 4-arg signature

This test compiles, but any overlooked call elsewhere will now fail at runtime/compile-time.
Run a quick grep to confirm:


🏁 Script executed:

rg "compress\(.*\,\s*.*\,\s*.*\,\s*.*\,\s*" js | cat

Length of output: 171


Let’s list every compress( invocation in your JS/TS files to ensure they all have four arguments:


🏁 Script executed:

#!/usr/bin/env bash
# Find all compress call-sites in .js and .ts under js/
rg -n --color never "compress\(" --glob "*.{js,ts}" js

Length of output: 4722


Let’s inspect the compress signature to confirm its parameters:


🏁 Script executed:

#!/usr/bin/env bash
# Locate and print the compress function signature
grep -R "export async function compress" -n js/stateless.js/src/actions/compress.ts
sed -n '1,60p' js/stateless.js/src/actions/compress.ts

Length of output: 1951


All compress() invocations match the new 4-arg signature

I’ve checked every call in js/stateless.js and js/compressed-token—each compress( has at least the required

  1. rpc
  2. payer
  3. lamports
  4. toAddress

Optional outputStateTreeInfo or confirmOptions are passed where needed. No call‐site is missing arguments.

js/stateless.js/src/utils/validation.ts (1)

1-7: Import alias matches implementation – looks good.
Transitioning to CompressedAccountLegacy aligns this helper with the new state
type without behavioural change.

js/stateless.js/package.json (4)

3-3: Confirm version bump consistency.
Ensure the bumped version 0.21.0 aligns with release notes, CHANGELOG, and any interdependent packages.


91-91: Approve new unit test script.
The test:unit:tree-info target follows existing naming conventions and integrates cleanly.


94-94: Verify skip-prover script behavior.
Double-check that --skip-prover in test-validator-skip-prover works reliably in CI and doesn’t mask upstream failures.


97-98: Approve bail-on-first-failure flags.
Adding --bail=1 to test-rpc and rpc-interop improves feedback speed during end-to-end runs.

js/compressed-token/src/index.ts (2)

1-2: Review export ordering.
Grouping actions and utils first enhances discoverability, but please verify no missing exports (e.g., instructions) due to this reordering.


4-5: Approve reordered exports.
Relocating idl and layout exports higher up provides clearer structure.

js/compressed-token/tests/e2e/select-accounts.test.ts (2)

1-3: Approve BigNumber helper import.
Switching to the central bn utility from @lightprotocol/stateless.js standardizes big-number usage across tests.


5-890: Remaining changes uniformly replace BN with bn across multiple test cases—no additional issues detected.

js/stateless.js/src/programs/system/index.ts (1)

1-5: Approve new system program barrel file.
The consolidated exports (IDL, layout, pack, program, select-compressed-accounts) align with the refactor and improve import ergonomics.

js/stateless.js/src/test-helpers/test-rpc/get-parsed-events.ts (1)

26-26: Verify updated import path.
Ensure decodePublicTransactionEvent from ../../programs/system/layout resolves and matches the new module structure.

js/stateless.js/src/actions/decompress.ts (1)

47-50: Explicit endianness for bn(x.hash) avoids silent mis-interpretation

x.hash is a 32-byte BE array. bn() defaults to LE when the input is a Buffer, which silently flips the value.
Pass the endianness explicitly so the proof hash matches the program expectation.

-        userCompressedAccountsWithMerkleContext.map(x => bn(x.hash)),
+        userCompressedAccountsWithMerkleContext.map(x =>
+            bn(x.hash, undefined, 'be'),
+        ),
js/compressed-token/tests/e2e/mint-to.test.ts (1)

91-93: Assert that environment selection did not return undefined

selectStateTreeInfo / selectTokenPoolInfo may return undefined when the cluster
doesn’t have a ready tree or pool. All following tests will then fail with opaque
errors. Add an explicit assumption so failures surface immediately and with context.

-        stateTreeInfo = selectStateTreeInfo(await rpc.getStateTreeInfos());
-        tokenPoolInfo = selectTokenPoolInfo(await getTokenPoolInfos(rpc, mint));
+        stateTreeInfo = selectStateTreeInfo(await rpc.getStateTreeInfos());
+        tokenPoolInfo = selectTokenPoolInfo(await getTokenPoolInfos(rpc, mint));
+
+        expect(stateTreeInfo, 'No state tree available').toBeDefined();
+        expect(tokenPoolInfo, 'No token pool found for minted SPL').toBeDefined();
js/stateless.js/tests/e2e/layout.test.ts (1)

16-21: Imports & renamed fields updated correctly

The switch to programs/system/layout and the new COMPRESSED_TOKEN_PROGRAM_ID constant align with the refactor; renamed fields (queuePubkeyIndex, proveByIndex) match the updated IDL. No issues spotted.

Also applies to: 38-39, 143-147

js/compressed-token/tests/e2e/merge-token-accounts.test.ts (1)

67-68: Signature change propagated correctly

mergeTokenAccounts no longer needs stateTreeInfo; the updated calls compile and behave as expected. No additional changes required.

Also applies to: 85-87, 100-102

examples/browser/nextjs/src/app/page.tsx (1)

29-30: LGTM! Clean migration to dynamic state tree management.

The changes properly implement the migration from static state tree accounts to dynamic RPC-fetched state tree information, aligning with the broader V2 protocol updates.

Also applies to: 39-42, 57-57

js/stateless.js/src/test-helpers/test-rpc/get-compressed-accounts.ts (1)

46-68: Well-implemented dynamic tree info retrieval.

The code efficiently fetches state tree infos once and correctly maps each compressed account to its corresponding tree using the merkleTreeIndex. The comment about V2 trees having proveByIndex = true provides helpful context.

js/stateless.js/tests/e2e/test-rpc.test.ts (1)

32-33: Test updates correctly align with the new API.

The changes appropriately adapt to the updated compressed token API:

  • Fixed lamport values provide consistency
  • Simplified compress calls reflect the removal of explicit tree parameters
  • Conditional proof length assertion (26 vs 32) properly handles V1/V2 differences

Also applies to: 36-36, 42-42, 85-85, 113-113

js/compressed-token/tests/e2e/rpc-multi-trees.test.ts (2)

51-58: Verify the V2 tree selection logic.

When featureFlags.isV2() is true, both stateTreeInfo and stateTreeInfo2 are set to the same tree. This might be intentional if V2 only supports single tree scenarios, but please confirm this is the expected behavior for multi-tree tests.


30-33: Clean refactoring to dynamic tree and token pool management.

The test properly migrates from static tree references to dynamically fetched TreeInfo and TokenPoolInfo objects, with assertions correctly updated to use the new structure.

Also applies to: 70-72, 92-98, 108-113, 124-125, 133-141

js/compressed-token/tests/e2e/rpc-token-interop.test.ts (2)

36-41: Creation of two RPC connections looks intentional – just verify

You create both rpc = createRpc() and testRpc = await getTestRpc(lightWasm).
If this is to compare mainnet vs in-memory test RPC it’s fine; otherwise double-initialisation may slow the test suite.


266-279: Potential race between pool PDA discovery and pool initialisation

selectTokenPoolInfo(await getTokenPoolInfos(rpc, mint2)) is executed immediately after createMint, but before any explicit createTokenPool for mint2.
If createMint doesn’t implicitly initialise a pool, getTokenPoolInfos will throw (as seen in its guard clause).
Please confirm the implicit behaviour or add await createTokenPool(...) beforehand.

js/stateless.js/src/programs/system/idl.ts (1)

697-713: IDL field rename changes binary layout – ensure deserialisers are updated

queuePubkeyIndex replaces the previous nullifierQueuePubkeyIndex and new proveByIndex: bool is appended.
Confirm that:

  1. All off-chain serializers reflect the new order/size (the bool shifts subsequent fields).
  2. On-chain program is bumped/renamed to avoid incompatible account data parsing.

Also applies to: 1693-1713

js/compressed-token/src/actions/merge-token-accounts.ts (1)

58-60: API drift: use getValidityProofV0 for consistency

Other new actions switched to getValidityProofV0 (hash, tree, queue).
Sticking to the legacy method here may break once RPC deprecates it. Align with the rest of the codebase.

-        const proof = await rpc.getValidityProof(
-            batch.map(account => bn(account.compressedAccount.hash)),
-        );
+        const proof = await rpc.getValidityProofV0(
+            batch.map(account => ({
+                hash: account.compressedAccount.hash,
+                tree: account.compressedAccount.treeInfo.tree,
+                queue: account.compressedAccount.treeInfo.queue,
+            })),
+        );
js/compressed-token/tests/e2e/decompress.test.ts (1)

120-126: Creating a fresh TestRpc inside the loop resets cluster state.

getTestRpc initialises an independent in-memory ledger, so later decompressions run against a cluster that never received the mint / compress operations executed in beforeAll. Re-use the existing rpc instance or move the compress/mint steps inside the loop.

js/compressed-token/src/actions/transfer.ts (1)

76-77: Re-evaluate 500 k CU limit

Cutting the compute budget in half is great for fees, but a single transfer that spans several input accounts plus the proof-verification logic often goes north of 600 k CU on main-net settings.
Consider:

  1. Making the value configurable (env / param); or
  2. Falling back to a higher limit on TransactionExpiredBlockheightExceeded failures.

This will avoid sporadic “CU exhausted” errors when pools are busy.

js/compressed-token/tests/e2e/compress-spl-token-account.test.ts (1)

83-84: decompress signature drift

Earlier tests invoke decompress with the extra tokenPoolInfos parameter, but this invocation still uses the old 5-arg form.
Confirm the overload still exists or update to the new signature to avoid a compilation error.

js/compressed-token/src/actions/decompress.ts (3)

63-70: LGTM! Proper migration to V0 proof API.

The change from getValidityProof to getValidityProofV0 correctly includes the required tree and queue information for each account, aligning with the new multi-tree support architecture.


71-76: Good defensive programming with null coalescing.

The code properly handles the optional tokenPoolInfos parameter by fetching them if not provided, then selecting appropriate pools based on the amount. This maintains backward compatibility while supporting the new multi-pool architecture.


91-91:

❓ Verification inconclusive

Verify the compute unit limit reduction is sufficient.

The compute unit limit has been reduced from 1,000,000 to 350,000 (65% reduction). Please ensure this is sufficient for all decompression scenarios, especially when dealing with multiple input accounts or complex proof verifications.

Run the following script to check for any compute unit related errors in tests:


🏁 Script executed:

#!/bin/bash
# Description: Search for compute unit related errors or adjustments in decompress tests

# Check for compute unit errors in test files
rg -A 5 -B 5 "compute.*unit|ComputeBudget" js/compressed-token/tests/

# Check for any transaction failures related to compute budget
rg -A 10 "Program failed to complete|exceeded.*compute|compute.*exceeded" js/compressed-token/tests/

Length of output: 5021


🏁 Script executed:

#!/bin/bash
# Verify if there are any decompress-specific tests and their compute unit limits

# Search for any test files or mentions of 'decompress'
rg -A 5 -B 5 "decompress" js/compressed-token/tests/

# Search for compute unit settings in any decompress-related tests
rg -A 5 -B 5 "setComputeUnitLimit" js/compressed-token/tests/ | rg "decompress"

Length of output: 20435


Verify compute unit limit sufficiency in decompress.ts
File: js/compressed-token/src/actions/decompress.ts:91

The compute unit limit was reduced from 1_000_000 to 350_000. Please confirm that all decompress scenarios still complete under 350 k CU, in particular:

  • Multi‐pool decompress (up to 90 accounts) → tests/e2e/multi-pool.test.ts
  • Repeated decompress loops → tests/e2e/decompress.test.ts
  • Delegated decompress → tests/e2e/decompress-delegated.test.ts
  • Decompress within compress‐spl‐token‐account flows → tests/e2e/compress-spl-token-account.test.ts

Consider measuring on‐chain compute usage (e.g. via simulateTransaction or runtime logs) or adding CI/performance checks to catch any budget overruns.

js/stateless.js/tests/e2e/rpc-multi-trees.test.ts (1)

126-135: Good test coverage for multi-tree transfers.

The test properly validates that random output trees are selected and tracked correctly during transfers, ensuring the multi-tree functionality works as expected.

js/compressed-token/src/actions/decompress-delegated.ts (1)

1-99: Well-implemented delegated decompression functionality.

The implementation correctly handles delegated token decompression with proper proof generation, token pool selection, and transaction building. The function follows established patterns and integrates well with the delegation feature set.

js/compressed-token/tests/e2e/decompress-delegated.test.ts (1)

160-207: Excellent test coverage with error case validation.

The test comprehensively validates the delegated decompression flow, including:

  • Proper balance updates for delegate, owner, and recipient
  • No delegated remainder tokens left
  • Appropriate error when attempting to decompress more than delegated

This provides good confidence in the implementation.

js/compressed-token/src/actions/compress-spl-token-account.ts (1)

53-58: Clean implementation of default selection logic.

The null coalescing pattern for both outputStateTreeInfo and tokenPoolInfo provides good defaults while maintaining flexibility. This approach is consistent with other refactored actions.

js/stateless.js/src/programs/system/layout.ts (4)

46-54: LGTM! Simplified MerkleContext structure.

The renaming of nullifierQueuePubkeyIndex to queuePubkeyIndex and replacement of the optional queueIndex struct with a boolean proveByIndex simplifies the data structure while maintaining the same functionality. This change aligns with the protocol updates for v2 support.


100-108: Good improvement for consistent typed array usage.

Wrapping all buffer components in Uint8Array before concatenation ensures consistent type handling and improves compatibility across different JavaScript environments. This is a best practice when dealing with binary data.


255-292: Excellent documentation improvements!

The addition of comprehensive JSDoc comments for all fields in invokeAccountsLayoutParams greatly improves code readability and developer experience. The new feePayer field appropriately separates fee payment responsibility from transaction authority.


537-591: Consistent and improved big number handling.

The migration to the bn() helper function throughout the conversion logic provides better type handling and edge case management. The simplified sequenceNumbers mapping that returns raw values instead of wrapping in PublicKey or BN objects reduces unnecessary object creation and improves performance.

js/compressed-token/src/actions/create-mint.ts (1)

87-90: Clean helper function implementation.

The getPublicKey helper properly handles all input cases and provides a clean abstraction for extracting public keys from either PublicKey or Signer types.

js/compressed-token/src/actions/mint-to.ts (2)

44-60: Well-structured parameter updates with sensible defaults.

The migration from merkleTree to outputStateTreeInfo and addition of tokenPoolInfo aligns with the protocol's enhanced state management. The dynamic selection logic provides good defaults while maintaining flexibility for users to specify their preferred state tree and token pool.


62-82: Clean instruction creation and transaction handling.

The instruction creation properly utilizes the new structured parameters, and the direct return of sendAndConfirmTx result simplifies the code while maintaining the same functionality.

js/compressed-token/src/actions/compress.ts (2)

51-60: Consistent parameter updates across actions.

The migration to outputStateTreeInfo and tokenPoolInfo parameters maintains consistency with other actions in the codebase. The dynamic selection logic provides sensible defaults.


77-79: Smart compute unit optimization.

The dynamic compute unit calculation (130,000 + 20,000 * array length) is more efficient than the fixed 1,000,000 units, allocating resources based on actual workload. This optimization reduces transaction costs for simple operations while ensuring sufficient compute for batch compressions.

js/compressed-token/package.json (1)

3-3: Appropriate version bump for breaking changes.

The version bump to 0.21.0 correctly indicates breaking changes with the new delegation features, multi-pool support, and API modifications.

js/compressed-token/src/actions/create-token-pool.ts (1)

17-52: LGTM! Good improvements to the API.

The changes improve the function interface:

  • Parameter rename from mintAddress to mint is more concise and consistent
  • Optional tokenProgramId parameter enables SPL Token 2022 support
  • Method rename to getMintProgramId follows JavaScript naming conventions
js/compressed-token/src/actions/approve-and-mint-to.ts (1)

27-100: Good refactoring to support the new multi-pool architecture.

The changes properly integrate with the new state tree and token pool selection system. The dynamic compute unit calculation (150,000 + toArray(amount).length * 20,000) is a smart improvement that scales with the complexity of the amount being minted.

js/compressed-token/tests/e2e/transfer-delegated.test.ts (1)

1-411: Excellent test coverage for delegated transfers!

The test suite is comprehensive and well-structured:

  • The assertPostTransfer helper provides thorough validation of post-transfer states
  • Good coverage of edge cases including partial transfers and error scenarios
  • Clear error logging for debugging test failures
  • Proper integration with the new state tree and token pool selection architecture
js/compressed-token/src/utils/select-input-accounts.ts (2)

9-54: Smart selection strategy for approval operations.

The function implements an optimal two-tier selection strategy:

  1. First attempts exact match (most efficient for approvals)
  2. Falls back to minimum selection if no exact match exists

This approach minimizes transaction complexity and avoids unnecessary change outputs when possible.


56-84: Good API wrapper for decompression use cases.

The function provides a cleaner object-based API for decompression operations while reusing the existing selection logic. This improves code readability at call sites through named destructuring.

js/compressed-token/tests/e2e/compress.test.ts (2)

118-131: stateTreeInfo / tokenPoolInfo selection done at test start – add existence guard

If the RPC returns an empty array the subsequent tests will throw cryptic errors. Guard early to make the failure obvious.

-        stateTreeInfo = selectStateTreeInfo(await rpc.getStateTreeInfos());
-        tokenPoolInfo = selectTokenPoolInfo(await getTokenPoolInfos(rpc, mint));
+        stateTreeInfo = selectStateTreeInfo(await rpc.getStateTreeInfos());
+        tokenPoolInfo = selectTokenPoolInfo(await getTokenPoolInfos(rpc, mint));
+
+        if (!stateTreeInfo || !tokenPoolInfo) {
+            throw new Error('No active StateTreeInfo / TokenPoolInfo found – check test RPC setup');
+        }

286-296: Hard-coded compute unit limit is too low for max-batch compress

Empirically, a 15-recipient compress frequently exceeds 500 k CUs on devnet. Consider raising the limit or deriving it from recipients.length.

-            [ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 }), ix],
+            [ComputeBudgetProgram.setComputeUnitLimit({ units: 800_000 }), ix],
js/stateless.js/src/utils/state-tree-lookup-table.ts (2)

46-49: Good improvement: Proper type casting and deduplication.

The replacement of @ts-expect-error with proper Rpc type casting and the addition of dedupeSigner are solid improvements that enhance type safety and prevent duplicate signers.

Also applies to: 119-122, 208-215


170-172: Correct approach for address comparison.

Using base58 string representations for address comparison is the proper approach to ensure reliable equality checks between PublicKey instances.

Also applies to: 191-195

js/stateless.js/src/actions/create-account.ts (2)

25-48: Well-structured parameter updates align with new abstractions.

The consolidation of addressTree and addressQueue into a single addressTreeInfo parameter and the addition of outputStateTreeInfo properly implements the new tree info abstractions for improved scalability.


142-142:

❓ Verification inconclusive

Verify removal of bn() wrapper is intentional.

The code removes the bn() wrapper when passing account hashes to getValidityProof. Ensure this change is compatible with the updated RPC method signature.


🏁 Script executed:

#!/bin/bash
# Description: Verify the RPC getValidityProof method signature accepts plain hashes without bn() wrapper

# Search for getValidityProof method definition and usage
ast-grep --pattern 'getValidityProof($$$)' -A 10

# Also check for any bn() usage patterns with account.hash
rg 'bn\(.*account.*hash\)' --type ts

Length of output: 1527


🏁 Script executed:

#!/bin/bash
# Locate all occurrences of getValidityProof in the stateless client
rg -n "getValidityProof" -n js/stateless.js/src

Length of output: 2145


🏁 Script executed:

#!/bin/bash
# Display the TypeScript signature for getValidityProof in the RPC interface
sed -n '770,820p' js/stateless.js/src/rpc-interface.ts
echo "-----"
# Show implementation and parameter formatting for getValidityProofV0 in rpc.ts
sed -n '1800,1850p' js/stateless.js/src/rpc.ts

Length of output: 3990


🏁 Script executed:

#!/bin/bash
# Display the context around inputAccounts in create-account.ts
sed -n '1,120p' js/stateless.js/src/actions/create-account.ts
echo "-----"
sed -n '121,200p' js/stateless.js/src/actions/create-account.ts

Length of output: 5720


🏁 Script executed:

#!/bin/bash
# Show definition and properties of CompressedAccountWithMerkleContext
rg -n "interface CompressedAccountWithMerkleContext" -n js/stateless.js/src/state/types.ts -A 20

Length of output: 98


🏁 Script executed:

#!/bin/bash
# Search for the CompressedAccountWithMerkleContext interface and confirm the type of `hash`
rg -n "interface CompressedAccountWithMerkleContext" -A20 .

Length of output: 61


Verify hash type before removing bn() wrapper

The createAccountWithLamports helper now does:

const proof = await rpc.getValidityProof(
  inputAccounts.map(account => account.hash),
  [bn(address.toBytes())],
);

However, getValidityProof is declared as:

getValidityProof(
  hashes: BN254[],      // expects BN instances
  newAddresses: BN254[],
): Promise<ValidityProofWithContext>;

Please confirm that CompressedAccountWithMerkleContext.hash is already a BN (alias BN254). If it’s not (e.g. a raw number, Buffer or string), you’ll need to re-introduce the bn(account.hash) wrapper. Also ensure the same pattern is applied consistently in transfer.ts so both actions align with the RPC signature.

js/stateless.js/src/test-helpers/test-rpc/get-compressed-token-accounts.ts (3)

70-77: Good addition of error handling for token data decoding.

The try-catch block properly handles potential decoding errors and provides helpful error logging.


175-177: Improved comparison using BN equality.

Using BN's eq() method is more efficient and type-safe than JSON stringification for comparing BigNumbers.


97-113:

✅ Verification successful

Complex tree validation logic - ensure V2 handling is correct.

The conditional logic for V2 trees adds complexity. The nested conditions check both tree and queue equality based on feature flags. Ensure this aligns with the expected behavior for V2 trees.


🏁 Script executed:

#!/bin/bash
# Description: Verify V2 tree handling logic is used consistently across the codebase

# Search for similar V2 tree validation patterns
rg 'featureFlags\.isV2\(\).*queue\.equals' --type ts -A 5 -B 5

# Check for TreeType.StateV2 usage
ast-grep --pattern 'TreeType.StateV2'

Length of output: 1320


V2 tree validation logic is correct

The condition correctly throws only when neither the Merkle tree nor (for V2) its queue matches—and for non-V2 it throws on tree mismatch alone. No other featureFlags.isV2() … queue.equals patterns exist, and this aligns with all TreeType.StateV2 checks in tests and helpers. No changes required.

js/compressed-token/src/idl.ts (2)

1092-1189: Well-structured delegation instruction types.

The new CompressedTokenInstructionDataRevoke and CompressedTokenInstructionDataApprove types properly support the delegation features mentioned in the changelog. The field structures are consistent with existing instruction data types.


1484-1494: Simplified PackedMerkleContext structure.

The changes to PackedMerkleContext appropriately simplify the structure:

  • Renaming nullifierQueuePubkeyIndex to queuePubkeyIndex makes it more generic
  • Replacing the optional queueIndex struct with a boolean proveByIndex flag reduces complexity
js/stateless.js/src/programs/system/pack.ts (2)

102-187: Well-structured tree info packing implementation

The function properly handles different tree versions with appropriate feature flag checks and clear error messages for unsupported configurations.


207-309: Robust compressed account packing with proper validation

The function correctly enforces mutual exclusivity between input accounts and output state tree info, and handles tree versioning appropriately.

js/stateless.js/src/programs/system/program.ts (1)

292-353: Correct implementation of mutual exclusivity for tree info

The conditional passing of outputStateTreeInfo only when there are no input compressed accounts is consistent with the validation logic in the packing utilities.

🧰 Tools
🪛 Biome (1.9.4)

[error] 302-302: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 349-349: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

js/stateless.js/src/constants.ts (2)

6-27: Well-designed versioning system

The VERSION enum and feature flags provide a clean and maintainable way to manage protocol versions. The versionedEndpoint helper is particularly useful for API compatibility.


254-267: Significant fee reduction in V2

The state tree rollover fee drops from 300 (V1) to 1 (V2), which is a 99.7% reduction. Please confirm this dramatic fee reduction is intentional.

js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts (2)

256-446: Comprehensive refactor supporting multiple tree versions

The method has been successfully refactored to support both V1 and V2 trees with appropriate proof generation strategies. The validation ensuring all requested hashes belong to the same tree type is a good safety check.


758-937: Proper handling of validity proofs for different tree versions

The method correctly differentiates between V1 accounts that require actual proofs and other versions that use null proofs with zero roots. The inclusion of treeInfos and proveByIndices in the response structure supports the new protocol requirements.

js/stateless.js/src/rpc-interface.ts (4)

285-296: Good use of the bn helper function.

The migration from new BN() to bn() helper improves consistency and properly handles Uint8Array inputs.


537-544: Good typo fix.

Fixed the typo from MerkeProofResult to MerkleProofResult.


155-190: Well-structured validity proof context type.

The new ValidityProofWithContext type properly encapsulates validity proof data with comprehensive context including tree infos and prove-by-indices flags.


779-791: Correct interface updates for validity proof methods.

The method signatures properly return ValidityProofWithContext instead of the deprecated CompressedProofWithContext.

js/stateless.js/src/state/compressed-account.ts (2)

25-97: Well-designed type evolution with proper documentation.

The new CompressedAccount and MerkleContext types provide better structure by using TreeInfo objects instead of raw PublicKey values, enabling richer context and multi-tree support.


117-198: Good addition of packed types for instruction data.

The new PackedStateTreeInfo, PackedAddressTreeInfo, and CompressedAccountMeta types properly support the V2 instruction formats with proof metadata.

js/stateless.js/src/state/types.ts (4)

7-24: Good enum design for tree versioning.

The updated TreeType enum properly distinguishes between V1 and V2 trees for both state and address trees, enabling protocol versioning.


58-99: Excellent TreeInfo type design with comprehensive documentation.

The TreeInfo type properly encapsulates tree metadata with support for CPI contexts and tree chaining via nextTreeInfo. The documentation with usage examples is particularly helpful.


388-410: Clean ValidityProof interface with proper deprecation.

Good replacement of CompressedProof with ValidityProof, maintaining the same structure while improving naming clarity.


341-368: Good camelCase consistency in CompressedCpiContext.

The interface properly uses camelCase for all fields, improving consistency with TypeScript conventions.

js/stateless.js/src/rpc.ts (5)

617-669: Well-implemented caching for state tree infos.

The caching mechanism properly handles:

  • TTL-based cache expiration (1 hour)
  • Concurrent request deduplication via fetchPromise
  • Proper error handling in the finally block

726-737: Consistent V2 support pattern.

Good implementation of conditional parsing based on feature flags, enabling smooth migration to V2 while maintaining backward compatibility.


482-483: Consistent use of bn() helper function.

The file properly uses the bn() helper throughout instead of direct BN instantiation.

Also applies to: 796-796, 822-822


535-564: Good addition of nullifier metadata handling for V2.

The buildCompressedAccountWithMaybeTokenDataFromClosedAccountResultV2 function properly extracts the additional nullifier metadata from V2 responses.


1878-1936: Comprehensive validity proof V2 handling.

The implementation properly transforms V2 responses with separate accounts and addresses arrays into the unified format expected by ValidityProofWithContext, while maintaining V1 backward compatibility.

js/compressed-token/src/layout.ts (3)

95-160: LGTM! Well-structured batch compress implementation

The new batchCompressLayout struct and its encoding/decoding functions are properly implemented with correct discriminator handling and length prefixing.


123-126: Good improvement: Consistent buffer handling

The updates to use Uint8Array wrappers and subarray instead of slice improve consistency and efficiency across all encoding/decoding functions.

Also applies to: 133-134, 175-177, 184-185, 206-211


289-310: Well-designed account layout functions

The new account layout functions for addTokenPool, approve, revoke, freeze, and thaw operations are properly structured with consistent patterns. The aliasing of revokeAccountsLayout to approveAccountsLayout and thawAccountsLayout to freezeAccountsLayout is appropriate since they share identical account requirements.

Also applies to: 426-464, 466-506

js/compressed-token/src/program.ts (5)

474-499: Well-implemented delegated transfer parsing

The parseMaybeDelegatedTransfer function correctly handles the detection and parsing of delegated transfers, with appropriate logic for determining the delegate change account index.


1179-1280: Excellent V2 feature flag implementation

The compress method properly handles both V1 and V2 paths with clear separation of logic. The V2 path correctly uses the batch compress instruction while maintaining backward compatibility through the V1 path.

🧰 Tools
🪛 Biome (1.9.4)

[error] 1180-1180: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1201-1201: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1207-1207: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1218-1218: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1264-1264: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1266-1266: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1275-1275: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


1399-1403: Good validation in mergeTokenAccounts

The method properly validates that all accounts belong to the same mint and enforces a reasonable limit of 4 accounts per merge operation.


901-903: Consistent V2 tree/queue selection

The code consistently uses featureFlags.isV2() to determine whether to use the queue (V2) or tree (V1) for state insertion. This architectural change is properly implemented throughout the codebase.

Also applies to: 1206-1206, 1445-1447


96-101: Excellent architectural improvement with structured types

The migration from simple PublicKey parameters to structured TreeInfo and TokenPoolInfo types significantly improves type safety and provides better context throughout the API. The consistent use of checkTokenPoolInfo for validation ensures data integrity.

Also applies to: 127-132, 162-163, 333-337, 408-413

js/stateless.js/src/index.ts (1)

1-10:

✅ Verification successful

Verify backward compatibility for removed exports

The removal of the './instruction' export and the named exports (LightSystemProgram and IDL) from './idl' could be breaking changes for consumers of this package. Based on the AI summary, these exports have been relocated to './programs'.


🏁 Script executed:

#!/bin/bash
# Description: Verify that the removed exports are still accessible through other paths

# Check if LightSystemProgram and IDL are exported from programs module
echo "Checking for LightSystemProgram and IDL exports in programs module..."
rg -A 5 "export.*LightSystemProgram|export.*IDL" --glob "js/stateless.js/src/programs/**/*.ts"

# Check if instruction exports have been moved to programs
echo "Checking for instruction-related exports in programs..."
fd -e ts . js/stateless.js/src/programs --exec grep -l "pack.*[Cc]ompressed.*[Aa]ccounts" {} \;

# Check for any remaining references to the old paths in the codebase
echo "Checking for imports from removed paths..."
rg "from.*['\"].*\/instruction['\"]|from.*['\"].*\/idl['\"]" --glob "**/*.ts" --glob "**/*.js" -g "!node_modules"

Length of output: 2545


🏁 Script executed:

#!/bin/bash
# Verify relocated instruction exports in programs modules
echo "Searching for any 'Instruction' exports in programs modules..."
rg -i "export.*Instruction" --glob "js/stateless.js/src/programs/**/*.ts"

# Check for leftover imports from the removed './instruction' path
echo "Checking for imports from removed 'instruction' path..."
rg "from .*\/instruction" --glob "**/*.ts" --glob "**/*.js" --glob "!node_modules/**"

# Check for leftover imports from the removed named './idl' path
echo "Checking for imports from removed 'idl' path..."
rg "from .*\/idl" --glob "**/*.ts" --glob "**/*.js" --glob "!node_modules/**"

Length of output: 1560


🏁 Script executed:

#!/bin/bash
# Check for any remaining imports from the removed './instruction' path
echo "Checking for imports from './instruction'..."
rg --glob '**/*.ts' --glob '**/*.js' --glob '!node_modules/**' -n "from './instruction'"
rg --glob '**/*.ts' --glob '**/*.js' --glob '!node_modules/**' -n 'from "./instruction"'

# Check for any remaining imports from the removed './idl' path
echo "Checking for imports from './idl'..."
rg --glob '**/*.ts' --glob '**/*.js' --glob '!node_modules/**' -n "from './idl'"
rg --glob '**/*.ts' --glob '**/*.js' --glob '!node_modules/**' -n 'from "./idl"'

Length of output: 732


Backward compatibility verified – no changes required

  • LightSystemProgram and IDL are correctly re-exported from js/stateless.js/src/programs/system/index.ts (and thus available under programs in the root index).
  • No remaining imports from ./instruction or top-level ./idl paths were found.
  • All previously exported entities remain reachable via their new module paths.
js/compressed-token/tests/e2e/create-token-pool.test.ts (3)

28-61: LGTM! Proper support for multiple token programs

The addition of the optional tokenProgramId parameter with a sensible default maintains backward compatibility while enabling Token 2022 support.


225-299: Well-structured test for multi-pool functionality

The test comprehensively validates the new multi-token-pool feature, including:

  • Idempotent pool creation
  • Proper pool indexing
  • Initialization state verification
  • Pool info retrieval

301-412: Consistent Token 2022 multi-pool test coverage

The test properly validates multi-pool functionality for Token 2022 mints, maintaining consistency with the standard token test while correctly using TOKEN_2022_PROGRAM_ID throughout.

js/compressed-token/src/types.ts (2)

1-61: Proper migration to shared types from stateless.js

The migration to use ValidityProof, PackedMerkleContextLegacy, and CompressedCpiContext from the shared @lightprotocol/stateless.js package improves code organization and reusability.


67-97: Well-structured new types for enhanced functionality

The new types properly support:

  • Batch compress operations with BatchCompressInstructionData
  • Delegation control with CompressedTokenInstructionDataApprove and CompressedTokenInstructionDataRevoke
  • Type guard utility isSingleTokenPoolInfo for clean type discrimination

Also applies to: 167-185

js/stateless.js/src/utils/get-state-tree-infos.ts (2)

5-13: Good backward compatibility with deprecation notice

The deprecated wrapper properly maintains backward compatibility while directing users to the new getTreeInfoByPubkey function.


84-110: Well-designed tree selection with concurrency control

The selectStateTreeInfo function properly implements:

  • Active tree filtering
  • Tree type filtering with sensible V2 default
  • Concurrency limiting via MAX_HOTSPOTS to reduce contention
  • Clear error messages
js/compressed-token/tests/e2e/layout.test.ts (3)

10-53: Good migration to shared constants and utilities

Using COMPRESSED_TOKEN_PROGRAM_ID from the shared package instead of hardcoding improves maintainability.


164-169: Test data properly updated for new field names

The field renames from nullifierQueuePubkeyIndex to queuePubkeyIndex and the change from queueIndex to proveByIndex correctly align with the PackedMerkleContextLegacy type structure.

Also applies to: 265-270


761-877: Comprehensive test coverage for token pool selection

The test thoroughly validates both selectTokenPoolInfo and selectTokenPoolInfosForDecompression:

  • Ensures uninitialized pools are never selected
  • Validates balance-based pool selection for decompression
  • Tests edge cases with exact balance thresholds

Comment on lines +45 to +56
const compressedTokenAccounts = await rpc.getCompressedTokenAccountsByOwner(
owner.publicKey,
{
mint,
},
);

const [inputAccounts] = selectTokenAccountsForApprove(
compressedTokenAccounts.items,
amount,
);

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

Gracefully handle insufficient or empty balances

selectTokenAccountsForApprove will throw if compressedTokenAccounts.items is empty or the requested amount exceeds the owner’s balance, but the error message originates deep inside the selector.
Consider an explicit guard for clarity:

if (compressedTokenAccounts.items.length === 0) {
    throw new Error(`Owner ${owner.publicKey.toBase58()} holds no compressed tokens for mint ${mint.toBase58()}`);
}
🤖 Prompt for AI Agents
In js/compressed-token/src/actions/approve.ts around lines 45 to 56, add an
explicit check before calling selectTokenAccountsForApprove to verify if
compressedTokenAccounts.items is empty. If it is, throw a clear error indicating
that the owner holds no compressed tokens for the specified mint. This prevents
the selector from throwing a less clear error and improves error handling
clarity.

Comment on lines +201 to +229
export function selectTokenPoolInfosForDecompression(
infos: TokenPoolInfo[],
decompressAmount: number | BN,
): TokenPoolInfo[] {
if (infos.length === 0) {
throw new Error('Please pass at least one token pool info.');
}

infos = shuffleArray(infos);
// Find the first info where balance is 10x the requested amount
const sufficientBalanceInfo = infos.find(info =>
info.balance.gte(bn(decompressAmount).mul(bn(10))),
);

// filter only infos that are initialized
infos = infos
.filter(info => info.isInitialized)
.sort((a, b) => a.poolIndex - b.poolIndex);

const allBalancesZero = infos.every(info => info.balance.isZero());
if (allBalancesZero) {
throw new Error(
'All provided token pool balances are zero. Please pass recent token pool infos.',
);
}

// If none found, return all infos
return sufficientBalanceInfo ? [sufficientBalanceInfo] : infos;
}
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

Avoid mutating the input array

The function modifies the input infos array on line 209, which could cause unexpected side effects for callers. Consider creating a copy before shuffling.

 export function selectTokenPoolInfosForDecompression(
     infos: TokenPoolInfo[],
     decompressAmount: number | BN,
 ): TokenPoolInfo[] {
     if (infos.length === 0) {
         throw new Error('Please pass at least one token pool info.');
     }

-    infos = shuffleArray(infos);
+    const shuffledInfos = shuffleArray([...infos]);
     // Find the first info where balance is 10x the requested amount
-    const sufficientBalanceInfo = infos.find(info =>
+    const sufficientBalanceInfo = shuffledInfos.find(info =>
         info.balance.gte(bn(decompressAmount).mul(bn(10))),
     );

     // filter only infos that are initialized
-    infos = infos
+    const filteredInfos = shuffledInfos
         .filter(info => info.isInitialized)
         .sort((a, b) => a.poolIndex - b.poolIndex);

-    const allBalancesZero = infos.every(info => info.balance.isZero());
+    const allBalancesZero = filteredInfos.every(info => info.balance.isZero());
     if (allBalancesZero) {
         throw new Error(
             'All provided token pool balances are zero. Please pass recent token pool infos.',
         );
     }

     // If none found, return all infos
-    return sufficientBalanceInfo ? [sufficientBalanceInfo] : infos;
+    return sufficientBalanceInfo ? [sufficientBalanceInfo] : filteredInfos;
 }
📝 Committable suggestion

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

Suggested change
export function selectTokenPoolInfosForDecompression(
infos: TokenPoolInfo[],
decompressAmount: number | BN,
): TokenPoolInfo[] {
if (infos.length === 0) {
throw new Error('Please pass at least one token pool info.');
}
infos = shuffleArray(infos);
// Find the first info where balance is 10x the requested amount
const sufficientBalanceInfo = infos.find(info =>
info.balance.gte(bn(decompressAmount).mul(bn(10))),
);
// filter only infos that are initialized
infos = infos
.filter(info => info.isInitialized)
.sort((a, b) => a.poolIndex - b.poolIndex);
const allBalancesZero = infos.every(info => info.balance.isZero());
if (allBalancesZero) {
throw new Error(
'All provided token pool balances are zero. Please pass recent token pool infos.',
);
}
// If none found, return all infos
return sufficientBalanceInfo ? [sufficientBalanceInfo] : infos;
}
export function selectTokenPoolInfosForDecompression(
infos: TokenPoolInfo[],
decompressAmount: number | BN,
): TokenPoolInfo[] {
if (infos.length === 0) {
throw new Error('Please pass at least one token pool info.');
}
// avoid mutating the caller’s array
const shuffledInfos = shuffleArray([...infos]);
// Find the first info where balance is 10x the requested amount
const sufficientBalanceInfo = shuffledInfos.find(info =>
info.balance.gte(bn(decompressAmount).mul(bn(10))),
);
// filter only infos that are initialized
const filteredInfos = shuffledInfos
.filter(info => info.isInitialized)
.sort((a, b) => a.poolIndex - b.poolIndex);
const allBalancesZero = filteredInfos.every(info => info.balance.isZero());
if (allBalancesZero) {
throw new Error(
'All provided token pool balances are zero. Please pass recent token pool infos.',
);
}
// If none found, return all initialized infos
return sufficientBalanceInfo ? [sufficientBalanceInfo] : filteredInfos;
}
🤖 Prompt for AI Agents
In js/compressed-token/src/utils/get-token-pool-infos.ts around lines 201 to
229, the input array 'infos' is directly mutated by shuffling it, which can
cause side effects for callers. To fix this, create a shallow copy of the
'infos' array before shuffling it, ensuring the original input remains unchanged
throughout the function.

Comment on lines +92 to +101
export interface AddressWithTreeInfo {
/**
* Address.
*/
address: BN254;
/**
* Address tree info.
*/
addressTreeInfo: AddressTreeInfo;
}
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

Fix duplicate AddressWithTreeInfo interface definitions.

There are two conflicting definitions of AddressWithTreeInfo:

  1. Lines 92-101 with addressTreeInfo: AddressTreeInfo
  2. Lines 115-118 with treeInfo: AddressTreeInfo

This will cause TypeScript compilation errors and type inconsistencies.

Consider renaming one of the interfaces or consolidating them:

-export interface AddressWithTreeInfo {
-    address: BN254;
-    treeInfo: AddressTreeInfo;
-}
+export interface AddressWithTreeInfoLegacy {
+    address: BN254;
+    treeInfo: AddressTreeInfo;
+}

Also applies to: 115-118

🤖 Prompt for AI Agents
In js/stateless.js/src/rpc-interface.ts around lines 92-101 and 115-118, there
are two conflicting interface definitions named AddressWithTreeInfo with
different property names (addressTreeInfo vs treeInfo). To fix this, consolidate
these two interfaces into one consistent definition by choosing a single
property name for the tree info field, or rename one of the interfaces to avoid
duplication and type conflicts. Ensure the final interface is uniquely named and
used consistently throughout the code.

Comment on lines +815 to +824
if (poolIndex <= 0) {
throw new Error(
'Pool index must be greater than 0. For 0, use CreateTokenPool instead.',
);
}
if (poolIndex > 3) {
throw new Error(
`Invalid poolIndex ${poolIndex}. Max 4 pools per mint.`,
);
}
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

Clarify token pool index bounds

There's a potential confusion in the validation logic. The error message states "Max 4 pools per mint" but the code allows indices 0-3, which is indeed 4 pools. However, the method doesn't allow index 0 (handled by createTokenPool), so effectively only 3 additional pools can be added.

Consider updating the error message for clarity:

-                `Invalid poolIndex ${poolIndex}. Max 4 pools per mint.`,
+                `Invalid poolIndex ${poolIndex}. Valid range is 1-3 (pool 0 is created via createTokenPool).`,
📝 Committable suggestion

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

Suggested change
if (poolIndex <= 0) {
throw new Error(
'Pool index must be greater than 0. For 0, use CreateTokenPool instead.',
);
}
if (poolIndex > 3) {
throw new Error(
`Invalid poolIndex ${poolIndex}. Max 4 pools per mint.`,
);
}
if (poolIndex <= 0) {
throw new Error(
'Pool index must be greater than 0. For 0, use CreateTokenPool instead.',
);
}
if (poolIndex > 3) {
throw new Error(
`Invalid poolIndex ${poolIndex}. Valid range is 1-3 (pool 0 is created via createTokenPool).`,
);
}
🤖 Prompt for AI Agents
In js/compressed-token/src/program.ts around lines 815 to 824, the error message
for poolIndex exceeding the allowed range is misleading because it states "Max 4
pools per mint" while index 0 is disallowed here, effectively allowing only
indices 1 to 3. Update the error message to clarify that only 3 additional pools
(indices 1 to 3) are allowed beyond the initial pool at index 0, to avoid
confusion.

Comment on lines 207 to 209
'Dk9mNkbiZXJZ4By8DfSP6HEE4ojZzRvucwpawLeuwq8q'; // '8n8rH2bFRVA6cSGNDpgqcKHCndbFCT1bXxAQG89ejVsh';
export const nullifiedStateTreeLookupTableDevnet =
'5dhaJLBjnVBQFErr8oiCJmcVsx3Zj6xDekGB2zULPsnP';
'AXbHzp1NgjLvpfnD6JRTTovXZ7APUCdtWZFCRr5tCxse'; // '5dhaJLBjnVBQFErr8oiCJmcVsx3Zj6xDekGB2zULPsnP';
Copy link
Contributor

Choose a reason for hiding this comment

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

leftover?

import { hashToBn254FieldSizeBe, hashvToBn254FieldSizeBe } from './conversion';
import { defaultTestStateTreeAccounts } from '../constants';
import { getIndexOrAdd } from '../instruction';
import { getIndexOrAdd } from '../programs/system/pack';
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we support V2 address derivation yet?
(Probably not necessary for this pr.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch will add in new pr

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

♻️ Duplicate comments (2)
js/compressed-token/src/program.ts (2)

817-826: Error message still misleading about valid pool range
Same concern as raised previously: message says “Max 4 pools per mint” but indices 1-3 are the only valid additions here.
Consider clarifying to “Valid range is 1–3 (pool 0 is created via createTokenPool).”


1540-1545: Hard-coded CHANGE_INDEX TODO persists

The index is still derived from a ternary instead of being computed from actual remaining-account packing logic; earlier review already flagged this.

🧹 Nitpick comments (4)
js/stateless.js/src/constants.ts (2)

63-77: Avoid double-wrapping PublicKey returned from helper

getRegisteredProgramPda() already returns a PublicKey; wrapping it again with new PublicKey(...) is redundant and allocates an extra object.

-export const defaultStaticAccounts = () => [
-    new PublicKey(getRegisteredProgramPda()),
+export const defaultStaticAccounts = () => [
+    getRegisteredProgramPda(),

Same pattern occurs in defaultStaticAccountsStruct.


265-267: Redundant ternary – same value both branches

The ternary expression always returns new BN(392).

-export const ADDRESS_QUEUE_ROLLOVER_FEE = featureFlags.isV2()
-    ? new BN(392)
-    : new BN(392);
+export const ADDRESS_QUEUE_ROLLOVER_FEE = new BN(392);
js/stateless.js/src/rpc.ts (1)

111-145: Duplicated validator selection pattern

Several places repeat:

let res;
if (featureFlags.isV2()) {
  res = create(unsafeRes, jsonRpcResultAndContext(FooV2));
} else {
  res = create(unsafeRes, jsonRpcResultAndContext(Foo));
}

Consider a helper:

function versionedStruct<T, U>(raw: any, v1: Struct<T>, v2: Struct<U>) {
  return create(raw, jsonRpcResultAndContext(featureFlags.isV2() ? v2 : v1));
}

to DRY up ~20 such blocks.

js/compressed-token/src/program.ts (1)

678-688: Hard-coded seed string differs from canonical constant

deriveTokenPoolPdaWithIndex re-creates the seed with Buffer.from('pool') instead of re-using the already-exported POOL_SEED.
Besides stylistic inconsistency, a future change to the seed would silently break this path.

-            seeds = [Buffer.from('pool'), mint.toBuffer(), Buffer.from([])];
+            seeds = [POOL_SEED, mint.toBuffer(), Buffer.from([])];

Apply the same replacement to the else branch.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 808071c and 5fd074b.

📒 Files selected for processing (9)
  • js/compressed-token/src/actions/create-mint.ts (2 hunks)
  • js/compressed-token/src/program.ts (26 hunks)
  • js/compressed-token/src/types.ts (5 hunks)
  • js/stateless.js/src/actions/transfer.ts (4 hunks)
  • js/stateless.js/src/constants.ts (10 hunks)
  • js/stateless.js/src/rpc.ts (32 hunks)
  • js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts (13 hunks)
  • js/stateless.js/src/utils/conversion.ts (3 hunks)
  • js/stateless.js/src/utils/index.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • js/stateless.js/src/utils/index.ts
  • js/compressed-token/src/actions/create-mint.ts
  • js/stateless.js/src/actions/transfer.ts
  • js/compressed-token/src/types.ts
  • js/stateless.js/src/utils/conversion.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
js/stateless.js/src/constants.ts (1)
js/stateless.js/src/state/types.ts (1)
  • TreeInfo (77-99)
js/compressed-token/src/program.ts (10)
js/stateless.js/src/state/types.ts (3)
  • TreeInfo (77-99)
  • ValidityProof (397-410)
  • CompressedProof (373-386)
js/compressed-token/src/utils/get-token-pool-infos.ts (2)
  • TokenPoolInfo (104-141)
  • checkTokenPoolInfo (13-27)
js/stateless.js/src/rpc-interface.ts (1)
  • ParsedTokenAccount (236-239)
js/compressed-token/src/types.ts (6)
  • TokenTransferOutputData (11-28)
  • DelegatedTransfer (62-65)
  • CompressedTokenInstructionDataTransfer (94-132)
  • BatchCompressInstructionData (67-74)
  • CompressedTokenInstructionDataApprove (162-172)
  • CompressedTokenInstructionDataRevoke (174-180)
js/compressed-token/src/layout.ts (8)
  • createTokenPoolAccountsLayout (268-287)
  • addTokenPoolAccountsLayout (289-310)
  • mintToAccountsLayout (312-365)
  • encodeTransferInstructionData (193-213)
  • encodeApproveInstructionData (549-574)
  • approveAccountsLayout (426-462)
  • encodeRevokeInstructionData (588-613)
  • revokeAccountsLayout (464-464)
js/stateless.js/src/constants.ts (4)
  • defaultStaticAccountsStruct (79-89)
  • featureFlags (16-19)
  • noopProgram (58-58)
  • accountCompressionProgram (60-61)
js/stateless.js/src/utils/conversion.ts (1)
  • toArray (28-29)
js/stateless.js/src/programs/system/program.ts (1)
  • LightSystemProgram (171-537)
js/compressed-token/src/utils/pack-compressed-token-accounts.ts (1)
  • packCompressedTokenAccounts (39-159)
js/compressed-token/src/utils/validation.ts (1)
  • checkMint (11-24)
🪛 Biome (1.9.4)
js/compressed-token/src/program.ts

[error] 690-690: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 781-781: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 830-830: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 834-834: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 842-842: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 847-847: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 907-907: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1106-1106: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1183-1183: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1204-1204: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1210-1210: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1221-1221: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1267-1267: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1269-1269: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1278-1278: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1360-1360: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1366-1366: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1408-1408: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1570-1570: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1576-1576: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1583-1583: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1640-1640: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1646-1646: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 1653-1653: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

⏰ Context from checks skipped due to timeout of 90000ms (22)
  • GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test"])
  • GitHub Check: system-programs (token-escrow-test, [ "cargo test-sbf -p token-escrow" ])
  • GitHub Check: system-programs (counter-test, ["cargo test-sbf -p counter"])
  • GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-test"])
  • GitHub Check: Test sdk-libs
  • GitHub Check: Test program-libs
  • GitHub Check: Test concurrent-merkle-tree
  • GitHub Check: stateless-js
  • GitHub Check: test-double-registration
  • GitHub Check: test-state-photon-batched
  • GitHub Check: test-address-batched
  • GitHub Check: test-state-batched
  • GitHub Check: test-e2e
  • GitHub Check: test-2-foresters
  • GitHub Check: lint
  • GitHub Check: system-programs (system-cpi-test-v2, ["cargo-test-sbf -p system-cpi-v2-test"])
  • GitHub Check: system-programs (light-compressed-token, ["cargo-test-sbf -p compressed-token-test"])
  • GitHub Check: system-programs (system-cpi-test, ["cargo-test-sbf -p system-cpi-test"])
  • GitHub Check: system-programs (account-compression, ["cargo-test-sbf -p account-compression-test"])
  • GitHub Check: system-programs (light-registry, ["cargo-test-sbf -p registry-test"])
  • GitHub Check: system-programs (random-e2e-test, ["cargo-test-sbf -p e2e-test"])
  • GitHub Check: system-programs (light-system-program, ["cargo-test-sbf -p system-test"])
🔇 Additional comments (3)
js/stateless.js/src/constants.ts (1)

254-257: Drastic fee change – confirm economic impact

STATE_MERKLE_TREE_ROLLOVER_FEE drops from 300 to 1 lamport under V2.
Please confirm this isn’t accidental; a 300× reduction will materially alter funding assumptions.

js/compressed-token/src/program.ts (2)

688-907: Replace this with class name in static context (Bi-ome warnings)

Multiple static methods still reference this.programId / this.deriveTokenPoolPda… which Biome rightfully flags as confusing.
Using the explicit class name (CompressedTokenProgram) makes the call-site unambiguous and prevents subtle bugs once the class is subclassed or its static context is rebound.

A blanket find/replace is safe:

- PublicKey.findProgramAddressSync(seeds, this.programId)
+ PublicKey.findProgramAddressSync(seeds, CompressedTokenProgram.programId)

…same for every this. occurrence inside static methods.
[ suggest_essential_refactor ]

Also applies to: 1104-1111, 1180-1211, 1260-1278, 1357-1366, 1570-1583, 1640-1655

🧰 Tools
🪛 Biome (1.9.4)

[error] 690-690: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 699-699: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 750-750: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 781-781: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 788-788: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 793-793: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 830-830: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 834-834: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 842-842: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 847-847: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 895-895: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 907-907: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


475-500: Potentially wrong delegateChangeAccountIndex calculation

parseMaybeDelegatedTransfer unconditionally sets delegateChangeAccountIndex = 0 whenever there is more than one output.
If the first output is the “change” back to the owner (as is the case for normal transfers), index 0 would point to the owner’s side not to the delegate’s change account, breaking proof reconstruction.

Please double-check the expected ordering; it might need to be 1 (or computed dynamically).

Comment on lines +152 to +156
tree: new PublicKey(batchMerkleTree),
queue: new PublicKey(batchQueue),
cpiContext: PublicKey.default,
treeType: TreeType.StateV2,
nextTreeInfo: null,
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

Potential misuse of PublicKey.default

PublicKey.default is not part of the documented @solana/web3.js API (latest 1.104.x). Relying on a non-public static may break on library upgrades.
Consider PublicKey.defaultPublicKey (if available) or new PublicKey(0) instead.

🤖 Prompt for AI Agents
In js/stateless.js/src/constants.ts around lines 152 to 156, the code uses
PublicKey.default which is not part of the official @solana/web3.js API and may
break on upgrades. Replace PublicKey.default with a supported alternative such
as PublicKey.defaultPublicKey if it exists, or create a new PublicKey instance
with a zeroed value using new PublicKey(0) to ensure compatibility and
stability.

Comment on lines +364 to +383
} else if (treeInfo.treeType === TreeType.StateV2) {
const pathElements = merkleTree._zeros.slice(0, -1);
const bnPathElements = pathElements.map(value =>
bn(value),
);
const root = bn(merkleTree.root());

/// get leafIndex from leavesByTree for the given hash
const leafIndex = leavesByTree
.get(tree.toBase58())!
.leafIndices.findIndex(index =>
hashes[i].eq(
bn(
leavesByTree.get(tree.toBase58())!
.leaves[index],
),
),
);

const merkleProof: MerkleContextWithMerkleProof = {
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

Incorrect V2 leafIndex calculation

findIndex iterates over indices, but you compare the hash again inside:

const leafIndex = leavesByTree.get(tree.toBase58())!
    .leafIndices.findIndex(index =>
        hashes[i].eq(
            bn(leavesByTree.get(tree.toBase58())!.leaves[index]),
        ),
    );

index is already the wanted leafIndex; dereferencing leaves[index] mixes two arrays and may return ‑1 for valid leaves.

-const leafIndex = leavesByTree.get(tree.toBase58())!
-    .leafIndices.findIndex(index =>
-        hashes[i].eq(bn(leavesByTree.get(tree.toBase58())!.leaves[index])),
-    );
+const leafIndex = leavesByTree
+    .get(tree.toBase58())!
+    .leafIndices.find(idx => hashes[i].eq(bn(leaves[idx])));
🤖 Prompt for AI Agents
In js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts around lines 364 to
383, the leafIndex calculation incorrectly uses findIndex on leafIndices and
then dereferences leaves with that index, mixing two arrays. To fix this,
directly find the index in the leaves array that matches the hash without using
leafIndices, or adjust the logic to correctly map leafIndices to leaves. Ensure
the comparison is done on the correct array elements so leafIndex reflects the
actual position of the hash in the leaves array.

Comment on lines +321 to 333
merkleTree = new MerkleTree(
this.depth,
this.lightWasm,
leaves.map(leaf => bn(leaf).toString()),
);
} else if (treeInfo.treeType === TreeType.StateV2) {
/// In V2 State trees, The Merkle tree stays empty until the
/// first forester transaction. And since test-rpc is only used
/// for non-forested tests, we must return a tree with
/// zerovalues.
merkleTree = new MerkleTree(32, this.lightWasm, []);
} else {
throw new Error(
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

Leaking implementation detail: relying on private _zeros

merkleTree._zeros is a private field. Accessing it will break when the MerkleTree implementation changes or when TypeScript’s #private fields are enabled.

Replace with the library’s public helper (e.g., merkleTree.getZeros(depth) or construct the zero path manually).

🤖 Prompt for AI Agents
In js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts around lines 321 to
333, the code accesses the private field merkleTree._zeros, which is an
implementation detail and may break with future changes or TypeScript private
fields. To fix this, replace any usage of merkleTree._zeros with a public method
provided by the MerkleTree class, such as merkleTree.getZeros(depth), or
manually construct the zero path using public APIs. Avoid direct access to
private fields to ensure compatibility and maintainability.

Comment on lines 753 to 760
activeStateTreeInfo,
res.result.value.tree!,
tree,
);
const item = res.result.value;
const account = createCompressedAccountWithMerkleContext(
createMerkleContext(
item.tree!,
associatedQueue,
item.hash.toArray('be', 32),
item.leafIndex,
),

return createCompressedAccountWithMerkleContextLegacy(
createMerkleContextLegacy(stateTreeInfo, item.hash, item.leafIndex),
item.owner,
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

proveByIndex flag dropped for V2 accounts

When featureFlags.isV2() is true, the RPC returns a proveByIndex boolean, but the context here ignores it:

createMerkleContextLegacy(stateTreeInfo, item.hash, item.leafIndex)
// should forward proveByIndex
-createMerkleContextLegacy(stateTreeInfo, item.hash, item.leafIndex),
+createMerkleContextLegacy(
+    stateTreeInfo,
+    item.hash,
+    item.leafIndex,
+    featureFlags.isV2() ? item.proveByIndex : false,
+),

Down-stream verification will fail for proofs obtained via index.

🤖 Prompt for AI Agents
In js/stateless.js/src/rpc.ts around lines 753 to 760, the call to
createMerkleContextLegacy does not forward the proveByIndex flag returned by the
RPC when featureFlags.isV2() is true. Modify the code to extract proveByIndex
from the RPC result and pass it as an additional argument to
createMerkleContextLegacy to ensure correct proof verification downstream.

Comment on lines +578 to +592
const compressedAccount: CompressedAccountWithMerkleContext =
createCompressedAccountWithMerkleContextLegacy(
createMerkleContextLegacy(
compressedAccountResult.treeInfo,
compressedAccountResult.hash.toArray('be', 32),
compressedAccountResult.leafIndex,
compressedAccountResult.proveByIndex,
),
compressedAccountResult.owner,
bn(compressedAccountResult.lamports),
compressedAccountResult.data
? parseAccountData(compressedAccountResult.data)
: undefined,
compressedAccountResult.address || undefined,
);
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

Missing bn() conversion – hash fed as byte array

createMerkleContextLegacy expects a BN, but compressedAccountResult.hash.toArray() supplies a number[].

-                compressedAccountResult.hash.toArray('be', 32),
+                bn(compressedAccountResult.hash.toArray('be', 32)),

Without the cast the hash becomes an unintended huge integer.

📝 Committable suggestion

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

Suggested change
const compressedAccount: CompressedAccountWithMerkleContext =
createCompressedAccountWithMerkleContextLegacy(
createMerkleContextLegacy(
compressedAccountResult.treeInfo,
compressedAccountResult.hash.toArray('be', 32),
compressedAccountResult.leafIndex,
compressedAccountResult.proveByIndex,
),
compressedAccountResult.owner,
bn(compressedAccountResult.lamports),
compressedAccountResult.data
? parseAccountData(compressedAccountResult.data)
: undefined,
compressedAccountResult.address || undefined,
);
const compressedAccount: CompressedAccountWithMerkleContext =
createCompressedAccountWithMerkleContextLegacy(
createMerkleContextLegacy(
compressedAccountResult.treeInfo,
- compressedAccountResult.hash.toArray('be', 32),
+ bn(compressedAccountResult.hash.toArray('be', 32)),
compressedAccountResult.leafIndex,
compressedAccountResult.proveByIndex,
),
compressedAccountResult.owner,
bn(compressedAccountResult.lamports),
compressedAccountResult.data
? parseAccountData(compressedAccountResult.data)
: undefined,
compressedAccountResult.address || undefined,
);
🤖 Prompt for AI Agents
In js/stateless.js/src/rpc.ts around lines 578 to 592, the hash passed to
createMerkleContextLegacy is currently converted to a byte array using toArray,
but the function expects a BN instance. To fix this, convert
compressedAccountResult.hash to a BN before passing it to
createMerkleContextLegacy instead of passing the byte array, ensuring the hash
is correctly interpreted as a big number.

Comment on lines +655 to +663
for (let index = 0; index < 5; index++) {
const derivedPda =
CompressedTokenProgram.deriveTokenPoolPdaWithIndex(mint, index);
if (derivedPda[0].equals(poolPda)) {
return [index, derivedPda[1]];
}
}
throw new Error('Token pool not found');
}
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 12, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Inconsistent pool-count limits – off-by-one bug

findTokenPoolIndexAndBump iterates over indices 0…4 (< 5) while both the JSDoc above (supports 4 indexes) and the validation in addTokenPool reject any index > 3.
This desynchronisation risks returning an index (4) that other branches will later treat as invalid, leading to hard-to-trace runtime errors.

-        for (let index = 0; index < 5; index++) {
+        // index range must stay in sync with addTokenPool() validation (0–3)
+        for (let index = 0; index < 4; index++) {

Synchronise all three places (deriveTokenPoolPdaWithIndex doc, findTokenPoolIndexAndBump, addTokenPool guard) to avoid undefined behaviour.

📝 Committable suggestion

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

Suggested change
for (let index = 0; index < 5; index++) {
const derivedPda =
CompressedTokenProgram.deriveTokenPoolPdaWithIndex(mint, index);
if (derivedPda[0].equals(poolPda)) {
return [index, derivedPda[1]];
}
}
throw new Error('Token pool not found');
}
// index range must stay in sync with addTokenPool() validation (0–3)
for (let index = 0; index < 4; index++) {
const derivedPda =
CompressedTokenProgram.deriveTokenPoolPdaWithIndex(mint, index);
if (derivedPda[0].equals(poolPda)) {
return [index, derivedPda[1]];
}
}
throw new Error('Token pool not found');
}
🤖 Prompt for AI Agents
In js/compressed-token/src/program.ts around lines 655 to 663, the loop in
findTokenPoolIndexAndBump iterates from 0 to 4 inclusive, but the JSDoc and
addTokenPool validation only allow indices up to 3. To fix this off-by-one
error, change the loop condition to iterate only up to 3 (i.e., index < 4) so it
matches the documented and validated index range, ensuring consistent pool-count
limits across the code.

Copy link
Contributor

Choose a reason for hiding this comment

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

@SwenSchaeferjohann this one is probably valid

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we can ignore - TS has a guard that limits to 4 for now but the protocol supports up to 5

Copy link
Contributor

@ananas-block ananas-block left a comment

Choose a reason for hiding this comment

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

nice lgtm!
There is at least one new ai comment which is probably valid tagged you.

@SwenSchaeferjohann SwenSchaeferjohann merged commit 4dae0f2 into main Jun 14, 2025
32 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Nov 10, 2025
@coderabbitai coderabbitai bot mentioned this pull request Nov 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants