polkadot-cli 1.20.0 → 1.22.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 +157 -31
  2. package/dist/cli.mjs +874 -256
  3. package/package.json +6 -7
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,155 @@ 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/platform/cli.ts
921
+ function withHelp(command, printHelp) {
922
+ command[HELP_PRINTER] = printHelp ?? (() => command.outputHelp());
923
+ return command;
924
+ }
925
+ function readRawOptionValue(name, argv = process.argv) {
926
+ const flag = `--${name}`;
927
+ const prefix = `${flag}=`;
928
+ let value;
929
+ for (let i = 0;i < argv.length; i++) {
930
+ const arg = argv[i];
931
+ if (arg === "--")
932
+ break;
933
+ if (arg === flag && i + 1 < argv.length)
934
+ value = argv[i + 1];
935
+ else if (arg.startsWith(prefix))
936
+ value = arg.slice(prefix.length);
937
+ }
938
+ return value;
939
+ }
940
+ var HELP_PRINTER;
941
+ var init_cli = __esm(() => {
942
+ HELP_PRINTER = Symbol.for("polkadot-cli.helpPrinter");
943
+ });
944
+
945
+ // src/features/verifiable/lib.ts
946
+ var exports_lib = {};
947
+ __export(exports_lib, {
948
+ verifyRingProof: () => verifyRingProof,
949
+ verifyBandersnatchSig: () => verifyBandersnatchSig,
950
+ ringRoot: () => ringRoot,
951
+ ringProve: () => ringProve,
952
+ resolveEntropyKey: () => resolveEntropyKey,
953
+ isRingExponent: () => isRingExponent,
954
+ encodeMembers: () => encodeMembers,
955
+ encodeContext: () => encodeContext,
956
+ deriveMemberKey: () => deriveMemberKey,
957
+ deriveMemberEntropy: () => deriveMemberEntropy,
958
+ deriveBandersnatchMember: () => deriveBandersnatchMember,
959
+ deriveAlias: () => deriveAlias,
960
+ compactEncode: () => compactEncode,
961
+ bandersnatchSign: () => bandersnatchSign,
962
+ DEFAULT_RING_EXPONENT: () => DEFAULT_RING_EXPONENT
871
963
  });
872
964
  import { blake2b } from "@noble/hashes/blake2.js";
965
+ import { hexToBytes as hexToBytes3 } from "@noble/hashes/utils.js";
966
+ import { compact } from "@polkadot-api/substrate-bindings";
873
967
  import { mnemonicToEntropy as mnemonicToEntropy2 } from "@polkadot-labs/hdkd-helpers";
