naracli 1.0.17 → 1.0.22

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.
Files changed (41) hide show
  1. package/README.md +101 -114
  2. package/bin/nara-cli.ts +0 -20
  3. package/dist/nara-cli.mjs +49930 -2222
  4. package/index.ts +10 -58
  5. package/package.json +7 -6
  6. package/src/cli/commands/quest.ts +8 -7
  7. package/src/cli/commands/skills.ts +491 -0
  8. package/src/cli/commands/skillsInstall.ts +793 -0
  9. package/src/cli/commands/wallet.ts +13 -114
  10. package/src/cli/commands/zkid.ts +410 -0
  11. package/src/cli/index.ts +215 -9
  12. package/src/cli/prompts/searchMultiselect.ts +297 -0
  13. package/src/cli/types.ts +0 -138
  14. package/src/cli/utils/transaction.ts +1 -1
  15. package/src/cli/utils/validation.ts +0 -40
  16. package/src/cli/utils/wallet.ts +3 -1
  17. package/src/tests/helpers.ts +78 -0
  18. package/src/tests/skills.e2e.test.ts +126 -0
  19. package/src/tests/skills.test.ts +192 -0
  20. package/src/tests/test_skill.md +18 -0
  21. package/src/tests/zkid.e2e.test.ts +128 -0
  22. package/src/tests/zkid.test.ts +153 -0
  23. package/src/types/snarkjs.d.ts +4 -1
  24. package/dist/quest/nara_quest.json +0 -534
  25. package/dist/zk/answer_proof.wasm +0 -0
  26. package/dist/zk/answer_proof_final.zkey +0 -0
  27. package/src/cli/commands/config.ts +0 -125
  28. package/src/cli/commands/migrate.ts +0 -270
  29. package/src/cli/commands/pool.ts +0 -364
  30. package/src/cli/commands/swap.ts +0 -349
  31. package/src/cli/quest/nara_quest.json +0 -534
  32. package/src/cli/quest/nara_quest_types.ts +0 -540
  33. package/src/cli/zk/answer_proof.wasm +0 -0
  34. package/src/cli/zk/answer_proof_final.zkey +0 -0
  35. package/src/client.ts +0 -96
  36. package/src/config.ts +0 -132
  37. package/src/constants.ts +0 -35
  38. package/src/migrate.ts +0 -222
  39. package/src/pool.ts +0 -259
  40. package/src/quest.ts +0 -387
  41. package/src/swap.ts +0 -608
@@ -21,8 +21,9 @@ import { join } from "node:path";
21
21
  import { homedir } from "node:os";
22
22
  import { mkdir, writeFile, access } from "node:fs/promises";
23
23
  import bs58 from "bs58";
24
- import { NaraSDK } from "../../client";
25
- import { DEFAULT_WALLET_PATH as _DEFAULT_WALLET_PATH } from "../../constants";
24
+ import { NaraSDK } from "nara-sdk";
25
+
26
+ const _DEFAULT_WALLET_PATH = process.env.WALLET_PATH || "~/.config/nara/id.json";
26
27
  import { loadWallet, getRpcUrl } from "../utils/wallet";
27
28
  import { validatePublicKey, validatePositiveNumber } from "../utils/validation";
28
29
  import {
@@ -85,108 +86,6 @@ export function registerWalletCommands(program: Command): void {
85
86
  }
86
87
  });
87
88
 
