@tokamak-private-dapps/private-state-cli 2.1.1 → 2.2.0

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,51 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 2.2.0 - 2026-05-18
6
+
7
+ - Added `install --read-only` for channel-state read commands and commands that do not depend on channel state. This
8
+ mode installs only the bridge deployment, bridge ABI manifest, DApp deployment, and storage layout artifacts.
9
+ - Kept default `install` as full mode for proof-backed and channel-mutating commands, and made deployment artifact
10
+ validation mode-aware before command execution.
11
+ - Extended `help doctor` with per-command availability so read-only installs clearly report which commands are usable
12
+ and which commands still require full install.
13
+ - Added private-state CLI E2E coverage for the read-only install mode before the full install flow.
14
+
15
+ ## 2.1.2 - 2026-05-15
16
+
17
+ - Fixed wallet lifecycle recovery log lookups so account-specific registration and exit event scans
18
+ use the same chunked `eth_getLogs` path as the rest of RPC workspace recovery.
19
+ - Removed synthetic wallet lifecycle epoch fallback creation; wallet recovery now requires
20
+ lifecycle registration events to be found in RPC log history instead of fabricating epochs from
21
+ current registration state.
22
+ - Fixed RPC log request pacing so every `eth_getLogs` call passes through a shared async limiter
23
+ instead of relying on a race-prone timestamp throttle during concurrent scans.
24
+ - Added progress output for the wallet lifecycle registered/exited event scans that run before
25
+ note-delivery recovery in `wallet recover-workspace`.
26
+ - Added `set rpc` for per-network RPC configuration with built-in `eth_getLogs` scan-limit tables
27
+ for Ankr, Chainstack, Chainnodes, QuickNode, and Alchemy, using 90% of the provider reference
28
+ request-rate values.
29
+ - Simplified note-mutating command post-processing so accepted note transactions wait for the
30
+ receipt block, then refresh channel and wallet workspaces from their recovery indexes instead of
31
+ manually applying note lifecycle metadata.
32
+ - Extended wallet recovery to persist public creation/spend linkage from commitment and
33
+ nullifier storage observations so raw evidence export can package stored metadata without
34
+ running its own log scan.
35
+ - Fixed raw evidence spend linkage so nullifier observation transactions are used as the spend
36
+ transition references and included with their transaction, receipt, and event evidence.
37
+ - Redesigned the bundled investigator GUI around purpose-first disclosure requests, an interactive
38
+ SVG note-linkage graph, node detail overlays, and Markdown ASCII-art linkage report export.
39
+ - Updated private-state documentation to reflect `set rpc` as the only CLI RPC configuration path
40
+ for ordinary bridge-facing and wallet commands.
41
+ - Split the CLI entrypoint into command dispatch modules and moved the shared runtime implementation
42
+ under `lib/runtime.mjs`; the published package now includes the `commands/` modules.
43
+ - Removed the `channel get-meta` workspace mirror lookup fallback so contract lookup errors surface
44
+ directly instead of being hidden behind `null` metadata.
45
+ - Changed `channel join` to derive the wallet lifecycle epoch from the accepted join receipt instead
46
+ of rescanning full account registration and exit history.
47
+ - Changed investigator numeric block filters to reject invalid values instead of silently treating
48
+ them as absent filters.
49
+
5
50
  ## 2.1.1 - 2026-05-14
6
51
 
7
52
  - Changed `channel recover-workspace --from-genesis` to move any existing local channel workspace
package/README.md CHANGED
@@ -4,16 +4,16 @@ Command-line client for the Tokamak private-state DApp.
4
4
 
5
5
  The full private-state DApp documentation is published with the repository:
6
6
 
7
- - https://github.com/tokamak-network/Tokamak-zk-EVM-contracts/tree/main/packages/apps/private-state/docs
7
+ - https://github.com/tokamak-network/Tokamak-zk-EVM-contracts/tree/main/docs/dapps/private-state
8
8
 
9
- ## Terminology And CEX Boundary
9
+ ## Terminology And Exchange Boundary
10
10
 
11
11
  This npm README uses the same terminology as the repository README:
12
12
 
13
13
  - `Tokamak Private App Channels`: Ethereum-settled, validity-proven execution domains for bridge-coupled DApps.
14
14
  - `private-state DApp`: the current reference DApp that programs confidential application state inside a channel.
15
15
  - `canonical Tokamak Network Token`: the L1 asset whose custody remains anchored on Ethereum.
16
- - `self-custody L1 wallet`: a user-controlled L1 account, not a centralized-exchange deposit address.
16
+ - `self-custody L1 wallet`: a user-controlled L1 account, not an exchange deposit address.
17
17
  - `L1-transparent bridge edge`: public bridge deposit and withdrawal transactions involving the canonical token.
