-
Notifications
You must be signed in to change notification settings - Fork 79
feat: add ts program test #1975
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds a new workspace package Changes
Sequence Diagram(s)%%{init: {"themeVariables": {"actorBkg":"#E6F4EA","actorBorder":"#2D6A4F","noteBkg":"#FFF3BF"}}}%%
sequenceDiagram
autonumber
actor Dev
participant CI as GitHub Actions
participant WS as pnpm Workspace
participant PT as @lightprotocol/program-test
participant CT as js/compressed-token
Dev->>CI: push branch
CI->>WS: install workspace (includes js/program-test)
CI->>PT: build (pnpm run build)
CI->>PT: run tests (pnpm test)
CI->>CT: run `pnpm test:program-test` then e2e
CI-->>Dev: report status
%%{init: {"themeVariables": {"actorBkg":"#F0F7FF","actorBorder":"#0B5FFF","noteBkg":"#F8F9FA"}}}%%
sequenceDiagram
autonumber
participant Test as Test Suite
participant Hasher as NobleHasherFactory
participant Lite as createLiteSVMRpc
participant SPL as spl-token-utils
participant Prog as CompressedTokenProgram
Test->>Hasher: NobleHasherFactory.getInstance()
Test->>Lite: createLiteSVMRpc(lightWasm)
Test->>SPL: splCreateMint / splCreateAssociatedTokenAccount
Test->>SPL: splMintTo
Test->>Prog: invoke compress/mintTo/transfer actions
Prog-->>Lite: emit events & update state
Test->>Lite: query compressed accounts / proofs / balances
Lite-->>Test: return results
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (1)
🧰 Additional context used🧬 Code graph analysis (5)js/compressed-token/tests/program-test/decompress-delegated.test.ts (4)
js/compressed-token/tests/program-test/compress.test.ts (8)
js/program-test/src/hasher/noble-hasher.ts (3)
js/program-test/tests/poseidon-comparison.test.ts (3)
js/compressed-token/tests/program-test/compress-spl-token-account.test.ts (4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
🔇 Additional comments (15)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
c9b431d
to
61e7512
Compare
7c0c42d
to
52ee14d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
🧹 Nitpick comments (8)
js/stateless.js/src/utils/send-and-confirm.ts (1)
109-113
: Consider removing or replacing debug console.log.This debug statement logs transaction status on every polling iteration. While useful during development, production code should either remove this or replace it with a proper logging framework that can be configured/disabled.
If this utility is only used in tests, consider adding a comment clarifying that. Otherwise, apply this diff to remove the debug log:
- console.log('[confirmTx] Comparing:', { - statusValue, - expectedCommitment: commitment, - meetsCommitment, - }); - if (meetsCommitment) {js/program-test/tests/compress.test.ts (1)
24-24
: Consider removing or controlling console.log statements in production tests.The test includes multiple console.log statements for debugging. While useful during development, consider either removing them or using a test logger/debug flag to control output verbosity in CI environments.
Apply this diff to remove console output:
- console.log("Pre-compress balance:", preBalance); + // Pre-compress balance tracked for assertion- console.log("Compress signature:", signature);
- console.log("Post-compress balance:", postBalance);
- console.log("Compressed accounts:", compressedAccounts);
- console.log("Compressed balance:", compressedBalance.toString());
Also applies to: 33-33, 37-37, 43-43, 52-52
js/program-test/tests/test-rpc.test.ts (3)
64-71
: Avoid magic numbers; use network fee constantReplace literal 5000s with STATE_MERKLE_TREE_NETWORK_FEE to avoid drift if constants change.
- postCompressBalance, - preCompressBalance - - compressLamportsAmount - - 5000 - - 5000 - - STATE_MERKLE_TREE_ROLLOVER_FEE.toNumber(), + postCompressBalance, + preCompressBalance - + compressLamportsAmount - + 2 * STATE_MERKLE_TREE_NETWORK_FEE.toNumber() - + STATE_MERKLE_TREE_ROLLOVER_FEE.toNumber(),
75-75
: Remove unused variable
slot
is never used.- const slot = await rpc.getSlot(); + // const slot = await rpc.getSlot();
92-97
: Prefer BN equality check over structural equalityUse
.eq()
to avoid brittleness across BN object instances.- expect(compressedAccountProof.hash).toStrictEqual(refHash); - expect(compressedAccountProof.leafIndex).toStrictEqual( - compressedAccounts.items[0].leafIndex, - ); + expect(bn(compressedAccountProof.hash).eq(bn(refHash))).toBe(true); + expect(compressedAccountProof.leafIndex) + .toStrictEqual(compressedAccounts.items[0].leafIndex);sdk-tests/sdk-anchor-test/tests/test_v1.ts (1)
181-185
: Use getLatestBlockhash for consistency with LiteSVM RPCAligns with other tests and typical web3.js usage.
- tx.recentBlockhash = (await rpc.getRecentBlockhash()).blockhash; + { + const { blockhash } = await rpc.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + }If LiteSVMRpc supports both, fine; otherwise this avoids runtime mismatches.
Also applies to: 257-259, 328-331
js/program-test/src/test-rpc/get-compressed-accounts.ts (1)
41-96
: Optional: speed up unspent filtering with a SetCurrent O(n·m). Convert input hashes to a Set of strings first.
- const allInputAccountHashes: BN[] = []; + const allInputAccountHashes: BN[] = []; @@ - const unspentAccounts = allOutputAccounts.filter( - (account) => - !allInputAccountHashes.some((hash) => hash.eq(bn(account.hash))), - ); + const spentSet = new Set(allInputAccountHashes.map(h => h.toString(10))); + const unspentAccounts = allOutputAccounts.filter( + (account) => !spentSet.has(bn(account.hash).toString(10)), + );js/compressed-token/tests/program-test/compress.test.ts (1)
232-242
: Remove redundant loop; assert onceThe loop repeats the same assertion 15 times. Call assertCompress once.
- for (let i = 0; i < recipients.length; i++) { - await assertCompress( - rpc, - bn(senderAtaBalanceBefore.value.amount), - bobAta, - mint, - amounts.slice(0, 11), - recipients.slice(0, 11), - recipientCompressedTokenBalancesBefore.map(x => x.items), - ); - } + await assertCompress( + rpc, + bn(senderAtaBalanceBefore.value.amount), + bobAta, + mint, + amounts.slice(0, 11), + recipients.slice(0, 11), + recipientCompressedTokenBalancesBefore.map(x => x.items), + );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (65)
.github/workflows/js-v2.yml
(2 hunks).github/workflows/js.yml
(2 hunks)js/compressed-token/package.json
(3 hunks)js/compressed-token/tests/e2e/compress.test.ts
(1 hunks)js/compressed-token/tests/e2e/mint-to.test.ts
(2 hunks)js/compressed-token/tests/program-test/approve-and-mint-to.test.ts
(4 hunks)js/compressed-token/tests/program-test/compress-spl-token-account.test.ts
(8 hunks)js/compressed-token/tests/program-test/compress.test.ts
(1 hunks)js/compressed-token/tests/program-test/create-mint.test.ts
(2 hunks)js/compressed-token/tests/program-test/create-token-pool.test.ts
(2 hunks)js/compressed-token/tests/program-test/decompress-delegated.test.ts
(3 hunks)js/compressed-token/tests/program-test/decompress.test.ts
(3 hunks)js/compressed-token/tests/program-test/delegate.test.ts
(2 hunks)js/compressed-token/tests/program-test/mint-to.test.ts
(1 hunks)js/compressed-token/tests/program-test/multi-pool.test.ts
(2 hunks)js/compressed-token/tests/program-test/transfer-delegated.test.ts
(2 hunks)js/compressed-token/tests/program-test/transfer.test.ts
(4 hunks)js/program-test/.prettierignore
(1 hunks)js/program-test/README.md
(1 hunks)js/program-test/package.json
(1 hunks)js/program-test/rollup.config.js
(1 hunks)js/program-test/src/index.ts
(1 hunks)js/program-test/src/litesvm-rpc.ts
(1 hunks)js/program-test/src/merkle-tree/index.ts
(1 hunks)js/program-test/src/merkle-tree/indexed-array.ts
(1 hunks)js/program-test/src/merkle-tree/merkle-tree.ts
(1 hunks)js/program-test/src/spl-token-utils.ts
(1 hunks)js/program-test/src/test-rpc/get-compressed-accounts.ts
(1 hunks)js/program-test/src/test-rpc/get-compressed-token-accounts.ts
(1 hunks)js/program-test/src/test-rpc/get-parsed-events.ts
(1 hunks)js/program-test/src/test-rpc/index.ts
(1 hunks)js/program-test/src/test-rpc/test-rpc.ts
(1 hunks)js/program-test/src/test-utils.ts
(1 hunks)js/program-test/src/types.ts
(1 hunks)js/program-test/tests/compress.test.ts
(1 hunks)js/program-test/tests/merkle-tree.test.ts
(1 hunks)js/program-test/tests/rpc-interop.test.ts
(1 hunks)js/program-test/tests/rpc-multi-trees.test.ts
(1 hunks)js/program-test/tests/test-rpc.test.ts
(1 hunks)js/program-test/tests/transfer.test.ts
(1 hunks)js/program-test/tsconfig.json
(1 hunks)js/program-test/vitest.config.ts
(1 hunks)js/stateless.js/package.json
(2 hunks)js/stateless.js/rollup.config.js
(2 hunks)js/stateless.js/src/rpc.ts
(0 hunks)js/stateless.js/src/test-helpers/index.ts
(0 hunks)js/stateless.js/src/test-helpers/merkle-tree/index.ts
(0 hunks)js/stateless.js/src/test-helpers/merkle-tree/indexed-array.ts
(0 hunks)js/stateless.js/src/test-helpers/merkle-tree/merkle-tree.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/get-compressed-accounts.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/get-compressed-token-accounts.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/get-parsed-events.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/index.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts
(0 hunks)js/stateless.js/src/utils/send-and-confirm.ts
(1 hunks)js/stateless.js/tests/e2e/compress.test.ts
(0 hunks)js/stateless.js/tests/e2e/rpc-interop.test.ts
(0 hunks)js/stateless.js/tests/e2e/rpc-multi-trees.test.ts
(0 hunks)js/stateless.js/tests/e2e/test-rpc.test.ts
(0 hunks)js/stateless.js/tests/e2e/transfer.test.ts
(0 hunks)js/stateless.js/tests/unit/merkle-tree/merkle-tree.test.ts
(0 hunks)pnpm-workspace.yaml
(1 hunks)sdk-tests/sdk-anchor-test/package.json
(1 hunks)sdk-tests/sdk-anchor-test/tests/test_v1.ts
(4 hunks)sdk-tests/sdk-anchor-test/tests/test_v2.ts
(5 hunks)
💤 Files with no reviewable changes (16)
- js/stateless.js/src/test-helpers/index.ts
- js/stateless.js/tests/e2e/rpc-multi-trees.test.ts
- js/stateless.js/src/test-helpers/merkle-tree/index.ts
- js/stateless.js/src/test-helpers/test-rpc/get-compressed-token-accounts.ts
- js/stateless.js/src/test-helpers/test-rpc/index.ts
- js/stateless.js/tests/e2e/transfer.test.ts
- js/stateless.js/src/test-helpers/test-rpc/get-parsed-events.ts
- js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts
- js/stateless.js/tests/e2e/test-rpc.test.ts
- js/stateless.js/src/test-helpers/merkle-tree/indexed-array.ts
- js/stateless.js/src/test-helpers/merkle-tree/merkle-tree.ts
- js/stateless.js/tests/e2e/compress.test.ts
- js/stateless.js/tests/unit/merkle-tree/merkle-tree.test.ts
- js/stateless.js/src/test-helpers/test-rpc/get-compressed-accounts.ts
- js/stateless.js/tests/e2e/rpc-interop.test.ts
- js/stateless.js/src/rpc.ts
🧰 Additional context used
🧬 Code graph analysis (29)
js/program-test/src/types.ts (1)
js/program-test/src/index.ts (2)
CustomProgram
(14-14)LiteSVMConfig
(14-14)
js/stateless.js/rollup.config.js (2)
js/program-test/rollup.config.js (1)
rolls
(7-42)js/compressed-token/rollup.config.js (1)
rolls
(12-83)
js/program-test/src/test-utils.ts (2)
js/program-test/src/index.ts (3)
newAccountWithLamports
(10-10)sleep
(11-11)getOrCreateKeypair
(12-12)js/stateless.js/src/rpc.ts (1)
Rpc
(551-1892)
js/compressed-token/tests/program-test/create-token-pool.test.ts (1)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)
js/program-test/tests/test-rpc.test.ts (5)
js/stateless.js/src/constants.ts (4)
defaultTestStateTreeAccounts
(236-244)STATE_MERKLE_TREE_ROLLOVER_FEE
(338-340)featureFlags
(16-35)STATE_MERKLE_TREE_NETWORK_FEE
(356-356)js/stateless.js/src/state/compressed-account.ts (1)
CompressedAccountWithMerkleContext
(14-17)js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)
js/program-test/tests/compress.test.ts (3)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)
js/compressed-token/tests/program-test/decompress.test.ts (1)
js/program-test/src/spl-token-utils.ts (1)
splCreateAssociatedTokenAccount
(75-110)
js/program-test/src/merkle-tree/indexed-array.ts (3)
js/program-test/src/test-rpc/test-rpc.ts (1)
LightWasm
(63-68)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/stateless.js/src/constants.ts (1)
HIGHEST_ADDRESS_PLUS_ONE
(48-50)
js/program-test/src/test-rpc/get-parsed-events.ts (5)
js/stateless.js/src/rpc.ts (1)
Rpc
(551-1892)js/stateless.js/src/state/types.ts (2)
PublicTransactionEvent
(231-272)InstructionDataInvoke
(277-306)js/stateless.js/src/constants.ts (8)
defaultStaticAccountsStruct
(95-105)accountCompressionProgram
(76-77)COMPUTE_BUDGET_PATTERN
(52-52)noopProgram
(74-74)INVOKE_DISCRIMINATOR
(54-56)INVOKE_CPI_DISCRIMINATOR
(58-60)INVOKE_CPI_WITH_READ_ONLY_DISCRIMINATOR
(62-64)INSERT_INTO_QUEUES_DISCRIMINATOR
(70-72)js/stateless.js/src/programs/system/layout.ts (6)
decodePublicTransactionEvent
(392-396)decodeInstructionDataInvoke
(240-246)decodeInstructionDataInvokeCpi
(248-254)decodeInstructionDataInvokeCpiWithReadOnly
(234-238)deserializeAppendNullifyCreateAddressInputsIndexer
(441-514)convertToPublicTransactionEvent
(516-598)js/stateless.js/src/utils/conversion.ts (1)
convertInvokeCpiWithReadOnlyToInvoke
(121-191)
js/program-test/tests/rpc-multi-trees.test.ts (6)
js/stateless.js/src/rpc.ts (2)
Rpc
(551-1892)createRpc
(231-264)js/program-test/src/test-rpc/test-rpc.ts (2)
TestRpc
(114-928)getTestRpc
(85-104)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
selectStateTreeInfo
(84-110)js/stateless.js/src/utils/address.ts (2)
deriveAddressSeed
(11-18)deriveAddress
(29-46)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/stateless.js/src/actions/create-account.ts (1)
createAccountWithLamports
(113-175)
js/program-test/tests/merkle-tree.test.ts (4)
js/program-test/src/merkle-tree/indexed-array.ts (3)
IndexedElement
(5-36)IndexedArray
(58-307)newElement
(300-306)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/stateless.js/src/constants.ts (1)
HIGHEST_ADDRESS_PLUS_ONE
(48-50)js/program-test/src/merkle-tree/merkle-tree.ts (1)
MerkleTree
(13-214)
js/program-test/src/merkle-tree/merkle-tree.ts (1)
js/program-test/src/test-rpc/test-rpc.ts (1)
LightWasm
(63-68)
js/program-test/tests/rpc-interop.test.ts (9)
js/stateless.js/src/rpc.ts (2)
Rpc
(551-1892)createRpc
(231-264)js/program-test/src/test-rpc/test-rpc.ts (2)
TestRpc
(114-928)getTestRpc
(85-104)js/stateless.js/src/state/types.ts (1)
TreeInfo
(77-99)js/program-test/src/test-utils.ts (2)
newAccountWithLamports
(7-14)sleep
(19-21)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/stateless.js/src/utils/address.ts (2)
deriveAddressSeed
(11-18)deriveAddress
(29-46)js/stateless.js/src/actions/create-account.ts (1)
createAccountWithLamports
(113-175)js/stateless.js/src/constants.ts (2)
featureFlags
(16-35)getDefaultAddressTreeInfo
(221-229)
js/stateless.js/src/utils/send-and-confirm.ts (1)
forester-utils/src/rpc_pool.rs (1)
commitment
(152-155)
js/compressed-token/tests/program-test/multi-pool.test.ts (3)
js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)cli/test/helpers/helpers.ts (1)
createTestSplMint
(71-103)js/program-test/src/spl-token-utils.ts (2)
splGetOrCreateAssociatedTokenAccount
(246-294)splMintTo
(115-142)
js/program-test/rollup.config.js (1)
js/stateless.js/rollup.config.js (2)
rolls
(11-68)typesConfig
(70-74)
js/program-test/src/litesvm-rpc.ts (4)
js/program-test/src/index.ts (3)
LiteSVMRpc
(8-8)LiteSVMConfig
(14-14)createLiteSVMRpc
(8-8)js/program-test/src/test-rpc/test-rpc.ts (1)
TestRpc
(114-928)js/program-test/src/types.ts (1)
LiteSVMConfig
(16-27)js/stateless.js/src/constants.ts (1)
defaultTestStateTreeAccounts
(236-244)
sdk-tests/sdk-anchor-test/tests/test_v2.ts (3)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)js/program-test/src/index.ts (2)
createLiteSVMRpc
(8-8)newAccountWithLamports
(10-10)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)
js/program-test/src/test-rpc/get-compressed-token-accounts.ts (8)
js/stateless.js/src/rpc-interface.ts (2)
ParsedTokenAccount
(236-239)WithCursor
(250-255)js/stateless.js/src/state/types.ts (2)
CompressedAccountLegacy
(146-163)PublicTransactionEvent
(231-272)js/stateless.js/src/constants.ts (2)
COMPRESSED_TOKEN_PROGRAM_ID
(256-258)featureFlags
(16-35)js/stateless.js/src/rpc.ts (1)
Rpc
(551-1892)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
getStateTreeInfoByPubkey
(8-13)js/stateless.js/src/state/compressed-account.ts (2)
MerkleContext
(80-97)createCompressedAccountWithMerkleContextLegacy
(240-253)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/test-rpc/get-parsed-events.ts (1)
getParsedEvents
(34-140)
js/program-test/src/test-rpc/get-compressed-accounts.ts (5)
js/stateless.js/src/rpc.ts (1)
Rpc
(551-1892)js/stateless.js/src/state/compressed-account.ts (3)
CompressedAccountWithMerkleContext
(14-17)MerkleContext
(80-97)createCompressedAccountWithMerkleContextLegacy
(240-253)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/test-rpc/get-parsed-events.ts (1)
getParsedEvents
(34-140)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
getStateTreeInfoByPubkey
(8-13)
js/compressed-token/tests/program-test/compress.test.ts (10)
js/stateless.js/src/rpc-interface.ts (1)
ParsedTokenAccount
(236-239)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)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/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
selectStateTreeInfo
(84-110)js/program-test/src/spl-token-utils.ts (1)
splCreateAssociatedTokenAccount
(75-110)js/compressed-token/src/program.ts (1)
CompressedTokenProgram
(600-1659)js/stateless.js/src/utils/dedupe-signer.ts (1)
dedupeSigner
(4-11)js/stateless.js/src/utils/send-and-confirm.ts (2)
buildAndSignTx
(139-160)sendAndConfirmTx
(51-62)
js/program-test/tests/transfer.test.ts (3)
js/program-test/src/litesvm-rpc.ts (2)
LiteSVMRpc
(22-889)createLiteSVMRpc
(894-900)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)
js/program-test/src/test-rpc/test-rpc.ts (10)
js/stateless.js/src/constants.ts (3)
defaultTestStateTreeAccounts
(236-244)localTestActiveStateTreeInfos
(151-219)batchAddressTree
(304-304)js/stateless.js/src/rpc-interface.ts (7)
CompressionApiInterface
(762-867)WithCursor
(250-255)LatestNonVotingSignatures
(33-43)WithContext
(241-248)TokenBalance
(210-210)CompressedMintTokenHolders
(52-55)ValidityProofWithContext
(161-190)js/stateless.js/src/state/BN254.ts (1)
BN254
(12-12)js/program-test/src/merkle-tree/indexed-array.ts (2)
hash
(24-35)IndexedArray
(58-307)js/program-test/src/test-rpc/get-compressed-accounts.ts (4)
getCompressedAccountsForTest
(42-96)getCompressedAccountByHashTest
(23-29)getMultipleCompressedAccountsByHashTest
(31-39)getCompressedAccountsByOwnerTest
(14-21)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/test-rpc/get-parsed-events.ts (1)
getParsedEvents
(34-140)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
getStateTreeInfoByPubkey
(8-13)js/program-test/src/merkle-tree/merkle-tree.ts (2)
MerkleTree
(13-214)root
(74-78)js/stateless.js/src/rpc.ts (4)
MerkleContextWithNewAddressProof
(402-412)convertMerkleProofsWithContextToHex
(424-442)proverRequest
(336-390)convertNonInclusionMerkleProofInputsToHex
(444-470)
js/compressed-token/tests/program-test/compress-spl-token-account.test.ts (2)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)js/program-test/src/spl-token-utils.ts (2)
splCreateAssociatedTokenAccount
(75-110)splMintTo
(115-142)
js/compressed-token/tests/program-test/decompress-delegated.test.ts (1)
js/program-test/src/spl-token-utils.ts (1)
splCreateAssociatedTokenAccount
(75-110)
js/compressed-token/tests/program-test/mint-to.test.ts (6)
cli/test/helpers/helpers.ts (1)
TEST_TOKEN_DECIMALS
(69-69)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/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)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/tests/program-test/create-mint.test.ts (1)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)
js/compressed-token/tests/program-test/approve-and-mint-to.test.ts (9)
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/program-test/src/spl-token-utils.ts (1)
splGetOrCreateAssociatedTokenAccount
(246-294)js/compressed-token/src/program.ts (1)
CompressedTokenProgram
(600-1659)js/stateless.js/src/utils/dedupe-signer.ts (1)
dedupeSigner
(4-11)js/stateless.js/src/utils/send-and-confirm.ts (2)
buildAndSignTx
(139-160)sendAndConfirmTx
(51-62)js/stateless.js/src/utils/conversion.ts (1)
toArray
(28-29)js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)
sdk-tests/sdk-anchor-test/tests/test_v1.ts (2)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: stateless-js-v1
- GitHub Check: cli-v2
- GitHub Check: stateless-js-v2
- GitHub Check: lint
- GitHub Check: cli-v1
🔇 Additional comments (19)
js/compressed-token/package.json (1)
82-99
: Program-test coverage integration looks solidWiring the program-test suite ahead of the E2E runs (with dedicated entry points for each scenario) ensures the LiteSVM harness is exercised consistently before the heavier validator flow. The added Nx dependency on
@lightprotocol/program-test:build
also keeps the workspace build order sane. Nicely done..github/workflows/js.yml (1)
66-105
: Program-test coverage integrated cleanlyAdding explicit build/test steps with the shared devenv bootstrap keeps the new package under CI and matches existing workflow structure. Nice alignment.
.github/workflows/js-v2.yml (1)
66-107
: Parity for V2 pipeline looks greatMirroring the program-test build/test stages here keeps both versions consistent and ensures LiteSVM regressions surface early.
js/compressed-token/tests/program-test/transfer.test.ts (1)
19-125
: Great move to shared LiteSVM helpersSwitching the suite over to
createLiteSVMRpc
andnewAccountWithLamports
centralizes the setup logic and keeps it aligned with the new program-test package—this reads much cleaner.js/compressed-token/tests/program-test/decompress-delegated.test.ts (1)
18-139
: Nice reuse of program-test SPL helpersAdopting the packaged RPC and SPL helpers streamlines delegated decompression setup and keeps everything within the LiteSVM-aware toolchain.
js/compressed-token/tests/program-test/create-token-pool.test.ts (1)
19-118
: Consistent LiteSVM bootstrappingBringing the token-pool tests onto the shared RPC/account helpers removes bespoke wiring and keeps coverage aligned with program-test expectations.
js/program-test/README.md (1)
1-177
: Documentation is clear and actionableThe README gives a solid overview of LiteSVM usage, test commands, and exported APIs—great onboarding material for the new package.
js/compressed-token/tests/program-test/multi-pool.test.ts (1)
23-115
: Solid consolidation around program-test helpersUpdating the multi-pool flow to rely on the shared RPC and SPL utilities removes ad-hoc wiring and keeps the scenarios aligned with the new testing stack.
js/compressed-token/tests/program-test/transfer-delegated.test.ts (1)
11-14
: LGTM! Migration to program-test utilities is clean.The import reorganization and RPC initialization changes align well with the broader migration to LiteSVM-based testing infrastructure.
Also applies to: 182-182
js/compressed-token/tests/program-test/decompress.test.ts (1)
14-18
: LGTM! Consistent migration to program-test utilities.The changes properly migrate to:
createLiteSVMRpc
for RPC initializationnewAccountWithLamports
for test account creationsplCreateAssociatedTokenAccount
for token account setupAll signatures and usage patterns align with the program-test helper implementations shown in the relevant code snippets.
Also applies to: 83-83, 103-108
js/compressed-token/tests/program-test/compress-spl-token-account.test.ts (2)
18-23
: LGTM! Migration to program-test SPL token utilities.The import changes and usage of
createLiteSVMRpc
,splCreateAssociatedTokenAccount
, andsplMintTo
are consistent with the program-test package API surface.Also applies to: 44-44, 64-69
138-145
: Verify raw amount passed to splMintTo accounts for token decimals
splMintTo uses raw base units;bn(100).toNumber()
= 100 (1 token at 2 decimals). Confirm tests intend to mint 1 token or multiply by 10**TEST_TOKEN_DECIMALS to mint 100 tokens.js/program-test/package.json (2)
64-83
: LGTM! Comprehensive test script setup.The test scripts provide good coverage:
- Separate unit and e2e test suites
- V1/V2 protocol version testing
- Individual test file execution options
- Proper test validator management
31-40
: All key dependencies are up-to-date and secure. @solana/[email protected], @lightprotocol/[email protected], and [email protected] match the latest registry versions.js/compressed-token/tests/program-test/delegate.test.ts (1)
11-14
: LGTM! Standard migration to program-test utilities.The import reorganization and RPC initialization follow the established migration pattern across the test suite.
Also applies to: 119-119
js/compressed-token/tests/e2e/compress.test.ts (1)
38-38
: LGTM! Import source updated for getTestRpc.The import has been correctly relocated from
@lightprotocol/stateless.js
to@lightprotocol/program-test
, consistent with the test utilities consolidation.Note: The AI summary mentions several test cases were removed from this file (compress to multiple recipients, Token-2022 scenarios). While not visible in the provided diff, ensure these removals are intentional and that equivalent test coverage exists elsewhere in the test suite.
js/compressed-token/tests/program-test/create-mint.test.ts (1)
6-11
: LGTM! Import reorganization and RPC initialization are correct.The changes properly:
- Import
Rpc
type from stateless.js- Import
WasmFactory
from hasher.rs (correct source)- Import test utilities (
createLiteSVMRpc
,newAccountWithLamports
) from program-test- Initialize RPC with
createLiteSVMRpc(lightWasm)
All imports and usage patterns are consistent with the migration to the program-test package.
Also applies to: 56-56
js/program-test/tests/merkle-tree.test.ts (1)
6-183
: Solid coverage and deterministic root checksGood end-to-end assertions for append flows and Merkle roots; hashing path via WasmFactory is exercised well. No issues spotted.
js/compressed-token/tests/program-test/approve-and-mint-to.test.ts (1)
40-98
: Custom approve-and-mint path looks goodHelper composes SPL + compress ixs correctly, handles ATA creation, blockhash, deduped signers, and compute budget. Calls are consistent.
Also applies to: 147-150, 168-177, 207-216
rpc = createRpc(); | ||
|
||
testRpc = await getTestRpc(lightWasm); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the LiteSVM RPC instead of the plain createRpc()
Right now the suite calls createRpc()
, which points at localhost JSON‑RPC/compression hosts. In the program-test environment nothing is actually listening on those ports unless you also spin up a real validator/prover, so every airdrop/compress/transfer in this spec will fail in CI. All the other new specs bootstrap against LiteSVM via createLiteSVMRpc(lightWasm)
—do the same here (and drop the unused testRpc
if you don’t need it) so the tests run against the in-process LiteSVM harness this package provides.
Apply this diff:
- rpc = createRpc();
-
- testRpc = await getTestRpc(lightWasm);
+ rpc = await createLiteSVMRpc(lightWasm);
📝 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.
rpc = createRpc(); | |
testRpc = await getTestRpc(lightWasm); | |
rpc = await createLiteSVMRpc(lightWasm); |
🤖 Prompt for AI Agents
In js/program-test/tests/rpc-multi-trees.test.ts around lines 35 to 38, the test
currently calls createRpc() which targets external localhost RPC hosts not
present in the program-test harness; replace that call with
createLiteSVMRpc(lightWasm) so the suite uses the in-process LiteSVM harness,
and remove the unused testRpc variable (or stop awaiting getTestRpc(lightWasm))
if it isn’t needed.
e320e1f
to
cf7f1f6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
sdk-tests/sdk-anchor-test/tests/test_v1.ts (1)
127-136
: Missing type import for SdkAnchorTest breaks TS compileProgram is used but SdkAnchorTest isn’t imported. Import the generated Anchor type or drop the generic.
Apply one of the following:
Option A — import the generated type (preferred; typical Anchor path):
@@ -import idl from "../target/idl/sdk_anchor_test.json"; +import idl from "../target/idl/sdk_anchor_test.json"; +import type { SdkAnchorTest } from "../target/types/sdk_anchor_test";Option B — relax the signature:
- program: Program<SdkAnchorTest>, + program: Program,sdk-tests/sdk-anchor-test/tests/test_v2.ts (1)
141-149
: Missing type import for SdkAnchorTest (same as v1)Ensure SdkAnchorTest is imported, or relax the signature.
- program: Program<SdkAnchorTest>, + program: Program,Or add:
import type { SdkAnchorTest } from "../target/types/sdk_anchor_test";
🧹 Nitpick comments (30)
js/program-test/.prettierignore (1)
1-2
: Drop the redundant photon.log pattern.
test-ledger/*.log
already capturestest-ledger/photon.log
, so the dedicated entry is unnecessary noise. Trim the duplicate to keep the ignore list lean.js/program-test/vitest.config.ts (1)
1-7
: Minimal Vitest config is fine; consider adding env/coverage laterLooks good. Optionally set test.environment ('node' vs 'jsdom') and basic coverage thresholds to catch regressions as program-test grows.
js/compressed-token/tests/e2e/mint-to.test.ts (1)
108-133
: Add post-assertions to validate mint resultsCurrently the test only sends the tx. Verify at least a couple recipients to prevent false positives.
- const txId = await sendAndConfirmTx(rpc, tx); + const txId = await sendAndConfirmTx(rpc, tx); + // Spot-check first and last recipients + await assertMintTo(rpc, mint, amounts[0], recipients[0]); + await assertMintTo(rpc, mint, amounts[amounts.length - 1], recipients[recipients.length - 1]);js/compressed-token/tests/e2e/compress.test.ts (1)
171-199
: Add a minimal assertion to ensure compress had effectValidate one recipient received the expected compressed amount.
- await sendAndConfirmTx(rpc, tx); + await sendAndConfirmTx(rpc, tx); + const post = await rpc.getCompressedTokenAccountsByOwner(recipients[0], { mint }); + const received = post.items.some((it) => it.parsed.amount.eq(bn(2))); + expect(received).toBe(true);js/program-test/src/test-rpc/get-compressed-accounts.ts (2)
14-21
: Add explicit return typeAnnotate the return type for clarity and tooling.
-export async function getCompressedAccountsByOwnerTest( +export async function getCompressedAccountsByOwnerTest( rpc: Rpc, owner: PublicKey, -) { +): Promise<CompressedAccountWithMerkleContext[]> {
41-96
: Use Set/Map to dedupe and speed up unspent-account resolutionAvoid O(n^2) and keep the latest output per hash (events already reversed).
-export async function getCompressedAccountsForTest(rpc: Rpc) { +export async function getCompressedAccountsForTest( + rpc: Rpc, +): Promise<CompressedAccountWithMerkleContext[]> { const events = (await getParsedEvents(rpc)).reverse(); - const allOutputAccounts: CompressedAccountWithMerkleContext[] = []; - const allInputAccountHashes: BN[] = []; + const outputByHash = new Map<string, CompressedAccountWithMerkleContext>(); + const spentHashes = new Set<string>(); const infos = await rpc.getStateTreeInfos(); @@ - const merkleContext: MerkleContext = { + const merkleContext: MerkleContext = { treeInfo, - hash: bn(event.outputCompressedAccountHashes[index]), + hash: bn(event.outputCompressedAccountHashes[index]), leafIndex: event.outputLeafIndices[index], // V2 trees always have proveByIndex = true in test-rpc. proveByIndex: treeInfo.treeType === TreeType.StateV2, }; const withCtx: CompressedAccountWithMerkleContext = createCompressedAccountWithMerkleContextLegacy( merkleContext, account.compressedAccount.owner, account.compressedAccount.lamports, account.compressedAccount.data ?? undefined, account.compressedAccount.address ?? undefined, ); - allOutputAccounts.push(withCtx); + // Keep only the latest occurrence per hash (events processed newest-first) + const hashStr = bn(merkleContext.hash).toString(16); + if (!outputByHash.has(hashStr)) { + outputByHash.set(hashStr, withCtx); + } @@ - const hash = event.inputCompressedAccountHashes[index]; - allInputAccountHashes.push(bn(hash)); + const hash = event.inputCompressedAccountHashes[index]; + spentHashes.add(bn(hash).toString(16)); @@ - const unspentAccounts = allOutputAccounts.filter( - (account) => - !allInputAccountHashes.some((hash) => hash.eq(bn(account.hash))), - ); - unspentAccounts.sort((a, b) => b.leafIndex - a.leafIndex); - - return unspentAccounts; + const unspentAccounts = Array.from(outputByHash.entries()) + .filter(([hashStr]) => !spentHashes.has(hashStr)) + .map(([, acc]) => acc) + .sort((a, b) => b.leafIndex - a.leafIndex); + return unspentAccounts; }js/program-test/src/merkle-tree/merkle-tree.ts (1)
22-27
: Tighten typing for zeroElementAnnotate
zeroElement
to avoid implicit any.- zeroElement; + zeroElement: string;.github/workflows/js.yml (1)
103-106
: Consider adding retry logic for consistency.The program-test tests lack the retry logic present in the stateless.js and compressed-token test steps (lines 71-85, 87-101). If program-test tests exhibit similar flakiness, consider adding the same retry pattern for consistency.
Apply this diff to add retry logic:
- name: Run program-test tests with V1 run: | - source ./scripts/devenv.sh - npx nx test @lightprotocol/program-test + echo "Running program-test tests with retry logic (max 2 attempts)..." + attempt=1 + max_attempts=2 + until (source ./scripts/devenv.sh && npx nx test @lightprotocol/program-test); do + attempt=$((attempt + 1)) + if [ $attempt -gt $max_attempts ]; then + echo "Tests failed after $max_attempts attempts" + exit 1 + fi + echo "Attempt $attempt/$max_attempts failed, retrying..." + sleep 5 + done + echo "Tests passed on attempt $attempt".github/workflows/js-v2.yml (1)
103-106
: Consider adding retry logic for consistency.The program-test tests lack the retry logic present in the stateless.js and compressed-token test steps (lines 71-85, 87-101). If program-test tests exhibit similar flakiness, consider adding the same retry pattern for consistency.
Apply this diff to add retry logic:
- name: Run program-test tests with V2 run: | - source ./scripts/devenv.sh - npx nx test @lightprotocol/program-test + echo "Running program-test tests with retry logic (max 2 attempts)..." + attempt=1 + max_attempts=2 + until (source ./scripts/devenv.sh && npx nx test @lightprotocol/program-test); do + attempt=$((attempt + 1)) + if [ $attempt -gt $max_attempts ]; then + echo "Tests failed after $max_attempts attempts" + exit 1 + fi + echo "Attempt $attempt/$max_attempts failed, retrying..." + sleep 5 + done + echo "Tests passed on attempt $attempt"js/program-test/tests/compress.test.ts (1)
24-52
: Consider removing console.log statements.While logging is helpful during development, consider removing or converting these
console.log
statements to conditional debug logging for cleaner test output in CI environments.Example approach:
const DEBUG = process.env.DEBUG_TESTS === "true"; // Replace console.log with conditional logging if (DEBUG) { console.log("Pre-compress balance:", preBalance); }Or remove the logs entirely if not needed for debugging.
js/compressed-token/tests/program-test/mint-to.test.ts (1)
139-140
: Consider removing the commented code.The commented line appears to be a previous value for
maxRecipients
. Consider removing it to reduce clutter, unless it serves as documentation for the test's evolution.Apply this diff to clean up the commented code:
- // const maxRecipients = 18; const maxRecipients = 22;
js/compressed-token/tests/program-test/approve-and-mint-to.test.ts (1)
40-98
: Consider exposing a public API instead of duplicating logic.This local test helper duplicates functionality that could be part of the public API. Creating test-specific helpers increases maintenance burden when the underlying implementation changes.
Options:
- Preferred: Export this as part of
@lightprotocol/program-test
or@lightprotocol/compressed-token
public API if other consumers need it- Alternative: Add a comment explaining why this must remain test-local (e.g., LiteSVM-specific behavior)
Additionally, document the compute budget formula on line 87-88:
// Base: 150k units for transaction overhead // Per-recipient: 20k units for compression operations units: 150_000 + toArray(amount).length * 20_000,js/program-test/tests/transfer.test.ts (1)
24-59
: Consider accounting for transaction fees in assertions.The balance assertions only verify the transfer amount delta but don't account for transaction fees that may be deducted from the sender's balance. This could cause false negatives if fees are charged.
If transaction fees are expected, consider adjusting the assertion to account for them:
assert( postSenderBalance.sub(preSenderBalance).lte(bn(-transferAmount)), `Iteration ${i + 1}: Sender balance should decrease by at least ${transferAmount}`, );Or fetch and verify the exact fee from the transaction metadata.
js/program-test/src/test-utils.ts (1)
26-31
: Validate seed length in getOrCreateKeypair: ensureseed.length === 32
before callingKeypair.fromSeed
, either by throwing a descriptive error or padding to 32 bytes.js/program-test/tests/test-rpc.test.ts (4)
64-71
: Replace magic numbers with constants for fee checksHardcoded 5000s risk drift. Use STATE_MERKLE_TREE_NETWORK_FEE to compute both network-fee deductions.
Apply this diff:
- assert.equal( - postCompressBalance, - preCompressBalance - - compressLamportsAmount - - 5000 - - 5000 - - STATE_MERKLE_TREE_ROLLOVER_FEE.toNumber(), - ); + assert.equal( + postCompressBalance, + preCompressBalance - + compressLamportsAmount - + 2 * STATE_MERKLE_TREE_NETWORK_FEE.toNumber() - + STATE_MERKLE_TREE_ROLLOVER_FEE.toNumber(), + );
109-112
: Use constant instead of 5000 for fee deductionKeep fee expectations tied to constants.
- preCompressBalance - - 5000 - - STATE_MERKLE_TREE_ROLLOVER_FEE.toNumber() - - STATE_MERKLE_TREE_NETWORK_FEE.toNumber(), + preCompressBalance - + STATE_MERKLE_TREE_NETWORK_FEE.toNumber() - + STATE_MERKLE_TREE_ROLLOVER_FEE.toNumber() - + STATE_MERKLE_TREE_NETWORK_FEE.toNumber(),
79-83
: Trim noisy console logs in testsThese prints add noise and slow CI. Prefer expect/asserts; log only on failure or behind a DEBUG flag.
Also applies to: 87-89, 217-223
18-18
: Minor: remove unused and tighten types
- merkleTree is never used.
- Type rpc as Rpc for better IDE help.
Also applies to: 19-19
js/compressed-token/tests/program-test/compress.test.ts (2)
232-242
: Remove redundant loop calling assertCompress N timesThe body doesn’t depend on i; it repeats the same assertions and wastes time.
- for (let i = 0; i < recipients.length; i++) { - await assertCompress( - rpc, - bn(senderAtaBalanceBefore.value.amount), - bobAta, - mint, - amounts.slice(0, 11), - recipients.slice(0, 11), - recipientCompressedTokenBalancesBefore.map(x => x.items), - ); - } + await assertCompress( + rpc, + bn(senderAtaBalanceBefore.value.amount), + bobAta, + mint, + amounts.slice(0, 11), + recipients.slice(0, 11), + recipientCompressedTokenBalancesBefore.map(x => x.items), + );
127-137
: Drop debug logs or guard behind DEBUGConsole noise slows CI and hides useful output. Keep only when diagnosing failures.
Also applies to: 145-146, 157-161, 176-176
js/program-test/tests/rpc-multi-trees.test.ts (2)
158-160
: Meaningless self-equality checkComparing an element to itself doesn’t validate anything. Assert against the requested hash or expected tree/queue.
- compressedAccountProof.merkleProof.forEach((proof, index) => { - assert.isTrue(proof.eq(compressedAccountProof.merkleProof[index])); - }); + assert.equal( + compressedAccountProof.hash.toString(), + hash.toString(), + 'Proof hash should match requested hash', + );
217-226
: Reduce console noiseBatch logs inside hot loops make CI output unwieldy. Consider removing or wrapping with a debug flag.
Also applies to: 237-243
js/compressed-token/tests/program-test/compress-spl-token-account.test.ts (1)
138-145
: Prefer passing bigint to splMintTo and keep calls consistentYou convert BN to number; safe here but brittle if amounts grow. Pass bigint (e.g., testAmount.toArrayLike(BigInt) is not straightforward) or a numeric literal where small. Also consider consolidating to a helper to avoid drift.
Also applies to: 221-228, 266-266, 290-290
sdk-tests/sdk-anchor-test/tests/test_v1.ts (1)
181-185
: Guard debug access to instruction indexAccessing tx.instructions[1] assumes at least two instructions (compute budget + method). If ordering changes, this can throw.
- console.log("tx ", tx.instructions[1].keys); + if (tx.instructions[1]) { + console.log("tx ", tx.instructions[1].keys); + }js/program-test/src/litesvm-rpc.ts (2)
370-386
: getSignaturesForAddress ignores the address paramCurrently returns all stored txs, which can skew consumers expecting address-scoped results.
Filter by presence of the address in the transaction’s account keys:
- return Array.from(this.storedTransactions.values()) + const matches = Array.from(this.storedRawTransactions.entries()).filter( + ([, rawTx]) => { + const keys = + "message" in rawTx + ? (rawTx.message.staticAccountKeys ?? + rawTx.message.accountKeys ?? + rawTx.message.getAccountKeys?.().staticAccountKeys ?? + []) + : (rawTx as Transaction).compileMessage().accountKeys; + return keys.some((k: PublicKey) => k.equals(address)); + }, + ); + return matches + .map(([sig]) => this.storedTransactions.get(sig)!) .reverse() .map((tx) => ({ signature: tx.signature, slot: tx.slot, err: null, memo: null, blockTime: tx.blockTime, confirmationStatus: tx.confirmationStatus, }));
239-274
: Excessive console logging in hot pathsVerbose logs in sendTransaction and getSignatureStatuses will spam CI logs.
- Gate logs behind a config flag (this.log) or environment variable.
- Use a debug logger and default to silent in tests.
Also applies to: 739-769
js/program-test/tests/rpc-interop.test.ts (1)
656-662
: Remove unused variableallAccountsTestRpc is declared but never used.
- const allAccountsTestRpc = await testRpc.getCompressedAccountsByOwner( - payer.publicKey, - ); - const allAccountsRpc = await rpc.getCompressedAccountsByOwner( + const allAccountsRpc = await rpc.getCompressedAccountsByOwner( payer.publicKey, );js/program-test/src/merkle-tree/indexed-array.ts (2)
24-35
: Preserve original error details in error messages.The catch block on line 32-33 throws a generic error message that discards the original error context. This makes debugging hash failures more difficult.
Apply this diff to preserve the original error:
public hash(lightWasm: LightWasm, nextValue: BN): Uint8Array { try { const hash = lightWasm.poseidonHash([ bn(this.value.toArray("be", 32)).toString(), bn(this.nextIndex).toString(), bn(nextValue.toArray("be", 32)).toString(), ]); return hash; } catch (error) { - throw new Error("Hashing failed"); + throw new Error(`Hashing failed: ${error instanceof Error ? error.message : String(error)}`); } }
164-180
: Consider consistent error handling for hashing.The
hashElement
method lacks error handling, unlikeIndexedElement.hash
(lines 24-35) which wraps the hashing call in a try-catch block. IfposeidonHash
fails, this will throw an unhandled error.Apply this diff to add consistent error handling:
public hashElement( lightWasm: LightWasm, index: number, ): Uint8Array | undefined { const element = this.elements[index]; if (!element) return undefined; const nextElement = this.elements[element.nextIndex]; if (!nextElement) return undefined; + try { const hash = lightWasm.poseidonHash([ bn(element.value.toArray("be", 32)).toString(), bn(element.nextIndex).toString(), bn(nextElement.value.toArray("be", 32)).toString(), ]); return hash; + } catch (error) { + throw new Error(`Hashing failed for element at index ${index}: ${error instanceof Error ? error.message : String(error)}`); + } }js/program-test/src/spl-token-utils.ts (1)
246-256
: Document or remove unused parameters.The
commitment
andconfirmOptions
parameters (lines 252-253) are declared but never used in the function body. If these are included for API compatibility with@solana/spl-token
'sgetOrCreateAssociatedTokenAccount
, consider adding a JSDoc comment explaining this. Otherwise, remove them to avoid confusion.If keeping for compatibility, add documentation:
/** * Get or create an associated token account * Replicates the behavior of getOrCreateAssociatedTokenAccount from @solana/spl-token + * + * @param commitment - Reserved for compatibility (not used with LiteSVM) + * @param confirmOptions - Reserved for compatibility (not used with LiteSVM) */ export async function splGetOrCreateAssociatedTokenAccount( rpc: Rpc, payer: Signer, mint: PublicKey, owner: PublicKey, allowOwnerOffCurve = false, commitment?: any, confirmOptions?: any, programId = TOKEN_PROGRAM_ID, associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID, ): Promise<{ address: PublicKey; isNew: boolean }> {Or remove them if not needed:
export async function splGetOrCreateAssociatedTokenAccount( rpc: Rpc, payer: Signer, mint: PublicKey, owner: PublicKey, allowOwnerOffCurve = false, - commitment?: any, - confirmOptions?: any, programId = TOKEN_PROGRAM_ID, associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID, ): Promise<{ address: PublicKey; isNew: boolean }> {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (65)
.github/workflows/js-v2.yml
(2 hunks).github/workflows/js.yml
(2 hunks)js/compressed-token/package.json
(3 hunks)js/compressed-token/tests/e2e/compress.test.ts
(1 hunks)js/compressed-token/tests/e2e/mint-to.test.ts
(2 hunks)js/compressed-token/tests/program-test/approve-and-mint-to.test.ts
(4 hunks)js/compressed-token/tests/program-test/compress-spl-token-account.test.ts
(8 hunks)js/compressed-token/tests/program-test/compress.test.ts
(1 hunks)js/compressed-token/tests/program-test/create-mint.test.ts
(2 hunks)js/compressed-token/tests/program-test/create-token-pool.test.ts
(2 hunks)js/compressed-token/tests/program-test/decompress-delegated.test.ts
(3 hunks)js/compressed-token/tests/program-test/decompress.test.ts
(3 hunks)js/compressed-token/tests/program-test/delegate.test.ts
(2 hunks)js/compressed-token/tests/program-test/mint-to.test.ts
(1 hunks)js/compressed-token/tests/program-test/multi-pool.test.ts
(2 hunks)js/compressed-token/tests/program-test/transfer-delegated.test.ts
(2 hunks)js/compressed-token/tests/program-test/transfer.test.ts
(4 hunks)js/program-test/.prettierignore
(1 hunks)js/program-test/README.md
(1 hunks)js/program-test/package.json
(1 hunks)js/program-test/rollup.config.js
(1 hunks)js/program-test/src/index.ts
(1 hunks)js/program-test/src/litesvm-rpc.ts
(1 hunks)js/program-test/src/merkle-tree/index.ts
(1 hunks)js/program-test/src/merkle-tree/indexed-array.ts
(1 hunks)js/program-test/src/merkle-tree/merkle-tree.ts
(1 hunks)js/program-test/src/spl-token-utils.ts
(1 hunks)js/program-test/src/test-rpc/get-compressed-accounts.ts
(1 hunks)js/program-test/src/test-rpc/get-compressed-token-accounts.ts
(1 hunks)js/program-test/src/test-rpc/get-parsed-events.ts
(1 hunks)js/program-test/src/test-rpc/index.ts
(1 hunks)js/program-test/src/test-rpc/test-rpc.ts
(1 hunks)js/program-test/src/test-utils.ts
(1 hunks)js/program-test/src/types.ts
(1 hunks)js/program-test/tests/compress.test.ts
(1 hunks)js/program-test/tests/merkle-tree.test.ts
(1 hunks)js/program-test/tests/rpc-interop.test.ts
(1 hunks)js/program-test/tests/rpc-multi-trees.test.ts
(1 hunks)js/program-test/tests/test-rpc.test.ts
(1 hunks)js/program-test/tests/transfer.test.ts
(1 hunks)js/program-test/tsconfig.json
(1 hunks)js/program-test/vitest.config.ts
(1 hunks)js/stateless.js/package.json
(2 hunks)js/stateless.js/rollup.config.js
(2 hunks)js/stateless.js/src/rpc.ts
(0 hunks)js/stateless.js/src/test-helpers/index.ts
(0 hunks)js/stateless.js/src/test-helpers/merkle-tree/index.ts
(0 hunks)js/stateless.js/src/test-helpers/merkle-tree/indexed-array.ts
(0 hunks)js/stateless.js/src/test-helpers/merkle-tree/merkle-tree.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/get-compressed-accounts.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/get-compressed-token-accounts.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/get-parsed-events.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/index.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts
(0 hunks)js/stateless.js/src/utils/send-and-confirm.ts
(1 hunks)js/stateless.js/tests/e2e/compress.test.ts
(0 hunks)js/stateless.js/tests/e2e/rpc-interop.test.ts
(0 hunks)js/stateless.js/tests/e2e/rpc-multi-trees.test.ts
(0 hunks)js/stateless.js/tests/e2e/test-rpc.test.ts
(0 hunks)js/stateless.js/tests/e2e/transfer.test.ts
(0 hunks)js/stateless.js/tests/unit/merkle-tree/merkle-tree.test.ts
(0 hunks)pnpm-workspace.yaml
(1 hunks)sdk-tests/sdk-anchor-test/package.json
(1 hunks)sdk-tests/sdk-anchor-test/tests/test_v1.ts
(4 hunks)sdk-tests/sdk-anchor-test/tests/test_v2.ts
(5 hunks)
💤 Files with no reviewable changes (16)
- js/stateless.js/src/test-helpers/test-rpc/index.ts
- js/stateless.js/src/test-helpers/test-rpc/get-compressed-accounts.ts
- js/stateless.js/src/test-helpers/index.ts
- js/stateless.js/src/test-helpers/merkle-tree/index.ts
- js/stateless.js/src/test-helpers/merkle-tree/merkle-tree.ts
- js/stateless.js/src/test-helpers/test-rpc/get-compressed-token-accounts.ts
- js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts
- js/stateless.js/src/test-helpers/merkle-tree/indexed-array.ts
- js/stateless.js/tests/unit/merkle-tree/merkle-tree.test.ts
- js/stateless.js/tests/e2e/test-rpc.test.ts
- js/stateless.js/src/rpc.ts
- js/stateless.js/tests/e2e/transfer.test.ts
- js/stateless.js/src/test-helpers/test-rpc/get-parsed-events.ts
- js/stateless.js/tests/e2e/rpc-interop.test.ts
- js/stateless.js/tests/e2e/compress.test.ts
- js/stateless.js/tests/e2e/rpc-multi-trees.test.ts
🧰 Additional context used
🧬 Code graph analysis (29)
js/stateless.js/src/utils/send-and-confirm.ts (1)
forester-utils/src/rpc_pool.rs (1)
commitment
(152-155)
js/program-test/tests/merkle-tree.test.ts (5)
js/program-test/src/merkle-tree/indexed-array.ts (3)
IndexedElement
(5-36)IndexedArray
(58-307)newElement
(300-306)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/stateless.js/src/constants.ts (1)
HIGHEST_ADDRESS_PLUS_ONE
(48-50)program-tests/merkle-tree/src/lib.rs (1)
leaf
(293-298)js/program-test/src/merkle-tree/merkle-tree.ts (1)
MerkleTree
(13-214)
js/program-test/tests/test-rpc.test.ts (5)
js/stateless.js/src/constants.ts (4)
defaultTestStateTreeAccounts
(236-244)STATE_MERKLE_TREE_ROLLOVER_FEE
(338-340)featureFlags
(16-35)STATE_MERKLE_TREE_NETWORK_FEE
(356-356)js/stateless.js/src/state/compressed-account.ts (1)
CompressedAccountWithMerkleContext
(14-17)js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)
js/program-test/src/test-utils.ts (1)
js/program-test/src/index.ts (3)
newAccountWithLamports
(10-10)sleep
(11-11)getOrCreateKeypair
(12-12)
js/program-test/src/test-rpc/get-parsed-events.ts (4)
js/stateless.js/src/state/types.ts (2)
PublicTransactionEvent
(231-272)InstructionDataInvoke
(277-306)js/stateless.js/src/constants.ts (8)
defaultStaticAccountsStruct
(95-105)accountCompressionProgram
(76-77)COMPUTE_BUDGET_PATTERN
(52-52)noopProgram
(74-74)INVOKE_DISCRIMINATOR
(54-56)INVOKE_CPI_DISCRIMINATOR
(58-60)INVOKE_CPI_WITH_READ_ONLY_DISCRIMINATOR
(62-64)INSERT_INTO_QUEUES_DISCRIMINATOR
(70-72)js/stateless.js/src/programs/system/layout.ts (6)
decodePublicTransactionEvent
(392-396)decodeInstructionDataInvoke
(240-246)decodeInstructionDataInvokeCpi
(248-254)decodeInstructionDataInvokeCpiWithReadOnly
(234-238)deserializeAppendNullifyCreateAddressInputsIndexer
(441-514)convertToPublicTransactionEvent
(516-598)js/stateless.js/src/utils/conversion.ts (1)
convertInvokeCpiWithReadOnlyToInvoke
(121-191)
js/program-test/src/test-rpc/get-compressed-accounts.ts (5)
js/stateless.js/src/rpc.ts (1)
Rpc
(551-1892)js/stateless.js/src/state/compressed-account.ts (3)
CompressedAccountWithMerkleContext
(14-17)MerkleContext
(80-97)createCompressedAccountWithMerkleContextLegacy
(240-253)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/test-rpc/get-parsed-events.ts (1)
getParsedEvents
(34-140)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
getStateTreeInfoByPubkey
(8-13)
js/compressed-token/tests/program-test/multi-pool.test.ts (3)
js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)cli/test/helpers/helpers.ts (1)
createTestSplMint
(71-103)js/program-test/src/spl-token-utils.ts (2)
splGetOrCreateAssociatedTokenAccount
(246-294)splMintTo
(115-142)
js/stateless.js/rollup.config.js (2)
js/program-test/rollup.config.js (1)
rolls
(7-42)js/compressed-token/rollup.config.js (1)
rolls
(12-83)
js/program-test/src/litesvm-rpc.ts (3)
js/program-test/src/test-rpc/test-rpc.ts (1)
TestRpc
(114-928)js/program-test/src/types.ts (1)
LiteSVMConfig
(16-27)js/stateless.js/src/constants.ts (2)
defaultTestStateTreeAccounts
(236-244)COMPRESSED_TOKEN_PROGRAM_ID
(256-258)
sdk-tests/sdk-anchor-test/tests/test_v1.ts (3)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)js/program-test/src/index.ts (2)
createLiteSVMRpc
(8-8)newAccountWithLamports
(10-10)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)
js/program-test/tests/compress.test.ts (3)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)
sdk-tests/sdk-anchor-test/tests/test_v2.ts (2)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)
js/compressed-token/tests/program-test/decompress-delegated.test.ts (1)
js/program-test/src/spl-token-utils.ts (1)
splCreateAssociatedTokenAccount
(75-110)
js/program-test/src/merkle-tree/merkle-tree.ts (1)
js/program-test/src/test-rpc/test-rpc.ts (1)
LightWasm
(63-68)
js/program-test/tests/transfer.test.ts (3)
js/program-test/src/litesvm-rpc.ts (2)
LiteSVMRpc
(22-889)createLiteSVMRpc
(894-900)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)
js/program-test/src/test-rpc/get-compressed-token-accounts.ts (7)
js/stateless.js/src/rpc-interface.ts (2)
ParsedTokenAccount
(236-239)WithCursor
(250-255)js/stateless.js/src/state/types.ts (2)
CompressedAccountLegacy
(146-163)PublicTransactionEvent
(231-272)js/stateless.js/src/constants.ts (2)
COMPRESSED_TOKEN_PROGRAM_ID
(256-258)featureFlags
(16-35)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
getStateTreeInfoByPubkey
(8-13)js/stateless.js/src/state/compressed-account.ts (2)
MerkleContext
(80-97)createCompressedAccountWithMerkleContextLegacy
(240-253)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/test-rpc/get-parsed-events.ts (1)
getParsedEvents
(34-140)
js/compressed-token/tests/program-test/compress.test.ts (11)
js/stateless.js/src/rpc-interface.ts (1)
ParsedTokenAccount
(236-239)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)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/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
selectStateTreeInfo
(84-110)js/program-test/src/spl-token-utils.ts (1)
splCreateAssociatedTokenAccount
(75-110)js/compressed-token/src/program.ts (1)
CompressedTokenProgram
(600-1659)js/stateless.js/src/utils/dedupe-signer.ts (1)
dedupeSigner
(4-11)js/stateless.js/src/utils/send-and-confirm.ts (2)
buildAndSignTx
(139-160)sendAndConfirmTx
(51-62)
js/program-test/src/types.ts (1)
js/program-test/src/index.ts (2)
CustomProgram
(14-14)LiteSVMConfig
(14-14)
js/compressed-token/tests/program-test/compress-spl-token-account.test.ts (2)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)js/program-test/src/spl-token-utils.ts (2)
splCreateAssociatedTokenAccount
(75-110)splMintTo
(115-142)
js/compressed-token/tests/program-test/mint-to.test.ts (6)
cli/test/helpers/helpers.ts (1)
TEST_TOKEN_DECIMALS
(69-69)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/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)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/program-test/rollup.config.js (1)
js/stateless.js/rollup.config.js (2)
rolls
(11-68)typesConfig
(70-74)
js/program-test/tests/rpc-multi-trees.test.ts (7)
js/stateless.js/src/rpc.ts (2)
Rpc
(551-1892)createRpc
(231-264)js/program-test/src/test-rpc/test-rpc.ts (2)
TestRpc
(114-928)getTestRpc
(85-104)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/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-14)js/stateless.js/src/utils/address.ts (2)
deriveAddressSeed
(11-18)deriveAddress
(29-46)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)
js/program-test/src/merkle-tree/indexed-array.ts (3)
js/program-test/src/test-rpc/test-rpc.ts (1)
LightWasm
(63-68)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/stateless.js/src/constants.ts (1)
HIGHEST_ADDRESS_PLUS_ONE
(48-50)
js/compressed-token/tests/program-test/create-token-pool.test.ts (1)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)
js/compressed-token/tests/program-test/approve-and-mint-to.test.ts (9)
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/program-test/src/spl-token-utils.ts (1)
splGetOrCreateAssociatedTokenAccount
(246-294)js/compressed-token/src/program.ts (1)
CompressedTokenProgram
(600-1659)js/stateless.js/src/utils/dedupe-signer.ts (1)
dedupeSigner
(4-11)js/stateless.js/src/utils/send-and-confirm.ts (2)
buildAndSignTx
(139-160)sendAndConfirmTx
(51-62)js/stateless.js/src/utils/conversion.ts (1)
toArray
(28-29)js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)
js/program-test/tests/rpc-interop.test.ts (8)
js/stateless.js/src/rpc.ts (2)
Rpc
(551-1892)createRpc
(231-264)js/program-test/src/test-rpc/test-rpc.ts (2)
TestRpc
(114-928)getTestRpc
(85-104)js/stateless.js/src/state/types.ts (1)
TreeInfo
(77-99)js/program-test/src/test-utils.ts (2)
newAccountWithLamports
(7-14)sleep
(19-21)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/stateless.js/src/utils/address.ts (2)
deriveAddressSeed
(11-18)deriveAddress
(29-46)js/stateless.js/src/constants.ts (2)
featureFlags
(16-35)getDefaultAddressTreeInfo
(221-229)
js/program-test/src/test-rpc/test-rpc.ts (9)
js/stateless.js/src/constants.ts (3)
defaultTestStateTreeAccounts
(236-244)localTestActiveStateTreeInfos
(151-219)batchAddressTree
(304-304)js/stateless.js/src/rpc-interface.ts (16)
CompressionApiInterface
(762-867)GetCompressedAccountsByOwnerConfig
(45-50)WithCursor
(250-255)LatestNonVotingSignaturesPaginated
(57-67)LatestNonVotingSignatures
(33-43)GetCompressedTokenAccountsByOwnerOrDelegateOptions
(205-209)ParsedTokenAccount
(236-239)WithContext
(241-248)TokenBalance
(210-210)SignatureWithMetadata
(69-73)CompressedTransaction
(120-142)PaginatedOptions
(217-220)CompressedMintTokenHolders
(52-55)HashWithTree
(103-107)AddressWithTree
(109-113)ValidityProofWithContext
(161-190)js/stateless.js/src/state/types.ts (2)
TreeInfo
(77-99)PublicTransactionEvent
(231-272)js/program-test/src/merkle-tree/indexed-array.ts (2)
hash
(24-35)IndexedArray
(58-307)js/program-test/src/test-rpc/get-compressed-accounts.ts (4)
getCompressedAccountsForTest
(42-96)getCompressedAccountByHashTest
(23-29)getMultipleCompressedAccountsByHashTest
(31-39)getCompressedAccountsByOwnerTest
(14-21)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/test-rpc/get-parsed-events.ts (1)
getParsedEvents
(34-140)js/program-test/src/merkle-tree/merkle-tree.ts (2)
MerkleTree
(13-214)root
(74-78)js/stateless.js/src/rpc.ts (4)
MerkleContextWithNewAddressProof
(402-412)convertMerkleProofsWithContextToHex
(424-442)proverRequest
(336-390)convertNonInclusionMerkleProofInputsToHex
(444-470)
js/compressed-token/tests/program-test/decompress.test.ts (1)
js/program-test/src/spl-token-utils.ts (1)
splCreateAssociatedTokenAccount
(75-110)
js/compressed-token/tests/program-test/create-mint.test.ts (1)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(894-900)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: stateless-js-v1
- GitHub Check: cli-v2
- GitHub Check: stateless-js-v2
- GitHub Check: cli-v1
🔇 Additional comments (26)
js/stateless.js/rollup.config.js (2)
41-61
: Ensure consistent minification strategy across Rollup configs.Stateless applies terser only for CJS, compressed-token applies it for all formats, and program-test omits it entirely. Confirm whether this divergence is intentional; if not, align the configs or document the differences clearly.
80-80
: Package.json exports updated: now include ES/node build at./dist/es/node/index.js
.js/program-test/src/types.ts (1)
1-27
: Interfaces look goodType surface is clear and matches usage. No issues spotted.
js/program-test/README.md (1)
1-177
: Excellent documentation for the new package!The README provides comprehensive coverage of features, installation, usage patterns, testing workflows, API surface, and development commands. The clear distinction between unit tests (LiteSVM-based) and E2E tests (requiring test validator) helps users understand when and how to use each testing approach.
js/compressed-token/tests/program-test/create-mint.test.ts (1)
1-112
: Clean migration to program-test infrastructure.The test successfully migrates to the new program-test package, using
createLiteSVMRpc
andnewAccountWithLamports
from@lightprotocol/program-test
. The test logic is sound, with proper setup inbeforeAll
and comprehensive assertions for mint creation and pool account validation.js/program-test/src/test-rpc/index.ts (1)
1-3
: LGTM!Standard barrel export pattern that consolidates the test-rpc module's public API surface.
js/program-test/tsconfig.json (1)
1-21
: LGTM!Solid TypeScript configuration with strict mode enabled, declaration files, and appropriate module resolution for a Node.js library package.
js/program-test/package.json (2)
64-83
: Well-organized test and build scripts.The script organization clearly separates unit tests (LiteSVM-based), E2E tests (requiring validator), and version-specific test runs. The naming convention is consistent and intuitive.
31-40
: All pinned dependency versions are valid and secure. @lightprotocol/hasher.rs 0.2.1 exists; @solana/web3.js 1.98.4 includes patches for all known advisories; litesvm ^0.3.3 is current and has no vulnerabilities.js/compressed-token/tests/program-test/decompress-delegated.test.ts (1)
18-22
: LGTM! Clean migration to program-test utilities.The changes correctly migrate from
getTestRpc
tocreateLiteSVMRpc
and adopt the newsplCreateAssociatedTokenAccount
wrapper from the program-test package. The imports are well-organized and the test logic remains unchanged.Also applies to: 113-113, 133-133
js/program-test/rollup.config.js (1)
1-50
: LGTM! Appropriate Rollup configuration for test utilities.The configuration is simpler than the stateless.js setup, which is suitable for a test utilities package. The external dependencies list is comprehensive, and the lack of minification/terser is appropriate for development tooling.
sdk-tests/sdk-anchor-test/package.json (1)
9-9
: LGTM! Dependencies correctly updated for program-test migration.The package correctly adds the new program-test dependency and hasher.rs, and simplifies the test script. The additions align with the broader PR migration to LiteSVM-backed testing.
Also applies to: 14-15, 23-23
js/compressed-token/tests/program-test/delegate.test.ts (1)
11-14
: LGTM! Consistent migration to program-test.The RPC initialization and imports are correctly updated to use the program-test utilities. The changes follow the same pattern as other migrated test files.
Also applies to: 119-119
js/compressed-token/tests/program-test/decompress.test.ts (1)
14-18
: LGTM! Complete migration to program-test utilities.All three aspects of the migration are correctly implemented: imports, RPC initialization, and token account creation helper. The changes maintain test functionality while adopting the new testing infrastructure.
Also applies to: 83-83, 103-103
js/compressed-token/tests/program-test/transfer-delegated.test.ts (1)
11-14
: LGTM! Standard migration to program-test.The changes correctly update the RPC initialization and imports. The migration follows the established pattern across all test files.
Also applies to: 182-182
js/compressed-token/tests/program-test/mint-to.test.ts (1)
1-188
: LGTM! Well-structured test suite.The new mint-to test comprehensively covers:
- Single recipient minting
- Multiple recipient scenarios (3 and 10 recipients)
- Error handling for incorrect authority
- Error handling for mismatched amounts/recipients arrays
The test correctly uses the program-test utilities and follows established patterns.
js/compressed-token/tests/program-test/transfer.test.ts (1)
19-22
: LGTM! Consistent migration across both test suites.Both test suites in this file correctly adopt the program-test utilities. The RPC initialization is properly updated in both
beforeAll
hooks, and the imports are clean and organized.Also applies to: 105-105, 302-302
js/program-test/tests/merkle-tree.test.ts (1)
98-183
: LGTM! Comprehensive integration test.The integration test thoroughly validates the IndexedArray and MerkleTree interaction by:
- Initializing an indexed array
- Performing three sequential appends with different values (30, 42, 12)
- Verifying the Merkle tree root matches expected reference values after each operation
This demonstrates proper ordering and hash computation in the indexed structure.
js/compressed-token/tests/program-test/approve-and-mint-to.test.ts (1)
147-149
: LGTM! Proper migration to LiteSVM.The test correctly initializes the RPC using
createLiteSVMRpc(lightWasm)
for LiteSVM-backed testing, consistent with the PR's migration strategy.js/compressed-token/tests/program-test/multi-pool.test.ts (2)
23-28
: LGTM! Proper migration to program-test abstractions.The import changes correctly replace direct SPL token calls with program-test equivalents, aligning with the PR's consolidation strategy for LiteSVM-based testing.
87-114
: LGTM! Consistent use of program-test utilities.The migration properly updates:
- RPC initialization to use
createLiteSVMRpc
- Explicit lamport amounts (1e9) for account funding
- Token operations to use
spl*
prefixed wrappers from program-testThis ensures consistency across the test suite.
js/program-test/src/index.ts (1)
8-17
: Public surface looks coherentExports cover LiteSVM RPC, test utils, types, and helper modules. LGTM.
js/compressed-token/tests/program-test/compress-spl-token-account.test.ts (2)
44-45
: Good: moved to createLiteSVMRpc for local testsUsing LiteSVM-backed RPC here aligns with program-test approach.
340-348
: Verify mintTo argument list consistencyIn earlier calls mintTo includes tokenPoolInfo; here it’s omitted for Token 2022 path. If mintTo requires pool info for Token 2022 as well, add tokenPoolInfoT22.
Please confirm mintTo’s signature for Token 2022. If required, update to:
await mintTo(rpc, payer, mint, alice.publicKey, mintAuthority, bn(1000), stateTreeInfo, tokenPoolInfoT22);js/compressed-token/package.json (1)
46-46
: LGTM! Well-structured test integration.The addition of the program-test dependency and comprehensive test scripts follows monorepo best practices. The nx configuration properly declares build and test dependencies.
Also applies to: 82-99, 144-152
js/program-test/src/spl-token-utils.ts (1)
67-67
: Matchrpc.sendTransaction
calls to its declared signature instead of usingas any
. Remove allas any
casts onrpc.sendTransaction
(lines 67, 107, 141, 177, 291) and update each call to pass the correct parameters/return type as defined in theRpc
interface of@lightprotocol/stateless.js
.
return { | ||
slot: tx.slot, | ||
blockTime: tx.blockTime, | ||
transaction: { | ||
message: { | ||
accountKeys, | ||
compiledInstructions, | ||
recentBlockhash: | ||
message.recentBlockhash || message.header?.recentBlockhash || "", | ||
addressTableLookups: message.addressTableLookups || [], | ||
}, | ||
signatures: | ||
"signatures" in rawTx ? rawTx.signatures : [(rawTx as any).signature], | ||
}, | ||
meta: { | ||
err: null, | ||
logMessages: tx.logs, | ||
innerInstructions: tx.innerInstructions || [], | ||
preBalances: [], | ||
postBalances: [], | ||
preTokenBalances: [], | ||
postTokenBalances: [], | ||
rewards: [], | ||
fee: 5000, | ||
}, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getTransaction returns non-spec “signatures”
You’re returning Transaction.signatures (objects) for legacy txs. JSON-RPC getTransaction expects base58 signature strings.
@@
- transaction: {
+ transaction: {
message: {
@@
},
- signatures:
- "signatures" in rawTx ? rawTx.signatures : [(rawTx as any).signature],
+ signatures: [sigString],
},
Committable suggestion skipped: line range outside the PR's diff.
import { | ||
Rpc, | ||
createRpc, | ||
LightSystemProgram, | ||
TreeInfo, | ||
bn, | ||
compress, | ||
createAccount, | ||
createAccountWithLamports, | ||
deriveAddress, | ||
deriveAddressSeed, | ||
featureFlags, | ||
getDefaultAddressTreeInfo, | ||
selectStateTreeInfo, | ||
sleep, | ||
transfer, | ||
newAccountWithLamports, | ||
} from "@lightprotocol/stateless.js"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Wrong source and arity for newAccountWithLamports (and sleep import)
newAccountWithLamports in program-test takes (rpc, lamports). Here it’s imported from stateless.js and called with 3 args, causing TS error.
Apply:
@@
-import {
- Rpc,
- createRpc,
- LightSystemProgram,
- TreeInfo,
- bn,
- compress,
- createAccount,
- createAccountWithLamports,
- deriveAddress,
- deriveAddressSeed,
- featureFlags,
- getDefaultAddressTreeInfo,
- selectStateTreeInfo,
- sleep,
- transfer,
- newAccountWithLamports,
-} from "@lightprotocol/stateless.js";
+import {
+ Rpc,
+ createRpc,
+ LightSystemProgram,
+ TreeInfo,
+ bn,
+ compress,
+ createAccount,
+ createAccountWithLamports,
+ deriveAddress,
+ deriveAddressSeed,
+ featureFlags,
+ getDefaultAddressTreeInfo,
+ selectStateTreeInfo,
+ transfer,
+} from "@lightprotocol/stateless.js";
+import { getTestRpc, TestRpc, newAccountWithLamports, sleep } from "../src";
@@
-import { getTestRpc, TestRpc } from "../src";
+// moved above
And fix calls:
@@
- payer = await newAccountWithLamports(rpc, 10e9, 256);
- bob = await newAccountWithLamports(rpc, 10e9, 256);
+ payer = await newAccountWithLamports(rpc, 10e9);
+ bob = await newAccountWithLamports(rpc, 10e9);
Also applies to: 21-24
🏁 Script executed:
#!/bin/bash
# Verify exported symbols and signatures
rg -n --type=ts "export (async )?function newAccountWithLamports" -C2
rg -n --type=ts "export async function sleep" -C2
rg -n --type=ts "from [\"']@lightprotocol/stateless.js[\"']" js | rg -n "newAccountWithLamports|sleep" -C2
Length of output: 1108
Import and call fixes for newAccountWithLamports and sleep
- Remove
newAccountWithLamports
andsleep
from the@lightprotocol/stateless.js
import and instead import them from../src
alongsidegetTestRpc
/TestRpc
. - Update calls to drop the third argument:
payer = await newAccountWithLamports(rpc, 10e9); bob = await newAccountWithLamports(rpc, 10e9);
[js/program-test/tests/rpc-interop.test.ts:3–20]
🤖 Prompt for AI Agents
In js/program-test/tests/rpc-interop.test.ts around lines 3 to 20, the test
currently imports newAccountWithLamports and sleep from
"@lightprotocol/stateless.js" and calls newAccountWithLamports with three
arguments; update the imports to remove newAccountWithLamports and sleep from
the stateless package and instead import newAccountWithLamports and sleep from
../src alongside getTestRpc/TestRpc, then update all calls to
newAccountWithLamports to pass only (rpc, 10e9) (i.e., drop the third argument)
and ensure any usage of sleep is now using the relocated import.
const lightWasm = await WasmFactory.getInstance(); | ||
rpc = createRpc(); | ||
|
||
testRpc = await getTestRpc(lightWasm); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use TestRpc as the Rpc under test (avoid external createRpc())
createRpc() targets external endpoints; getTestRpc(lightWasm) provides the in‑process LiteSVM harness these tests rely on. Currently testRpc is created but unused.
- const lightWasm = await WasmFactory.getInstance();
- rpc = createRpc();
-
- testRpc = await getTestRpc(lightWasm);
+ const lightWasm = await WasmFactory.getInstance();
+ rpc = await getTestRpc(lightWasm);
Optionally remove testRpc variable if unused elsewhere.
📝 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.
const lightWasm = await WasmFactory.getInstance(); | |
rpc = createRpc(); | |
testRpc = await getTestRpc(lightWasm); | |
const lightWasm = await WasmFactory.getInstance(); | |
rpc = await getTestRpc(lightWasm); |
🤖 Prompt for AI Agents
In js/program-test/tests/rpc-multi-trees.test.ts around lines 34 to 38, the test
creates both rpc = createRpc() (which targets external endpoints) and testRpc =
await getTestRpc(lightWasm) (the in-process LiteSVM harness), but only
createRpc() is used; replace uses of the external rpc with the in-process
testRpc so the tests run against the local harness, or if the external rpc is
not needed remove the createRpc() call and use testRpc everywhere; ensure any
imports/typed variables refer to testRpc and delete the unused rpc variable.
}); | ||
|
||
const numberOfTransfers = 10; | ||
it(`should send compressed lamports alice -> bob for ${numberOfTransfers} transfers in a loop`, async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test description refers to "alice" but uses "payer" variable.
The test description mentions "alice -> bob" but the actual variables are payer
and bob
. Update the description for consistency.
- it(`should send compressed lamports alice -> bob for ${numberOfTransfers} transfers in a loop`, async () => {
+ it(`should send compressed lamports payer -> bob for ${numberOfTransfers} transfers in a loop`, async () => {
📝 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.
it(`should send compressed lamports alice -> bob for ${numberOfTransfers} transfers in a loop`, async () => { | |
it(`should send compressed lamports payer -> bob for ${numberOfTransfers} transfers in a loop`, async () => { |
🤖 Prompt for AI Agents
In js/program-test/tests/transfer.test.ts around line 22, the test description
mentions "alice -> bob" but the test uses variables named `payer` and `bob`;
update the it(...) description string to reference "payer -> bob" (or change
`payer` to `alice` consistently) so the description matches the variables used;
keep the rest of the test unchanged.
bn, | ||
CompressedAccountWithMerkleContext, | ||
createRpc, | ||
type CompressedAccountWithMerkleContext, | ||
defaultTestStateTreeAccounts, | ||
deriveAddressSeedV2, | ||
deriveAddressV2, | ||
PackedAccounts, | ||
Rpc, | ||
sleep, | ||
type Rpc, | ||
SystemAccountMetaConfig, | ||
} from "@lightprotocol/stateless.js"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type import missing and magic numbers for TreeType
- Program without importing SdkAnchorTest will not compile.
- Using 2/3 for treeType is brittle; use TreeType enum.
@@
-import {
- bn,
- type CompressedAccountWithMerkleContext,
- defaultTestStateTreeAccounts,
- deriveAddressSeedV2,
- deriveAddressV2,
- PackedAccounts,
- type Rpc,
- SystemAccountMetaConfig,
-} from "@lightprotocol/stateless.js";
+import {
+ bn,
+ type CompressedAccountWithMerkleContext,
+ deriveAddressSeedV2,
+ deriveAddressV2,
+ PackedAccounts,
+ type Rpc,
+ SystemAccountMetaConfig,
+ TreeType,
+} from "@lightprotocol/stateless.js";
+import type { SdkAnchorTest } from "../target/types/sdk_anchor_test";
@@
- const stateTreeInfo = existingTreeInfos.find(
- (info) => info.treeType === 2 || info.treeType === 3
- ); // StateV1 or StateV2
+ const stateTreeInfo = existingTreeInfos.find(
+ (info) =>
+ info.treeType === TreeType.StateV1 || info.treeType === TreeType.StateV2,
+ ); // StateV1 or StateV2
Also consider importing SdkAnchorTest at the top and keep Program as-is.
Also applies to: 37-51
🤖 Prompt for AI Agents
In sdk-tests/sdk-anchor-test/tests/test_v2.ts around lines 5 to 13 (and also
check 37 to 51), the file is missing the SdkAnchorTest type import and uses
magic numbers (2/3) for treeType; add an import for SdkAnchorTest from the
appropriate module (e.g., "@lightprotocol/stateless.js") and replace literal 2
and 3 with the TreeType enum values (e.g., TreeType.SomethingA /
TreeType.SomethingB) so Program<SdkAnchorTest> compiles and treeType is robust;
update the other occurrences in lines 37–51 similarly.
cf7f1f6
to
f848318
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (5)
cli/package.json
(2 hunks)cli/test/commands/approve-and-mint-to/index.test.ts
(1 hunks)cli/test/commands/create-token-pool/index.test.ts
(1 hunks)cli/test/helpers/helpers.ts
(1 hunks)js/program-test/eslint.config.cjs
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
- GitHub Check: stateless-js-v1
- GitHub Check: cli-v2
- GitHub Check: stateless-js-v2
- GitHub Check: cli-v1
- GitHub Check: lint
🔇 Additional comments (5)
cli/package.json (2)
74-74
: LGTM: Dependency addition is correct.The devDependency addition is appropriate since program-test is used only in test files.
173-174
: LGTM: CI build dependency properly configured.Adding
@lightprotocol/program-test:build
to the build-ci dependsOn list ensures the program-test package is built before CLI tests execute, preventing build-time errors.cli/test/commands/approve-and-mint-to/index.test.ts (1)
8-8
: LGTM: Consistent import migration.The import change matches the pattern used in other test files and maintains the same usage of
getTestRpc
.cli/test/helpers/helpers.ts (1)
16-16
: LGTM: Shared helper migration is correct.This helper file is used across multiple CLI tests (create-token-pool, approve-and-mint-to, etc.), so migrating the import here ensures consistency. All three usages of
getTestRpc
(lines 27, 35, 56) maintain the same signature.cli/test/commands/create-token-pool/index.test.ts (1)
7-7
: Approve import migration from stateless.js to program-test
getTestRpc
is correctly exported by@lightprotocol/program-test
(src/test-rpc/test-rpc.ts:85), and no legacy imports remain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (3)
js/program-test/src/test-rpc/test-rpc.ts (1)
288-404
: Leaf ↔ index bookkeeping still breaks proofs for sparse historiesWe still materialize each tree by pushing hashes into a dense array (Lines 309‑321) and then rebuilding the Merkle tree directly from that array (Lines 331‑338). Whenever
outputLeafIndices
jump—e.g. the indexer only fetched the latest ~1 000 txs but on-chain indices are already at 20 000—this drops every gap on the floor. The Merkle tree we rebuild no longer matches the on-chain layout, the root we hand back is wrong, and the computed path indices/path elements don’t line up with the real leaf index. Downstream inclusion proofs (and the prover request) are invalid in exactly the scenarios we use this helper for.Please keep the hash ↔ on-chain index pairing intact and rebuild the tree at the correct positions (e.g. store a
Map<leafIndex, hash>
, instantiate an empty MerkleTree, then callupdate(leafIndex, value)
/materialize a zero-filled array up to the highest index before hashing). Without that,getMultipleCompressedAccountProofs
still returns bogus proofs for the common sparse-history case.js/program-test/src/litesvm-rpc.ts (1)
452-466
: Return JSON-RPC compliant transaction fieldsWe’re still returning the raw
Transaction.signatures
/VersionedTransaction.signatures
objects andPublicKey
instances here. JSON-RPC clients expect base58 signature strings plus address-key indices and instruction data serialized as base64. As written this breaks any consumer that deserializes according to the RPC spec (same blocker I pointed out earlier).Tighten the payload to strings and plain numbers before storing:
transaction: { - message: { - accountKeys, - compiledInstructions, + message: { + accountKeys: accountKeys.map((key) => key.toBase58()), + compiledInstructions: compiledInstructions.map((ix: any) => { + const accountIndexes = + "accountKeyIndexes" in ix ? ix.accountKeyIndexes : ix.accounts; + return { + programIdIndex: ix.programIdIndex, + accounts: Array.from(accountIndexes ?? []), + data: Buffer.from(ix.data ?? []).toString("base64"), + }; + }), recentBlockhash: message.recentBlockhash || message.header?.recentBlockhash || "", addressTableLookups: message.addressTableLookups || [], }, - signatures: - "signatures" in rawTx ? rawTx.signatures : [(rawTx as any).signature], + signatures: [tx.signature], },This keeps the stored shape compatible with Solana’s RPC schema and avoids leaking Buffer objects.
js/program-test/src/merkle-tree/indexed-array.ts (1)
203-246
: Preserve the original nextIndex when appending
newElementWithLowElementIndex
mutateslowElement.nextIndex
before we reach theif (lowElement.nextIndex === 0)
check, so the branch never fires andhighestElementIndex
stays stale. On the very next insert of a larger value we attempt to wedge it between the old low element and its new successor, hitvalue.gte(nextElement.value)
, and throw. Capture the originalnextIndex
first and base both the ordering check and highest-element update on that snapshot:- const lowElement = this.elements[lowElementIndex]; - - if (lowElement.nextIndex === 0) { - if (value.lte(lowElement.value)) { - throw new Error( - "New element value must be greater than the low element value.", - ); - } - } else { - const nextElement = this.elements[lowElement.nextIndex]; - - if (value.lte(lowElement.value)) { - throw new Error( - "New element value must be greater than the low element value.", - ); - } - - if (value.gte(nextElement.value)) { - throw new Error( - "New element value must be less than the next element value.", - ); - } - } + const lowElement = this.elements[lowElementIndex]; + const originalNextIndex = lowElement.nextIndex; + + if (value.lte(lowElement.value)) { + throw new Error( + "New element value must be greater than the low element value.", + ); + } + + if (originalNextIndex !== 0) { + const nextElement = this.elements[originalNextIndex]; + + if (value.gte(nextElement.value)) { + throw new Error( + "New element value must be less than the next element value.", + ); + } + } @@ - if (lowElement.nextIndex === 0) { + if (originalNextIndex === 0) { this.highestElementIndex = newElementBundle.newElement.index; }This keeps
highestElementIndex
in sync and restores the Rust parity.
🧹 Nitpick comments (3)
js/compressed-token/vitest.config.ts (1)
7-15
: LGTM! Consider making maxForks configurable.The fork-based pool configuration is appropriate for handling native modules like LiteSVM, which are not thread-safe. The comments clearly explain the rationale, and the resource limits (maxForks: 4, minForks: 1) are reasonable defaults.
For improved flexibility across different environments (CI vs. local, varying CPU counts), consider making
maxForks
configurable via an environment variable:poolOptions: { forks: { - maxForks: 4, + maxForks: parseInt(process.env.MAX_FORKS || '4', 10), minForks: 1, }, },This allows developers to adjust parallelism based on their system resources without modifying the config.
js/program-test/src/test-rpc/get-parsed-events.ts (2)
191-202
: Remove unnecessary mapping operation.Line 194 uses
data.map((byte) => byte)
which creates an identical copy of the data. SinceBuffer.from()
already accepts the input Buffer directly, the mapping is redundant.Apply this diff:
export const parsePublicTransactionEventWithIdl = ( data: Buffer, ): PublicTransactionEvent | null => { - const numericData = Buffer.from(data.map((byte) => byte)); - try { - return decodePublicTransactionEvent(numericData); + return decodePublicTransactionEvent(data); } catch (error) {
204-266
: Consider simplifying the final null check.The logic correctly parses system instructions and extracts invoke/append data. However, the null check at line 257 is redundant since line 241 already returns null if no system instruction was found, guaranteeing
invokeData
is non-null beyond that point.Optionally simplify by removing the redundant check:
if (invokeData) { return convertToPublicTransactionEvent( appendInputsData, accountKeys[accountKeys.length - 1], invokeData, ); - } else { - return null; } + return null;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
js/compressed-token/tests/program-test/compress.test.ts
(1 hunks)js/compressed-token/vitest.config.ts
(1 hunks)js/program-test/src/litesvm-rpc.ts
(1 hunks)js/program-test/src/merkle-tree/indexed-array.ts
(1 hunks)js/program-test/src/spl-token-utils.ts
(1 hunks)js/program-test/src/test-rpc/get-parsed-events.ts
(1 hunks)js/program-test/src/test-rpc/test-rpc.ts
(1 hunks)js/program-test/src/test-utils.ts
(1 hunks)js/stateless.js/src/utils/send-and-confirm.ts
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
js/stateless.js/src/utils/send-and-confirm.ts (1)
forester-utils/src/rpc_pool.rs (1)
commitment
(152-155)
js/program-test/src/litesvm-rpc.ts (3)
js/program-test/src/test-rpc/test-rpc.ts (1)
TestRpc
(114-934)js/program-test/src/types.ts (1)
LiteSVMConfig
(16-27)js/stateless.js/src/constants.ts (1)
defaultTestStateTreeAccounts
(236-244)
js/program-test/src/test-utils.ts (1)
js/program-test/src/index.ts (3)
newAccountWithLamports
(10-10)sleep
(11-11)getOrCreateKeypair
(12-12)
js/program-test/src/test-rpc/get-parsed-events.ts (3)
js/stateless.js/src/constants.ts (8)
defaultStaticAccountsStruct
(95-105)accountCompressionProgram
(76-77)COMPUTE_BUDGET_PATTERN
(52-52)noopProgram
(74-74)INVOKE_DISCRIMINATOR
(54-56)INVOKE_CPI_DISCRIMINATOR
(58-60)INVOKE_CPI_WITH_READ_ONLY_DISCRIMINATOR
(62-64)INSERT_INTO_QUEUES_DISCRIMINATOR
(70-72)js/stateless.js/src/programs/system/layout.ts (5)
decodeInstructionDataInvoke
(240-246)decodeInstructionDataInvokeCpi
(248-254)decodeInstructionDataInvokeCpiWithReadOnly
(234-238)deserializeAppendNullifyCreateAddressInputsIndexer
(441-514)convertToPublicTransactionEvent
(516-598)js/stateless.js/src/utils/conversion.ts (1)
convertInvokeCpiWithReadOnlyToInvoke
(121-191)
js/program-test/src/test-rpc/test-rpc.ts (11)
js/stateless.js/src/constants.ts (3)
defaultTestStateTreeAccounts
(236-244)localTestActiveStateTreeInfos
(151-219)batchAddressTree
(304-304)js/stateless.js/src/rpc-interface.ts (7)
CompressionApiInterface
(762-867)GetCompressedAccountsByOwnerConfig
(45-50)WithCursor
(250-255)WithContext
(241-248)HashWithTree
(103-107)AddressWithTree
(109-113)ValidityProofWithContext
(161-190)js/stateless.js/src/state/types.ts (2)
TreeInfo
(77-99)PublicTransactionEvent
(231-272)js/program-test/src/merkle-tree/indexed-array.ts (2)
hash
(24-35)IndexedArray
(58-307)js/stateless.js/src/state/compressed-account.ts (2)
CompressedAccountWithMerkleContext
(14-17)MerkleContextWithMerkleProof
(102-115)js/program-test/src/test-rpc/get-compressed-accounts.ts (4)
getCompressedAccountsForTest
(42-96)getCompressedAccountByHashTest
(23-29)getMultipleCompressedAccountsByHashTest
(31-39)getCompressedAccountsByOwnerTest
(14-21)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/test-rpc/get-parsed-events.ts (1)
getParsedEvents
(34-144)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
getStateTreeInfoByPubkey
(8-13)js/program-test/src/merkle-tree/merkle-tree.ts (2)
MerkleTree
(13-214)root
(74-78)js/stateless.js/src/rpc.ts (4)
MerkleContextWithNewAddressProof
(402-412)convertMerkleProofsWithContextToHex
(424-442)proverRequest
(336-390)convertNonInclusionMerkleProofInputsToHex
(444-470)
js/compressed-token/tests/program-test/compress.test.ts (11)
js/stateless.js/src/rpc-interface.ts (1)
ParsedTokenAccount
(236-239)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)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/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(890-896)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-15)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
selectStateTreeInfo
(84-110)js/program-test/src/spl-token-utils.ts (1)
splCreateAssociatedTokenAccount
(75-110)js/compressed-token/src/program.ts (1)
CompressedTokenProgram
(600-1659)js/stateless.js/src/utils/dedupe-signer.ts (1)
dedupeSigner
(4-11)js/stateless.js/src/utils/send-and-confirm.ts (2)
buildAndSignTx
(145-166)sendAndConfirmTx
(51-62)
js/program-test/src/merkle-tree/indexed-array.ts (3)
js/program-test/src/test-rpc/test-rpc.ts (1)
LightWasm
(63-68)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/stateless.js/src/constants.ts (1)
HIGHEST_ADDRESS_PLUS_ONE
(48-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: stateless-js-v1
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
- GitHub Check: cli-v2
- GitHub Check: stateless-js-v2
- GitHub Check: lint
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
- GitHub Check: cli-v1
🔇 Additional comments (2)
js/program-test/src/test-rpc/get-parsed-events.ts (1)
146-188
: LGTM!The generic event parser correctly filters transactions, extracts NOOP program instructions, and applies the provided deserializer. The null checks and type guards are appropriate.
js/stateless.js/src/utils/send-and-confirm.ts (1)
100-119
: Early failure on transaction errors looks solid.Thanks for short‑circuiting on
result.err
and clarifying the commitment ladder—this prevents unnecessary polling and keeps the status check aligned with Solana’s confirmation hierarchy.
txRaw!.meta!.innerInstructions && | ||
txRaw!.meta!.innerInstructions.length > 0 | ||
) { | ||
for (const innerGroup of txRaw!.meta!.innerInstructions) { | ||
for (const ix of innerGroup.instructions) { | ||
const group = ix.accounts.map( | ||
(accountIdx: number) => allAccounts[accountIdx], | ||
); | ||
groupedAccountVec.push(group); | ||
if (ix.data && ix.data.length > 0) { | ||
const decodedData = bs58.decode(ix.data); | ||
dataVec.push(decodedData); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add null safety check for txRaw.
Using non-null assertions (txRaw!.meta!
) without verifying txRaw
is not null/undefined is unsafe. The code at line 71 retrieves txRaw
from an async call that can return null, and line 76 already uses optional chaining.
Apply this diff to add a safety check:
const groupedAccountVec: PublicKey[][] = [];
+ if (!txRaw || !txRaw.meta) {
+ continue;
+ }
+
if (
- txRaw!.meta!.innerInstructions &&
- txRaw!.meta!.innerInstructions.length > 0
+ txRaw.meta.innerInstructions &&
+ txRaw.meta.innerInstructions.length > 0
) {
- for (const innerGroup of txRaw!.meta!.innerInstructions) {
+ for (const innerGroup of txRaw.meta.innerInstructions) {
for (const ix of innerGroup.instructions) {
📝 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.
txRaw!.meta!.innerInstructions && | |
txRaw!.meta!.innerInstructions.length > 0 | |
) { | |
for (const innerGroup of txRaw!.meta!.innerInstructions) { | |
for (const ix of innerGroup.instructions) { | |
const group = ix.accounts.map( | |
(accountIdx: number) => allAccounts[accountIdx], | |
); | |
groupedAccountVec.push(group); | |
if (ix.data && ix.data.length > 0) { | |
const decodedData = bs58.decode(ix.data); | |
dataVec.push(decodedData); | |
} | |
} | |
} | |
} | |
const groupedAccountVec: PublicKey[][] = []; | |
if (!txRaw || !txRaw.meta) { | |
continue; | |
} | |
if ( | |
txRaw.meta.innerInstructions && | |
txRaw.meta.innerInstructions.length > 0 | |
) { | |
for (const innerGroup of txRaw.meta.innerInstructions) { | |
for (const ix of innerGroup.instructions) { | |
const group = ix.accounts.map( | |
(accountIdx: number) => allAccounts[accountIdx], | |
); | |
groupedAccountVec.push(group); | |
if (ix.data && ix.data.length > 0) { | |
const decodedData = bs58.decode(ix.data); | |
dataVec.push(decodedData); | |
} | |
} | |
} | |
} |
🤖 Prompt for AI Agents
In js/program-test/src/test-rpc/get-parsed-events.ts around lines 96 to 111, the
code uses non-null assertions txRaw! and txRaw!.meta! without verifying txRaw is
present; add an explicit null/undefined check for txRaw before accessing meta
(e.g., if (!txRaw) return or continue) and replace the forced non-null
assertions with safe optional chaining or guarded access so the
innerInstructions loop only runs when txRaw and txRaw.meta are defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
js/compressed-token/vitest.config.ts (1)
12-13
: Document the performance trade-off of sequential test execution.Setting both
maxForks
andminForks
to 1 eliminates test parallelism, which will significantly increase total test execution time. While this may be necessary for LiteSVM's native module constraints, consider documenting:
- The approximate performance impact (e.g., "tests now run sequentially, taking ~X minutes instead of ~Y minutes")
- Whether this limitation applies to all test files or could be scoped to specific LiteSVM-dependent tests
- Any plans to investigate parallel execution in the future
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (5)
cli/package.json
(2 hunks)js/compressed-token/package.json
(3 hunks)js/compressed-token/vitest.config.ts
(1 hunks)js/program-test/.prettierignore
(1 hunks)js/stateless.js/package.json
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- cli/package.json
- js/stateless.js/package.json
- js/program-test/.prettierignore
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: cli-v2
- GitHub Check: stateless-js-v1
- GitHub Check: lint
- GitHub Check: stateless-js-v2
- GitHub Check: cli-v1
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
🔇 Additional comments (1)
js/compressed-token/vitest.config.ts (1)
7-18
: Re-examine memoryLimit and isolation strategy
memoryLimit: 1
is in megabytes, so tests will recycle immediately upon any allocation, not necessarily between files—causing unpredictable restarts and overhead. Remove it or set a realistic threshold.- For true per-file isolation, use Vitest’s
isolate: true
option or document why the LiteSVM workaround is needed.
a11f54e
to
bff3d53
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (1)
js/program-test/src/test-rpc/test-rpc.ts (1)
527-530
: Refactor type coercion to avoidas any
cast.The bigint-to-BN conversion uses
as any
which bypasses TypeScript's type safety. Consider creating a typed helper function for this common pattern.Add a helper function at the top of the class or in a utility module:
function toBN(value: bigint | BN | number | string): BN { if (typeof value === 'bigint') { return bn(value.toString()); } return bn(value); }Then replace the three occurrences:
- const amountBN = typeof rawAmount === 'bigint' - ? bn((rawAmount as any).toString()) - : bn(rawAmount); + const amountBN = toBN(rawAmount);- const balance = typeof rawAmount === 'bigint' - ? bn((rawAmount as any).toString()) - : bn(rawAmount); + const balance = toBN(rawAmount);- const balance = typeof rawAmount === 'bigint' - ? bn((rawAmount as any).toString()) - : bn(rawAmount); + const balance = toBN(rawAmount);Also applies to: 552-554, 584-586
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
js/program-test/src/litesvm-rpc.ts
(1 hunks)js/program-test/src/spl-token-utils.ts
(1 hunks)js/program-test/src/test-rpc/test-rpc.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- js/program-test/src/litesvm-rpc.ts
🧰 Additional context used
🧬 Code graph analysis (1)
js/program-test/src/test-rpc/test-rpc.ts (9)
js/stateless.js/src/constants.ts (3)
defaultTestStateTreeAccounts
(236-244)localTestActiveStateTreeInfos
(151-219)batchAddressTree
(304-304)js/stateless.js/src/rpc-interface.ts (16)
CompressionApiInterface
(762-867)GetCompressedAccountsByOwnerConfig
(45-50)WithCursor
(250-255)LatestNonVotingSignaturesPaginated
(57-67)LatestNonVotingSignatures
(33-43)GetCompressedTokenAccountsByOwnerOrDelegateOptions
(205-209)ParsedTokenAccount
(236-239)WithContext
(241-248)TokenBalance
(210-210)SignatureWithMetadata
(69-73)CompressedTransaction
(120-142)PaginatedOptions
(217-220)CompressedMintTokenHolders
(52-55)HashWithTree
(103-107)AddressWithTree
(109-113)ValidityProofWithContext
(161-190)js/stateless.js/src/state/types.ts (2)
TreeInfo
(77-99)PublicTransactionEvent
(231-272)js/program-test/src/merkle-tree/indexed-array.ts (2)
hash
(24-35)IndexedArray
(58-307)js/program-test/src/test-rpc/get-compressed-accounts.ts (4)
getCompressedAccountsForTest
(42-96)getCompressedAccountByHashTest
(23-29)getMultipleCompressedAccountsByHashTest
(31-39)getCompressedAccountsByOwnerTest
(14-21)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/test-rpc/get-parsed-events.ts (1)
getParsedEvents
(34-144)js/program-test/src/merkle-tree/merkle-tree.ts (2)
MerkleTree
(13-214)root
(74-78)js/stateless.js/src/rpc.ts (4)
MerkleContextWithNewAddressProof
(402-412)convertMerkleProofsWithContextToHex
(424-442)proverRequest
(336-390)convertNonInclusionMerkleProofInputsToHex
(444-470)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
- GitHub Check: cli-v1
- GitHub Check: cli-v2
- GitHub Check: Forester e2e test
- GitHub Check: stateless-js-v1
- GitHub Check: stateless-js-v2
- GitHub Check: lint
🔇 Additional comments (7)
js/program-test/src/test-rpc/test-rpc.ts (7)
1-104
: LGTM!The imports, interface definitions, and factory function are well-structured and follow TypeScript best practices.
114-188
: LGTM!The class structure and basic methods are appropriate for a test mock. Deprecated methods are properly marked.
190-266
: LGTM!The account query methods appropriately delegate to test helpers and include proper error handling for the test context.
376-389
: LGTM! V2 leafIndex fix correctly implemented.The fix properly addresses the previous concern about returning actual on-chain leaf indices. The code now:
- Finds the array position via
findIndex
- Validates the position exists
- Retrieves the actual on-chain index from
leafIndices[arrayPosition]
This ensures that
proveByIndex
consumers receive valid on-chain positions rather than local array indices.
597-674
: LGTM!Stub implementations for signature query and health methods are appropriate for a test mock. Throwing errors for unsupported operations provides clear feedback during testing.
684-740
: LGTM! Documented limitation for test mock.The empty
allAddresses
array and TODO comment at lines 690-692 document that cranked address tree support is intentionally not implemented. The Merkle tree root doesn't advance beyondinit()
unless the address queue is emptied, which is acceptable for this test-only mock.
742-955
: LGTM! Validity proof implementation with documented limitations.The validity proof methods implement complex logic for combining inclusion and new-address proofs. The TODO comments at lines 916, 920-922, and 950 document known limitations regarding:
- Better solution needed for hasV1Accounts root handling
- Dynamic root indices for forester support
- Custom tree support in V0 method
These are reasonable constraints for a test mock focused on non-forested scenarios.
console.log('[spl-token-utils.ts:195] Converting amount:', typeof data.amount, data.amount); | ||
const amount = | ||
typeof data.amount === "bigint" | ||
? data.amount | ||
: BigInt((data.amount as any).toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove debug logging from the helpers.
The console.log
statements left in these helpers will spam every caller and make tests noisy. Please drop them before shipping.
Apply this diff to clean them up:
- console.log('[spl-token-utils.ts:195] Converting amount:', typeof data.amount, data.amount);
const amount =
typeof data.amount === "bigint"
? data.amount
: BigInt((data.amount as any).toString());
@@
- console.log('[spl-token-utils.ts:223] Converting supply:', typeof data.supply, data.supply);
-
const supply =
typeof data.supply === "bigint"
? data.supply
: BigInt((data.supply as any).toString());
Also applies to: 222-227
🤖 Prompt for AI Agents
In js/program-test/src/spl-token-utils.ts around lines 194-198 (and similarly
222-227), remove the leftover console.log debugging statements so helpers no
longer print during normal operation; simply delete the console.log lines and
leave the amount conversion logic unchanged (i.e., keep the typeof check and
BigInt conversion) ensuring no other behavior is modified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (7)
js/program-test/src/test-rpc/get-parsed-events.ts (1)
95-111
: Add null safety check for txRaw.The code uses non-null assertions (
txRaw!.meta!
) without verifyingtxRaw
is not null. Line 71 retrievestxRaw
fromgetTransaction
, which can return null.Apply this diff to add a safety check:
const groupedAccountVec: PublicKey[][] = []; + if (!txRaw || !txRaw.meta) { + continue; + } + if ( - txRaw!.meta!.innerInstructions && - txRaw!.meta!.innerInstructions.length > 0 + txRaw.meta.innerInstructions && + txRaw.meta.innerInstructions.length > 0 ) { - for (const innerGroup of txRaw!.meta!.innerInstructions) { + for (const innerGroup of txRaw.meta.innerInstructions) { for (const ix of innerGroup.instructions) { const group = ix.accounts.map( (accountIdx: number) => allAccounts[accountIdx],js/program-test/src/litesvm-rpc.ts (1)
459-484
: Convert transaction signatures to base58 strings.The
signatures
field at line 471 returnsTransaction.signatures
objects or raw signature arrays, but JSON-RPCgetTransaction
expects an array of base58-encoded signature strings. Legacy transactions havesignatures: Array<{signature: Buffer | null, publicKey: PublicKey}>
, while versioned transactions havesignatures: Uint8Array[]
. Both need conversion to base58 strings.Apply this diff to convert signatures to the correct format:
+ // Convert signatures to base58 strings + let signatures: string[]; + if ("signatures" in rawTx && Array.isArray(rawTx.signatures)) { + if (rawTx.signatures.length > 0) { + // Check if it's legacy (object with signature/publicKey) or versioned (Uint8Array) + const firstSig = rawTx.signatures[0]; + if (firstSig && typeof firstSig === 'object' && 'signature' in firstSig) { + // Legacy transaction signatures + signatures = rawTx.signatures + .map((sig: any) => sig.signature ? bs58.encode(sig.signature) : '') + .filter((s: string) => s !== ''); + } else { + // Versioned transaction signatures (Uint8Array[]) + signatures = rawTx.signatures.map((sig: Uint8Array) => bs58.encode(sig)); + } + } else { + signatures = []; + } + } else { + // Fallback to stored signature + signatures = [sigString]; + } + return { slot: tx.slot, blockTime: tx.blockTime, transaction: { message: { accountKeys, compiledInstructions, recentBlockhash: message.recentBlockhash || message.header?.recentBlockhash || "", addressTableLookups: message.addressTableLookups || [], }, - signatures: - "signatures" in rawTx ? rawTx.signatures : [(rawTx as any).signature], + signatures, },js/program-test/src/spl-token-utils.ts (2)
190-211
: Remove debug console.log statement.Lines 201-205 contain debug logging that should be removed before merging. This will spam logs during normal test execution.
Apply this diff:
const data = AccountLayout.decode(accountInfo.data); - console.log( - "[spl-token-utils.ts:195] Converting amount:", - typeof data.amount, - data.amount, - ); const amount = typeof data.amount === "bigint" ? data.amount : BigInt((data.amount as any).toString()); return amount;
216-259
: Remove debug console.log statement.Lines 233-237 contain debug logging that should be removed before merging.
Apply this diff:
const data = MintLayout.decode(accountInfo.data); - console.log( - "[spl-token-utils.ts:223] Converting supply:", - typeof data.supply, - data.supply, - ); - const supply = typeof data.supply === "bigint" ? data.supply : BigInt((data.supply as any).toString());js/program-test/src/test-rpc/test-rpc.ts (3)
522-531
: Remove debug console.log statement.Line 525 contains a debug log that should be removed before merging. This is a development artifact that will spam logs during test execution.
Apply this diff to remove the debug log:
const account = await getCompressedTokenAccountByHashTest(this, hash); const rawAmount = account.parsed.amount; - console.log('[test-rpc.ts:524] Converting amount:', typeof rawAmount, rawAmount); // Convert amount to BN first (could be bigint or BN from Borsh u64 decoder) const amountBN = typeof rawAmount === 'bigint' ? bn(String(rawAmount)) : bn(rawAmount); return { amount: amountBN };
538-562
: Remove debug console.log statement.Line 550 contains a debug log that should be removed before merging.
Apply this diff:
items: accounts.items.map((account) => { const rawAmount = account.parsed.amount; - console.log('[test-rpc.ts:543] Converting amount:', typeof rawAmount, rawAmount); // Convert amount to BN first (could be bigint or BN from Borsh u64 decoder) const balance = typeof rawAmount === 'bigint' ? bn(String(rawAmount)) : bn(rawAmount);
568-595
: Remove debug console.log statement.Line 582 contains a debug log that should be removed before merging.
Apply this diff:
items: accounts.items.map((account) => { const rawAmount = account.parsed.amount; - console.log('[test-rpc.ts:567] Converting amount:', typeof rawAmount, rawAmount); // Convert amount to BN first (could be bigint or BN from Borsh u64 decoder) const balance = typeof rawAmount === 'bigint' ? bn(String(rawAmount)) : bn(rawAmount);
🧹 Nitpick comments (2)
js/program-test/src/test-rpc/get-parsed-events.ts (1)
194-194
: Remove redundant Buffer mapping.The expression
Buffer.from(data.map((byte) => byte))
is redundant sincedata
is already a Buffer and mapping each byte to itself adds unnecessary overhead.Apply this diff:
- const numericData = Buffer.from(data.map((byte) => byte)); + const numericData = Buffer.from(data);js/program-test/src/test-rpc/test-rpc.ts (1)
411-424
: Document or explicitly handle StateV2 validation.The validation loop (lines 412-423) only validates StateV1 proofs and silently skips StateV2 proofs. Since StateV2 uses
proveByIndex: true
with zero-filled paths, this may be intentional. However, the silent skip makes the intent unclear.Consider adding an explicit comment or handling for StateV2:
// Validate proofs merkleProofsMap.forEach((proof, index) => { if (proof.treeInfo.treeType === TreeType.StateV1) { const leafIndex = proof.leafIndex; const computedHash = leavesByTree.get(proof.treeInfo.tree.toBase58())! .leaves[leafIndex]; const hashArr = bn(computedHash); if (!hashArr.eq(proof.hash)) { throw new Error( `Mismatch at index ${index}: expected ${proof.hash.toString()}, got ${hashArr.toString()}`, ); } + } else if (proof.treeInfo.treeType === TreeType.StateV2) { + // StateV2 uses proveByIndex with zero-filled paths - validation not required + // as the proof structure is deterministic and leaf index is verified via on-chain index } });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
js/program-test/src/litesvm-rpc.ts
(1 hunks)js/program-test/src/spl-token-utils.ts
(1 hunks)js/program-test/src/test-rpc/get-parsed-events.ts
(1 hunks)js/program-test/src/test-rpc/test-rpc.ts
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
js/program-test/src/litesvm-rpc.ts (4)
js/program-test/src/index.ts (3)
LiteSVMRpc
(8-8)LiteSVMConfig
(14-14)createLiteSVMRpc
(8-8)js/program-test/src/test-rpc/test-rpc.ts (1)
TestRpc
(114-956)js/program-test/src/types.ts (1)
LiteSVMConfig
(16-27)js/stateless.js/src/constants.ts (3)
defaultTestStateTreeAccounts
(236-244)COMPRESSED_TOKEN_PROGRAM_ID
(256-258)featureFlags
(16-35)
js/program-test/src/test-rpc/test-rpc.ts (9)
js/stateless.js/src/constants.ts (3)
defaultTestStateTreeAccounts
(236-244)localTestActiveStateTreeInfos
(151-219)batchAddressTree
(304-304)js/stateless.js/src/rpc-interface.ts (16)
CompressionApiInterface
(762-867)GetCompressedAccountsByOwnerConfig
(45-50)WithCursor
(250-255)LatestNonVotingSignaturesPaginated
(57-67)LatestNonVotingSignatures
(33-43)GetCompressedTokenAccountsByOwnerOrDelegateOptions
(205-209)ParsedTokenAccount
(236-239)WithContext
(241-248)TokenBalance
(210-210)SignatureWithMetadata
(69-73)CompressedTransaction
(120-142)PaginatedOptions
(217-220)CompressedMintTokenHolders
(52-55)HashWithTree
(103-107)AddressWithTree
(109-113)ValidityProofWithContext
(161-190)js/program-test/src/merkle-tree/indexed-array.ts (2)
hash
(24-35)IndexedArray
(58-307)js/program-test/src/test-rpc/get-compressed-accounts.ts (4)
getCompressedAccountsForTest
(42-96)getCompressedAccountByHashTest
(23-29)getMultipleCompressedAccountsByHashTest
(31-39)getCompressedAccountsByOwnerTest
(14-21)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/test-rpc/get-parsed-events.ts (1)
getParsedEvents
(34-144)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
getStateTreeInfoByPubkey
(8-13)js/program-test/src/merkle-tree/merkle-tree.ts (2)
MerkleTree
(13-214)root
(74-78)js/stateless.js/src/rpc.ts (4)
MerkleContextWithNewAddressProof
(402-412)convertMerkleProofsWithContextToHex
(424-442)proverRequest
(336-390)convertNonInclusionMerkleProofInputsToHex
(444-470)
js/program-test/src/test-rpc/get-parsed-events.ts (4)
js/stateless.js/src/state/types.ts (2)
PublicTransactionEvent
(231-272)InstructionDataInvoke
(277-306)js/stateless.js/src/constants.ts (8)
defaultStaticAccountsStruct
(95-105)accountCompressionProgram
(76-77)COMPUTE_BUDGET_PATTERN
(52-52)noopProgram
(74-74)INVOKE_DISCRIMINATOR
(54-56)INVOKE_CPI_DISCRIMINATOR
(58-60)INVOKE_CPI_WITH_READ_ONLY_DISCRIMINATOR
(62-64)INSERT_INTO_QUEUES_DISCRIMINATOR
(70-72)js/stateless.js/src/programs/system/layout.ts (6)
decodePublicTransactionEvent
(392-396)decodeInstructionDataInvoke
(240-246)decodeInstructionDataInvokeCpi
(248-254)decodeInstructionDataInvokeCpiWithReadOnly
(234-238)deserializeAppendNullifyCreateAddressInputsIndexer
(441-514)convertToPublicTransactionEvent
(516-598)js/stateless.js/src/utils/conversion.ts (1)
convertInvokeCpiWithReadOnlyToInvoke
(121-191)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: lint
- GitHub Check: cli-v2
- GitHub Check: stateless-js-v1
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
- GitHub Check: Forester e2e test
- GitHub Check: cli-v1
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: stateless-js-v2
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
🔇 Additional comments (1)
js/program-test/src/test-rpc/get-parsed-events.ts (1)
76-91
: LGTM! Base64 decoding issue resolved.The code now correctly handles both string (base64) and Uint8Array cases for
ix.data
. The string case is properly base64-decoded, and the Uint8Array case is used directly without incorrect re-encoding.
console.log("signatureBytes ", signatureBytes); | ||
const signature = bs58.encode(signatureBytes); | ||
console.log("bs58 signature ", signature); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove debug console.log statements.
Lines 324 and 326 contain debug logging that should be removed before merging. These are development artifacts that will spam logs during test execution.
Apply this diff to remove the debug logs:
const logs = result.logs();
const signatureBytes = result.signature();
- console.log("signatureBytes ", signatureBytes);
const signature = bs58.encode(signatureBytes);
- console.log("bs58 signature ", signature);
📝 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.
console.log("signatureBytes ", signatureBytes); | |
const signature = bs58.encode(signatureBytes); | |
console.log("bs58 signature ", signature); | |
const logs = result.logs(); | |
const signatureBytes = result.signature(); | |
const signature = bs58.encode(signatureBytes); |
🤖 Prompt for AI Agents
In js/program-test/src/litesvm-rpc.ts around lines 324 to 326, remove the two
debug console.log statements that print signatureBytes and the bs58 signature;
keep the bs58.encode(signatureBytes) assignment to signature and any necessary
surrounding code intact, and run lint/format to ensure no leftover trailing
whitespace or unused-variable warnings.
if (invokeData) { | ||
return convertToPublicTransactionEvent( | ||
appendInputsData, | ||
accountKeys[accountKeys.length - 1], | ||
invokeData, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Verify accountKeys is non-empty before accessing.
Line 260 accesses accountKeys[accountKeys.length - 1]
without verifying the array is non-empty. If no inner instructions were processed, accountKeys
could be empty, resulting in undefined
being passed to convertToPublicTransactionEvent
.
Apply this diff to add a safety check:
if (invokeData) {
+ if (accountKeys.length === 0) {
+ return null;
+ }
return convertToPublicTransactionEvent(
appendInputsData,
accountKeys[accountKeys.length - 1],
invokeData,
);
📝 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.
if (invokeData) { | |
return convertToPublicTransactionEvent( | |
appendInputsData, | |
accountKeys[accountKeys.length - 1], | |
invokeData, | |
); | |
if (invokeData) { | |
if (accountKeys.length === 0) { | |
return null; | |
} | |
return convertToPublicTransactionEvent( | |
appendInputsData, | |
accountKeys[accountKeys.length - 1], | |
invokeData, | |
); | |
} |
🤖 Prompt for AI Agents
In js/program-test/src/test-rpc/get-parsed-events.ts around lines 257 to 262,
the code accesses accountKeys[accountKeys.length - 1] without ensuring
accountKeys is non-empty; add an explicit guard such as if (!accountKeys ||
accountKeys.length === 0) return undefined (or otherwise handle/error out)
before calling convertToPublicTransactionEvent, and only pass
accountKeys[accountKeys.length - 1] when the array has at least one element.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (2)
js/program-test/src/test-rpc/test-rpc.ts (2)
424-437
: Add explicit StateV2 validation or document skip rationale.The validation loop only validates StateV1 proofs; StateV2 proofs are silently skipped. Given that StateV2 uses
proveByIndex: true
with zero-valued paths and a fixedleafIndex
from the on-chain index, add explicit validation (e.g., check thatleafIndex
is within bounds,rootIndex
is 0, path elements match_zeros
) or document why StateV2 proofs don't require validation.Apply this diff to add explicit handling:
// Validate proofs merkleProofsMap.forEach((proof, index) => { if (proof.treeInfo.treeType === TreeType.StateV1) { const leafIndex = proof.leafIndex; const computedHash = leavesByTree.get(proof.treeInfo.tree.toBase58())! .leaves[leafIndex]; const hashArr = bn(computedHash); if (!hashArr.eq(proof.hash)) { throw new Error( `Mismatch at index ${index}: expected ${proof.hash.toString()}, got ${hashArr.toString()}`, ); } + } else if (proof.treeInfo.treeType === TreeType.StateV2) { + // StateV2 proofs use proveByIndex with zero-valued paths. + // Validate that leafIndex is non-negative and rootIndex is 0. + if (proof.leafIndex < 0) { + throw new Error(`Invalid StateV2 leafIndex at index ${index}: ${proof.leafIndex}`); + } + if (proof.rootIndex !== 0) { + throw new Error(`Invalid StateV2 rootIndex at index ${index}: expected 0, got ${proof.rootIndex}`); + } } });
541-541
: Remove debug console.log statement (still present).This console.log was flagged in a previous review and claimed to be addressed in commit 0afa6c5, but it's still present. Please remove it before merging.
Apply this diff:
const account = await getCompressedTokenAccountByHashTest(this, hash); const rawAmount = account.parsed.amount; - console.log('[test-rpc.ts:524] Converting amount:', typeof rawAmount, rawAmount); // Convert amount to BN first (could be bigint or BN from Borsh u64 decoder)
Also applies to: 566-566, 598-598
🧹 Nitpick comments (2)
js/program-test/src/test-rpc/test-rpc.ts (2)
389-392
: Use already-availableleaves
variable instead of redundant lookup.Line 390-392 performs
leavesByTree.get(tree.toBase58())!.leaves.findIndex(...)
, butleaves
is already available from the destructuring at line 334. Use the simplerleaves.findIndex(...)
instead.Apply this diff:
/// Find array position, then get actual on-chain leaf index - const arrayPosition = leavesByTree - .get(tree.toBase58())! - .leaves.findIndex((leaf) => bn(leaf).eq(hashes[i])); + const arrayPosition = leaves.findIndex((leaf) => bn(leaf).eq(hashes[i]));
364-383
: Consider using on-chain leafIndex for StateV1 consistency.StateV1 currently uses the array position from
findIndex
directly asleafIndex
(line 364, 376). While this may work under test-rpc's constraints (fresh tree, <1000 transactions), for consistency with the StateV2 fix (lines 389-402) and to guard against scenarios where leaf indices are non-contiguous, consider explicitly retrieving the on-chain leaf index from the storedleafIndices
array.This would make the code more robust and consistent across both tree types.
Apply this diff:
for (let i = 0; i < hashes.length; i++) { - const leafIndex = leaves.findIndex((leaf) => bn(leaf).eq(hashes[i])); + const arrayPosition = leaves.findIndex((leaf) => bn(leaf).eq(hashes[i])); /// If leaf is part of current tree, return proof - if (leafIndex !== -1) { + if (arrayPosition !== -1) { if (treeInfo.treeType === TreeType.StateV1) { - const pathElements = merkleTree.path(leafIndex).pathElements; + const pathElements = merkleTree.path(arrayPosition).pathElements; const bnPathElements = pathElements.map((value) => bn(value)); const root = bn(merkleTree.root()); + const leafIndex = leavesByTree.get(tree.toBase58())!.leafIndices[arrayPosition]; + const merkleProof: MerkleContextWithMerkleProof = { hash: bn(hashes[i].toArray("be", 32)), treeInfo, leafIndex, merkleProof: bnPathElements, proveByIndex: false, - rootIndex: leaves.length, + rootIndex: leaves.length, root, };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
js/program-test/src/test-rpc/test-rpc.ts
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
js/program-test/src/test-rpc/test-rpc.ts (9)
js/stateless.js/src/constants.ts (3)
defaultTestStateTreeAccounts
(236-244)localTestActiveStateTreeInfos
(151-219)batchAddressTree
(304-304)js/stateless.js/src/rpc-interface.ts (16)
CompressionApiInterface
(762-867)GetCompressedAccountsByOwnerConfig
(45-50)WithCursor
(250-255)LatestNonVotingSignaturesPaginated
(57-67)LatestNonVotingSignatures
(33-43)GetCompressedTokenAccountsByOwnerOrDelegateOptions
(205-209)ParsedTokenAccount
(236-239)WithContext
(241-248)TokenBalance
(210-210)SignatureWithMetadata
(69-73)CompressedTransaction
(120-142)PaginatedOptions
(217-220)CompressedMintTokenHolders
(52-55)HashWithTree
(103-107)AddressWithTree
(109-113)ValidityProofWithContext
(161-190)js/program-test/src/merkle-tree/indexed-array.ts (2)
hash
(24-35)IndexedArray
(58-307)js/program-test/src/test-rpc/get-compressed-accounts.ts (4)
getCompressedAccountsForTest
(42-96)getCompressedAccountByHashTest
(23-29)getMultipleCompressedAccountsByHashTest
(31-39)getCompressedAccountsByOwnerTest
(14-21)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/test-rpc/get-parsed-events.ts (1)
getParsedEvents
(34-144)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
getStateTreeInfoByPubkey
(8-13)js/program-test/src/merkle-tree/merkle-tree.ts (2)
MerkleTree
(13-214)root
(74-78)js/stateless.js/src/rpc.ts (4)
MerkleContextWithNewAddressProof
(402-412)convertMerkleProofsWithContextToHex
(424-442)proverRequest
(336-390)convertNonInclusionMerkleProofInputsToHex
(444-470)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
- GitHub Check: Forester e2e test
- GitHub Check: stateless-js-v1
- GitHub Check: cli-v1
- GitHub Check: cli-v2
- GitHub Check: stateless-js-v2
- GitHub Check: lint
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: INPUT - hashes:', hashes.map(h => h.toString('hex').slice(0, 16) + '...')); | ||
|
||
// Parse events and organize leaves by their respective merkle trees | ||
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Calling getParsedEvents...'); | ||
const events: PublicTransactionEvent[] = await getParsedEvents(this).then( | ||
(events) => events.reverse(), | ||
); | ||
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Got', events.length, 'events'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove debug console.log statements.
Multiple debug console.log statements appear at lines 275, 278, and 282. These should be removed before merging to production.
Apply this diff:
- console.log('[TEST-RPC] getMultipleCompressedAccountProofs: INPUT - hashes:', hashes.map(h => h.toString('hex').slice(0, 16) + '...'));
-
// Parse events and organize leaves by their respective merkle trees
- console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Calling getParsedEvents...');
const events: PublicTransactionEvent[] = await getParsedEvents(this).then(
(events) => events.reverse(),
);
- console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Got', events.length, 'events');
📝 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.
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: INPUT - hashes:', hashes.map(h => h.toString('hex').slice(0, 16) + '...')); | |
// Parse events and organize leaves by their respective merkle trees | |
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Calling getParsedEvents...'); | |
const events: PublicTransactionEvent[] = await getParsedEvents(this).then( | |
(events) => events.reverse(), | |
); | |
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Got', events.length, 'events'); | |
// Parse events and organize leaves by their respective merkle trees | |
const events: PublicTransactionEvent[] = await getParsedEvents(this).then( | |
(events) => events.reverse(), | |
); |
🤖 Prompt for AI Agents
js/program-test/src/test-rpc/test-rpc.ts around lines 275 to 282: remove the
three debug console.log statements that log hashes, a pre-call message for
getParsedEvents, and the events count; simply delete those console.log lines (or
replace them with an appropriate logger debug call if project logging
conventions require) so no debug console output remains in production code.
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Processing', leavesByTree.size, 'trees'); | ||
|
||
for (const [treeKey, { leaves, treeInfo }] of leavesByTree.entries()) { | ||
const tree = new PublicKey(treeKey); | ||
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Processing tree:', treeKey, 'with', leaves.length, 'leaves, treeType:', treeInfo.treeType); | ||
|
||
let merkleTree: MerkleTree | undefined; | ||
if (treeInfo.treeType === TreeType.StateV1) { | ||
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Creating V1 MerkleTree with depth', this.depth); | ||
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: All leaves:', JSON.stringify(leaves)); | ||
const leafStrings = leaves.map((leaf) => bn(leaf).toString()); | ||
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Leaf strings:', JSON.stringify(leafStrings)); | ||
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Calling new MerkleTree...'); | ||
merkleTree = new MerkleTree( | ||
this.depth, | ||
this.lightWasm, | ||
leafStrings, | ||
); | ||
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: MerkleTree created successfully'); | ||
} 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( | ||
`Invalid tree type: ${treeInfo.treeType} in test-rpc.ts`, | ||
); | ||
} | ||
|
||
for (let i = 0; i < hashes.length; i++) { | ||
const leafIndex = leaves.findIndex((leaf) => bn(leaf).eq(hashes[i])); | ||
|
||
/// If leaf is part of current tree, return proof | ||
if (leafIndex !== -1) { | ||
if (treeInfo.treeType === TreeType.StateV1) { | ||
const pathElements = merkleTree.path(leafIndex).pathElements; | ||
const bnPathElements = pathElements.map((value) => bn(value)); | ||
const root = bn(merkleTree.root()); | ||
|
||
const merkleProof: MerkleContextWithMerkleProof = { | ||
hash: bn(hashes[i].toArray("be", 32)), | ||
treeInfo, | ||
leafIndex, | ||
merkleProof: bnPathElements, | ||
proveByIndex: false, | ||
rootIndex: leaves.length, | ||
root, | ||
}; | ||
|
||
merkleProofsMap.set(hashes[i].toString(), merkleProof); | ||
} 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()); | ||
|
||
/// Find array position, then get actual on-chain leaf index | ||
const arrayPosition = leavesByTree | ||
.get(tree.toBase58())! | ||
.leaves.findIndex((leaf) => bn(leaf).eq(hashes[i])); | ||
|
||
if (arrayPosition === -1) { | ||
throw new Error( | ||
`Hash ${hashes[i].toString()} not found in tree ${tree.toBase58()}`, | ||
); | ||
} | ||
|
||
const leafIndex = leavesByTree.get(tree.toBase58())!.leafIndices[ | ||
arrayPosition | ||
]; | ||
|
||
const merkleProof: MerkleContextWithMerkleProof = { | ||
// Hash is 0 for proveByIndex trees in test-rpc. | ||
hash: bn(hashes[i].toArray("be", 32)), | ||
// hash: bn(new Array(32).fill(0)), | ||
treeInfo, | ||
leafIndex, | ||
merkleProof: bnPathElements, | ||
proveByIndex: true, | ||
// Root index is 0 for proveByIndex trees in | ||
// test-rpc. | ||
rootIndex: 0, | ||
root, | ||
}; | ||
|
||
merkleProofsMap.set(hashes[i].toString(), merkleProof); | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Validate proofs | ||
merkleProofsMap.forEach((proof, index) => { | ||
if (proof.treeInfo.treeType === TreeType.StateV1) { | ||
const leafIndex = proof.leafIndex; | ||
const computedHash = leavesByTree.get(proof.treeInfo.tree.toBase58())! | ||
.leaves[leafIndex]; | ||
const hashArr = bn(computedHash); | ||
if (!hashArr.eq(proof.hash)) { | ||
throw new Error( | ||
`Mismatch at index ${index}: expected ${proof.hash.toString()}, got ${hashArr.toString()}`, | ||
); | ||
} | ||
} | ||
}); | ||
|
||
// Ensure all requested hashes belong to the same tree type | ||
const uniqueTreeTypes = new Set( | ||
hashes.map((hash) => { | ||
const proof = merkleProofsMap.get(hash.toString()); | ||
if (!proof) { | ||
throw new Error(`Proof not found for hash: ${hash.toString()}`); | ||
} | ||
return proof.treeInfo.treeType; | ||
}), | ||
); | ||
|
||
if (uniqueTreeTypes.size > 1) { | ||
throw new Error( | ||
"Requested hashes belong to different tree types (V1/V2)", | ||
); | ||
} | ||
|
||
// Return proofs in the order of requested hashes | ||
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Returning proofs for', hashes.length, 'hashes'); | ||
const results = hashes.map((hash) => { | ||
const proof = merkleProofsMap.get(hash.toString()); | ||
if (!proof) { | ||
throw new Error(`No proof found for hash: ${hash.toString()}`); | ||
} | ||
return proof; | ||
}); | ||
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: OUTPUT - Success, returning', results.length, 'proofs'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove remaining debug console.log statements.
Additional debug console.log statements remain at lines 332, 336, 340, 342, 343, 344, 350, 457, and 465. Remove all of these before merging.
Apply this diff:
- console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Processing', leavesByTree.size, 'trees');
-
for (const [treeKey, { leaves, treeInfo }] of leavesByTree.entries()) {
const tree = new PublicKey(treeKey);
- console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Processing tree:', treeKey, 'with', leaves.length, 'leaves, treeType:', treeInfo.treeType);
let merkleTree: MerkleTree | undefined;
if (treeInfo.treeType === TreeType.StateV1) {
- console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Creating V1 MerkleTree with depth', this.depth);
- console.log('[TEST-RPC] getMultipleCompressedAccountProofs: All leaves:', JSON.stringify(leaves));
const leafStrings = leaves.map((leaf) => bn(leaf).toString());
- console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Leaf strings:', JSON.stringify(leafStrings));
- console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Calling new MerkleTree...');
merkleTree = new MerkleTree(
this.depth,
this.lightWasm,
leafStrings,
);
- console.log('[TEST-RPC] getMultipleCompressedAccountProofs: MerkleTree created successfully');
}
// ... rest of code ...
}
// ... validation code ...
// Return proofs in the order of requested hashes
- console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Returning proofs for', hashes.length, 'hashes');
const results = hashes.map((hash) => {
// ... mapping code ...
});
- console.log('[TEST-RPC] getMultipleCompressedAccountProofs: OUTPUT - Success, returning', results.length, 'proofs');
return results;
📝 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.
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Processing', leavesByTree.size, 'trees'); | |
for (const [treeKey, { leaves, treeInfo }] of leavesByTree.entries()) { | |
const tree = new PublicKey(treeKey); | |
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Processing tree:', treeKey, 'with', leaves.length, 'leaves, treeType:', treeInfo.treeType); | |
let merkleTree: MerkleTree | undefined; | |
if (treeInfo.treeType === TreeType.StateV1) { | |
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Creating V1 MerkleTree with depth', this.depth); | |
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: All leaves:', JSON.stringify(leaves)); | |
const leafStrings = leaves.map((leaf) => bn(leaf).toString()); | |
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Leaf strings:', JSON.stringify(leafStrings)); | |
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Calling new MerkleTree...'); | |
merkleTree = new MerkleTree( | |
this.depth, | |
this.lightWasm, | |
leafStrings, | |
); | |
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: MerkleTree created successfully'); | |
} 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( | |
`Invalid tree type: ${treeInfo.treeType} in test-rpc.ts`, | |
); | |
} | |
for (let i = 0; i < hashes.length; i++) { | |
const leafIndex = leaves.findIndex((leaf) => bn(leaf).eq(hashes[i])); | |
/// If leaf is part of current tree, return proof | |
if (leafIndex !== -1) { | |
if (treeInfo.treeType === TreeType.StateV1) { | |
const pathElements = merkleTree.path(leafIndex).pathElements; | |
const bnPathElements = pathElements.map((value) => bn(value)); | |
const root = bn(merkleTree.root()); | |
const merkleProof: MerkleContextWithMerkleProof = { | |
hash: bn(hashes[i].toArray("be", 32)), | |
treeInfo, | |
leafIndex, | |
merkleProof: bnPathElements, | |
proveByIndex: false, | |
rootIndex: leaves.length, | |
root, | |
}; | |
merkleProofsMap.set(hashes[i].toString(), merkleProof); | |
} 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()); | |
/// Find array position, then get actual on-chain leaf index | |
const arrayPosition = leavesByTree | |
.get(tree.toBase58())! | |
.leaves.findIndex((leaf) => bn(leaf).eq(hashes[i])); | |
if (arrayPosition === -1) { | |
throw new Error( | |
`Hash ${hashes[i].toString()} not found in tree ${tree.toBase58()}`, | |
); | |
} | |
const leafIndex = leavesByTree.get(tree.toBase58())!.leafIndices[ | |
arrayPosition | |
]; | |
const merkleProof: MerkleContextWithMerkleProof = { | |
// Hash is 0 for proveByIndex trees in test-rpc. | |
hash: bn(hashes[i].toArray("be", 32)), | |
// hash: bn(new Array(32).fill(0)), | |
treeInfo, | |
leafIndex, | |
merkleProof: bnPathElements, | |
proveByIndex: true, | |
// Root index is 0 for proveByIndex trees in | |
// test-rpc. | |
rootIndex: 0, | |
root, | |
}; | |
merkleProofsMap.set(hashes[i].toString(), merkleProof); | |
} | |
} | |
} | |
} | |
// Validate proofs | |
merkleProofsMap.forEach((proof, index) => { | |
if (proof.treeInfo.treeType === TreeType.StateV1) { | |
const leafIndex = proof.leafIndex; | |
const computedHash = leavesByTree.get(proof.treeInfo.tree.toBase58())! | |
.leaves[leafIndex]; | |
const hashArr = bn(computedHash); | |
if (!hashArr.eq(proof.hash)) { | |
throw new Error( | |
`Mismatch at index ${index}: expected ${proof.hash.toString()}, got ${hashArr.toString()}`, | |
); | |
} | |
} | |
}); | |
// Ensure all requested hashes belong to the same tree type | |
const uniqueTreeTypes = new Set( | |
hashes.map((hash) => { | |
const proof = merkleProofsMap.get(hash.toString()); | |
if (!proof) { | |
throw new Error(`Proof not found for hash: ${hash.toString()}`); | |
} | |
return proof.treeInfo.treeType; | |
}), | |
); | |
if (uniqueTreeTypes.size > 1) { | |
throw new Error( | |
"Requested hashes belong to different tree types (V1/V2)", | |
); | |
} | |
// Return proofs in the order of requested hashes | |
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: Returning proofs for', hashes.length, 'hashes'); | |
const results = hashes.map((hash) => { | |
const proof = merkleProofsMap.get(hash.toString()); | |
if (!proof) { | |
throw new Error(`No proof found for hash: ${hash.toString()}`); | |
} | |
return proof; | |
}); | |
console.log('[TEST-RPC] getMultipleCompressedAccountProofs: OUTPUT - Success, returning', results.length, 'proofs'); | |
for (const [treeKey, { leaves, treeInfo }] of leavesByTree.entries()) { | |
const tree = new PublicKey(treeKey); | |
let merkleTree: MerkleTree | undefined; | |
if (treeInfo.treeType === TreeType.StateV1) { | |
const leafStrings = leaves.map((leaf) => bn(leaf).toString()); | |
merkleTree = new MerkleTree( | |
this.depth, | |
this.lightWasm, | |
leafStrings, | |
); | |
} 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( | |
`Invalid tree type: ${treeInfo.treeType} in test-rpc.ts`, | |
); | |
} | |
for (let i = 0; i < hashes.length; i++) { | |
const leafIndex = leaves.findIndex((leaf) => bn(leaf).eq(hashes[i])); | |
/// If leaf is part of current tree, return proof | |
if (leafIndex !== -1) { | |
if (treeInfo.treeType === TreeType.StateV1) { | |
const pathElements = merkleTree.path(leafIndex).pathElements; | |
const bnPathElements = pathElements.map((value) => bn(value)); | |
const root = bn(merkleTree.root()); | |
const merkleProof: MerkleContextWithMerkleProof = { | |
hash: bn(hashes[i].toArray("be", 32)), | |
treeInfo, | |
leafIndex, | |
merkleProof: bnPathElements, | |
proveByIndex: false, | |
rootIndex: leaves.length, | |
root, | |
}; | |
merkleProofsMap.set(hashes[i].toString(), merkleProof); | |
} 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()); | |
/// Find array position, then get actual on-chain leaf index | |
const arrayPosition = leavesByTree | |
.get(tree.toBase58())! | |
.leaves.findIndex((leaf) => bn(leaf).eq(hashes[i])); | |
if (arrayPosition === -1) { | |
throw new Error( | |
`Hash ${hashes[i].toString()} not found in tree ${tree.toBase58()}`, | |
); | |
} | |
const leafIndex = leavesByTree.get(tree.toBase58())!.leafIndices[ | |
arrayPosition | |
]; | |
const merkleProof: MerkleContextWithMerkleProof = { | |
// Hash is 0 for proveByIndex trees in test-rpc. | |
hash: bn(hashes[i].toArray("be", 32)), | |
treeInfo, | |
leafIndex, | |
merkleProof: bnPathElements, | |
proveByIndex: true, | |
// Root index is 0 for proveByIndex trees in | |
// test-rpc. | |
rootIndex: 0, | |
root, | |
}; | |
merkleProofsMap.set(hashes[i].toString(), merkleProof); | |
} | |
} | |
} | |
} | |
// Validate proofs | |
merkleProofsMap.forEach((proof, index) => { | |
if (proof.treeInfo.treeType === TreeType.StateV1) { | |
const leafIndex = proof.leafIndex; | |
const computedHash = leavesByTree.get(proof.treeInfo.tree.toBase58())! | |
.leaves[leafIndex]; | |
const hashArr = bn(computedHash); | |
if (!hashArr.eq(proof.hash)) { | |
throw new Error( | |
`Mismatch at index ${index}: expected ${proof.hash.toString()}, got ${hashArr.toString()}`, | |
); | |
} | |
} | |
}); | |
// Ensure all requested hashes belong to the same tree type | |
const uniqueTreeTypes = new Set( | |
hashes.map((hash) => { | |
const proof = merkleProofsMap.get(hash.toString()); | |
if (!proof) { | |
throw new Error(`Proof not found for hash: ${hash.toString()}`); | |
} | |
return proof.treeInfo.treeType; | |
}), | |
); | |
if (uniqueTreeTypes.size > 1) { | |
throw new Error( | |
"Requested hashes belong to different tree types (V1/V2)", | |
); | |
} | |
// Return proofs in the order of requested hashes | |
const results = hashes.map((hash) => { | |
const proof = merkleProofsMap.get(hash.toString()); | |
if (!proof) { | |
throw new Error(`No proof found for hash: ${hash.toString()}`); | |
} | |
return proof; | |
}); | |
return results; |
🤖 Prompt for AI Agents
In js/program-test/src/test-rpc/test-rpc.ts around lines 332-465, remove the
leftover debug console.log statements at lines 332, 336, 340, 342, 343, 344,
350, 457, and 465; delete those console.log calls (do not replace with console
statements) and ensure the remaining logic and variable usages compile cleanly
after removal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
js/program-test/src/litesvm-rpc.ts
(1 hunks)js/program-test/src/test-rpc/test-rpc.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- js/program-test/src/litesvm-rpc.ts
🧰 Additional context used
🧬 Code graph analysis (1)
js/program-test/src/test-rpc/test-rpc.ts (10)
js/stateless.js/src/constants.ts (3)
defaultTestStateTreeAccounts
(236-244)localTestActiveStateTreeInfos
(151-219)batchAddressTree
(304-304)js/stateless.js/src/rpc-interface.ts (16)
CompressionApiInterface
(762-867)GetCompressedAccountsByOwnerConfig
(45-50)WithCursor
(250-255)LatestNonVotingSignaturesPaginated
(57-67)LatestNonVotingSignatures
(33-43)GetCompressedTokenAccountsByOwnerOrDelegateOptions
(205-209)ParsedTokenAccount
(236-239)WithContext
(241-248)TokenBalance
(210-210)SignatureWithMetadata
(69-73)CompressedTransaction
(120-142)PaginatedOptions
(217-220)CompressedMintTokenHolders
(52-55)HashWithTree
(103-107)AddressWithTree
(109-113)ValidityProofWithContext
(161-190)js/stateless.js/src/state/types.ts (2)
TreeInfo
(77-99)PublicTransactionEvent
(231-272)js/program-test/src/merkle-tree/indexed-array.ts (2)
hash
(24-35)IndexedArray
(58-307)js/program-test/src/test-rpc/get-compressed-accounts.ts (4)
getCompressedAccountsForTest
(42-96)getCompressedAccountByHashTest
(23-29)getMultipleCompressedAccountsByHashTest
(31-39)getCompressedAccountsByOwnerTest
(14-21)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/test-rpc/get-parsed-events.ts (1)
getParsedEvents
(34-144)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
getStateTreeInfoByPubkey
(8-13)js/program-test/src/merkle-tree/merkle-tree.ts (2)
MerkleTree
(13-214)root
(74-78)js/stateless.js/src/rpc.ts (4)
MerkleContextWithNewAddressProof
(402-412)convertMerkleProofsWithContextToHex
(424-442)proverRequest
(336-390)convertNonInclusionMerkleProofInputsToHex
(444-470)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: Forester e2e test
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
- GitHub Check: cli-v1
- GitHub Check: cli-v2
- GitHub Check: stateless-js-v2
- GitHub Check: stateless-js-v1
- GitHub Check: lint
async getCompressedTokenAccountBalance(hash: BN254): Promise<{ amount: BN }> { | ||
const account = await getCompressedTokenAccountByHashTest(this, hash); | ||
const rawAmount = account.parsed.amount; | ||
console.log( | ||
"[test-rpc.ts:524] Converting amount:", | ||
typeof rawAmount, | ||
rawAmount, | ||
); | ||
// Convert amount to BN first (could be bigint or BN from Borsh u64 decoder) | ||
const amountBN = | ||
typeof rawAmount === "bigint" ? bn(String(rawAmount)) : bn(rawAmount); | ||
return { amount: amountBN }; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard against missing token account.
getCompressedTokenAccountByHashTest
can return undefined
, so dereferencing account.parsed
throws a plain TypeError instead of the expected “account not found” error. Add the same null check we do for compressed accounts before reading fields.
async getCompressedTokenAccountBalance(hash: BN254): Promise<{ amount: BN }> {
- const account = await getCompressedTokenAccountByHashTest(this, hash);
- const rawAmount = account.parsed.amount;
+ const account = await getCompressedTokenAccountByHashTest(this, hash);
+ if (!account) {
+ throw new Error(
+ `Compressed token account not found for hash ${hash.toString()}`,
+ );
+ }
+ const rawAmount = account.parsed.amount;
📝 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.
async getCompressedTokenAccountBalance(hash: BN254): Promise<{ amount: BN }> { | |
const account = await getCompressedTokenAccountByHashTest(this, hash); | |
const rawAmount = account.parsed.amount; | |
console.log( | |
"[test-rpc.ts:524] Converting amount:", | |
typeof rawAmount, | |
rawAmount, | |
); | |
// Convert amount to BN first (could be bigint or BN from Borsh u64 decoder) | |
const amountBN = | |
typeof rawAmount === "bigint" ? bn(String(rawAmount)) : bn(rawAmount); | |
return { amount: amountBN }; | |
} | |
async getCompressedTokenAccountBalance(hash: BN254): Promise<{ amount: BN }> { | |
const account = await getCompressedTokenAccountByHashTest(this, hash); | |
if (!account) { | |
throw new Error( | |
`Compressed token account not found for hash ${hash.toString()}`, | |
); | |
} | |
const rawAmount = account.parsed.amount; | |
console.log( | |
"[test-rpc.ts:524] Converting amount:", | |
typeof rawAmount, | |
rawAmount, | |
); | |
// Convert amount to BN first (could be bigint or BN from Borsh u64 decoder) | |
const amountBN = | |
typeof rawAmount === "bigint" ? bn(String(rawAmount)) : bn(rawAmount); | |
return { amount: amountBN }; | |
} |
🤖 Prompt for AI Agents
In js/program-test/src/test-rpc/test-rpc.ts around lines 675 to 687, the code
dereferences account.parsed without checking whether
getCompressedTokenAccountByHashTest returned undefined; add the same null guard
used elsewhere for compressed accounts: if account is undefined/null, throw the
same “account not found” (or “Compressed token account not found”) error used by
other compressed-account helpers, otherwise proceed to read account.parsed and
convert amount to BN; ensure the thrown error message and type match the
existing pattern so callers receive the expected error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
♻️ Duplicate comments (2)
js/program-test/src/litesvm-rpc.ts (2)
353-355
: Remove debug console.log statements.These debug logs were flagged in a previous review and should be removed before merging.
Apply this diff:
const logs = result.logs(); const signatureBytes = result.signature(); - console.log("signatureBytes ", signatureBytes); const signature = bs58.encode(signatureBytes); - console.log("bs58 signature ", signature);
488-513
: Fix transaction signatures format.The current implementation returns
Transaction.signatures
(objects with publicKey and signature fields) for legacy transactions, but the JSON-RPCgetTransaction
spec requires an array of base58-encoded signature strings.Apply this diff to use the stored base58 signature:
return { slot: tx.slot, blockTime: tx.blockTime, transaction: { message: { accountKeys, compiledInstructions, recentBlockhash: message.recentBlockhash || message.header?.recentBlockhash || "", addressTableLookups: message.addressTableLookups || [], }, - signatures: - "signatures" in rawTx ? rawTx.signatures : [(rawTx as any).signature], + signatures: [tx.signature], }, meta: { err: null, logMessages: tx.logs, innerInstructions: tx.innerInstructions || [], preBalances: [], postBalances: [], preTokenBalances: [], postTokenBalances: [], rewards: [], fee: 5000, }, };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
js/compressed-token/tests/program-test/decompress-delegated.test.ts
(4 hunks)js/program-test/src/litesvm-rpc.ts
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
js/program-test/src/litesvm-rpc.ts (3)
js/program-test/src/test-rpc/test-rpc.ts (1)
TestRpc
(114-1120)js/program-test/src/types.ts (1)
LiteSVMConfig
(16-27)js/stateless.js/src/constants.ts (2)
defaultTestStateTreeAccounts
(236-244)featureFlags
(16-35)
js/compressed-token/tests/program-test/decompress-delegated.test.ts (2)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(993-999)js/program-test/src/spl-token-utils.ts (1)
splCreateAssociatedTokenAccount
(78-115)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: Forester e2e test
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
- GitHub Check: cli-v2
- GitHub Check: lint
- GitHub Check: stateless-js-v2
- GitHub Check: cli-v1
- GitHub Check: stateless-js-v1
🔇 Additional comments (3)
js/compressed-token/tests/program-test/decompress-delegated.test.ts (3)
18-22
: LGTM! Imports migrated to program-test package.The imports correctly reference the new
@lightprotocol/program-test
package utilities, aligning with the PR's objective to centralize testing infrastructure.
117-117
: LGTM! RPC initialization updated to LiteSVM.The change from
getTestRpc
tocreateLiteSVMRpc
correctly adopts the new program-test RPC factory, enabling in-memory deterministic testing.
137-142
: LGTM! Token account creation migrated to program-test utilities.The switch to
splCreateAssociatedTokenAccount
from the program-test package correctly standardizes token account creation across tests.
const recipientBalance = await rpc.getTokenAccountBalance(recipient); | ||
console.log('[TEST] getBalances - recipientBalance:', JSON.stringify(recipientBalance)); | ||
console.log('[TEST] getBalances - recipientBalance.value:', recipientBalance.value); | ||
console.log('[TEST] getBalances - recipientBalance.value.amount:', recipientBalance.value?.amount); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove debug console.log statements.
These debug logs are development artifacts and should be removed before merging to avoid spamming test output.
Apply this diff to remove the debug logs:
- const recipientBalance = await rpc.getTokenAccountBalance(recipient);
- console.log('[TEST] getBalances - recipientBalance:', JSON.stringify(recipientBalance));
- console.log('[TEST] getBalances - recipientBalance.value:', recipientBalance.value);
- console.log('[TEST] getBalances - recipientBalance.value.amount:', recipientBalance.value?.amount);
+ const recipientBalance = await rpc.getTokenAccountBalance(recipient);
📝 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.
const recipientBalance = await rpc.getTokenAccountBalance(recipient); | |
console.log('[TEST] getBalances - recipientBalance:', JSON.stringify(recipientBalance)); | |
console.log('[TEST] getBalances - recipientBalance.value:', recipientBalance.value); | |
console.log('[TEST] getBalances - recipientBalance.value.amount:', recipientBalance.value?.amount); | |
const recipientBalance = await rpc.getTokenAccountBalance(recipient); |
🤖 Prompt for AI Agents
In js/compressed-token/tests/program-test/decompress-delegated.test.ts around
lines 43 to 46, remove the three debug console.log statements that print
recipientBalance and its fields; simply delete those console.log lines so the
test no longer outputs development debug information and keeps test output
clean.
console.log("[LITESVM] Constructor called, PID:", process.pid); | ||
|
||
// Initialize TestRpc with dummy endpoints | ||
super( | ||
"http://127.0.0.1:8899", | ||
lightWasm, | ||
"http://127.0.0.1:8784", | ||
proverEndpoint, | ||
{ commitment: "confirmed" }, | ||
{ depth: defaultTestStateTreeAccounts().merkleTreeHeight }, | ||
); | ||
|
||
this.storedTransactions = new Map(); | ||
this.storedRawTransactions = new Map(); | ||
|
||
// Initialize LiteSVM with configuration | ||
console.log("[LITESVM] Creating new LiteSVM()..."); | ||
this.litesvm = new LiteSVM() | ||
.withSysvars() | ||
.withBuiltins() | ||
.withDefaultPrograms() | ||
.withPrecompiles(); | ||
console.log("[LITESVM] LiteSVM created successfully"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove excessive console.log statements from constructor.
Multiple debug logs in the constructor (lines 36, 52-53, 58) will spam output during test execution. These are development artifacts that should be removed or replaced with a proper logging framework before production use.
Consider removing these logs or using a debug-level logger:
constructor(
lightWasm: any,
config?: LiteSVMConfig,
proverEndpoint: string = "http://127.0.0.1:3001",
) {
- console.log("[LITESVM] Constructor called, PID:", process.pid);
// Initialize TestRpc with dummy endpoints
super(
"http://127.0.0.1:8899",
lightWasm,
"http://127.0.0.1:8784",
proverEndpoint,
{ commitment: "confirmed" },
{ depth: defaultTestStateTreeAccounts().merkleTreeHeight },
);
this.storedTransactions = new Map();
this.storedRawTransactions = new Map();
// Initialize LiteSVM with configuration
- console.log("[LITESVM] Creating new LiteSVM()...");
this.litesvm = new LiteSVM()
.withSysvars()
.withBuiltins()
.withDefaultPrograms()
.withPrecompiles();
- console.log("[LITESVM] LiteSVM created successfully");
📝 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.
console.log("[LITESVM] Constructor called, PID:", process.pid); | |
// Initialize TestRpc with dummy endpoints | |
super( | |
"http://127.0.0.1:8899", | |
lightWasm, | |
"http://127.0.0.1:8784", | |
proverEndpoint, | |
{ commitment: "confirmed" }, | |
{ depth: defaultTestStateTreeAccounts().merkleTreeHeight }, | |
); | |
this.storedTransactions = new Map(); | |
this.storedRawTransactions = new Map(); | |
// Initialize LiteSVM with configuration | |
console.log("[LITESVM] Creating new LiteSVM()..."); | |
this.litesvm = new LiteSVM() | |
.withSysvars() | |
.withBuiltins() | |
.withDefaultPrograms() | |
.withPrecompiles(); | |
console.log("[LITESVM] LiteSVM created successfully"); | |
constructor( | |
lightWasm: any, | |
config?: LiteSVMConfig, | |
proverEndpoint: string = "http://127.0.0.1:3001", | |
) { | |
// Initialize TestRpc with dummy endpoints | |
super( | |
"http://127.0.0.1:8899", | |
lightWasm, | |
"http://127.0.0.1:8784", | |
proverEndpoint, | |
{ commitment: "confirmed" }, | |
{ depth: defaultTestStateTreeAccounts().merkleTreeHeight }, | |
); | |
this.storedTransactions = new Map(); | |
this.storedRawTransactions = new Map(); | |
// Initialize LiteSVM with configuration | |
this.litesvm = new LiteSVM() | |
.withSysvars() | |
.withBuiltins() | |
.withDefaultPrograms() | |
.withPrecompiles(); | |
} |
🤖 Prompt for AI Agents
In js/program-test/src/litesvm-rpc.ts around lines 36 to 58, remove the
excessive console.log calls in the constructor (the PID log at ~36, the
"Creating new LiteSVM()" and "LiteSVM created successfully" logs at ~52-53 and
~58) to avoid spamming test output; replace them with calls to a proper logger
at debug level (e.g., this.logger.debug(...)) if a logging facility exists, or
guard them behind a debug flag/config so they only run when verbose debugging is
enabled — ensure no functional behavior changes beyond logging removal and that
any necessary startup errors remain logged at an appropriate error/warn level.
console.log( | ||
"[LITESVM] addProgramFromFile: light_system_program_pinocchio.so", | ||
); | ||
this.litesvm.addProgramFromFile( | ||
LIGHT_SYSTEM_PROGRAM_ID, | ||
path.join(deployPath, "light_system_program_pinocchio.so"), | ||
); | ||
console.log("[LITESVM] addProgramFromFile: account_compression.so"); | ||
this.litesvm.addProgramFromFile( | ||
ACCOUNT_COMPRESSION_PROGRAM_ID, | ||
path.join(deployPath, "account_compression.so"), | ||
); | ||
console.log("[LITESVM] addProgramFromFile: light_compressed_token.so"); | ||
this.litesvm.addProgramFromFile( | ||
COMPRESSED_TOKEN_PROGRAM_ID, | ||
path.join(deployPath, "light_compressed_token.so"), | ||
); | ||
console.log("[LITESVM] All programs loaded successfully"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove excessive console.log statements from loadLightPrograms.
Multiple debug logs (lines 116-118, 123, 128, 133) will spam output during test initialization. Remove these or use a debug-level logger.
🤖 Prompt for AI Agents
In js/program-test/src/litesvm-rpc.ts around lines 116 to 133, the function
loadLightPrograms contains multiple console.log calls that spam test output;
remove these console.log statements (or replace them with a debug-level logger
call if a logging framework is available) so that only necessary startup
messages remain; specifically delete the individual "[LITESVM]
addProgramFromFile: ..." and the final "[LITESVM] All programs loaded
successfully" console.log lines, or change them to use the project's debug
logger instead.
console.log("[LITESVM] loadAccountFixtures: Starting..."); | ||
// Find repo root by looking for cli/accounts | ||
// Works whether running from source (src/) or built (dist/cjs/) | ||
let repoRoot = __dirname; | ||
while (!fs.existsSync(path.join(repoRoot, "cli/accounts"))) { | ||
const parent = path.dirname(repoRoot); | ||
if (parent === repoRoot) { | ||
throw new Error("Could not find cli/accounts directory"); | ||
} | ||
repoRoot = parent; | ||
} | ||
const accountsPath = path.join(repoRoot, "cli/accounts"); | ||
console.log( | ||
"[LITESVM] loadAccountFixtures: Found accounts path:", | ||
accountsPath, | ||
); | ||
|
||
// Load all account JSON files from cli/accounts | ||
const files = fs.readdirSync(accountsPath); | ||
console.log("[LITESVM] loadAccountFixtures: Found", files.length, "files"); | ||
|
||
for (const filename of files) { | ||
if (!filename.endsWith(".json")) continue; | ||
|
||
console.log("[LITESVM] loadAccountFixtures: Loading", filename); | ||
const filepath = path.join(accountsPath, filename); | ||
const accountData = JSON.parse(fs.readFileSync(filepath, "utf-8")); | ||
const pubkey = new PublicKey(accountData.pubkey); | ||
|
||
// Handle rentEpoch: if it exceeds JavaScript's MAX_SAFE_INTEGER or approaches u64::MAX, | ||
// set it to 0 to avoid overflow issues (same approach as litesvm's copyAccounts test) | ||
let rentEpoch = accountData.account.rentEpoch || 0; | ||
if (rentEpoch > Number.MAX_SAFE_INTEGER) { | ||
rentEpoch = 0; | ||
} | ||
|
||
const account = { | ||
lamports: accountData.account.lamports, | ||
data: Buffer.from(accountData.account.data[0], "base64"), | ||
owner: new PublicKey(accountData.account.owner), | ||
executable: accountData.account.executable, | ||
rentEpoch, | ||
}; | ||
console.log( | ||
"[LITESVM] setAccount:", | ||
pubkey.toBase58(), | ||
"lamports:", | ||
account.lamports, | ||
); | ||
this.litesvm.setAccount(pubkey, account); | ||
console.log("[LITESVM] setAccount: Success for", filename); | ||
} | ||
console.log("[LITESVM] loadAccountFixtures: Complete"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove excessive console.log statements from loadAccountFixtures.
Multiple debug logs throughout this method will spam output. The rentEpoch overflow handling (lines 176-179) is a good safeguard, but the excessive logging should be removed or moved to a debug-level logger.
🤖 Prompt for AI Agents
In js/program-test/src/litesvm-rpc.ts around lines 145 to 197, remove or replace
the excessive console.log statements in loadAccountFixtures so normal runs
aren't spammed; keep the rentEpoch overflow handling as-is (lines ~176-179).
Specifically, delete the multiple console.log calls that announce start, account
path discovery, file counts, per-file loading, setAccount debug messages, and
completion, or replace them with a single debug-level logger call (e.g.,
logger.debug(...)) if a logger is available; ensure functional behavior and
error handling are unchanged.
console.log( | ||
"[LITESVM] sendTransaction: Calling litesvm.sendTransaction()...", | ||
); | ||
const result = this.litesvm.sendTransaction(transaction); | ||
console.log("[LITESVM] sendTransaction: Transaction sent"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove excessive console.log statements from sendTransaction.
These logs will spam output during test execution. Remove or use a debug-level logger.
🤖 Prompt for AI Agents
In js/program-test/src/litesvm-rpc.ts around lines 323 to 327, remove the two
console.log calls that announce sendTransaction start/completion (they spam test
output); either delete them entirely or replace them with a debug-level logger
call (e.g. debug(...) or this.logger.debug(...)) so normal test runs remain
quiet while preserving optional verbose logging when enabled.
console.log("[LITESVM] airdrop:", pubkey.toBase58(), lamports); | ||
this.litesvm.airdrop(pubkey, BigInt(lamports)); | ||
console.log("[LITESVM] airdrop: Success"); | ||
return "mock-airdrop-signature"; | ||
} | ||
|
||
/** | ||
* Get account info using LiteSVM | ||
*/ | ||
override async getAccountInfo( | ||
publicKey: PublicKey, | ||
commitmentOrConfig?: any, | ||
): Promise<any> { | ||
console.log("[LITESVM] getAccount:", publicKey.toBase58()); | ||
const account = this.litesvm.getAccount(publicKey); | ||
if (!account) { | ||
console.log("[LITESVM] getAccount: Not found"); | ||
return null; | ||
} | ||
console.log("[LITESVM] getAccount: Found"); | ||
return { | ||
executable: account.executable, | ||
owner: new PublicKey(account.owner), | ||
lamports: Number(account.lamports), | ||
data: account.data, | ||
rentEpoch: account.rentEpoch, | ||
}; | ||
} | ||
|
||
/** | ||
* Get multiple account infos using LiteSVM | ||
*/ | ||
override async getMultipleAccountsInfo( | ||
publicKeys: PublicKey[], | ||
commitmentOrConfig?: any, | ||
): Promise<(any | null)[]> { | ||
return publicKeys.map((publicKey) => { | ||
const account = this.litesvm.getAccount(publicKey); | ||
if (!account) { | ||
return null; | ||
} | ||
return { | ||
executable: account.executable, | ||
owner: new PublicKey(account.owner), | ||
lamports: Number(account.lamports), | ||
data: account.data, | ||
rentEpoch: account.rentEpoch, | ||
}; | ||
}); | ||
} | ||
|
||
/** | ||
* Get balance using LiteSVM | ||
*/ | ||
override async getBalance(publicKey: PublicKey): Promise<number> { | ||
console.log("[LITESVM] getBalance:", publicKey.toBase58()); | ||
const balance = Number(this.litesvm.getBalance(publicKey)); | ||
console.log("[LITESVM] getBalance: Result:", balance); | ||
return balance; | ||
} | ||
|
||
/** | ||
* Get minimum balance for rent exemption | ||
*/ | ||
override async getMinimumBalanceForRentExemption( | ||
dataLength: number, | ||
commitment?: any, | ||
): Promise<number> { | ||
console.log("[LITESVM] minimumBalanceForRentExemption:", dataLength); | ||
const balance = Number( | ||
this.litesvm.minimumBalanceForRentExemption(BigInt(dataLength)), | ||
); | ||
console.log("[LITESVM] minimumBalanceForRentExemption: Result:", balance); | ||
return balance; | ||
} | ||
|
||
/** | ||
* Simulate a transaction without executing it | ||
*/ | ||
override async simulateTransaction( | ||
transactionOrMessage: any, | ||
configOrSigners?: any, | ||
includeAccounts?: any, | ||
): Promise<any> { | ||
// Extract transaction from possible message wrapper | ||
const transaction = | ||
"message" in transactionOrMessage | ||
? transactionOrMessage | ||
: transactionOrMessage; | ||
|
||
console.log("[LITESVM] simulateTransaction: Calling..."); | ||
const result = this.litesvm.simulateTransaction(transaction); | ||
console.log("[LITESVM] simulateTransaction: Complete"); | ||
|
||
// Check if simulation failed | ||
if ("err" in result && typeof result.err === "function") { | ||
const error = result.err(); | ||
return { | ||
context: { slot: 1 }, | ||
value: { | ||
err: error, | ||
logs: [], | ||
accounts: null, | ||
unitsConsumed: 0, | ||
returnData: null, | ||
}, | ||
}; | ||
} | ||
|
||
const simResult = result as any; | ||
const meta = simResult.meta(); | ||
|
||
return { | ||
context: { slot: 1 }, | ||
value: { | ||
err: null, | ||
logs: meta.logs(), | ||
accounts: null, | ||
unitsConsumed: Number(meta.computeUnitsConsumed()), | ||
returnData: meta.returnData() | ||
? { | ||
programId: new PublicKey( | ||
meta.returnData().programId(), | ||
).toBase58(), | ||
data: [ | ||
Buffer.from(meta.returnData().data()).toString("base64"), | ||
"base64", | ||
], | ||
} | ||
: null, | ||
}, | ||
}; | ||
} | ||
|
||
/** | ||
* Get epoch schedule | ||
*/ | ||
override async getEpochSchedule(): Promise<any> { | ||
console.log("[LITESVM] getEpochSchedule: Calling..."); | ||
const schedule = this.litesvm.getEpochSchedule(); | ||
console.log("[LITESVM] getEpochSchedule: Success"); | ||
return { | ||
slotsPerEpoch: Number(schedule.slotsPerEpoch), | ||
leaderScheduleSlotOffset: Number(schedule.leaderScheduleSlotOffset), | ||
warmup: schedule.warmup, | ||
firstNormalEpoch: Number(schedule.firstNormalEpoch), | ||
firstNormalSlot: Number(schedule.firstNormalSlot), | ||
}; | ||
} | ||
|
||
/** | ||
* Get latest blockhash from LiteSVM | ||
*/ | ||
override async getRecentBlockhash(): Promise<any> { | ||
const blockhash = this.litesvm.latestBlockhash(); | ||
return { | ||
blockhash, | ||
feeCalculator: { | ||
lamportsPerSignature: 5000, | ||
}, | ||
}; | ||
} | ||
|
||
/** | ||
* Get latest blockhash (modern API) | ||
*/ | ||
override async getLatestBlockhash(commitment?: any): Promise<any> { | ||
console.log("[LITESVM] latestBlockhash: Calling..."); | ||
const blockhash = this.litesvm.latestBlockhash(); | ||
console.log("[LITESVM] latestBlockhash:", blockhash); | ||
return { | ||
blockhash, | ||
lastValidBlockHeight: 1000000, | ||
}; | ||
} | ||
|
||
/** | ||
* Confirm transaction (instant for LiteSVM) | ||
*/ | ||
override async confirmTransaction( | ||
signature: string | any, | ||
commitment?: any, | ||
): Promise<any> { | ||
return { | ||
context: { slot: 1 }, | ||
value: { err: null }, | ||
}; | ||
} | ||
|
||
/** | ||
* Get signature statuses (return instant confirmation for LiteSVM) | ||
* Since LiteSVM executes synchronously, all transactions are immediately finalized | ||
*/ | ||
override async getSignatureStatuses( | ||
signatures: string[], | ||
config?: any, | ||
): Promise<any> { | ||
// LiteSVM executes synchronously, so all transactions are immediately finalized | ||
const commitment = "finalized"; | ||
return { | ||
context: { slot: 1 }, | ||
value: signatures.map((signature) => { | ||
// Check if we have this transaction stored | ||
const tx = this.storedTransactions.get(signature); | ||
if (!tx) { | ||
return null; // Transaction not found | ||
} | ||
const statusObj = { | ||
slot: 1, | ||
confirmations: null, | ||
err: null, | ||
confirmationStatus: commitment as any, // Return the requested commitment level | ||
}; | ||
return statusObj; | ||
}), | ||
}; | ||
} | ||
|
||
/** | ||
* Get current slot from LiteSVM | ||
*/ | ||
override async getSlot(commitment?: any): Promise<number> { | ||
console.log("[LITESVM] getClock: Calling..."); | ||
const slot = Number(this.litesvm.getClock().slot); | ||
console.log("[LITESVM] getClock: slot =", slot); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove excessive console.log statements from query methods.
Multiple query methods (requestAirdrop, getAccountInfo, getBalance, getMinimumBalanceForRentExemption, simulateTransaction, getEpochSchedule, getLatestBlockhash, getSlot) contain debug logs that will spam output. These should be removed or moved to a debug-level logger for production use.
🤖 Prompt for AI Agents
In js/program-test/src/litesvm-rpc.ts around lines 600 to 824, several query
methods (requestAirdrop, getAccountInfo, getMultipleAccountsInfo, getBalance,
getMinimumBalanceForRentExemption, simulateTransaction, getEpochSchedule,
getLatestBlockhash, getSlot) contain excessive console.log debug prints; remove
these console.log statements (or replace them with calls to a debug-level logger
if one exists) so normal operation does not spam stdout, keeping only essential
error/warning logs and returning the same values and behavior.
console.log( | ||
"[litesvm-rpc.ts] getTokenAccountBalance called for:", | ||
tokenAccount.toBase58(), | ||
); | ||
const account = await this.getAccountInfo(tokenAccount); | ||
if (!account) { | ||
throw new Error(`Token account ${tokenAccount.toBase58()} not found`); | ||
} | ||
|
||
console.log( | ||
"[litesvm-rpc.ts] Account data length:", | ||
account.data.length, | ||
"type:", | ||
typeof account.data, | ||
); | ||
// Parse SPL token account data using proper layout | ||
let accountData; | ||
try { | ||
accountData = AccountLayout.decode(Buffer.from(account.data)); | ||
console.log("[litesvm-rpc.ts] AccountLayout.decode succeeded"); | ||
} catch (err) { | ||
console.error("[litesvm-rpc.ts] ERROR decoding account data:", err); | ||
console.error("[litesvm-rpc.ts] Account data (all bytes):", account.data); | ||
throw err; | ||
} | ||
console.log( | ||
"[litesvm-rpc.ts] accountData full object:", | ||
JSON.stringify(accountData, (key, value) => | ||
typeof value === 'bigint' ? value.toString() + 'n' : value | ||
) | ||
); | ||
console.log( | ||
"[litesvm-rpc.ts] Converting amount:", | ||
"type:", typeof accountData.amount, | ||
"value:", accountData.amount, | ||
"isUndefined:", accountData.amount === undefined, | ||
"isNull:", accountData.amount === null, | ||
); | ||
// Convert amount to bigint first (it could be BN or bigint depending on spl-token version) | ||
let amountBigInt: bigint; | ||
if (accountData.amount === undefined || accountData.amount === null) { | ||
console.warn("[litesvm-rpc.ts] WARNING: accountData.amount is undefined/null, using 0 as fallback"); | ||
amountBigInt = BigInt(0); | ||
} else if (typeof accountData.amount === "bigint") { | ||
amountBigInt = accountData.amount; | ||
} else { | ||
try { | ||
amountBigInt = BigInt((accountData.amount as any).toString()); | ||
} catch (err) { | ||
console.error("[litesvm-rpc.ts] ERROR converting amount to BigInt:", err); | ||
console.error("[litesvm-rpc.ts] Falling back to 0"); | ||
amountBigInt = BigInt(0); | ||
} | ||
} | ||
const mintPubkey = new PublicKey(accountData.mint); | ||
|
||
// Fetch mint account to get decimals | ||
const mintAccount = await this.getAccountInfo(mintPubkey); | ||
if (!mintAccount) { | ||
throw new Error(`Mint account ${mintPubkey.toBase58()} not found`); | ||
} | ||
|
||
// Parse mint account using MintLayout to get decimals | ||
const mintData = MintLayout.decode(Buffer.from(mintAccount.data)); | ||
const decimals = mintData.decimals; | ||
|
||
const result = { | ||
context: { slot: 1 }, | ||
value: { | ||
amount: amountBigInt.toString(), | ||
decimals, | ||
uiAmount: Number(amountBigInt) / Math.pow(10, decimals), | ||
uiAmountString: ( | ||
Number(amountBigInt) / Math.pow(10, decimals) | ||
).toString(), | ||
}, | ||
}; | ||
console.log( | ||
"[litesvm-rpc.ts] getTokenAccountBalance returning:", | ||
JSON.stringify(result), | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove excessive console.log statements from getTokenAccountBalance.
This method contains extensive debug logging throughout (lines 835-876, 912-914) that will spam output. The logic correctly fetches decimals from the mint account (addressing a past review concern), but the debug logs should be removed.
Based on learnings
🤖 Prompt for AI Agents
In js/program-test/src/litesvm-rpc.ts around lines 835-915, remove the excessive
debug console.log statements introduced for troubleshooting (specifically the
logs at ~835-876 and the final return log at ~912-914). Keep only essential
diagnostics (errors/warnings used for real failure handling, e.g., console.error
on decode failure and console.warn when amount is missing) and remove or replace
the remaining console.log calls with no-ops or minimal, non-verbose logging.
Ensure the function still throws/returns as before and that only critical
error/warning logs remain.
0e1c4c8
to
4d9ca66
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (3)
js/compressed-token/tests/program-test/decompress-delegated.test.ts (1)
43-46
: Remove debug logs from getBalances.These spam test output and add no value. Keep the fetched balance only.
const recipientBalance = await rpc.getTokenAccountBalance(recipient); - console.log('[TEST] getBalances - recipientBalance:', JSON.stringify(recipientBalance)); - console.log('[TEST] getBalances - recipientBalance.value:', recipientBalance.value); - console.log('[TEST] getBalances - recipientBalance.value.amount:', recipientBalance.value?.amount);js/program-test/src/litesvm-rpc.ts (2)
36-36
: Strip or guard debug console.log statements.These flood test output and slow CI. Remove or behind a debug flag/logger.
Example (constructor/sendTransaction):
- console.log("[LITESVM] Constructor called, PID:", process.pid); @@ - console.log("[LITESVM] Creating new LiteSVM()..."); @@ - console.log("[LITESVM] LiteSVM created successfully"); @@ - console.log( - "[LITESVM] sendTransaction: Calling litesvm.sendTransaction()...", - ); @@ - console.log("[LITESVM] sendTransaction: Transaction sent"); @@ - console.log("signatureBytes ", signatureBytes); @@ - console.log("bs58 signature ", signature);Also applies to: 52-59, 116-134, 145-198, 281-291, 323-328, 353-356, 600-603, 613-620, 655-658, 668-673, 690-693, 738-741, 767-770, 822-825, 835-838, 844-865, 867-876, 921-924
499-501
: Return base58 signature strings per JSON-RPC spec.Currently returns objects/bytes; should be array of base58 strings. You already have sigString—use it.
- signatures: - "signatures" in rawTx ? rawTx.signatures : [(rawTx as any).signature], + signatures: [sigString],
🧹 Nitpick comments (6)
js/compressed-token/tests/program-test/decompress-delegated.test.ts (1)
81-86
: Remove debug logs from assertDecompress.Noisy console logs slow CI and hide real failures. Prefer assertions only.
- // Defensive type conversion: ensure amount is always a string before passing to bn() - console.log('[TEST] assertDecompress - initialBalances.recipient.value.amount:', typeof initialBalances.recipient.value.amount, initialBalances.recipient.value.amount); - console.log('[TEST] assertDecompress - finalBalances.recipient.value.amount:', typeof finalBalances.recipient.value.amount, finalBalances.recipient.value.amount); - console.log('[TEST] assertDecompress - amount:', amount.toString()); + // Defensive type conversion: ensure amount is always a string before passing to bn() @@ - console.log('[TEST] assertDecompress - expectedRecipientBalance:', expectedRecipientBalance.toString()); - console.log('[TEST] assertDecompress - actualRecipientBalance:', actualRecipientBalance.toString());Also applies to: 92-94
js/compressed-token/tests/program-test/compress.test.ts (4)
62-69
: Remove debug logs in assertCompress.Keep assertions; drop verbose logs.
- const afterAmountStr = String(refSenderAtaBalanceAfter.value.amount); - console.log('[TEST] assertCompress - refSenderAtaBalanceAfter.value.amount:', typeof refSenderAtaBalanceAfter.value.amount, refSenderAtaBalanceAfter.value.amount); - console.log('[TEST] assertCompress - afterAmountStr:', typeof afterAmountStr, afterAmountStr); - console.log('[TEST] assertCompress - refSenderAtaBalanceBefore:', refSenderAtaBalanceBefore.toString()); - console.log('[TEST] assertCompress - totalAmount:', totalAmount.toString()); - console.log('[TEST] assertCompress - expected:', refSenderAtaBalanceBefore.sub(totalAmount).toString()); + const afterAmountStr = String(refSenderAtaBalanceAfter.value.amount);
135-145
: Remove setup-time debug logs in beforeAll.These are dev artifacts and clutter CI output.
- console.log('post mint'); stateTreeInfo = selectStateTreeInfo(await rpc.getStateTreeInfos()); - console.log('post stateTreeInfo'); tokenPoolInfo = selectTokenPoolInfo(await getTokenPoolInfos(rpc, mint)); - console.log('post tokenPoolInfo'); bob = await newAccountWithLamports(rpc, 1e9); - console.log('post bob'); charlie = await newAccountWithLamports(rpc, 1e9); - console.log('post charlie'); @@ - console.log('post bobAta'); @@ - console.log('post mintTo'); await decompress(rpc, payer, mint, bn(900), bob, bobAta); @@ - console.log('post decompress'); @@ - console.log('post lut');Also applies to: 153-153, 165-165, 168-168, 184-184
242-251
: Redundant loop around assertCompress; call once.You run the same assertion 15 times. Remove the loop and assert once.
- for (let i = 0; i < recipients.length; i++) { - await assertCompress( - rpc, - bn(String(senderAtaBalanceBefore.value.amount)), - bobAta, - mint, - amounts.slice(0, 11), - recipients.slice(0, 11), - recipientCompressedTokenBalancesBefore.map(x => x.items), - ); - } + await assertCompress( + rpc, + bn(String(senderAtaBalanceBefore.value.amount)), + bobAta, + mint, + amounts.slice(0, 11), + recipients.slice(0, 11), + recipientCompressedTokenBalancesBefore.map(x => x.items), + );
262-265
: Remove debug logs in batch compress assertion.- console.log('[TEST] compress.test - beforeAmount:', typeof beforeAmount, beforeAmount); - console.log('[TEST] compress.test - afterAmount:', typeof afterAmount, afterAmount); - console.log('[TEST] compress.test - totalCompressed:', totalCompressed.toString());js/program-test/src/litesvm-rpc.ts (1)
399-415
: Filter getSignaturesForAddress by address (or document limitation).Currently returns all stored txs, ignoring the address parameter. If feasible, filter by address or note the limitation in code comments.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
js/compressed-token/tests/program-test/compress.test.ts
(1 hunks)js/compressed-token/tests/program-test/decompress-delegated.test.ts
(5 hunks)js/program-test/src/litesvm-rpc.ts
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
js/program-test/src/litesvm-rpc.ts (4)
js/program-test/src/index.ts (3)
LiteSVMRpc
(8-8)LiteSVMConfig
(14-14)createLiteSVMRpc
(8-8)js/program-test/src/test-rpc/test-rpc.ts (1)
TestRpc
(114-1120)js/program-test/src/types.ts (1)
LiteSVMConfig
(16-27)js/stateless.js/src/constants.ts (2)
defaultTestStateTreeAccounts
(236-244)featureFlags
(16-35)
js/compressed-token/tests/program-test/compress.test.ts (8)
js/stateless.js/src/rpc-interface.ts (1)
ParsedTokenAccount
(236-239)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/compressed-token/src/utils/get-token-pool-infos.ts (3)
TokenPoolInfo
(104-141)selectTokenPoolInfo
(173-187)getTokenPoolInfos
(37-93)js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(1002-1008)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-15)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
selectStateTreeInfo
(84-110)js/program-test/src/spl-token-utils.ts (1)
splCreateAssociatedTokenAccount
(78-115)js/compressed-token/src/program.ts (1)
CompressedTokenProgram
(600-1659)
js/compressed-token/tests/program-test/decompress-delegated.test.ts (3)
js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(1002-1008)js/program-test/src/spl-token-utils.ts (1)
splCreateAssociatedTokenAccount
(78-115)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: Forester e2e test
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
- GitHub Check: cli-v2
- GitHub Check: stateless-js-v2
- GitHub Check: stateless-js-v1
- GitHub Check: cli-v1
- GitHub Check: lint
🔇 Additional comments (1)
js/program-test/src/litesvm-rpc.ts (1)
203-225
: LGTM: Proper handling of legacy vs versioned raw transactions.The 0x80 mask detection and VersionedTransaction.deserialize path look correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
js/compressed-token/tests/program-test/compress-spl-token-account.test.ts
(10 hunks)js/compressed-token/tests/program-test/compress.test.ts
(1 hunks)js/compressed-token/tests/program-test/decompress-delegated.test.ts
(5 hunks)js/program-test/src/litesvm-rpc.ts
(1 hunks)js/stateless.js/src/state/bn.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- js/compressed-token/tests/program-test/decompress-delegated.test.ts
- js/compressed-token/tests/program-test/compress.test.ts
- js/compressed-token/tests/program-test/compress-spl-token-account.test.ts
- js/program-test/src/litesvm-rpc.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: lint
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
- GitHub Check: stateless-js-v1
- GitHub Check: cli-v1
- GitHub Check: cli-v2
- GitHub Check: stateless-js-v2
- GitHub Check: Forester e2e test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
js/compressed-token/tests/e2e/merge-token-accounts.test.ts (1)
60-60
: Remove.only
before merging.The
.only
modifier causes only this test to run, skipping the second test in the suite. This debug artifact should be removed to ensure full test coverage.Apply this diff:
- it.only('should merge all token accounts', async () => { + it('should merge all token accounts', async () => {js/compressed-token/tests/e2e/rpc-token-interop.test.ts (1)
255-346
: Add test-rpc parity and annotate missing stubs
- Mirror the RPC assertions in the multi-mint
getCompressedTokenAccountsByOwner
test by callingtestRpc.getCompressedTokenAccountsByOwner
and comparing its output torpc
.- Prefix the two
getCompressedMintTokenHolders
tests with[test-rpc missing]
sincetestRpc.getCompressedMintTokenHolders
currently throws “not implemented.”
🧹 Nitpick comments (6)
js/program-test/vitest.config.ts (2)
8-16
: Thread pool configuration is appropriate for native addon stability.The thread-based pool and single-thread execution are well-justified for avoiding litesvm native addon issues. Be aware this will serialize all tests, potentially increasing execution time.
Minor note:
fileParallelism: false
andsingleThread: true
achieve similar goals. ThesingleThread: true
option alone should ensure sequential execution. Consider removingfileParallelism: false
to reduce redundancy.Apply this diff to remove the redundant setting:
pool: "threads", - // Run all tests sequentially (no parallel test files) - fileParallelism: false, poolOptions: { threads: { // Run all tests sequentially in a single thread singleThread: true, }, },
4-19
: Consider adding explicit test environment and timeout configuration.For integration tests using native components like litesvm, explicit configuration can improve clarity and prevent timeouts:
- Test environment: Explicitly specify
environment: 'node'
to document the intended runtime- Timeout: Add
testTimeout
(e.g., 30000ms) for potentially slower integration testsApply this diff to add recommended configuration:
test: { + environment: 'node', + testTimeout: 30000, // 30s for integration tests // litesvm fails with bad alloc if not configured // Use threads pool instead of forks to avoid native addon corruptionjs/compressed-token/tests/e2e/merge-token-accounts.test.ts (1)
84-113
: Consider removing debug console.log statements.Lines 87, 93, 102, 104, and 111 contain console.log/console.error statements that appear to be debug artifacts. Consider removing them or replacing with proper test assertions or structured logging if the debug information is needed.
.github/workflows/js.yml (1)
103-106
: Add retry logic for consistency.The existing stateless.js and compressed-token jobs retry once to absorb intermittent flakes; consider wrapping the new program-test test command in the same retry loop to keep CI stability consistent.
js/compressed-token/tests/program-test/compress-spl-token-account.test.ts (2)
256-256
: Use bn-based comparison for consistency.This direct comparison is inconsistent with other balance assertions in the test that use
bn()
for numerical comparisons. While it may work, maintaining a consistent pattern improves readability and reduces potential type coercion issues.Apply this diff:
- expect(balanceAfter.value.amount).toBe(balanceBefore.value.amount); + expect(bn(String(balanceAfter.value.amount)).eq(bn(String(balanceBefore.value.amount)))).toBe(true);
115-115
: Defensive String() conversions are safe but potentially redundant.The
String()
conversions ensure type safety before passing tobn()
, which is a reasonable defensive practice. However,value.amount
fromgetTokenAccountBalance
likely already returns a string or number thatbn()
handles directly (as per thebn
function signature acceptingstring | number | BN | Buffer | Uint8Array | number[]
).The current approach is safe and the explanatory comments make the intent clear, so this is acceptable as-is.
Also applies to: 130-130, 198-198, 214-214, 245-245, 382-382, 402-402
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
.github/workflows/js-v2.yml
(3 hunks).github/workflows/js.yml
(3 hunks)js/compressed-token/tests/e2e/merge-token-accounts.test.ts
(1 hunks)js/compressed-token/tests/e2e/rpc-token-interop.test.ts
(1 hunks)js/compressed-token/tests/program-test/compress-spl-token-account.test.ts
(15 hunks)js/compressed-token/vitest.config.ts
(1 hunks)js/program-test/vitest.config.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- js/compressed-token/vitest.config.ts
- .github/workflows/js-v2.yml
🧰 Additional context used
🧬 Code graph analysis (1)
js/compressed-token/tests/program-test/compress-spl-token-account.test.ts (3)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(1002-1008)js/program-test/src/spl-token-utils.ts (2)
splCreateAssociatedTokenAccount
(78-115)splMintTo
(120-149)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: cli-v1
- GitHub Check: lint
- GitHub Check: stateless-js-v2
- GitHub Check: Forester e2e test
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
- GitHub Check: cli-v2
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
- GitHub Check: stateless-js-v1
🔇 Additional comments (7)
js/program-test/vitest.config.ts (1)
1-2
: LGTM!Correct usage of
defineConfig
fromvitest/config
for Vitest 3.x.js/compressed-token/tests/e2e/merge-token-accounts.test.ts (2)
12-12
: LGTM! Import migration aligns with PR objectives.The import of
getTestRpc
from@lightprotocol/program-test
correctly reflects the migration of test utilities to the new program-test package.
83-84
: TODO acknowledged for edge case coverage.The TODO comment notes that additional coverage is needed for the edge case of merging when only one account exists. The test below provides basic coverage, but additional scenarios may be beneficial when prioritized.
.github/workflows/js.yml (1)
24-24
: Confirm warp runner availability.Please double-check that this repository/organization is authorized for the
warp-ubuntu-latest-x64-4x
GitHub-hosted runner; otherwise this job will fail immediately at queue time.js/compressed-token/tests/program-test/compress-spl-token-account.test.ts (1)
16-23
: LGTM! Clean migration to program-test utilities.The migration from legacy test helpers to
@lightprotocol/program-test
is well-executed:
createLiteSVMRpc
properly replaces the old RPC setupsplCreateAssociatedTokenAccount
andsplMintTo
are correctly used with appropriate signatures- Import consolidation improves maintainability
Also applies to: 44-44, 64-69
js/compressed-token/tests/e2e/rpc-token-interop.test.ts (2)
214-244
: Tests appropriately document test-rpc feature gaps.The test names clearly indicate that
getSignaturesForTokenOwner
(line 214) andgetTransactionWithCompressionInfo
(line 227) are not yet implemented in test-rpc. This is an acceptable approach for tracking pending test-rpc feature parity.
19-19
: Verified getTestRpc import from program-testgetTestRpc is exported in
js/program-test/src/test-rpc/test-rpc.ts
and used consistently across tests; the import change is correct.
console.log('totalCompressedAmount ', totalCompressedAmount); | ||
console.log('ataBalanceBefore', ataBalanceBefore); | ||
console.log( | ||
'ataBalanceBefore.value.amount ', | ||
ataBalanceBefore.value.amount, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove debug console.log statements.
These debug logging statements should be removed before merging to keep test output clean and professional.
Apply this diff:
- console.log('totalCompressedAmount ', totalCompressedAmount);
- console.log('ataBalanceBefore', ataBalanceBefore);
- console.log(
- 'ataBalanceBefore.value.amount ',
- ataBalanceBefore.value.amount,
- );
- // Defensive type conversion: ensure amount is always a string before passing to bn()
+ // Defensive type conversion: ensure amount is always a string before passing to bn()
📝 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.
console.log('totalCompressedAmount ', totalCompressedAmount); | |
console.log('ataBalanceBefore', ataBalanceBefore); | |
console.log( | |
'ataBalanceBefore.value.amount ', | |
ataBalanceBefore.value.amount, | |
); | |
// Defensive type conversion: ensure amount is always a string before passing to bn() |
🤖 Prompt for AI Agents
In js/compressed-token/tests/program-test/compress-spl-token-account.test.ts
around lines 393 to 398, there are debug console.log statements that should be
removed; delete the three console.log calls (totalCompressedAmount,
ataBalanceBefore, ataBalanceBefore.value.amount) so the test output stays clean
and ensure no other debugging logs remain in this test file.
6376344
to
3b7c4c6
Compare
a35d8a5
to
82f0ed4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
js/stateless.js/package.json (1)
73-85
: Replace eslint-plugin-vitest with @vitest/eslint-plugin
eslint-plugin-vitest v0.5.4 is unmaintained and breaks with Vitest v2.x. In package.json, remove it and install "@vitest/eslint-plugin" (v1.x+), then update your ESLint config to use the new plugin.sdk-tests/sdk-anchor-test/tests/test_v2.ts (1)
70-88
: Pass the correct address queue (signature + call + proof input)The V2 validity proof request sets queue to addressTree; pass the actual address queue and include it in the function signature.
@@ - const addressTreeInfo = await rpc.getAddressTreeInfoV2(); - const addressTree = addressTreeInfo.tree; + const addressTreeInfo = await rpc.getAddressTreeInfoV2(); + const addressTree = addressTreeInfo.tree; @@ - await createCompressedAccount( + await createCompressedAccount( rpc, addressTree, + addressTreeInfo.queue, address, program, outputQueue, signer, name ); @@ -async function createCompressedAccount( +async function createCompressedAccount( rpc: Rpc, addressTree: web3.PublicKey, + addressQueue: web3.PublicKey, address: web3.PublicKey, program: Program<SdkAnchorTest>, outputMerkleTree: web3.PublicKey, signer: web3.Keypair, name: string ) { @@ - { - tree: addressTree, - queue: addressTree, - address: bn(address.toBytes()), - }, + { + tree: addressTree, + queue: addressQueue, + address: bn(address.toBytes()), + },Also applies to: 141-149, 150-159
♻️ Duplicate comments (20)
js/program-test/src/test-rpc/get-compressed-token-accounts.ts (2)
57-79
: Past concern remains: Non-token outputs cause hard failures.As noted in the previous review,
parseTokenLayoutWithIdl
throws when the account owner doesn't match the expected program ID (lines 67-70). If events contain non-token accounts, this will cause the parser to fail.Consider returning
null
instead of throwing to make parsing resilient to mixed account types:export function parseTokenLayoutWithIdl( compressedAccount: CompressedAccountLegacy, programId: PublicKey = COMPRESSED_TOKEN_PROGRAM_ID, ): TokenData | null { if (compressedAccount.data === null) return null; const { data } = compressedAccount.data; if (data.length === 0) return null; if (compressedAccount.owner.toBase58() !== programId.toBase58()) { - throw new Error( - `Invalid owner ${compressedAccount.owner.toBase58()} for token layout`, - ); + return null; } try { const decoded = TokenDataLayout.decode(Buffer.from(data)); return decoded; } catch (error) { console.error("Decoding error:", error); throw error; } }
85-140
: Past concern remains: Event parsing throws on non-token accounts.As noted in the previous review,
parseEventWithTokenTlvData
unconditionally attempts to decode every output account, throwing on missing data (line 118) or invalid token data (line 122). This will fail if events contain non-token accounts.Consider filtering non-token or data-less accounts during event parsing:
const outputCompressedAccountsWithParsedTokenData: ParsedTokenAccount[] = - event.outputCompressedAccounts.map((compressedAccount, i) => { + event.outputCompressedAccounts + .map((compressedAccount, i) => { const maybeTree = pubkeyArray[event.outputCompressedAccounts[i].merkleTreeIndex]; const treeInfo = getStateTreeInfoByPubkey(infos, maybeTree); if ( !treeInfo.tree.equals( pubkeyArray[event.outputCompressedAccounts[i].merkleTreeIndex], ) && (featureFlags.isV2() ? !treeInfo.queue.equals( pubkeyArray[event.outputCompressedAccounts[i].merkleTreeIndex], ) : true) ) { throw new Error("Invalid tree"); } const merkleContext: MerkleContext = { treeInfo, hash: bn(outputHashes[i]), leafIndex: event.outputLeafIndices[i], // V2 trees are always proveByIndex in test-rpc. proveByIndex: treeInfo.treeType === TreeType.StateV2, }; - if (!compressedAccount.compressedAccount.data) throw new Error("No data"); + if (!compressedAccount.compressedAccount.data) return null; const parsedData = parseTokenLayoutWithIdl( compressedAccount.compressedAccount, ); - if (!parsedData) throw new Error("Invalid token data"); + if (!parsedData) return null; const withMerkleContext = createCompressedAccountWithMerkleContextLegacy( merkleContext, compressedAccount.compressedAccount.owner, compressedAccount.compressedAccount.lamports, compressedAccount.compressedAccount.data, compressedAccount.compressedAccount.address ?? undefined, ); return { compressedAccount: withMerkleContext, parsed: parsedData, }; - }); + }) + .filter((x): x is ParsedTokenAccount => x !== null);js/program-test/src/merkle-tree/indexed-array.ts (1)
231-246
: highestElementIndex never updates due to post-mutation checkYou check lowElement.nextIndex after newElementWithLowElementIndex mutates it, so the condition is never true. Capture the pre-call state and use it after insertion.
Apply this diff:
public appendWithLowElementIndex( lowElementIndex: number, value: BN, ): IndexedElementBundle { const lowElement = this.elements[lowElementIndex]; + const wasLowElementHighest = lowElement.nextIndex === 0; - if (lowElement.nextIndex === 0) { + if (wasLowElementHighest) { if (value.lte(lowElement.value)) { throw new Error( "New element value must be greater than the low element value.", ); } } else { const nextElement = this.elements[lowElement.nextIndex]; if (value.lte(lowElement.value)) { throw new Error( "New element value must be greater than the low element value.", ); } if (value.gte(nextElement.value)) { throw new Error( "New element value must be less than the next element value.", ); } } const newElementBundle = this.newElementWithLowElementIndex( lowElementIndex, value, ); - if (lowElement.nextIndex === 0) { + if (wasLowElementHighest) { this.highestElementIndex = newElementBundle.newElement.index; }js/program-test/src/test-rpc/get-parsed-events.ts (2)
95-111
: Null-guard txRaw/meta and drop non-null assertions
txRaw
can be null; accessingtxRaw!.meta!.innerInstructions
is unsafe. Guard and use the guarded variable.- if ( - txRaw!.meta!.innerInstructions && - txRaw!.meta!.innerInstructions.length > 0 - ) { - for (const innerGroup of txRaw!.meta!.innerInstructions) { + if (!txRaw || !txRaw.meta) { + continue; + } + if (txRaw.meta.innerInstructions && txRaw.meta.innerInstructions.length > 0) { + for (const innerGroup of txRaw.meta.innerInstructions) {
257-262
: Guard empty accountKeys before indexing last groupIf no inner instructions produced groups,
accountKeys[accountKeys.length - 1]
is undefined.- if (invokeData) { - return convertToPublicTransactionEvent( - appendInputsData, - accountKeys[accountKeys.length - 1], - invokeData, - ); + if (invokeData) { + if (!accountKeys || accountKeys.length === 0) { + return null; + } + return convertToPublicTransactionEvent( + appendInputsData, + accountKeys[accountKeys.length - 1], + invokeData, + );js/program-test/src/litesvm-rpc.ts (2)
36-59
: Trim verbose console logging or guard behind a debug flagConstructor, program loading, fixtures, sendTransaction, and query methods log extensively and will spam CI.
- Remove console.log or guard with an env/config (e.g., if (process.env.LITESVM_DEBUG) ...).
- Keep only error/warn logs for failures.
Also applies to: 116-134, 145-198, 323-328, 353-356, 600-605, 613-627, 655-659, 668-674, 690-693, 738-741, 767-770, 822-825, 921-924
488-501
: JSON-RPC contract: signatures must be base58 stringsYou’re returning raw signature objects/arrays from Transaction/VersionedTransaction. getTransaction.transaction.signatures must be string[].
transaction: { message: { @@ - signatures: - "signatures" in rawTx ? rawTx.signatures : [(rawTx as any).signature], + // Return base58 strings per JSON-RPC + signatures: [sigString], },js/compressed-token/tests/program-test/decompress-delegated.test.ts (1)
43-46
: Remove debug console logs from testsThese logs are noisy and unnecessary in CI.
- const recipientBalance = await rpc.getTokenAccountBalance(recipient); - console.log('[TEST] getBalances - recipientBalance:', JSON.stringify(recipientBalance)); - console.log('[TEST] getBalances - recipientBalance.value:', recipientBalance.value); - console.log('[TEST] getBalances - recipientBalance.value.amount:', recipientBalance.value?.amount); + const recipientBalance = await rpc.getTokenAccountBalance(recipient); @@ - // Defensive type conversion: ensure amount is always a string before passing to bn() - console.log('[TEST] assertDecompress - initialBalances.recipient.value.amount:', typeof initialBalances.recipient.value.amount, initialBalances.recipient.value.amount); - console.log('[TEST] assertDecompress - finalBalances.recipient.value.amount:', typeof finalBalances.recipient.value.amount, finalBalances.recipient.value.amount); - console.log('[TEST] assertDecompress - amount:', amount.toString()); + // Defensive type conversion: ensure amount is always a string before passing to bn() @@ - console.log('[TEST] assertDecompress - expectedRecipientBalance:', expectedRecipientBalance.toString()); - console.log('[TEST] assertDecompress - actualRecipientBalance:', actualRecipientBalance.toString());Also applies to: 83-86, 92-94
js/program-test/tests/rpc-multi-trees.test.ts (1)
34-38
: Use the LiteSVM RPC (drop external createRpc)Replace createRpc() with the in-process LiteSVM RPC to make this spec self-contained in CI. This was flagged previously.
-import { - Rpc, - createRpc, +import { + Rpc, LightSystemProgram, TreeInfo, bn, compress, createAccount, createAccountWithLamports, deriveAddress, deriveAddressSeed, featureFlags, selectStateTreeInfo, transfer, - newAccountWithLamports, } from "@lightprotocol/stateless.js"; -import { getTestRpc, TestRpc } from "../src"; +import { getTestRpc, TestRpc, createLiteSVMRpc, newAccountWithLamports } from "../src"; @@ - const lightWasm = await NobleHasherFactory.getInstance(); - rpc = createRpc(); + const lightWasm = await NobleHasherFactory.getInstance(); + rpc = await createLiteSVMRpc(lightWasm);js/program-test/tests/rpc-interop.test.ts (2)
3-24
: Import program-test utilities here (newAccountWithLamports, sleep) and remove from statelessAlign with program-test API and correct function arity.
import { Rpc, - createRpc, LightSystemProgram, TreeInfo, bn, compress, createAccount, createAccountWithLamports, deriveAddress, deriveAddressSeed, featureFlags, - getDefaultAddressTreeInfo, - selectStateTreeInfo, - sleep, - transfer, - newAccountWithLamports, + getDefaultAddressTreeInfo, + selectStateTreeInfo, + transfer, } from "@lightprotocol/stateless.js"; -import { getTestRpc, TestRpc } from "../src"; +import { getTestRpc, TestRpc, createLiteSVMRpc, newAccountWithLamports, sleep } from "../src"; import { NobleHasherFactory } from "../src";
54-62
: Use in-process LiteSVM RPC and fix newAccountWithLamports arityAvoid external endpoints and update helper calls.
- const lightWasm = await NobleHasherFactory.getInstance(); - rpc = createRpc(); - - testRpc = await getTestRpc(lightWasm); + const lightWasm = await NobleHasherFactory.getInstance(); + rpc = await createLiteSVMRpc(lightWasm); + testRpc = await getTestRpc(lightWasm); @@ - payer = await newAccountWithLamports(rpc, 10e9, 256); - bob = await newAccountWithLamports(rpc, 10e9, 256); + payer = await newAccountWithLamports(rpc, 10e9); + bob = await newAccountWithLamports(rpc, 10e9);sdk-tests/sdk-anchor-test/tests/test_v2.ts (1)
14-22
: Import missing types/enums (SdkAnchorTest, TreeType)Program is used and treeType comparisons should use the enum. Add imports.
import { bn, type CompressedAccountWithMerkleContext, - defaultTestStateTreeAccounts, deriveAddressSeedV2, deriveAddressV2, PackedAccounts, type Rpc, SystemAccountMetaConfig, + TreeType, } from "@lightprotocol/stateless.js"; +import type { SdkAnchorTest } from "../target/types/sdk_anchor_test";js/program-test/src/test-rpc/test-rpc.ts (4)
552-566
: Explicitly validate V2 proofs or document intentional skipCurrently only V1 proofs are validated. Either add a V2 branch to assert expected defaults (proveByIndex, pathElements/indices) or comment why it's intentionally skipped.
Example:
if (proof.treeInfo.treeType === TreeType.StateV2) { if (!proof.proveByIndex) throw new Error("V2 proofs must be proveByIndex"); // Optionally assert zeros length and leafIndex bounds }
274-291
: Remove debug console.log noise from getMultipleCompressedAccountProofsThese logs are very verbose and clutter CI. Remove them or guard by a debug flag.
- console.log("[TEST-RPC] getMultipleCompressedAccountProofs: INPUT - hashes:", ...); - console.log("[TEST-RPC] getMultipleCompressedAccountProofs: Calling getParsedEvents..."); - console.log("[TEST-RPC] getMultipleCompressedAccountProofs: Got", events.length, "events"); - console.log("[TEST-RPC] getMultipleCompressedAccountProofs: Processing", leavesByTree.size, "trees"); - console.log("[TEST-RPC] getMultipleCompressedAccountProofs: Processing tree:", treeKey, ...); - console.log("[TEST-RPC] treeInfo.treeType value:", ...); - console.log("[TEST-RPC] getMultipleCompressedAccountProofs: Creating V1 MerkleTree with depth", this.depth); - console.log("[TEST-RPC] getMultipleCompressedAccountProofs: All leaves:", ...); - console.log("[TEST-RPC] getMultipleCompressedAccountProofs: Leaf strings:", ...); - console.log("[TEST-RPC] getMultipleCompressedAccountProofs: Calling new MerkleTree..."); - console.log("[TEST-RPC] getMultipleCompressedAccountProofs: MerkleTree created successfully"); - console.log("[TEST-RPC] Starting hash matching loop, hashes.length:", ...); - console.log("[TEST-RPC] Processing hash[${i}]...", ...); - console.log("[TEST-RPC] Finding leafIndex in", leaves.length, "leaves"); - console.log("[TEST-RPC] leaves[0] sample:", ...); - console.log("[TEST-RPC] Found leafIndex:", leafIndex); - console.log("[TEST-RPC] getMultipleCompressedAccountProofs: Returning proofs for", hashes.length, "hashes"); - console.log("[TEST-RPC] getMultipleCompressedAccountProofs: OUTPUT - Success, returning", results.length, "proofs"); + // Consider: if (this.log) console.debug("...") for selective debuggingAlso applies to: 340-366, 367-411, 416-437, 443-471, 584-602
671-686
: Remove debug logs in token balance helpersDelete console.log lines during amount conversion.
- console.log( - "[test-rpc.ts:524] Converting amount:", - typeof rawAmount, - rawAmount, - ); ... - console.log( - "[test-rpc.ts:543] Converting amount:", - typeof rawAmount, - rawAmount, - ); ... - console.log( - "[test-rpc.ts:567] Converting amount:", - typeof rawAmount, - rawAmount, - );Also applies to: 703-718, 736-757
376-408
: Build V1 MerkleTree with on-chain indices (sparse), not dense array orderUsing
leaves.map(...)
drops actual on-chain indices, producing invalid roots/proofs when indices are sparse. Materialize level-0 by trueleafIndices
.- // Detailed logging for each leaf - const leafStrings = leaves.map((leaf, idx) => { - try { - const leafBn = bn(leaf); - const leafStr = leafBn.toString(); - console.log(`[TEST-RPC] Leaf[${idx}]:`, { - raw: JSON.stringify(leaf).slice(0, 100), - bn: leafBn.toString(16).slice(0, 32) + "...", - decimal: leafStr, - length: leafStr.length, - valid: /^[0-9]+$/.test(leafStr), - }); - return leafStr; - } catch (err) { - console.log( - `[TEST-RPC] ERROR converting leaf[${idx}]:`, - err, - "raw:", - JSON.stringify(leaf).slice(0, 100), - ); - throw err; - } - }); - console.log( - "[TEST-RPC] getMultipleCompressedAccountProofs: Leaf strings:", - JSON.stringify(leafStrings), - ); - console.log( - "[TEST-RPC] getMultipleCompressedAccountProofs: Calling new MerkleTree...", - ); - merkleTree = new MerkleTree(this.depth, this.lightWasm, leafStrings); + // Materialize sparse leaves at their real on-chain indices + const treeData = leavesByTree.get(treeKey)!; + const maxLeafIndex = Math.max(...treeData.leafIndices); + const zeroLeaf = bn(0).toString(); + const leafStrings = Array.from({ length: maxLeafIndex + 1 }, () => zeroLeaf); + for (let i = 0; i < leaves.length; i++) { + const onChainIdx = treeData.leafIndices[i]; + leafStrings[onChainIdx] = bn(leaves[i]).toString(); + } + merkleTree = new MerkleTree(this.depth, this.lightWasm, leafStrings);js/compressed-token/tests/program-test/compress-spl-token-account.test.ts (1)
393-400
: Remove leftover debug logsDelete the console.log statements to keep test output clean.
- console.log('totalCompressedAmount ', totalCompressedAmount); - console.log('ataBalanceBefore', ataBalanceBefore); - console.log( - 'ataBalanceBefore.value.amount ', - ataBalanceBefore.value.amount, - ); - // Defensive type conversion: ensure amount is always a string before passing to bn() + // Defensive type conversion: ensure amount is always a string before passing to bn()js/program-test/src/merkle-tree/merkle-tree.ts (1)
204-213
: Critical: deserialize doesn’t initialize _lightWasm (methods will throw)Deserialized instances lack
_lightWasm
, soinsert/bulkInsert/update/path
crash. Accept a LightWasm and set it.- static deserialize( - data: any, - hashFunction: (left: string, right: string) => string, - ) { - const instance = Object.assign(Object.create(this.prototype), data); - instance._hash = hashFunction; - instance.capacity = 2 ** instance.levels; - instance.zeroElement = instance._zeros[0]; - return instance; - } + static deserialize(data: any, lightWasm: LightWasm) { + const instance = Object.assign(Object.create(this.prototype), data); + instance._lightWasm = lightWasm; + instance.capacity = 2 ** instance.levels; + instance.zeroElement = instance._zeros[0]; + return instance; + }js/program-test/src/spl-token-utils.ts (2)
201-205
: Remove leftover debug logging.This
console.log
statement will spam test output and should have been removed per previous review feedback. Production test utilities should not include debug logging.Apply this diff:
const data = AccountLayout.decode(accountInfo.data); - console.log( - "[spl-token-utils.ts:195] Converting amount:", - typeof data.amount, - data.amount, - ); const amount = typeof data.amount === "bigint" ? data.amount : BigInt((data.amount as any).toString());
233-237
: Remove leftover debug logging.This
console.log
statement will spam test output and should have been removed per previous review feedback.Apply this diff:
const data = MintLayout.decode(accountInfo.data); - console.log( - "[spl-token-utils.ts:223] Converting supply:", - typeof data.supply, - data.supply, - ); - const supply = typeof data.supply === "bigint" ? data.supply : BigInt((data.supply as any).toString());
🧹 Nitpick comments (21)
js/program-test/vitest.config.ts (1)
3-19
: Configuration is appropriate for litesvm constraints.The sequential execution setup (threads pool + no file parallelism + single thread) correctly addresses litesvm's concurrency limitations documented in the comments. The triple-layered approach ensures safety with the native addon.
Optionally consider adding timeout configurations for integration tests:
export default defineConfig({ test: { + testTimeout: 30000, + hookTimeout: 30000, // litesvm fails with bad alloc if not configured // Use threads pool instead of forks to avoid native addon corruptionThis would help prevent timeouts on slower integration tests with litesvm, though the current defaults may be sufficient.
js/compressed-token/tests/program-test/delegate.test.ts (1)
11-15
: LGTM! Migration to program-test package is correct.The imports for
createLiteSVMRpc
,newAccountWithLamports
, andNobleHasherFactory
from@lightprotocol/program-test
are correct and align with the new test infrastructure.Consider consolidating the imports from
@lightprotocol/program-test
into a single import statement for consistency:-import { - createLiteSVMRpc, - newAccountWithLamports, -} from '@lightprotocol/program-test'; -import { NobleHasherFactory } from '@lightprotocol/program-test'; +import { + createLiteSVMRpc, + newAccountWithLamports, + NobleHasherFactory, +} from '@lightprotocol/program-test';js/stateless.js/src/utils/send-and-confirm.ts (1)
87-125
: Add exception handling for RPC errors.If
rpc.getSignatureStatuses([txId])
throws an exception (network error, rate limiting, etc.), the interval continues running and the promise remains pending until the 80-second timeout, masking the actual error with a generic timeout message.Wrap the RPC call in a try-catch block:
const intervalId = setInterval(async () => { elapsed += interval; if (elapsed >= timeout) { clearInterval(intervalId); reject( new Error(`Transaction ${txId}'s confirmation timed out`), ); + return; } - const status = await rpc.getSignatureStatuses([txId]); - const result = status?.value[0]; + try { + const status = await rpc.getSignatureStatuses([txId]); + const result = status?.value[0]; - // Check for transaction errors - if (result?.err) { - clearInterval(intervalId); - reject( - new Error( - `Transaction ${txId} failed: ${JSON.stringify(result.err)}`, - ), - ); - return; - } + // Check for transaction errors + if (result?.err) { + clearInterval(intervalId); + reject( + new Error( + `Transaction ${txId} failed: ${JSON.stringify(result.err)}`, + ), + ); + return; + } - const statusValue = result?.confirmationStatus; - // Check if the status meets or exceeds the requested commitment - // finalized > confirmed > processed - const meetsCommitment = - statusValue === commitment || - (commitment === 'processed' && - (statusValue === 'confirmed' || - statusValue === 'finalized')) || - (commitment === 'confirmed' && statusValue === 'finalized'); + const statusValue = result?.confirmationStatus; + // Check if the status meets or exceeds the requested commitment + // finalized > confirmed > processed + const meetsCommitment = + statusValue === commitment || + (commitment === 'processed' && + (statusValue === 'confirmed' || + statusValue === 'finalized')) || + (commitment === 'confirmed' && statusValue === 'finalized'); - if (meetsCommitment) { - clearInterval(intervalId); - resolve(txId); - } + if (meetsCommitment) { + clearInterval(intervalId); + resolve(txId); + } + } catch (error) { + clearInterval(intervalId); + reject( + new Error( + `RPC error while confirming transaction ${txId}: ${error instanceof Error ? error.message : String(error)}`, + ), + ); + } }, interval);js/stateless.js/package.json (3)
98-98
: Run both E2E tests in a single Vitest process for speedAvoid double startup by invoking both files in one run.
- "test:e2e:all": "vitest run tests/e2e/layout.test.ts && vitest run tests/e2e/safe-conversion.test.ts", + "test:e2e:all": "vitest run tests/e2e/layout.test.ts tests/e2e/safe-conversion.test.ts",
38-40
: Exclude compromised @solana/web3.js versions in peer rangeProtect downstreams by excluding 1.95.6 and 1.95.7 from the allowed peer range.
- "@solana/web3.js": ">=1.73.5" + "@solana/web3.js": ">=1.73.5 <1.95.6 || >=1.95.8"Based on learnings
42-42
: Remove duplicate dependency: @coral-xyz/borshPackage is listed in both dependencies and devDependencies. Keep it only in dependencies.
"devDependencies": { - "@coral-xyz/anchor": "^0.29.0", - "@coral-xyz/borsh": "^0.29.0", + "@coral-xyz/anchor": "^0.29.0",Also applies to: 54-54
js/program-test/rollup.config.js (1)
25-36
: Avoid duplicate declaration emission with dts bundlingIf tsconfig has declaration: true, the TS plugin may emit .d.ts into dist/{fmt} while rollup-plugin-dts also writes dist/types/index.d.ts. Disable declarations for runtime builds or move dts input to the generated index.d.ts to prevent conflicts.
Apply one of:
- Set declaration: false in the plugin options:
typescript({ target: fmt === "es" ? "ES2022" : "ES2017", outDir: `dist/${fmt}`, rootDir: "src", + declaration: false, }),
- Or change the DTS input to the tsc output (if you enable declarations in tsconfig):
-const typesConfig = { - input: "src/index.ts", +const typesConfig = { + input: "dist/types/index.d.ts", output: [{ file: "dist/types/index.d.ts", format: "es" }], plugins: [dts()], };js/program-test/src/merkle-tree/indexed-array.ts (1)
24-35
: Preserve error context in hash()Catching and rethrowing "Hashing failed" hides the root cause. Include the original error message or use Error cause.
public hash(lightWasm: LightWasm, nextValue: BN): Uint8Array { try { const hash = lightWasm.poseidonHash([ bn(this.value.toArray("be", 32)).toString(), bn(this.nextIndex).toString(), bn(nextValue.toArray("be", 32)).toString(), ]); return hash; } catch (error) { - throw new Error("Hashing failed"); + throw new Error( + `Hashing failed: ${error instanceof Error ? error.message : String(error)}` + ); } }js/compressed-token/package.json (1)
79-97
: Ensure program-test is built before local testsCI wires a dependsOn for program-test, but local
pnpm test
may fail if@lightprotocol/program-test
isn’t built. Add a pretest to build it."scripts": { + "pretest": "pnpm --filter @lightprotocol/program-test build", "test": "pnpm test:program-test && pnpm test:e2e:all",
Also verify eslint-plugin-vitest (^0.5.4) is compatible with your Vitest version (^2.1.1); upgrade the plugin if needed.
js/compressed-token/tests/program-test/transfer-delegated.test.ts (1)
406-412
: Make failure assertion less brittleError messages change over time; assert on a stable substring instead of the exact string.
- ).rejects.toThrowError( - 'Insufficient balance for transfer. Required: 500, available: 400.', - ); + ).rejects.toThrow(/Insufficient balance for transfer/i);js/program-test/tests/merkle-tree.test.ts (1)
35-38
: Minor naming nitWasmFactory actually points to NobleHasherFactory. Consider renaming the variable to NobleHasherFactory for clarity.
js/compressed-token/tests/program-test/decompress-delegated.test.ts (1)
30-34
: Type shape suggestion for BalanceInfo.recipientgetTokenAccountBalance returns an object with context/value; your interface only models value.amount. Either narrow at callsite or adjust the type to match the response to improve type safety.
js/program-test/src/litesvm-rpc.ts (1)
910-919
: Avoid precision loss for uiAmount; build uiAmountString via bigint mathUsing Number() can lose precision for large amounts. Keep uiAmountString exact and set uiAmount to null when unsafe.
- value: { - amount: amountBigInt.toString(), // Use toString() instead of toLocaleString() to ensure consistent string format - decimals, - uiAmount: Number(amountBigInt) / Math.pow(10, decimals), - uiAmountString: ( - Number(amountBigInt) / Math.pow(10, decimals) - ).toString(), - }, + value: { + amount: amountBigInt.toString(), + decimals, + uiAmount: amountBigInt <= BigInt(Number.MAX_SAFE_INTEGER) + ? Number(amountBigInt) / Math.pow(10, decimals) + : null, + // Exact string math + uiAmountString: (() => { + const scale = BigInt(10) ** BigInt(decimals); + const intPart = amountBigInt / scale; + const fracPart = amountBigInt % scale; + if (decimals === 0) return intPart.toString(); + const frac = fracPart.toString().padStart(decimals, "0"); + return `${intPart.toString()}.${frac}`; + })(), + },js/program-test/src/hasher/noble-hasher.ts (2)
4-4
: Use type-only import for LightWasm to avoid runtime dependency/cycleImporting the interface as a value can create an unnecessary runtime dependency and potential cycles. Switch to a type-only import.
-import { LightWasm } from "../test-rpc/test-rpc"; +import type { LightWasm } from "../test-rpc/test-rpc";
87-104
: Deduplicate hashing logic across the three methodsAll three methods share the same length checks, state construction, and permutation. Extract a private helper that returns bigint, and keep thin wrappers for Uint8Array/string/BN.
export class NobleHasher implements LightWasm { + private hashToBigInt(input: string[] | BN[]): bigint { + const inputs = toBigIntArray(input); + if (inputs.length === 2) { + return poseidonNoble3([POSEIDON_CAPACITY, inputs[0], inputs[1]])[0]; + } else if (inputs.length === 3) { + return poseidonNoble4([POSEIDON_CAPACITY, inputs[0], inputs[1], inputs[2]])[0]; + } + throw new Error(`Expected 2 or 3 inputs, got ${inputs.length}`); + } poseidonHash(input: string[] | BN[]): Uint8Array { - const inputs = toBigIntArray(input); - let hash: bigint; - if (inputs.length === 2) { - const state = poseidonNoble3([POSEIDON_CAPACITY, inputs[0], inputs[1]]); - hash = state[0]; - } else if (inputs.length === 3) { - const state = poseidonNoble4([POSEIDON_CAPACITY, inputs[0], inputs[1], inputs[2]]); - hash = state[0]; - } else { - throw new Error(`Expected 2 or 3 inputs, got ${inputs.length}`); - } - return bigintToUint8Array(hash); + return bigintToUint8Array(this.hashToBigInt(input)); } poseidonHashString(input: string[] | BN[]): string { - const inputs = toBigIntArray(input); - let hash: bigint; - if (inputs.length === 2) { - const state = poseidonNoble3([POSEIDON_CAPACITY, inputs[0], inputs[1]]); - hash = state[0]; - } else if (inputs.length === 3) { - const state = poseidonNoble4([POSEIDON_CAPACITY, inputs[0], inputs[1], inputs[2]]); - hash = state[0]; - } else { - throw new Error(`Expected 2 or 3 inputs, got ${inputs.length}`); - } - return hash.toString(); + return this.hashToBigInt(input).toString(); } poseidonHashBN(input: string[] | BN[]): BN { - const inputs = toBigIntArray(input); - let hash: bigint; - if (inputs.length === 2) { - const state = poseidonNoble3([POSEIDON_CAPACITY, inputs[0], inputs[1]]); - hash = state[0]; - } else if (inputs.length === 3) { - const state = poseidonNoble4([POSEIDON_CAPACITY, inputs[0], inputs[1], inputs[2]]); - hash = state[0]; - } else { - throw new Error(`Expected 2 or 3 inputs, got ${inputs.length}`); - } - return bn(hash.toString()); + return bn(this.hashToBigInt(input).toString()); } }Also applies to: 111-128, 135-152
sdk-tests/sdk-anchor-test/tests/test_v2.ts (1)
61-64
: Avoid magic numbers for treeTypeUse the TreeType enum for clarity and safety.
- const stateTreeInfo = existingTreeInfos.find( - (info) => info.treeType === 2 || info.treeType === 3 - ); // StateV1 or StateV2 + const stateTreeInfo = existingTreeInfos.find( + (info) => + info.treeType === TreeType.StateV1 || info.treeType === TreeType.StateV2 + ); // StateV1 or StateV2js/program-test/tests/poseidon-comparison.test.ts (2)
282-355
: Silence verbose console output in testsDrop or gate logs behind an env flag to keep CI output clean.
Example:
-console.log("Light Protocol hash [0, 0]:", lightHash); -console.log("@noble/curves hash [0, 0]:", nobleHash[0].toString()); +if (process.env.VITEST_DEBUG) { + console.debug("Light Protocol hash [0, 0]:", lightHash); + console.debug("@noble/curves hash [0, 0]:", nobleHash[0].toString()); +}Also applies to: 382-400, 453-477, 497-499, 537-538, 575-576
32-49
: Avoid duplicating Poseidon constants; import from sourceImport MDS_3 and CONSTANTS_3_FLAT from js/program-test/src/hasher/constants.ts instead of re-declaring in the test to prevent drift.
-import * as mod from "@noble/curves/abstract/modular.js"; -import * as poseidon from "@noble/curves/abstract/poseidon.js"; -// ... manual MDS_3 and CONSTANTS_3_FLAT definitions ... +import * as mod from "@noble/curves/abstract/modular.js"; +import * as poseidon from "@noble/curves/abstract/poseidon.js"; +import { MDS_3, CONSTANTS_3_FLAT } from "../src/hasher/constants";Also applies to: 51-248
js/program-test/src/merkle-tree/merkle-tree.ts (1)
132-141
: JSDoc return type mismatchDocs mention pathIndex but code returns pathIndices and pathElements as string[]. Update the comment for accuracy.
- * @returns {{pathElements: number[], pathIndex: number[]}} An object containing adjacent elements and left-right index + * @returns {{ pathElements: string[]; pathIndices: number[] }} Adjacent elements and left-right indicesjs/program-test/src/spl-token-utils.ts (2)
62-70
: Replaceas any
cast with proper type conversion.The cast to
any
bypasses type safety and risks runtime errors if the underlying implementation expectations change. Consider one of these approaches:
- If
Transaction
should be supported, update theRpc
interface to acceptTransaction | VersionedTransaction
- Use a proper conversion method like
VersionedTransaction.fromTransaction(transaction)
if available- Build the transaction as a
VersionedTransaction
from the startThis pattern repeats in other functions (lines 112, 148, 184, 321), so a consistent solution should be applied across all of them.
282-283
: Consider removing unused parameters or documenting their purpose.The
commitment
andconfirmOptions
parameters are typed asany
and unused in the function body. If they exist for API compatibility with@solana/spl-token
, document that; otherwise, remove them to reduce confusion.If these parameters are intentionally present for future use or compatibility, add a JSDoc comment explaining:
/** * Get or create an associated token account * Replicates the behavior of getOrCreateAssociatedTokenAccount from @solana/spl-token * * @param commitment - Reserved for future use (currently unused) * @param confirmOptions - Reserved for future use (currently unused) */
console.log('[TEST] assertCompress - refSenderAtaBalanceAfter.value.amount:', typeof refSenderAtaBalanceAfter.value.amount, refSenderAtaBalanceAfter.value.amount); | ||
console.log('[TEST] assertCompress - afterAmountStr:', typeof afterAmountStr, afterAmountStr); | ||
console.log('[TEST] assertCompress - refSenderAtaBalanceBefore:', refSenderAtaBalanceBefore.toString()); | ||
console.log('[TEST] assertCompress - totalAmount:', totalAmount.toString()); | ||
console.log('[TEST] assertCompress - expected:', refSenderAtaBalanceBefore.sub(totalAmount).toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Remove debug console.log statements.
Multiple console.log statements are present throughout the test file (setup and assertions). These should be removed before merging to production.
Apply this diff to remove debug logging:
- console.log('post mint');
- console.log('post stateTreeInfo');
- console.log('post tokenPoolInfo');
- console.log('post bob');
- console.log('post charlie');
- console.log('post bobAta');
- console.log('post mintTo');
- console.log('post decompress');
- console.log('post lut');
- console.log('[TEST] assertCompress - refSenderAtaBalanceAfter.value.amount:', typeof refSenderAtaBalanceAfter.value.amount, refSenderAtaBalanceAfter.value.amount);
- console.log('[TEST] assertCompress - afterAmountStr:', typeof afterAmountStr, afterAmountStr);
- console.log('[TEST] assertCompress - refSenderAtaBalanceBefore:', refSenderAtaBalanceBefore.toString());
- console.log('[TEST] assertCompress - totalAmount:', totalAmount.toString());
- console.log('[TEST] assertCompress - expected:', refSenderAtaBalanceBefore.sub(totalAmount).toString());
- console.log('[TEST] compress.test - beforeAmount:', typeof beforeAmount, beforeAmount);
- console.log('[TEST] compress.test - afterAmount:', typeof afterAmount, afterAmount);
- console.log('[TEST] compress.test - totalCompressed:', totalCompressed.toString());
Also applies to: 135-184, 262-264
🤖 Prompt for AI Agents
In js/compressed-token/tests/program-test/compress.test.ts around lines 64 to 68
(and also remove similar debug logs at ranges 135-184 and 262-264), remove the
temporary console.log debug statements that print test internals; replace them
with nothing (delete the lines) or, if preserving context is necessary, use
proper test assertions or test framework logging instead; ensure no stray
console.log calls remain in those ranges before merging.
for (let i = 0; i < recipients.length; i++) { | ||
await assertCompress( | ||
rpc, | ||
bn(String(senderAtaBalanceBefore.value.amount)), | ||
bobAta, | ||
mint, | ||
amounts.slice(0, 11), | ||
recipients.slice(0, 11), | ||
recipientCompressedTokenBalancesBefore.map(x => x.items), | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Remove redundant loop.
Lines 242-252 loop through recipients but call assertCompress
with identical parameters each iteration, making the loop redundant. Remove the loop and call assertCompress
once.
Apply this diff:
- // Defensive type conversion: ensure amount is always a string before passing to bn()
- for (let i = 0; i < recipients.length; i++) {
- await assertCompress(
- rpc,
- bn(String(senderAtaBalanceBefore.value.amount)),
- bobAta,
- mint,
- amounts.slice(0, 11),
- recipients.slice(0, 11),
- recipientCompressedTokenBalancesBefore.map(x => x.items),
- );
- }
+ // Defensive type conversion: ensure amount is always a string before passing to bn()
+ await assertCompress(
+ rpc,
+ bn(String(senderAtaBalanceBefore.value.amount)),
+ bobAta,
+ mint,
+ amounts.slice(0, 11),
+ recipients.slice(0, 11),
+ recipientCompressedTokenBalancesBefore.map(x => x.items),
+ );
📝 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.
for (let i = 0; i < recipients.length; i++) { | |
await assertCompress( | |
rpc, | |
bn(String(senderAtaBalanceBefore.value.amount)), | |
bobAta, | |
mint, | |
amounts.slice(0, 11), | |
recipients.slice(0, 11), | |
recipientCompressedTokenBalancesBefore.map(x => x.items), | |
); | |
} | |
// Defensive type conversion: ensure amount is always a string before passing to bn() | |
await assertCompress( | |
rpc, | |
bn(String(senderAtaBalanceBefore.value.amount)), | |
bobAta, | |
mint, | |
amounts.slice(0, 11), | |
recipients.slice(0, 11), | |
recipientCompressedTokenBalancesBefore.map(x => x.items), | |
); |
🤖 Prompt for AI Agents
In js/compressed-token/tests/program-test/compress.test.ts around lines 242-252,
the code contains a redundant for-loop that repeatedly calls assertCompress with
the same arguments; remove the loop and replace it with a single call to
assertCompress using the existing parameters (rpc,
bn(String(senderAtaBalanceBefore.value.amount)), bobAta, mint, amounts.slice(0,
11), recipients.slice(0, 11), recipientCompressedTokenBalancesBefore.map(x =>
x.items)).
/// These are constant test accounts in between test runs | ||
payer = await newAccountWithLamports(rpc, 10e9, 256); | ||
bob = await newAccountWithLamports(rpc, 10e9, 256); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix newAccountWithLamports import and arity
Import from program-test and drop the third argument; current call signature causes TS/runtime errors.
- payer = await newAccountWithLamports(rpc, 10e9, 256);
- bob = await newAccountWithLamports(rpc, 10e9, 256);
+ payer = await newAccountWithLamports(rpc, 10e9);
+ bob = await newAccountWithLamports(rpc, 10e9);
📝 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.
/// These are constant test accounts in between test runs | |
payer = await newAccountWithLamports(rpc, 10e9, 256); | |
bob = await newAccountWithLamports(rpc, 10e9, 256); | |
/// These are constant test accounts in between test runs | |
- payer = await newAccountWithLamports(rpc, 10e9, 256); | |
payer = await newAccountWithLamports(rpc, 10e9); | |
bob = await newAccountWithLamports(rpc, 10e9); |
🤖 Prompt for AI Agents
In js/program-test/tests/rpc-multi-trees.test.ts around lines 45 to 48, the test
calls newAccountWithLamports with an incorrect import and a spurious third
argument which causes TypeScript/runtime errors; update the import to pull
newAccountWithLamports from the program-test module (where it is exported) and
remove the third numeric argument from both calls so they match the actual
function signature (e.g., newAccountWithLamports(rpc, 10e9)).
import path from "path"; | ||
import os from "os"; | ||
import dotenv from "dotenv"; | ||
dotenv.config(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing type import for Program
Program is used but SdkAnchorTest isn’t imported. Add the generated Anchor type to avoid TS errors.
import idl from "../target/idl/sdk_anchor_test.json";
+import type { SdkAnchorTest } from "../target/types/sdk_anchor_test";
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In sdk-tests/sdk-anchor-test/tests/test_v1.ts around lines 19 to 22, the file
uses Program<SdkAnchorTest> but never imports the generated Anchor type
SdkAnchorTest; add the missing type import from the generated IDL/types file
(for example: import type { SdkAnchorTest } from
"../target/types/sdk_anchor_test";) and adjust the relative path to the actual
generated types file so TypeScript recognizes Program<SdkAnchorTest>.
const programPath = path.join( | ||
__dirname, | ||
"../../../target/deploy/sdk_anchor_test.so" | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix programPath resolution (points too high in the tree)
From tests/, the .so lives in ../target/deploy/. Adjust the path to avoid ENOENT.
- const programPath = path.join(
- __dirname,
- "../../../target/deploy/sdk_anchor_test.so"
- );
+ const programPath = path.join(
+ __dirname,
+ "../target/deploy/sdk_anchor_test.so"
+ );
📝 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.
const programPath = path.join( | |
__dirname, | |
"../../../target/deploy/sdk_anchor_test.so" | |
); | |
const programPath = path.join( | |
__dirname, | |
"../target/deploy/sdk_anchor_test.so" | |
); |
🤖 Prompt for AI Agents
In sdk-tests/sdk-anchor-test/tests/test_v1.ts around lines 39 to 42, the
programPath currently points too high in the tree and causes ENOENT; update the
path.join call to reference the correct relative location from tests/ (use
"../target/deploy/sdk_anchor_test.so" instead of
"../../../target/deploy/sdk_anchor_test.so") and ensure the filename matches the
built .so; optionally add a quick fs.existsSync check to fail fast if the file
is still missing.
const programPath = path.join( | ||
__dirname, | ||
"../../../target/deploy/sdk_anchor_test.so" | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix programPath resolution (too many ../ segments)
Point to ../target/deploy from tests/.
- const programPath = path.join(
- __dirname,
- "../../../target/deploy/sdk_anchor_test.so"
- );
+ const programPath = path.join(
+ __dirname,
+ "../target/deploy/sdk_anchor_test.so"
+ );
📝 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.
const programPath = path.join( | |
__dirname, | |
"../../../target/deploy/sdk_anchor_test.so" | |
); | |
const programPath = path.join( | |
__dirname, | |
"../target/deploy/sdk_anchor_test.so" | |
); |
🤖 Prompt for AI Agents
In sdk-tests/sdk-anchor-test/tests/test_v2.ts around lines 39 to 42, the
programPath uses too many "../" segments and resolves incorrectly; update the
path to point from the tests/ directory into ../target/deploy (i.e., replace the
current join that goes up three levels with one that goes up two levels to
target/deploy/sdk_anchor_test.so) so the test loads the correct binary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
sdk-tests/sdk-anchor-test/tests/test_v1.ts (1)
132-136
: Missing SdkAnchorTest type import (compile error).Program requires importing the generated type.
Add at the top:
import type { SdkAnchorTest } from "../target/types/sdk_anchor_test";
♻️ Duplicate comments (10)
js/program-test/eslint.config.cjs (1)
82-89
: AnchorparserOptions.project
to this package.Linting from the workspace root makes
@typescript-eslint/parser
resolve"./tsconfig.json"
against the CWD, so it can’t findjs/program-test/tsconfig.json
and type-aware rules break. AddtsconfigRootDir: __dirname
(or an absolute path) so the parser loads the correct config.parserOptions: { project: "./tsconfig.json", + tsconfigRootDir: __dirname, ecmaVersion: 2022, sourceType: "module", },
js/program-test/tests/rpc-interop.test.ts (1)
1-62
: FixnewAccountWithLamports
import & call signature
The helper moved into@lightprotocol/program-test
and now takes(rpc, lamports)
only. Keeping the import from@lightprotocol/stateless.js
and passing a third argument (256
) still produces the TS error noted earlier. Please importnewAccountWithLamports
(andsleep
) from../src
and drop the extra argument.-import { - ... - sleep, - transfer, - newAccountWithLamports, -} from "@lightprotocol/stateless.js"; -import { getTestRpc, TestRpc } from "../src"; +import { + ... + transfer, +} from "@lightprotocol/stateless.js"; +import { + getTestRpc, + newAccountWithLamports, + sleep, + TestRpc, +} from "../src"; @@ - payer = await newAccountWithLamports(rpc, 10e9, 256); - bob = await newAccountWithLamports(rpc, 10e9, 256); + payer = await newAccountWithLamports(rpc, 10e9); + bob = await newAccountWithLamports(rpc, 10e9);js/program-test/src/merkle-tree/merkle-tree.ts (1)
204-213
: Fix deserialize to initialise the hasher
deserialize
returns an instance with_lightWasm === undefined
. Any call topath
,update
, etc., will immediately throw because they all invokethis._lightWasm.poseidonHashString
. Replace the unusedhashFunction
parameter with aLightWasm
argument and store it on the instance.- static deserialize( - data: any, - hashFunction: (left: string, right: string) => string, - ) { + static deserialize(data: any, lightWasm: LightWasm) { const instance = Object.assign(Object.create(this.prototype), data); - instance._hash = hashFunction; + instance._lightWasm = lightWasm; instance.capacity = 2 ** instance.levels; instance.zeroElement = instance._zeros[0]; return instance; }js/program-test/src/test-rpc/get-parsed-events.ts (1)
95-111
: GuardtxRaw
before dereferencing
getTransaction
can returnnull
, yet the code immediately usestxRaw!.meta!.innerInstructions
, which will throw when RPC misses a slot. Add a null check so we skip those transactions safely.- if ( - txRaw!.meta!.innerInstructions && - txRaw!.meta!.innerInstructions.length > 0 - ) { - for (const innerGroup of txRaw!.meta!.innerInstructions) { + if (!txRaw || !txRaw.meta || !txRaw.meta.innerInstructions) { + continue; + } + + if (txRaw.meta.innerInstructions.length > 0) { + for (const innerGroup of txRaw.meta.innerInstructions) {js/program-test/src/test-rpc/test-rpc.ts (1)
367-547
: Restore sparse-index Merkle construction for valid proofs
The V1 branch rebuilds the tree by piling hashes into a dense array and then uses the array position (leafIndex = findIndex(...)
) everywhere. As soon as the indexer skips history, on-chain leaf indices jump (e.g., 18 432) and this dense array no longer matches reality—the recomputed root is wrong and every returned proof fails verification. Please materialize the tree with zeros at missing indices and use the true on-chain index whenever deriving paths/metadata.- let merkleTree: MerkleTree | undefined; + let merkleTree: MerkleTree | undefined; … - if (treeInfo.treeType === TreeType.StateV1) { + if (treeInfo.treeType === TreeType.StateV1) { … - const leafStrings = leaves.map((leaf, idx) => { - … - return leafStr; - }); - merkleTree = new MerkleTree(this.depth, this.lightWasm, leafStrings); + const treeData = leavesByTree.get(tree.toBase58())!; + if (treeData.leaves.length === 0) { + merkleTree = new MerkleTree(this.depth, this.lightWasm, []); + } else { + const zeroLeaf = bn(0).toString(); + const maxLeafIndex = Math.max(...treeData.leafIndices); + const leafStrings = Array.from( + { length: maxLeafIndex + 1 }, + () => zeroLeaf, + ); + for (let idx = 0; idx < treeData.leaves.length; idx++) { + const onChainIndex = treeData.leafIndices[idx]; + leafStrings[onChainIndex] = bn(treeData.leaves[idx]).toString(); + } + merkleTree = new MerkleTree(this.depth, this.lightWasm, leafStrings); + } … - let leafIndex: number; + let leafArrayIndex: number; try { - leafIndex = leaves.findIndex((leaf, leafIdx) => { + leafArrayIndex = leaves.findIndex((leaf, leafIdx) => { … }); - console.log(`[TEST-RPC] Found leafIndex:`, leafIndex); + console.log(`[TEST-RPC] Found leaf array index:`, leafArrayIndex); } catch (err) { … } - if (leafIndex !== -1) { + if (leafArrayIndex !== -1) { + const onChainLeafIndex = + leavesByTree.get(tree.toBase58())!.leafIndices[leafArrayIndex]; if (treeInfo.treeType === TreeType.StateV1) { - const pathElements = merkleTree.path(leafIndex).pathElements; + const pathElements = + merkleTree.path(onChainLeafIndex).pathElements; … const merkleProof: MerkleContextWithMerkleProof = { … - leafIndex, + leafIndex: onChainLeafIndex, … - rootIndex: leaves.length, + rootIndex: leavesByTree.get(tree.toBase58())!.leafIndices.length, };js/program-test/tests/transfer.test.ts (1)
26-26
: Test description mentions "alice" but code uses "payer".The test description refers to "alice -> bob" transfers, but the actual implementation uses the
payer
variable. This inconsistency was previously flagged.sdk-tests/sdk-anchor-test/tests/test_v2.ts (2)
62-65
: Replace magic numbers with TreeType enum.Use TreeType.StateV1/StateV2 instead of 2/3 for robustness.
Apply:
-import { - bn, - type CompressedAccountWithMerkleContext, - defaultTestStateTreeAccounts, - deriveAddressSeedV2, - deriveAddressV2, - PackedAccounts, - type Rpc, - SystemAccountMetaConfig, -} from "@lightprotocol/stateless.js"; +import { + bn, + type CompressedAccountWithMerkleContext, + deriveAddressSeedV2, + deriveAddressV2, + PackedAccounts, + type Rpc, + SystemAccountMetaConfig, + TreeType, +} from "@lightprotocol/stateless.js"; @@ -const stateTreeInfo = existingTreeInfos.find( - (info) => info.treeType === 2 || info.treeType === 3 -); // StateV1 or StateV2 +const stateTreeInfo = existingTreeInfos.find( + (info) => + info.treeType === TreeType.StateV1 || info.treeType === TreeType.StateV2, +); // StateV1 or StateV2
141-149
: Missing SdkAnchorTest type import (compile error).Program requires importing the generated type.
Add at the top:
import type { SdkAnchorTest } from "../target/types/sdk_anchor_test";js/compressed-token/tests/program-test/compress-spl-token-account.test.ts (1)
393-398
: Remove debug logging from tests.Drop noisy console.log statements.
- console.log('totalCompressedAmount ', totalCompressedAmount); - console.log('ataBalanceBefore', ataBalanceBefore); - console.log( - 'ataBalanceBefore.value.amount ', - ataBalanceBefore.value.amount, - );js/program-test/src/spl-token-utils.ts (1)
201-205
: Remove leftover debug logs in helpers.These pollute output for all callers.
- console.log( - "[spl-token-utils.ts:195] Converting amount:", - typeof data.amount, - data.amount, - ); @@ - console.log( - "[spl-token-utils.ts:223] Converting supply:", - typeof data.supply, - data.supply, - );Also applies to: 233-237
🧹 Nitpick comments (1)
js/program-test/tests/poseidon-comparison.test.ts (1)
32-248
: Import shared Poseidon constants instead of duplicating.We just exported these MDS matrices and round constants from
src/hasher/constants.ts
. Repeating 600+ literal lines here risks drift the moment we fix a constant upstream. Please importMDS_3
/CONSTANTS_3_FLAT
(and the t=4 variants) from the shared module and reuse them.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (79)
.github/actions/setup-and-build/action.yml
(0 hunks).github/workflows/js-v2.yml
(3 hunks).github/workflows/js.yml
(3 hunks)cli/package.json
(2 hunks)cli/test/commands/approve-and-mint-to/index.test.ts
(1 hunks)cli/test/commands/create-token-pool/index.test.ts
(1 hunks)cli/test/helpers/helpers.ts
(1 hunks)js/compressed-token/package.json
(3 hunks)js/compressed-token/tests/e2e/compress.test.ts
(2 hunks)js/compressed-token/tests/e2e/merge-token-accounts.test.ts
(2 hunks)js/compressed-token/tests/e2e/mint-to.test.ts
(3 hunks)js/compressed-token/tests/e2e/rpc-token-interop.test.ts
(2 hunks)js/compressed-token/tests/program-test/approve-and-mint-to.test.ts
(4 hunks)js/compressed-token/tests/program-test/compress-spl-token-account.test.ts
(15 hunks)js/compressed-token/tests/program-test/compress.test.ts
(1 hunks)js/compressed-token/tests/program-test/create-mint.test.ts
(2 hunks)js/compressed-token/tests/program-test/create-token-pool.test.ts
(2 hunks)js/compressed-token/tests/program-test/decompress-delegated.test.ts
(5 hunks)js/compressed-token/tests/program-test/decompress.test.ts
(3 hunks)js/compressed-token/tests/program-test/delegate.test.ts
(2 hunks)js/compressed-token/tests/program-test/mint-to.test.ts
(1 hunks)js/compressed-token/tests/program-test/multi-pool.test.ts
(2 hunks)js/compressed-token/tests/program-test/transfer-delegated.test.ts
(2 hunks)js/compressed-token/tests/program-test/transfer.test.ts
(4 hunks)js/compressed-token/vitest.config.ts
(1 hunks)js/program-test/.prettierignore
(1 hunks)js/program-test/README.md
(1 hunks)js/program-test/eslint.config.cjs
(1 hunks)js/program-test/package.json
(1 hunks)js/program-test/rollup.config.js
(1 hunks)js/program-test/src/hasher/constants.ts
(1 hunks)js/program-test/src/hasher/index.ts
(1 hunks)js/program-test/src/hasher/noble-hasher.ts
(1 hunks)js/program-test/src/index.ts
(1 hunks)js/program-test/src/litesvm-rpc.ts
(1 hunks)js/program-test/src/merkle-tree/index.ts
(1 hunks)js/program-test/src/merkle-tree/indexed-array.ts
(1 hunks)js/program-test/src/merkle-tree/merkle-tree.ts
(1 hunks)js/program-test/src/spl-token-utils.ts
(1 hunks)js/program-test/src/test-rpc/get-compressed-accounts.ts
(1 hunks)js/program-test/src/test-rpc/get-compressed-token-accounts.ts
(1 hunks)js/program-test/src/test-rpc/get-parsed-events.ts
(1 hunks)js/program-test/src/test-rpc/index.ts
(1 hunks)js/program-test/src/test-rpc/test-rpc.ts
(1 hunks)js/program-test/src/test-utils.ts
(1 hunks)js/program-test/src/types.ts
(1 hunks)js/program-test/tests/compress.test.ts
(1 hunks)js/program-test/tests/merkle-tree.test.ts
(1 hunks)js/program-test/tests/poseidon-comparison.test.ts
(1 hunks)js/program-test/tests/rpc-interop.test.ts
(1 hunks)js/program-test/tests/rpc-multi-trees.test.ts
(1 hunks)js/program-test/tests/test-rpc.test.ts
(1 hunks)js/program-test/tests/transfer.test.ts
(1 hunks)js/program-test/tsconfig.json
(1 hunks)js/program-test/vitest.config.ts
(1 hunks)js/stateless.js/package.json
(2 hunks)js/stateless.js/rollup.config.js
(2 hunks)js/stateless.js/src/rpc.ts
(0 hunks)js/stateless.js/src/test-helpers/index.ts
(0 hunks)js/stateless.js/src/test-helpers/merkle-tree/index.ts
(0 hunks)js/stateless.js/src/test-helpers/merkle-tree/indexed-array.ts
(0 hunks)js/stateless.js/src/test-helpers/merkle-tree/merkle-tree.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/get-compressed-accounts.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/get-compressed-token-accounts.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/get-parsed-events.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/index.ts
(0 hunks)js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts
(0 hunks)js/stateless.js/src/utils/send-and-confirm.ts
(1 hunks)js/stateless.js/tests/e2e/compress.test.ts
(0 hunks)js/stateless.js/tests/e2e/rpc-interop.test.ts
(0 hunks)js/stateless.js/tests/e2e/rpc-multi-trees.test.ts
(0 hunks)js/stateless.js/tests/e2e/test-rpc.test.ts
(0 hunks)js/stateless.js/tests/e2e/transfer.test.ts
(0 hunks)js/stateless.js/tests/unit/merkle-tree/merkle-tree.test.ts
(0 hunks)pnpm-workspace.yaml
(1 hunks)scripts/devenv/versions.sh
(1 hunks)sdk-tests/sdk-anchor-test/package.json
(1 hunks)sdk-tests/sdk-anchor-test/tests/test_v1.ts
(4 hunks)sdk-tests/sdk-anchor-test/tests/test_v2.ts
(5 hunks)
💤 Files with no reviewable changes (17)
- js/stateless.js/src/test-helpers/merkle-tree/index.ts
- js/stateless.js/tests/e2e/transfer.test.ts
- js/stateless.js/tests/e2e/rpc-interop.test.ts
- js/stateless.js/src/test-helpers/test-rpc/index.ts
- js/stateless.js/src/test-helpers/test-rpc/get-compressed-accounts.ts
- .github/actions/setup-and-build/action.yml
- js/stateless.js/tests/e2e/compress.test.ts
- js/stateless.js/tests/e2e/rpc-multi-trees.test.ts
- js/stateless.js/tests/e2e/test-rpc.test.ts
- js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts
- js/stateless.js/src/test-helpers/merkle-tree/indexed-array.ts
- js/stateless.js/src/test-helpers/index.ts
- js/stateless.js/tests/unit/merkle-tree/merkle-tree.test.ts
- js/stateless.js/src/test-helpers/test-rpc/get-compressed-token-accounts.ts
- js/stateless.js/src/test-helpers/merkle-tree/merkle-tree.ts
- js/stateless.js/src/rpc.ts
- js/stateless.js/src/test-helpers/test-rpc/get-parsed-events.ts
✅ Files skipped from review due to trivial changes (3)
- js/program-test/README.md
- js/compressed-token/tests/program-test/mint-to.test.ts
- js/program-test/src/merkle-tree/indexed-array.ts
🚧 Files skipped from review as they are similar to previous changes (23)
- js/program-test/src/merkle-tree/index.ts
- js/program-test/src/test-utils.ts
- js/stateless.js/rollup.config.js
- js/compressed-token/tests/e2e/rpc-token-interop.test.ts
- cli/test/helpers/helpers.ts
- js/program-test/tsconfig.json
- js/program-test/src/types.ts
- js/program-test/tests/rpc-multi-trees.test.ts
- js/program-test/tests/merkle-tree.test.ts
- js/program-test/.prettierignore
- js/program-test/src/test-rpc/index.ts
- scripts/devenv/versions.sh
- js/compressed-token/tests/program-test/delegate.test.ts
- js/program-test/src/index.ts
- js/compressed-token/tests/e2e/merge-token-accounts.test.ts
- pnpm-workspace.yaml
- cli/test/commands/create-token-pool/index.test.ts
- js/compressed-token/tests/program-test/compress.test.ts
- js/program-test/tests/compress.test.ts
- js/program-test/vitest.config.ts
- js/compressed-token/tests/program-test/decompress-delegated.test.ts
- js/stateless.js/package.json
- js/program-test/src/test-rpc/get-compressed-token-accounts.ts
🧰 Additional context used
🧬 Code graph analysis (23)
js/program-test/rollup.config.js (1)
js/stateless.js/rollup.config.js (1)
rolls
(11-68)
js/compressed-token/tests/program-test/create-token-pool.test.ts (2)
js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(1002-1008)
js/program-test/src/litesvm-rpc.ts (4)
js/program-test/src/index.ts (3)
LiteSVMRpc
(8-8)LiteSVMConfig
(14-14)createLiteSVMRpc
(8-8)js/program-test/src/test-rpc/test-rpc.ts (1)
TestRpc
(113-1119)js/program-test/src/types.ts (1)
LiteSVMConfig
(16-27)js/stateless.js/src/constants.ts (3)
defaultTestStateTreeAccounts
(236-244)COMPRESSED_TOKEN_PROGRAM_ID
(256-258)featureFlags
(16-35)
js/program-test/src/test-rpc/test-rpc.ts (11)
js/stateless.js/src/constants.ts (3)
defaultTestStateTreeAccounts
(236-244)localTestActiveStateTreeInfos
(151-219)batchAddressTree
(304-304)js/stateless.js/src/rpc-interface.ts (16)
CompressionApiInterface
(762-867)GetCompressedAccountsByOwnerConfig
(45-50)WithCursor
(250-255)LatestNonVotingSignaturesPaginated
(57-67)LatestNonVotingSignatures
(33-43)GetCompressedTokenAccountsByOwnerOrDelegateOptions
(205-209)ParsedTokenAccount
(236-239)WithContext
(241-248)TokenBalance
(210-210)SignatureWithMetadata
(69-73)CompressedTransaction
(120-142)PaginatedOptions
(217-220)CompressedMintTokenHolders
(52-55)HashWithTree
(103-107)AddressWithTree
(109-113)ValidityProofWithContext
(161-190)js/stateless.js/src/state/types.ts (2)
TreeInfo
(77-99)PublicTransactionEvent
(231-272)js/program-test/src/merkle-tree/indexed-array.ts (2)
hash
(24-35)IndexedArray
(58-307)js/program-test/src/test-rpc/get-compressed-accounts.ts (4)
getCompressedAccountsForTest
(42-96)getCompressedAccountByHashTest
(23-29)getMultipleCompressedAccountsByHashTest
(31-39)getCompressedAccountsByOwnerTest
(14-21)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/test-rpc/get-parsed-events.ts (1)
getParsedEvents
(34-144)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
getStateTreeInfoByPubkey
(8-13)js/program-test/src/merkle-tree/merkle-tree.ts (2)
MerkleTree
(13-214)root
(74-78)js/program-test/src/test-rpc/get-compressed-token-accounts.ts (3)
getCompressedTokenAccountsByOwnerTest
(179-195)getCompressedTokenAccountsByDelegateTest
(197-212)getCompressedTokenAccountByHashTest
(214-229)js/stateless.js/src/rpc.ts (4)
MerkleContextWithNewAddressProof
(402-412)convertMerkleProofsWithContextToHex
(424-442)proverRequest
(336-390)convertNonInclusionMerkleProofInputsToHex
(444-470)
js/program-test/src/test-rpc/get-compressed-accounts.ts (5)
js/stateless.js/src/rpc.ts (1)
Rpc
(551-1892)js/stateless.js/src/state/compressed-account.ts (3)
CompressedAccountWithMerkleContext
(14-17)MerkleContext
(80-97)createCompressedAccountWithMerkleContextLegacy
(240-253)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)js/program-test/src/test-rpc/get-parsed-events.ts (1)
getParsedEvents
(34-144)js/stateless.js/src/utils/get-state-tree-infos.ts (1)
getStateTreeInfoByPubkey
(8-13)
js/compressed-token/tests/e2e/compress.test.ts (1)
js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)
js/program-test/tests/rpc-interop.test.ts (8)
js/stateless.js/src/rpc.ts (2)
Rpc
(551-1892)createRpc
(231-264)js/program-test/src/test-rpc/test-rpc.ts (2)
TestRpc
(113-1119)getTestRpc
(84-103)js/stateless.js/src/state/types.ts (1)
TreeInfo
(77-99)js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)js/program-test/src/test-utils.ts (2)
newAccountWithLamports
(7-15)sleep
(20-22)js/stateless.js/src/utils/address.ts (2)
deriveAddressSeed
(11-18)deriveAddress
(29-46)js/stateless.js/src/actions/create-account.ts (1)
createAccountWithLamports
(113-175)js/stateless.js/src/constants.ts (1)
featureFlags
(16-35)
js/program-test/tests/poseidon-comparison.test.ts (2)
js/program-test/src/test-rpc/test-rpc.ts (1)
LightWasm
(63-67)js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)
js/program-test/src/test-rpc/get-parsed-events.ts (5)
js/stateless.js/src/rpc.ts (1)
Rpc
(551-1892)js/stateless.js/src/state/types.ts (2)
PublicTransactionEvent
(231-272)InstructionDataInvoke
(277-306)js/stateless.js/src/constants.ts (7)
accountCompressionProgram
(76-77)COMPUTE_BUDGET_PATTERN
(52-52)noopProgram
(74-74)INVOKE_DISCRIMINATOR
(54-56)INVOKE_CPI_DISCRIMINATOR
(58-60)INVOKE_CPI_WITH_READ_ONLY_DISCRIMINATOR
(62-64)INSERT_INTO_QUEUES_DISCRIMINATOR
(70-72)js/stateless.js/src/programs/system/layout.ts (6)
decodePublicTransactionEvent
(392-396)decodeInstructionDataInvoke
(240-246)decodeInstructionDataInvokeCpi
(248-254)decodeInstructionDataInvokeCpiWithReadOnly
(234-238)deserializeAppendNullifyCreateAddressInputsIndexer
(441-514)convertToPublicTransactionEvent
(516-598)js/stateless.js/src/utils/conversion.ts (1)
convertInvokeCpiWithReadOnlyToInvoke
(121-191)
js/compressed-token/tests/program-test/approve-and-mint-to.test.ts (10)
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/program-test/src/spl-token-utils.ts (1)
splGetOrCreateAssociatedTokenAccount
(276-324)js/compressed-token/src/program.ts (1)
CompressedTokenProgram
(600-1659)js/stateless.js/src/utils/dedupe-signer.ts (1)
dedupeSigner
(4-11)js/stateless.js/src/utils/send-and-confirm.ts (2)
buildAndSignTx
(145-166)sendAndConfirmTx
(51-62)js/stateless.js/src/utils/conversion.ts (1)
toArray
(28-29)js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(1002-1008)
js/compressed-token/tests/program-test/transfer.test.ts (1)
js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)
js/compressed-token/tests/e2e/mint-to.test.ts (1)
js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)
js/program-test/tests/transfer.test.ts (4)
js/program-test/src/litesvm-rpc.ts (2)
LiteSVMRpc
(23-997)createLiteSVMRpc
(1002-1008)js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-15)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)
js/program-test/src/hasher/noble-hasher.ts (3)
js/program-test/src/hasher/constants.ts (4)
CONSTANTS_3_FLAT
(48-244)MDS_3
(2-18)CONSTANTS_4_FLAT
(245-565)MDS_4
(20-45)js/program-test/src/test-rpc/test-rpc.ts (1)
LightWasm
(63-67)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)
js/compressed-token/tests/program-test/create-mint.test.ts (2)
js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(1002-1008)
js/compressed-token/tests/program-test/multi-pool.test.ts (4)
js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-15)cli/test/helpers/helpers.ts (1)
createTestSplMint
(71-103)js/program-test/src/spl-token-utils.ts (2)
splGetOrCreateAssociatedTokenAccount
(276-324)splMintTo
(120-149)
js/program-test/tests/test-rpc.test.ts (6)
js/stateless.js/src/constants.ts (4)
defaultTestStateTreeAccounts
(236-244)STATE_MERKLE_TREE_ROLLOVER_FEE
(338-340)featureFlags
(16-35)STATE_MERKLE_TREE_NETWORK_FEE
(356-356)js/stateless.js/src/state/compressed-account.ts (1)
CompressedAccountWithMerkleContext
(14-17)js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(1002-1008)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-15)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)
js/compressed-token/tests/program-test/transfer-delegated.test.ts (2)
js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)js/program-test/src/index.ts (1)
createLiteSVMRpc
(8-8)
sdk-tests/sdk-anchor-test/tests/test_v2.ts (2)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(1002-1008)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-15)
js/program-test/src/merkle-tree/merkle-tree.ts (1)
js/program-test/src/test-rpc/test-rpc.ts (1)
LightWasm
(63-67)
js/compressed-token/tests/program-test/decompress.test.ts (2)
js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)js/program-test/src/spl-token-utils.ts (1)
splCreateAssociatedTokenAccount
(78-115)
sdk-tests/sdk-anchor-test/tests/test_v1.ts (2)
js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(1002-1008)js/program-test/src/test-utils.ts (1)
newAccountWithLamports
(7-15)
js/compressed-token/tests/program-test/compress-spl-token-account.test.ts (4)
js/program-test/src/hasher/noble-hasher.ts (1)
NobleHasherFactory
(159-183)js/program-test/src/litesvm-rpc.ts (1)
createLiteSVMRpc
(1002-1008)js/program-test/src/spl-token-utils.ts (2)
splCreateAssociatedTokenAccount
(78-115)splMintTo
(120-149)js/stateless.js/src/state/bn.ts (1)
bn
(3-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: cli-v1
- GitHub Check: Forester e2e test
- GitHub Check: stateless-js-v1
- GitHub Check: cli-v2
- GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client, cargo test...
- GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
- GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
- GitHub Check: lint
- GitHub Check: stateless-js-v2
🔇 Additional comments (13)
js/stateless.js/src/utils/send-and-confirm.ts (2)
98-109
: LGTM! Error handling correctly implemented.The error handling logic correctly addresses the past review concerns by:
- Checking
result?.err
before continuing to poll- Rejecting immediately with a descriptive error message
- Using
return
to prevent further execution in the interval iterationThis prevents unnecessary polling until timeout when a transaction has already failed.
111-124
: LGTM! Commitment threshold logic correctly implements the hierarchy.The commitment comparison logic correctly implements the finalized > confirmed > processed hierarchy:
- Direct equality check handles exact matches
- 'processed' accepts 'confirmed' or 'finalized' (higher commitments)
- 'confirmed' accepts 'finalized' (higher commitment)
- Gracefully handles null/undefined statusValue by continuing to poll
This approach is more robust than direct equality and aligns with typical commitment semantics.
js/compressed-token/vitest.config.ts (1)
7-18
: Document LiteSVM failure and scope parallelism
- Attach the exact LiteSVM “bad alloc” error (stack trace or repro).
- Benchmark with default parallel Vitest:
time npx vitest run --config js/compressed-token/vitest.config.ts
- Scope sequential execution to failing suites via
describe.sequential
ortest.concurrent(false)
instead of disabling all parallelism.- // litesvm fails with bad alloc if not configured - // Use threads pool instead of forks to avoid native addon corruption - // Threads share the same V8 isolate and native addon context + // LiteSVM exhibits memory-allocation errors under parallel execution (see <error-log-link>). + // Using threads pool with singleThread=true ensures isolated native addon state. + // TODO: add error logs, benchmark default parallel run, and limit sequential execution + // to affected suites (tracked in issue #XXXX)..github/workflows/js-v2.yml (1)
24-24
: LGTM! Workflow changes align with program-test integration.The changes appropriately:
- Upgrade to a more powerful runner (
warp-ubuntu-latest-x64-4x
) for improved test performance- Add program-test build step following the existing pattern
- Add program-test V2 test execution with proper environment setup
Also applies to: 66-69, 103-106
js/program-test/src/hasher/index.ts (1)
1-2
: LGTM! Clean barrel export pattern.The module appropriately consolidates hasher exports into a single public API surface.
.github/workflows/js.yml (1)
24-24
: LGTM! Workflow changes mirror V2 and integrate program-test.The V1 workflow updates are consistent with the V2 workflow changes, adding program-test build and test steps while upgrading to a more capable runner.
Also applies to: 66-69, 103-106
js/compressed-token/tests/program-test/create-mint.test.ts (1)
6-11
: LGTM! Clean migration to program-test utilities.The test correctly migrates from
WasmFactory
toNobleHasherFactory
and fromgetTestRpc
tocreateLiteSVMRpc
, consolidating test infrastructure under the new program-test package.Also applies to: 55-56
js/compressed-token/tests/e2e/mint-to.test.ts (1)
9-9
: LGTM! Migration follows established pattern.The test correctly adopts the NobleHasherFactory and sources
getTestRpc
from the program-test package, maintaining consistency with the migration strategy.Also applies to: 23-23, 29-29, 70-70
js/compressed-token/tests/program-test/decompress.test.ts (1)
12-18
: LGTM! Comprehensive migration to program-test infrastructure.The test successfully migrates to:
NobleHasherFactory
for hasher initializationcreateLiteSVMRpc
for RPC creationsplCreateAssociatedTokenAccount
for token account creationAll changes align with the program-test migration strategy.
Also applies to: 82-83, 103-103
js/program-test/tests/transfer.test.ts (1)
1-65
: LGTM! Transfer test logic is sound.The test correctly:
- Initializes RPC using the new program-test infrastructure
- Creates funded accounts and compresses initial lamports
- Executes transfers in a loop with proper balance verification
- Uses big-number arithmetic for accurate comparisons
js/compressed-token/tests/program-test/transfer-delegated.test.ts (1)
11-15
: LGTM! Migration aligns with program-test strategy.The test correctly adopts
NobleHasherFactory
for hasher initialization andcreateLiteSVMRpc
for RPC creation, consistent with the systematic migration across all test files.Also applies to: 181-182
sdk-tests/sdk-anchor-test/tests/test_v2.ts (1)
150-159
: Verify address queue passed to getValidityProofV0.You pass queue: addressTree. Ensure this is correct for V2; typically queue differs from tree.
If available, prefer:
const { tree: addressTree, queue: addressQueue } = await rpc.getAddressTreeInfoV2(); ... { tree: addressTree, queue: addressQueue, address: bn(address.toBytes()) }js/compressed-token/tests/program-test/multi-pool.test.ts (1)
86-115
: Migration to program-test utilities looks solid.NobleHasherFactory + createLiteSVMRpc + spl* helpers are used correctly; account funding and mint setup are consistent.
import { Keypair } from "@solana/web3.js"; | ||
import { createTestSplMint, requestAirdrop } from "../../helpers/helpers"; | ||
import { getTestRpc } from "@lightprotocol/stateless.js"; | ||
import { getTestRpc } from "@lightprotocol/program-test"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
Verify the migration is complete and tests pass.
The import change from @lightprotocol/stateless.js
to @lightprotocol/program-test
aligns with the PR objective. The usage at line 24 remains unchanged, suggesting API compatibility.
Please confirm:
@lightprotocol/program-test
is properly added to the package dependencies- The
getTestRpc
function signature is identical or compatible - All tests pass with the new import
🏁 Script executed:
#!/bin/bash
# Verify package dependency and getTestRpc usage
# Check if @lightprotocol/program-test is in package.json
echo "=== Checking package.json for @lightprotocol/program-test dependency ==="
fd -t f "package.json" cli | xargs cat | jq '.dependencies, .devDependencies' 2>/dev/null | grep -i "program-test" || echo "Note: Check if dependency is added"
# Search for all getTestRpc usages to verify migration consistency
echo -e "\n=== Finding all getTestRpc usages ==="
rg -n --type=ts 'getTestRpc' cli/test
# Check if there are remaining imports from stateless.js test helpers
echo -e "\n=== Checking for remaining stateless.js test-helper imports ==="
rg -n --type=ts "from ['\"]@lightprotocol/stateless.js['\"]" cli/test | grep -v "node_modules" || echo "No remaining imports found"
Length of output: 1024
Remove remaining stateless.js import in test helpers
cli/test/helpers/helpers.ts:15 – replace or remove the import from @lightprotocol/stateless.js
and switch to @lightprotocol/program-test
equivalents to complete the migration.
🤖 Prompt for AI Agents
In cli/test/commands/approve-and-mint-to/index.test.ts around line 8 and
specifically in cli/test/helpers/helpers.ts at line 15, remove the remaining
import from `@lightprotocol/stateless.js` and replace it with the corresponding
exports from `@lightprotocol/program-test`; update the helpers file to import
the `program-test` equivalents (e.g., getTestRpc and any other helper functions
used) and refactor any usages in helpers.ts to call those `program-test`
functions instead so the test helpers no longer depend on `stateless.js`.
message: { | ||
accountKeys, | ||
compiledInstructions, | ||
recentBlockhash: | ||
message.recentBlockhash || message.header?.recentBlockhash || "", | ||
addressTableLookups: message.addressTableLookups || [], | ||
}, | ||
signatures: | ||
"signatures" in rawTx ? rawTx.signatures : [(rawTx as any).signature], | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return base58 signature strings in getTransaction
.
getTransaction
still returns whatever is stored on the Transaction
object (SignaturePubkeyPair
objects for legacy txs, Uint8Array
s for versioned txs). JSON-RPC requires an array of base58 strings. Downstream consumers (and our own tooling) will break when they try to deserialize these non-string values. Please encode every signature with bs58.encode(...)
before returning.
🤖 Prompt for AI Agents
In js/program-test/src/litesvm-rpc.ts around lines 492-501, the signatures being
returned are raw objects/Uint8Arrays instead of base58 strings; map the
signatures array to strings by importing bs58 and converting each entry: if it's
a SignaturePubkeyPair object use its .signature field, if it's a
Uint8Array/Buffer use it directly, and for non-null signatures call
bs58.encode(...) to produce a base58 string (leave null/undefined as null if
present); replace the current signatures value with this mapped array so
getTransaction returns an array of base58 signature strings.
Issue:
Summary by CodeRabbit