@tokamak-private-dapps/private-state-cli 2.2.1 → 2.3.1
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 +15 -0
- package/README.md +10 -2
- package/commands/system.mjs +6 -0
- package/lib/private-state-cli-command-registry.mjs +18 -3
- package/lib/runtime.mjs +123 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 2.3.1 - 2026-05-20
|
|
6
|
+
|
|
7
|
+
- Added `wallet recover-workspace --wallet-secret-path` support for rederiving and storing an active
|
|
8
|
+
wallet spending key from the original L1 account and wallet secret source.
|
|
9
|
+
- Validated recovered spending keys against the current on-chain L2 address and channel token-vault
|
|
10
|
+
storage key before received-note recovery starts.
|
|
11
|
+
- Documented the active-wallet-only spending-key recovery policy in the CLI README and private-state
|
|
12
|
+
DApp README.
|
|
13
|
+
|
|
14
|
+
## 2.3.0 - 2026-05-20
|
|
15
|
+
|
|
16
|
+
- Added `help observer` to print the deployed public observer URL for the private-state monitoring
|
|
17
|
+
surface.
|
|
18
|
+
- Documented the deployed public observer in the monitoring audit packet and observability matrix.
|
|
19
|
+
|
|
5
20
|
## 2.2.1 - 2026-05-18
|
|
6
21
|
|
|
7
22
|
- Added `channel recover-workspace --source rpc --output-raw` to append raw JSON-RPC request and response history
|
package/README.md
CHANGED
|
@@ -388,6 +388,13 @@ flow. The spending key needs the same L1 private key, the same channel context,
|
|
|
388
388
|
spending-key file is lost and the wallet secret source is also lost, the CLI cannot reconstruct the spending key and the
|
|
389
389
|
notes for that wallet cannot be spent, transferred, or redeemed through the normal note flow.
|
|
390
390
|
|
|
391
|
+
`wallet recover-workspace` restores the viewing key by default. Add `--wallet-secret-path <PATH>` only when the
|
|
392
|
+
account is currently active in the channel and you need to rederive the spending key. In that mode, the CLI checks the
|
|
393
|
+
derived L2 address and channel token-vault storage key against the current on-chain registration before received-note
|
|
394
|
+
recovery starts, then stores the protected spending-key file. Exited or non-active accounts must be recovered without
|
|
395
|
+
`--wallet-secret-path`; that restores viewing/evidence history but not spending authority. The wallet secret source is
|
|
396
|
+
read for derivation and is not stored.
|
|
397
|
+
|
|
391
398
|
### Wallet Backup, Viewing, And Spending Authority
|
|
392
399
|
|
|
393
400
|
The wallet workspace is split so that a backup is not a full-control wallet export. Backup metadata stores the
|
|
@@ -405,8 +412,9 @@ transactions and then proves authorized note use when inputs are consumed.
|
|
|
405
412
|
|
|
406
413
|
Key recovery is intentionally split. Recreating the viewing key requires the original L1 private key and the same channel
|
|
407
414
|
context. Recreating the spending key requires the original L1 private key, the same channel context, and the same wallet
|
|
408
|
-
secret source used at `channel join`.
|
|
409
|
-
|
|
415
|
+
secret source used at `channel join`. `wallet recover-workspace --wallet-secret-path <PATH>` performs this spending-key
|
|
416
|
+
rederivation only for active channel registrations. Importing `wallet-viewing.key` or `wallet-spending.key` restores the
|
|
417
|
+
corresponding capability without rerunning derivation, but a backup ZIP alone never restores either capability.
|
|
410
418
|
|
|
411
419
|
`wallet get-notes --export-evidence <PATH> --acknowledge-full-note-plaintext-export` writes a local raw evidence ZIP.
|
|
412
420
|
The bundle is not a key export. It includes plaintext note facts for locally known notes so that
|
package/commands/system.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
assertDoctorArgs,
|
|
3
3
|
assertGuideArgs,
|
|
4
|
+
assertObserverArgs,
|
|
4
5
|
assertInstallZkEvmArgs,
|
|
5
6
|
assertSetRpcArgs,
|
|
6
7
|
assertTransactionFeesArgs,
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
assertUpdateArgs,
|
|
9
10
|
handleDoctor,
|
|
10
11
|
handleGuide,
|
|
12
|
+
handleObserver,
|
|
11
13
|
handleInstallZkEvm,
|
|
12
14
|
handleSetRpc,
|
|
13
15
|
handleTransactionFees,
|
|
@@ -41,6 +43,10 @@ export const systemCommands = Object.freeze({
|
|
|
41
43
|
assertGuideArgs(args);
|
|
42
44
|
await handleGuide({ args });
|
|
43
45
|
},
|
|
46
|
+
"help-observer": async (args) => {
|
|
47
|
+
assertObserverArgs(args);
|
|
48
|
+
handleObserver();
|
|
49
|
+
},
|
|
44
50
|
"help-transaction-fees": async (args) => {
|
|
45
51
|
assertTransactionFeesArgs(args);
|
|
46
52
|
const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args);
|
|
@@ -329,6 +329,17 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
|
|
|
329
329
|
usage: "optional --network, --channel-name, --account, and --wallet",
|
|
330
330
|
help: ["Does not accept --rpc-url and never writes RPC configuration"],
|
|
331
331
|
},
|
|
332
|
+
{
|
|
333
|
+
id: "help-observer",
|
|
334
|
+
display: "help observer",
|
|
335
|
+
description: "Show the deployed public observer URL.",
|
|
336
|
+
fields: [],
|
|
337
|
+
usage: "no options",
|
|
338
|
+
help: [
|
|
339
|
+
"Prints the deployed observer URL so terminals can present it as a clickable link",
|
|
340
|
+
"The observer is a public monitoring surface; it is not a wallet, key manager, or disclosure authority",
|
|
341
|
+
],
|
|
342
|
+
},
|
|
332
343
|
{
|
|
333
344
|
id: "help-transaction-fees",
|
|
334
345
|
display: "help transaction-fees",
|
|
@@ -473,11 +484,15 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
|
|
|
473
484
|
display: "wallet recover-workspace",
|
|
474
485
|
description: "Rebuild a recoverable local wallet from on-chain channel state.",
|
|
475
486
|
installMode: "read-only",
|
|
476
|
-
fields: ["channelName", "network", "account", "fromGenesis"],
|
|
477
|
-
|
|
487
|
+
fields: ["channelName", "network", "account", "walletSecretPath", "fromGenesis"],
|
|
488
|
+
optionalFields: ["walletSecretPath"],
|
|
489
|
+
usage: "--channel-name, --network, --account, optional --wallet-secret-path, optional --from-genesis",
|
|
478
490
|
help: [
|
|
479
|
-
"Rebuilds backup metadata from channel state without recreating the spending key",
|
|
491
|
+
"Rebuilds backup metadata from channel state without recreating the spending key by default",
|
|
480
492
|
"Derives and stores the viewing key when the local account signer can reproduce the registered viewing public key",
|
|
493
|
+
"Use --wallet-secret-path only for an active channel registration when you need to rederive and store the spending key",
|
|
494
|
+
"--wallet-secret-path requires the derived spending key to match the current on-chain L2 address and storage key before note recovery starts",
|
|
495
|
+
"Exited or non-active accounts can be recovered for viewing/evidence history only; omit --wallet-secret-path for those wallets",
|
|
481
496
|
"Before wallet recovery, refreshes stale channel workspace state only when the saved recovery index delta fits the pre-command budget",
|
|
482
497
|
"Fails and asks for channel recover-workspace first when the channel workspace is missing, unusable, or too stale for automatic recovery",
|
|
483
498
|
"Use --from-genesis to restart received-note scanning from channel genesis; it does not rebuild the channel workspace from genesis",
|
package/lib/runtime.mjs
CHANGED
|
@@ -137,6 +137,7 @@ const PRIVATE_STATE_UNINSTALL_CONFIRMATION =
|
|
|
137
137
|
const ACTION_IMPACT_CONFIRMATION =
|
|
138
138
|
"I understand the public and private impact of this action";
|
|
139
139
|
const PRIVATE_STATE_CLI_PACKAGE_NAME = privateStateCliPackageJson.name;
|
|
140
|
+
const PRIVATE_STATE_OBSERVER_URL = "https://project-scw1r.vercel.app";
|
|
140
141
|
const GROTH16_PACKAGE_NAME = "@tokamak-private-dapps/groth16";
|
|
141
142
|
const TOKAMAK_ZKEVM_CLI_PACKAGE_NAME = "@tokamak-zk-evm/cli";
|
|
142
143
|
const WALLET_BACKUP_EXPORT_FORMAT = "tokamak-private-state-wallet-backup-export";
|
|
@@ -2330,6 +2331,13 @@ async function handleRecoverWallet({ args, network, provider, rpcUrl }) {
|
|
|
2330
2331
|
account: signer.address,
|
|
2331
2332
|
});
|
|
2332
2333
|
const registration = await context.channelManager.getChannelTokenVaultRegistration(signer.address);
|
|
2334
|
+
const recoveredSpendingIdentity = await deriveRecoverWalletSpendingIdentity({
|
|
2335
|
+
args,
|
|
2336
|
+
signer,
|
|
2337
|
+
channelName,
|
|
2338
|
+
context,
|
|
2339
|
+
registration,
|
|
2340
|
+
});
|
|
2333
2341
|
const recoveryEventScan = await scanWalletRecoveryEvents({
|
|
2334
2342
|
context,
|
|
2335
2343
|
provider,
|
|
@@ -2357,7 +2365,27 @@ async function handleRecoverWallet({ args, network, provider, rpcUrl }) {
|
|
|
2357
2365
|
Number(registeredNoteReceivePubKey.yParity) === Number(noteReceiveKeyMaterial.noteReceivePubKey.yParity),
|
|
2358
2366
|
"The existing note-receive public key parity does not match the derived note-receive public key.",
|
|
2359
2367
|
);
|
|
2360
|
-
|
|
2368
|
+
if (recoveredSpendingIdentity) {
|
|
2369
|
+
const expectedRecoveredStorageKey = deriveLiquidBalanceStorageKey(
|
|
2370
|
+
recoveredSpendingIdentity.l2Address,
|
|
2371
|
+
context.workspace.liquidBalancesSlot,
|
|
2372
|
+
);
|
|
2373
|
+
expect(
|
|
2374
|
+
lifecycleEpoch.lifecycleStatus === "active",
|
|
2375
|
+
"--wallet-secret-path can only recover the spending key for an active wallet epoch.",
|
|
2376
|
+
);
|
|
2377
|
+
expect(
|
|
2378
|
+
ethers.toBigInt(getAddress(lifecycleEpoch.l2Address))
|
|
2379
|
+
=== ethers.toBigInt(getAddress(recoveredSpendingIdentity.l2Address)),
|
|
2380
|
+
"The recovered spending key does not match the recovered wallet lifecycle L2 address.",
|
|
2381
|
+
);
|
|
2382
|
+
expect(
|
|
2383
|
+
ethers.toBigInt(normalizeBytes32Hex(lifecycleEpoch.channelTokenVaultKey))
|
|
2384
|
+
=== ethers.toBigInt(normalizeBytes32Hex(expectedRecoveredStorageKey)),
|
|
2385
|
+
"The recovered spending key does not match the recovered wallet lifecycle storage key.",
|
|
2386
|
+
);
|
|
2387
|
+
}
|
|
2388
|
+
const l2Identity = recoveredSpendingIdentity ?? {
|
|
2361
2389
|
l2PrivateKey: null,
|
|
2362
2390
|
l2PublicKey: null,
|
|
2363
2391
|
l2Address: getAddress(lifecycleEpoch.l2Address),
|
|
@@ -2376,6 +2404,14 @@ async function handleRecoverWallet({ args, network, provider, rpcUrl }) {
|
|
|
2376
2404
|
if (existingWallet) {
|
|
2377
2405
|
existingWallet.wallet.noteReceivePrivateKey = noteReceiveKeyMaterial.privateKey;
|
|
2378
2406
|
applyWalletLifecycleEpoch(existingWallet.wallet, lifecycleEpoch);
|
|
2407
|
+
if (recoveredSpendingIdentity) {
|
|
2408
|
+
existingWallet.wallet.l2PrivateKey = ethers.hexlify(recoveredSpendingIdentity.l2PrivateKey);
|
|
2409
|
+
existingWallet.wallet.l2PublicKey = ethers.hexlify(recoveredSpendingIdentity.l2PublicKey);
|
|
2410
|
+
existingWallet.wallet.l2Address = recoveredSpendingIdentity.l2Address;
|
|
2411
|
+
existingWallet.wallet.l2DerivationMode = CHANNEL_BOUND_L2_DERIVATION_MODE;
|
|
2412
|
+
existingWallet.wallet.l2DerivationChannelName = channelName;
|
|
2413
|
+
existingWallet.wallet.l2StorageKey = storageKey;
|
|
2414
|
+
}
|
|
2379
2415
|
persistWalletKeys(existingWallet);
|
|
2380
2416
|
persistWallet(existingWallet);
|
|
2381
2417
|
persistWalletIndexForContext(existingWallet);
|
|
@@ -2408,6 +2444,7 @@ async function handleRecoverWallet({ args, network, provider, rpcUrl }) {
|
|
|
2408
2444
|
l1Address: signer.address,
|
|
2409
2445
|
l2Address: l2Identity.l2Address,
|
|
2410
2446
|
l2StorageKey: storageKey,
|
|
2447
|
+
spendingKeyRecovered: Boolean(recoveredSpendingIdentity),
|
|
2411
2448
|
leafIndex: lifecycleEpoch.leafIndex.toString(),
|
|
2412
2449
|
epochId: lifecycleEpoch.epochId,
|
|
2413
2450
|
lifecycleStatus: lifecycleEpoch.lifecycleStatus,
|
|
@@ -2467,6 +2504,7 @@ async function handleRecoverWallet({ args, network, provider, rpcUrl }) {
|
|
|
2467
2504
|
l1Address: signer.address,
|
|
2468
2505
|
l2Address: l2Identity.l2Address,
|
|
2469
2506
|
l2StorageKey: storageKey,
|
|
2507
|
+
spendingKeyRecovered: Boolean(recoveredSpendingIdentity),
|
|
2470
2508
|
leafIndex: lifecycleEpoch.leafIndex.toString(),
|
|
2471
2509
|
epochId: lifecycleEpoch.epochId,
|
|
2472
2510
|
lifecycleStatus: lifecycleEpoch.lifecycleStatus,
|
|
@@ -2480,6 +2518,41 @@ async function handleRecoverWallet({ args, network, provider, rpcUrl }) {
|
|
|
2480
2518
|
});
|
|
2481
2519
|
}
|
|
2482
2520
|
|
|
2521
|
+
async function deriveRecoverWalletSpendingIdentity({
|
|
2522
|
+
args,
|
|
2523
|
+
signer,
|
|
2524
|
+
channelName,
|
|
2525
|
+
context,
|
|
2526
|
+
registration,
|
|
2527
|
+
}) {
|
|
2528
|
+
if (args.walletSecretPath === undefined) {
|
|
2529
|
+
return null;
|
|
2530
|
+
}
|
|
2531
|
+
expect(
|
|
2532
|
+
registration.exists,
|
|
2533
|
+
[
|
|
2534
|
+
"--wallet-secret-path can only recover a spending key for an active channel registration.",
|
|
2535
|
+
"This account is not currently registered in the channel.",
|
|
2536
|
+
"Run wallet recover-workspace without --wallet-secret-path to recover viewing/evidence history for exited wallets.",
|
|
2537
|
+
].join(" "),
|
|
2538
|
+
);
|
|
2539
|
+
const walletSecret = readWalletSecretSourceFile(args);
|
|
2540
|
+
const l2Identity = await deriveParticipantIdentityFromSigner({
|
|
2541
|
+
channelName,
|
|
2542
|
+
walletSecret,
|
|
2543
|
+
signer,
|
|
2544
|
+
});
|
|
2545
|
+
const expectedStorageKey = deriveLiquidBalanceStorageKey(
|
|
2546
|
+
l2Identity.l2Address,
|
|
2547
|
+
context.workspace.liquidBalancesSlot,
|
|
2548
|
+
);
|
|
2549
|
+
expect(
|
|
2550
|
+
walletRegistrationMatchesIdentity({ registration, l2Identity, expectedStorageKey }),
|
|
2551
|
+
"The recovered spending key does not match the current registered L2 address or channel token vault key.",
|
|
2552
|
+
);
|
|
2553
|
+
return l2Identity;
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2483
2556
|
async function handleInstallZkEvm({ args }) {
|
|
2484
2557
|
const installMode = args.readOnly === true
|
|
2485
2558
|
? PRIVATE_STATE_INSTALL_MODES.READ_ONLY
|
|
@@ -3078,6 +3151,18 @@ async function handleDoctor({ args }) {
|
|
|
3078
3151
|
}
|
|
3079
3152
|
}
|
|
3080
3153
|
|
|
3154
|
+
function handleObserver() {
|
|
3155
|
+
printJson({
|
|
3156
|
+
action: "observer",
|
|
3157
|
+
url: PRIVATE_STATE_OBSERVER_URL,
|
|
3158
|
+
scope: "Public monitoring observer for Tokamak Private App Channels and the private-state DApp.",
|
|
3159
|
+
notes: [
|
|
3160
|
+
"The observer helps users and reviewers inspect public monitoring surfaces.",
|
|
3161
|
+
"The observer does not receive wallet secrets, spending keys, viewing keys, or private note plaintext.",
|
|
3162
|
+
],
|
|
3163
|
+
});
|
|
3164
|
+
}
|
|
3165
|
+
|
|
3081
3166
|
function handleInvestigator() {
|
|
3082
3167
|
const htmlPath = resolveInvestigatorIndexPath();
|
|
3083
3168
|
const fileUrl = pathToFileURL(htmlPath).href;
|
|
@@ -4295,10 +4380,7 @@ async function loadWalletChannelRegistrationState({
|
|
|
4295
4380
|
const l2Identity = restoreParticipantIdentityFromWallet(walletContext.wallet);
|
|
4296
4381
|
const registration = await context.channelManager.getChannelTokenVaultRegistration(signer.address);
|
|
4297
4382
|
const expectedStorageKey = deriveLiquidBalanceStorageKey(l2Identity.l2Address, context.workspace.liquidBalancesSlot);
|
|
4298
|
-
const matchesWallet = registration
|
|
4299
|
-
&& ethers.toBigInt(getAddress(registration.l2Address)) === ethers.toBigInt(getAddress(l2Identity.l2Address))
|
|
4300
|
-
&& ethers.toBigInt(normalizeBytes32Hex(registration.channelTokenVaultKey))
|
|
4301
|
-
=== ethers.toBigInt(normalizeBytes32Hex(expectedStorageKey));
|
|
4383
|
+
const matchesWallet = walletRegistrationMatchesIdentity({ registration, l2Identity, expectedStorageKey });
|
|
4302
4384
|
|
|
4303
4385
|
if (requireRegistration) {
|
|
4304
4386
|
expect(
|
|
@@ -4323,6 +4405,13 @@ async function loadWalletChannelRegistrationState({
|
|
|
4323
4405
|
};
|
|
4324
4406
|
}
|
|
4325
4407
|
|
|
4408
|
+
function walletRegistrationMatchesIdentity({ registration, l2Identity, expectedStorageKey }) {
|
|
4409
|
+
return registration.exists
|
|
4410
|
+
&& ethers.toBigInt(getAddress(registration.l2Address)) === ethers.toBigInt(getAddress(l2Identity.l2Address))
|
|
4411
|
+
&& ethers.toBigInt(normalizeBytes32Hex(registration.channelTokenVaultKey))
|
|
4412
|
+
=== ethers.toBigInt(normalizeBytes32Hex(expectedStorageKey));
|
|
4413
|
+
}
|
|
4414
|
+
|
|
4326
4415
|
function selectWalletLifecycleEpoch({ epochs, registration = null }) {
|
|
4327
4416
|
if (registration?.exists) {
|
|
4328
4417
|
const active = [...epochs].reverse().find((epoch) => (
|
|
@@ -9722,6 +9811,10 @@ function prepareJoinWalletSecretForName({
|
|
|
9722
9811
|
`For exited history, keep using wallet recover-workspace --channel-name ${channelName} --network ${networkName} --account ${args.account ?? "<ACCOUNT>"}.`,
|
|
9723
9812
|
].join(" "),
|
|
9724
9813
|
);
|
|
9814
|
+
return readWalletSecretSourceFile(args);
|
|
9815
|
+
}
|
|
9816
|
+
|
|
9817
|
+
function readWalletSecretSourceFile(args) {
|
|
9725
9818
|
const sourcePath = path.resolve(String(requireArg(args.walletSecretPath, "--wallet-secret-path")));
|
|
9726
9819
|
return readImportSecretSourceFile(sourcePath, "--wallet-secret-path");
|
|
9727
9820
|
}
|
|
@@ -10255,6 +10348,10 @@ function assertGuideArgs(args) {
|
|
|
10255
10348
|
assertAllowedCommandSchema(args, "help-guide");
|
|
10256
10349
|
}
|
|
10257
10350
|
|
|
10351
|
+
function assertObserverArgs(args) {
|
|
10352
|
+
assertAllowedCommandSchema(args, "help-observer");
|
|
10353
|
+
}
|
|
10354
|
+
|
|
10258
10355
|
function assertTransactionFeesArgs(args) {
|
|
10259
10356
|
assertAllowedCommandSchema(args, "help-transaction-fees");
|
|
10260
10357
|
}
|
|
@@ -11206,6 +11303,7 @@ function loadWalletCommandRuntime(args, { prepareArtifacts = false } = {}) {
|
|
|
11206
11303
|
const HUMAN_RESULT_RENDERERS = Object.freeze({
|
|
11207
11304
|
guide: printGuideHumanResult,
|
|
11208
11305
|
investigator: printInvestigatorHumanResult,
|
|
11306
|
+
observer: printObserverHumanResult,
|
|
11209
11307
|
"transaction-fees": printTransactionFeesHumanResult,
|
|
11210
11308
|
update: printUpdateHumanResult,
|
|
11211
11309
|
});
|
|
@@ -11288,6 +11386,24 @@ function printInvestigatorHumanResult(result) {
|
|
|
11288
11386
|
console.log(lines.join("\n"));
|
|
11289
11387
|
}
|
|
11290
11388
|
|
|
11389
|
+
function printObserverHumanResult(result) {
|
|
11390
|
+
const lines = [
|
|
11391
|
+
"Private-State Public Observer",
|
|
11392
|
+
`URL: ${formatHumanValue(result.url)}`,
|
|
11393
|
+
];
|
|
11394
|
+
if (result.scope) {
|
|
11395
|
+
lines.push(`Scope: ${formatHumanValue(result.scope)}`);
|
|
11396
|
+
}
|
|
11397
|
+
if (Array.isArray(result.notes) && result.notes.length > 0) {
|
|
11398
|
+
lines.push(
|
|
11399
|
+
"",
|
|
11400
|
+
"Notes",
|
|
11401
|
+
...result.notes.map((note) => `- ${note}`),
|
|
11402
|
+
);
|
|
11403
|
+
}
|
|
11404
|
+
console.log(lines.join("\n"));
|
|
11405
|
+
}
|
|
11406
|
+
|
|
11291
11407
|
function printTransactionFeesHumanResult(report) {
|
|
11292
11408
|
const lines = [
|
|
11293
11409
|
"Transaction Fees",
|
|
@@ -11700,6 +11816,7 @@ export {
|
|
|
11700
11816
|
assertUpdateArgs,
|
|
11701
11817
|
assertDoctorArgs,
|
|
11702
11818
|
assertGuideArgs,
|
|
11819
|
+
assertObserverArgs,
|
|
11703
11820
|
assertTransactionFeesArgs,
|
|
11704
11821
|
assertInvestigatorArgs,
|
|
11705
11822
|
assertAccountGetL1AddressArgs,
|
|
@@ -11733,6 +11850,7 @@ export {
|
|
|
11733
11850
|
handleUpdate,
|
|
11734
11851
|
handleDoctor,
|
|
11735
11852
|
handleGuide,
|
|
11853
|
+
handleObserver,
|
|
11736
11854
|
handleTransactionFees,
|
|
11737
11855
|
handleInvestigator,
|
|
11738
11856
|
handleAccountGetL1Address,
|
package/package.json
CHANGED