18
18
  - `channel-local accounting balance`: liquid application balance inside a channel before or after note use.
19
19
  - `private-state note`: a channel-local application note, not an exchange-supported token or deposit asset.
@@ -22,10 +22,20 @@ This npm README uses the same terminology as the repository README:
22
22
  - `viewing key`: the note-receive private key used to decrypt note-delivery events for the registered note-receive public key.
23
23
  - `spending key`: the channel-bound L2 private key used to authorize proof-backed note use.
24
24
 
25
- Tokamak Private App Channels are not a centralized-exchange deposit network. CEX-facing token transfers and bridge
25
+ Tokamak Private App Channels are not an exchange deposit network. Exchange-facing token transfers and bridge
26
26
  entry or exit remain public L1 activity. Internal private-state note counterparty relationships and note provenance are
27
27
  not public by default and are not reconstructed by Tokamak on a user's behalf.
28
28
 
29
+ ## Address And Key-Safety Warnings
30
+
31
+ Do not use an exchange deposit address as a private-state wallet address. Private-state notes are not
32
+ supported exchange assets. Always withdraw TON to a self-custody L1 wallet before using a channel.
33
+
34
+ Bridge deposits and withdrawals are public L1 events. Internal note transfers are private by design and are not
35
+ automatically reconstructible by Tokamak, exchanges, or public observers.
36
+
37
+ This CLI does not send your spending key, wallet secret, or private note plaintext to Tokamak.
38
+
29
39
  ## Tokamak-Operated Mainnet Channels
30
40
 
31
41
  The table below lists private-state mainnet channels directly opened by Tokamak Network. Dates are
@@ -41,8 +51,8 @@ UTC.
41
51
  npm install -g @tokamak-private-dapps/private-state-cli
42
52
  ```
43
53
 
44
- Install the local Tokamak zk-EVM runtime workspace, Groth16 runtime workspace, and public private-state deployment
45
- artifacts:
54
+ Install the full local Tokamak zk-EVM runtime workspace, Groth16 runtime workspace, and public private-state deployment
55
+ artifacts needed by transaction-sending channel commands:
46
56
 
47
57
  ```bash
48
58
  private-state-cli install
@@ -61,6 +71,18 @@ selected Groth16 CLI package version.
61
71
  The Tokamak zk-EVM installer requires the selected CLI package to declare
62
72
  `tokamakZkEvm.compatibleBackendVersion` as a canonical major.minor version matching the selected package version.
63
73
 
74
+ For read-only channel recovery, channel metadata lookup, wallet recovery, wallet metadata lookup, bridge balance
75
+ lookup, bridge deposit, bridge withdrawal, and local helper commands, install only the read-only artifact subset:
76
+
77
+ ```bash
78
+ private-state-cli install --read-only
79
+ ```
80
+
81
+ Read-only install materializes only `bridge.<chainId>.json`, `bridge-abi-manifest.<chainId>.json`,
82
+ `deployment.<chainId>.latest.json`, and `storage-layout.<chainId>.latest.json`. It does not install the Tokamak
83
+ zk-EVM runtime, Groth16 runtime, Groth16 zkey, callable DApp ABI, or DApp registration artifact, so commands that
84
+ create or mutate channel state require a later full `private-state-cli install`.
85
+
64
86
  `install` downloads public deployment artifacts from the configured artifact index. It does not read repository-local
65
87
  `deployment/` outputs by default. Repository development workflows that need local anvil artifacts can opt in explicitly:
66
88
 
@@ -147,8 +169,8 @@ Static warning scope:
147
169
  | `wallet redeem-notes` | L1 submitter, input nullifier, accounting update, root update | Consumes notes | Prior path by which the note was received |
148
170
  | `wallet withdraw-channel` | L1 submitter, registered L2 address, amount, channel id, accounting update | No direct note spend | Prior private-state note path behind the liquid balance |
149
171
 
150
- `account deposit-bridge` and `account withdraw-bridge` also print a centralized-exchange address warning. Do not use a
151
- centralized-exchange controlled address as a self-custody bridge source or as the direct bridge withdrawal target
172
+ `account deposit-bridge` and `account withdraw-bridge` also print an exchange-controlled address warning. Do not use an
173
+ exchange-controlled address as a self-custody bridge source or as the direct bridge withdrawal target
152
174
  unless the user explicitly understands the compliance implications. Prefer a self-custody L1 wallet.
153
175
 
154
176
  Workspace recovery commands use saved recovery indexes by default. If the local channel workspace is missing,
@@ -208,7 +230,7 @@ already exists. The channel leader can build the static mirror files with
208
230
  host. If the existing mirror manifest is unreadable or invalid, the leader can use
209
231
  `channel publish-workspace-mirror --force` to write a full checkpoint without trusting that remote
