polkadot-cli 1.20.0 → 1.21.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.
Files changed (3) hide show
  1. package/README.md +110 -29
  2. package/dist/cli.mjs +785 -243
  3. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -73,7 +73,9 @@ var init_errors = __esm(() => {
73
73
  /codec/i,
74
74
  /decod(e|ing)/i,
75
75
  /Lookup failed/i,
76
- /metadata.*mismatch/i
76
+ /metadata.*mismatch/i,
77
+ /BadProof/,
78
+ /AncientBirthBlock/
77
79
  ];
78
80
  BLOCK_UNAVAILABLE_PATTERNS = [
79
81
  /is not pinned/i,
@@ -231,31 +233,84 @@ var init_types = __esm(() => {
231
233
  BUILTIN_CHAIN_NAMES = new Set(Object.keys(DEFAULT_CONFIG.chains));
232
234
  });
233
235
 
236
+ // src/config/workspace.ts
237
+ import { realpathSync, statSync } from "node:fs";
238
+ import { homedir } from "node:os";
239
+ import { dirname, join, resolve } from "node:path";
240
+ function isDirectory(path) {
241
+ try {
242
+ return statSync(path).isDirectory();
243
+ } catch {
244
+ return false;
245
+ }
246
+ }
247
+ function canonicalPath(path) {
248
+ try {
249
+ return realpathSync(path);
250
+ } catch {
251
+ return resolve(path);
252
+ }
253
+ }
254
+ function findWorkspace(startDir, home = homedir()) {
255
+ const homePath = canonicalPath(home);
256
+ let dir = canonicalPath(startDir);
257
+ while (dir !== homePath) {
258
+ const candidate = join(dir, WORKSPACE_DIR_NAME);
259
+ if (isDirectory(candidate))
260
+ return candidate;
261
+ const parent = dirname(dir);
262
+ if (parent === dir)
263
+ return null;
264
+ dir = parent;
265
+ }
266
+ return null;
267
+ }
268
+ var WORKSPACE_DIR_NAME = ".polkadot";
269
+ var init_workspace = () => {};
270
+
234
271
  // src/config/store.ts
235
272
  import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
236
- import { homedir } from "node:os";
237
- import { join } from "node:path";
238
- function getConfigDir() {
273
+ import { homedir as homedir2 } from "node:os";
274
+ import { join as join2 } from "node:path";
275
+ function resolveConfigDir(cwd = process.cwd()) {
239
276
  const override = process.env.DOT_HOME;
240
- return override && override.length > 0 ? override : join(homedir(), ".polkadot");
277
+ if (override && override.length > 0)
278
+ return { path: override, source: "env" };
279
+ const workspace = findWorkspace(cwd);
280
+ if (workspace)
281
+ return { path: workspace, source: "workspace" };
282
+ return { path: join2(homedir2(), ".polkadot"), source: "global" };
283
+ }
284
+ function describeConfigDir(resolved = resolveConfigDir()) {
285
+ switch (resolved.source) {
286
+ case "env":
287
+ return `DOT_HOME ${resolved.path}`;
288
+ case "workspace":
289
+ return `workspace ${resolved.path}`;
290
+ case "global":
291
+ return `global config ${resolved.path}`;
292
+ }
293
+ }
294
+ function getConfigDir() {
295
+ return resolveConfigDir().path;
241
296
  }
242
297
  function getChainsDir() {
243
- return join(getConfigDir(), "chains");
298
+ return join2(getConfigDir(), "chains");
244
299
  }
245
300
  function getChainDir(chainName) {
246
- return join(getChainsDir(), chainName);
301
+ return join2(getChainsDir(), chainName);
247
302
  }
248
303
  function getMetadataPath(chainName) {
249
- return join(getChainDir(chainName), "metadata.bin");
304
+ return join2(getChainDir(chainName), "metadata.bin");
250
305
  }
251
306
  function getMetadataFingerprintPath(chainName) {
252
- return join(getChainDir(chainName), "metadata.fingerprint.json");
307
+ return join2(getChainDir(chainName), "metadata.fingerprint.json");
253
308
  }
254
309
  function getRpcMethodsPath(chainName) {
255
- return join(getChainDir(chainName), "rpc-methods.json");
310
+ return join2(getChainDir(chainName), "rpc-methods.json");
256
311
  }
257
312
  function getConfigPath() {
258
- return join(getConfigDir(), "config.json");
313
+ return join2(getConfigDir(), "config.json");
259
314
  }
260
315
  async function ensureDir(dir) {
261
316
  await mkdir(dir, { recursive: true });
@@ -360,20 +415,21 @@ function resolveChain(config, chainFlag) {
360
415
  }
361
416
  const name = findChainName(config, chainFlag);
362
417
  if (!name) {
363
- throw new CliError(`Unknown chain "${chainFlag}". Available chains: ${available}`);
418
+ throw new CliError(`Unknown chain "${chainFlag}" in ${describeConfigDir()}. Available chains: ${available}`);
364
419
  }
365
420
  return { name, chain: config.chains[name] };
366
421
  }
367
422
  var init_store = __esm(() => {
368
423
  init_errors();
369
424
  init_types();
425
+ init_workspace();
370
426
  });
371
427
 
372
428
  // src/config/accounts-store.ts
373
429
  import { access as access2, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
374
- import { join as join2 } from "node:path";
430
+ import { join as join3 } from "node:path";
375
431
  function getAccountsPath() {
376
- return join2(getConfigDir(), "accounts.json");
432
+ return join3(getConfigDir(), "accounts.json");
377
433
  }
378
434
  async function ensureDir2(dir) {
379
435
  await mkdir2(dir, { recursive: true });
@@ -645,6 +701,16 @@ function tryDerivePublicKey(envVarName, path = "") {
645
701
  return null;
646
702
  }
647
703
  }
704
+ function unknownAccountError(name, accountsFile) {
705
+ const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
706
+ const suggestions = findClosest(name, available);
707
+ const hint = suggestions.length > 0 ? `
708
+ Did you mean: ${suggestions.join(", ")}?` : "";
709
+ const list = available.map((a) => `
710
+ - ${a}`).join("");
711
+ return new Error(`Unknown account "${name}" in ${describeConfigDir()}.${hint}
712
+ Available accounts:${list}`);
713
+ }
648
714
  async function resolveAccountKeypair(name) {
649
715
  if (isDevAccount(name)) {
650
716
  return getDevKeypair(name);
@@ -652,14 +718,7 @@ async function resolveAccountKeypair(name) {
652
718
  const accountsFile = await loadAccounts();
653
719
  const account = findAccount(accountsFile, name);
654
720
  if (!account) {
655
- const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
656
- const suggestions = findClosest(name, available);
657
- const hint = suggestions.length > 0 ? `
658
- Did you mean: ${suggestions.join(", ")}?` : "";
659
- const list = available.map((a) => `
660
- - ${a}`).join("");
661
- throw new Error(`Unknown account "${name}".${hint}
662
- Available accounts:${list}`);
721
+ throw unknownAccountError(name, accountsFile);
663
722
  }
664
723
  if (account.secret === undefined) {
665
724
  throw new Error(`Account "${name}" is watch-only (no secret). Cannot sign. Import with --secret or --env.`);
@@ -678,14 +737,7 @@ async function resolveAccountExpandedSecret(name) {
678
737
  const accountsFile = await loadAccounts();
679
738
  const account = findAccount(accountsFile, name);
680
739
  if (!account) {
681
- const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
682
- const suggestions = findClosest(name, available);
683
- const hint = suggestions.length > 0 ? `
684
- Did you mean: ${suggestions.join(", ")}?` : "";
685
- const list = available.map((a) => `
686
- - ${a}`).join("");
687
- throw new Error(`Unknown account "${name}".${hint}
688
- Available accounts:${list}`);
740
+ throw unknownAccountError(name, accountsFile);
689
741
  }
690
742
  if (account.secret === undefined) {
691
743
  throw new Error(`Account "${name}" is watch-only (no secret). Cannot derive private key. Import with --secret or --env.`);
@@ -698,6 +750,7 @@ function bytesToHex(bytes) {
698
750
  var DEV_NAMES, EXPANDED_SECRET_RE, HEX_SEED_RE, DERIVATION_RE;
699
751
  var init_accounts = __esm(() => {
700
752
  init_accounts_store();
753
+ init_store();
701
754
  DEV_NAMES = ["alice", "bob", "charlie", "dave", "eve", "ferdie"];
702
755
  EXPANDED_SECRET_RE = /^0x[0-9a-fA-F]{128}$/;
703
756
  HEX_SEED_RE = /^0x[0-9a-fA-F]{64}$/;
@@ -759,8 +812,8 @@ function isJsonOutput(opts) {
759
812
  return opts.json === true || opts.output === "json";
760
813
  }
761
814
  function writeStdout(text) {
762
- return new Promise((resolve) => {
763
- process.stdout.write(text, () => resolve());
815
+ return new Promise((resolve2) => {
816
+ process.stdout.write(text, () => resolve2());
764
817
  });
765
818
  }
766
819
  function printJsonLine(data) {
@@ -864,24 +917,130 @@ var init_output = __esm(() => {
864
917
  SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
865
918
  });
866
919
 
867
- // src/core/bandersnatch.ts
868
- var exports_bandersnatch = {};
869
- __export(exports_bandersnatch, {
870
- deriveBandersnatchMember: () => deriveBandersnatchMember
920
+ // src/features/verifiable/lib.ts
921
+ var exports_lib = {};
922
+ __export(exports_lib, {
923
+ verifyRingProof: () => verifyRingProof,
924
+ verifyBandersnatchSig: () => verifyBandersnatchSig,
925
+ ringRoot: () => ringRoot,
926
+ ringProve: () => ringProve,
927
+ resolveEntropyKey: () => resolveEntropyKey,
928
+ isRingExponent: () => isRingExponent,
929
+ encodeMembers: () => encodeMembers,
930
+ encodeContext: () => encodeContext,
931
+ deriveMemberKey: () => deriveMemberKey,
932
+ deriveMemberEntropy: () => deriveMemberEntropy,
933
+ deriveBandersnatchMember: () => deriveBandersnatchMember,
934
+ deriveAlias: () => deriveAlias,
935
+ compactEncode: () => compactEncode,
936
+ bandersnatchSign: () => bandersnatchSign,
937
+ DEFAULT_RING_EXPONENT: () => DEFAULT_RING_EXPONENT
871
938
  });
872
939
  import { blake2b } from "@noble/hashes/blake2.js";
940
+ import { hexToBytes as hexToBytes3 } from "@noble/hashes/utils.js";
941
+ import { compact } from "@polkadot-api/substrate-bindings";
873
942
  import { mnemonicToEntropy as mnemonicToEntropy2 } from "@polkadot-labs/hdkd-helpers";
874
- import { member_from_entropy } from "verifiablejs/nodejs";
875
- function deriveBandersnatchMember(mnemonic, context) {
943
+ import {
944
+ alias_in_context,
945
+ member_from_entropy,
946
+ members_root,
947
+ one_shot,
948
+ sign as sign2,
949
+ validate,
950
+ validate_with_commitment,
951
+ verify_signature
952
+ } from "verifiablejs/nodejs";
953
+ function isRingExponent(n) {
954
+ return RING_EXPONENTS.includes(n);
955
+ }
956
+ function textOrHexBytes(value, label) {
957
+ if (value.startsWith("0x")) {
958
+ const hex = value.slice(2);
959
+ if (hex.length % 2 !== 0) {
960
+ throw new Error(`Invalid hex ${label}: odd number of characters`);
961
+ }
962
+ return hexToBytes3(hex);
963
+ }
964
+ return new TextEncoder().encode(value);
965
+ }
966
+ function resolveEntropyKey(value) {
967
+ if (value === undefined || value === "")
968
+ return;
969
+ return textOrHexBytes(value, "entropy-key");
970
+ }
971
+ function deriveMemberEntropy(mnemonic, entropyKey) {
876
972
  const entropy = mnemonicToEntropy2(mnemonic);
877
973
  const opts = { dkLen: 32 };
878
- if (context) {
879
- opts.key = new TextEncoder().encode(context);
974
+ if (entropyKey !== undefined && entropyKey.length > 0) {
975
+ opts.key = entropyKey;
976
+ }
977
+ return blake2b(entropy, opts);
978
+ }
979
+ function deriveMemberKey(entropy) {
980
+ return member_from_entropy(entropy);
981
+ }
982
+ function deriveBandersnatchMember(mnemonic, entropyKey) {
983
+ return deriveMemberKey(deriveMemberEntropy(mnemonic, resolveEntropyKey(entropyKey)));
984
+ }
985
+ function deriveAlias(entropy, context) {
986
+ return alias_in_context(entropy, context);
987
+ }
988
+ function bandersnatchSign(entropy, message) {
989
+ return sign2(entropy, message);
990
+ }
991
+ function verifyBandersnatchSig(signature, message, member) {
992
+ return verify_signature(signature, message, member);
993
+ }
994
+ function ringProve(ringExp, entropy, members, context, message) {
995
+ const result = one_shot(ringExp, entropy, members, context, message);
996
+ return { proof: result.proof, alias: result.alias };
997
+ }
998
+ function verifyRingProof(ringExp, proof, source, context, message) {
999
+ if (source.commitment !== undefined) {
1000
+ return validate_with_commitment(ringExp, proof, source.commitment, context, message);
880
1001
  }
881
- const hashed = blake2b(entropy, opts);
882
- return member_from_entropy(hashed);
1002
+ if (source.members !== undefined) {
1003
+ return validate(ringExp, proof, source.members, context, message);
1004
+ }
1005
+ throw new Error("verifyRingProof requires either `members` or `commitment`");
1006
+ }
1007
+ function ringRoot(ringExp, members) {
1008
+ return members_root(ringExp, members);
1009
+ }
1010
+ function compactEncode(n) {
1011
+ if (!Number.isInteger(n) || n < 0)
1012
+ throw new Error("compactEncode: non-negative integer required");
1013
+ return compact.enc(n);
1014
+ }
1015
+ function encodeMembers(members) {
1016
+ for (const m of members) {
1017
+ if (m.length !== 32) {
1018
+ throw new Error(`member key must be 32 bytes (got ${m.length})`);
1019
+ }
1020
+ }
1021
+ const prefix = compactEncode(members.length);
1022
+ const out = new Uint8Array(prefix.length + members.length * 32);
1023
+ out.set(prefix, 0);
1024
+ let offset = prefix.length;
1025
+ for (const m of members) {
1026
+ out.set(m, offset);
1027
+ offset += 32;
1028
+ }
1029
+ return out;
1030
+ }
1031
+ function encodeContext(input) {
1032
+ const bytes = textOrHexBytes(input, "context");
1033
+ if (bytes.length > 32) {
1034
+ throw new Error(`Context must be at most 32 bytes (got ${bytes.length})`);
1035
+ }
1036
+ const out = new Uint8Array(32);
1037
+ out.set(bytes, 0);
1038
+ return out;
883
1039
  }
884
- var init_bandersnatch = () => {};
1040
+ var RING_EXPONENTS, DEFAULT_RING_EXPONENT = 9;
1041
+ var init_lib = __esm(() => {
1042
+ RING_EXPONENTS = [9, 10, 14];
1043
+ });
885
1044
 
886
1045
  // src/core/client.ts
887
1046
  import { createClient } from "polkadot-api";
@@ -1007,9 +1166,9 @@ function compactEntry(entry, color) {
1007
1166
  }
1008
1167
  }
1009
1168
  function expandEntry(entry, indent, width, color, prefix = 0) {
1010
- const compact = compactEntry(entry, color);
1011
- if (visualWidth(compact) + indent + prefix <= width)
1012
- return compact;
1169
+ const compact2 = compactEntry(entry, color);
1170
+ if (visualWidth(compact2) + indent + prefix <= width)
1171
+ return compact2;
1013
1172
  switch (entry.type) {
1014
1173
  case "struct":
1015
1174
  return expandStruct(entry.value, indent, width, color);
@@ -1043,7 +1202,7 @@ ${closePadding}>`;
1043
1202
  case "lookupEntry":
1044
1203
  return expandEntry(entry.value, indent, width, color);
1045
1204
  default:
1046
- return compact;
1205
+ return compact2;
1047
1206
  }
1048
1207
  }
1049
1208
  function expandStruct(fields, indent, width, color) {
@@ -1102,9 +1261,9 @@ ${closePadding}${close}`;
1102
1261
  }
1103
1262
  function prettyType(entry, opts = {}) {
1104
1263
  const { indent, prefix, width, color } = resolveOpts(opts);
1105
- const compact = compactEntry(entry, color);
1106
- if (visualWidth(compact) + indent + prefix <= width)
1107
- return compact;
1264
+ const compact2 = compactEntry(entry, color);
1265
+ if (visualWidth(compact2) + indent + prefix <= width)
1266
+ return compact2;
1108
1267
  return expandEntry(entry, indent, width, color);
1109
1268
  }
1110
1269
  function prettyTypeById(lookup, typeId, opts = {}) {
@@ -1208,15 +1367,15 @@ function renderArgsFromFields(fields, opts) {
1208
1367
  case "void":
1209
1368
  return "()";
1210
1369
  case "named": {
1211
- const compact = `(${fields.fields.map(([k, v]) => `${paint(color, CYAN2, k)}: ${compactEntry(v, color)}`).join(", ")})`;
1212
- if (visualWidth(compact) + lead <= width)
1213
- return compact;
1370
+ const compact2 = `(${fields.fields.map(([k, v]) => `${paint(color, CYAN2, k)}: ${compactEntry(v, color)}`).join(", ")})`;
1371
+ if (visualWidth(compact2) + lead <= width)
1372
+ return compact2;
1214
1373
  return renderFieldList(fields.fields, "(", ")", indent, width, color);
1215
1374
  }
1216
1375
  case "positional": {
1217
- const compact = `(${fields.types.map((t) => compactEntry(t, color)).join(", ")})`;
1218
- if (visualWidth(compact) + lead <= width)
1219
- return compact;
1376
+ const compact2 = `(${fields.types.map((t) => compactEntry(t, color)).join(", ")})`;
1377
+ if (visualWidth(compact2) + lead <= width)
1378
+ return compact2;
1220
1379
  const innerIndent = indent + 2;
1221
1380
  const padding = " ".repeat(innerIndent);
1222
1381
  const closePadding = " ".repeat(indent);
@@ -1227,9 +1386,9 @@ ${lines.join(`,
1227
1386
  ${closePadding})`;
1228
1387
  }
1229
1388
  case "single": {
1230
- const compact = `(${compactEntry(fields.type, color)})`;
1231
- if (visualWidth(compact) + lead <= width)
1232
- return compact;
1389
+ const compact2 = `(${compactEntry(fields.type, color)})`;
1390
+ if (visualWidth(compact2) + lead <= width)
1391
+ return compact2;
1233
1392
  const inner = expandEntry(fields.type, indent + 2, width, color);
1234
1393
  return `(
1235
1394
  ${" ".repeat(indent + 2)}${inner},
@@ -1300,7 +1459,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
1300
1459
  let bytes;
1301
1460
  try {
1302
1461
  const hex = await withTimeout(client._request("state_call", ["Metadata_metadata_at_version", v15Arg]), chainName);
1303
- const raw = hexToBytes3(hex);
1462
+ const raw = hexToBytes4(hex);
1304
1463
  const decoded = optionalOpaqueBytes.dec(raw);
1305
1464
  if (decoded !== undefined) {
1306
1465
  bytes = new Uint8Array(decoded);
@@ -1309,7 +1468,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
1309
1468
  if (!bytes) {
1310
1469
  try {
1311
1470
  const hex = await withTimeout(client._request("state_getMetadata", []), chainName);
1312
- bytes = hexToBytes3(hex);
1471
+ bytes = hexToBytes4(hex);
1313
1472
  } catch (err) {
1314
1473
  if (err instanceof ConnectionError)
1315
1474
  throw err;
@@ -1505,7 +1664,7 @@ function describeCallArgs(meta, palletName, callName) {
1505
1664
  function describeEventFields(meta, palletName, eventName) {
1506
1665
  return compactArgsString(getEventFields(meta, palletName, eventName));
1507
1666
  }
1508
- function hexToBytes3(hex) {
1667
+ function hexToBytes4(hex) {
1509
1668
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
1510
1669
  const bytes = new Uint8Array(clean.length / 2);
1511
1670
  for (let i = 0;i < clean.length; i += 2) {
@@ -3234,7 +3393,7 @@ var init_xxh64 = __esm(() => {
3234
3393
  import { blake2b as blake2b2 } from "@noble/hashes/blake2.js";
3235
3394
  import { sha256 } from "@noble/hashes/sha2.js";
3236
3395
  import { keccak_256 as keccak_2562 } from "@noble/hashes/sha3.js";
3237
- import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes4 } from "@noble/hashes/utils.js";
3396
+ import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes5 } from "@noble/hashes/utils.js";
3238
3397
  function computeHash(algorithm, data) {
3239
3398
  const algo = ALGORITHMS[algorithm];
3240
3399
  if (!algo) {
@@ -3248,7 +3407,7 @@ function parseInputData(input) {
3248
3407
  if (hex.length % 2 !== 0) {
3249
3408
  throw new Error(`Invalid hex input: odd number of characters`);
3250
3409
  }
3251
- return hexToBytes4(hex);
3410
+ return hexToBytes5(hex);
3252
3411
  }
3253
3412
  return new TextEncoder().encode(input);
3254
3413
  }
@@ -3303,6 +3462,349 @@ var init_hash = __esm(() => {
3303
3462
  };
3304
3463
  });
3305
3464
 
3465
+ // src/core/input.ts
3466
+ import { readFile as readFile5 } from "node:fs/promises";
3467
+ async function resolveDataInput(inline, opts, messages) {
3468
+ const sources = [inline !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
3469
+ if (sources > 1) {
3470
+ throw new CliError(messages?.conflict ?? "Provide only one of: inline data, --file, or --stdin");
3471
+ }
3472
+ if (sources === 0) {
3473
+ throw new CliError(messages?.missing ?? "No input provided. Pass data as argument, or use --file or --stdin");
3474
+ }
3475
+ if (opts.file) {
3476
+ const buf = await readFile5(opts.file);
3477
+ return new Uint8Array(buf);
3478
+ }
3479
+ if (opts.stdin) {
3480
+ const chunks = [];
3481
+ for await (const chunk of process.stdin) {
3482
+ chunks.push(chunk);
3483
+ }
3484
+ return new Uint8Array(Buffer.concat(chunks));
3485
+ }
3486
+ return parseInputData(inline);
3487
+ }
3488
+ var init_input = __esm(() => {
3489
+ init_errors();
3490
+ init_hash();
3491
+ });
3492
+
3493
+ // src/platform/cli.ts
3494
+ function readRawOptionValue(name, argv = process.argv) {
3495
+ const flag = `--${name}`;
3496
+ const prefix = `${flag}=`;
3497
+ let value;
3498
+ for (let i = 0;i < argv.length; i++) {
3499
+ const arg = argv[i];
3500
+ if (arg === "--")
3501
+ break;
3502
+ if (arg === flag && i + 1 < argv.length)
3503
+ value = argv[i + 1];
3504
+ else if (arg.startsWith(prefix))
3505
+ value = arg.slice(prefix.length);
3506
+ }
3507
+ return value;
3508
+ }
3509
+
3510
+ // src/platform/index.ts
3511
+ var init_platform = __esm(() => {
3512
+ init_accounts_store();
3513
+ init_accounts();
3514
+ init_hash();
3515
+ init_input();
3516
+ init_output();
3517
+ init_errors();
3518
+ });
3519
+
3520
+ // src/features/verifiable/commands.ts
3521
+ var exports_commands = {};
3522
+ __export(exports_commands, {
3523
+ runVerifiable: () => runVerifiable
3524
+ });
3525
+ import { readFile as readFile8 } from "node:fs/promises";
3526
+ import { DEV_PHRASE as DEV_PHRASE2 } from "@polkadot-labs/hdkd-helpers";
3527
+ async function runVerifiable(action, rest, opts) {
3528
+ switch (action) {
3529
+ case "member":
3530
+ return deriveMember(rest[0], opts);
3531
+ case "alias":
3532
+ return deriveAliasCmd(rest[0], opts);
3533
+ case "sign":
3534
+ return signCmd(rest[0], opts);
3535
+ case "prove":
3536
+ return proveCmd(rest[0], opts);
3537
+ case "verify":
3538
+ return verifyCmd(opts);
3539
+ case "verify-sig":
3540
+ return verifySigCmd(opts);
3541
+ case "members":
3542
+ return membersCmd(rest, opts);
3543
+ default:
3544
+ return deriveMember(action, opts);
3545
+ }
3546
+ }
3547
+ function mnemonicFromStored(stored, account, accountsFile) {
3548
+ if (!stored) {
3549
+ const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
3550
+ const suggestions = findClosest(account, available);
3551
+ const hint = suggestions.length > 0 ? `
3552
+ Did you mean: ${suggestions.join(", ")}?` : "";
3553
+ const list = available.map((a) => `
3554
+ - ${a}`).join("");
3555
+ throw new Error(`Unknown account "${account}".${hint}
3556
+ Available accounts:${list}`);
3557
+ }
3558
+ if (isWatchOnly(stored)) {
3559
+ throw new Error(`Account "${account}" is watch-only (no secret). Cannot derive Bandersnatch key.`);
3560
+ }
3561
+ const secret = resolveSecret(stored.secret);
3562
+ if (isHexPublicKey(`0x${secret.replace(/^0x/, "")}`)) {
3563
+ throw new Error(`Account "${account}" uses a hex seed. Bandersnatch derivation requires a BIP39 mnemonic.`);
3564
+ }
3565
+ return secret;
3566
+ }
3567
+ async function resolveMnemonic(account) {
3568
+ if (isDevAccount(account)) {
3569
+ return DEV_PHRASE2;
3570
+ }
3571
+ const accountsFile = await loadAccounts();
3572
+ return mnemonicFromStored(findAccount(accountsFile, account), account, accountsFile);
3573
+ }
3574
+ async function resolveEntropy(account, entropyKey) {
3575
+ const mnemonic = await resolveMnemonic(account);
3576
+ return deriveMemberEntropy(mnemonic, resolveEntropyKey(entropyKey));
3577
+ }
3578
+ function requireAccount(account, action) {
3579
+ if (!account) {
3580
+ throw new CliError(`dot verifiable ${action} requires an account. See "dot verifiable".`);
3581
+ }
3582
+ return account;
3583
+ }
3584
+ function resolveMessage(opts) {
3585
+ return resolveDataInput(opts.message, opts, {
3586
+ conflict: "Provide only one of: --message, --file, or --stdin",
3587
+ missing: "No message provided. Use --message, --file, or --stdin"
3588
+ });
3589
+ }
3590
+ async function resolveBytesArg(value, name, allowFile = false) {
3591
+ if (value.startsWith("0x")) {
3592
+ return parseInputData(value);
3593
+ }
3594
+ if (allowFile) {
3595
+ const buf = await readFile8(value);
3596
+ const text = buf.toString("utf8").trim();
3597
+ return text.startsWith("0x") ? parseInputData(text) : new Uint8Array(buf);
3598
+ }
3599
+ throw new CliError(`${name} must be 0x-prefixed hex`);
3600
+ }
3601
+ function resolveRingExponent(opts) {
3602
+ if (opts.ringExponent === undefined)
3603
+ return DEFAULT_RING_EXPONENT;
3604
+ const n = Number(opts.ringExponent);
3605
+ if (!isRingExponent(n)) {
3606
+ throw new CliError(`Invalid --ring-exponent "${opts.ringExponent}". Supported: 9, 10, 14.`);
3607
+ }
3608
+ return n;
3609
+ }
3610
+ function requireOption(value, flag, action) {
3611
+ if (value === undefined) {
3612
+ throw new CliError(`dot verifiable ${action} requires ${flag}.`);
3613
+ }
3614
+ return value;
3615
+ }
3616
+ async function deriveMember(accountArg, opts) {
3617
+ const account = requireAccount(accountArg, "member");
3618
+ const usedDeprecatedContext = opts.entropyKey === undefined && opts.context !== undefined;
3619
+ if (usedDeprecatedContext) {
3620
+ if (opts.context.startsWith("0x")) {
3621
+ throw new CliError(`"--context" on "dot verifiable" now means the 32-byte ring context, and hex ` + `entropy keys changed meaning in this release. Pass "--entropy-key ${opts.context}" explicitly.`);
3622
+ }
3623
+ process.stderr.write(`Warning: "--context" on "dot verifiable" now refers to the 32-byte ring context. ` + `For member-key derivation use "--entropy-key". Treating "--context ${opts.context}" ` + `as the entropy key for now.
3624
+ `);
3625
+ }
3626
+ const entropyKeyStr = opts.entropyKey ?? opts.context;
3627
+ let mnemonic;
3628
+ let accountsFile;
3629
+ let stored;
3630
+ if (isDevAccount(account)) {
3631
+ mnemonic = DEV_PHRASE2;
3632
+ } else {
3633
+ accountsFile = await loadAccounts();
3634
+ stored = findAccount(accountsFile, account);
3635
+ mnemonic = mnemonicFromStored(stored, account, accountsFile);
3636
+ }
3637
+ const memberKeyHex = publicKeyToHex(deriveBandersnatchMember(mnemonic, entropyKeyStr));
3638
+ if (stored && accountsFile) {
3639
+ if (!stored.bandersnatch)
3640
+ stored.bandersnatch = {};
3641
+ const entryKey = entropyKeyStr ?? "";
3642
+ if (stored.bandersnatch[entryKey] !== memberKeyHex) {
3643
+ stored.bandersnatch[entryKey] = memberKeyHex;
3644
+ await saveAccounts(accountsFile);
3645
+ }
3646
+ }
3647
+ const fieldKey = usedDeprecatedContext ? "context" : "entropyKey";
3648
+ if (isJsonOutput(opts)) {
3649
+ const result = { account, memberKey: memberKeyHex };
3650
+ if (entropyKeyStr)
3651
+ result[fieldKey] = entropyKeyStr;
3652
+ console.log(formatJson(result));
3653
+ } else {
3654
+ printHeading("Bandersnatch Member Key");
3655
+ console.log(` ${BOLD}Account:${RESET} ${account}`);
3656
+ if (entropyKeyStr) {
3657
+ const line = usedDeprecatedContext ? ` ${BOLD}Context:${RESET} ${entropyKeyStr}` : ` ${BOLD}Entropy Key:${RESET} ${entropyKeyStr}`;
3658
+ console.log(line);
3659
+ }
3660
+ console.log(` ${BOLD}Member Key:${RESET} ${memberKeyHex}`);
3661
+ console.log();
3662
+ }
3663
+ }
3664
+ async function deriveAliasCmd(accountArg, opts) {
3665
+ const account = requireAccount(accountArg, "alias");
3666
+ const contextStr = requireOption(opts.context, "--context", "alias");
3667
+ const entropy = await resolveEntropy(account, opts.entropyKey);
3668
+ const context = encodeContext(contextStr);
3669
+ const aliasHex = toHex2(deriveAlias(entropy, context));
3670
+ if (isJsonOutput(opts)) {
3671
+ console.log(formatJson({ account, context: contextStr, alias: aliasHex }));
3672
+ } else {
3673
+ printHeading("Verifiable Alias");
3674
+ console.log(` ${BOLD}Account:${RESET} ${account}`);
3675
+ console.log(` ${BOLD}Context:${RESET} ${contextStr}`);
3676
+ console.log(` ${BOLD}Alias:${RESET} ${aliasHex}`);
3677
+ console.log();
3678
+ }
3679
+ }
3680
+ async function signCmd(accountArg, opts) {
3681
+ const account = requireAccount(accountArg, "sign");
3682
+ const message = await resolveMessage(opts);
3683
+ const entropy = await resolveEntropy(account, opts.entropyKey);
3684
+ const signature = bandersnatchSign(entropy, message);
3685
+ const member = deriveMemberKey(entropy);
3686
+ const sigHex = toHex2(signature);
3687
+ const result = {
3688
+ type: "Bandersnatch",
3689
+ account,
3690
+ message: toHex2(message),
3691
+ signature: sigHex,
3692
+ member: toHex2(member),
3693
+ enum: `Bandersnatch(${sigHex})`
3694
+ };
3695
+ if (isJsonOutput(opts)) {
3696
+ console.log(formatJson(result));
3697
+ } else {
3698
+ console.log(` ${BOLD}Type:${RESET} ${result.type}`);
3699
+ console.log(` ${BOLD}Message:${RESET} ${result.message}`);
3700
+ console.log(` ${BOLD}Signature:${RESET} ${result.signature}`);
3701
+ console.log(` ${BOLD}Member:${RESET} ${result.member}`);
3702
+ console.log(` ${BOLD}Enum:${RESET} ${result.enum}`);
3703
+ }
3704
+ }
3705
+ async function proveCmd(accountArg, opts) {
3706
+ const account = requireAccount(accountArg, "prove");
3707
+ const contextStr = requireOption(opts.context, "--context", "prove");
3708
+ const membersArg = requireOption(opts.members, "--members", "prove");
3709
+ const message = await resolveMessage(opts);
3710
+ const ringExponent = resolveRingExponent(opts);
3711
+ const entropy = await resolveEntropy(account, opts.entropyKey);
3712
+ const context = encodeContext(contextStr);
3713
+ const members = await resolveBytesArg(membersArg, "--members", true);
3714
+ const { proof, alias } = ringProve(ringExponent, entropy, members, context, message);
3715
+ const result = {
3716
+ account,
3717
+ context: contextStr,
3718
+ ringExponent,
3719
+ alias: toHex2(alias),
3720
+ proof: toHex2(proof)
3721
+ };
3722
+ if (isJsonOutput(opts)) {
3723
+ console.log(formatJson(result));
3724
+ } else {
3725
+ printHeading("Ring-VRF Proof");
3726
+ console.log(` ${BOLD}Account:${RESET} ${account}`);
3727
+ console.log(` ${BOLD}Context:${RESET} ${contextStr}`);
3728
+ console.log(` ${BOLD}Ring exponent:${RESET} ${ringExponent}`);
3729
+ console.log(` ${BOLD}Alias:${RESET} ${result.alias}`);
3730
+ console.log(` ${BOLD}Proof:${RESET} ${result.proof}`);
3731
+ console.log();
3732
+ }
3733
+ }
3734
+ async function verifyCmd(opts) {
3735
+ const proofArg = requireOption(opts.proof, "--proof", "verify");
3736
+ const contextStr = requireOption(opts.context, "--context", "verify");
3737
+ if (!opts.members && !opts.root) {
3738
+ throw new CliError("dot verifiable verify requires --members or --root.");
3739
+ }
3740
+ if (opts.members && opts.root) {
3741
+ throw new CliError("Provide either --members or --root, not both.");
3742
+ }
3743
+ const message = await resolveMessage(opts);
3744
+ const ringExponent = resolveRingExponent(opts);
3745
+ const proof = await resolveBytesArg(proofArg, "--proof", true);
3746
+ const context = encodeContext(contextStr);
3747
+ const source = opts.root ? { commitment: await resolveBytesArg(opts.root, "--root", true) } : { members: await resolveBytesArg(opts.members, "--members", true) };
3748
+ let aliasHex;
3749
+ try {
3750
+ aliasHex = toHex2(verifyRingProof(ringExponent, proof, source, context, message));
3751
+ } catch (err) {
3752
+ const exponentHint = opts.ringExponent === undefined ? ` (assumed ring exponent ${ringExponent} — pass --ring-exponent if the ring uses 10 or 14)` : "";
3753
+ throw new CliError(`Ring-VRF proof is invalid: ${err instanceof Error ? err.message : String(err)}${exponentHint}`);
3754
+ }
3755
+ if (isJsonOutput(opts)) {
3756
+ console.log(formatJson({ valid: true, alias: aliasHex, ringExponent }));
3757
+ } else {
3758
+ printHeading("Ring-VRF Verification");
3759
+ console.log(` ${BOLD}Valid:${RESET} yes`);
3760
+ console.log(` ${BOLD}Alias:${RESET} ${aliasHex}`);
3761
+ console.log();
3762
+ }
3763
+ }
3764
+ async function verifySigCmd(opts) {
3765
+ const sigArg = requireOption(opts.signature, "--signature", "verify-sig");
3766
+ const memberArg = requireOption(opts.member, "--member", "verify-sig");
3767
+ const message = await resolveMessage(opts);
3768
+ const signature = await resolveBytesArg(sigArg, "--signature", true);
3769
+ const member = await resolveBytesArg(memberArg, "--member");
3770
+ const valid = verifyBandersnatchSig(signature, message, member);
3771
+ if (!valid) {
3772
+ throw new CliError("Bandersnatch signature is invalid.");
3773
+ }
3774
+ if (isJsonOutput(opts)) {
3775
+ console.log(formatJson({ valid: true }));
3776
+ } else {
3777
+ printHeading("Bandersnatch Signature Verification");
3778
+ console.log(` ${BOLD}Valid:${RESET} yes`);
3779
+ console.log();
3780
+ }
3781
+ }
3782
+ async function membersCmd(rest, opts) {
3783
+ if (rest.length === 0) {
3784
+ throw new CliError("dot verifiable members requires one or more 0x-hex member keys.");
3785
+ }
3786
+ const memberBytes = rest.map((k) => {
3787
+ const bytes = parseInputData(k);
3788
+ if (bytes.length !== 32) {
3789
+ throw new CliError(`member key must be 32 bytes (got ${bytes.length}): ${k}`);
3790
+ }
3791
+ return bytes;
3792
+ });
3793
+ const encoded = toHex2(encodeMembers(memberBytes));
3794
+ if (isJsonOutput(opts)) {
3795
+ console.log(formatJson({ count: rest.length, members: encoded }));
3796
+ } else {
3797
+ printHeading("Encoded Members");
3798
+ console.log(` ${BOLD}Count:${RESET} ${rest.length}`);
3799
+ console.log(` ${BOLD}Members:${RESET} ${encoded}`);
3800
+ console.log();
3801
+ }
3802
+ }
3803
+ var init_commands = __esm(() => {
3804
+ init_platform();
3805
+ init_lib();
3806
+ });
3807
+
3306
3808
  // src/completions/complete.ts
3307
3809
  var exports_complete = {};
3308
3810
  __export(exports_complete, {
@@ -3664,7 +4166,16 @@ var init_complete = __esm(() => {
3664
4166
  ext: "extensions",
3665
4167
  rpc: "rpc"
3666
4168
  };
3667
- NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "sign", "completions"];
4169
+ NAMED_COMMANDS = [
4170
+ "chain",
4171
+ "account",
4172
+ "inspect",
4173
+ "hash",
4174
+ "sign",
4175
+ "completions",
4176
+ "init",
4177
+ "which"
4178
+ ];
3668
4179
  CHAIN_SUBCOMMANDS = ["add", "info", "list", "remove", "update"];
3669
4180
  ACCOUNT_SUBCOMMANDS = [
3670
4181
  "add",
@@ -3696,7 +4207,7 @@ var init_complete = __esm(() => {
3696
4207
  // src/cli.ts
3697
4208
  import cac from "cac";
3698
4209
  // package.json
3699
- var version = "1.20.0";
4210
+ var version = "1.21.0";
3700
4211
 
3701
4212
  // src/commands/account.ts
3702
4213
  init_accounts_store();
@@ -3943,7 +4454,7 @@ async function accountCreate(name, opts) {
3943
4454
  const { mnemonic, publicKey } = createNewAccount(path);
3944
4455
  const hexPub = publicKeyToHex(publicKey);
3945
4456
  const address = toSs58(publicKey);
3946
- const { deriveBandersnatchMember: deriveBandersnatchMember2 } = await Promise.resolve().then(() => (init_bandersnatch(), exports_bandersnatch));
4457
+ const { deriveBandersnatchMember: deriveBandersnatchMember2 } = await Promise.resolve().then(() => (init_lib(), exports_lib));
3947
4458
  const bandersnatch = {};
3948
4459
  bandersnatch[""] = publicKeyToHex(deriveBandersnatchMember2(mnemonic));
3949
4460
  bandersnatch.candidate = publicKeyToHex(deriveBandersnatchMember2(mnemonic, "candidate"));
@@ -5600,7 +6111,7 @@ function registerCompletionsCommand(cli) {
5600
6111
  process.exit(1);
5601
6112
  }
5602
6113
  const instructions = SETUP_INSTRUCTIONS[shell];
5603
- if (instructions) {
6114
+ if (instructions && process.stdout.isTTY) {
5604
6115
  process.stderr.write(`${instructions}
5605
6116
  `);
5606
6117
  }
@@ -6381,30 +6892,9 @@ async function handleExtensions(target, opts) {
6381
6892
 
6382
6893
  // src/commands/hash.ts
6383
6894
  init_hash();
6895
+ init_input();
6384
6896
  init_output();
6385
6897
  init_errors();
6386
- import { readFile as readFile5 } from "node:fs/promises";
6387
- async function resolveInput(data, opts) {
6388
- const sources = [data !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
6389
- if (sources > 1) {
6390
- throw new CliError("Provide only one of: inline data, --file, or --stdin");
6391
- }
6392
- if (sources === 0) {
6393
- throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
6394
- }
6395
- if (opts.file) {
6396
- const buf = await readFile5(opts.file);
6397
- return new Uint8Array(buf);
6398
- }
6399
- if (opts.stdin) {
6400
- const chunks = [];
6401
- for await (const chunk of process.stdin) {
6402
- chunks.push(chunk);
6403
- }
6404
- return new Uint8Array(Buffer.concat(chunks));
6405
- }
6406
- return parseInputData(data);
6407
- }
6408
6898
  function printAlgorithmHelp() {
6409
6899
  console.log(`${BOLD}Usage:${RESET} dot hash <algorithm> <data> [options]
6410
6900
  `);
@@ -6433,7 +6923,7 @@ function registerHashCommand(cli) {
6433
6923
  if (!isValidAlgorithm(algorithm)) {
6434
6924
  throw new CliError(suggestMessage("algorithm", algorithm, getAlgorithmNames()));
6435
6925
  }
6436
- const input = await resolveInput(data, opts);
6926
+ const input = await resolveDataInput(data, opts);
6437
6927
  const hash = computeHash(algorithm, input);
6438
6928
  const hexHash = toHex2(hash);
6439
6929
  if (isJsonOutput(opts)) {
@@ -7339,9 +7829,9 @@ async function handleRpc(method, args, opts) {
7339
7829
  // src/commands/sign.ts
7340
7830
  init_accounts();
7341
7831
  init_hash();
7832
+ init_input();
7342
7833
  init_output();
7343
7834
  init_errors();
7344
- import { readFile as readFile6 } from "node:fs/promises";
7345
7835
  var SUPPORTED_TYPES = ["sr25519"];
7346
7836
  function isSupportedType(type) {
7347
7837
  return SUPPORTED_TYPES.includes(type.toLowerCase());
@@ -7352,27 +7842,6 @@ function variantName(type) {
7352
7842
  return "Sr25519";
7353
7843
  }
7354
7844
  }
7355
- async function resolveInput2(data, opts) {
7356
- const sources = [data !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
7357
- if (sources > 1) {
7358
- throw new CliError("Provide only one of: inline data, --file, or --stdin");
7359
- }
7360
- if (sources === 0) {
7361
- throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
7362
- }
7363
- if (opts.file) {
7364
- const buf = await readFile6(opts.file);
7365
- return new Uint8Array(buf);
7366
- }
7367
- if (opts.stdin) {
7368
- const chunks = [];
7369
- for await (const chunk of process.stdin) {
7370
- chunks.push(chunk);
7371
- }
7372
- return new Uint8Array(Buffer.concat(chunks));
7373
- }
7374
- return parseInputData(data);
7375
- }
7376
7845
  function printSignHelp() {
7377
7846
  console.log(`${BOLD}Usage:${RESET} dot sign <message> --from <account> [options]
7378
7847
  `);
@@ -7406,7 +7875,7 @@ function registerSignCommand(cli) {
7406
7875
  if (!isSupportedType(type)) {
7407
7876
  throw new CliError(`Unsupported signature type "${opts.type}". Supported: ${SUPPORTED_TYPES.join(", ")}`);
7408
7877
  }
7409
- const input = await resolveInput2(message, opts);
7878
+ const input = await resolveDataInput(message, opts);
7410
7879
  const keypair = await resolveAccountKeypair(opts.from);
7411
7880
  const signature = keypair.sign(input);
7412
7881
  const hexMessage = toHex2(input);
@@ -8778,7 +9247,7 @@ function buildGeneralTx(meta, callData, userExtOverrides) {
8778
9247
  }
8779
9248
  function watchTransaction(observable, level, options) {
8780
9249
  const spinner = new Spinner;
8781
- return new Promise((resolve, reject) => {
9250
+ return new Promise((resolve2, reject) => {
8782
9251
  let settled = false;
8783
9252
  spinner.start(options?.unsigned ? "Submitting..." : "Signing...");
8784
9253
  const subscription = observable.subscribe({
@@ -8798,7 +9267,7 @@ function watchTransaction(observable, level, options) {
8798
9267
  spinner.succeed("Broadcasted");
8799
9268
  settled = true;
8800
9269
  subscription.unsubscribe();
8801
- resolve(event);
9270
+ resolve2(event);
8802
9271
  } else {
8803
9272
  spinner.succeed("Broadcasted");
8804
9273
  spinner.start("In best block...");
@@ -8810,7 +9279,7 @@ function watchTransaction(observable, level, options) {
8810
9279
  spinner.succeed(`In best block #${event.block.number}`);
8811
9280
  settled = true;
8812
9281
  subscription.unsubscribe();
8813
- resolve(event);
9282
+ resolve2(event);
8814
9283
  } else {
8815
9284
  spinner.succeed(`In best block #${event.block.number}`);
8816
9285
  spinner.start("Finalizing...");
@@ -8822,7 +9291,7 @@ function watchTransaction(observable, level, options) {
8822
9291
  case "finalized":
8823
9292
  spinner.succeed(`Finalized in block #${event.block.number}`);
8824
9293
  settled = true;
8825
- resolve(event);
9294
+ resolve2(event);
8826
9295
  break;
8827
9296
  }
8828
9297
  },
@@ -8836,7 +9305,7 @@ function watchTransaction(observable, level, options) {
8836
9305
  });
8837
9306
  }
8838
9307
  function watchTransactionJson(observable, level, options) {
8839
- return new Promise((resolve, reject) => {
9308
+ return new Promise((resolve2, reject) => {
8840
9309
  let settled = false;
8841
9310
  const subscription = observable.subscribe({
8842
9311
  next(event) {
@@ -8853,7 +9322,7 @@ function watchTransactionJson(observable, level, options) {
8853
9322
  if (level === "broadcast") {
8854
9323
  settled = true;
8855
9324
  subscription.unsubscribe();
8856
- resolve(event);
9325
+ resolve2(event);
8857
9326
  }
8858
9327
  break;
8859
9328
  case "txBestBlocksState":
@@ -8862,13 +9331,13 @@ function watchTransactionJson(observable, level, options) {
8862
9331
  if (level === "best-block") {
8863
9332
  settled = true;
8864
9333
  subscription.unsubscribe();
8865
- resolve(event);
9334
+ resolve2(event);
8866
9335
  }
8867
9336
  }
8868
9337
  break;
8869
9338
  case "finalized":
8870
9339
  settled = true;
8871
- resolve(event);
9340
+ resolve2(event);
8872
9341
  break;
8873
9342
  }
8874
9343
  },
@@ -8881,119 +9350,91 @@ function watchTransactionJson(observable, level, options) {
8881
9350
  });
8882
9351
  }
8883
9352
 
8884
- // src/commands/verifiable.ts
8885
- init_accounts_store();
8886
- init_accounts();
8887
- init_bandersnatch();
9353
+ // src/commands/workspace.ts
9354
+ init_store();
9355
+ init_workspace();
8888
9356
  init_output();
8889
- var DEV_PHRASE2 = "bottom drive obey lake curtain smoke basket hold race lonely fit walk";
8890
- var VERIFIABLE_HELP = `
8891
- ${BOLD}Usage:${RESET}
8892
- $ dot verifiable <account> [--context <value>]
8893
-
8894
- ${BOLD}Arguments:${RESET}
8895
- account Account name (stored or dev account)
8896
-
8897
- ${BOLD}Options:${RESET}
8898
- --context <value> Blake2b context for keyed derivation (e.g. "candidate")
8899
-
8900
- ${BOLD}Examples:${RESET}
8901
- $ dot verifiable alice Unkeyed derivation (lite person)
8902
- $ dot verifiable alice --context candidate Keyed with "candidate" (full person)
8903
- $ dot verifiable my-account --context candidate
8904
-
8905
- ${BOLD}How it works:${RESET}
8906
-
8907
- Mnemonic (12/24 words)
8908
- │ mnemonicToEntropy() (raw BIP39 entropy, NOT miniSecret)
8909
-
8910
- blake2b256(entropy, context?) keyed or unkeyed
8911
-
8912
- member_from_entropy() verifiablejs WASM (Bandersnatch curve)
8913
-
8914
- 32-byte member key for on-chain member set registration
8915
- `.trimStart();
8916
- function registerVerifiableCommands(cli) {
8917
- cli.command("verifiable [account]", "Derive Bandersnatch member key from account mnemonic").option("--context <value>", "Blake2b context for keyed derivation (e.g. candidate)").action(async (account, opts) => {
8918
- if (!account) {
8919
- console.log(VERIFIABLE_HELP);
8920
- return;
8921
- }
8922
- return deriveVerifiable(account, opts);
8923
- });
8924
- }
8925
- async function deriveVerifiable(account, opts) {
8926
- const mnemonic = await resolveMnemonic(account);
8927
- const memberKey = deriveBandersnatchMember(mnemonic, opts.context);
8928
- const memberKeyHex = publicKeyToHex(memberKey);
8929
- if (!isDevAccount(account)) {
8930
- const accountsFile = await loadAccounts();
8931
- const stored = findAccount(accountsFile, account);
8932
- if (stored) {
8933
- if (!stored.bandersnatch)
8934
- stored.bandersnatch = {};
8935
- stored.bandersnatch[opts.context ?? ""] = memberKeyHex;
8936
- await saveAccounts(accountsFile);
8937
- }
9357
+ init_errors();
9358
+ import { mkdir as mkdir3, stat } from "node:fs/promises";
9359
+ import { homedir as homedir3 } from "node:os";
9360
+ import { join as join4 } from "node:path";
9361
+ async function initWorkspace(cwd, home = homedir3()) {
9362
+ const dir = canonicalPath(cwd);
9363
+ if (dir === canonicalPath(home)) {
9364
+ throw new CliError(`Cannot initialize a workspace in your home directory — ${join4(dir, WORKSPACE_DIR_NAME)} is the global config root.`);
8938
9365
  }
8939
- if (isJsonOutput(opts)) {
8940
- const result = {
8941
- account,
8942
- memberKey: memberKeyHex
8943
- };
8944
- if (opts.context)
8945
- result.context = opts.context;
8946
- console.log(formatJson(result));
8947
- } else {
8948
- printHeading("Bandersnatch Member Key");
8949
- console.log(` ${BOLD}Account:${RESET} ${account}`);
8950
- if (opts.context)
8951
- console.log(` ${BOLD}Context:${RESET} ${opts.context}`);
8952
- console.log(` ${BOLD}Member Key:${RESET} ${memberKeyHex}`);
8953
- console.log();
9366
+ const workspacePath = join4(dir, WORKSPACE_DIR_NAME);
9367
+ const exists = await stat(workspacePath).then(() => true).catch(() => false);
9368
+ if (exists) {
9369
+ throw new CliError(`A workspace already exists at ${workspacePath}.`);
8954
9370
  }
8955
- }
8956
- async function resolveMnemonic(account) {
8957
- if (isDevAccount(account)) {
8958
- return DEV_PHRASE2;
9371
+ const warnings = [];
9372
+ const parentWorkspace = findWorkspace(dir, home);
9373
+ if (parentWorkspace) {
9374
+ warnings.push(`This workspace shadows ${parentWorkspace} for commands run below ${dir}.`);
8959
9375
  }
8960
- const accountsFile = await loadAccounts();
8961
- const stored = findAccount(accountsFile, account);
8962
- if (!stored) {
8963
- const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
8964
- const suggestions = findClosest(account, available);
8965
- const hint = suggestions.length > 0 ? `
8966
- Did you mean: ${suggestions.join(", ")}?` : "";
8967
- const list = available.map((a) => `
8968
- - ${a}`).join("");
8969
- throw new Error(`Unknown account "${account}".${hint}
8970
- Available accounts:${list}`);
9376
+ const dotHome = process.env.DOT_HOME;
9377
+ if (dotHome && dotHome.length > 0) {
9378
+ warnings.push(`DOT_HOME is set (${dotHome}) and takes precedence — this workspace will not be picked up until you unset it.`);
8971
9379
  }
8972
- if (isWatchOnly(stored)) {
8973
- throw new Error(`Account "${account}" is watch-only (no secret). Cannot derive Bandersnatch key.`);
9380
+ await mkdir3(workspacePath, { recursive: true });
9381
+ return { workspacePath, warnings };
9382
+ }
9383
+ var SOURCE_LABELS = {
9384
+ env: "DOT_HOME environment variable",
9385
+ workspace: "local workspace (discovered from current directory)",
9386
+ global: "global config"
9387
+ };
9388
+ async function handleInit(cwd = process.cwd()) {
9389
+ const result = await initWorkspace(cwd);
9390
+ for (const warning of result.warnings) {
9391
+ process.stderr.write(`Warning: ${warning}
9392
+ `);
8974
9393
  }
8975
- const secret = resolveSecret(stored.secret);
8976
- if (isHexPublicKey(`0x${secret.replace(/^0x/, "")}`)) {
8977
- throw new Error(`Account "${account}" uses a hex seed. Bandersnatch derivation requires a BIP39 mnemonic.`);
9394
+ await writeStdout(`Initialized empty dot workspace at ${result.workspacePath}
9395
+ ` + `Check which workspace is active with: dot which
9396
+ `);
9397
+ }
9398
+ async function handleWhich(opts, cwd = process.cwd()) {
9399
+ const resolved = resolveConfigDir(cwd);
9400
+ if (isJsonOutput(opts)) {
9401
+ await writeStdout(`${JSON.stringify({ path: resolved.path, source: resolved.source })}
9402
+ `);
9403
+ return;
8978
9404
  }
8979
- return secret;
9405
+ await writeStdout(`${resolved.path}
9406
+ Source: ${SOURCE_LABELS[resolved.source]}
9407
+ `);
9408
+ }
9409
+ function registerWorkspaceCommands(cli) {
9410
+ cli.command("init", "Initialize a local .polkadot workspace in the current directory").action(() => handleInit());
9411
+ cli.command("which", "Show the active config root (workspace, DOT_HOME, or global)").action((opts) => handleWhich(opts));
8980
9412
  }
8981
9413
 
8982
9414
  // src/config/store.ts
8983
9415
  init_errors();
8984
9416
  init_types();
8985
- import { access as access3, mkdir as mkdir3, readFile as readFile7, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
8986
- import { homedir as homedir2 } from "node:os";
8987
- import { join as join3 } from "node:path";
8988
- function getConfigDir2() {
9417
+ init_workspace();
9418
+ import { access as access3, mkdir as mkdir4, readFile as readFile6, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
9419
+ import { homedir as homedir4 } from "node:os";
9420
+ import { join as join5 } from "node:path";
9421
+ function resolveConfigDir2(cwd = process.cwd()) {
8989
9422
  const override = process.env.DOT_HOME;
8990
- return override && override.length > 0 ? override : join3(homedir2(), ".polkadot");
9423
+ if (override && override.length > 0)
9424
+ return { path: override, source: "env" };
9425
+ const workspace = findWorkspace(cwd);
9426
+ if (workspace)
9427
+ return { path: workspace, source: "workspace" };
9428
+ return { path: join5(homedir4(), ".polkadot"), source: "global" };
9429
+ }
9430
+ function getConfigDir2() {
9431
+ return resolveConfigDir2().path;
8991
9432
  }
8992
9433
  function getConfigPath2() {
8993
- return join3(getConfigDir2(), "config.json");
9434
+ return join5(getConfigDir2(), "config.json");
8994
9435
  }
8995
9436
  async function ensureDir3(dir) {
8996
- await mkdir3(dir, { recursive: true });
9437
+ await mkdir4(dir, { recursive: true });
8997
9438
  }
8998
9439
  async function fileExists3(path) {
8999
9440
  try {
@@ -9007,7 +9448,7 @@ async function loadConfig2() {
9007
9448
  await ensureDir3(getConfigDir2());
9008
9449
  const configPath = getConfigPath2();
9009
9450
  if (await fileExists3(configPath)) {
9010
- const saved = JSON.parse(await readFile7(configPath, "utf-8"));
9451
+ const saved = JSON.parse(await readFile6(configPath, "utf-8"));
9011
9452
  const chains = {};
9012
9453
  for (const [name, defaultConfig] of Object.entries(DEFAULT_CONFIG.chains)) {
9013
9454
  chains[name] = saved.chains[name] ? { ...defaultConfig, ...saved.chains[name] } : defaultConfig;
@@ -9030,7 +9471,7 @@ async function saveConfig2(config) {
9030
9471
 
9031
9472
  // src/core/file-loader.ts
9032
9473
  init_errors();
9033
- import { access as access4, readFile as readFile8 } from "node:fs/promises";
9474
+ import { access as access4, readFile as readFile7 } from "node:fs/promises";
9034
9475
  import { parse as parseYaml } from "yaml";
9035
9476
  var CATEGORIES = ["tx", "query", "const", "apis"];
9036
9477
  var FILE_EXTENSIONS = [".json", ".yaml", ".yml"];
@@ -9095,7 +9536,7 @@ async function loadCommandFile(filePath, cliVars) {
9095
9536
  } catch {
9096
9537
  throw new CliError(`File not found: ${filePath}`);
9097
9538
  }
9098
- const rawText = await readFile8(filePath, "utf-8");
9539
+ const rawText = await readFile7(filePath, "utf-8");
9099
9540
  if (!rawText.trim()) {
9100
9541
  throw new CliError(`File is empty: ${filePath}`);
9101
9542
  }
@@ -9167,8 +9608,8 @@ async function loadCommandFile(filePath, cliVars) {
9167
9608
  // src/core/update-notifier.ts
9168
9609
  init_store();
9169
9610
  import { readFileSync } from "node:fs";
9170
- import { mkdir as mkdir4, writeFile as writeFile6 } from "node:fs/promises";
9171
- import { join as join4 } from "node:path";
9611
+ import { mkdir as mkdir5, writeFile as writeFile6 } from "node:fs/promises";
9612
+ import { join as join6 } from "node:path";
9172
9613
  var CACHE_FILE = "update-check.json";
9173
9614
  var STALE_MS = 24 * 60 * 60 * 1000;
9174
9615
  var FETCH_TIMEOUT_MS = 5000;
@@ -9224,7 +9665,7 @@ function buildNotificationBox(current, latest) {
9224
9665
  `);
9225
9666
  }
9226
9667
  function getCachePath() {
9227
- return join4(getConfigDir(), CACHE_FILE);
9668
+ return join6(getConfigDir(), CACHE_FILE);
9228
9669
  }
9229
9670
  function readCache() {
9230
9671
  try {
@@ -9236,7 +9677,7 @@ function readCache() {
9236
9677
  }
9237
9678
  async function writeCache(cache) {
9238
9679
  try {
9239
- await mkdir4(getConfigDir(), { recursive: true });
9680
+ await mkdir5(getConfigDir(), { recursive: true });
9240
9681
  await writeFile6(getCachePath(), `${JSON.stringify(cache)}
9241
9682
  `);
9242
9683
  } catch {}
@@ -9264,7 +9705,7 @@ function startBackgroundCheck(currentVersion) {
9264
9705
  async function waitForPendingCheck() {
9265
9706
  if (!pendingCheck)
9266
9707
  return;
9267
- const timeout = new Promise((resolve) => setTimeout(resolve, EXIT_WAIT_TIMEOUT_MS));
9708
+ const timeout = new Promise((resolve2) => setTimeout(resolve2, EXIT_WAIT_TIMEOUT_MS));
9268
9709
  await Promise.race([pendingCheck.catch(() => {}), timeout]);
9269
9710
  pendingCheck = null;
9270
9711
  }
@@ -9284,6 +9725,117 @@ function getUpdateNotification(currentVersion) {
9284
9725
  return null;
9285
9726
  }
9286
9727
 
9728
+ // src/features/verifiable/register.ts
9729
+ init_platform();
9730
+ var VERIFIABLE_HELP = `
9731
+ ${BOLD}Usage:${RESET}
9732
+ $ dot verifiable [account] [--entropy-key <key>] Derive the member key (default action)
9733
+ $ dot verifiable <action> [account] [options]
9734
+
9735
+ ${BOLD}Actions:${RESET}
9736
+ member <account> Derive the Bandersnatch member key (default if omitted)
9737
+ alias <account> Derive the alias for a 32-byte ring context
9738
+ sign <account> Standalone Bandersnatch signature (64 bytes)
9739
+ prove <account> Ring-VRF proof (one_shot) over a members set
9740
+ verify Locally verify a ring-VRF proof against members/root
9741
+ verify-sig Verify a standalone Bandersnatch signature
9742
+ members <key…> SCALE-encode member keys as Vec<[u8;32]>
9743
+
9744
+ verify / verify-sig exit non-zero on failure (the verdict is the exit code);
9745
+ on success they print the recovered alias / {"valid":true}.
9746
+
9747
+ ${BOLD}Key concepts (do not conflate these):${RESET}
9748
+ --entropy-key <text|0xhex>
9749
+ Key mixed into the keyed-blake2b that turns your mnemonic into the
9750
+ Bandersnatch member entropy. Omit for a ${BOLD}lite${RESET} person (unkeyed);
9751
+ use "candidate" for a ${BOLD}full${RESET} person. Must match the key used when the
9752
+ member was recognised on-chain, or you derive a different (unrecognised)
9753
+ member key. It is NOT a derivation path and NOT the ring --context.
9754
+ --context <text|0xhex>
9755
+ The 32-byte ring/proof namespace (e.g. "dotns"), zero-padded right to 32
9756
+ bytes like Solidity bytes32(). Determines the alias. Used by alias/prove/verify.
9757
+
9758
+ ${BOLD}Options:${RESET}
9759
+ --entropy-key <key> Entropy-derivation key (see above)
9760
+ --context <value> 32-byte ring context (alias/prove/verify)
9761
+ --message <data> Message to sign / bind / verify (text or 0x hex)
9762
+ --file <path> Read the message from a file (raw bytes)
9763
+ --stdin Read the message from stdin
9764
+ --members <hex|file> SCALE-encoded Vec<[u8;32]> ring (prove/verify)
9765
+ --root <hex> 768-byte ring root / commitment (verify)
9766
+ --proof <hex> Ring-VRF proof bytes (verify)
9767
+ --signature <hex> Bandersnatch signature (verify-sig)
9768
+ --member <hex> 32-byte member public key (verify-sig)
9769
+ --ring-exponent <n> Ring exponent: 9 (default), 10, or 14
9770
+ --output json Output as JSON
9771
+
9772
+ ${BOLD}Examples:${RESET}
9773
+ $ dot verifiable alice Lite member key
9774
+ $ dot verifiable alice --entropy-key candidate Full member key
9775
+ $ dot verifiable alias alice --entropy-key candidate --context dotns
9776
+ $ dot verifiable sign alice --message "hello" --entropy-key candidate
9777
+ $ dot verifiable prove alice --entropy-key candidate --context dotns \\
9778
+ --message 0x… --members 0x…
9779
+ $ dot verifiable verify --proof 0x… --context dotns --message 0x… --members 0x…
9780
+ $ dot verifiable members 0x… 0x…
9781
+
9782
+ ${BOLD}Derivation flow:${RESET}
9783
+
9784
+ Mnemonic ─BIP39─▶ entropy ─keyed blake2b─▶ member entropy ─▶ member key / secret
9785
+ (key = --entropy-key) │
9786
+ ring proof: one_shot(…, --context, --message)
9787
+ `.trimStart();
9788
+ var RAW_STRING_FLAGS = [
9789
+ ["entropy-key", "entropyKey"],
9790
+ ["context", "context"],
9791
+ ["message", "message"],
9792
+ ["members", "members"],
9793
+ ["root", "root"],
9794
+ ["proof", "proof"],
9795
+ ["signature", "signature"],
9796
+ ["member", "member"]
9797
+ ];
9798
+ function registerVerifiableCommands(cli) {
9799
+ cli.command("verifiable [action] [...rest]", "Bandersnatch member keys, ring-VRF proofs, signing and verification").option("--entropy-key <key>", "Entropy-derivation key (omit = lite, 'candidate' = full)").option("--context <value>", "32-byte ring/proof context (alias/prove/verify)").option("--message <data>", "Message to sign/bind/verify (text or 0x hex)").option("--file <path>", "Read message from a file (raw bytes)").option("--stdin", "Read message from stdin").option("--members <hex|file>", "SCALE-encoded Vec<[u8;32]> ring (prove/verify)").option("--root <hex>", "768-byte ring root/commitment (verify)").option("--proof <hex>", "Ring-VRF proof bytes (verify)").option("--signature <hex>", "Bandersnatch signature (verify-sig)").option("--member <hex>", "32-byte member public key (verify-sig)").option("--ring-exponent <n>", "Ring exponent: 9 (default), 10, or 14").action(async (action, rest, opts) => {
9800
+ if (!action) {
9801
+ console.log(VERIFIABLE_HELP);
9802
+ return;
9803
+ }
9804
+ for (const [flag, key] of RAW_STRING_FLAGS) {
9805
+ const raw = readRawOptionValue(flag);
9806
+ if (raw !== undefined)
9807
+ opts[key] = raw;
9808
+ }
9809
+ const { runVerifiable: runVerifiable2 } = await Promise.resolve().then(() => (init_commands(), exports_commands));
9810
+ return runVerifiable2(action, rest, opts);
9811
+ });
9812
+ }
9813
+
9814
+ // src/platform/cli.ts
9815
+ function registerGlobalOptions(cli) {
9816
+ cli.option("--chain <name>", "Target chain (required)");
9817
+ cli.option("--rpc <url>", "Override RPC endpoint for this call");
9818
+ cli.option("--output <format>", "Output format: pretty or json", {
9819
+ default: "pretty"
9820
+ });
9821
+ cli.option("--json", "Output as JSON (shorthand for --output json)");
9822
+ }
9823
+ function readRawOptionValue2(name, argv = process.argv) {
9824
+ const flag = `--${name}`;
9825
+ const prefix = `${flag}=`;
9826
+ let value;
9827
+ for (let i = 0;i < argv.length; i++) {
9828
+ const arg = argv[i];
9829
+ if (arg === "--")
9830
+ break;
9831
+ if (arg === flag && i + 1 < argv.length)
9832
+ value = argv[i + 1];
9833
+ else if (arg.startsWith(prefix))
9834
+ value = arg.slice(prefix.length);
9835
+ }
9836
+ return value;
9837
+ }
9838
+
9287
9839
  // src/utils/errors.ts
9288
9840
  class CliError2 extends Error {
9289
9841
  constructor(message) {
@@ -9406,15 +9958,7 @@ if (process.argv[2] === "__complete") {
9406
9958
  process.exit(0);
9407
9959
  })();
9408
9960
  } else {
9409
- let readAtFromArgv = function(argv) {
9410
- for (let i = 0;i < argv.length; i++) {
9411
- if (argv[i] === "--at" && i + 1 < argv.length)
9412
- return argv[i + 1];
9413
- if (argv[i].startsWith("--at="))
9414
- return argv[i].slice(5);
9415
- }
9416
- return;
9417
- }, collectVarFlags = function(argv) {
9961
+ let collectVarFlags = function(argv) {
9418
9962
  const vars = [];
9419
9963
  for (let i = 0;i < argv.length; i++) {
9420
9964
  if (argv[i] === "--var" && i + 1 < argv.length) {
@@ -9470,8 +10014,10 @@ if (process.argv[2] === "__complete") {
9470
10014
  console.log(" hash Hash utilities");
9471
10015
  console.log(" sign Sign a message with an account keypair");
9472
10016
  console.log(" parachain Derive parachain sovereign accounts (deprecated \u2014 use `account inspect --parachain`)");
9473
- console.log(" verifiable Derive Bandersnatch member key from mnemonic");
10017
+ console.log(" verifiable Bandersnatch member keys, ring-VRF proofs, sign/verify");
9474
10018
  console.log(" completions <sh> Generate shell completions (zsh, bash, fish)");
10019
+ console.log(" init Initialize a local .polkadot workspace in this directory");
10020
+ console.log(" which Show the active config root (workspace, DOT_HOME, or global)");
9475
10021
  console.log();
9476
10022
  console.log("Global options:");
9477
10023
  console.log(" --chain <name> Target chain (required)");
@@ -9483,12 +10029,7 @@ if (process.argv[2] === "__complete") {
9483
10029
  };
9484
10030
  startBackgroundCheck(version);
9485
10031
  const cli = cac("dot");
9486
- cli.option("--chain <name>", "Target chain (required)");
9487
- cli.option("--rpc <url>", "Override RPC endpoint for this call");
9488
- cli.option("--output <format>", "Output format: pretty or json", {
9489
- default: "pretty"
9490
- });
9491
- cli.option("--json", "Output as JSON (shorthand for --output json)");
10032
+ registerGlobalOptions(cli);
9492
10033
  registerChainCommands(cli);
9493
10034
  registerInspectCommand(cli);
9494
10035
  registerMetadataCommand(cli);
@@ -9498,6 +10039,7 @@ if (process.argv[2] === "__complete") {
9498
10039
  registerParachainCommand(cli);
9499
10040
  registerCompletionsCommand(cli);
9500
10041
  registerVerifiableCommands(cli);
10042
+ registerWorkspaceCommands(cli);
9501
10043
  cli.command("[dotpath] [...args]").option("--from <name>", "Account to sign with (for tx)").option("--dry-run", "Estimate fees without submitting (for tx)").option("--encode", "Encode call to hex without signing (for tx)").option("--to-yaml", "Decode call to YAML file format (for tx)").option("--to-json", "Decode call to JSON file format (for tx)").option("--ext <json>", "Custom signed extension values as JSON (for tx)").option("--asset <json>", "Pay fees in an alternative asset (XCM location JSON, for tx)").option("-w, --wait <level>", "Resolve at: broadcast, best-block (or best), finalized (for tx)", {
9502
10044
  default: "finalized"
9503
10045
  }).option("--dump", "Dump all entries of a storage map (without specifying a key)").option("--var <kv>", "Template variable for file input (KEY=VALUE, repeatable)").option("--nonce <n>", "Custom nonce for manual tx sequencing (for tx)").option("--tip <amount>", "Tip to prioritize transaction (for tx)").option("--mortality <spec>", '"immortal" or period number (for tx)').option("--at <block>", 'Block hash, "best", or "finalized" to read/validate against (tx, query, apis)').option("--unsigned", "Submit as unsigned/bare transaction (no signer required, for tx)").option("--refresh", "Refresh the cached RPC method list from the node (for rpc)").action(async (dotpath, args, opts) => {
@@ -9505,7 +10047,7 @@ if (process.argv[2] === "__complete") {
9505
10047
  printHelp();
9506
10048
  return;
9507
10049
  }
9508
- const atRaw = readAtFromArgv(process.argv);
10050
+ const atRaw = readRawOptionValue2("at");
9509
10051
  if (isFilePath(dotpath)) {
9510
10052
  const cliVars = collectVarFlags(process.argv);
9511
10053
  const cmd = await loadCommandFile(dotpath, cliVars);