From 9efdc350da2d26c0713cf33deef82468e39ee842 Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Sat, 12 Apr 2025 06:16:07 +0100 Subject: [PATCH 1/2] wip --- package-lock.json | 185 ++++++------------ package.json | 6 +- src/constants.ts | 20 +- src/internal/create-account-pulse.ts | 9 +- .../create-account-with-lamports-pulse.ts | 14 +- src/internal/extend-state-lut.ts | 6 +- src/internal/transfer-pulse.ts | 14 +- src/scripts/airdrop/airdrop.ts | 10 +- src/scripts/airdrop/create-instructions.ts | 17 +- src/scripts/batch-compress.ts | 23 ++- src/scripts/compress-with-delegate.ts | 17 +- src/scripts/compress.ts | 39 ++-- src/scripts/mint-spl-22-manual.ts | 12 +- src/scripts/mint-spl-22.ts | 10 +- src/scripts/mint-spl.ts | 2 - src/scripts/transfer.ts | 29 ++- 16 files changed, 175 insertions(+), 238 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3eb29b6..078e16c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "version": "0.1.0", "license": "ISC", "dependencies": { - "@lightprotocol/compressed-token": "0.20.9", - "@lightprotocol/stateless.js": "0.20.9", + "@lightprotocol/compressed-token": "file:../light-protocol/js/compressed-token", + "@lightprotocol/stateless.js": "file:../light-protocol/js/stateless.js", "@solana/spl-token": "0.3.9", "@solana/spl-token-metadata": "^0.1.6", "@solana/web3.js": "^1.98.0", @@ -25,10 +25,62 @@ "@types/bn.js": "^5.1.6" } }, + "../light-protocol/js/compressed-token": { + "name": "@lightprotocol/compressed-token", + "version": "0.20.9", + "license": "Apache-2.0", + "dependencies": { + "@coral-xyz/borsh": "^0.29.0", + "bn.js": "^5.2.1", + "buffer": "6.0.3" + }, + "devDependencies": { + "@coral-xyz/anchor": "^0.29.0", + "@esbuild-plugins/node-globals-polyfill": "^0.2.3", + "@lightprotocol/hasher.rs": "0.2.1", + "@lightprotocol/programs": "workspace:*", + "@rollup/plugin-alias": "^5.1.0", + "@rollup/plugin-babel": "^6.0.4", + "@rollup/plugin-commonjs": "^26.0.1", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-replace": "^5.0.7", + "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-typescript": "^11.1.6", + "@solana/spl-token": "0.4.8", + "@solana/web3.js": "1.98.0", + "@types/bn.js": "^5.1.5", + "@types/node": "^22.5.5", + "@typescript-eslint/eslint-plugin": "^7.13.1", + "@typescript-eslint/parser": "^7.13.1", + "add": "^2.0.6", + "crypto-browserify": "^3.12.0", + "eslint": "^8.56.0", + "eslint-plugin-import": "^2.30.0", + "eslint-plugin-n": "^17.10.2", + "eslint-plugin-promise": "^7.1.0", + "eslint-plugin-vitest": "^0.5.4", + "prettier": "^3.3.3", + "rimraf": "^6.0.1", + "rollup": "^4.21.3", + "rollup-plugin-copy": "^3.5.0", + "rollup-plugin-dts": "^6.1.1", + "rollup-plugin-polyfill-node": "^0.13.0", + "rollup-plugin-visualizer": "^5.12.0", + "ts-node": "^10.9.2", + "tslib": "^2.7.0", + "typescript": "^5.6.2", + "vitest": "^2.1.1" + }, + "peerDependencies": { + "@lightprotocol/stateless.js": "workspace:*", + "@solana/spl-token": ">=0.3.9", + "@solana/web3.js": ">=1.73.5" + } + }, "../light-protocol/js/stateless.js": { "name": "@lightprotocol/stateless.js", "version": "0.20.0", - "extraneous": true, "license": "Apache-2.0", "dependencies": { "@coral-xyz/borsh": "^0.29.0", @@ -92,22 +144,6 @@ "node": ">=6.9.0" } }, - "node_modules/@coral-xyz/borsh": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.29.0.tgz", - "integrity": "sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ==", - "license": "Apache-2.0", - "dependencies": { - "bn.js": "^5.1.2", - "buffer-layout": "^1.2.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@solana/web3.js": "^1.68.0" - } - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -146,40 +182,12 @@ } }, "node_modules/@lightprotocol/compressed-token": { - "version": "0.20.9", - "resolved": "https://registry.npmjs.org/@lightprotocol/compressed-token/-/compressed-token-0.20.9.tgz", - "integrity": "sha512-svpFD+UbMxYHHcp1GERyOMMRl1WWhZfKDxR+he/mSHwXu0xf4AgtXsBeVubSow3Ruxw9sxz0oa5HqtSCtrlG0g==", - "license": "Apache-2.0", - "dependencies": { - "@coral-xyz/borsh": "^0.29.0", - "bn.js": "^5.2.1", - "buffer": "6.0.3" - }, - "peerDependencies": { - "@lightprotocol/stateless.js": "0.20.9", - "@solana/spl-token": ">=0.3.9", - "@solana/web3.js": ">=1.73.5" - } + "resolved": "../light-protocol/js/compressed-token", + "link": true }, "node_modules/@lightprotocol/stateless.js": { - "version": "0.20.9", - "resolved": "https://registry.npmjs.org/@lightprotocol/stateless.js/-/stateless.js-0.20.9.tgz", - "integrity": "sha512-YRvK7eKbA5E2lTDis8JWxVM3oxOhyD690BZBjIstKcVnweBW7NGHm5Xc033hjxSN1aO5WfjUuRNSVlXvbALfWQ==", - "license": "Apache-2.0", - "dependencies": { - "@coral-xyz/borsh": "^0.29.0", - "@noble/hashes": "1.5.0", - "bn.js": "^5.2.1", - "bs58": "^6.0.0", - "buffer": "6.0.3", - "buffer-layout": "^1.2.2", - "camelcase": "^8.0.0", - "camelcase-keys": "^9.1.3", - "superstruct": "2.0.2" - }, - "peerDependencies": { - "@solana/web3.js": ">=1.73.5" - } + "resolved": "../light-protocol/js/stateless.js", + "link": true }, "node_modules/@noble/curves": { "version": "1.8.0", @@ -674,15 +682,6 @@ "ieee754": "^1.2.1" } }, - "node_modules/buffer-layout": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz", - "integrity": "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==", - "license": "MIT", - "engines": { - "node": ">=4.5" - } - }, "node_modules/bufferutil": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz", @@ -697,36 +696,6 @@ "node": ">=6.14.2" } }, - "node_modules/camelcase": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-keys": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-9.1.3.tgz", - "integrity": "sha512-Rircqi9ch8AnZscQcsA1C47NFdaO3wukpmIRzYcDOrmvgt78hM/sj5pZhZNec2NM12uk5vTwRHZ4anGcrC4ZTg==", - "license": "MIT", - "dependencies": { - "camelcase": "^8.0.0", - "map-obj": "5.0.0", - "quick-lru": "^6.1.1", - "type-fest": "^4.3.2" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/chalk": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", @@ -939,18 +908,6 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "license": "ISC" }, - "node_modules/map-obj": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-5.0.0.tgz", - "integrity": "sha512-2L3MIgJynYrZ3TYMriLDLWocz15okFakV6J12HXvMXDHui2x/zgChzg1u9mFFGbbGWE+GsLpQByt4POb9Or+uA==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -989,18 +946,6 @@ "node-gyp-build-test": "build-test.js" } }, - "node_modules/quick-lru": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", - "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", @@ -1155,18 +1100,6 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, - "node_modules/type-fest": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.38.0.tgz", - "integrity": "sha512-2dBz5D5ycHIoliLYLi0Q2V7KRaDlH0uWIvmk7TYlAg5slqwiPv1ezJdZm1QEM0xgk29oYWMCbIG7E6gHpvChlg==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typescript": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", diff --git a/package.json b/package.json index 2433811..f5c1a6c 100644 --- a/package.json +++ b/package.json @@ -35,13 +35,13 @@ }, "homepage": "https://github.com/Lightprotocol/example-nodejs-client#readme", "dependencies": { - "@lightprotocol/compressed-token": "0.20.9", - "@lightprotocol/stateless.js": "0.20.9", + "@lightprotocol/compressed-token": "file:../light-protocol/js/compressed-token", + "@lightprotocol/stateless.js": "file:../light-protocol/js/stateless.js", "@solana/spl-token": "0.3.9", "@solana/spl-token-metadata": "^0.1.6", "@solana/web3.js": "^1.98.0", "@types/node": "^20.12.7", - "bn.js": "^5.2.1", + "bn.js": "^5.1.2", "bs58": "^6.0.0", "dotenv": "^16.4.5", "ts-node": "^10.9.2", diff --git a/src/constants.ts b/src/constants.ts index 00858d9..db0814d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -7,18 +7,13 @@ export const RPC_ENDPOINT = process.env.RPC_ENDPOINT; export const PAYER_KEYPAIR = Keypair.fromSecretKey( bs58.decode(process.env.PAYER_KEYPAIR!) ); - export const MINT_ADDRESS = new PublicKey(process.env.MINT_ADDRESS!); - -export const AUTHORITY_KEYPAIR = process.env.LUT_AUTHORITY_KEYPAIR - ? Keypair.fromSecretKey( - Uint8Array.from(JSON.parse(process.env.LUT_AUTHORITY_KEYPAIR)) - ) - : undefined; - -export const BOB_KEYPAIR = process.env.BOB_KEYPAIR - ? Keypair.fromSecretKey(Uint8Array.from(JSON.parse(process.env.BOB_KEYPAIR))) - : undefined; +export const AUTHORITY_KEYPAIR = Keypair.fromSecretKey( + Uint8Array.from(JSON.parse(process.env.LUT_AUTHORITY_KEYPAIR!)) +); +export const BOB_KEYPAIR = Keypair.fromSecretKey( + Uint8Array.from(JSON.parse(process.env.BOB_KEYPAIR!)) +); export const LUT_MAINNET_AUTHORITY_KEYPAIR = process.env.LUT_AUTHORITY_KEYPAIR ? Keypair.fromSecretKey( @@ -34,5 +29,8 @@ if (!RPC_ENDPOINT) throw new Error("Please set RPC_ENDPOINT in .env"); if (!PAYER_KEYPAIR) throw new Error("Please set PAYER_KEYPAIR as bs58 string in .env"); if (!MINT_ADDRESS) throw new Error("Please set MINT_ADDRESS in .env"); +if (!AUTHORITY_KEYPAIR) + throw new Error("Please set LUT_AUTHORITY_KEYPAIR in .env"); +if (!BOB_KEYPAIR) throw new Error("Please set BOB_KEYPAIR in .env"); console.log("PAYER PUBLIC KEY:", PAYER_KEYPAIR.publicKey.toString()); diff --git a/src/internal/create-account-pulse.ts b/src/internal/create-account-pulse.ts index 6d1fd6d..533e147 100644 --- a/src/internal/create-account-pulse.ts +++ b/src/internal/create-account-pulse.ts @@ -3,10 +3,10 @@ import { createRpc, LightSystemProgram, Rpc, + selectStateTreeInfo, } from "@lightprotocol/stateless.js"; import { PAYER_KEYPAIR, RPC_ENDPOINT } from "../constants"; import { randomBytes } from "crypto"; -import { PublicKey } from "@solana/web3.js"; const fromKeypair = PAYER_KEYPAIR; @@ -26,7 +26,9 @@ const trees = [ (async () => { try { while (true) { - const pseudoRandomTree = trees[Math.floor(Math.random() * trees.length)]; + const infos = await connection.getCachedActiveStateTreeInfos(); + const info = selectStateTreeInfo(infos); + // Create account with random address const randomSeed = new Uint8Array(randomBytes(32)); const txId = await createAccount( @@ -35,8 +37,7 @@ const trees = [ [randomSeed], LightSystemProgram.programId, undefined, - undefined, - new PublicKey(pseudoRandomTree) + info ); console.log( `Compressed Account Creation Success. Transaction Signature:`, diff --git a/src/internal/create-account-with-lamports-pulse.ts b/src/internal/create-account-with-lamports-pulse.ts index 16ca0bd..d87eced 100644 --- a/src/internal/create-account-with-lamports-pulse.ts +++ b/src/internal/create-account-with-lamports-pulse.ts @@ -4,8 +4,8 @@ import { createAccountWithLamports, createRpc, LightSystemProgram, - pickRandomTreeAndQueue, Rpc, + selectStateTreeInfo, } from "@lightprotocol/stateless.js"; import { PAYER_KEYPAIR, RPC_ENDPOINT } from "../constants"; import { randomBytes } from "crypto"; @@ -16,16 +16,15 @@ const connection: Rpc = createRpc(RPC_ENDPOINT); (async () => { try { while (true) { - const activeStateTrees = await connection.getCachedActiveStateTreeInfo(); - - const { tree, queue } = pickRandomTreeAndQueue(activeStateTrees); - console.log("Picked output state tree:", tree.toBase58()); + const infos = await connection.getCachedActiveStateTreeInfos(); + const info = selectStateTreeInfo(infos); + console.log("Picked output state tree:", info.tree.toBase58()); const compressedTxId = await compress( connection, fromKeypair, bn(10), fromKeypair.publicKey, - tree + info ); console.log("Compressed TxId", compressedTxId); // Creat account with random address @@ -36,8 +35,7 @@ const connection: Rpc = createRpc(RPC_ENDPOINT); 10, LightSystemProgram.programId, undefined, - undefined, - tree + info ); console.log( `Compressed Account Creation Success. Transaction Signature:`, diff --git a/src/internal/extend-state-lut.ts b/src/internal/extend-state-lut.ts index 76fa87a..7efb364 100644 --- a/src/internal/extend-state-lut.ts +++ b/src/internal/extend-state-lut.ts @@ -53,7 +53,7 @@ const cpis = [ const data = await createStateTreeLookupTable({ connection, payer, - authority: AUTHORITY_KEYPAIR, + authority: AUTHORITY_KEYPAIR!, recentSlot: await connection.getSlot(), }); @@ -67,7 +67,7 @@ const cpis = [ newQueueAddresses: queueAddresses.slice(0, queueAddresses.length / 2), newCpiContextAddresses: cpis.slice(0, cpis.length / 2), payer, - authority: AUTHORITY_KEYPAIR, + authority: AUTHORITY_KEYPAIR!, }); await extendStateTreeLookupTable({ @@ -79,6 +79,6 @@ const cpis = [ newQueueAddresses: queueAddresses.slice(queueAddresses.length / 2), newCpiContextAddresses: cpis.slice(cpis.length / 2), payer, - authority: AUTHORITY_KEYPAIR, + authority: AUTHORITY_KEYPAIR!, }); })(); diff --git a/src/internal/transfer-pulse.ts b/src/internal/transfer-pulse.ts index 2fdf381..9e977a0 100644 --- a/src/internal/transfer-pulse.ts +++ b/src/internal/transfer-pulse.ts @@ -2,13 +2,12 @@ import { bn, compress, createRpc, - pickRandomTreeAndQueue, Rpc, + selectStateTreeInfo, sleep, transfer, } from "@lightprotocol/stateless.js"; import { PAYER_KEYPAIR, RPC_ENDPOINT } from "../constants"; -import { PublicKey } from "@solana/web3.js"; const fromKeypair = PAYER_KEYPAIR; const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT); @@ -16,17 +15,16 @@ const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT); const batchSize = 10; (async () => { try { - const activeStateTrees = await connection.getCachedActiveStateTreeInfo(); - - const { tree, queue } = pickRandomTreeAndQueue(activeStateTrees); - console.log("Picked output state tree:", tree.toBase58()); + const infos = await connection.getCachedActiveStateTreeInfos(); + const info = selectStateTreeInfo(infos); + console.log("Picked output state tree:", info.tree.toBase58()); const compressedTxId = await compress( connection, fromKeypair, bn(1e5), fromKeypair.publicKey, - tree + info ); while (true) { console.log("Compressed TxId", compressedTxId); @@ -41,7 +39,7 @@ const batchSize = 10; 1, fromKeypair, fromKeypair.publicKey, - tree, + info, { skipPreflight: false, } diff --git a/src/scripts/airdrop/airdrop.ts b/src/scripts/airdrop/airdrop.ts index 3dcb8f4..999fc55 100644 --- a/src/scripts/airdrop/airdrop.ts +++ b/src/scripts/airdrop/airdrop.ts @@ -3,8 +3,9 @@ import { calculateComputeUnitPrice, createRpc, Rpc, + selectStateTreeInfo, } from "@lightprotocol/stateless.js"; -import { createMint } from "@lightprotocol/compressed-token"; +import { createMint, getTokenPoolInfos } from "@lightprotocol/compressed-token"; import { getOrCreateAssociatedTokenAccount, mintTo } from "@solana/spl-token"; import { createAirdropInstructions } from "./create-instructions"; import { BatchResultType, signAndSendAirdropBatches } from "./sign-and-send"; @@ -75,15 +76,16 @@ const recipients = [ ); console.log(`mint-to success! txId: ${mintToTxId}`); - const activeStateTrees = await connection.getCachedActiveStateTreeInfo(); - + const treeInfos = await connection.getCachedActiveStateTreeInfos(); + const tokenPoolInfos = await getTokenPoolInfos(connection, mint); const instructionBatches = await createAirdropInstructions({ amount: 1e6, recipients, payer: PAYER.publicKey, sourceTokenAccount: ata.address, mint, - stateTrees: activeStateTrees, + tokenPoolInfos, + outputStateTreeInfos: treeInfos, computeUnitPrice: calculateComputeUnitPrice(10_000, 500_000), }); diff --git a/src/scripts/airdrop/create-instructions.ts b/src/scripts/airdrop/create-instructions.ts index 4238715..a6b52e7 100644 --- a/src/scripts/airdrop/create-instructions.ts +++ b/src/scripts/airdrop/create-instructions.ts @@ -1,8 +1,9 @@ -import { CompressedTokenProgram } from "@lightprotocol/compressed-token"; +import { CompressedTokenProgram, selectTokenPoolInfo, TokenPoolInfo } from "@lightprotocol/compressed-token"; import { bn, - ActiveTreeBundle, pickRandomTreeAndQueue, + selectStateTreeInfo, + StateTreeInfo, } from "@lightprotocol/stateless.js"; import { ComputeBudgetProgram, @@ -16,7 +17,8 @@ interface CreateAirdropInstructionsParams { payer: PublicKey; sourceTokenAccount: PublicKey; mint: PublicKey; - stateTrees: ActiveTreeBundle[]; + tokenPoolInfos: TokenPoolInfo[]; + outputStateTreeInfos: StateTreeInfo[]; maxRecipientsPerInstruction?: number; maxInstructionsPerTransaction?: number; computeUnitLimit?: number; @@ -31,7 +33,8 @@ export async function createAirdropInstructions({ payer, sourceTokenAccount, mint, - stateTrees, + tokenPoolInfos, + outputStateTreeInfos, maxRecipientsPerInstruction = 5, maxInstructionsPerTransaction = 3, computeUnitLimit = 500_000, @@ -59,8 +62,9 @@ export async function createAirdropInstructions({ ); } - const { tree } = pickRandomTreeAndQueue(stateTrees); + const tree = selectStateTreeInfo(outputStateTreeInfos); + const tokenPoolInfo = selectTokenPoolInfo(tokenPoolInfos); for (let j = 0; j < maxInstructionsPerTransaction; j++) { const startIdx = i + j * maxRecipientsPerInstruction; const recipientBatch = recipients.slice( @@ -77,7 +81,8 @@ export async function createAirdropInstructions({ toAddress: recipientBatch, amount: recipientBatch.map(() => amountBn), mint, - outputStateTree: tree, + outputStateTreeInfo: tree, + tokenPoolInfo, }); instructions.push(compressIx); diff --git a/src/scripts/batch-compress.ts b/src/scripts/batch-compress.ts index de137c5..d1f50b4 100644 --- a/src/scripts/batch-compress.ts +++ b/src/scripts/batch-compress.ts @@ -1,15 +1,17 @@ import * as web3 from "@solana/web3.js"; import { RPC_ENDPOINT, PAYER_KEYPAIR, MINT_ADDRESS } from "../constants"; -import { CompressedTokenProgram } from "@lightprotocol/compressed-token"; - +import { + CompressedTokenProgram, + getTokenPoolInfos, + selectTokenPoolInfo, +} from "@lightprotocol/compressed-token"; import { bn, buildAndSignTx, createRpc, dedupeSigner, - pickRandomTreeAndQueue, Rpc, - sendAndConfirmTx, + selectStateTreeInfo, } from "@lightprotocol/stateless.js"; import * as splToken from "@solana/spl-token"; @@ -19,10 +21,14 @@ import * as splToken from "@solana/spl-token"; const mintAddress = MINT_ADDRESS; const payer = PAYER_KEYPAIR; - const activeStateTrees = await connection.getCachedActiveStateTreeInfo(); + const treeInfos = await connection.getCachedActiveStateTreeInfos(); + const treeInfo = selectStateTreeInfo(treeInfos); - const { tree } = pickRandomTreeAndQueue(activeStateTrees); - console.log("Picked output state tree:", tree.toBase58()); + // Get a token pool + const tokenPoolInfos = await getTokenPoolInfos(connection, mintAddress); + const tokenPoolInfo = selectTokenPoolInfo(tokenPoolInfos); + + // Get the source token account for the mint address // Get the source token account for the mint address const sourceTokenAccount = await splToken.getOrCreateAssociatedTokenAccount( @@ -78,7 +84,8 @@ import * as splToken from "@solana/spl-token"; toAddress: recipientBatch, amount: recipientBatch.map(() => amount), mint: mintAddress, - outputStateTree: tree, + outputStateTreeInfo: treeInfo, + tokenPoolInfo, }); instructions.push(compressIx); i += maxRecipientsPerInstruction; diff --git a/src/scripts/compress-with-delegate.ts b/src/scripts/compress-with-delegate.ts index 727a217..2693893 100644 --- a/src/scripts/compress-with-delegate.ts +++ b/src/scripts/compress-with-delegate.ts @@ -1,9 +1,4 @@ -import { - Rpc, - confirmTx, - createRpc, - pickRandomTreeAndQueue, -} from "@lightprotocol/stateless.js"; +import { Rpc, confirmTx, createRpc } from "@lightprotocol/stateless.js"; import { compress, createMint } from "@lightprotocol/compressed-token"; import { getOrCreateAssociatedTokenAccount, @@ -16,11 +11,9 @@ import { Keypair } from "@solana/web3.js"; const payer = PAYER_KEYPAIR; const delegate = Keypair.generate(); const recipient = Keypair.generate(); -console.log(`delegate: ${delegate.publicKey.toBase58()}`); -console.log(`recipient: ${recipient.publicKey.toBase58()}`); // Ensure you have light test-validator running -const connection: Rpc = createRpc(); +const connection: Rpc = createRpc(RPC_ENDPOINT); (async () => { /// airdrop lamports to pay fees @@ -66,9 +59,6 @@ const connection: Rpc = createRpc(); ); console.log(`approve success! txId: ${approveTxId}`); - const activeStateTrees = await connection.getCachedActiveStateTreeInfo(); - const { tree } = pickRandomTreeAndQueue(activeStateTrees); - const compressTxId = await compress( connection, payer, @@ -76,8 +66,7 @@ const connection: Rpc = createRpc(); 1e5, delegate, ata.address, - recipient.publicKey, - tree + recipient.publicKey ); console.log(`compress success! txId: ${compressTxId}`); })(); diff --git a/src/scripts/compress.ts b/src/scripts/compress.ts index fb58671..b71165b 100644 --- a/src/scripts/compress.ts +++ b/src/scripts/compress.ts @@ -1,14 +1,22 @@ -import * as web3 from "@solana/web3.js"; -import { Keypair, PublicKey } from "@solana/web3.js"; -import { CompressedTokenProgram } from "@lightprotocol/compressed-token"; +import { + ComputeBudgetProgram, + Keypair, + PublicKey, + TransactionInstruction, +} from "@solana/web3.js"; +import { + CompressedTokenProgram, + getTokenPoolInfos, + selectTokenPoolInfo, +} from "@lightprotocol/compressed-token"; import { bn, buildAndSignTx, calculateComputeUnitPrice, createRpc, dedupeSigner, - pickRandomTreeAndQueue, Rpc, + selectStateTreeInfo, sendAndConfirmTx, } from "@lightprotocol/stateless.js"; import * as splToken from "@solana/spl-token"; @@ -32,10 +40,14 @@ const PAYER_KEYPAIR = Keypair.fromSecretKey( const recipients = ["GMPWaPPrCeZPse5kwSR3WUrqYAPrVZBSVwymqh7auNW7"].map( (address) => new PublicKey(address) ); - const activeStateTrees = await connection.getCachedActiveStateTreeInfo(); - /// Pick a new tree for each transaction! - const { tree } = pickRandomTreeAndQueue(activeStateTrees); + // Get a state tree + const treeInfos = await connection.getCachedActiveStateTreeInfos(); + const treeInfo = selectStateTreeInfo(treeInfos); + + // Get a token pool + const tokenPool = await getTokenPoolInfos(connection, mintAddress); + const tokenPoolInfo = selectTokenPoolInfo(tokenPool); // Create an SPL token account for the sender. // The sender will send tokens from this account to the recipients as compressed tokens. @@ -50,11 +62,11 @@ const PAYER_KEYPAIR = Keypair.fromSecretKey( // 1 recipient = 120_000 CU // 5 recipients = 170_000 CU - const instructions: web3.TransactionInstruction[] = []; + const instructions: TransactionInstruction[] = []; instructions.push( - web3.ComputeBudgetProgram.setComputeUnitLimit({ units: 120_000 }), - web3.ComputeBudgetProgram.setComputeUnitPrice({ + ComputeBudgetProgram.setComputeUnitLimit({ units: 120_000 }), + ComputeBudgetProgram.setComputeUnitPrice({ // ideally replace this with a dynamic priority_fee based on network conditions. microLamports: calculateComputeUnitPrice(20_000, 120_000), }) @@ -63,17 +75,18 @@ const PAYER_KEYPAIR = Keypair.fromSecretKey( const compressInstruction = await CompressedTokenProgram.compress({ payer: payer.publicKey, owner: payer.publicKey, - source: sourceTokenAccount.address, // here, the owner of this account is also the payer. + source: sourceTokenAccount.address, toAddress: recipients, amount: recipients.map(() => amount), mint: mintAddress, - outputStateTree: tree, + outputStateTreeInfo: treeInfo, + tokenPoolInfo, }); instructions.push(compressInstruction); // Use zk-compression LUT for your network // https://www.zkcompression.com/developers/protocol-addresses-and-urls#lookup-tables - const lookupTableAddress = new web3.PublicKey( + const lookupTableAddress = new PublicKey( "9NYFyEqPkyXUhkerbGHXUXkvb4qpzeEdHuGpgbgpH1NJ" // mainnet // "qAJZMgnQJ8G6vA3WRcjD9Jan1wtKkaCFWLWskxJrR5V" // devnet ); diff --git a/src/scripts/mint-spl-22-manual.ts b/src/scripts/mint-spl-22-manual.ts index 29e2bd2..c1bd139 100644 --- a/src/scripts/mint-spl-22-manual.ts +++ b/src/scripts/mint-spl-22-manual.ts @@ -2,6 +2,7 @@ import { Rpc, createRpc, pickRandomTreeAndQueue, + selectStateTreeInfo, sendAndConfirmTx, } from "@lightprotocol/stateless.js"; import { @@ -37,9 +38,8 @@ const payer = PAYER_KEYPAIR; const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT); (async () => { - const activeStateTrees = await connection.getCachedActiveStateTreeInfo(); - - const { tree } = pickRandomTreeAndQueue(activeStateTrees); + const treeInfos = await connection.getCachedActiveStateTreeInfos(); + const treeInfo = selectStateTreeInfo(treeInfos); const mint = Keypair.generate(); const decimals = 9; @@ -151,9 +151,7 @@ const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT); payer, ata.address, payer.publicKey, - tree, - undefined, - TOKEN_2022_PROGRAM_ID + treeInfo ); console.log(`compressed-token success! txId: ${compressedTokenTxId}`); @@ -165,7 +163,7 @@ const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT); 1e5, payer, payer.publicKey, // self-transfer - tree + treeInfo ); console.log(`transfer-compressed success! txId: ${transferCompressedTxId}`); })(); diff --git a/src/scripts/mint-spl-22.ts b/src/scripts/mint-spl-22.ts index 7eddcb8..1c40bf3 100644 --- a/src/scripts/mint-spl-22.ts +++ b/src/scripts/mint-spl-22.ts @@ -2,6 +2,7 @@ import { Rpc, createRpc, pickRandomTreeAndQueue, + selectStateTreeInfo, sendAndConfirmTx, } from "@lightprotocol/stateless.js"; @@ -51,8 +52,9 @@ const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT); const metadataLen = TYPE_SIZE + LENGTH_SIZE + pack(metadata).length; - const activeStateTrees = await connection.getCachedActiveStateTreeInfo(); - const { tree } = pickRandomTreeAndQueue(activeStateTrees); + const treeInfos = await connection.getCachedActiveStateTreeInfos(); + const treeInfo = selectStateTreeInfo(treeInfos); + // airdrop for gas // await confirmTx( // connection, @@ -143,9 +145,7 @@ const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT); payer, ata.address, payer.publicKey, - tree, - undefined, - TOKEN_2022_PROGRAM_ID + treeInfo ); console.log(`compressed-token success! txId: ${compressedTokenTxId}`); diff --git a/src/scripts/mint-spl.ts b/src/scripts/mint-spl.ts index 3219769..05b88d8 100644 --- a/src/scripts/mint-spl.ts +++ b/src/scripts/mint-spl.ts @@ -16,8 +16,6 @@ const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT); // await connection.requestAirdrop(payer.publicKey, 1e7) // ); - const activeStateTrees = await connection.getCachedActiveStateTreeInfo(); - const { mint, transactionSignature } = await createMint( connection, payer, diff --git a/src/scripts/transfer.ts b/src/scripts/transfer.ts index d3fdf7e..d57ca81 100644 --- a/src/scripts/transfer.ts +++ b/src/scripts/transfer.ts @@ -5,6 +5,7 @@ import { dedupeSigner, sendAndConfirmTx, createRpc, + selectStateTreeInfo, } from "@lightprotocol/stateless.js"; import { CompressedTokenProgram, @@ -17,40 +18,37 @@ import { MINT_ADDRESS, BOB_KEYPAIR, } from "../constants"; -import { BN } from "bn.js"; - const payer = PAYER_KEYPAIR; -const tokenRecipient = BOB_KEYPAIR!; -console.log(payer.publicKey.toBase58()); -console.log(tokenRecipient.publicKey.toBase58()); -/// Localnet, expects `light test-validator` to be running: -// const connection: Rpc = createRpc(); +const tokenRecipient = BOB_KEYPAIR; /// Uncomment to use env: const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT, RPC_ENDPOINT); -const amount = new BN(100); +const amount = bn(100); (async () => { + // 1. Fetch token accounts const compressedTokenAccounts = await connection.getCompressedTokenAccountsByOwner(payer.publicKey, { mint: MINT_ADDRESS, }); - console.log( - compressedTokenAccounts.items - .reduce((acc, curr) => acc.add(curr.parsed.amount), new BN(0)) - .toString() - ); - + // 2. Select token accounts const [inputAccounts] = selectMinCompressedTokenAccountsForTransfer( compressedTokenAccounts.items, amount ); + // 3. Select state tree + const info = selectStateTreeInfo( + await connection.getCachedActiveStateTreeInfos() + ); + + // 4. Fetch validity proof const proof = await connection.getValidityProof( inputAccounts.map((account) => bn(account.compressedAccount.hash)) ); + // 5. Build instruction const ix = await CompressedTokenProgram.transfer({ payer: payer.publicKey, inputCompressedTokenAccounts: inputAccounts, @@ -58,12 +56,11 @@ const amount = new BN(100); amount, recentInputStateRootIndices: proof.rootIndices, recentValidityProof: proof.compressedProof, - outputStateTrees: undefined, + outputStateTreeInfo: info, }); const { blockhash } = await connection.getLatestBlockhash(); const additionalSigners = dedupeSigner(payer, [payer]); - const signedTx = buildAndSignTx( [ComputeBudgetProgram.setComputeUnitLimit({ units: 350_000 }), ix], payer, From f2e3f22b8b943371636243a492cfd1bc05b1dcae Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Sun, 13 Apr 2025 01:43:14 +0100 Subject: [PATCH 2/2] sync to latest --- package-lock.json | 17 ++--------------- package.json | 4 ---- src/internal/create-account-pulse.ts | 1 + src/internal/extend-lut.ts | 12 ++++++++---- src/scripts/batch-compress.ts | 4 ++-- 5 files changed, 13 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 078e16c..f4cab07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,14 +15,10 @@ "@solana/spl-token-metadata": "^0.1.6", "@solana/web3.js": "^1.98.0", "@types/node": "^20.12.7", - "bn.js": "^5.2.1", "bs58": "^6.0.0", "dotenv": "^16.4.5", "ts-node": "^10.9.2", "typescript": "^5.4.5" - }, - "devDependencies": { - "@types/bn.js": "^5.1.6" } }, "../light-protocol/js/compressed-token": { @@ -80,7 +76,7 @@ }, "../light-protocol/js/stateless.js": { "name": "@lightprotocol/stateless.js", - "version": "0.20.0", + "version": "0.20.9", "license": "Apache-2.0", "dependencies": { "@coral-xyz/borsh": "^0.29.0", @@ -90,6 +86,7 @@ "buffer": "6.0.3", "buffer-layout": "^1.2.2", "camelcase": "^8.0.0", + "camelcase-keys": "^9.1.3", "superstruct": "2.0.2" }, "devDependencies": { @@ -472,16 +469,6 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "license": "MIT" }, - "node_modules/@types/bn.js": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.6.tgz", - "integrity": "sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", diff --git a/package.json b/package.json index f5c1a6c..3f6ac16 100644 --- a/package.json +++ b/package.json @@ -41,13 +41,9 @@ "@solana/spl-token-metadata": "^0.1.6", "@solana/web3.js": "^1.98.0", "@types/node": "^20.12.7", - "bn.js": "^5.1.2", "bs58": "^6.0.0", "dotenv": "^16.4.5", "ts-node": "^10.9.2", "typescript": "^5.4.5" - }, - "devDependencies": { - "@types/bn.js": "^5.1.6" } } diff --git a/src/internal/create-account-pulse.ts b/src/internal/create-account-pulse.ts index 533e147..e334c7a 100644 --- a/src/internal/create-account-pulse.ts +++ b/src/internal/create-account-pulse.ts @@ -23,6 +23,7 @@ const trees = [ "smt9ReAYRF5eFjTd5gBJMn5aKwNRcmp3ub2CQr2vW7j", "smtAvYA5UbTRyKAkAj5kHs1CmrA42t6WkVLi4c6mA1f", ]; + (async () => { try { while (true) { diff --git a/src/internal/extend-lut.ts b/src/internal/extend-lut.ts index 07721d6..f6435b5 100644 --- a/src/internal/extend-lut.ts +++ b/src/internal/extend-lut.ts @@ -3,7 +3,7 @@ import { buildAndSignTx, createRpc, dedupeSigner, - getLightStateTreeInfo, + getActiveStateTreeInfos, sendAndConfirmTx, } from "@lightprotocol/stateless.js"; import { @@ -36,10 +36,14 @@ const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT, RPC_ENDPOINT); "5dhaJLBjnVBQFErr8oiCJmcVsx3Zj6xDekGB2zULPsnP" ); - const info = await getLightStateTreeInfo({ + const info = await getActiveStateTreeInfos({ connection, - stateTreeLookupTableAddress: stateTreeLookupTableDevnet, - nullifyTableAddress: nullifiedStateTreeLookupTableDevnet, + stateTreeLUTPairs: [ + { + stateTreeLookupTable: stateTreeLookupTableDevnet, + nullifyLookupTable: nullifiedStateTreeLookupTableDevnet, + }, + ], }); await extend( diff --git a/src/scripts/batch-compress.ts b/src/scripts/batch-compress.ts index d1f50b4..4591bcd 100644 --- a/src/scripts/batch-compress.ts +++ b/src/scripts/batch-compress.ts @@ -94,9 +94,9 @@ import * as splToken from "@solana/spl-token"; // Use zk-compression LUT for your network // https://www.zkcompression.com/developers/protocol-addresses-and-urls#lookup-tables - // Default: DA35UyyzGTonmEjsbw1VGRACpKxbKUPS2DvrG193QYHC const lookupTableAddress = new web3.PublicKey( - "qAJZMgnQJ8G6vA3WRcjD9Jan1wtKkaCFWLWskxJrR5V" // devnet + // "qAJZMgnQJ8G6vA3WRcjD9Jan1wtKkaCFWLWskxJrR5V" // devnet + "9NYFyEqPkyXUhkerbGHXUXkvb4qpzeEdHuGpgbgpH1NJ" // devnet ); // Get the lookup table account