210
232
  manifest as a delta base. The CLI protocol is documented at
211
- https://github.com/tokamak-network/Tokamak-zk-EVM-contracts/blob/main/packages/apps/private-state/docs/channel-workspace-mirror-protocol.md.
233
+ https://github.com/tokamak-network/Tokamak-zk-EVM-contracts/blob/main/docs/dapps/private-state/channel-workspace-mirror-protocol.md.
212
234
 
213
235
  Back up a local wallet with:
214
236
 
@@ -264,8 +286,11 @@ private-state-cli investigator
264
286
  ```
265
287
 
266
288
  The command prints the bundled investigator HTML path and file URL, then opens the static browser GUI. Load the raw
267
- evidence ZIP in that GUI, apply the requested filters, and export a narrower user-consent disclosure ZIP. The GUI runs
268
- locally in the browser and does not send files over the network.
289
+ evidence ZIP in that GUI, choose the disclosure request type, inspect the interactive note-linkage graph, and export a
290
+ narrower user-consent disclosure ZIP. The graph view renders every matched note as a node and shows creation, spend,
291
+ and local note-to-note linkage edges when the raw bundle contains enough local evidence. The GUI can also export a
292
+ Markdown ASCII-art linkage report with a compact graph section and per-note detail sections. The GUI runs locally in the
293
+ browser and does not send files over the network.
269
294
 
270
295
  The investigator accepts current epoch-aware evidence bundles only. If a bundle was generated by an older CLI, rebuild
271
296
  the wallet workspace with `wallet recover-workspace` and export a new bundle with `wallet get-notes --export-evidence`.
@@ -273,7 +298,7 @@ the wallet workspace with `wallet recover-workspace` and export a new bundle wit
273
298
  Estimate live transaction costs before sending commands with:
274
299
 
275
300
  ```bash
276
- private-state-cli help transaction-fees --network mainnet --rpc-url <RPC_URL>
301
+ private-state-cli help transaction-fees --network mainnet
277
302
  ```
278
303
 
279
304
  `help transaction-fees` uses the measured gas data packaged in `assets/tx-fees.json`, the selected network's live fee data,
@@ -306,9 +331,11 @@ Channel policy warning:
306
331
  a new channel; it does not rewrite the policy of an already-created channel.
307
332
 
308
333
  `private-state-cli help doctor` reports the CLI package version, dependency versions recorded by the last
309
- `private-state-cli install`, selected proof backend runtime versions, current dependency versions through `tokamak-l2js`, and Tokamak zk-EVM runtime
310
- install mode, Docker mode, CUDA runtime metadata, live `nvidia-smi` and Docker GPU probe results, and Groth16
311
- runtime health. The doctor check fails when the Tokamak Docker `useGpus` metadata does not match the live GPU probes.
334
+ `private-state-cli install`, selected proof backend runtime versions when full mode was installed, current dependency
335
+ versions through `tokamak-l2js`, Tokamak zk-EVM runtime install mode, Docker mode, CUDA runtime metadata, live
336
+ `nvidia-smi` and Docker GPU probe results, Groth16 runtime health, deployment artifact readiness, and per-command
337
+ availability. In read-only install mode, proof runtime checks are skipped and proof-backed or channel-mutating
338
+ commands are reported unavailable until full install is completed.
312
339
 
313
340
  Local helper commands:
314
341
 
@@ -366,8 +393,9 @@ note-delivery events and rebuild the user's readable note view. Sharing it gives
366
393
  registered note-receive public key, but not spending authority.
367
394
 
368
395
  The spending key is the channel-bound L2 private key. It authorizes proof-backed use of the wallet identity. Commands
369
- that consume existing notes, such as `wallet transfer-notes` and `wallet redeem-notes`, need both the viewing key and
370
- the spending key because the CLI must first reconstruct the plaintext notes and then prove authorized use of them.
396
+ that create or consume notes, such as `wallet mint-notes`, `wallet transfer-notes`, and `wallet redeem-notes`, need both
397
+ the viewing key and the spending key because the CLI refreshes the readable note workspace after accepted note
398
+ transactions and then proves authorized note use when inputs are consumed.
371
399
 
372
400
  Key recovery is intentionally split. Recreating the viewing key requires the original L1 private key and the same channel
373
401
  context. Recreating the spending key requires the original L1 private key, the same channel context, and the same wallet
@@ -390,11 +418,40 @@ The CLI stores user workspaces under:
390
418
  Wallet backup metadata lives under the channel workspace. Viewing and spending private keys live as separate protected
391
419
  key files under `~/tokamak-private-channels/secrets/<network>/wallets/<wallet>/`.
392
420
 
393
- Bridge-facing commands accept optional `--rpc-url <URL>`. When `--rpc-url` is provided, the CLI stores it in
394
- `~/tokamak-private-channels/secrets/<network>/.env` as `RPC_URL=<URL>` with protected canonical secret permissions.
395
- When `--rpc-url` is omitted, the CLI reads `RPC_URL` from that file. The `anvil` network falls back to
396
- `http://127.0.0.1:8545` when no saved RPC URL exists. Canonical CLI secrets are checked on read: macOS/Linux uses
397
- `0600`, while Windows uses ACL repair and inspection when possible.
421
+ Configure the network RPC endpoint before bridge-facing or wallet recovery commands:
422
+
423
+ ```bash
424
+ private-state-cli set rpc --network mainnet --rpc-url <RPC_URL> --provider alchemy
425
+ ```
426
+
427
+ The CLI writes `~/tokamak-private-channels/workspace/<network>/rpc-config.env` and later commands read `RPC_URL`,
428
+ `LOG_CHUNK_SIZE`, `LOG_REQUESTS_PER_SECOND`, and `RPC_BLOCK_RANGE_CAP` from that file. Built-in provider limits are
429
+ set to 90% of the provider reference values: Ankr `27 calls/s, 3000 blocks`; Chainstack `22.5 calls/s, 100 blocks`;
430
+ Chainnodes `22.5 calls/s, 20000 blocks`; QuickNode `13.5 calls/s, 5 blocks`; Alchemy `7.497 calls/s, 10 blocks`.
431
+ If the provider is not listed, use
432
+ `--log-requests-per-second <N>` and `--block-range-cap <N>` instead of `--provider`.
433
+
434
+ ### Slow Workspace Recovery
435
+
436
+ `channel recover-workspace` and `wallet recover-workspace` scan on-chain logs with `eth_getLogs`. If recovery is
437
+ unexpectedly slow, first check the RPC scan limits saved by `set rpc`. The main speed factors are the provider's
438
+ `eth_getLogs` block range cap and the allowed log request rate. A small block range cap can turn the same channel scan
439
+ into thousands of RPC calls.
440
+
441
+ For example, an Alchemy free-tier-style cap of `10` blocks is much slower for long recovery scans than Ankr's built-in
442
+ `3000` block setting or Chainnodes' built-in `20000` block setting. When recovery is too slow, re-run `set rpc` with a
443
+ provider that supports a larger `eth_getLogs` block range cap, or provide explicit values:
444
+
445
+ ```bash
446
+ private-state-cli set rpc --network mainnet --rpc-url <RPC_URL> --provider ankr
447
+ private-state-cli set rpc --network mainnet --rpc-url <RPC_URL> --log-requests-per-second <N> --block-range-cap <N>
448
+ ```
449
+
450
+ If an RPC provider rejects the configured range or rate during recovery, run `set rpc` again with limits that match the
451
+ provider's documented `eth_getLogs` policy, then retry the recovery command.
452
+
453
+ Canonical CLI secrets are checked on read: macOS/Linux uses `0600`, while Windows uses ACL repair and inspection when
454
+ possible.
398
455
 