88
- // wallet address
89
- wallet
90
- .command("address")
91
- .description("Show wallet address")
92
- .action(async (options: GlobalOptions) => {
93
- try {
94
- await handleWalletAddress(options);
95
- } catch (error: any) {
96
- printError(error.message);
97
- process.exit(1);
98
- }
99
- });
100
-
101
- // wallet balance
102
- wallet
103
- .command("balance")
104
- .description("Check NSO balance")
105
- .argument("[address]", "Wallet address (optional, defaults to current wallet)")
106
- .action(async (address: string | undefined, options: WalletBalanceOptions) => {
107
- try {
108
- await handleWalletBalance(address, options);
109
- } catch (error: any) {
110
- printError(error.message);
111
- process.exit(1);
112
- }
113
- });
114
-
115
- // wallet token-balance
116
- wallet
117
- .command("token-balance <token-address>")
118
- .description("Check token balance")
119
- .option("--owner <address>", "Owner address (optional, defaults to current wallet)")
120
- .action(
121
- async (
122
- tokenAddress: string,
123
- options: Omit<TokenBalanceOptions, "tokenAddress">
124
- ) => {
125
- try {
126
- await handleTokenBalance(tokenAddress, options);
127
- } catch (error: any) {
128
- printError(error.message);
129
- process.exit(1);
130
- }
131
- }
132
- );
133
-
134
- // wallet tx-status
135
- wallet
136
- .command("tx-status <signature>")
137
- .description("Check transaction status")
138
- .action(
139
- async (signature: string, options: Omit<TxStatusOptions, "signature">) => {
140
- try {
141
- await handleTxStatus(signature, options);
142
- } catch (error: any) {
143
- printError(error.message);
144
- process.exit(1);
145
- }
146
- }
147
- );
148
-
149
- // wallet transfer
150
- wallet
151
- .command("transfer <to> <amount>")
152
- .description("Transfer NSO to another wallet")
153
- .option("-e, --export-tx", "Export unsigned transaction", false)
154
- .action(
155
- async (
156
- to: string,
157
- amount: string,
158
- options: Omit<TransferSolOptions, "to" | "amount">
159
- ) => {
160
- try {
161
- await handleTransferSol(to, amount, options);
162
- } catch (error: any) {
163
- printError(error.message);
164
- process.exit(1);
165
- }
166
- }
167
- );
168
-
169
- // wallet transfer-token
170
- wallet
171
- .command("transfer-token <token-address> <to> <amount>")
172
- .description("Transfer tokens to another wallet")
173
- .option("--decimals <number>", "Token decimals", "6")
174
- .option("-e, --export-tx", "Export unsigned transaction", false)
175
- .action(
176
- async (
177
- tokenAddress: string,
178
- to: string,
179
- amount: string,
180
- options: Omit<TransferTokenOptions, "tokenAddress" | "to" | "amount">
181
- ) => {
182
- try {
183
- await handleTransferToken(tokenAddress, to, amount, options);
184
- } catch (error: any) {
185
- printError(error.message);
186
- process.exit(1);
187
- }
188
- }
189
- );
190
89
  }
191
90
 
192
91
  /**
@@ -194,7 +93,7 @@ export function registerWalletCommands(program: Command): void {
194
93
  * @param address Wallet address
195
94
  * @param options Command options
196
95
  */
