From 6b9209b543bebdc78c9fc2f3212e0830f79c49a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Swen=20Sch=C3=A4ferjohann?= <42959314+SwenSchaeferjohann@users.noreply.github.com> Date: Thu, 10 Apr 2025 02:24:43 +0100 Subject: [PATCH] chore: backport breaking api changes to v1 js SDKs (#1661) * stateless.js add treeinfos * wip * add getTokenPoolInfos * wip * wip * wip - storageoptions * wip * wip * wip * wip - known bug in rpc-interop.test.ts if using random trees * debugged test-rpc in ctoken * all ctoken tests working * rm logs * clean * ctxs to infos, removed redundant getMintProgramId calls * rm deadcode, storageoptions * fix cli getMindProgramId use * fix tokenpool * fix test stateless.js add treeinfos wip add getTokenPoolInfos wip wip wip - storageoptions wip wip wip wip - known bug in rpc-interop.test.ts if using random trees debugged test-rpc in ctoken all ctoken tests working rm logs clean ctxs to infos, removed redundant getMintProgramId calls rm deadcode, storageoptions fix cli getMindProgramId use fix tokenpool fix test update CHANGELOG.md files update changelog update CHANGELOG.md export get-token-pool-infos.ts wip clean wip refactor merklecontext refactor StateTreeInfo wip wip debug test-rpc getCompressedTokenAccountsByOwner fix unit test wip wip wip debug .readUIntLE wip wip wip fix buffer conversion at test-rpc decode wip tests working compressedProof -> validityProof update changelog wip refactor: do not allow output trees for decompress, transfer fmt wip rename getCachedStateTreeInfos upd changelog use with getCachedStateTreeInfos getStateTreeInfos wip wip delegate test wip added transfer-delegated.test.ts added transfer-delegated test cases. all green add tests for decompress-delegated fix rm logs from program update changelog update err msg fix state-tree-luts wip getActiveStateTreeInfos -> getAllStateTreeInfos wip fix sigs for nullifyStateTree wip wip cleanup add getCachedActiveStateTreeInfos mock revert to compressedProof add tokenpools tests update comment update CHANGELOG.md update changelog 0.21.0 update changelog fix fix event parsing post rebase stateless js refactor dedupe types link to computebudgetprogram wip fix DX for instructions: add docstrings to call signatures wip dont break mergeTokenAccounts wip wip add v2 trees to test-rpc v1 mergeable test-rpc tests working rpc-interop tests working with v2 all stateless.js tests working with v2 compressed-token tests working rebase to main fixup cargo lock wip chore: backport breaking api changes to v1 js SDKs (#1661) * stateless.js add treeinfos * wip * add getTokenPoolInfos * wip * wip * wip - storageoptions * wip * wip * wip * wip - known bug in rpc-interop.test.ts if using random trees * debugged test-rpc in ctoken * all ctoken tests working * rm logs * clean * ctxs to infos, removed redundant getMintProgramId calls * rm deadcode, storageoptions * fix cli getMindProgramId use * fix tokenpool * fix test stateless.js add treeinfos wip add getTokenPoolInfos wip wip wip - storageoptions wip wip wip wip - known bug in rpc-interop.test.ts if using random trees debugged test-rpc in ctoken all ctoken tests working rm logs clean ctxs to infos, removed redundant getMintProgramId calls rm deadcode, storageoptions fix cli getMindProgramId use fix tokenpool fix test update CHANGELOG.md files update changelog update CHANGELOG.md export get-token-pool-infos.ts wip clean wip refactor merklecontext refactor StateTreeInfo wip wip debug test-rpc getCompressedTokenAccountsByOwner fix unit test wip wip wip debug .readUIntLE wip wip wip fix buffer conversion at test-rpc decode wip tests working compressedProof -> validityProof update changelog wip refactor: do not allow output trees for decompress, transfer fmt wip rename getCachedStateTreeInfos upd changelog use with getCachedStateTreeInfos getStateTreeInfos wip wip delegate test wip added transfer-delegated.test.ts added transfer-delegated test cases. all green add tests for decompress-delegated fix rm logs from program update changelog update err msg fix state-tree-luts wip getActiveStateTreeInfos -> getAllStateTreeInfos wip fix sigs for nullifyStateTree wip wip cleanup add getCachedActiveStateTreeInfos mock revert to compressedProof add tokenpools tests update comment update CHANGELOG.md update changelog 0.21.0 update changelog fix fix event parsing post rebase stateless js refactor dedupe types link to computebudgetprogram wip fix DX for instructions: add docstrings to call signatures wip dont break mergeTokenAccounts wip wip add v2 trees to test-rpc v1 mergeable test-rpc tests working rpc-interop tests working with v2 all stateless.js tests working with v2 compressed-token tests working rebase to main fixup cargo lock wip wip rename statetreeinfo -> treeinfo wip wip wip cli test works wip wip wip wip wip wip wip wip wip wip wip wip rm logs fix lint clean upd comment wip stateless.js tests working ctoken tests working --- js/compressed-token/src/program.ts | 8 +- js/stateless.js/src/constants.ts | 7 +- js/stateless.js/src/rpc.ts | 103 +++++++++++------- .../src/test-helpers/test-rpc/test-rpc.ts | 4 - .../tests/unit/utils/conversion.test.ts | 2 - 5 files changed, 72 insertions(+), 52 deletions(-) diff --git a/js/compressed-token/src/program.ts b/js/compressed-token/src/program.ts index 02e3eb90ae..b2b81fb986 100644 --- a/js/compressed-token/src/program.ts +++ b/js/compressed-token/src/program.ts @@ -1533,6 +1533,8 @@ export class CompressedTokenProgram { inputCompressedTokenAccounts, ); + const CHANGE_INDEX = featureFlags.isV2() ? 1 : 0; // TODO: find better solution. + const rawData: CompressedTokenInstructionDataApprove = { proof: recentValidityProof, mint, @@ -1540,8 +1542,8 @@ export class CompressedTokenProgram { cpiContext: null, delegate: toAddress, delegatedAmount: bn(amount), - delegateMerkleTreeIndex: 1, // TODO: find better solution. - changeAccountMerkleTreeIndex: 1, + delegateMerkleTreeIndex: CHANGE_INDEX, + changeAccountMerkleTreeIndex: CHANGE_INDEX, delegateLamports: null, }; @@ -1610,7 +1612,7 @@ export class CompressedTokenProgram { mint, inputTokenDataWithContext, cpiContext: null, - outputAccountMerkleTreeIndex: 2, // Because of the delegate account. + outputAccountMerkleTreeIndex: featureFlags.isV2() ? 2 : 1, }; const data = encodeRevokeInstructionData(rawData); diff --git a/js/stateless.js/src/constants.ts b/js/stateless.js/src/constants.ts index 4f58008a76..17aaefff13 100644 --- a/js/stateless.js/src/constants.ts +++ b/js/stateless.js/src/constants.ts @@ -3,13 +3,18 @@ import { Buffer } from 'buffer'; import { ConfirmOptions, PublicKey } from '@solana/web3.js'; import { TreeInfo, TreeType } from './state/types'; +export enum VERSION { + V1 = 'V1', + V2 = 'V2', +} + /** /** * @internal * Feature flags. Only use if you know what you are doing. */ export const featureFlags = { - version: 'V2' as 'V1' | 'V2', + version: VERSION.V1, isV2: () => featureFlags.version.toUpperCase() === 'V2', }; diff --git a/js/stateless.js/src/rpc.ts b/js/stateless.js/src/rpc.ts index f101bb563b..b7df40845b 100644 --- a/js/stateless.js/src/rpc.ts +++ b/js/stateless.js/src/rpc.ts @@ -86,6 +86,7 @@ import { LightWasm } from './test-helpers'; import { getAllStateTreeInfos, getStateTreeInfoByPubkey, + getTreeInfoByPubkey, } from './utils/get-state-tree-infos'; import { TreeInfo } from './state/types'; import { validateNumbersForProof } from './utils'; @@ -988,7 +989,7 @@ export class Rpc extends Connection implements CompressionApiInterface { treeInfos, featureFlags.isV2() ? (proof as any).treeContext.tree - : (proof as any).tree!, + : (proof as any).merkleTree, ); const value: MerkleContextWithMerkleProof = { hash: bn(proof.hash.toArray('be', 32)), @@ -1064,7 +1065,7 @@ export class Rpc extends Connection implements CompressionApiInterface { stateTreeInfo, bn(item.hash.toArray('be', 32)), item.leafIndex, - true, + false, ), item.owner, bn(item.lamports), @@ -1856,7 +1857,6 @@ export class Rpc extends Connection implements CompressionApiInterface { jsonRpcResultAndContext(ValidityProofResultV2), ); } else { - throw new Error('V1 is not supported'); res = create( unsafeRes, jsonRpcResultAndContext(ValidityProofResult), @@ -1876,44 +1876,63 @@ export class Rpc extends Connection implements CompressionApiInterface { } const value = res.result.value as any; - return { - value: { - compressedProof: value.compressedProof, - leaves: value.accounts - .map((r: any) => r.hash) - .concat(value.addresses.map((r: any) => r.address)), - roots: value.accounts - .map((r: any) => r.root) - .concat(value.addresses.map((r: any) => r.root)), - rootIndices: value.accounts - .map((r: any) => r.rootIndex.rootIndex) - .concat(value.addresses.map((r: any) => r.rootIndex)), - proveByIndices: value.accounts - .map((r: any) => r.rootIndex.proveByIndex) - .concat(value.addresses.map((r: any) => false)), - treeInfos: value.accounts - .map((r: any) => r.merkleContext) - .concat(value.addresses.map((r: any) => r.merkleContext)), - leafIndices: value.accounts - .map((r: any) => r.leafIndex) - .concat(value.addresses.map((r: any) => 0)), - }, - context: res.result.context, - }; - // TODO: enable with v1 support. - // return { - // value: { - // compressedProof: value.compressedProof, - // roots: value.roots, - // rootIndices: value.rootIndices.map((r: any) => r.rootIndex), - // leafIndices: value.leafIndices, - // leaves: value.leaves, - // treeInfos: value.merkleContexts, - // proveByIndices: value.rootIndices.map( - // (r: any) => r.proveByIndex, - // ), - // }, - // context: res.result.context, - // }; + + if (featureFlags.isV2()) { + return { + value: { + compressedProof: value.compressedProof, + leaves: value.accounts + .map((r: any) => r.hash) + .concat(value.addresses.map((r: any) => r.address)), + roots: value.accounts + .map((r: any) => r.root) + .concat(value.addresses.map((r: any) => r.root)), + rootIndices: value.accounts + .map((r: any) => r.rootIndex.rootIndex) + .concat(value.addresses.map((r: any) => r.rootIndex)), + proveByIndices: value.accounts + .map((r: any) => r.rootIndex.proveByIndex) + .concat(value.addresses.map((r: any) => false)), + treeInfos: value.accounts + .map((r: any) => r.merkleContext) + .concat( + value.addresses.map((r: any) => r.merkleContext), + ), + leafIndices: value.accounts + .map((r: any) => r.leafIndex) + .concat(value.addresses.map((r: any) => 0)), + }, + context: res.result.context, + }; + } else { + // Temporary fix for v1 backward compatibility. + const allInfos = await this.getStateTreeInfos(); + const infos = value.merkleTrees.map((r: PublicKey) => { + if (r.equals(defaultTestStateTreeAccounts().addressTree)) { + return { + tree: r, + queue: defaultTestStateTreeAccounts().addressQueue, + treeType: TreeType.AddressV1, + nextTreeInfo: null, + }; + } + return getTreeInfoByPubkey(allInfos, r); + }); + + return { + value: { + compressedProof: value.compressedProof, + roots: value.roots, + rootIndices: value.rootIndices.map((r: any) => r), + leafIndices: value.leafIndices, + leaves: value.leaves, + treeInfos: infos, + proveByIndices: value.rootIndices.map( + (r: any) => r.proveByIndex, + ), + }, + context: res.result.context, + }; + } } } diff --git a/js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts b/js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts index 651ca87a7c..7cabc08e5f 100644 --- a/js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts +++ b/js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts @@ -339,10 +339,6 @@ export class TestRpc extends Connection implements CompressionApiInterface { const leafIndex = leaves.findIndex(leaf => bn(leaf).eq(hashes[i]), ); - // const stateTreeInfo = getStateTreeInfoByPubkey( - // cachedStateTreeInfos, - // tree, - // ); /// If leaf is part of current tree, return proof if (leafIndex !== -1) { diff --git a/js/stateless.js/tests/unit/utils/conversion.test.ts b/js/stateless.js/tests/unit/utils/conversion.test.ts index 7743029443..b71c2157ff 100644 --- a/js/stateless.js/tests/unit/utils/conversion.test.ts +++ b/js/stateless.js/tests/unit/utils/conversion.test.ts @@ -662,8 +662,6 @@ describe('convertInvokeCpiWithReadOnlyToInvoke', () => { // First account (from input_compressed_accounts) const firstAccount = result.inputCompressedAccountsWithMerkleContext[0]; expect(firstAccount.rootIndex).toBe(789); - console.log('result', result); - console.log('firstAccount', firstAccount); expect(firstAccount.readOnly).toBe(false); expect(firstAccount.compressedAccount.lamports).toEqual(new BN(2000));