399
456
  ## LLM Agent Guidance
400
457
 
@@ -429,8 +486,9 @@ Operating rules:
429
486
  - A viewing key decrypts encrypted note-delivery events for the registered note-receive public key. A spending key is
430
487
  the channel-bound L2 private key used to authorize note use. Do not describe either key as interchangeable with the
431
488
  other.
432
- - The network RPC URL is the endpoint used to read and write chain state. It can be supplied once with `--rpc-url`
433
- on a bridge-facing command, after which the CLI saves it under the selected network.
489
+ - The network RPC URL is the endpoint used to read and write chain state. It must be configured once with
490
+ `private-state-cli set rpc --network <NETWORK> --rpc-url <URL> --provider <PROVIDER>`, or with explicit
491
+ `--log-requests-per-second` and `--block-range-cap` values when the provider is not built in.
434
492
  - A workspace recovery index is the saved block pointer and state-root hash that lets the CLI resume log scanning
435
493
  without replaying the channel from its creation block. If it is missing, explain `--from-genesis` before using it
436
494
  because genesis replay can take much longer.
@@ -438,25 +496,32 @@ Operating rules:
438
496
  recovery can be very slow because it scans channel logs from the creation block. If a channel workspace mirror is
439
497
  available, try mirror-based recovery first, and use RPC genesis replay only when mirror recovery is unavailable or
440
498
  unsuitable.
499
+ - When `channel recover-workspace` or `wallet recover-workspace` is unexpectedly slow, first inspect the RPC provider
500
+ configured by `set rpc`. Explain that recovery speed is dominated by `eth_getLogs` block range cap and log request
501
+ rate. Suggest re-running `set rpc` with a provider that supports a larger block range cap, such as Ankr or Chainnodes
502
+ when appropriate, or with explicit `--log-requests-per-second` and `--block-range-cap` values from the provider's
503
+ documentation.
441
504
  - When a CLI command fails, read the error message and any printed `Try:` hints first. Prefer the corrective action