197
- async function handleWalletBalance(
96
+ export async function handleWalletBalance(
198
97
  address: string | undefined,
199
98
  options: WalletBalanceOptions
200
99
  ): Promise<void> {
@@ -236,7 +135,7 @@ async function handleWalletBalance(
236
135
  console.log(JSON.stringify(output, null, 2));
237
136
  } else {
238
137
  console.log(`\nWallet: ${pubkey.toBase58()}`);
239
- console.log(`Balance: ${balanceSOL.toFixed(4)} NSO (${balance.toLocaleString()} lamports)`);
138
+ console.log(`Balance: ${balanceSOL.toFixed(4)} NARA (${balance.toLocaleString()} lamports)`);
240
139
  }
241
140
  }
242
141
 
@@ -245,7 +144,7 @@ async function handleWalletBalance(
245
144
  * @param tokenAddress Token address
246
145
  * @param options Command options
247
146
  */
248
- async function handleTokenBalance(
147
+ export async function handleTokenBalance(
249
148
  tokenAddress: string,
250
149
  options: Omit<TokenBalanceOptions, "tokenAddress">
251
150
  ): Promise<void> {
@@ -329,7 +228,7 @@ async function handleTokenBalance(
329
228
  * @param signature Transaction signature
330
229
  * @param options Command options
331
230
  */
332
- async function handleTxStatus(
231
+ export async function handleTxStatus(
333
232
  signature: string,
334
233
  options: Omit<TxStatusOptions, "signature">
335
234
  ): Promise<void> {
@@ -399,7 +298,7 @@ async function handleTxStatus(
399
298
  console.log(`Time: ${date.toISOString()}`);
400
299
  }
401
300
  if (output.fee) {
402
- console.log(`Fee: ${output.fee / LAMPORTS_PER_SOL} NSO`);
301
+ console.log(`Fee: ${output.fee / LAMPORTS_PER_SOL} NARA`);
403
302
  }
404
303
  if (output.error) {
405
304
  console.log(`Error: ${JSON.stringify(output.error)}`);
@@ -416,7 +315,7 @@ async function handleTxStatus(
416
315
  * @param amount Amount in SOL
417
316
  * @param options Command options
418
317
  */
419
- async function handleTransferSol(
318
+ export async function handleTransferSol(
420
319
  to: string,
421
320
  amount: string,
422
321
  options: Omit<TransferSolOptions, "to" | "amount">
@@ -434,7 +333,7 @@ async function handleTransferSol(
434
333
  const lamports = Math.floor(amountSOL * LAMPORTS_PER_SOL);
435
334
 
436
335
  printInfo(`To: ${recipient.toBase58()}`);
437
- printInfo(`Amount: ${amountSOL} NSO`);
336
+ printInfo(`Amount: ${amountSOL} NARA`);
438
337
 
439
338
  // Initialize SDK
440
339
  const sdk = new NaraSDK({
@@ -482,7 +381,7 @@ async function handleTransferSol(
482
381
  console.log(`\nTransfer Details:`);
483
382
  console.log(` From: ${wallet.publicKey.toBase58()}`);
484
383
  console.log(` To: ${recipient.toBase58()}`);
485
- console.log(` Amount: ${amountSOL} NSO`);
384
+ console.log(` Amount: ${amountSOL} NARA`);
486
385
  printTransactionResult(txResult, false);
487
386
  }
488
387
  }
@@ -494,7 +393,7 @@ async function handleTransferSol(
494
393
  * @param amount Amount in tokens
495
394
  * @param options Command options
496
395
  */
497
- async function handleTransferToken(
396
+ export async function handleTransferToken(
498
397
  tokenAddress: string,
499
398
  to: string,
500
399
  amount: string,
@@ -709,7 +608,7 @@ async function handleWalletImport(options: {
709
608
  * Handle wallet address command
710
609
  * @param options Command options
711
610
  */
712
- async function handleWalletAddress(options: GlobalOptions): Promise<void> {
611
+ export async function handleWalletAddress(options: GlobalOptions): Promise<void> {
713
612
  const wallet = await loadWallet(options.wallet);
714
613
 
715
614
  if (options.json) {
@@ -0,0 +1,410 @@
1
+ /**
2
+ * ZK ID commands - interact with nara-zk anonymous identity protocol
3
+ */
4
+
5
+ import { Command } from "commander";
6
+ import { Connection, PublicKey } from "@solana/web3.js";
7
+ import { loadWallet, getRpcUrl } from "../utils/wallet";
8
+ import {
9
+ printError,
10
+ printInfo,
11
+ printSuccess,
12
+ printWarning,
13
+ formatOutput,
14
+ } from "../utils/output";
15
+ import type { GlobalOptions } from "../types";
16
+ import {
17
+ createZkId,
18
+ getZkIdInfo,
19
+ deposit,
20
+ scanClaimableDeposits,
21
+ withdraw,
22
+ transferZkIdByCommitment,
23
+ deriveIdSecret,
24
+ computeIdCommitment,
25
+ isValidRecipient,
26
+ generateValidRecipient,
27
+ ZKID_DENOMINATIONS,
28
+ } from "nara-sdk";
29
+ import BN from "bn.js";
30
+
31
+ // ─── Denomination helpers ────────────────────────────────────────
32
+
33
+ const VALID_AMOUNTS = [1, 10, 100, 1000, 10000, 100000] as const;
34
+ type ValidAmount = (typeof VALID_AMOUNTS)[number];
35
+
36
+ const DENOM_MAP: Record<ValidAmount, BN> = {
37
+ 1: ZKID_DENOMINATIONS.NARA_1,
38
+ 10: ZKID_DENOMINATIONS.NARA_10,
39
+ 100: ZKID_DENOMINATIONS.NARA_100,
40
+ 1000: ZKID_DENOMINATIONS.NARA_1000,
41
+ 10000: ZKID_DENOMINATIONS.NARA_10000,
42
+ 100000: ZKID_DENOMINATIONS.NARA_100000,
43
+ };
44
+
45
+ function parseDenomination(amount: string): BN {
46
+ const n = parseInt(amount, 10);
47
+ if (!VALID_AMOUNTS.includes(n as ValidAmount)) {
48
+ throw new Error(
49
+ `Invalid amount "${amount}". Valid denominations: ${VALID_AMOUNTS.join(", ")} NARA`
50
+ );
51
+ }
52
+ return DENOM_MAP[n as ValidAmount];
53
+ }
54
+
55
+ // ─── Command handlers ────────────────────────────────────────────
56
+
57
+ async function handleZkIdCreate(name: string, options: GlobalOptions) {
58
+ const rpcUrl = getRpcUrl(options.rpcUrl);
59
+ const connection = new Connection(rpcUrl, "confirmed");
60
+ const wallet = await loadWallet(options.wallet);
61
+
62
+ if (!options.json) printInfo(`Deriving idSecret for "${name}"...`);
63
+ const idSecret = await deriveIdSecret(wallet, name);
64
+
65
+ // Check if already exists
66
+ const existing = await getZkIdInfo(connection, name);
67
+ if (existing) {
68
+ printWarning(`ZK ID "${name}" already exists (depositCount: ${existing.depositCount})`);
69
+ process.exit(0);
70
+ }
71
+
72
+ if (!options.json) printInfo(`Registering ZK ID "${name}"...`);
73
+ const signature = await createZkId(connection, wallet, name, idSecret);
74
+ if (!options.json) printSuccess(`ZK ID "${name}" registered!`);
75
+
76
+ if (options.json) {
77
+ formatOutput({ name, signature }, true);
78
+ } else {
79
+ console.log(` Transaction: ${signature}`);
80
+ }
81
+ }
82
+
83
+ async function handleZkIdInfo(name: string, options: GlobalOptions) {
84
+ const rpcUrl = getRpcUrl(options.rpcUrl);
85
+ const connection = new Connection(rpcUrl, "confirmed");
86
+
87
+ const info = await getZkIdInfo(connection, name);
88
+ if (!info) {
89
+ if (options.json) {
90
+ formatOutput({ name, exists: false }, true);
91
+ } else {
92
+ printWarning(`ZK ID "${name}" does not exist`);
93
+ }
94
+ return;
95
+ }
96
+
97
+ const data = {
98
+ name,
99
+ depositCount: info.depositCount,
100
+ commitmentStartIndex: info.commitmentStartIndex,
101
+ idCommitment: Buffer.from(info.idCommitment).toString("hex"),
102
+ };
103
+
104
+ if (options.json) {
105
+ formatOutput(data, true);
106
+ } else {
107
+ console.log("");
108
+ console.log(` Name: ${name}`);
109
+ console.log(` Deposit count: ${info.depositCount}`);
110
+ console.log(` Commitment start index: ${info.commitmentStartIndex}`);
111
+ console.log(` ID commitment: ${data.idCommitment}`);
112
+ console.log("");
113
+ }
114
+ }
115
+
116
+ async function handleZkIdDeposit(
117
+ name: string,
118
+ amount: string,
119
+ options: GlobalOptions
120
+ ) {
121
+ const denomination = parseDenomination(amount);
122
+ const rpcUrl = getRpcUrl(options.rpcUrl);
123
+ const connection = new Connection(rpcUrl, "confirmed");
124
+ const wallet = await loadWallet(options.wallet);
125
+
126
+ if (!options.json) printInfo(`Depositing ${amount} NARA into ZK ID "${name}"...`);
127
+ const signature = await deposit(connection, wallet, name, denomination);
128
+ if (!options.json) printSuccess("Deposit complete!");
129
+
130
+ if (options.json) {
131
+ formatOutput({ name, amount: parseInt(amount), signature }, true);
132
+ } else {
133
+ console.log(` Transaction: ${signature}`);
134
+ }
135
+ }
136
+
137
+ async function handleZkIdScan(name: string, options: GlobalOptions) {
138
+ const rpcUrl = getRpcUrl(options.rpcUrl);
139
+ const connection = new Connection(rpcUrl, "confirmed");
140
+ const wallet = await loadWallet(options.wallet);
141
+
142
+ if (!options.json) printInfo(`Scanning claimable deposits for "${name}"...`);
143
+ const idSecret = await deriveIdSecret(wallet, name);
144
+ const claimable = await scanClaimableDeposits(connection, name, idSecret);
145
+
146
+ if (options.json) {
147
+ formatOutput(
148
+ {
149
+ name,
150
+ count: claimable.length,
151
+ deposits: claimable.map((d) => ({
152
+ leafIndex: d.leafIndex.toString(),
153
+ depositIndex: d.depositIndex,
154
+ denomination: d.denomination.toString(),
155
+ nara: Number(d.denomination) / 1e9,
156
+ })),
157
+ },
158
+ true
159
+ );
160
+ return;
161
+ }
162
+
163
+ if (claimable.length === 0) {
164
+ printInfo("No claimable deposits found");
165
+ return;
166
+ }
167
+
168
+ console.log(`\n Found ${claimable.length} claimable deposit(s):`);
169
+ claimable.forEach((d, i) => {
170
+ const nara = Number(d.denomination) / 1e9;
171
+ console.log(
172
+ ` [${i}] ${nara} NARA leafIndex=${d.leafIndex} depositIndex=${d.depositIndex}`
173
+ );
174
+ });
175
+ console.log("");
176
+ }
177
+
178
+ async function handleZkIdWithdraw(
179
+ name: string,
180
+ options: GlobalOptions & { recipient?: string }
181
+ ) {
182
+ // Validate recipient early (before any network calls)
183
+ let recipient: PublicKey | undefined;
184
+ if (options.recipient) {
185
+ try {
186
+ recipient = new PublicKey(options.recipient);
187
+ } catch {
188
+ throw new Error(`Invalid recipient address: ${options.recipient}`);
189
+ }
190
+ if (!isValidRecipient(recipient)) {
191
+ throw new Error(
192
+ "Recipient address is not a valid BN254 field element. " +
193
+ "Use `zkid withdraw` without --recipient to auto-generate a valid address."
194
+ );
195
+ }
196
+ }
197
+
198
+ const rpcUrl = getRpcUrl(options.rpcUrl);
199
+ const connection = new Connection(rpcUrl, "confirmed");
200
+ const wallet = await loadWallet(options.wallet);
201
+
202
+ if (!options.json) printInfo(`Scanning deposits for "${name}"...`);
203
+ const idSecret = await deriveIdSecret(wallet, name);
204
+ const claimable = await scanClaimableDeposits(connection, name, idSecret);
205
+
206
+ if (claimable.length === 0) {
207
+ printWarning("No claimable deposits found");
208
+ process.exit(0);
209
+ }
210
+
211
+ // Auto-generate recipient if not provided
212
+ if (!recipient) {
213
+ const kp = generateValidRecipient();
214
+ recipient = kp.publicKey;
215
+ if (!options.json) printInfo(`Auto-generated recipient: ${recipient.toBase58()}`);
216
+ }
217
+
218
+ const depositInfo = claimable[0]!;
219
+ const nara = Number(depositInfo.denomination) / 1e9;
220
+ if (!options.json) printInfo(`Withdrawing ${nara} NARA (depositIndex=${depositInfo.depositIndex})...`);
221
+
222
+ const signature = await withdraw(
223
+ connection,
224
+ wallet,
225
+ name,
226
+ idSecret,
227
+ depositInfo,
228
+ recipient
229
+ );
230
+ if (!options.json) printSuccess("Withdrawal complete!");
231
+
232
+ if (options.json) {
233
+ formatOutput(
234
+ {
235
+ name,
236
+ recipient: recipient.toBase58(),
237
+ nara,
238
+ depositIndex: depositInfo.depositIndex,
239
+ signature,
240
+ },
241
+ true
242
+ );
243
+ } else {
244
+ console.log(` Recipient: ${recipient.toBase58()}`);
245
+ console.log(` Transaction: ${signature}`);
246
+ }
247
+ }
248
+
249
+ async function handleZkIdCommitment(name: string, options: GlobalOptions) {
250
+ const wallet = await loadWallet(options.wallet);
251
+ const commitment = await computeIdCommitment(wallet, name);
252
+
253
+ if (options.json) {
254
+ formatOutput({ name, idCommitment: commitment }, true);
255
+ } else {
256
+ console.log("");
257
+ console.log(` ZK ID name: ${name}`);
258
+ console.log(` ID commitment: ${commitment}`);
259
+ console.log("");
260
+ printInfo("Share this commitment with the current owner to transfer the ZK ID.");
261
+ }
262
+ }
263
+
264
+ async function handleZkIdTransfer(
265
+ name: string,
266
+ newIdCommitmentHex: string,
267
+ options: GlobalOptions
268
+ ) {
269
+ // Parse hex commitment
270
+ let newIdCommitment: bigint;
271
+ try {
272
+ if (!/^[0-9a-fA-F]{64}$/.test(newIdCommitmentHex)) {
273
+ throw new Error("must be a 64-char hex string");
274
+ }
275
+ newIdCommitment = BigInt("0x" + newIdCommitmentHex);
276
+ } catch (e: any) {
277
+ throw new Error(`Invalid id-commitment: ${e.message}`);
278
+ }
279
+
280
+ const rpcUrl = getRpcUrl(options.rpcUrl);
281
+ const connection = new Connection(rpcUrl, "confirmed");
282
+ const wallet = await loadWallet(options.wallet);
283
+
284
+ if (!options.json) printInfo(`Generating ownership proof for "${name}"...`);
285
+ const currentIdSecret = await deriveIdSecret(wallet, name);
286
+
287
+ if (!options.json) printInfo("Transferring ZK ID ownership...");
288
+ const signature = await transferZkIdByCommitment(
289
+ connection,
290
+ wallet,
291
+ name,
292
+ currentIdSecret,
293
+ newIdCommitment
294
+ );
295
+ if (!options.json) printSuccess("ZK ID ownership transferred!");
296
+
297
+ if (options.json) {
298
+ formatOutput({ name, newIdCommitment: newIdCommitmentHex, signature }, true);
299
+ } else {
300
+ console.log(` New commitment: ${newIdCommitmentHex}`);
301
+ console.log(` Transaction: ${signature}`);
302
+ }
303
+ }
304
+
305
+ // ─── Register commands ───────────────────────────────────────────
306
+
307
+ export function registerZkIdCommands(program: Command): void {
308
+ const zkid = program
309
+ .command("zkid")
310
+ .description("ZK ID commands (anonymous identity protocol)");
311
+
312
+ // zkid create
313
+ zkid
314
+ .command("create <name>")
315
+ .description("Register a new ZK ID on-chain")
316
+ .action(async (name: string, _opts: any, cmd: Command) => {
317
+ try {
318
+ const globalOpts = cmd.optsWithGlobals() as GlobalOptions;
319
+ await handleZkIdCreate(name, globalOpts);
320
+ } catch (error: any) {
321
+ printError(error.message);
322
+ process.exit(1);
323
+ }
324
+ });
325
+
326
+ // zkid info
327
+ zkid
328
+ .command("info <name>")
329
+ .description("Get ZK ID account info (read-only)")
330
+ .action(async (name: string, _opts: any, cmd: Command) => {
331
+ try {
332
+ const globalOpts = cmd.optsWithGlobals() as GlobalOptions;
333
+ await handleZkIdInfo(name, globalOpts);
334
+ } catch (error: any) {
335
+ printError(error.message);
336
+ process.exit(1);
337
+ }
338
+ });
339
+
340
+ // zkid deposit
341
+ zkid
342
+ .command("deposit <name> <amount>")
343
+ .description(`Deposit fixed-denomination NARA into a ZK ID (valid: ${VALID_AMOUNTS.join(", ")})`)
344
+ .action(async (name: string, amount: string, _opts: any, cmd: Command) => {
345
+ try {
346
+ const globalOpts = cmd.optsWithGlobals() as GlobalOptions;
347
+ await handleZkIdDeposit(name, amount, globalOpts);
348
+ } catch (error: any) {
349
+ printError(error.message);
350
+ process.exit(1);
351
+ }
352
+ });
353
+
354
+ // zkid scan
355
+ zkid
356
+ .command("scan <name>")
357
+ .description("Scan claimable deposits for a ZK ID (requires wallet)")
358
+ .action(async (name: string, _opts: any, cmd: Command) => {
359
+ try {
360
+ const globalOpts = cmd.optsWithGlobals() as GlobalOptions;
361
+ await handleZkIdScan(name, globalOpts);
362
+ } catch (error: any) {
363
+ printError(error.message);
364
+ process.exit(1);
365
+ }
366
+ });
367
+
368
+ // zkid withdraw
369
+ zkid
370
+ .command("withdraw <name>")
371
+ .description("Anonymously withdraw the first claimable deposit")
372
+ .option("--recipient <address>", "Recipient address (must be a valid BN254 field element)")
373
+ .action(async (name: string, opts: { recipient?: string }, cmd: Command) => {
374
+ try {
375
+ const globalOpts = cmd.optsWithGlobals() as GlobalOptions;
376
+ await handleZkIdWithdraw(name, { ...globalOpts, ...opts });
377
+ } catch (error: any) {
378
+ printError(error.message);
379
+ process.exit(1);
380
+ }
381
+ });
382
+
383
+ // zkid id-commitment
384
+ zkid
385
+ .command("id-commitment <name>")
386
+ .description("Derive and display your idCommitment (share with current owner to receive transfer)")
387
+ .action(async (name: string, _opts: any, cmd: Command) => {
388
+ try {
389
+ const globalOpts = cmd.optsWithGlobals() as GlobalOptions;
390
+ await handleZkIdCommitment(name, globalOpts);
391
+ } catch (error: any) {
392
+ printError(error.message);
393
+ process.exit(1);
394
+ }
395
+ });
396
+
397
+ // zkid transfer
398
+ zkid
399
+ .command("transfer <name> <new-id-commitment>")
400
+ .description("Transfer ZK ID ownership to the holder of a new idCommitment")
401
+ .action(async (name: string, newIdCommitment: string, _opts: any, cmd: Command) => {
402
+ try {
403
+ const globalOpts = cmd.optsWithGlobals() as GlobalOptions;
404
+ await handleZkIdTransfer(name, newIdCommitment, globalOpts);
405
+ } catch (error: any) {
406
+ printError(error.message);
407
+ process.exit(1);
408
+ }
409
+ });
410
+ }