874
- import { member_from_entropy } from "verifiablejs/nodejs";
875
- function deriveBandersnatchMember(mnemonic, context) {
968
+ import {
969
+ alias_in_context,
970
+ member_from_entropy,
971
+ members_root,
972
+ one_shot,
973
+ sign as sign2,
974
+ validate,
975
+ validate_with_commitment,
976
+ verify_signature
977
+ } from "verifiablejs/nodejs";
978
+ function isRingExponent(n) {
979
+ return RING_EXPONENTS.includes(n);
980
+ }
981
+ function textOrHexBytes(value, label) {
982
+ if (value.startsWith("0x")) {
983
+ const hex = value.slice(2);
984
+ if (hex.length % 2 !== 0) {
985
+ throw new Error(`Invalid hex ${label}: odd number of characters`);
986
+ }
987
+ return hexToBytes3(hex);
988
+ }
989
+ return new TextEncoder().encode(value);
990
+ }
991
+ function resolveEntropyKey(value) {
992
+ if (value === undefined || value === "")
993
+ return;
994
+ return textOrHexBytes(value, "entropy-key");
995
+ }
996
+ function deriveMemberEntropy(mnemonic, entropyKey) {
876
997
  const entropy = mnemonicToEntropy2(mnemonic);
877
998
  const opts = { dkLen: 32 };
878
- if (context) {
879
- opts.key = new TextEncoder().encode(context);
999
+ if (entropyKey !== undefined && entropyKey.length > 0) {
1000
+ opts.key = entropyKey;
1001
+ }
1002
+ return blake2b(entropy, opts);
1003
+ }
1004
+ function deriveMemberKey(entropy) {
1005
+ return member_from_entropy(entropy);
1006
+ }
1007
+ function deriveBandersnatchMember(mnemonic, entropyKey) {
1008
+ return deriveMemberKey(deriveMemberEntropy(mnemonic, resolveEntropyKey(entropyKey)));
1009
+ }
1010
+ function deriveAlias(entropy, context) {
1011
+ return alias_in_context(entropy, context);
1012
+ }
1013
+ function bandersnatchSign(entropy, message) {
1014
+ return sign2(entropy, message);
1015
+ }
1016
+ function verifyBandersnatchSig(signature, message, member) {
1017
+ return verify_signature(signature, message, member);
1018
+ }
1019
+ function ringProve(ringExp, entropy, members, context, message) {
1020
+ const result = one_shot(ringExp, entropy, members, context, message);
1021
+ return { proof: result.proof, alias: result.alias };
1022
+ }
1023
+ function verifyRingProof(ringExp, proof, source, context, message) {
1024
+ if (source.commitment !== undefined) {
1025
+ return validate_with_commitment(ringExp, proof, source.commitment, context, message);
1026
+ }
1027
+ if (source.members !== undefined) {
1028
+ return validate(ringExp, proof, source.members, context, message);
1029
+ }
1030
+ throw new Error("verifyRingProof requires either `members` or `commitment`");
1031
+ }
1032
+ function ringRoot(ringExp, members) {
1033
+ return members_root(ringExp, members);
1034
+ }
1035
+ function compactEncode(n) {
1036
+ if (!Number.isInteger(n) || n < 0)
1037
+ throw new Error("compactEncode: non-negative integer required");
1038
+ return compact.enc(n);
1039
+ }
1040
+ function encodeMembers(members) {
1041
+ for (const m of members) {
1042
+ if (m.length !== 32) {
1043
+ throw new Error(`member key must be 32 bytes (got ${m.length})`);
1044
+ }
1045
+ }
1046
+ const prefix = compactEncode(members.length);
1047
+ const out = new Uint8Array(prefix.length + members.length * 32);
1048
+ out.set(prefix, 0);
1049
+ let offset = prefix.length;
1050
+ for (const m of members) {
1051
+ out.set(m, offset);
1052
+ offset += 32;
1053
+ }
1054
+ return out;
1055
+ }
1056
+ function encodeContext(input) {
1057
+ const bytes = textOrHexBytes(input, "context");
1058
+ if (bytes.length > 32) {
1059
+ throw new Error(`Context must be at most 32 bytes (got ${bytes.length})`);
880
1060
  }
881
- const hashed = blake2b(entropy, opts);
882
- return member_from_entropy(hashed);
1061
+ const out = new Uint8Array(32);
1062
+ out.set(bytes, 0);
1063
+ return out;
883
1064
  }
884
- var init_bandersnatch = () => {};
1065
+ var RING_EXPONENTS, DEFAULT_RING_EXPONENT = 9;
1066
+ var init_lib = __esm(() => {
1067
+ RING_EXPONENTS = [9, 10, 14];
1068
+ });
885
1069
 
886
1070
  // src/core/client.ts
887
1071
  import { createClient } from "polkadot-api";
@@ -1007,9 +1191,9 @@ function compactEntry(entry, color) {
1007
1191
  }
1008
1192
  }
1009
1193
  function expandEntry(entry, indent, width, color, prefix = 0) {
1010
- const compact = compactEntry(entry, color);
1011
- if (visualWidth(compact) + indent + prefix <= width)
1012
- return compact;
1194
+ const compact2 = compactEntry(entry, color);
1195
+ if (visualWidth(compact2) + indent + prefix <= width)
1196
+ return compact2;
1013
1197
  switch (entry.type) {
1014
1198
  case "struct":
1015
1199
  return expandStruct(entry.value, indent, width, color);
@@ -1043,7 +1227,7 @@ ${closePadding}>`;
1043
1227
  case "lookupEntry":
1044
1228
  return expandEntry(entry.value, indent, width, color);
1045
1229
  default:
1046
- return compact;
1230
+ return compact2;
1047
1231
  }
1048
1232
  }
1049
1233
  function expandStruct(fields, indent, width, color) {
@@ -1102,9 +1286,9 @@ ${closePadding}${close}`;
1102
1286
  }
1103
1287
  function prettyType(entry, opts = {}) {
1104
1288
  const { indent, prefix, width, color } = resolveOpts(opts);
1105
- const compact = compactEntry(entry, color);
1106
- if (visualWidth(compact) + indent + prefix <= width)
1107
- return compact;
1289
+ const compact2 = compactEntry(entry, color);
1290
+ if (visualWidth(compact2) + indent + prefix <= width)
1291
+ return compact2;
1108
1292
  return expandEntry(entry, indent, width, color);
1109
1293
  }
1110
1294
  function prettyTypeById(lookup, typeId, opts = {}) {
@@ -1208,15 +1392,15 @@ function renderArgsFromFields(fields, opts) {
1208
1392
  case "void":
1209
1393
  return "()";
1210
1394
  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;
1395
+ const compact2 = `(${fields.fields.map(([k, v]) => `${paint(color, CYAN2, k)}: ${compactEntry(v, color)}`).join(", ")})`;
1396
+ if (visualWidth(compact2) + lead <= width)
1397
+ return compact2;
1214
1398
  return renderFieldList(fields.fields, "(", ")", indent, width, color);
1215
1399
  }
1216
1400
  case "positional": {
1217
- const compact = `(${fields.types.map((t) => compactEntry(t, color)).join(", ")})`;
1218
- if (visualWidth(compact) + lead <= width)
1219
- return compact;
1401
+ const compact2 = `(${fields.types.map((t) => compactEntry(t, color)).join(", ")})`;
1402
+ if (visualWidth(compact2) + lead <= width)
1403
+ return compact2;
1220
1404
  const innerIndent = indent + 2;
1221
1405
  const padding = " ".repeat(innerIndent);
1222
1406
  const closePadding = " ".repeat(indent);
@@ -1227,9 +1411,9 @@ ${lines.join(`,
1227
1411
  ${closePadding})`;
1228
1412
  }
1229
1413
  case "single": {
1230
- const compact = `(${compactEntry(fields.type, color)})`;
1231
- if (visualWidth(compact) + lead <= width)
1232
- return compact;
1414
+ const compact2 = `(${compactEntry(fields.type, color)})`;
1415
+ if (visualWidth(compact2) + lead <= width)
1416
+ return compact2;
1233
1417
  const inner = expandEntry(fields.type, indent + 2, width, color);
1234
1418
  return `(
1235
1419
  ${" ".repeat(indent + 2)}${inner},
@@ -1300,7 +1484,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
1300
1484
  let bytes;
1301
1485
  try {
1302
1486
  const hex = await withTimeout(client._request("state_call", ["Metadata_metadata_at_version", v15Arg]), chainName);
1303
- const raw = hexToBytes3(hex);
1487
+ const raw = hexToBytes4(hex);
1304
1488
  const decoded = optionalOpaqueBytes.dec(raw);
1305
1489
  if (decoded !== undefined) {
1306
1490
  bytes = new Uint8Array(decoded);
@@ -1309,7 +1493,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
1309
1493
  if (!bytes) {
1310
1494
  try {
1311
1495
  const hex = await withTimeout(client._request("state_getMetadata", []), chainName);
1312
- bytes = hexToBytes3(hex);
1496
+ bytes = hexToBytes4(hex);
1313
1497
  } catch (err) {
1314
1498
  if (err instanceof ConnectionError)
1315
1499
  throw err;
@@ -1505,7 +1689,7 @@ function describeCallArgs(meta, palletName, callName) {
1505
1689
  function describeEventFields(meta, palletName, eventName) {
1506
1690
  return compactArgsString(getEventFields(meta, palletName, eventName));
1507
1691
  }
1508
- function hexToBytes3(hex) {
1692
+ function hexToBytes4(hex) {
1509
1693
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
1510
1694
  const bytes = new Uint8Array(clean.length / 2);
1511
1695
  for (let i = 0;i < clean.length; i += 2) {
@@ -3234,7 +3418,7 @@ var init_xxh64 = __esm(() => {
3234
3418
  import { blake2b as blake2b2 } from "@noble/hashes/blake2.js";
3235
3419
  import { sha256 } from "@noble/hashes/sha2.js";
3236
3420
  import { keccak_256 as keccak_2562 } from "@noble/hashes/sha3.js";
3237
- import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes4 } from "@noble/hashes/utils.js";
3421
+ import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes5 } from "@noble/hashes/utils.js";
3238
3422
  function computeHash(algorithm, data) {
3239
3423
  const algo = ALGORITHMS[algorithm];
3240
3424
  if (!algo) {
@@ -3248,7 +3432,7 @@ function parseInputData(input) {
3248
3432
  if (hex.length % 2 !== 0) {
3249
3433
  throw new Error(`Invalid hex input: odd number of characters`);
3250
3434
  }
3251
- return hexToBytes4(hex);
3435
+ return hexToBytes5(hex);
3252
3436
  }
3253
3437
  return new TextEncoder().encode(input);
3254
3438
  }
@@ -3303,6 +3487,333 @@ var init_hash = __esm(() => {
3303
3487
  };
3304
3488
  });
3305
3489
 
3490
+ // src/core/input.ts
3491
+ import { readFile as readFile5 } from "node:fs/promises";
3492
+ async function resolveDataInput(inline, opts, messages) {
3493
+ const sources = [inline !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
3494
+ if (sources > 1) {
3495
+ throw new CliError(messages?.conflict ?? "Provide only one of: inline data, --file, or --stdin");
3496
+ }
3497
+ if (sources === 0) {
3498
+ throw new CliError(messages?.missing ?? "No input provided. Pass data as argument, or use --file or --stdin");
3499
+ }
3500
+ if (opts.file) {
3501
+ const buf = await readFile5(opts.file);
3502
+ return new Uint8Array(buf);
3503
+ }
3504
+ if (opts.stdin) {
3505
+ const chunks = [];
3506
+ for await (const chunk of process.stdin) {
3507
+ chunks.push(chunk);
3508
+ }
3509
+ return new Uint8Array(Buffer.concat(chunks));
3510
+ }
3511
+ return parseInputData(inline);
3512
+ }
3513
+ var init_input = __esm(() => {
3514
+ init_errors();
3515
+ init_hash();
3516
+ });
3517
+
3518
+ // src/platform/index.ts
3519
+ var init_platform = __esm(() => {
3520
+ init_accounts_store();
3521
+ init_accounts();
3522
+ init_hash();
3523
+ init_input();
3524
+ init_output();
3525
+ init_errors();
3526
+ init_cli();
3527
+ });
3528
+
3529
+ // src/features/verifiable/commands.ts
3530
+ var exports_commands = {};
3531
+ __export(exports_commands, {
3532
+ runVerifiable: () => runVerifiable
3533
+ });
3534
+ import { readFile as readFile8 } from "node:fs/promises";
3535
+ import { DEV_PHRASE as DEV_PHRASE2 } from "@polkadot-labs/hdkd-helpers";
3536
+ async function runVerifiable(action, rest, opts) {
3537
+ switch (action) {
3538
+ case "member":
3539
+ return deriveMember(rest[0], opts);
3540
+ case "alias":
3541
+ return deriveAliasCmd(rest[0], opts);
3542
+ case "sign":
3543
+ return signCmd(rest[0], opts);
3544
+ case "prove":
3545
+ return proveCmd(rest[0], opts);
3546
+ case "verify":
3547
+ return verifyCmd(opts);
3548
+ case "verify-sig":
3549
+ return verifySigCmd(opts);
3550
+ case "members":
3551
+ return membersCmd(rest, opts);
3552
+ default:
3553
+ return deriveMember(action, opts);
3554
+ }
3555
+ }
3556
+ function mnemonicFromStored(stored, account, accountsFile) {
3557
+ if (!stored) {
3558
+ const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
3559
+ const suggestions = findClosest(account, available);
3560
+ const hint = suggestions.length > 0 ? `
3561
+ Did you mean: ${suggestions.join(", ")}?` : "";
3562
+ const list = available.map((a) => `
3563
+ - ${a}`).join("");
3564
+ throw new Error(`Unknown account "${account}".${hint}
3565
+ Available accounts:${list}`);
3566
+ }
3567
+ if (isWatchOnly(stored)) {
3568
+ throw new Error(`Account "${account}" is watch-only (no secret). Cannot derive Bandersnatch key.`);
3569
+ }
3570
+ const secret = resolveSecret(stored.secret);
3571
+ if (isHexPublicKey(`0x${secret.replace(/^0x/, "")}`)) {
3572
+ throw new Error(`Account "${account}" uses a hex seed. Bandersnatch derivation requires a BIP39 mnemonic.`);
3573
+ }
3574
+ return secret;
3575
+ }
3576
+ async function resolveMnemonic(account) {
3577
+ if (isDevAccount(account)) {
3578
+ return DEV_PHRASE2;
3579
+ }
3580
+ const accountsFile = await loadAccounts();
3581
+ return mnemonicFromStored(findAccount(accountsFile, account), account, accountsFile);
3582
+ }
3583
+ async function resolveEntropy(account, entropyKey) {
3584
+ const mnemonic = await resolveMnemonic(account);
3585
+ return deriveMemberEntropy(mnemonic, resolveEntropyKey(entropyKey));
3586
+ }
3587
+ function requireAccount(account, action) {
3588
+ if (!account) {
3589
+ throw new CliError(`dot verifiable ${action} requires an account. See "dot verifiable".`);
3590
+ }
3591
+ return account;
3592
+ }
3593
+ function resolveMessage(opts) {
3594
+ return resolveDataInput(opts.message, opts, {
3595
+ conflict: "Provide only one of: --message, --file, or --stdin",
3596
+ missing: "No message provided. Use --message, --file, or --stdin"
3597
+ });
3598
+ }
3599
+ async function resolveBytesArg(value, name, allowFile = false) {
3600
+ if (value.startsWith("0x")) {
3601
+ return parseInputData(value);
3602
+ }
3603
+ if (allowFile) {
3604
+ const buf = await readFile8(value);
3605
+ const text = buf.toString("utf8").trim();
3606
+ return text.startsWith("0x") ? parseInputData(text) : new Uint8Array(buf);
3607
+ }
3608
+ throw new CliError(`${name} must be 0x-prefixed hex`);
3609
+ }
3610
+ function resolveRingExponent(opts) {
3611
+ if (opts.ringExponent === undefined)
3612
+ return DEFAULT_RING_EXPONENT;
3613
+ const n = Number(opts.ringExponent);
3614
+ if (!isRingExponent(n)) {
3615
+ throw new CliError(`Invalid --ring-exponent "${opts.ringExponent}". Supported: 9, 10, 14.`);
3616
+ }
3617
+ return n;
3618
+ }
3619
+ function requireOption(value, flag, action) {
3620
+ if (value === undefined) {
3621
+ throw new CliError(`dot verifiable ${action} requires ${flag}.`);
3622
+ }
3623
+ return value;
3624
+ }
3625
+ async function deriveMember(accountArg, opts) {
3626
+ const account = requireAccount(accountArg, "member");
3627
+ const usedDeprecatedContext = opts.entropyKey === undefined && opts.context !== undefined;
3628
+ if (usedDeprecatedContext) {
3629
+ if (opts.context.startsWith("0x")) {
3630
+ 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.`);
3631
+ }
3632
+ 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.
3633
+ `);
3634
+ }
3635
+ const entropyKeyStr = opts.entropyKey ?? opts.context;
3636
+ let mnemonic;
3637
+ let accountsFile;
3638
+ let stored;
3639
+ if (isDevAccount(account)) {
3640
+ mnemonic = DEV_PHRASE2;
3641
+ } else {
3642
+ accountsFile = await loadAccounts();
3643
+ stored = findAccount(accountsFile, account);
3644
+ mnemonic = mnemonicFromStored(stored, account, accountsFile);
3645
+ }
3646
+ const memberKeyHex = publicKeyToHex(deriveBandersnatchMember(mnemonic, entropyKeyStr));
3647
+ if (stored && accountsFile) {
3648
+ if (!stored.bandersnatch)
3649
+ stored.bandersnatch = {};
3650
+ const entryKey = entropyKeyStr ?? "";
3651
+ if (stored.bandersnatch[entryKey] !== memberKeyHex) {
3652
+ stored.bandersnatch[entryKey] = memberKeyHex;
3653
+ await saveAccounts(accountsFile);
3654
+ }
3655
+ }
3656
+ const fieldKey = usedDeprecatedContext ? "context" : "entropyKey";
3657
+ if (isJsonOutput(opts)) {
3658
+ const result = { account, memberKey: memberKeyHex };
3659
+ if (entropyKeyStr)
3660
+ result[fieldKey] = entropyKeyStr;
3661
+ console.log(formatJson(result));
3662
+ } else {
3663
+ printHeading("Bandersnatch Member Key");
3664
+ console.log(` ${BOLD}Account:${RESET} ${account}`);
3665
+ if (entropyKeyStr) {
3666
+ const line = usedDeprecatedContext ? ` ${BOLD}Context:${RESET} ${entropyKeyStr}` : ` ${BOLD}Entropy Key:${RESET} ${entropyKeyStr}`;
3667
+ console.log(line);
3668
+ }
3669
+ console.log(` ${BOLD}Member Key:${RESET} ${memberKeyHex}`);
3670
+ console.log();
3671
+ }
3672
+ }
3673
+ async function deriveAliasCmd(accountArg, opts) {
3674
+ const account = requireAccount(accountArg, "alias");
3675
+ const contextStr = requireOption(opts.context, "--context", "alias");
3676
+ const entropy = await resolveEntropy(account, opts.entropyKey);
3677
+ const context = encodeContext(contextStr);
3678
+ const aliasHex = toHex2(deriveAlias(entropy, context));
3679
+ if (isJsonOutput(opts)) {
3680
+ console.log(formatJson({ account, context: contextStr, alias: aliasHex }));
3681
+ } else {
3682
+ printHeading("Verifiable Alias");
3683
+ console.log(` ${BOLD}Account:${RESET} ${account}`);
3684
+ console.log(` ${BOLD}Context:${RESET} ${contextStr}`);
3685
+ console.log(` ${BOLD}Alias:${RESET} ${aliasHex}`);
3686
+ console.log();
3687
+ }
3688
+ }
3689
+ async function signCmd(accountArg, opts) {
3690
+ const account = requireAccount(accountArg, "sign");
3691
+ const message = await resolveMessage(opts);
3692
+ const entropy = await resolveEntropy(account, opts.entropyKey);
3693
+ const signature = bandersnatchSign(entropy, message);
3694
+ const member = deriveMemberKey(entropy);
3695
+ const sigHex = toHex2(signature);
3696
+ const result = {
3697
+ type: "Bandersnatch",
3698
+ account,
3699
+ message: toHex2(message),
3700
+ signature: sigHex,
3701
+ member: toHex2(member),
3702
+ enum: `Bandersnatch(${sigHex})`
3703
+ };
3704
+ if (isJsonOutput(opts)) {
3705
+ console.log(formatJson(result));
3706
+ } else {
3707
+ console.log(` ${BOLD}Type:${RESET} ${result.type}`);
3708
+ console.log(` ${BOLD}Message:${RESET} ${result.message}`);
3709
+ console.log(` ${BOLD}Signature:${RESET} ${result.signature}`);
3710
+ console.log(` ${BOLD}Member:${RESET} ${result.member}`);
3711
+ console.log(` ${BOLD}Enum:${RESET} ${result.enum}`);
3712
+ }
3713
+ }
3714
+ async function proveCmd(accountArg, opts) {
3715
+ const account = requireAccount(accountArg, "prove");
3716
+ const contextStr = requireOption(opts.context, "--context", "prove");
3717
+ const membersArg = requireOption(opts.members, "--members", "prove");
3718
+ const message = await resolveMessage(opts);
3719
+ const ringExponent = resolveRingExponent(opts);
3720
+ const entropy = await resolveEntropy(account, opts.entropyKey);
3721
+ const context = encodeContext(contextStr);
3722
+ const members = await resolveBytesArg(membersArg, "--members", true);
3723
+ const { proof, alias } = ringProve(ringExponent, entropy, members, context, message);
3724
+ const result = {
3725
+ account,
3726
+ context: contextStr,
3727
+ ringExponent,
3728
+ alias: toHex2(alias),
3729
+ proof: toHex2(proof)
3730
+ };
3731
+ if (isJsonOutput(opts)) {
3732
+ console.log(formatJson(result));
3733
+ } else {
3734
+ printHeading("Ring-VRF Proof");
3735
+ console.log(` ${BOLD}Account:${RESET} ${account}`);
3736
+ console.log(` ${BOLD}Context:${RESET} ${contextStr}`);
3737
+ console.log(` ${BOLD}Ring exponent:${RESET} ${ringExponent}`);
3738
+ console.log(` ${BOLD}Alias:${RESET} ${result.alias}`);
3739
+ console.log(` ${BOLD}Proof:${RESET} ${result.proof}`);
3740
+ console.log();
3741
+ }
3742
+ }
3743
+ async function verifyCmd(opts) {
3744
+ const proofArg = requireOption(opts.proof, "--proof", "verify");
3745
+ const contextStr = requireOption(opts.context, "--context", "verify");
3746
+ if (!opts.members && !opts.root) {
3747
+ throw new CliError("dot verifiable verify requires --members or --root.");
3748
+ }
3749
+ if (opts.members && opts.root) {
3750
+ throw new CliError("Provide either --members or --root, not both.");
3751
+ }
3752
+ const message = await resolveMessage(opts);
3753
+ const ringExponent = resolveRingExponent(opts);
3754
+ const proof = await resolveBytesArg(proofArg, "--proof", true);
3755
+ const context = encodeContext(contextStr);
3756
+ const source = opts.root ? { commitment: await resolveBytesArg(opts.root, "--root", true) } : { members: await resolveBytesArg(opts.members, "--members", true) };
3757
+ let aliasHex;
3758
+ try {
3759
+ aliasHex = toHex2(verifyRingProof(ringExponent, proof, source, context, message));
3760
+ } catch (err) {
3761
+ const exponentHint = opts.ringExponent === undefined ? ` (assumed ring exponent ${ringExponent} — pass --ring-exponent if the ring uses 10 or 14)` : "";
3762
+ throw new CliError(`Ring-VRF proof is invalid: ${err instanceof Error ? err.message : String(err)}${exponentHint}`);
3763
+ }
3764
+ if (isJsonOutput(opts)) {
3765
+ console.log(formatJson({ valid: true, alias: aliasHex, ringExponent }));
3766
+ } else {
3767
+ printHeading("Ring-VRF Verification");
3768
+ console.log(` ${BOLD}Valid:${RESET} yes`);
3769
+ console.log(` ${BOLD}Alias:${RESET} ${aliasHex}`);
3770
+ console.log();
3771
+ }
3772
+ }
3773
+ async function verifySigCmd(opts) {
3774
+ const sigArg = requireOption(opts.signature, "--signature", "verify-sig");
3775
+ const memberArg = requireOption(opts.member, "--member", "verify-sig");
3776
+ const message = await resolveMessage(opts);
3777
+ const signature = await resolveBytesArg(sigArg, "--signature", true);
3778
+ const member = await resolveBytesArg(memberArg, "--member");
3779
+ const valid = verifyBandersnatchSig(signature, message, member);
3780
+ if (!valid) {
3781
+ throw new CliError("Bandersnatch signature is invalid.");
3782
+ }
3783
+ if (isJsonOutput(opts)) {
3784
+ console.log(formatJson({ valid: true }));
3785
+ } else {
3786
+ printHeading("Bandersnatch Signature Verification");
3787
+ console.log(` ${BOLD}Valid:${RESET} yes`);
3788
+ console.log();
3789
+ }
3790
+ }
3791
+ async function membersCmd(rest, opts) {
3792
+ if (rest.length === 0) {
3793
+ throw new CliError("dot verifiable members requires one or more 0x-hex member keys.");
3794
+ }
3795
+ const memberBytes = rest.map((k) => {
3796
+ const bytes = parseInputData(k);
3797
+ if (bytes.length !== 32) {
3798
+ throw new CliError(`member key must be 32 bytes (got ${bytes.length}): ${k}`);
3799
+ }
3800
+ return bytes;
3801
+ });
3802
+ const encoded = toHex2(encodeMembers(memberBytes));
3803
+ if (isJsonOutput(opts)) {
3804
+ console.log(formatJson({ count: rest.length, members: encoded }));
3805
+ } else {
3806
+ printHeading("Encoded Members");
3807
+ console.log(` ${BOLD}Count:${RESET} ${rest.length}`);
3808
+ console.log(` ${BOLD}Members:${RESET} ${encoded}`);
3809
+ console.log();
3810
+ }
3811
+ }
3812
+ var init_commands = __esm(() => {
3813
+ init_platform();
3814
+ init_lib();
3815
+ });
3816
+
3306
3817
  // src/completions/complete.ts
3307
3818
  var exports_complete = {};
3308
3819
  __export(exports_complete, {
@@ -3664,7 +4175,16 @@ var init_complete = __esm(() => {
3664
4175
  ext: "extensions",
3665
4176
  rpc: "rpc"
3666
4177
  };
3667
- NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "sign", "completions"];
4178
+ NAMED_COMMANDS = [
4179
+ "chain",
4180
+ "account",
4181
+ "inspect",
4182
+ "hash",
4183
+ "sign",
4184
+ "completions",
4185
+ "init",
4186
+ "which"
4187
+ ];
3668
4188
  CHAIN_SUBCOMMANDS = ["add", "info", "list", "remove", "update"];
3669
4189
  ACCOUNT_SUBCOMMANDS = [
3670
4190
  "add",
@@ -3696,7 +4216,7 @@ var init_complete = __esm(() => {
3696
4216
  // src/cli.ts
3697
4217
  import cac from "cac";
3698
4218
  // package.json
3699
- var version = "1.20.0";
4219
+ var version = "1.22.0";
3700
4220
 
3701
4221
  // src/commands/account.ts
3702
4222
  init_accounts_store();
@@ -3830,6 +4350,7 @@ function isValidParaId(value) {
3830
4350
  }
3831
4351
 
3832
4352
  // src/commands/account.ts
4353
+ init_cli();
3833
4354
  var ACCOUNT_HELP = `
3834
4355
  ${BOLD}Usage:${RESET}
3835
4356
  $ dot account add <name> <ss58|hex> Add a watch-only address (no secret)
@@ -3846,7 +4367,7 @@ ${BOLD}Usage:${RESET}
3846
4367
  $ dot account inspect --pallet-id <id> [--prefix <N>] Derive a pallet sovereign (no save — script-friendly)
3847
4368
  $ dot account inspect --parachain <id> --parachain-type <t> Derive a parachain sovereign (no save — script-friendly)
3848
4369
  $ dot account list List all accounts
3849
- $ dot account remove|delete <name> [name2] ... Remove stored account(s)
4370
+ $ dot account remove|delete|rm <name> [name2] ... Remove stored account(s)
3850
4371
 
3851
4372
  ${BOLD}Examples:${RESET}
3852
4373
  $ dot account add treasury 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
@@ -3884,7 +4405,7 @@ ${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
3884
4405
  Raw private keys cannot be HD-derived, so --path is rejected for them.${RESET}
3885
4406
  `.trimStart();
3886
4407
  function registerAccountCommands(cli) {
3887
- cli.command("account [action] [...names]", "Manage local accounts (create, import, list, remove, export)").alias("accounts").option("--secret <value>", "Secret for import: BIP39 mnemonic, 0x 32-byte hex seed, or 0x 64-byte raw private key").option("--env <varName>", "Environment variable name holding the secret").option("--path <derivation>", "Derivation path (e.g. //staking, //polkadot//0/wallet)").option("--parachain <id>", "Derive a parachain sovereign account (requires --parachain-type)").option("--parachain-type <type>", "Parachain sovereign type: child or sibling").option("--pallet-id <id>", "Derive a pallet sovereign account from an 8-byte PalletId").option("--prefix <number>", "SS58 prefix for address encoding (default: 42)").option("--file <path>", "Input/output file for batch import/export").option("--overwrite", "Overwrite existing accounts on batch import").option("--dry-run", "Preview batch import without applying changes").option("--include-secrets", "Include secrets in export (redacted by default)").option("--watch-only", "Export only watch-only accounts").option("--show-secret", "Reveal the 64-byte sr25519 expanded private key (inspect only)").action(async (action, names, opts) => {
4408
+ const command = cli.command("account [action] [...names]", "Manage local accounts (create, import, list, remove, export)").alias("accounts").option("--secret <value>", "Secret for import: BIP39 mnemonic, 0x 32-byte hex seed, or 0x 64-byte raw private key").option("--env <varName>", "Environment variable name holding the secret").option("--path <derivation>", "Derivation path (e.g. //staking, //polkadot//0/wallet)").option("--parachain <id>", "Derive a parachain sovereign account (requires --parachain-type)").option("--parachain-type <type>", "Parachain sovereign type: child or sibling").option("--pallet-id <id>", "Derive a pallet sovereign account from an 8-byte PalletId").option("--prefix <number>", "SS58 prefix for address encoding (default: 42)").option("--file <path>", "Input/output file for batch import/export").option("--overwrite", "Overwrite existing accounts on batch import").option("--dry-run", "Preview batch import without applying changes").option("--include-secrets", "Include secrets in export (redacted by default)").option("--watch-only", "Export only watch-only accounts").option("--show-secret", "Reveal the 64-byte sr25519 expanded private key (inspect only)").action(async (action, names, opts) => {
3888
4409
  if (!action) {
3889
4410
  if (process.argv[2] === "accounts")
3890
4411
  return accountList(opts);
@@ -3917,6 +4438,7 @@ function registerAccountCommands(cli) {
3917
4438
  return accountList(opts);
3918
4439
  case "delete":
3919
4440
  case "remove":
4441
+ case "rm":
3920
4442
  return accountRemove(names, opts);
3921
4443
  case "inspect":
3922
4444
  return accountInspect(names[0], opts);
@@ -3924,6 +4446,7 @@ function registerAccountCommands(cli) {
3924
4446
  return accountInspect(action, opts);
3925
4447
  }
3926
4448
  });
4449
+ withHelp(command, () => console.log(ACCOUNT_HELP));
3927
4450
  }
3928
4451
  async function accountCreate(name, opts) {
3929
4452
  if (!name) {
@@ -3943,7 +4466,7 @@ async function accountCreate(name, opts) {
3943
4466
  const { mnemonic, publicKey } = createNewAccount(path);
3944
4467
  const hexPub = publicKeyToHex(publicKey);
3945
4468
  const address = toSs58(publicKey);
3946
- const { deriveBandersnatchMember: deriveBandersnatchMember2 } = await Promise.resolve().then(() => (init_bandersnatch(), exports_bandersnatch));
4469
+ const { deriveBandersnatchMember: deriveBandersnatchMember2 } = await Promise.resolve().then(() => (init_lib(), exports_lib));
3947
4470
  const bandersnatch = {};
3948
4471
  bandersnatch[""] = publicKeyToHex(deriveBandersnatchMember2(mnemonic));
3949
4472
  bandersnatch.candidate = publicKeyToHex(deriveBandersnatchMember2(mnemonic, "candidate"));
@@ -5017,6 +5540,7 @@ async function fetchRpcMethods(rpcUrl) {
5017
5540
 
5018
5541
  // src/commands/chain.ts
5019
5542
  init_rpc_registry();
5543
+ init_cli();
5020
5544
  var CHAIN_HELP = `
5021
5545
  ${BOLD}Usage:${RESET}
5022
5546
  $ dot chain add <name> --rpc <url> Add a chain via WebSocket RPC
@@ -5050,7 +5574,7 @@ ${BOLD}Examples:${RESET}
5050
5574
  $ dot chain import my-chains.json --no-metadata
5051
5575
  `.trimStart();
5052
5576
  function registerChainCommands(cli) {
5053
- cli.command("chain [action] [...names]", "Manage chains (add, remove, update, list, export, import)").alias("chains").option("--all", "Update/export all configured chains").option("--relay <name>", "Parent relay chain for this parachain").option("--parachain-id <id>", "Parachain ID (auto-detected if omitted with --relay)").option("--file <path>", "Output/input file for export/import").option("--overwrite", "Overwrite existing chains on import").option("--dry-run", "Preview import without applying changes").option("--no-metadata", "Skip automatic metadata fetch after import").option("-v, --verbose", "Show RPC endpoints in `chains` list output").action(async (action, names, opts) => {
5577
+ const command = cli.command("chain [action] [...names]", "Manage chains (add, remove, update, list, export, import)").alias("chains").option("--all", "Update/export all configured chains").option("--relay <name>", "Parent relay chain for this parachain").option("--parachain-id <id>", "Parachain ID (auto-detected if omitted with --relay)").option("--file <path>", "Output/input file for export/import").option("--overwrite", "Overwrite existing chains on import").option("--dry-run", "Preview import without applying changes").option("--no-metadata", "Skip automatic metadata fetch after import").option("-v, --verbose", "Show RPC endpoints in `chains` list output").action(async (action, names, opts) => {
5054
5578
  if (!action) {
5055
5579
  if (process.argv[2] === "chains")
5056
5580
  return chainList(opts);
@@ -5084,6 +5608,7 @@ function registerChainCommands(cli) {
5084
5608
  }
5085
5609
  }
5086
5610
  });
5611
+ withHelp(command, () => console.log(CHAIN_HELP));
5087
5612
  }
5088
5613
  async function refreshRpcMethods(chainName, rpcUrl, silent = false) {
5089
5614
  try {
@@ -5525,6 +6050,7 @@ async function chainImport(filePath, opts) {
5525
6050
  }
5526
6051
 
5527
6052
  // src/commands/completions.ts
6053
+ init_cli();
5528
6054
  var ZSH_SCRIPT = `_dot_completions() {
5529
6055
  emulate -L zsh
5530
6056
  local -a completions
@@ -5593,19 +6119,20 @@ var SCRIPTS = {
5593
6119
  fish: FISH_SCRIPT
5594
6120
  };
5595
6121
  function registerCompletionsCommand(cli) {
5596
- cli.command("completions <shell>", "Generate shell completion script (zsh, bash, fish)").action((shell) => {
6122
+ const command = cli.command("completions <shell>", "Generate shell completion script (zsh, bash, fish)").action((shell) => {
5597
6123
  const script = SCRIPTS[shell];
5598
6124
  if (!script) {
5599
6125
  console.error(`Unsupported shell "${shell}". Supported: ${Object.keys(SCRIPTS).join(", ")}`);
5600
6126
  process.exit(1);
5601
6127
  }
5602
6128
  const instructions = SETUP_INSTRUCTIONS[shell];
5603
- if (instructions) {
6129
+ if (instructions && process.stdout.isTTY) {
5604
6130
  process.stderr.write(`${instructions}
5605
6131
  `);
5606
6132
  }
5607
6133
  console.log(script);
5608
6134
  });
6135
+ withHelp(command);
5609
6136
  }
5610
6137
 
5611
6138
  // src/commands/const.ts
@@ -6381,30 +6908,10 @@ async function handleExtensions(target, opts) {
6381
6908
 
6382
6909
  // src/commands/hash.ts
6383
6910
  init_hash();
6911
+ init_input();
6384
6912
  init_output();
6913
+ init_cli();
6385
6914
  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
6915
  function printAlgorithmHelp() {
6409
6916
  console.log(`${BOLD}Usage:${RESET} dot hash <algorithm> <data> [options]
6410
6917
  `);
@@ -6425,7 +6932,7 @@ ${BOLD}Examples:${RESET}`);
6425
6932
  console.log(` ${DIM}$ echo -n "hello" | dot hash sha256 --stdin${RESET}`);
6426
6933
  }
6427
6934
  function registerHashCommand(cli) {
6428
- cli.command("hash [algorithm] [data]", "Compute cryptographic hashes").option("--file <path>", "Hash file contents (raw bytes)").option("--stdin", "Read data from stdin").action(async (algorithm, data, opts) => {
6935
+ const command = cli.command("hash [algorithm] [data]", "Compute cryptographic hashes").option("--file <path>", "Hash file contents (raw bytes)").option("--stdin", "Read data from stdin").action(async (algorithm, data, opts) => {
6429
6936
  if (!algorithm) {
6430
6937
  printAlgorithmHelp();
6431
6938
  return;
@@ -6433,7 +6940,7 @@ function registerHashCommand(cli) {
6433
6940
  if (!isValidAlgorithm(algorithm)) {
6434
6941
  throw new CliError(suggestMessage("algorithm", algorithm, getAlgorithmNames()));
6435
6942
  }
6436
- const input = await resolveInput(data, opts);
6943
+ const input = await resolveDataInput(data, opts);
6437
6944
  const hash = computeHash(algorithm, input);
6438
6945
  const hexHash = toHex2(hash);
6439
6946
  if (isJsonOutput(opts)) {
@@ -6446,6 +6953,7 @@ function registerHashCommand(cli) {
6446
6953
  console.log(hexHash);
6447
6954
  }
6448
6955
  });
6956
+ withHelp(command, printAlgorithmHelp);
6449
6957
  }
6450
6958
 
6451
6959
  // src/commands/inspect.ts
@@ -6454,6 +6962,7 @@ init_client();
6454
6962
  init_metadata();
6455
6963
  init_output();
6456
6964
  init_pretty_type();
6965
+ init_cli();
6457
6966
 
6458
6967
  // src/utils/parse-target.ts
6459
6968
  function parseTarget(input, options) {
@@ -6506,7 +7015,7 @@ function resolveTargetChain(target, chainFlag) {
6506
7015
 
6507
7016
  // src/commands/inspect.ts
6508
7017
  function registerInspectCommand(cli) {
6509
- cli.command("inspect [target]", "Inspect chain metadata (pallets, storage, constants, calls, events, errors)").alias("explore").option("--chain <name>", "Target chain").option("--rpc <url>", "Override RPC endpoint").action(async (target, opts) => {
7018
+ const command = cli.command("inspect [target]", "Inspect chain metadata (pallets, storage, constants, calls, events, errors)").alias("explore").option("--chain <name>", "Target chain").option("--rpc <url>", "Override RPC endpoint").action(async (target, opts) => {
6510
7019
  const config = await loadConfig();
6511
7020
  const knownChains = Object.keys(config.chains);
6512
7021
  let effectiveChain = opts.chain;
@@ -6874,6 +7383,7 @@ function registerInspectCommand(cli) {
6874
7383
  ];
6875
7384
  throw new Error(suggestMessage(`item in ${pallet.name}`, itemName, allItems));
6876
7385
  });
7386
+ withHelp(command);
6877
7387
  }
6878
7388
 
6879
7389
  // src/commands/metadata.ts
@@ -6881,6 +7391,7 @@ init_store();
6881
7391
  init_client();
6882
7392
  init_metadata();
6883
7393
  init_output();
7394
+ init_cli();
6884
7395
  init_errors();
6885
7396
  function buildMetadataPayload(chainName, meta, fingerprint) {
6886
7397
  return {
@@ -6923,12 +7434,14 @@ async function handleMetadata(chain, opts) {
6923
7434
  `);
6924
7435
  }
6925
7436
  function registerMetadataCommand(cli) {
6926
- cli.command("metadata <chain>", "Fetch chain metadata (decoded JSON; --raw for SCALE hex)").option("--raw", "Print SCALE-encoded metadata bytes as hex instead of decoded JSON").option("--cached", "Use cached metadata instead of fetching fresh from the chain").option("--rpc <url>", "Override RPC endpoint(s)").action((chain, opts) => handleMetadata(chain, opts));
7437
+ const command = cli.command("metadata <chain>", "Fetch chain metadata (decoded JSON; --raw for SCALE hex)").option("--raw", "Print SCALE-encoded metadata bytes as hex instead of decoded JSON").option("--cached", "Use cached metadata instead of fetching fresh from the chain").option("--rpc <url>", "Override RPC endpoint(s)").action((chain, opts) => handleMetadata(chain, opts));
7438
+ withHelp(command);
6927
7439
  }
6928
7440
 
6929
7441
  // src/commands/parachain.ts
6930
7442
  init_accounts();
6931
7443
  init_output();
7444
+ init_cli();
6932
7445
  init_errors();
6933
7446
  function printParachainHelp() {
6934
7447
  console.log(`${BOLD}Usage:${RESET} dot parachain <paraId> [options]
@@ -6957,7 +7470,7 @@ function validateType(type) {
6957
7470
  throw new CliError(`Unknown account type "${type}". Valid types: child, sibling.`);
6958
7471
  }
6959
7472
  function registerParachainCommand(cli) {
6960
- cli.command("parachain [paraId]", "Derive parachain sovereign accounts").option("--type <type>", "Account type: child, sibling (default: both)").option("--prefix <number>", "SS58 prefix for address encoding (default: 42)").action(async (paraIdStr, opts) => {
7473
+ const command = cli.command("parachain [paraId]", "Derive parachain sovereign accounts").option("--type <type>", "Account type: child, sibling (default: both)").option("--prefix <number>", "SS58 prefix for address encoding (default: 42)").action(async (paraIdStr, opts) => {
6961
7474
  console.error("Warning: `dot parachain` is deprecated; use `dot account inspect --parachain <id> --parachain-type <child|sibling>` instead. Will be removed in a future release.");
6962
7475
  if (!paraIdStr) {
6963
7476
  printParachainHelp();
@@ -6997,6 +7510,7 @@ function registerParachainCommand(cli) {
6997
7510
  }
6998
7511
  }
6999
7512
  });
7513
+ withHelp(command, printParachainHelp);
7000
7514
  }
7001
7515
 
7002
7516
  // src/commands/query.ts
@@ -7339,9 +7853,10 @@ async function handleRpc(method, args, opts) {
7339
7853
  // src/commands/sign.ts
7340
7854
  init_accounts();
7341
7855
  init_hash();
7856
+ init_input();
7342
7857
  init_output();
7858
+ init_cli();
7343
7859
  init_errors();
7344
- import { readFile as readFile6 } from "node:fs/promises";
7345
7860
  var SUPPORTED_TYPES = ["sr25519"];
7346
7861
  function isSupportedType(type) {
7347
7862
  return SUPPORTED_TYPES.includes(type.toLowerCase());
@@ -7352,27 +7867,6 @@ function variantName(type) {
7352
7867
  return "Sr25519";
7353
7868
  }
7354
7869
  }
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
7870
  function printSignHelp() {
7377
7871
  console.log(`${BOLD}Usage:${RESET} dot sign <message> --from <account> [options]
7378
7872
  `);
@@ -7394,7 +7888,7 @@ ${BOLD}Examples:${RESET}`);
7394
7888
  console.log(` ${DIM}$ dot sign "hello" --from alice --output json${RESET}`);
7395
7889
  }
7396
7890
  function registerSignCommand(cli) {
7397
- cli.command("sign [message]", "Sign a message with an account keypair").option("--from <name>", "Account to sign with").option("--type <algo>", "Signature type (default: sr25519)").option("--file <path>", "Sign file contents (raw bytes)").option("--stdin", "Read data from stdin").action(async (message, opts) => {
7891
+ const command = cli.command("sign [message]", "Sign a message with an account keypair").option("--from <name>", "Account to sign with").option("--type <algo>", "Signature type (default: sr25519)").option("--file <path>", "Sign file contents (raw bytes)").option("--stdin", "Read data from stdin").action(async (message, opts) => {
7398
7892
  if (!message && !opts.file && !opts.stdin) {
7399
7893
  printSignHelp();
7400
7894
  return;
@@ -7406,7 +7900,7 @@ function registerSignCommand(cli) {
7406
7900
  if (!isSupportedType(type)) {
7407
7901
  throw new CliError(`Unsupported signature type "${opts.type}". Supported: ${SUPPORTED_TYPES.join(", ")}`);
7408
7902
  }
7409
- const input = await resolveInput2(message, opts);
7903
+ const input = await resolveDataInput(message, opts);
7410
7904
  const keypair = await resolveAccountKeypair(opts.from);
7411
7905
  const signature = keypair.sign(input);
7412
7906
  const hexMessage = toHex2(input);
@@ -7428,6 +7922,7 @@ function registerSignCommand(cli) {
7428
7922
  console.log(` ${BOLD}Enum:${RESET} ${result.enum}`);
7429
7923
  }
7430
7924
  });
7925
+ withHelp(command, printSignHelp);
7431
7926
  }
7432
7927
 
7433
7928
  // src/commands/tx.ts
@@ -8778,7 +9273,7 @@ function buildGeneralTx(meta, callData, userExtOverrides) {
8778
9273
  }
8779
9274
  function watchTransaction(observable, level, options) {
8780
9275
  const spinner = new Spinner;
8781
- return new Promise((resolve, reject) => {
9276
+ return new Promise((resolve2, reject) => {
8782
9277
  let settled = false;
8783
9278
  spinner.start(options?.unsigned ? "Submitting..." : "Signing...");
8784
9279
  const subscription = observable.subscribe({
@@ -8798,7 +9293,7 @@ function watchTransaction(observable, level, options) {
8798
9293
  spinner.succeed("Broadcasted");
8799
9294
  settled = true;
8800
9295
  subscription.unsubscribe();
8801
- resolve(event);
9296
+ resolve2(event);
8802
9297
  } else {
8803
9298
  spinner.succeed("Broadcasted");
8804
9299
  spinner.start("In best block...");
@@ -8810,7 +9305,7 @@ function watchTransaction(observable, level, options) {
8810
9305
  spinner.succeed(`In best block #${event.block.number}`);
8811
9306
  settled = true;
8812
9307
  subscription.unsubscribe();
8813
- resolve(event);
9308
+ resolve2(event);
8814
9309
  } else {
8815
9310
  spinner.succeed(`In best block #${event.block.number}`);
8816
9311
  spinner.start("Finalizing...");
@@ -8822,7 +9317,7 @@ function watchTransaction(observable, level, options) {
8822
9317
  case "finalized":
8823
9318
  spinner.succeed(`Finalized in block #${event.block.number}`);
8824
9319
  settled = true;
8825
- resolve(event);
9320
+ resolve2(event);
8826
9321
  break;
8827
9322
  }
8828
9323
  },
@@ -8836,7 +9331,7 @@ function watchTransaction(observable, level, options) {
8836
9331
  });
8837
9332
  }
8838
9333
  function watchTransactionJson(observable, level, options) {
8839
- return new Promise((resolve, reject) => {
9334
+ return new Promise((resolve2, reject) => {
8840
9335
  let settled = false;
8841
9336
  const subscription = observable.subscribe({
8842
9337
  next(event) {
@@ -8853,7 +9348,7 @@ function watchTransactionJson(observable, level, options) {
8853
9348
  if (level === "broadcast") {
8854
9349
  settled = true;
8855
9350
  subscription.unsubscribe();
8856
- resolve(event);
9351
+ resolve2(event);
8857
9352
  }
8858
9353
  break;
8859
9354
  case "txBestBlocksState":
@@ -8862,13 +9357,13 @@ function watchTransactionJson(observable, level, options) {
8862
9357
  if (level === "best-block") {
8863
9358
  settled = true;
8864
9359
  subscription.unsubscribe();
8865
- resolve(event);
9360
+ resolve2(event);
8866
9361
  }
8867
9362
  }
8868
9363
  break;
8869
9364
  case "finalized":
8870
9365
  settled = true;
8871
- resolve(event);
9366
+ resolve2(event);
8872
9367
  break;
8873
9368
  }
8874
9369
  },
@@ -8881,119 +9376,94 @@ function watchTransactionJson(observable, level, options) {
8881
9376
  });
8882
9377
  }
8883
9378
 
8884
- // src/commands/verifiable.ts
8885
- init_accounts_store();
8886
- init_accounts();
8887
- init_bandersnatch();
9379
+ // src/commands/workspace.ts
9380
+ init_store();
9381
+ init_workspace();
8888
9382
  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
- }
9383
+ init_cli();
9384
+ init_errors();
9385
+ import { mkdir as mkdir3, stat } from "node:fs/promises";
9386
+ import { homedir as homedir3 } from "node:os";
9387
+ import { join as join4 } from "node:path";
9388
+ async function initWorkspace(cwd, home = homedir3()) {
9389
+ const dir = canonicalPath(cwd);
9390
+ if (dir === canonicalPath(home)) {
9391
+ throw new CliError(`Cannot initialize a workspace in your home directory — ${join4(dir, WORKSPACE_DIR_NAME)} is the global config root.`);
8938
9392
  }
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();
9393
+ const workspacePath = join4(dir, WORKSPACE_DIR_NAME);
9394
+ const exists = await stat(workspacePath).then(() => true).catch(() => false);
9395
+ if (exists) {
9396
+ throw new CliError(`A workspace already exists at ${workspacePath}.`);
8954
9397
  }
8955
- }
8956
- async function resolveMnemonic(account) {
8957
- if (isDevAccount(account)) {
8958
- return DEV_PHRASE2;
9398
+ const warnings = [];
9399
+ const parentWorkspace = findWorkspace(dir, home);
9400
+ if (parentWorkspace) {
9401
+ warnings.push(`This workspace shadows ${parentWorkspace} for commands run below ${dir}.`);
8959
9402
  }
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}`);
9403
+ const dotHome = process.env.DOT_HOME;
9404
+ if (dotHome && dotHome.length > 0) {
9405
+ warnings.push(`DOT_HOME is set (${dotHome}) and takes precedence — this workspace will not be picked up until you unset it.`);
8971
9406
  }
8972
- if (isWatchOnly(stored)) {
8973
- throw new Error(`Account "${account}" is watch-only (no secret). Cannot derive Bandersnatch key.`);
9407
+ await mkdir3(workspacePath, { recursive: true });
9408
+ return { workspacePath, warnings };
9409
+ }
9410
+ var SOURCE_LABELS = {
9411
+ env: "DOT_HOME environment variable",
9412
+ workspace: "local workspace (discovered from current directory)",
9413
+ global: "global config"
9414
+ };
9415
+ async function handleInit(cwd = process.cwd()) {
9416
+ const result = await initWorkspace(cwd);
9417
+ for (const warning of result.warnings) {
9418
+ process.stderr.write(`Warning: ${warning}
9419
+ `);
8974
9420
  }
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.`);
9421
+ await writeStdout(`Initialized empty dot workspace at ${result.workspacePath}
9422
+ ` + `Check which workspace is active with: dot which
9423
+ `);
9424
+ }
9425
+ async function handleWhich(opts, cwd = process.cwd()) {
9426
+ const resolved = resolveConfigDir(cwd);
9427
+ if (isJsonOutput(opts)) {
9428
+ await writeStdout(`${JSON.stringify({ path: resolved.path, source: resolved.source })}
9429
+ `);
9430
+ return;
8978
9431
  }
8979
- return secret;
9432
+ await writeStdout(`${resolved.path}
9433
+ Source: ${SOURCE_LABELS[resolved.source]}
9434
+ `);
9435
+ }
9436
+ function registerWorkspaceCommands(cli) {
9437
+ const initCommand = cli.command("init", "Initialize a local .polkadot workspace in the current directory").action(() => handleInit());
9438
+ withHelp(initCommand);
9439
+ const whichCommand = cli.command("which", "Show the active config root (workspace, DOT_HOME, or global)").action((opts) => handleWhich(opts));
9440
+ withHelp(whichCommand);
8980
9441
  }
8981
9442
 
8982
9443
  // src/config/store.ts
8983
9444
  init_errors();
8984
9445
  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() {
9446
+ init_workspace();
9447
+ import { access as access3, mkdir as mkdir4, readFile as readFile6, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
9448
+ import { homedir as homedir4 } from "node:os";
9449
+ import { join as join5 } from "node:path";
9450
+ function resolveConfigDir2(cwd = process.cwd()) {
8989
9451
  const override = process.env.DOT_HOME;
8990
- return override && override.length > 0 ? override : join3(homedir2(), ".polkadot");
9452
+ if (override && override.length > 0)
9453
+ return { path: override, source: "env" };
9454
+ const workspace = findWorkspace(cwd);
9455
+ if (workspace)
9456
+ return { path: workspace, source: "workspace" };
9457
+ return { path: join5(homedir4(), ".polkadot"), source: "global" };
9458
+ }
9459
+ function getConfigDir2() {
9460
+ return resolveConfigDir2().path;
8991
9461
  }
8992
9462
  function getConfigPath2() {
8993
- return join3(getConfigDir2(), "config.json");
9463
+ return join5(getConfigDir2(), "config.json");
8994
9464
  }
8995
9465
  async function ensureDir3(dir) {
8996
- await mkdir3(dir, { recursive: true });
9466
+ await mkdir4(dir, { recursive: true });
8997
9467
  }
8998
9468
  async function fileExists3(path) {
8999
9469
  try {
@@ -9007,7 +9477,7 @@ async function loadConfig2() {
9007
9477
  await ensureDir3(getConfigDir2());
9008
9478
  const configPath = getConfigPath2();
9009
9479
  if (await fileExists3(configPath)) {
9010
- const saved = JSON.parse(await readFile7(configPath, "utf-8"));
9480
+ const saved = JSON.parse(await readFile6(configPath, "utf-8"));
9011
9481
  const chains = {};
9012
9482
  for (const [name, defaultConfig] of Object.entries(DEFAULT_CONFIG.chains)) {
9013
9483
  chains[name] = saved.chains[name] ? { ...defaultConfig, ...saved.chains[name] } : defaultConfig;
@@ -9028,9 +9498,33 @@ async function saveConfig2(config) {
9028
9498
  `);
9029
9499
  }
9030
9500
 
9501
+ // src/core/dry-run.ts
9502
+ init_output();
9503
+ function isGlobalDryRun() {
9504
+ const raw = process.env.DOT_DRY_RUN?.trim().toLowerCase();
9505
+ if (raw === undefined || raw === "")
9506
+ return false;
9507
+ return raw === "1" || raw === "true" || raw === "yes" || raw === "on";
9508
+ }
9509
+ function resolveDryRun(explicitFlag, decodeOnly = false) {
9510
+ if (explicitFlag !== undefined)
9511
+ return explicitFlag;
9512
+ if (decodeOnly)
9513
+ return false;
9514
+ return isGlobalDryRun();
9515
+ }
9516
+ var hintPrinted = false;
9517
+ function printGlobalDryRunHint() {
9518
+ if (hintPrinted)
9519
+ return;
9520
+ hintPrinted = true;
9521
+ process.stderr.write(`${YELLOW}${BOLD}⚠ DOT_DRY_RUN is set${RESET}${YELLOW} — extrinsics will be simulated, not submitted.${RESET}
9522
+ `);
9523
+ }
9524
+
9031
9525
  // src/core/file-loader.ts
9032
9526
  init_errors();
9033
- import { access as access4, readFile as readFile8 } from "node:fs/promises";
9527
+ import { access as access4, readFile as readFile7 } from "node:fs/promises";
9034
9528
  import { parse as parseYaml } from "yaml";
9035
9529
  var CATEGORIES = ["tx", "query", "const", "apis"];
9036
9530
  var FILE_EXTENSIONS = [".json", ".yaml", ".yml"];
@@ -9095,7 +9589,7 @@ async function loadCommandFile(filePath, cliVars) {
9095
9589
  } catch {
9096
9590
  throw new CliError(`File not found: ${filePath}`);
9097
9591
  }
9098
- const rawText = await readFile8(filePath, "utf-8");
9592
+ const rawText = await readFile7(filePath, "utf-8");
9099
9593
  if (!rawText.trim()) {
9100
9594
  throw new CliError(`File is empty: ${filePath}`);
9101
9595
  }
@@ -9167,8 +9661,8 @@ async function loadCommandFile(filePath, cliVars) {
9167
9661
  // src/core/update-notifier.ts
9168
9662
  init_store();
9169
9663
  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";
9664
+ import { mkdir as mkdir5, writeFile as writeFile6 } from "node:fs/promises";
9665
+ import { join as join6 } from "node:path";
9172
9666
  var CACHE_FILE = "update-check.json";
9173
9667
  var STALE_MS = 24 * 60 * 60 * 1000;
9174
9668
  var FETCH_TIMEOUT_MS = 5000;
@@ -9224,7 +9718,7 @@ function buildNotificationBox(current, latest) {
9224
9718
  `);
9225
9719
  }
9226
9720
  function getCachePath() {
9227
- return join4(getConfigDir(), CACHE_FILE);
9721
+ return join6(getConfigDir(), CACHE_FILE);
9228
9722
  }
9229
9723
  function readCache() {
9230
9724
  try {
@@ -9236,7 +9730,7 @@ function readCache() {
9236
9730
  }
9237
9731
  async function writeCache(cache) {
9238
9732
  try {
9239
- await mkdir4(getConfigDir(), { recursive: true });
9733
+ await mkdir5(getConfigDir(), { recursive: true });
9240
9734
  await writeFile6(getCachePath(), `${JSON.stringify(cache)}
9241
9735
  `);
9242
9736
  } catch {}
@@ -9264,7 +9758,7 @@ function startBackgroundCheck(currentVersion) {
9264
9758
  async function waitForPendingCheck() {
9265
9759
  if (!pendingCheck)
9266
9760
  return;
9267
- const timeout = new Promise((resolve) => setTimeout(resolve, EXIT_WAIT_TIMEOUT_MS));
9761
+ const timeout = new Promise((resolve2) => setTimeout(resolve2, EXIT_WAIT_TIMEOUT_MS));
9268
9762
  await Promise.race([pendingCheck.catch(() => {}), timeout]);
9269
9763
  pendingCheck = null;
9270
9764
  }
@@ -9284,6 +9778,127 @@ function getUpdateNotification(currentVersion) {
9284
9778
  return null;
9285
9779
  }
9286
9780
 
9781
+ // src/features/verifiable/register.ts
9782
+ init_platform();
9783
+ var VERIFIABLE_HELP = `
9784
+ ${BOLD}Usage:${RESET}
9785
+ $ dot verifiable [account] [--entropy-key <key>] Derive the member key (default action)
9786
+ $ dot verifiable <action> [account] [options]
9787
+
9788
+ ${BOLD}Actions:${RESET}
9789
+ member <account> Derive the Bandersnatch member key (default if omitted)
9790
+ alias <account> Derive the alias for a 32-byte ring context
9791
+ sign <account> Standalone Bandersnatch signature (64 bytes)
9792
+ prove <account> Ring-VRF proof (one_shot) over a members set
9793
+ verify Locally verify a ring-VRF proof against members/root
9794
+ verify-sig Verify a standalone Bandersnatch signature
9795
+ members <key…> SCALE-encode member keys as Vec<[u8;32]>
9796
+
9797
+ verify / verify-sig exit non-zero on failure (the verdict is the exit code);
9798
+ on success they print the recovered alias / {"valid":true}.
9799
+
9800
+ ${BOLD}Key concepts (do not conflate these):${RESET}
9801
+ --entropy-key <text|0xhex>
9802
+ Key mixed into the keyed-blake2b that turns your mnemonic into the
9803
+ Bandersnatch member entropy. Omit for a ${BOLD}lite${RESET} person (unkeyed);
9804
+ use "candidate" for a ${BOLD}full${RESET} person. Must match the key used when the
9805
+ member was recognised on-chain, or you derive a different (unrecognised)
9806
+ member key. It is NOT a derivation path and NOT the ring --context.
9807
+ --context <text|0xhex>
9808
+ The 32-byte ring/proof namespace (e.g. "dotns"), zero-padded right to 32
9809
+ bytes like Solidity bytes32(). Determines the alias. Used by alias/prove/verify.
9810
+
9811
+ ${BOLD}Options:${RESET}
9812
+ --entropy-key <key> Entropy-derivation key (see above)
9813
+ --context <value> 32-byte ring context (alias/prove/verify)
9814
+ --message <data> Message to sign / bind / verify (text or 0x hex)
9815
+ --file <path> Read the message from a file (raw bytes)
9816
+ --stdin Read the message from stdin
9817
+ --members <hex|file> SCALE-encoded Vec<[u8;32]> ring (prove/verify)
9818
+ --root <hex> 768-byte ring root / commitment (verify)
9819
+ --proof <hex> Ring-VRF proof bytes (verify)
9820
+ --signature <hex> Bandersnatch signature (verify-sig)
9821
+ --member <hex> 32-byte member public key (verify-sig)
9822
+ --ring-exponent <n> Ring exponent: 9 (default), 10, or 14
9823
+ --output json Output as JSON
9824
+
9825
+ ${BOLD}Examples:${RESET}
9826
+ $ dot verifiable alice Lite member key
9827
+ $ dot verifiable alice --entropy-key candidate Full member key
9828
+ $ dot verifiable alias alice --entropy-key candidate --context dotns
9829
+ $ dot verifiable sign alice --message "hello" --entropy-key candidate
9830
+ $ dot verifiable prove alice --entropy-key candidate --context dotns \\
9831
+ --message 0x… --members 0x…
9832
+ $ dot verifiable verify --proof 0x… --context dotns --message 0x… --members 0x…
9833
+ $ dot verifiable members 0x… 0x…
9834
+
9835
+ ${BOLD}Derivation flow:${RESET}
9836
+
9837
+ Mnemonic ─BIP39─▶ entropy ─keyed blake2b─▶ member entropy ─▶ member key / secret
9838
+ (key = --entropy-key) │
9839
+ ring proof: one_shot(…, --context, --message)
9840
+ `.trimStart();
9841
+ var RAW_STRING_FLAGS = [
9842
+ ["entropy-key", "entropyKey"],
9843
+ ["context", "context"],
9844
+ ["message", "message"],
9845
+ ["members", "members"],
9846
+ ["root", "root"],
9847
+ ["proof", "proof"],
9848
+ ["signature", "signature"],
9849
+ ["member", "member"]
9850
+ ];
9851
+ function registerVerifiableCommands(cli) {
9852
+ const command = 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) => {
9853
+ if (!action) {
9854
+ console.log(VERIFIABLE_HELP);
9855
+ return;
9856
+ }
9857
+ for (const [flag, key] of RAW_STRING_FLAGS) {
9858
+ const raw = readRawOptionValue(flag);
9859
+ if (raw !== undefined)
9860
+ opts[key] = raw;
9861
+ }
9862
+ const { runVerifiable: runVerifiable2 } = await Promise.resolve().then(() => (init_commands(), exports_commands));
9863
+ return runVerifiable2(action, rest, opts);
9864
+ });
9865
+ withHelp(command, () => console.log(VERIFIABLE_HELP));
9866
+ }
9867
+
9868
+ // src/platform/cli.ts
9869
+ var HELP_PRINTER2 = Symbol.for("polkadot-cli.helpPrinter");
9870
+ function printMatchedCommandHelp(cli) {
9871
+ const matched = cli.matchedCommand;
9872
+ const printer = matched?.[HELP_PRINTER2];
9873
+ if (!printer)
9874
+ return false;
9875
+ printer();
9876
+ return true;
9877
+ }
9878
+ function registerGlobalOptions(cli) {
9879
+ cli.option("--chain <name>", "Target chain (required)");
9880
+ cli.option("--rpc <url>", "Override RPC endpoint for this call");
9881
+ cli.option("--output <format>", "Output format: pretty or json", {
9882
+ default: "pretty"
9883
+ });
9884
+ cli.option("--json", "Output as JSON (shorthand for --output json)");
9885
+ }
9886
+ function readRawOptionValue2(name, argv = process.argv) {
9887
+ const flag = `--${name}`;
9888
+ const prefix = `${flag}=`;
9889
+ let value;
9890
+ for (let i = 0;i < argv.length; i++) {
9891
+ const arg = argv[i];
9892
+ if (arg === "--")
9893
+ break;
9894
+ if (arg === flag && i + 1 < argv.length)
9895
+ value = argv[i + 1];
9896
+ else if (arg.startsWith(prefix))
9897
+ value = arg.slice(prefix.length);
9898
+ }
9899
+ return value;
9900
+ }
9901
+
9287
9902
  // src/utils/errors.ts
9288
9903
  class CliError2 extends Error {
9289
9904
  constructor(message) {
@@ -9406,15 +10021,7 @@ if (process.argv[2] === "__complete") {
9406
10021
  process.exit(0);
9407
10022
  })();
9408
10023
  } 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) {
10024
+ let collectVarFlags = function(argv) {
9418
10025
  const vars = [];
9419
10026
  for (let i = 0;i < argv.length; i++) {
9420
10027
  if (argv[i] === "--var" && i + 1 < argv.length) {
@@ -9470,8 +10077,10 @@ if (process.argv[2] === "__complete") {
9470
10077
  console.log(" hash Hash utilities");
9471
10078
  console.log(" sign Sign a message with an account keypair");
9472
10079
  console.log(" parachain Derive parachain sovereign accounts (deprecated \u2014 use `account inspect --parachain`)");
9473
- console.log(" verifiable Derive Bandersnatch member key from mnemonic");
10080
+ console.log(" verifiable Bandersnatch member keys, ring-VRF proofs, sign/verify");
9474
10081
  console.log(" completions <sh> Generate shell completions (zsh, bash, fish)");
10082
+ console.log(" init Initialize a local .polkadot workspace in this directory");
10083
+ console.log(" which Show the active config root (workspace, DOT_HOME, or global)");
9475
10084
  console.log();
9476
10085
  console.log("Global options:");
9477
10086
  console.log(" --chain <name> Target chain (required)");
@@ -9483,12 +10092,7 @@ if (process.argv[2] === "__complete") {
9483
10092
  };
9484
10093
  startBackgroundCheck(version);
9485
10094
  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)");
10095
+ registerGlobalOptions(cli);
9492
10096
  registerChainCommands(cli);
9493
10097
  registerInspectCommand(cli);
9494
10098
  registerMetadataCommand(cli);
@@ -9498,6 +10102,7 @@ if (process.argv[2] === "__complete") {
9498
10102
  registerParachainCommand(cli);
9499
10103
  registerCompletionsCommand(cli);
9500
10104
  registerVerifiableCommands(cli);
10105
+ registerWorkspaceCommands(cli);
9501
10106
  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
10107
  default: "finalized"
9503
10108
  }).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 +10110,7 @@ if (process.argv[2] === "__complete") {
9505
10110
  printHelp();
9506
10111
  return;
9507
10112
  }
9508
- const atRaw = readAtFromArgv(process.argv);
10113
+ const atRaw = readRawOptionValue2("at");
9509
10114
  if (isFilePath(dotpath)) {
9510
10115
  const cliVars = collectVarFlags(process.argv);
9511
10116
  const cmd = await loadCommandFile(dotpath, cliVars);
@@ -9518,12 +10123,17 @@ if (process.argv[2] === "__complete") {
9518
10123
  };
9519
10124
  const target2 = `${cmd.pallet}.${cmd.item}`;
9520
10125
  switch (cmd.category) {
9521
- case "tx":
10126
+ case "tx": {
10127
+ const fileDecodeOnly = Boolean(opts.encode || opts.toYaml || opts.toJson);
10128
+ const fileDryRun = resolveDryRun(opts.dryRun, fileDecodeOnly);
10129
+ if (fileDryRun && isGlobalDryRun() && opts.dryRun === undefined) {
10130
+ printGlobalDryRunHint();
10131
+ }
9522
10132
  await handleTx(target2, args, {
9523
10133
  ...handlerOpts2,
9524
10134
  from: opts.from,
9525
10135
  unsigned: opts.unsigned ?? cmd.unsigned,
9526
- dryRun: opts.dryRun,
10136
+ dryRun: fileDryRun,
9527
10137
  encode: opts.encode,
9528
10138
  toYaml: opts.toYaml,
9529
10139
  toJson: opts.toJson,
@@ -9537,6 +10147,7 @@ if (process.argv[2] === "__complete") {
9537
10147
  parsedArgs: cmd.args
9538
10148
  });
9539
10149
  break;
10150
+ }
9540
10151
  case "query":
9541
10152
  await handleQuery(target2, args, {
9542
10153
  ...handlerOpts2,
@@ -9593,11 +10204,16 @@ if (process.argv[2] === "__complete") {
9593
10204
  await handleQuery(target, args, { ...handlerOpts, dump: opts.dump, at: atRaw });
9594
10205
  break;
9595
10206
  case "tx": {
10207
+ const decodeOnly = Boolean(opts.encode || opts.toYaml || opts.toJson);
10208
+ const dryRun = resolveDryRun(opts.dryRun, decodeOnly);
10209
+ if (dryRun && isGlobalDryRun() && opts.dryRun === undefined) {
10210
+ printGlobalDryRunHint();
10211
+ }
9596
10212
  const txOpts = {
9597
10213
  ...handlerOpts,
9598
10214
  from: opts.from,
9599
10215
  unsigned: opts.unsigned,
9600
- dryRun: opts.dryRun,
10216
+ dryRun,
9601
10217
  encode: opts.encode,
9602
10218
  toYaml: opts.toYaml,
9603
10219
  toJson: opts.toJson,
@@ -9682,7 +10298,9 @@ if (process.argv[2] === "__complete") {
9682
10298
  if (cli.options.version) {
9683
10299
  await showUpdateAndExit(0);
9684
10300
  } else if (cli.options.help) {
9685
- if (cli.matchedCommand) {
10301
+ if (printMatchedCommandHelp(cli)) {
10302
+ await showUpdateAndExit(0);
10303
+ } else if (cli.matchedCommand) {
9686
10304
  const result = cli.runMatchedCommand();
9687
10305
  if (result && typeof result.then === "function") {
9688
10306
  await result.then(() => showUpdateAndExit(0), handleError);