442
505
  suggested by the CLI before inventing a different recovery sequence.
443
506
  - When the user does not have a network RPC URL yet, explain that they need an Ethereum JSON-RPC endpoint for the
444
- selected network. They can obtain one from an infrastructure provider such as Alchemy, Infura, QuickNode, or from
445
- their own node. Ask the user to create or select the endpoint in that provider's UI, then paste only the endpoint URL
446
- into the CLI command that accepts `--rpc-url`; do not ask for provider account passwords, API dashboards, seed phrases,
447
- private keys, or wallet secrets.
507
+ selected network. They can obtain one from an infrastructure provider such as Alchemy, Ankr, Chainstack, Chainnodes,
508
+ QuickNode, or from their own node. Ask the user to create or select the endpoint in that provider's UI, then paste only
509
+ the endpoint URL into `private-state-cli set rpc`; do not ask for provider account passwords, API dashboards, seed
510
+ phrases, private keys, or wallet secrets.
448
511
  - When a user wants to join a channel, do not jump straight to `channel join`. Walk them through:
449
512
  1. choose the network and channel name
450
513
  2. run `private-state-cli install`
451
514
  3. run `private-state-cli help doctor`
452
515
  4. obtain or confirm a network RPC URL for the selected network
453
- 5. prepare a private key source file locally, without pasting the key into chat
454
- 6. run `account import --account <NAME> --network <NETWORK> --private-key-file <PATH>`
455
- 7. prepare a wallet secret source file locally, for example with `openssl rand -hex 32 > ./wallet-secret.txt`
456
- 8. inspect the channel with `channel get-meta` if it already exists, or create it with `channel create` if the user is
516
+ 5. run `set rpc --network <NETWORK> --rpc-url <URL> --provider <PROVIDER>`, or use explicit scan limits for an
517
+ unlisted provider
518
+ 6. prepare a private key source file locally, without pasting the key into chat
519
+ 7. run `account import --account <NAME> --network <NETWORK> --private-key-file <PATH>`
520
+ 8. prepare a wallet secret source file locally, for example with `openssl rand -hex 32 > ./wallet-secret.txt`
521
+ 9. inspect the channel with `channel get-meta` if it already exists, or create it with `channel create` if the user is
457
522
  the channel creator
458
- 9. explain the immutable policy warning printed by the CLI
459
- 10. run `channel join --channel-name <CHANNEL> --network <NETWORK> --account <ACCOUNT> --wallet-secret-path <PATH> --acknowledge-action-impact`
523
+ 10. explain the immutable policy warning printed by the CLI
524
+ 11. run `channel join --channel-name <CHANNEL> --network <NETWORK> --account <ACCOUNT> --wallet-secret-path <PATH> --acknowledge-action-impact`
460
525
  - Before executing any command for a user that requires an `--acknowledge-*` option, strongly warn the user in plain
461
526
  language about what that acknowledgement means and ask for explicit confirmation. Do not add
462
527
  `--acknowledge-action-impact` or `--acknowledge-full-note-plaintext-export` on the user's behalf until they confirm.
@@ -520,8 +585,9 @@ command.
520
585
 
521
586
  ## Artifacts
522
587
 
523
- Proof-backed commands require installed bridge, DApp, and Groth16 artifacts. Run `private-state-cli install` before
524
- using bridge-facing commands on a new machine.
588
+ Proof-backed and channel-mutating commands require full installed bridge, DApp, and Groth16 artifacts. Run
589
+ `private-state-cli install` before creating, joining, exiting, or mutating channels on a new machine. Channel-state read
590
+ commands and commands unrelated to channel state can run after `private-state-cli install --read-only`.
525
591
 
526
592
  Channel balance commands such as `wallet deposit-channel` and `wallet withdraw-channel` use the installed Groth16 runtime workspace
527
593
  directly. Proof generation writes to the fixed workspace paths under `~/tokamak-private-channels/groth16/proof`; the CLI
