From 80fb05c6c0718e0a235e8f02eaab77477827da5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Swen=20Sch=C3=A4ferjohann?= <42959314+SwenSchaeferjohann@users.noreply.github.com> Date: Wed, 20 Nov 2024 23:17:42 +0000 Subject: [PATCH] feat: bump to 0.50.0, add getCompressedTokenBalancesByOwnerV2 (#1353) * bump to 0.50.0, add getCompressedTokenBalancesByOwnerV2, extend rem. endpoints with opt. cursors * upd name paginatedoptions * released js * add --no-reset flag to install.sh * clean readme * upd readme * upd readme for ctoken * capitalization * bump js release, readme updates --------- Co-authored-by: Swenschaeferjohann --- cli/README.md | 6 +- cli/package.json | 6 +- cli/src/utils/constants.ts | 2 +- js/compressed-token/README.md | 26 ++++-- js/compressed-token/package.json | 2 +- .../tests/e2e/rpc-token-interop.test.ts | 38 ++++++++ js/stateless.js/README.md | 26 +----- js/stateless.js/package.json | 2 +- js/stateless.js/src/rpc-interface.ts | 26 +++++- js/stateless.js/src/rpc.ts | 93 +++++++++++++++++-- .../src/test-helpers/test-rpc/test-rpc.ts | 44 +++++++-- scripts/install.sh | 15 ++- 12 files changed, 227 insertions(+), 59 deletions(-) diff --git a/cli/README.md b/cli/README.md index a2af2adfe..0c5f9fbe1 100644 --- a/cli/README.md +++ b/cli/README.md @@ -1,6 +1,6 @@ # ZK Compression CLI -CLI to interact with ZK compression. +CLI to interact with compressed accounts and compressed tokens on Solana. ## Requirements @@ -179,9 +179,7 @@ FLAGS ``` -#### Compress native SOL - -> **Note:** Ensure the SOL omnibus account of the Light system program is already initialized by running: `light init-sol-pool` +#### Assign native SOL to a compressed account ```bash light compress-sol --amount 1000 --to "YOUR_WALLET_ADDRESS_BASE58" diff --git a/cli/package.json b/cli/package.json index 9bffdb80a..bd4e74b70 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@lightprotocol/zk-compression-cli", - "version": "0.19.3", + "version": "0.20.1", "description": "ZK Compression: Secure Scaling on Solana", "maintainers": [ { @@ -21,6 +21,10 @@ "./config.json", "/npm-shrinkwrap.json", "/oclif.manifest.json", + "!bin/proving-keys/address-append_26_1.key", + "!bin/proving-keys/address-append_26_1.vkey", + "!bin/proving-keys/address-append_26_10.key", + "!bin/proving-keys/address-append_26_10.vkey", "!bin/proving-keys/append-with-proofs_26_10.key", "!bin/proving-keys/append-with-proofs_26_10.vkey", "!bin/proving-keys/append-with-subtrees_26_10.key", diff --git a/cli/src/utils/constants.ts b/cli/src/utils/constants.ts index 34366e4f1..05c4bcc67 100644 --- a/cli/src/utils/constants.ts +++ b/cli/src/utils/constants.ts @@ -19,7 +19,7 @@ export const SOLANA_VALIDATOR_PROCESS_NAME = "solana-test-validator"; export const LIGHT_PROVER_PROCESS_NAME = "light-prover"; export const INDEXER_PROCESS_NAME = "photon"; -export const PHOTON_VERSION = "0.48.0"; +export const PHOTON_VERSION = "0.50.0"; export const LIGHT_PROTOCOL_PROGRAMS_DIR_ENV = "LIGHT_PROTOCOL_PROGRAMS_DIR"; export const BASE_PATH = "../../bin/"; diff --git a/js/compressed-token/README.md b/js/compressed-token/README.md index 755c1600c..3e59da4ee 100644 --- a/js/compressed-token/README.md +++ b/js/compressed-token/README.md @@ -1,24 +1,36 @@ -# TS client for Interacting with the Compressed-Token Program +

+ +

+ +

@lightprotocol/compressed-token

+ +

+ This is the JavaScript SDK for interacting with the Compressed Token program on Solana. +

+ +

+ + package npm version + + package license + package weekly downloads +

-Use this to interact with the compressed-token program on Solana via the -Compression RPC API. ### Installation -**For use in Node.js or a web application** +**For use in Node.js or web** ```bash npm install --save \ @lightprotocol/compressed-token \ @lightprotocol/stateless.js \ - @solana/web3.js \ - @coral-xyz/anchor@0.29 ``` ### Documentation and examples - [Latest Source code](https://github.com/lightprotocol/lightprotocol/tree/main/js/compressed-token) -- Documentation and examples will be linked here soon! +- [Creating and sending compressed tokens](https://www.zkcompression.com/developers/typescript-client#creating-minting-and-transferring-a-compressed-token) ### Getting help diff --git a/js/compressed-token/package.json b/js/compressed-token/package.json index 31b89abd1..abebc852b 100644 --- a/js/compressed-token/package.json +++ b/js/compressed-token/package.json @@ -1,6 +1,6 @@ { "name": "@lightprotocol/compressed-token", - "version": "0.14.3", + "version": "0.15.1", "description": "JS client to interact with the compressed-token program", "sideEffects": false, "main": "dist/cjs/node/index.cjs", diff --git a/js/compressed-token/tests/e2e/rpc-token-interop.test.ts b/js/compressed-token/tests/e2e/rpc-token-interop.test.ts index 761fc00a6..8512cc6a5 100644 --- a/js/compressed-token/tests/e2e/rpc-token-interop.test.ts +++ b/js/compressed-token/tests/e2e/rpc-token-interop.test.ts @@ -152,6 +152,44 @@ describe('rpc-interop token', () => { }); }); + + it('getCompressedTokenBalancesByOwnerV2 should match', async () => { + const balances = ( + await rpc.getCompressedTokenBalancesByOwnerV2(bob.publicKey, { + mint, + }) + ).value.items; + const balancesTest = ( + await testRpc.getCompressedTokenBalancesByOwnerV2(bob.publicKey, { + mint, + }) + ).value.items; + + assert.equal(balances.length, balancesTest.length); + + balances.forEach((balance, index) => { + assert.isTrue(balance.balance.eq(balancesTest[index].balance)); + }); + + const balancesReceiver = ( + await rpc.getCompressedTokenBalancesByOwnerV2(charlie.publicKey, { + mint, + }) + ).value.items; + const balancesReceiverTest = ( + await testRpc.getCompressedTokenBalancesByOwnerV2(charlie.publicKey, { + mint, + }) + ).value.items; + + assert.equal(balancesReceiver.length, balancesReceiverTest.length); + balancesReceiver.forEach((balance, index) => { + assert.isTrue( + balance.balance.eq(balancesReceiverTest[index].balance), + ); + }); + }); + it('[test-rpc missing] getSignaturesForTokenOwner should match', async () => { const signatures = ( await rpc.getCompressionSignaturesForTokenOwner(bob.publicKey) diff --git a/js/stateless.js/README.md b/js/stateless.js/README.md index 5f75ed491..a0a65b7bf 100644 --- a/js/stateless.js/README.md +++ b/js/stateless.js/README.md @@ -2,10 +2,10 @@

-

Stateless.js

+

@lightprotocol/stateless.js

- Integrate server and web applications with ZK Compression on Solana. + This is the JavaScript SDK for building Solana applications with ZK Compression for Node and web.

@@ -16,14 +16,6 @@ package weekly downloads

-## Overview - -This package provides server and web applications with clients, utilities, and types to leverage the power of [ZK Compression](https://www.zkcompression.com/) on Solana via the Compression RPC API. - -> The core ZK Compression Solana programs and clients are maintained by -> [Light](https://github.com/lightprotocol) as a part of the Light Protocol. The RPC API and indexer are maintained by -> [Helius Labs](https://github.com/helius-labs). - ## Usage ### Installation @@ -31,22 +23,14 @@ This package provides server and web applications with clients, utilities, and t Install this package in your project by running the following terminal command: ```bin -npm install --save \ - @lightprotocol/stateless.js \ - @solana/web3.js \ - @coral-xyz/anchor@0.29 +npm install --save @lightprotocol/stateless.js ``` -### Dependencies - -- [`@solana/web3.js`](https://www.npmjs.com/package/@solana/web3.js) — provides access to the Solana network via RPC. -- [`@coral-xyz/anchor`](https://www.npmjs.com/package/@coral-xyz/anchor) — a client for [Anchor](https://www.anchor-lang.com/) Solana programs. - ## Documentation and Examples For a more detailed documentation on usage, please check [the respective section at the ZK Compression documentation.](https://www.zkcompression.com/developers/typescript-client) -For example implementations, including web and server, refer to the respective repositories: +For example implementations, including web and Node, refer to the respective repositories: - [Web application example implementation](https://github.com/Lightprotocol/example-web-client) @@ -55,7 +39,7 @@ For example implementations, including web and server, refer to the respective r ## Troubleshooting Have a question or a problem? -Feel free to ask in the [Light](https://discord.gg/CYvjBgzRFP) and [Helius](https://discord.gg/Uzzf6a7zKr) developer Discord servers. Please, include the following information to better be able to respond: +Feel free to ask in the [Light](https://discord.gg/CYvjBgzRFP) and [Helius](https://discord.gg/Uzzf6a7zKr) developer Discord servers. Please, include the following information: - A detailed description or context of the issue or what you are trying to achieve. - A code example that we can use to test and debug (if possible). Use [CodeSandbox](https://codesandbox.io/p/sandbox/vanilla-ts) or any other live environment provider. diff --git a/js/stateless.js/package.json b/js/stateless.js/package.json index 8064da679..6dab22af3 100644 --- a/js/stateless.js/package.json +++ b/js/stateless.js/package.json @@ -1,6 +1,6 @@ { "name": "@lightprotocol/stateless.js", - "version": "0.14.4", + "version": "0.15.1", "description": "JavaScript API for Light and ZK Compression", "sideEffects": false, "main": "dist/cjs/node/index.cjs", diff --git a/js/stateless.js/src/rpc-interface.ts b/js/stateless.js/src/rpc-interface.ts index 1cccfa565..ebdc1fade 100644 --- a/js/stateless.js/src/rpc-interface.ts +++ b/js/stateless.js/src/rpc-interface.ts @@ -130,8 +130,14 @@ export interface GetCompressedTokenAccountsByOwnerOrDelegateOptions { cursor?: string; limit?: BN; } +export type TokenBalance = { balance: BN; mint: PublicKey }; -export interface GetCompressedMintTokenHoldersOptions { +/** + * **Cursor** is a unique identifier for a page of results by which the next page can be fetched. + * + * **Limit** is the maximum number of results to return per page. + */ +export interface PaginatedOptions { cursor?: string; limit?: BN; } @@ -441,6 +447,11 @@ export const TokenBalanceListResult = pick({ cursor: nullable(string()), }); +export const TokenBalanceListResultV2 = pick({ + items: array(TokenBalanceResult), + cursor: nullable(string()), +}); + export const CompressedMintTokenHoldersResult = pick({ cursor: nullable(string()), items: array( @@ -546,7 +557,7 @@ export interface CompressionApiInterface { getCompressedMintTokenHolders( mint: PublicKey, - options?: GetCompressedMintTokenHoldersOptions, + options?: PaginatedOptions, ): Promise>>; getCompressedTokenAccountsByOwner( @@ -564,7 +575,12 @@ export interface CompressionApiInterface { getCompressedTokenBalancesByOwner( publicKey: PublicKey, options: GetCompressedTokenAccountsByOwnerOrDelegateOptions, - ): Promise>; + ): Promise>; + + getCompressedTokenBalancesByOwnerV2( + publicKey: PublicKey, + options: GetCompressedTokenAccountsByOwnerOrDelegateOptions, + ): Promise>>; getTransactionWithCompressionInfo( signature: string, @@ -576,18 +592,22 @@ export interface CompressionApiInterface { getCompressionSignaturesForAddress( address: PublicKey, + options?: PaginatedOptions, ): Promise>; getCompressionSignaturesForOwner( owner: PublicKey, + options?: PaginatedOptions, ): Promise>; getCompressionSignaturesForTokenOwner( owner: PublicKey, + options?: PaginatedOptions, ): Promise>; getLatestNonVotingSignatures( limit?: number, + cursor?: string, ): Promise; getLatestCompressionSignatures( diff --git a/js/stateless.js/src/rpc.ts b/js/stateless.js/src/rpc.ts index c86319305..0370dc9f4 100644 --- a/js/stateless.js/src/rpc.ts +++ b/js/stateless.js/src/rpc.ts @@ -41,7 +41,9 @@ import { HashWithTree, CompressedMintTokenHoldersResult, CompressedMintTokenHolders, - GetCompressedMintTokenHoldersOptions, + TokenBalance, + TokenBalanceListResultV2, + PaginatedOptions, } from './rpc-interface'; import { MerkleContextWithMerkleProof, @@ -806,13 +808,15 @@ export class Rpc extends Connection implements CompressionApiInterface { } /** + * @deprecated use {@link getCompressedTokenBalancesByOwnerV2} instead. + * * Fetch all the compressed token balances owned by the specified public - * key. Can filter by mint + * key. Can filter by mint. Returns without context. */ async getCompressedTokenBalancesByOwner( owner: PublicKey, options?: GetCompressedTokenAccountsByOwnerOrDelegateOptions, - ): Promise> { + ): Promise> { if (!options) options = {}; const unsafeRes = await rpcRequest( @@ -855,6 +859,59 @@ export class Rpc extends Connection implements CompressionApiInterface { }; } + /** + * Fetch the compressed token balances owned by the specified public + * key. Paginated. Can filter by mint. Returns with context. + */ + async getCompressedTokenBalancesByOwnerV2( + owner: PublicKey, + options?: GetCompressedTokenAccountsByOwnerOrDelegateOptions, + ): Promise>> { + if (!options) options = {}; + + const unsafeRes = await rpcRequest( + this.compressionApiEndpoint, + 'getCompressedTokenBalancesByOwnerV2', + { + owner: owner.toBase58(), + mint: options.mint?.toBase58(), + limit: options.limit?.toNumber(), + cursor: options.cursor, + }, + ); + + const res = create( + unsafeRes, + jsonRpcResultAndContext(TokenBalanceListResultV2), + ); + if ('error' in res) { + throw new SolanaJSONRPCError( + res.error, + `failed to get compressed token balances for owner ${owner.toBase58()}`, + ); + } + if (res.result.value === null) { + throw new Error( + `failed to get compressed token balances for owner ${owner.toBase58()}`, + ); + } + + const maybeFiltered = options.mint + ? res.result.value.items.filter( + tokenBalance => + tokenBalance.mint.toBase58() === options.mint!.toBase58(), + ) + : res.result.value.items; + + return { + context: res.result.context, + value: { + items: maybeFiltered, + cursor: res.result.value.cursor, + }, + }; + } + /** * Returns confirmed compression signatures for transactions involving the specified * account hash forward in time from genesis to the most recent confirmed @@ -985,11 +1042,16 @@ export class Rpc extends Connection implements CompressionApiInterface { */ async getCompressionSignaturesForAddress( address: PublicKey, + options?: PaginatedOptions, ): Promise> { const unsafeRes = await rpcRequest( this.compressionApiEndpoint, 'getCompressionSignaturesForAddress', - { address: address.toBase58() }, + { + address: address.toBase58(), + cursor: options?.cursor, + limit: options?.limit?.toNumber(), + }, ); const res = create( @@ -1020,11 +1082,16 @@ export class Rpc extends Connection implements CompressionApiInterface { */ async getCompressionSignaturesForOwner( owner: PublicKey, + options?: PaginatedOptions, ): Promise> { const unsafeRes = await rpcRequest( this.compressionApiEndpoint, 'getCompressionSignaturesForOwner', - { owner: owner.toBase58() }, + { + owner: owner.toBase58(), + cursor: options?.cursor, + limit: options?.limit?.toNumber(), + }, ); const res = create( @@ -1046,7 +1113,6 @@ export class Rpc extends Connection implements CompressionApiInterface { return res.result.value; } - /// TODO(photon): needs mint /** * Returns confirmed signatures for compression transactions involving the * specified token account owner forward in time from genesis to the most @@ -1054,11 +1120,16 @@ export class Rpc extends Connection implements CompressionApiInterface { */ async getCompressionSignaturesForTokenOwner( owner: PublicKey, + options?: PaginatedOptions, ): Promise> { const unsafeRes = await rpcRequest( this.compressionApiEndpoint, 'getCompressionSignaturesForTokenOwner', - { owner: owner.toBase58() }, + { + owner: owner.toBase58(), + cursor: options?.cursor, + limit: options?.limit?.toNumber(), + }, ); const res = create( @@ -1132,9 +1203,12 @@ export class Rpc extends Connection implements CompressionApiInterface { return res.result; } + /** + * Fetch all the compressed token holders for a given mint. Paginated. + */ async getCompressedMintTokenHolders( mint: PublicKey, - options?: GetCompressedMintTokenHoldersOptions, + options?: PaginatedOptions, ): Promise>> { const unsafeRes = await rpcRequest( this.compressionApiEndpoint, @@ -1189,11 +1263,12 @@ export class Rpc extends Connection implements CompressionApiInterface { */ async getLatestNonVotingSignatures( limit?: number, + cursor?: string, ): Promise { const unsafeRes = await rpcRequest( this.compressionApiEndpoint, 'getLatestNonVotingSignatures', - { limit }, + { limit, cursor }, ); const res = create( unsafeRes, 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 adc1ea5e9..cdba8c2dc 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 @@ -19,7 +19,7 @@ import { CompressedMintTokenHolders, CompressedTransaction, GetCompressedAccountsByOwnerConfig, - GetCompressedMintTokenHoldersOptions, + PaginatedOptions, HashWithTree, LatestNonVotingSignatures, LatestNonVotingSignaturesPaginated, @@ -32,6 +32,7 @@ import { CompressionApiInterface, GetCompressedTokenAccountsByOwnerOrDelegateOptions, ParsedTokenAccount, + TokenBalance, } from '../../rpc-interface'; import { BN254, @@ -418,8 +419,9 @@ export class TestRpc extends Connection implements CompressionApiInterface { } /** + * @deprecated use {@link getCompressedTokenBalancesByOwnerV2}. * Fetch all the compressed token balances owned by the specified public - * key. Can filter by mint + * key. Can filter by mint. */ async getCompressedTokenBalancesByOwner( publicKey: PublicKey, @@ -439,6 +441,31 @@ export class TestRpc extends Connection implements CompressionApiInterface { }; } + /** + * Fetch all the compressed token balances owned by the specified public + * key. Can filter by mint. Uses context. + */ + async getCompressedTokenBalancesByOwnerV2( + publicKey: PublicKey, + options: GetCompressedTokenAccountsByOwnerOrDelegateOptions, + ): Promise>> { + const accounts = await getCompressedTokenAccountsByOwnerTest( + this, + publicKey, + options.mint!, + ); + return { + context: { slot: 1 }, + value: { + items: accounts.items.map(account => ({ + balance: bn(account.parsed.amount), + mint: account.parsed.mint, + })), + cursor: null, + }, + }; + } + /** * Returns confirmed signatures for transactions involving the specified * account hash forward in time from genesis to the most recent confirmed @@ -447,7 +474,7 @@ export class TestRpc extends Connection implements CompressionApiInterface { * @param hash queried account hash */ async getCompressionSignaturesForAccount( - hash: BN254, + _hash: BN254, ): Promise { throw new Error( 'getCompressionSignaturesForAccount not implemented in test-rpc', @@ -459,7 +486,7 @@ export class TestRpc extends Connection implements CompressionApiInterface { * CompressionInfo */ async getTransactionWithCompressionInfo( - signature: string, + _signature: string, ): Promise { throw new Error('getCompressedTransaction not implemented in test-rpc'); } @@ -473,6 +500,7 @@ export class TestRpc extends Connection implements CompressionApiInterface { */ async getCompressionSignaturesForAddress( _address: PublicKey, + _options?: PaginatedOptions, ): Promise> { throw new Error('getSignaturesForAddress3 not implemented'); } @@ -485,7 +513,8 @@ export class TestRpc extends Connection implements CompressionApiInterface { * @param owner queried owner public key */ async getCompressionSignaturesForOwner( - owner: PublicKey, + _owner: PublicKey, + _options?: PaginatedOptions, ): Promise> { throw new Error('getSignaturesForOwner not implemented'); } @@ -496,7 +525,8 @@ export class TestRpc extends Connection implements CompressionApiInterface { * recent confirmed block */ async getCompressionSignaturesForTokenOwner( - owner: PublicKey, + _owner: PublicKey, + _options?: PaginatedOptions, ): Promise> { throw new Error('getSignaturesForTokenOwner not implemented'); } @@ -581,7 +611,7 @@ export class TestRpc extends Connection implements CompressionApiInterface { async getCompressedMintTokenHolders( _mint: PublicKey, - _options?: GetCompressedMintTokenHoldersOptions, + _options?: PaginatedOptions, ): Promise>> { throw new Error( 'getCompressedMintTokenHolders not implemented in test-rpc', diff --git a/scripts/install.sh b/scripts/install.sh index b3df6d3d3..6b09fadb5 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -13,7 +13,7 @@ VERSIONS=( "solana:1.18.22" "anchor:anchor-v0.29.0" "jq:jq-1.7.1" - "photon:0.48.0" + "photon:0.50.0" ) # Architecture-specific suffixes @@ -187,14 +187,19 @@ install_dependencies() { main() { mkdir -p "${PREFIX}/bin" - # Parse command line arguments + # Parse command line arguments local key_type="light" + local reset_log=true while [[ $# -gt 0 ]]; do case $1 in --full-keys) key_type="full" shift ;; + --no-reset) + reset_log=false + shift + ;; *) echo "Unknown option: $1" exit 1 @@ -202,6 +207,10 @@ main() { esac done + if $reset_log; then + rm -f "$INSTALL_LOG" + fi + install_go install_rust install_node @@ -212,8 +221,6 @@ main() { download_gnark_keys "$key_type" install_dependencies - rm -f "$INSTALL_LOG" - echo "✨ Light Protocol development dependencies installed" }