@lightprotocol/zk-compression-cli 0.28.0-beta.5 → 0.28.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
Binary file
Binary file
@@ -21,7 +21,7 @@ class RegisterMintCommand extends core_1.Command {
21
21
  try {
22
22
  const payer = (0, utils_1.defaultSolanaWalletKeypair)();
23
23
  const mintAddress = new web3_js_1.PublicKey(flags.mint);
24
- const txId = await (0, compressed_token_1.createTokenPool)((0, utils_1.rpc)(), payer, mintAddress);
24
+ const txId = await (0, compressed_token_1.createSplInterface)((0, utils_1.rpc)(), payer, mintAddress);
25
25
  loader.stop(false);
26
26
  console.log("\x1b[1mMint public key:\x1b[0m ", mintAddress.toBase58());
27
27
  console.log("\x1b[1mMint tx:\x1b[0m ", (0, utils_1.generateSolanaTransactionURL)("tx", txId, "custom"));
@@ -6,6 +6,9 @@ declare class SetupCommand extends Command {
6
6
  static flags: {
7
7
  "skip-indexer": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
8
  "skip-prover": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ forester: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ "forester-port": import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ "light-pda-program": import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
12
  "skip-system-accounts": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
13
  "relax-indexer-version-constraint": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
14
  "indexer-db-url": import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -23,6 +26,8 @@ declare class SetupCommand extends Command {
23
26
  mainnet: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
24
27
  verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
25
28
  "skip-reset": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
29
+ "use-surfpool": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
30
+ "account-dir": import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
26
31
  };
27
32
  validatePrograms(programs: {
28
33
  address: string;
@@ -34,6 +34,20 @@ class SetupCommand extends core_1.Command {
34
34
  description: "Runs a test validator without starting a new prover service.",
35
35
  default: false,
36
36
  }),
37
+ forester: core_1.Flags.boolean({
38
+ description: "Start the forester service for auto-compression of compressible accounts.",
39
+ default: false,
40
+ }),
41
+ "forester-port": core_1.Flags.integer({
42
+ description: "Port for the forester API server.",
43
+ required: false,
44
+ default: 8080,
45
+ }),
46
+ "light-pda-program": core_1.Flags.string({
47
+ description: "Light PDA programs to track. Format: 'program_id:discriminator_base58'. Can be specified multiple times.",
48
+ required: false,
49
+ multiple: true,
50
+ }),
37
51
  "skip-system-accounts": core_1.Flags.boolean({
38
52
  description: "Runs a test validator without initialized light system accounts.",
39
53
  default: false,
@@ -120,6 +134,17 @@ class SetupCommand extends core_1.Command {
120
134
  description: "Skip resetting the ledger.",
121
135
  default: false,
122
136
  }),
137
+ "use-surfpool": core_1.Flags.boolean({
138
+ description: "Use surfpool instead of solana-test-validator (default). Pass --no-use-surfpool to use solana-test-validator.",
139
+ default: true,
140
+ allowNo: true,
141
+ }),
142
+ "account-dir": core_1.Flags.string({
143
+ description: "Additional directory containing account JSON files to preload. Can be specified multiple times.",
144
+ required: false,
145
+ multiple: true,
146
+ summary: "Usage: --account-dir <path/to/accounts/>",
147
+ }),
123
148
  };
124
149
  validatePrograms(programs, upgradeablePrograms) {
125
150
  // Check for duplicate addresses among all provided programs
@@ -167,6 +192,7 @@ class SetupCommand extends core_1.Command {
167
192
  await (0, initTestEnv_1.stopTestEnv)({
168
193
  indexer: !flags["skip-indexer"],
169
194
  prover: !flags["skip-prover"],
195
+ forester: flags.forester,
170
196
  });
171
197
  this.log("\nTest validator stopped successfully \x1b[32m✔\x1b[0m");
172
198
  }
@@ -209,6 +235,9 @@ class SetupCommand extends core_1.Command {
209
235
  indexerPort: flags["indexer-port"],
210
236
  proverPort: flags["prover-port"],
211
237
  prover: !flags["skip-prover"],
238
+ forester: flags.forester,
239
+ foresterPort: flags["forester-port"],
240
+ lightPdaPrograms: flags["light-pda-program"],
212
241
  skipSystemAccounts: flags["skip-system-accounts"],
213
242
  geyserConfig: flags["geyser-config"],
214
243
  validatorArgs: flags["validator-args"],
@@ -219,6 +248,8 @@ class SetupCommand extends core_1.Command {
219
248
  : undefined,
220
249
  verbose: flags.verbose,
221
250
  skipReset: flags["skip-reset"],
251
+ useSurfpool: flags["use-surfpool"],
252
+ additionalAccountDirs: flags["account-dir"],
222
253
  });
223
254
  this.log("\nSetup tasks completed successfully \x1b[32m✔\x1b[0m");
224
255
  }
@@ -10,12 +10,12 @@ export declare const DEFAULT_CONFIG: {
10
10
  };
11
11
  export declare const CARGO_GENERATE_TAG = "v0.18.4";
12
12
  export declare const SOLANA_VALIDATOR_PROCESS_NAME = "solana-test-validator";
13
+ export declare const SURFPOOL_PROCESS_NAME = "surfpool";
13
14
  export declare const LIGHT_PROVER_PROCESS_NAME = "light-prover";
14
15
  export declare const INDEXER_PROCESS_NAME = "photon";
15
- export declare const PHOTON_VERSION = "0.51.2";
16
- export declare const USE_PHOTON_FROM_GIT = true;
17
- export declare const PHOTON_GIT_REPO = "https://github.com/lightprotocol/photon.git";
18
- export declare const PHOTON_GIT_COMMIT = "ac7df6c388db847b7693a7a1cb766a7c9d7809b5";
16
+ export declare const FORESTER_PROCESS_NAME = "forester";
17
+ export declare const SURFPOOL_VERSION = "1.0.1";
18
+ export declare const SURFPOOL_RELEASE_TAG = "v1.0.1-light";
19
19
  export declare const LIGHT_PROTOCOL_PROGRAMS_DIR_ENV = "LIGHT_PROTOCOL_PROGRAMS_DIR";
20
20
  export declare const BASE_PATH = "../../bin/";
21
21
  export declare const PROGRAM_ID = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.STATELESS_JS_VERSION = exports.LIGHT_CLIENT_VERSION = exports.LIGHT_PROGRAM_TEST_VERSION = exports.LIGHT_COMPRESSED_ACCOUNT_VERSION = exports.LIGHT_SDK_TYPES_VERSION = exports.LIGHT_SDK_VERSION = exports.LIGHT_SDK_MACROS_VERSION = exports.LIGHT_MACROS_VERSION = exports.LIGHT_HASHER_VERSION = exports.SOLANA_CLI_VERSION = exports.LIGHT_CLI_VERSION = exports.TOKIO_VERSION = exports.COMPRESSED_PROGRAM_TEMPLATE_TAG = exports.ANCHOR_VERSION = exports.SOLANA_SDK_VERSION = exports.PROGRAM_ID = exports.BASE_PATH = exports.LIGHT_PROTOCOL_PROGRAMS_DIR_ENV = exports.PHOTON_GIT_COMMIT = exports.PHOTON_GIT_REPO = exports.USE_PHOTON_FROM_GIT = exports.PHOTON_VERSION = exports.INDEXER_PROCESS_NAME = exports.LIGHT_PROVER_PROCESS_NAME = exports.SOLANA_VALIDATOR_PROCESS_NAME = exports.CARGO_GENERATE_TAG = exports.DEFAULT_CONFIG = exports.CONFIG_FILE_NAME = exports.CONFIG_PATH = exports.LIGHT_COMPRESSED_TOKEN_TAG = exports.LIGHT_REGISTRY_TAG = exports.LIGHT_SYSTEM_PROGRAM_TAG = exports.LIGHT_ACCOUNT_COMPRESSION_TAG = exports.SPL_NOOP_PROGRAM_TAG = void 0;
3
+ exports.STATELESS_JS_VERSION = exports.LIGHT_CLIENT_VERSION = exports.LIGHT_PROGRAM_TEST_VERSION = exports.LIGHT_COMPRESSED_ACCOUNT_VERSION = exports.LIGHT_SDK_TYPES_VERSION = exports.LIGHT_SDK_VERSION = exports.LIGHT_SDK_MACROS_VERSION = exports.LIGHT_MACROS_VERSION = exports.LIGHT_HASHER_VERSION = exports.SOLANA_CLI_VERSION = exports.LIGHT_CLI_VERSION = exports.TOKIO_VERSION = exports.COMPRESSED_PROGRAM_TEMPLATE_TAG = exports.ANCHOR_VERSION = exports.SOLANA_SDK_VERSION = exports.PROGRAM_ID = exports.BASE_PATH = exports.LIGHT_PROTOCOL_PROGRAMS_DIR_ENV = exports.SURFPOOL_RELEASE_TAG = exports.SURFPOOL_VERSION = exports.FORESTER_PROCESS_NAME = exports.INDEXER_PROCESS_NAME = exports.LIGHT_PROVER_PROCESS_NAME = exports.SURFPOOL_PROCESS_NAME = exports.SOLANA_VALIDATOR_PROCESS_NAME = exports.CARGO_GENERATE_TAG = exports.DEFAULT_CONFIG = exports.CONFIG_FILE_NAME = exports.CONFIG_PATH = exports.LIGHT_COMPRESSED_TOKEN_TAG = exports.LIGHT_REGISTRY_TAG = exports.LIGHT_SYSTEM_PROGRAM_TAG = exports.LIGHT_ACCOUNT_COMPRESSION_TAG = exports.SPL_NOOP_PROGRAM_TAG = void 0;
4
4
  exports.SPL_NOOP_PROGRAM_TAG = "spl-noop-v0.2.0";
5
5
  exports.LIGHT_ACCOUNT_COMPRESSION_TAG = "account-compression-v1.0.0";
6
6
  exports.LIGHT_SYSTEM_PROGRAM_TAG = "light-system-program-v1.0.0";
@@ -15,13 +15,14 @@ exports.DEFAULT_CONFIG = {
15
15
  // Fixed version because 11/11/23 release (v0.18.5) fails
16
16
  exports.CARGO_GENERATE_TAG = "v0.18.4";
17
17
  exports.SOLANA_VALIDATOR_PROCESS_NAME = "solana-test-validator";
18
+ exports.SURFPOOL_PROCESS_NAME = "surfpool";
18
19
  exports.LIGHT_PROVER_PROCESS_NAME = "light-prover";
19
20
  exports.INDEXER_PROCESS_NAME = "photon";
20
- exports.PHOTON_VERSION = "0.51.2";
21
- // Set these to override Photon requirements with a specific git commit:
22
- exports.USE_PHOTON_FROM_GIT = true; // If true, will show git install command instead of crates.io.
23
- exports.PHOTON_GIT_REPO = "https://github.com/lightprotocol/photon.git";
24
- exports.PHOTON_GIT_COMMIT = "ac7df6c388db847b7693a7a1cb766a7c9d7809b5"; // If empty, will use main branch.
21
+ exports.FORESTER_PROCESS_NAME = "forester";
22
+ exports.SURFPOOL_VERSION = "1.0.1";
23
+ exports.SURFPOOL_RELEASE_TAG = "v1.0.1-light";
24
+ // PHOTON_VERSION, PHOTON_GIT_COMMIT, and PHOTON_GIT_REPO are auto-generated
25
+ // from the external/photon submodule at build time. See photonVersion.generated.ts.
25
26
  exports.LIGHT_PROTOCOL_PROGRAMS_DIR_ENV = "LIGHT_PROTOCOL_PROGRAMS_DIR";
26
27
  exports.BASE_PATH = "../../bin/";
27
28
  exports.PROGRAM_ID = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS";
@@ -8,8 +8,9 @@ export declare const SYSTEM_PROGRAMS: Program[];
8
8
  export declare function stopTestEnv(options: {
9
9
  indexer: boolean;
10
10
  prover: boolean;
11
+ forester?: boolean;
11
12
  }): Promise<void>;
12
- export declare function initTestEnv({ additionalPrograms, upgradeablePrograms, skipSystemAccounts, indexer, prover, rpcPort, indexerPort, proverPort, gossipHost, checkPhotonVersion, photonDatabaseUrl, limitLedgerSize, geyserConfig, validatorArgs, cloneNetwork, verbose, skipReset, }: {
13
+ export declare function initTestEnv({ additionalPrograms, upgradeablePrograms, skipSystemAccounts, indexer, prover, forester, rpcPort, indexerPort, proverPort, foresterPort, gossipHost, checkPhotonVersion, photonDatabaseUrl, limitLedgerSize, geyserConfig, validatorArgs, cloneNetwork, verbose, skipReset, useSurfpool, lightPdaPrograms, additionalAccountDirs, }: {
13
14
  additionalPrograms?: {
14
15
  address: string;
15
16
  path: string;
@@ -22,9 +23,11 @@ export declare function initTestEnv({ additionalPrograms, upgradeablePrograms, s
22
23
  skipSystemAccounts?: boolean;
23
24
  indexer: boolean;
24
25
  prover: boolean;
26
+ forester?: boolean;
25
27
  rpcPort?: number;
26
28
  indexerPort?: number;
27
29
  proverPort?: number;
30
+ foresterPort?: number;
28
31
  gossipHost?: string;
29
32
  checkPhotonVersion?: boolean;
30
33
  photonDatabaseUrl?: string;
@@ -34,6 +37,9 @@ export declare function initTestEnv({ additionalPrograms, upgradeablePrograms, s
34
37
  cloneNetwork?: "devnet" | "mainnet";
35
38
  verbose?: boolean;
36
39
  skipReset?: boolean;
40
+ useSurfpool?: boolean;
41
+ lightPdaPrograms?: string[];
42
+ additionalAccountDirs?: string[];
37
43
  }): Promise<void>;
38
44
  export declare function initTestEnvIfNeeded({ additionalPrograms, skipSystemAccounts, indexer, prover, geyserConfig, validatorArgs, }?: {
39
45
  additionalPrograms?: {
@@ -67,7 +73,23 @@ export declare function getSolanaArgs({ additionalPrograms, upgradeablePrograms,
67
73
  verbose?: boolean;
68
74
  skipReset?: boolean;
69
75
  }): Promise<Array<string>>;
70
- export declare function startTestValidator({ additionalPrograms, upgradeablePrograms, skipSystemAccounts, limitLedgerSize, rpcPort, gossipHost, validatorArgs, geyserConfig, cloneNetwork, verbose, skipReset, }: {
76
+ export declare function getSurfpoolArgs({ additionalPrograms, upgradeablePrograms, skipSystemAccounts, rpcPort, gossipHost, downloadBinaries, additionalAccountDirs, }: {
77
+ additionalPrograms?: {
78
+ address: string;
79
+ path: string;
80
+ }[];
81
+ upgradeablePrograms?: {
82
+ address: string;
83
+ path: string;
84
+ upgradeAuthority: string;
85
+ }[];
86
+ skipSystemAccounts?: boolean;
87
+ rpcPort?: number;
88
+ gossipHost?: string;
89
+ downloadBinaries?: boolean;
90
+ additionalAccountDirs?: string[];
91
+ }): Promise<Array<string>>;
92
+ export declare function startTestValidator({ additionalPrograms, upgradeablePrograms, skipSystemAccounts, limitLedgerSize, rpcPort, gossipHost, validatorArgs, geyserConfig, cloneNetwork, verbose, skipReset, useSurfpool, additionalAccountDirs, }: {
71
93
  additionalPrograms?: {
72
94
  address: string;
73
95
  path: string;
@@ -86,6 +108,8 @@ export declare function startTestValidator({ additionalPrograms, upgradeableProg
86
108
  cloneNetwork?: "devnet" | "mainnet";
87
109
  verbose?: boolean;
88
110
  skipReset?: boolean;
111
+ useSurfpool?: boolean;
112
+ additionalAccountDirs?: string[];
89
113
  }): Promise<void>;
90
- export declare function killTestValidator(): Promise<void>;
114
+ export declare function killTestValidator(rpcPort?: number): Promise<void>;
91
115
  export {};
@@ -7,17 +7,22 @@ exports.initTestEnvIfNeeded = initTestEnvIfNeeded;
7
7
  exports.programsDirPath = programsDirPath;
8
8
  exports.programFilePath = programFilePath;
9
9
  exports.getSolanaArgs = getSolanaArgs;
10
+ exports.getSurfpoolArgs = getSurfpoolArgs;
10
11
  exports.startTestValidator = startTestValidator;
11
12
  exports.killTestValidator = killTestValidator;
12
13
  const tslib_1 = require("tslib");
13
14
  const utils_1 = require("./utils");
14
15
  const constants_1 = require("./constants");
16
+ const fs_1 = tslib_1.__importDefault(require("fs"));
15
17
  const path_1 = tslib_1.__importDefault(require("path"));
18
+ const os_1 = tslib_1.__importDefault(require("os"));
16
19
  const psp_utils_1 = require("../psp-utils");
17
20
  const process_1 = require("./process");
18
21
  const processProverServer_1 = require("./processProverServer");
19
22
  const processPhotonIndexer_1 = require("./processPhotonIndexer");
23
+ const processForester_1 = require("./processForester");
20
24
  const web3_js_1 = require("@solana/web3.js");
25
+ const child_process_1 = require("child_process");
21
26
  exports.SYSTEM_PROGRAMS = [
22
27
  {
23
28
  id: "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV",
@@ -79,6 +84,7 @@ async function getProgramOwnedAccounts(programId, rpcUrl) {
79
84
  }
80
85
  async function stopTestEnv(options) {
81
86
  const processesToKill = [
87
+ { name: "forester", condition: options.forester ?? false, killFunction: processForester_1.killForester },
82
88
  { name: "photon", condition: options.indexer, killFunction: processPhotonIndexer_1.killIndexer },
83
89
  { name: "prover", condition: options.prover, killFunction: processProverServer_1.killProver },
84
90
  {
@@ -103,24 +109,50 @@ async function stopTestEnv(options) {
103
109
  await Promise.all(killPromises);
104
110
  console.log("All specified processes and validator stopped.");
105
111
  }
106
- async function initTestEnv({ additionalPrograms, upgradeablePrograms, skipSystemAccounts, indexer = true, prover = true, rpcPort = 8899, indexerPort = 8784, proverPort = 3001, gossipHost = "127.0.0.1", checkPhotonVersion = true, photonDatabaseUrl, limitLedgerSize, geyserConfig, validatorArgs, cloneNetwork, verbose, skipReset, }) {
107
- // We cannot await this promise directly because it will hang the process
108
- startTestValidator({
109
- additionalPrograms,
110
- upgradeablePrograms,
111
- skipSystemAccounts,
112
- limitLedgerSize,
113
- rpcPort,
114
- gossipHost,
115
- validatorArgs,
116
- geyserConfig,
117
- cloneNetwork,
118
- verbose,
119
- skipReset,
120
- });
121
- await (0, process_1.waitForServers)([{ port: rpcPort, path: "/health" }]);
122
- await (0, process_1.confirmServerStability)(`http://127.0.0.1:${rpcPort}/health`);
123
- await (0, process_1.confirmRpcReadiness)(`http://127.0.0.1:${rpcPort}`);
112
+ async function initTestEnv({ additionalPrograms, upgradeablePrograms, skipSystemAccounts, indexer = true, prover = true, forester = false, rpcPort = 8899, indexerPort = 8784, proverPort = 3001, foresterPort = 8080, gossipHost = "127.0.0.1", checkPhotonVersion = true, photonDatabaseUrl, limitLedgerSize, geyserConfig, validatorArgs, cloneNetwork, verbose, skipReset, useSurfpool, lightPdaPrograms, additionalAccountDirs, }) {
113
+ if (useSurfpool) {
114
+ // For surfpool we can await startTestValidator because spawnBinary returns
115
+ // immediately (surfpool starts in ~30ms). For solana-test-validator we must
116
+ // NOT await because the validator is a long-running process.
117
+ await startTestValidator({
118
+ additionalPrograms,
119
+ upgradeablePrograms,
120
+ skipSystemAccounts,
121
+ limitLedgerSize,
122
+ rpcPort,
123
+ gossipHost,
124
+ validatorArgs,
125
+ geyserConfig,
126
+ cloneNetwork,
127
+ verbose,
128
+ skipReset,
129
+ useSurfpool,
130
+ additionalAccountDirs,
131
+ });
132
+ // Surfpool only supports JSON-RPC POST, not GET /health.
133
+ await (0, process_1.confirmRpcReadiness)(`http://127.0.0.1:${rpcPort}`);
134
+ }
135
+ else {
136
+ // We cannot await this promise directly because it will hang the process
137
+ startTestValidator({
138
+ additionalPrograms,
139
+ upgradeablePrograms,
140
+ skipSystemAccounts,
141
+ limitLedgerSize,
142
+ rpcPort,
143
+ gossipHost,
144
+ validatorArgs,
145
+ geyserConfig,
146
+ cloneNetwork,
147
+ verbose,
148
+ skipReset,
149
+ useSurfpool,
150
+ additionalAccountDirs,
151
+ });
152
+ await (0, process_1.waitForServers)([{ port: rpcPort, path: "/health" }]);
153
+ await (0, process_1.confirmServerStability)(`http://127.0.0.1:${rpcPort}/health`);
154
+ await (0, process_1.confirmRpcReadiness)(`http://127.0.0.1:${rpcPort}`);
155
+ }
124
156
  if (prover) {
125
157
  const config = (0, utils_1.getConfig)();
126
158
  config.proverUrl = `http://127.0.0.1:${proverPort}`;
@@ -140,7 +172,56 @@ async function initTestEnv({ additionalPrograms, upgradeablePrograms, skipSystem
140
172
  const proverUrlForIndexer = prover
141
173
  ? `http://127.0.0.1:${proverPort}`
142
174
  : undefined;
143
- await (0, processPhotonIndexer_1.startIndexer)(`http://127.0.0.1:${rpcPort}`, indexerPort, checkPhotonVersion, photonDatabaseUrl, proverUrlForIndexer);
175
+ // Surfpool's first available block may not be slot 0.
176
+ // Query the RPC so Photon starts from the correct slot.
177
+ let startSlot;
178
+ if (useSurfpool) {
179
+ const conn = new web3_js_1.Connection(`http://127.0.0.1:${rpcPort}`);
180
+ startSlot = await conn.getFirstAvailableBlock();
181
+ }
182
+ await (0, processPhotonIndexer_1.startIndexer)(`http://127.0.0.1:${rpcPort}`, indexerPort, checkPhotonVersion, photonDatabaseUrl, proverUrlForIndexer, startSlot);
183
+ }
184
+ if (forester) {
185
+ if (!indexer || !prover) {
186
+ throw new Error("Forester requires both indexer and prover to be running");
187
+ }
188
+ try {
189
+ const payer = (0, processForester_1.getPayerForForester)();
190
+ await (0, processForester_1.startForester)({
191
+ rpcUrl: `http://127.0.0.1:${rpcPort}`,
192
+ wsRpcUrl: `ws://127.0.0.1:${rpcPort + 1}`,
193
+ indexerUrl: `http://127.0.0.1:${indexerPort}`,
194
+ proverUrl: `http://127.0.0.1:${proverPort}`,
195
+ payer,
196
+ foresterPort,
197
+ lightPdaPrograms,
198
+ });
199
+ }
200
+ catch (error) {
201
+ console.error("Failed to start forester:", error);
202
+ throw error;
203
+ }
204
+ }
205
+ if (forester) {
206
+ if (!indexer || !prover) {
207
+ throw new Error("Forester requires both indexer and prover to be running");
208
+ }
209
+ try {
210
+ const payer = (0, processForester_1.getPayerForForester)();
211
+ await (0, processForester_1.startForester)({
212
+ rpcUrl: `http://127.0.0.1:${rpcPort}`,
213
+ wsRpcUrl: `ws://127.0.0.1:${rpcPort + 1}`,
214
+ indexerUrl: `http://127.0.0.1:${indexerPort}`,
215
+ proverUrl: `http://127.0.0.1:${proverPort}`,
216
+ payer,
217
+ foresterPort,
218
+ lightPdaPrograms,
219
+ });
220
+ }
221
+ catch (error) {
222
+ console.error("Failed to start forester:", error);
223
+ throw error;
224
+ }
144
225
  }
145
226
  }
146
227
  async function initTestEnvIfNeeded({ additionalPrograms, skipSystemAccounts, indexer = false, prover = false, geyserConfig, validatorArgs, } = {}) {
@@ -270,36 +351,172 @@ async function getSolanaArgs({ additionalPrograms, upgradeablePrograms, skipSyst
270
351
  }
271
352
  return solanaArgs;
272
353
  }
273
- async function startTestValidator({ additionalPrograms, upgradeablePrograms, skipSystemAccounts, limitLedgerSize, rpcPort, gossipHost, validatorArgs, geyserConfig, cloneNetwork, verbose, skipReset, }) {
274
- const command = "solana-test-validator";
275
- const solanaArgs = await getSolanaArgs({
276
- additionalPrograms,
277
- upgradeablePrograms,
278
- skipSystemAccounts,
279
- limitLedgerSize,
280
- rpcPort,
281
- gossipHost,
282
- cloneNetwork,
283
- verbose,
284
- skipReset,
354
+ async function getSurfpoolArgs({ additionalPrograms, upgradeablePrograms, skipSystemAccounts, rpcPort, gossipHost, downloadBinaries = true, additionalAccountDirs, }) {
355
+ const dirPath = programsDirPath();
356
+ const args = ["start", "--offline", "--no-tui", "--no-deploy", "--no-studio"];
357
+ args.push("--port", String(rpcPort));
358
+ args.push("--host", String(gossipHost));
359
+ // Load system programs
360
+ for (const program of exports.SYSTEM_PROGRAMS) {
361
+ const localFilePath = programFilePath(program.name);
362
+ if (program.name === "spl_noop.so" || downloadBinaries) {
363
+ await (0, psp_utils_1.downloadBinIfNotExists)({
364
+ localFilePath,
365
+ dirPath,
366
+ owner: "Lightprotocol",
367
+ repoName: "light-protocol",
368
+ remoteFileName: program.name,
369
+ tag: program.tag,
370
+ });
371
+ }
372
+ args.push("--bpf-program", program.id, localFilePath);
373
+ }
374
+ // Load additional programs
375
+ if (additionalPrograms) {
376
+ for (const program of additionalPrograms) {
377
+ args.push("--bpf-program", program.address, program.path);
378
+ }
379
+ }
380
+ // Load upgradeable programs with full BPF upgradeable loader account layout
381
+ if (upgradeablePrograms) {
382
+ for (const program of upgradeablePrograms) {
383
+ args.push("--upgradeable-program", program.address, program.path, program.upgradeAuthority);
384
+ }
385
+ }
386
+ // Load system accounts
387
+ if (!skipSystemAccounts) {
388
+ const accountsRelPath = "../../accounts";
389
+ const accountsPath = path_1.default.resolve(__dirname, accountsRelPath);
390
+ args.push("--account-dir", accountsPath);
391
+ }
392
+ // Load additional account directories
393
+ if (additionalAccountDirs) {
394
+ for (const accountDir of additionalAccountDirs) {
395
+ args.push("--account-dir", path_1.default.resolve(accountDir));
396
+ }
397
+ }
398
+ return args;
399
+ }
400
+ function getSurfpoolAssetName() {
401
+ const platform = process.platform; // "darwin" | "linux"
402
+ const arch = process.arch; // "arm64" | "x64"
403
+ return `surfpool-${platform}-${arch}.tar.gz`;
404
+ }
405
+ function getSurfpoolBinDir() {
406
+ return path_1.default.join(os_1.default.homedir(), ".config", "light", "bin");
407
+ }
408
+ function getSurfpoolBinaryPath() {
409
+ return path_1.default.join(getSurfpoolBinDir(), "surfpool");
410
+ }
411
+ function getInstalledSurfpoolVersion() {
412
+ const binaryPath = getSurfpoolBinaryPath();
413
+ if (!fs_1.default.existsSync(binaryPath)) {
414
+ return null;
415
+ }
416
+ try {
417
+ const output = (0, child_process_1.execSync)(`"${binaryPath}" --version`, {
418
+ encoding: "utf-8",
419
+ timeout: 5000,
420
+ }).trim();
421
+ const match = output.match(/(\d+\.\d+\.\d+)/);
422
+ return match ? match[1] : null;
423
+ }
424
+ catch {
425
+ return null;
426
+ }
427
+ }
428
+ async function downloadSurfpoolBinary() {
429
+ const binPath = getSurfpoolBinaryPath();
430
+ const dirPath = getSurfpoolBinDir();
431
+ const assetName = getSurfpoolAssetName();
432
+ if (!fs_1.default.existsSync(dirPath)) {
433
+ fs_1.default.mkdirSync(dirPath, { recursive: true });
434
+ }
435
+ // Remove existing binary so downloadBinIfNotExists actually downloads
436
+ if (fs_1.default.existsSync(binPath)) {
437
+ fs_1.default.unlinkSync(binPath);
438
+ }
439
+ await (0, psp_utils_1.downloadBinIfNotExists)({
440
+ localFilePath: binPath,
441
+ dirPath,
442
+ owner: "Lightprotocol",
443
+ repoName: "surfpool",
444
+ remoteFileName: assetName,
445
+ tag: constants_1.SURFPOOL_RELEASE_TAG,
285
446
  });
286
- await killTestValidator();
287
- await new Promise((r) => setTimeout(r, 1000));
288
- // Add geyser config if provided
289
- if (geyserConfig) {
290
- solanaArgs.push("--geyser-plugin-config", geyserConfig);
291
- }
292
- // Add custom validator args last
293
- if (validatorArgs) {
294
- solanaArgs.push(...validatorArgs.split(" "));
295
- }
296
- console.log("Starting test validator...");
297
- // Use spawnBinary instead of executeCommand to properly detach the process.
298
- // This ensures the validator survives when the CLI exits (executeCommand uses
299
- // piped stdio which causes SIGPIPE when parent exits).
300
- // Pass process.env directly to maintain same env behavior as before.
301
- (0, process_1.spawnBinary)(command, solanaArgs, process.env);
302
447
  }
303
- async function killTestValidator() {
448
+ async function ensureSurfpoolBinary() {
449
+ const binPath = getSurfpoolBinaryPath();
450
+ const installedVersion = getInstalledSurfpoolVersion();
451
+ if (installedVersion === constants_1.SURFPOOL_VERSION) {
452
+ return binPath;
453
+ }
454
+ if (installedVersion) {
455
+ console.log(`Surfpool version mismatch. Expected: ${constants_1.SURFPOOL_VERSION}, Found: ${installedVersion}. Downloading correct version...`);
456
+ }
457
+ else if (fs_1.default.existsSync(binPath)) {
458
+ console.log("Surfpool binary found but version could not be determined. Downloading latest version...");
459
+ }
460
+ await downloadSurfpoolBinary();
461
+ return binPath;
462
+ }
463
+ async function startTestValidator({ additionalPrograms, upgradeablePrograms, skipSystemAccounts, limitLedgerSize, rpcPort, gossipHost, validatorArgs, geyserConfig, cloneNetwork, verbose, skipReset, useSurfpool, additionalAccountDirs, }) {
464
+ if (useSurfpool) {
465
+ const command = await ensureSurfpoolBinary();
466
+ const surfpoolArgs = await getSurfpoolArgs({
467
+ additionalPrograms,
468
+ upgradeablePrograms,
469
+ skipSystemAccounts,
470
+ rpcPort,
471
+ gossipHost,
472
+ additionalAccountDirs,
473
+ });
474
+ await killTestValidator(rpcPort);
475
+ await new Promise((r) => setTimeout(r, 1000));
476
+ console.log("Starting surfpool...");
477
+ (0, process_1.spawnBinary)(command, surfpoolArgs, process.env);
478
+ }
479
+ else {
480
+ const command = "solana-test-validator";
481
+ const solanaArgs = await getSolanaArgs({
482
+ additionalPrograms,
483
+ upgradeablePrograms,
484
+ skipSystemAccounts,
485
+ limitLedgerSize,
486
+ rpcPort,
487
+ gossipHost,
488
+ cloneNetwork,
489
+ verbose,
490
+ skipReset,
491
+ });
492
+ await killTestValidator(rpcPort);
493
+ await new Promise((r) => setTimeout(r, 1000));
494
+ // Add geyser config if provided
495
+ if (geyserConfig) {
496
+ solanaArgs.push("--geyser-plugin-config", geyserConfig);
497
+ }
498
+ // Add custom validator args last
499
+ if (validatorArgs) {
500
+ solanaArgs.push(...validatorArgs.split(" "));
501
+ }
502
+ console.log("Starting test validator...");
503
+ // Use spawnBinary instead of executeCommand to properly detach the process.
504
+ // This ensures the validator survives when the CLI exits (executeCommand uses
505
+ // piped stdio which causes SIGPIPE when parent exits).
506
+ // Pass process.env directly to maintain same env behavior as before.
507
+ (0, process_1.spawnBinary)(command, solanaArgs, process.env);
508
+ }
509
+ }
510
+ async function killTestValidator(rpcPort = 8899) {
304
511
  await (0, process_1.killProcess)("solana-test-validator");
512
+ await (0, process_1.killProcess)("surfpool");
513
+ // Fallback: kill anything still listening on the RPC port.
514
+ // find-process name matching can miss processes depending on platform
515
+ // and how the binary path appears in the process table.
516
+ try {
517
+ await (0, process_1.killProcessByPort)(rpcPort);
518
+ }
519
+ catch {
520
+ // No process listening on the port, nothing to do.
521
+ }
305
522
  }
@@ -0,0 +1,3 @@
1
+ export declare const PHOTON_VERSION = "0.51.2";
2
+ export declare const PHOTON_GIT_COMMIT = "0df2397c2c7d8458f45df9279e999a730ba56482";
3
+ export declare const PHOTON_GIT_REPO = "https://github.com/lightprotocol/photon.git";
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PHOTON_GIT_REPO = exports.PHOTON_GIT_COMMIT = exports.PHOTON_VERSION = void 0;
4
+ // Auto-generated from external/photon submodule - do not edit manually
5
+ exports.PHOTON_VERSION = "0.51.2";
6
+ exports.PHOTON_GIT_COMMIT = "0df2397c2c7d8458f45df9279e999a730ba56482";
7
+ exports.PHOTON_GIT_REPO = "https://github.com/lightprotocol/photon.git";
@@ -55,19 +55,17 @@ async function logProverFileContents() {
55
55
  }
56
56
  async function killProcess(processName) {
57
57
  const processList = await (0, find_process_1.default)("name", processName);
58
- const targetProcesses = processList.filter((proc) => proc.name.includes(processName) || proc.cmd.includes(processName));
58
+ const targetProcesses = processList.filter((proc) => proc.pid !== process.pid &&
59
+ proc.pid !== process.ppid &&
60
+ (proc.name.includes(processName) || proc.cmd.includes(processName)));
59
61
  for (const proc of targetProcesses) {
60
62
  try {
61
63
  process.kill(proc.pid, "SIGKILL");
62
64
  }
63
65
  catch (error) {
64
- console.error(`Failed to kill process ${proc.pid}: ${error}`);
66
+ // Process may have already exited between find and kill.
65
67
  }
66
68
  }
67
- const remainingProcesses = await (0, find_process_1.default)("name", processName);
68
- if (remainingProcesses.length > 0) {
69
- console.warn(`Warning: ${remainingProcesses.length} processes still running after kill attempt`);
70
- }
71
69
  }
72
70
  async function killProcessByPort(port) {
73
71
  if (port < 0) {
@@ -185,11 +183,28 @@ function spawnBinary(command, args = [], env) {
185
183
  RUST_LOG: process.env.RUST_LOG || "debug",
186
184
  },
187
185
  });
186
+ // Close parent's copy of the file descriptors so the child owns them
187
+ // exclusively and node's event loop isn't held open.
188
+ fs_1.default.closeSync(out);
189
+ fs_1.default.closeSync(err);
190
+ // Allow node to exit without waiting for the detached child.
191
+ spawnedProcess.unref();
188
192
  spawnedProcess.on("close", async (code) => {
189
193
  console.log(`${binaryName} process exited with code ${code}`);
190
- if (code !== 0 && binaryName.includes("prover")) {
191
- console.error(`Prover process failed with exit code ${code}`);
192
- await logProverFileContents();
194
+ if (code !== 0) {
195
+ console.error(`${binaryName} process failed with exit code ${code}`);
196
+ try {
197
+ const contents = fs_1.default.readFileSync(logPath, "utf8");
198
+ console.error(`--- ${binaryName}.log ---`);
199
+ console.error(contents);
200
+ console.error(`--- End of ${binaryName}.log ---`);
201
+ }
202
+ catch {
203
+ // log file may not exist yet
204
+ }
205
+ if (binaryName.includes("prover")) {
206
+ await logProverFileContents();
207
+ }
193
208
  }
194
209
  });
195
210
  return spawnedProcess;
@@ -0,0 +1,23 @@
1
+ export interface ForesterConfig {
2
+ rpcUrl: string;
3
+ wsRpcUrl: string;
4
+ indexerUrl: string;
5
+ proverUrl: string;
6
+ payer: string;
7
+ foresterPort: number;
8
+ lightPdaPrograms?: string[];
9
+ }
10
+ /**
11
+ * Starts the forester service for auto-compression of compressible accounts.
12
+ *
13
+ * @param config - Forester configuration
14
+ */
15
+ export declare function startForester(config: ForesterConfig): Promise<undefined>;
16
+ export declare function killForester(): Promise<void>;
17
+ /**
18
+ * Gets the payer keypair as a JSON array string for forester.
19
+ * Reads from ~/.config/solana/id.json or SOLANA_PAYER environment variable.
20
+ *
21
+ * @returns JSON array string of the keypair bytes
22
+ */
23
+ export declare function getPayerForForester(): string;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startForester = startForester;
4
+ exports.killForester = killForester;
5
+ exports.getPayerForForester = getPayerForForester;
6
+ const tslib_1 = require("tslib");
7
+ const which_1 = tslib_1.__importDefault(require("which"));
8
+ const process_1 = require("./process");
9
+ const constants_1 = require("./constants");
10
+ const node_child_process_1 = require("node:child_process");
11
+ const util = tslib_1.__importStar(require("node:util"));
12
+ const node_process_1 = require("node:process");
13
+ const fs = tslib_1.__importStar(require("fs"));
14
+ const path = tslib_1.__importStar(require("path"));
15
+ const execAsync = util.promisify(node_child_process_1.exec);
16
+ async function isForesterInstalled() {
17
+ try {
18
+ const resolvedOrNull = which_1.default.sync("forester", { nothrow: true });
19
+ return resolvedOrNull !== null;
20
+ }
21
+ catch (error) {
22
+ return false;
23
+ }
24
+ }
25
+ function getForesterInstallMessage() {
26
+ return `\nForester not found. Please install it by running: "cargo install --git https://github.com/Lightprotocol/light-protocol forester --locked --force"`;
27
+ }
28
+ /**
29
+ * Starts the forester service for auto-compression of compressible accounts.
30
+ *
31
+ * @param config - Forester configuration
32
+ */
33
+ async function startForester(config) {
34
+ await killForester();
35
+ if (!(await isForesterInstalled())) {
36
+ console.log(getForesterInstallMessage());
37
+ return (0, node_process_1.exit)(1);
38
+ }
39
+ console.log("Starting forester...");
40
+ const args = [
41
+ "start",
42
+ "--rpc-url",
43
+ config.rpcUrl,
44
+ "--ws-rpc-url",
45
+ config.wsRpcUrl,
46
+ "--indexer-url",
47
+ config.indexerUrl,
48
+ "--prover-url",
49
+ config.proverUrl,
50
+ "--payer",
51
+ config.payer,
52
+ "--api-server-port",
53
+ config.foresterPort.toString(),
54
+ "--enable-compressible",
55
+ ];
56
+ // Add light PDA programs if specified
57
+ if (config.lightPdaPrograms && config.lightPdaPrograms.length > 0) {
58
+ for (const program of config.lightPdaPrograms) {
59
+ args.push("--light-pda-program", program);
60
+ }
61
+ }
62
+ (0, process_1.spawnBinary)(constants_1.FORESTER_PROCESS_NAME, args);
63
+ await (0, process_1.waitForServers)([{ port: config.foresterPort, path: "/health" }]);
64
+ console.log("Forester started successfully!");
65
+ }
66
+ async function killForester() {
67
+ await (0, process_1.killProcess)(constants_1.FORESTER_PROCESS_NAME);
68
+ }
69
+ /**
70
+ * Gets the payer keypair as a JSON array string for forester.
71
+ * Reads from ~/.config/solana/id.json or SOLANA_PAYER environment variable.
72
+ *
73
+ * @returns JSON array string of the keypair bytes
74
+ */
75
+ function getPayerForForester() {
76
+ // Check for SOLANA_PAYER environment variable first
77
+ if (process.env.SOLANA_PAYER) {
78
+ return process.env.SOLANA_PAYER;
79
+ }
80
+ // Default to standard Solana keypair location
81
+ const homeDir = process.env.HOME || process.env.USERPROFILE || "";
82
+ const keypairPath = path.join(homeDir, ".config", "solana", "id.json");
83
+ if (fs.existsSync(keypairPath)) {
84
+ const keypairData = fs.readFileSync(keypairPath, "utf-8");
85
+ return keypairData.trim();
86
+ }
87
+ throw new Error("No payer keypair found. Set SOLANA_PAYER environment variable or create ~/.config/solana/id.json");
88
+ }
@@ -1,2 +1,2 @@
1
- export declare function startIndexer(rpcUrl: string, indexerPort: number, checkPhotonVersion?: boolean, photonDatabaseUrl?: string, proverUrl?: string): Promise<undefined>;
1
+ export declare function startIndexer(rpcUrl: string, indexerPort: number, checkPhotonVersion?: boolean, photonDatabaseUrl?: string, proverUrl?: string, startSlot?: number): Promise<undefined>;
2
2
  export declare function killIndexer(): Promise<void>;
@@ -6,6 +6,7 @@ const tslib_1 = require("tslib");
6
6
  const which_1 = tslib_1.__importDefault(require("which"));
7
7
  const process_1 = require("./process");
8
8
  const constants_1 = require("./constants");
9
+ const photonVersion_generated_1 = require("./photonVersion.generated");
9
10
  const node_child_process_1 = require("node:child_process");
10
11
  const util = tslib_1.__importStar(require("node:util"));
11
12
  const node_process_1 = require("node:process");
@@ -22,21 +23,13 @@ async function isExpectedPhotonVersion(requiredVersion) {
22
23
  }
23
24
  }
24
25
  function getPhotonInstallMessage() {
25
- if (constants_1.USE_PHOTON_FROM_GIT && constants_1.PHOTON_GIT_COMMIT) {
26
- return `\nPhoton indexer ${constants_1.PHOTON_VERSION} (commit ${constants_1.PHOTON_GIT_COMMIT}) not found. Please install it by running: "cargo install --git ${constants_1.PHOTON_GIT_REPO} --rev ${constants_1.PHOTON_GIT_COMMIT} --locked --force"`;
27
- }
28
- else if (constants_1.USE_PHOTON_FROM_GIT) {
29
- return `\nPhoton indexer ${constants_1.PHOTON_VERSION} not found. Please install it by running: "cargo install --git ${constants_1.PHOTON_GIT_REPO} --locked --force"`;
30
- }
31
- else {
32
- return `\nPhoton indexer ${constants_1.PHOTON_VERSION} not found. Please install it by running: "cargo install photon-indexer --version ${constants_1.PHOTON_VERSION} --locked --force"`;
33
- }
26
+ return `\nPhoton indexer ${photonVersion_generated_1.PHOTON_VERSION} (commit ${photonVersion_generated_1.PHOTON_GIT_COMMIT}) not found. Please install it by running: "cargo install --git ${photonVersion_generated_1.PHOTON_GIT_REPO} --rev ${photonVersion_generated_1.PHOTON_GIT_COMMIT} --locked --force"`;
34
27
  }
35
- async function startIndexer(rpcUrl, indexerPort, checkPhotonVersion = true, photonDatabaseUrl, proverUrl) {
28
+ async function startIndexer(rpcUrl, indexerPort, checkPhotonVersion = true, photonDatabaseUrl, proverUrl, startSlot) {
36
29
  await killIndexer();
37
30
  const resolvedOrNull = which_1.default.sync("photon", { nothrow: true });
38
31
  if (resolvedOrNull === null ||
39
- (checkPhotonVersion && !(await isExpectedPhotonVersion(constants_1.PHOTON_VERSION)))) {
32
+ (checkPhotonVersion && !(await isExpectedPhotonVersion(photonVersion_generated_1.PHOTON_VERSION)))) {
40
33
  console.log(getPhotonInstallMessage());
41
34
  return (0, node_process_1.exit)(1);
42
35
  }
@@ -54,6 +47,9 @@ async function startIndexer(rpcUrl, indexerPort, checkPhotonVersion = true, phot
54
47
  if (proverUrl) {
55
48
  args.push("--prover-url", proverUrl);
56
49
  }
50
+ if (startSlot !== undefined) {
51
+ args.push("--start-slot", startSlot.toString());
52
+ }
57
53
  (0, process_1.spawnBinary)(constants_1.INDEXER_PROCESS_NAME, args);
58
54
  await (0, process_1.waitForServers)([{ port: indexerPort, path: "/getIndexerHealth" }]);
59
55
  console.log("Indexer started successfully!");
@@ -90,23 +90,15 @@
90
90
  "index.js"
91
91
  ]
92
92
  },
93
- "compress-spl": {
93
+ "compress-sol": {
94
94
  "aliases": [],
95
95
  "args": {},
96
96
  "examples": [
97
- "$ light compress-spl --mint PublicKey --to PublicKey --amount 10"
97
+ "$ light compress-sol --to PublicKey --amount 10"
98
98
  ],
99
99
  "flags": {
100
- "mint": {
101
- "description": "Specify the mint address.",
102
- "name": "mint",
103
- "required": true,
104
- "hasDynamicHelp": false,
105
- "multiple": false,
106
- "type": "option"
107
- },
108
100
  "to": {
109
- "description": "Specify the recipient address (owner of destination compressed token account).",
101
+ "description": "Specify the recipient address.",
110
102
  "name": "to",
111
103
  "required": true,
112
104
  "hasDynamicHelp": false,
@@ -114,7 +106,7 @@
114
106
  "type": "option"
115
107
  },
116
108
  "amount": {
117
- "description": "Amount to compress, in tokens.",
109
+ "description": "Amount to compress, in lamports.",
118
110
  "name": "amount",
119
111
  "required": true,
120
112
  "hasDynamicHelp": false,
@@ -124,30 +116,38 @@
124
116
  },
125
117
  "hasDynamicHelp": false,
126
118
  "hiddenAliases": [],
127
- "id": "compress-spl",
119
+ "id": "compress-sol",
128
120
  "pluginAlias": "@lightprotocol/zk-compression-cli",
129
121
  "pluginName": "@lightprotocol/zk-compression-cli",
130
122
  "pluginType": "core",
131
123
  "strict": true,
132
- "summary": "Compress SPL tokens.",
124
+ "summary": "Compress SOL.",
133
125
  "enableJsonFlag": false,
134
126
  "isESM": false,
135
127
  "relativePath": [
136
128
  "dist",
137
129
  "commands",
138
- "compress-spl",
130
+ "compress-sol",
139
131
  "index.js"
140
132
  ]
141
133
  },
142
- "compress-sol": {
134
+ "compress-spl": {
143
135
  "aliases": [],
144
136
  "args": {},
145
137
  "examples": [
146
- "$ light compress-sol --to PublicKey --amount 10"
138
+ "$ light compress-spl --mint PublicKey --to PublicKey --amount 10"
147
139
  ],
148
140
  "flags": {
141
+ "mint": {
142
+ "description": "Specify the mint address.",
143
+ "name": "mint",
144
+ "required": true,
145
+ "hasDynamicHelp": false,
146
+ "multiple": false,
147
+ "type": "option"
148
+ },
149
149
  "to": {
150
- "description": "Specify the recipient address.",
150
+ "description": "Specify the recipient address (owner of destination compressed token account).",
151
151
  "name": "to",
152
152
  "required": true,
153
153
  "hasDynamicHelp": false,
@@ -155,7 +155,7 @@
155
155
  "type": "option"
156
156
  },
157
157
  "amount": {
158
- "description": "Amount to compress, in lamports.",
158
+ "description": "Amount to compress, in tokens.",
159
159
  "name": "amount",
160
160
  "required": true,
161
161
  "hasDynamicHelp": false,
@@ -165,18 +165,18 @@
165
165
  },
166
166
  "hasDynamicHelp": false,
167
167
  "hiddenAliases": [],
168
- "id": "compress-sol",
168
+ "id": "compress-spl",
169
169
  "pluginAlias": "@lightprotocol/zk-compression-cli",
170
170
  "pluginName": "@lightprotocol/zk-compression-cli",
171
171
  "pluginType": "core",
172
172
  "strict": true,
173
- "summary": "Compress SOL.",
173
+ "summary": "Compress SPL tokens.",
174
174
  "enableJsonFlag": false,
175
175
  "isESM": false,
176
176
  "relativePath": [
177
177
  "dist",
178
178
  "commands",
179
- "compress-sol",
179
+ "compress-spl",
180
180
  "index.js"
181
181
  ]
182
182
  },
@@ -369,15 +369,23 @@
369
369
  "index.js"
370
370
  ]
371
371
  },
372
- "decompress-sol": {
372
+ "decompress-spl": {
373
373
  "aliases": [],
374
374
  "args": {},
375
375
  "examples": [
376
- "$ light decompress-sol --to PublicKey --amount 10"
376
+ "$ light decompress-spl --mint PublicKey --to PublicKey --amount 10"
377
377
  ],
378
378
  "flags": {
379
+ "mint": {
380
+ "description": "Specify the mint address.",
381
+ "name": "mint",
382
+ "required": true,
383
+ "hasDynamicHelp": false,
384
+ "multiple": false,
385
+ "type": "option"
386
+ },
379
387
  "to": {
380
- "description": "Specify the recipient address.",
388
+ "description": "Specify the recipient address. (owner of destination token account)",
381
389
  "name": "to",
382
390
  "required": true,
383
391
  "hasDynamicHelp": false,
@@ -385,7 +393,7 @@
385
393
  "type": "option"
386
394
  },
387
395
  "amount": {
388
- "description": "Amount to decompress, in lamports.",
396
+ "description": "Amount to decompress, in tokens.",
389
397
  "name": "amount",
390
398
  "required": true,
391
399
  "hasDynamicHelp": false,
@@ -395,38 +403,30 @@
395
403
  },
396
404
  "hasDynamicHelp": false,
397
405
  "hiddenAliases": [],
398
- "id": "decompress-sol",
406
+ "id": "decompress-spl",
399
407
  "pluginAlias": "@lightprotocol/zk-compression-cli",
400
408
  "pluginName": "@lightprotocol/zk-compression-cli",
401
409
  "pluginType": "core",
402
410
  "strict": true,
403
- "summary": "Decompress SOL.",
411
+ "summary": "Decompress into SPL tokens.",
404
412
  "enableJsonFlag": false,
405
413
  "isESM": false,
406
414
  "relativePath": [
407
415
  "dist",
408
416
  "commands",
409
- "decompress-sol",
417
+ "decompress-spl",
410
418
  "index.js"
411
419
  ]
412
420
  },
413
- "decompress-spl": {
421
+ "decompress-sol": {
414
422
  "aliases": [],
415
423
  "args": {},
416
424
  "examples": [
417
- "$ light decompress-spl --mint PublicKey --to PublicKey --amount 10"
425
+ "$ light decompress-sol --to PublicKey --amount 10"
418
426
  ],
419
427
  "flags": {
420
- "mint": {
421
- "description": "Specify the mint address.",
422
- "name": "mint",
423
- "required": true,
424
- "hasDynamicHelp": false,
425
- "multiple": false,
426
- "type": "option"
427
- },
428
428
  "to": {
429
- "description": "Specify the recipient address. (owner of destination token account)",
429
+ "description": "Specify the recipient address.",
430
430
  "name": "to",
431
431
  "required": true,
432
432
  "hasDynamicHelp": false,
@@ -434,7 +434,7 @@
434
434
  "type": "option"
435
435
  },
436
436
  "amount": {
437
- "description": "Amount to decompress, in tokens.",
437
+ "description": "Amount to decompress, in lamports.",
438
438
  "name": "amount",
439
439
  "required": true,
440
440
  "hasDynamicHelp": false,
@@ -444,18 +444,18 @@
444
444
  },
445
445
  "hasDynamicHelp": false,
446
446
  "hiddenAliases": [],
447
- "id": "decompress-spl",
447
+ "id": "decompress-sol",
448
448
  "pluginAlias": "@lightprotocol/zk-compression-cli",
449
449
  "pluginName": "@lightprotocol/zk-compression-cli",
450
450
  "pluginType": "core",
451
451
  "strict": true,
452
- "summary": "Decompress into SPL tokens.",
452
+ "summary": "Decompress SOL.",
453
453
  "enableJsonFlag": false,
454
454
  "isESM": false,
455
455
  "relativePath": [
456
456
  "dist",
457
457
  "commands",
458
- "decompress-spl",
458
+ "decompress-sol",
459
459
  "index.js"
460
460
  ]
461
461
  },
@@ -527,6 +527,63 @@
527
527
  "index.js"
528
528
  ]
529
529
  },
530
+ "mint-to": {
531
+ "aliases": [],
532
+ "args": {},
533
+ "examples": [
534
+ "$ light mint-to --mint PublicKey --to PublicKey --amount 1000"
535
+ ],
536
+ "flags": {
537
+ "mint-authority": {
538
+ "description": "Specify the filepath of the mint authority keypair. Defaults to your local solana wallet.",
539
+ "name": "mint-authority",
540
+ "required": false,
541
+ "hasDynamicHelp": false,
542
+ "multiple": false,
543
+ "type": "option"
544
+ },
545
+ "mint": {
546
+ "description": "Specify the mint address.",
547
+ "name": "mint",
548
+ "required": true,
549
+ "hasDynamicHelp": false,
550
+ "multiple": false,
551
+ "type": "option"
552
+ },
553
+ "to": {
554
+ "description": "Specify the recipient address.",
555
+ "name": "to",
556
+ "required": true,
557
+ "hasDynamicHelp": false,
558
+ "multiple": false,
559
+ "type": "option"
560
+ },
561
+ "amount": {
562
+ "description": "Amount to mint, in tokens.",
563
+ "name": "amount",
564
+ "required": true,
565
+ "hasDynamicHelp": false,
566
+ "multiple": false,
567
+ "type": "option"
568
+ }
569
+ },
570
+ "hasDynamicHelp": false,
571
+ "hiddenAliases": [],
572
+ "id": "mint-to",
573
+ "pluginAlias": "@lightprotocol/zk-compression-cli",
574
+ "pluginName": "@lightprotocol/zk-compression-cli",
575
+ "pluginType": "core",
576
+ "strict": true,
577
+ "summary": "Mint tokens to an account.",
578
+ "enableJsonFlag": false,
579
+ "isESM": false,
580
+ "relativePath": [
581
+ "dist",
582
+ "commands",
583
+ "mint-to",
584
+ "index.js"
585
+ ]
586
+ },
530
587
  "start-prover": {
531
588
  "aliases": [],
532
589
  "args": {},
@@ -664,63 +721,6 @@
664
721
  "index.js"
665
722
  ]
666
723
  },
667
- "mint-to": {
668
- "aliases": [],
669
- "args": {},
670
- "examples": [
671
- "$ light mint-to --mint PublicKey --to PublicKey --amount 1000"
672
- ],
673
- "flags": {
674
- "mint-authority": {
675
- "description": "Specify the filepath of the mint authority keypair. Defaults to your local solana wallet.",
676
- "name": "mint-authority",
677
- "required": false,
678
- "hasDynamicHelp": false,
679
- "multiple": false,
680
- "type": "option"
681
- },
682
- "mint": {
683
- "description": "Specify the mint address.",
684
- "name": "mint",
685
- "required": true,
686
- "hasDynamicHelp": false,
687
- "multiple": false,
688
- "type": "option"
689
- },
690
- "to": {
691
- "description": "Specify the recipient address.",
692
- "name": "to",
693
- "required": true,
694
- "hasDynamicHelp": false,
695
- "multiple": false,
696
- "type": "option"
697
- },
698
- "amount": {
699
- "description": "Amount to mint, in tokens.",
700
- "name": "amount",
701
- "required": true,
702
- "hasDynamicHelp": false,
703
- "multiple": false,
704
- "type": "option"
705
- }
706
- },
707
- "hasDynamicHelp": false,
708
- "hiddenAliases": [],
709
- "id": "mint-to",
710
- "pluginAlias": "@lightprotocol/zk-compression-cli",
711
- "pluginName": "@lightprotocol/zk-compression-cli",
712
- "pluginType": "core",
713
- "strict": true,
714
- "summary": "Mint tokens to an account.",
715
- "enableJsonFlag": false,
716
- "isESM": false,
717
- "relativePath": [
718
- "dist",
719
- "commands",
720
- "mint-to",
721
- "index.js"
722
- ]
723
- },
724
724
  "test-validator": {
725
725
  "aliases": [],
726
726
  "args": {},
@@ -748,6 +748,29 @@
748
748
  "allowNo": false,
749
749
  "type": "boolean"
750
750
  },
751
+ "forester": {
752
+ "description": "Start the forester service for auto-compression of compressible accounts.",
753
+ "name": "forester",
754
+ "allowNo": false,
755
+ "type": "boolean"
756
+ },
757
+ "forester-port": {
758
+ "description": "Port for the forester API server.",
759
+ "name": "forester-port",
760
+ "required": false,
761
+ "default": 8080,
762
+ "hasDynamicHelp": false,
763
+ "multiple": false,
764
+ "type": "option"
765
+ },
766
+ "light-pda-program": {
767
+ "description": "Light PDA programs to track. Format: 'program_id:discriminator_base58'. Can be specified multiple times.",
768
+ "name": "light-pda-program",
769
+ "required": false,
770
+ "hasDynamicHelp": false,
771
+ "multiple": true,
772
+ "type": "option"
773
+ },
751
774
  "skip-system-accounts": {
752
775
  "description": "Runs a test validator without initialized light system accounts.",
753
776
  "name": "skip-system-accounts",
@@ -899,6 +922,21 @@
899
922
  "name": "skip-reset",
900
923
  "allowNo": false,
901
924
  "type": "boolean"
925
+ },
926
+ "use-surfpool": {
927
+ "description": "Use surfpool instead of solana-test-validator (default). Pass --no-use-surfpool to use solana-test-validator.",
928
+ "name": "use-surfpool",
929
+ "allowNo": true,
930
+ "type": "boolean"
931
+ },
932
+ "account-dir": {
933
+ "description": "Additional directory containing account JSON files to preload. Can be specified multiple times.",
934
+ "name": "account-dir",
935
+ "required": false,
936
+ "summary": "Usage: --account-dir <path/to/accounts/>",
937
+ "hasDynamicHelp": false,
938
+ "multiple": true,
939
+ "type": "option"
902
940
  }
903
941
  },
904
942
  "hasDynamicHelp": false,
@@ -918,5 +956,5 @@
918
956
  ]
919
957
  }
920
958
  },
921
- "version": "0.28.0-beta.5"
959
+ "version": "0.28.0-beta.6"
922
960
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightprotocol/zk-compression-cli",
3
- "version": "0.28.0-beta.5",
3
+ "version": "0.28.0-beta.6",
4
4
  "description": "ZK Compression: Secure Scaling on Solana",
5
5
  "maintainers": [
6
6
  {
@@ -49,8 +49,8 @@
49
49
  "tweetnacl": "^1.0.3",
50
50
  "wait-on": "^7.2.0",
51
51
  "which": "^5.0.0",
52
- "@lightprotocol/compressed-token": "0.23.0-beta.5",
53
- "@lightprotocol/stateless.js": "0.23.0-beta.5"
52
+ "@lightprotocol/compressed-token": "0.23.0-beta.6",
53
+ "@lightprotocol/stateless.js": "0.23.0-beta.6"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@eslint/js": "9.36.0",
@@ -100,8 +100,9 @@
100
100
  "scripts": {
101
101
  "postinstall": "[ -d ./bin ] && find ./bin -type f -exec chmod +x {} + || echo 'No bin directory found, skipping chmod'",
102
102
  "sync-prover-version": "./scripts/syncProverVersion.sh",
103
- "build": "shx rm -rf dist && pnpm sync-prover-version && pnpm tsc -p tsconfig.json && pnpm tsc -p tsconfig.test.json",
104
- "build-release": "shx rm -rf dist && pnpm sync-prover-version && pnpm tsc -p tsconfig.json && pnpm tsc -p tsconfig.test.json",
103
+ "sync-photon-version": "./scripts/syncPhotonVersion.sh",
104
+ "build": "shx rm -rf dist && pnpm sync-prover-version && pnpm sync-photon-version && pnpm tsc -p tsconfig.json && pnpm tsc -p tsconfig.test.json",
105
+ "build-release": "shx rm -rf dist && pnpm sync-prover-version && pnpm sync-photon-version && pnpm tsc -p tsconfig.json && pnpm tsc -p tsconfig.test.json",
105
106
  "format": "pnpm prettier --write \"src/**/*.{ts,js}\" \"test/**/*.{ts,js}\" -w",
106
107
  "format:check": "pnpm prettier \"src/**/*{ts,js}\" \"test/**/*.{ts,js}\" --check",
107
108
  "lint": "eslint .",