@@ -538,11 +604,16 @@ Release order matters for npm publication. `@tokamak-private-dapps/common-librar
538
604
 
539
605
  It installs the `private-state-cli` terminal command and the local files needed by that command.
540
606
  It does not install bridge contracts, app contracts, or local deployment outputs. The `private-state-cli install`
541
- command provisions the local Tokamak zk-EVM and Groth16 runtime workspaces used by proof-backed commands.
607
+ command defaults to full mode, which provisions local Tokamak zk-EVM and Groth16 runtime workspaces used by
608
+ proof-backed commands. `private-state-cli install --read-only` installs only the public bridge and private-state DApp
609
+ artifacts needed by channel-state read commands and commands that do not depend on channel state.
542
610
 
543
611
  ### When should I run `private-state-cli install`?
544
612
 
545
- Run it once on a new machine, or after public bridge, DApp, Groth16, or Tokamak zk-EVM runtime artifacts are updated.
613
+ Run full install once on a machine that will create, join, exit, or mutate channels. Run read-only install on a machine
614
+ that only needs channel recovery, wallet recovery, metadata lookup, bridge balance lookup, bridge deposit or withdrawal,
615
+ and local import/export/helper commands. Re-run the relevant mode after public bridge, DApp, Groth16, or Tokamak zk-EVM
616
+ runtime artifacts are updated.
546
617
 
547
618
  ### Does this package publish private user data?
548
619
 
@@ -413,16 +413,22 @@
413
413
  <h2>Command Guide</h2>
414
414
  <div class="box">
415
415
  <ol class="guide-list">
416
+ <li>
417
+ <span class="guide-label">Configure RPC:</span> <code>set rpc</code>
418
+ <div class="guide-example">
419
+ <code>set rpc --network 'sepolia' --rpc-url '__RPC_URL__' --provider 'alchemy'</code>
420
+ </div>
421
+ </li>
416
422
  <li>
417
423
  <span class="guide-label">Join a channel:</span> <code>channel join</code>
418
424
  <div class="guide-example">
419
- <code>channel join --channel-name 'my-private-channel' --network 'sepolia' --account 'my-account' --wallet-secret-path '/path/to/wallet-secret' --rpc-url '__RPC_URL__'</code>
425
+ <code>channel join --channel-name 'my-private-channel' --network 'sepolia' --account 'my-account' --wallet-secret-path '/path/to/wallet-secret'</code>
420
426
  </div>
421
427
  </li>
422
428
  <li>
423
429
  <span class="guide-label">Create notes:</span> <code>account deposit-bridge</code> -&gt; <code>wallet deposit-channel</code> -&gt; <code>wallet mint-notes</code>
424
430
  <div class="guide-example">
425
- <code>account deposit-bridge --amount '100' --network 'sepolia' --account 'my-account' --rpc-url '__RPC_URL__'</code><br />
431
+ <code>account deposit-bridge --amount '100' --network 'sepolia' --account 'my-account'</code><br />
426
432
  <code>wallet deposit-channel --wallet 'my-private-channel-0xYourL1Address' --network 'sepolia' --amount '100'</code><br />
427
433
  <code>wallet mint-notes --wallet 'my-private-channel-0xYourL1Address' --network 'sepolia' --amounts '[&quot;50&quot;,&quot;50&quot;,&quot;0&quot;]'</code>
428
434
  </div>
@@ -438,7 +444,7 @@
438
444
  <div class="guide-example">
439
445
  <code>wallet redeem-notes --wallet 'my-private-channel-0xYourL1Address' --network 'sepolia' --note-ids '[&quot;0xNoteIdA&quot;]'</code><br />
440
446
  <code>wallet withdraw-channel --wallet 'my-private-channel-0xYourL1Address' --network 'sepolia' --amount '25'</code><br />
441
- <code>account withdraw-bridge --amount '25' --network 'sepolia' --account 'my-account' --rpc-url '__RPC_URL__'</code>
447
+ <code>account withdraw-bridge --amount '25' --network 'sepolia' --account 'my-account'</code>
442
448
  </div>
443
449
  </li>
444
450
  </ol>
@@ -1420,7 +1426,7 @@
1420
1426
  ].forEach((fieldKey) => {
1421
1427
  memoryFieldsEl.appendChild(createField(fieldKey));
1422
1428
  });
1423
- memoryStatusEl.textContent = "Use account import --private-key-file before signing commands. Optional --rpc-url is saved as the network RPC_URL for later commands.";
1429
+ memoryStatusEl.textContent = "Use set rpc once before bridge-facing commands, then use account import --private-key-file before signing commands.";
1424
1430
  }
1425
1431
 
1426
1432
  function renderCommandSelect() {
@@ -1480,7 +1486,7 @@
1480
1486
  : "",
1481
1487
  ].join("");
1482
1488
  warningEl.textContent = missing.length === 0
1483
- ? `Ready. If --rpc-url is omitted, the CLI uses the saved network RPC_URL.${workspaceWarnings}`
1489
+ ? `Ready. Bridge-facing commands use the saved network RPC configuration.${workspaceWarnings}`
1484
1490
  : `Missing inputs: ${missing.map((fieldKey) => fieldCatalog[fieldKey].label).join(", ")}.${workspaceWarnings}`;
1485
1491
  }
1486
1492
 
@@ -0,0 +1,39 @@
1
+ import {
2
+ assertAccountGetBridgeFundArgs,
3
+ assertAccountGetL1AddressArgs,
4
+ assertAccountImportArgs,
5
+ assertDepositBridgeArgs,
6
+ assertWithdrawBridgeArgs,
7
+ handleAccountGetBridgeFund,
8
+ handleAccountGetL1Address,
9
+ handleAccountImport,
10
+ handleDepositBridge,
11
+ handleWithdrawBridge,
12
+ loadExplicitCommandRuntime,
13
+ } from "../lib/runtime.mjs";
14
+
15
+ export const accountCommands = Object.freeze({
16
+ "account-get-l1-address": async (args) => {
17
+ assertAccountGetL1AddressArgs(args);
18
+ handleAccountGetL1Address({ args });
19
+ },
20
+ "account-import": async (args) => {
21
+ assertAccountImportArgs(args);
22
+ handleAccountImport({ args });
23
+ },
24
+ "account-get-bridge-fund": async (args) => {
25
+ assertAccountGetBridgeFundArgs(args);
26
+ const { provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
27
+ await handleAccountGetBridgeFund({ args, provider });
28
+ },
29
+ "account-deposit-bridge": async (args) => {
30
+ assertDepositBridgeArgs(args);
31
+ const { network, provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
32
+ await handleDepositBridge({ args, network, provider });
33
+ },
34
+ "account-withdraw-bridge": async (args) => {
35
+ assertWithdrawBridgeArgs(args);
36
+ const { network, provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
37
+ await handleWithdrawBridge({ args, network, provider });
38
+ },
39
+ });
@@ -0,0 +1,58 @@
1
+ import {
2
+ assertCreateChannelArgs,
3
+ assertExitChannelArgs,
4
+ assertGetChannelArgs,
5
+ assertJoinChannelArgs,
6
+ assertProviderChainIdMatchesNetwork,
7
+ assertPublishWorkspaceMirrorArgs,
8
+ assertRecoverWorkspaceArgs,
9
+ assertSetWorkspaceMirrorArgs,
10
+ handleChannelCreate,
11
+ handleExitChannel,
12
+ handleGetChannel,
13
+ handleJoinChannel,
14
+ handlePublishChannelWorkspaceMirror,
15
+ handleSetChannelWorkspaceMirror,
16
+ handleWorkspaceInit,
17
+ loadExplicitCommandRuntime,
18
+ loadWalletCommandRuntime,
19
+ } from "../lib/runtime.mjs";
20
+
21
+ export const channelCommands = Object.freeze({
22
+ "channel-create": async (args) => {
23
+ assertCreateChannelArgs(args);
24
+ const { network, provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
25
+ await handleChannelCreate({ args, network, provider });
26
+ },
27
+ "channel-recover-workspace": async (args) => {
28
+ assertRecoverWorkspaceArgs(args);
29
+ const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args, { staticNetwork: true, prepareArtifacts: true });
30
+ await assertProviderChainIdMatchesNetwork({ provider, network, rpcUrl });
31
+ await handleWorkspaceInit({ args, network, provider });
32
+ },
33
+ "channel-get-meta": async (args) => {
34
+ assertGetChannelArgs(args);
35
+ const { network, provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
36
+ await handleGetChannel({ args, network, provider });
37
+ },
38
+ "channel-set-workspace-mirror": async (args) => {
39
+ assertSetWorkspaceMirrorArgs(args);
40
+ const { network, provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
41
+ await handleSetChannelWorkspaceMirror({ args, network, provider });
42
+ },
43
+ "channel-publish-workspace-mirror": async (args) => {
44
+ assertPublishWorkspaceMirrorArgs(args);
45
+ const { network, provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
46
+ await handlePublishChannelWorkspaceMirror({ args, network, provider });
47
+ },
48
+ "channel-join": async (args) => {
49
+ assertJoinChannelArgs(args);
50
+ const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
51
+ await handleJoinChannel({ args, network, provider, rpcUrl });
52
+ },
53
+ "channel-exit": async (args) => {
54
+ assertExitChannelArgs(args);
55
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
56
+ await handleExitChannel({ args, provider });
57
+ },
58
+ });
@@ -0,0 +1,62 @@
1
+ import {
2
+ assertHelpCommandsArgs,
3
+ assertVersionArgs,
4
+ configureOutput,
5
+ formatCliErrorForDisplay,
6
+ parseArgs,
7
+ printHelp,
8
+ printVersion,
9
+ } from "../lib/runtime.mjs";
10
+ import { accountCommands } from "./account.mjs";
11
+ import { channelCommands } from "./channel.mjs";
12
+ import { investigatorCommands } from "./investigator.mjs";
13
+ import { notesCommands } from "./notes.mjs";
14
+ import { systemCommands } from "./system.mjs";
15
+ import { walletCommands } from "./wallet.mjs";
16
+
17
+ const COMMANDS = Object.freeze({
18
+ ...systemCommands,
19
+ ...investigatorCommands,
20
+ ...accountCommands,
21
+ ...channelCommands,
22
+ ...walletCommands,
23
+ ...notesCommands,
24
+ });
25
+
26
+ export async function runPrivateStateCli(argv) {
27
+ let args = {};
28
+ try {
29
+ args = parseArgs(argv);
30
+ configureOutput(args);
31
+
32
+ if (args.version !== undefined) {
33
+ assertVersionArgs(args);
34
+ printVersion();
35
+ return;
36
+ }
37
+
38
+ if (args.help || !args.command) {
39
+ printHelp();
40
+ return;
41
+ }
42
+
43
+ if (args.command === "help-commands") {
44
+ assertHelpCommandsArgs(args);
45
+ printHelp();
46
+ return;
47
+ }
48
+
49
+ const command = COMMANDS[args.command];
50
+ if (!command) {
51
+ throw new Error(`Unsupported command: ${args.command}`);
52
+ }
53
+ await command(args);
54
+ } catch (error) {
55
+ console.error(formatCliErrorForDisplay(error, args));
56
+ process.exitCode = 1;
57
+ }
58
+ }
59
+
60
+ export function privateStateCliDispatchTable() {
61
+ return COMMANDS;
62
+ }
@@ -0,0 +1,11 @@
1
+ import {
2
+ assertInvestigatorArgs,
3
+ handleInvestigator,
4
+ } from "../lib/runtime.mjs";
5
+
6
+ export const investigatorCommands = Object.freeze({
7
+ investigator: async (args) => {
8
+ assertInvestigatorArgs(args);
9
+ handleInvestigator();
10
+ },
11
+ });
@@ -0,0 +1,34 @@
1
+ import {
2
+ assertMintNotesArgs,
3
+ assertRedeemNotesArgs,
4
+ assertTransferNotesArgs,
5
+ assertWalletGetNotesArgs,
6
+ handleMintNotes,
7
+ handleRedeemNotes,
8
+ handleTransferNotes,
9
+ handleWalletGetNotes,
10
+ loadWalletCommandRuntime,
11
+ } from "../lib/runtime.mjs";
12
+
13
+ export const notesCommands = Object.freeze({
14
+ "wallet-mint-notes": async (args) => {
15
+ assertMintNotesArgs(args);
16
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
17
+ await handleMintNotes({ args, provider });
18
+ },
19
+ "wallet-redeem-notes": async (args) => {
20
+ assertRedeemNotesArgs(args);
21
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
22
+ await handleRedeemNotes({ args, provider });
23
+ },
24
+ "wallet-get-notes": async (args) => {
25
+ assertWalletGetNotesArgs(args);
26
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
27
+ await handleWalletGetNotes({ args, provider });
28
+ },
29
+ "wallet-transfer-notes": async (args) => {
30
+ assertTransferNotesArgs(args);
31
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
32
+ await handleTransferNotes({ args, provider });
33
+ },
34
+ });
@@ -0,0 +1,49 @@
1
+ import {
2
+ assertDoctorArgs,
3
+ assertGuideArgs,
4
+ assertInstallZkEvmArgs,
5
+ assertSetRpcArgs,
6
+ assertTransactionFeesArgs,
7
+ assertUninstallArgs,
8
+ assertUpdateArgs,
9
+ handleDoctor,
10
+ handleGuide,
11
+ handleInstallZkEvm,
12
+ handleSetRpc,
13
+ handleTransactionFees,
14
+ handleUninstall,
15
+ handleUpdate,
16
+ loadExplicitCommandRuntime,
17
+ } from "../lib/runtime.mjs";
18
+
19
+ export const systemCommands = Object.freeze({
20
+ install: async (args) => {
21
+ assertInstallZkEvmArgs(args);
22
+ await handleInstallZkEvm({ args });
23
+ },
24
+ uninstall: async (args) => {
25
+ assertUninstallArgs(args);
26
+ await handleUninstall();
27
+ },
28
+ "set-rpc": async (args) => {
29
+ assertSetRpcArgs(args);
30
+ await handleSetRpc({ args });
31
+ },
32
+ "help-update": async (args) => {
33
+ assertUpdateArgs(args);
34
+ await handleUpdate();
35
+ },
36
+ "help-doctor": async (args) => {
37
+ assertDoctorArgs(args);
38
+ await handleDoctor({ args });
39
+ },
40
+ "help-guide": async (args) => {
41
+ assertGuideArgs(args);
42
+ await handleGuide({ args });
43
+ },
44
+ "help-transaction-fees": async (args) => {
45
+ assertTransactionFeesArgs(args);
46
+ const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args);
47
+ await handleTransactionFees({ network, provider, rpcUrl });
48
+ },
49
+ });