polkadot-cli 1.11.0 → 1.13.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 +174 -51
  2. package/dist/cli.mjs +1334 -280
  3. package/package.json +9 -10
package/dist/cli.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { createRequire } from "node:module";
3
2
  var __defProp = Object.defineProperty;
4
3
  var __returnValue = (v) => v;
5
4
  function __exportSetter(name, newValue) {
@@ -15,7 +14,6 @@ var __export = (target, all) => {
15
14
  });
16
15
  };
17
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
18
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
17
 
20
18
  // src/config/types.ts
21
19
  function primaryRpc(rpc) {
@@ -53,7 +51,9 @@ var init_types = __esm(() => {
53
51
  "wss://statemint-rpc-tn.dwellir.com",
54
52
  "wss://statemint.public.curie.radiumblock.co/ws",
55
53
  "wss://asset-hub-polkadot.rpc.permanence.io"
56
- ]
54
+ ],
55
+ relay: "polkadot",
56
+ parachainId: 1000
57
57
  },
58
58
  "polkadot-bridge-hub": {
59
59
  rpc: [
@@ -65,7 +65,9 @@ var init_types = __esm(() => {
65
65
  "wss://bridgehub-polkadot.api.onfinality.io/public-ws",
66
66
  "wss://polkadot-bridge-hub-rpc-tn.dwellir.com",
67
67
  "wss://bridgehub-polkadot.public.curie.radiumblock.co/ws"
68
- ]
68
+ ],
69
+ relay: "polkadot",
70
+ parachainId: 1002
69
71
  },
70
72
  "polkadot-collectives": {
71
73
  rpc: [
@@ -77,7 +79,9 @@ var init_types = __esm(() => {
77
79
  "wss://collectives.api.onfinality.io/public-ws",
78
80
  "wss://polkadot-collectives-rpc-tn.dwellir.com",
79
81
  "wss://collectives.public.curie.radiumblock.co/ws"
80
- ]
82
+ ],
83
+ relay: "polkadot",
84
+ parachainId: 1001
81
85
  },
82
86
  "polkadot-coretime": {
83
87
  rpc: [
@@ -87,7 +91,9 @@ var init_types = __esm(() => {
87
91
  "wss://coretime-polkadot-rpc.n.dwellir.com",
88
92
  "wss://rpc-coretime-polkadot.luckyfriday.io",
89
93
  "wss://coretime-polkadot.api.onfinality.io/public-ws"
90
- ]
94
+ ],
95
+ relay: "polkadot",
96
+ parachainId: 1005
91
97
  },
92
98
  "polkadot-people": {
93
99
  rpc: [
@@ -97,7 +103,9 @@ var init_types = __esm(() => {
97
103
  "wss://people-polkadot-rpc.n.dwellir.com",
98
104
  "wss://rpc-people-polkadot.luckyfriday.io",
99
105
  "wss://people-polkadot.api.onfinality.io/public-ws"
100
- ]
106
+ ],
107
+ relay: "polkadot",
108
+ parachainId: 1004
101
109
  },
102
110
  paseo: {
103
111
  rpc: [
@@ -113,23 +121,33 @@ var init_types = __esm(() => {
113
121
  "wss://asset-hub-paseo.dotters.network",
114
122
  "wss://asset-hub-paseo-rpc.n.dwellir.com",
115
123
  "wss://sys.turboflakes.io/asset-hub-paseo"
116
- ]
124
+ ],
125
+ relay: "paseo",
126
+ parachainId: 1000
117
127
  },
118
128
  "paseo-bridge-hub": {
119
- rpc: ["wss://bridge-hub-paseo.ibp.network", "wss://bridge-hub-paseo.dotters.network"]
129
+ rpc: ["wss://bridge-hub-paseo.ibp.network", "wss://bridge-hub-paseo.dotters.network"],
130
+ relay: "paseo",
131
+ parachainId: 1002
120
132
  },
121
133
  "paseo-collectives": {
122
- rpc: ["wss://collectives-paseo.ibp.network", "wss://collectives-paseo.dotters.network"]
134
+ rpc: ["wss://collectives-paseo.ibp.network", "wss://collectives-paseo.dotters.network"],
135
+ relay: "paseo",
136
+ parachainId: 1001
123
137
  },
124
138
  "paseo-coretime": {
125
- rpc: ["wss://coretime-paseo.ibp.network", "wss://coretime-paseo.dotters.network"]
139
+ rpc: ["wss://coretime-paseo.ibp.network", "wss://coretime-paseo.dotters.network"],
140
+ relay: "paseo",
141
+ parachainId: 1005
126
142
  },
127
143
  "paseo-people": {
128
144
  rpc: [
129
145
  "wss://people-paseo.ibp.network",
130
146
  "wss://people-paseo.dotters.network",
131
147
  "wss://people-paseo.rpc.amforc.com"
132
- ]
148
+ ],
149
+ relay: "paseo",
150
+ parachainId: 1004
133
151
  }
134
152
  }
135
153
  };
@@ -164,10 +182,16 @@ async function loadConfig() {
164
182
  await ensureDir(DOT_DIR);
165
183
  if (await fileExists(CONFIG_PATH)) {
166
184
  const saved = JSON.parse(await readFile(CONFIG_PATH, "utf-8"));
167
- return {
168
- ...saved,
169
- chains: { ...DEFAULT_CONFIG.chains, ...saved.chains }
170
- };
185
+ const chains = {};
186
+ for (const [name, defaultConfig] of Object.entries(DEFAULT_CONFIG.chains)) {
187
+ chains[name] = saved.chains[name] ? { ...defaultConfig, ...saved.chains[name] } : defaultConfig;
188
+ }
189
+ for (const [name, config] of Object.entries(saved.chains)) {
190
+ if (!(name in DEFAULT_CONFIG.chains)) {
191
+ chains[name] = config;
192
+ }
193
+ }
194
+ return { ...saved, chains };
171
195
  }
172
196
  await saveConfig(DEFAULT_CONFIG);
173
197
  return DEFAULT_CONFIG;
@@ -259,6 +283,42 @@ function isWatchOnly(account) {
259
283
  return account.secret === undefined;
260
284
  }
261
285
 
286
+ // src/utils/fuzzy-match.ts
287
+ function levenshtein(a, b) {
288
+ const la = a.length;
289
+ const lb = b.length;
290
+ const dp = Array.from({ length: la + 1 }, () => Array(lb + 1).fill(0));
291
+ for (let i = 0;i <= la; i++)
292
+ dp[i][0] = i;
293
+ for (let j = 0;j <= lb; j++)
294
+ dp[0][j] = j;
295
+ for (let i = 1;i <= la; i++) {
296
+ for (let j = 1;j <= lb; j++) {
297
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
298
+ dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
299
+ }
300
+ }
301
+ return dp[la][lb];
302
+ }
303
+ function findClosest(input, candidates, maxDistance = 3) {
304
+ const lower = input.toLowerCase();
305
+ const exact = candidates.find((c) => c.toLowerCase() === lower);
306
+ if (exact)
307
+ return [exact];
308
+ const scored = candidates.map((c) => ({ name: c, dist: levenshtein(lower, c.toLowerCase()) })).filter((s) => s.dist <= maxDistance).sort((a, b) => a.dist - b.dist);
309
+ return scored.slice(0, 3).map((s) => s.name);
310
+ }
311
+ function suggestMessage(kind, input, candidates) {
312
+ const suggestions = findClosest(input, candidates);
313
+ if (suggestions.length === 0) {
314
+ return `Unknown ${kind} "${input}".`;
315
+ }
316
+ if (suggestions.length === 1 && suggestions[0]?.toLowerCase() === input.toLowerCase()) {
317
+ return suggestions[0];
318
+ }
319
+ return `Unknown ${kind} "${input}". Did you mean: ${suggestions.join(", ")}?`;
320
+ }
321
+
262
322
  // src/core/accounts.ts
263
323
  import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
264
324
  import {
@@ -362,23 +422,31 @@ function tryDerivePublicKey(envVarName, path = "") {
362
422
  return null;
363
423
  }
364
424
  }
365
- async function resolveAccountSigner(name) {
425
+ async function resolveAccountKeypair(name) {
366
426
  if (isDevAccount(name)) {
367
- const keypair2 = getDevKeypair(name);
368
- return getPolkadotSigner(keypair2.publicKey, "Sr25519", keypair2.sign);
427
+ return getDevKeypair(name);
369
428
  }
370
429
  const accountsFile = await loadAccounts();
371
430
  const account = findAccount(accountsFile, name);
372
431
  if (!account) {
373
- const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)];
374
- throw new Error(`Unknown account "${name}". Available accounts: ${available.join(", ")}`);
432
+ const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
433
+ const suggestions = findClosest(name, available);
434
+ const hint = suggestions.length > 0 ? `
435
+ Did you mean: ${suggestions.join(", ")}?` : "";
436
+ const list = available.map((a) => `
437
+ - ${a}`).join("");
438
+ throw new Error(`Unknown account "${name}".${hint}
439
+ Available accounts:${list}`);
375
440
  }
376
441
  if (account.secret === undefined) {
377
442
  throw new Error(`Account "${name}" is watch-only (no secret). Cannot sign. Import with --secret or --env.`);
378
443
  }
379
444
  const secret = resolveSecret(account.secret);
380
445
  const isHexSeed = /^0x[0-9a-fA-F]{64}$/.test(secret);
381
- const keypair = isHexSeed ? deriveFromHexSeed(secret, account.derivationPath) : deriveFromMnemonic(secret, account.derivationPath);
446
+ return isHexSeed ? deriveFromHexSeed(secret, account.derivationPath) : deriveFromMnemonic(secret, account.derivationPath);
447
+ }
448
+ async function resolveAccountSigner(name) {
449
+ const keypair = await resolveAccountKeypair(name);
382
450
  return getPolkadotSigner(keypair.publicKey, "Sr25519", keypair.sign);
383
451
  }
384
452
  var DEV_NAMES;
@@ -388,6 +456,7 @@ var init_accounts = __esm(() => {
388
456
  });
389
457
 
390
458
  // src/utils/binary-display.ts
459
+ import { Binary } from "polkadot-api";
391
460
  function isReadableText(text) {
392
461
  for (let i = 0;i < text.length; i++) {
393
462
  const code = text.charCodeAt(i);
@@ -405,20 +474,17 @@ function isReadableText(text) {
405
474
  return true;
406
475
  }
407
476
  function binaryToDisplay(value) {
408
- const text = value.asText();
409
- return isReadableText(text) ? text : value.asHex();
477
+ const text = Binary.toText(value);
478
+ return isReadableText(text) ? text : Binary.toHex(value);
410
479
  }
480
+ var init_binary_display = () => {};
411
481
 
412
482
  // src/core/output.ts
413
- import { Binary } from "polkadot-api";
414
483
  function replacer(_key, value) {
415
484
  if (typeof value === "bigint")
416
485
  return value.toString();
417
- if (value instanceof Binary) {
418
- return binaryToDisplay(value);
419
- }
420
486
  if (value instanceof Uint8Array)
421
- return `0x${Buffer.from(value).toString("hex")}`;
487
+ return binaryToDisplay(value);
422
488
  return value;
423
489
  }
424
490
  function formatJson(data) {
@@ -440,6 +506,12 @@ function printResult(data, format = "pretty") {
440
506
  console.log(formatPretty(data));
441
507
  }
442
508
  }
509
+ function isJsonOutput(opts) {
510
+ return opts.json === true || opts.output === "json";
511
+ }
512
+ function printJsonLine(data) {
513
+ console.log(JSON.stringify(data, replacer));
514
+ }
443
515
  function printHeading(text) {
444
516
  console.log(`
445
517
  ${BOLD}${text}${RESET}
@@ -498,6 +570,7 @@ function firstSentence(docs) {
498
570
  }
499
571
  var isTTY, RESET, CYAN, GREEN, RED, YELLOW, MAGENTA, DIM, BOLD, CHECK_MARK = "✓", SPINNER_FRAMES;
500
572
  var init_output = __esm(() => {
573
+ init_binary_display();
501
574
  isTTY = process.stdout.isTTY ?? false;
502
575
  RESET = isTTY ? "\x1B[0m" : "";
503
576
  CYAN = isTTY ? "\x1B[36m" : "";
@@ -510,6 +583,25 @@ var init_output = __esm(() => {
510
583
  SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
511
584
  });
512
585
 
586
+ // src/core/bandersnatch.ts
587
+ var exports_bandersnatch = {};
588
+ __export(exports_bandersnatch, {
589
+ deriveBandersnatchMember: () => deriveBandersnatchMember
590
+ });
591
+ import { blake2b } from "@noble/hashes/blake2.js";
592
+ import { mnemonicToEntropy as mnemonicToEntropy2 } from "@polkadot-labs/hdkd-helpers";
593
+ import { member_from_entropy } from "verifiablejs/nodejs";
594
+ function deriveBandersnatchMember(mnemonic, context) {
595
+ const entropy = mnemonicToEntropy2(mnemonic);
596
+ const opts = { dkLen: 32 };
597
+ if (context) {
598
+ opts.key = new TextEncoder().encode(context);
599
+ }
600
+ const hashed = blake2b(entropy, opts);
601
+ return member_from_entropy(hashed);
602
+ }
603
+ var init_bandersnatch = () => {};
604
+
513
605
  // src/utils/errors.ts
514
606
  var CliError, ConnectionError, MetadataError;
515
607
  var init_errors = __esm(() => {
@@ -535,37 +627,33 @@ var init_errors = __esm(() => {
535
627
 
536
628
  // src/core/client.ts
537
629
  import { createClient } from "polkadot-api";
538
- import { withPolkadotSdkCompat } from "polkadot-api/polkadot-sdk-compat";
539
- import { getWsProvider } from "polkadot-api/ws-provider";
540
- import { WebSocket } from "ws";
541
- function suppressWsNoise() {
542
- const orig = console.error;
630
+ import { getWsProvider } from "polkadot-api/ws";
631
+ function suppressProviderNoise() {
632
+ const origError = console.error;
633
+ const origWarn = console.warn;
543
634
  console.error = (...args) => {
544
635
  if (typeof args[0] === "string" && args[0].includes("Unable to connect"))
545
636
  return;
546
- orig(...args);
637
+ origError(...args);
638
+ };
639
+ console.warn = (...args) => {
640
+ if (typeof args[0] === "string" && args[0].includes("ChainHead"))
641
+ return;
642
+ origWarn(...args);
547
643
  };
548
644
  return () => {
549
- console.error = orig;
645
+ console.error = origError;
646
+ console.warn = origWarn;
550
647
  };
551
648
  }
552
649
  async function createChainClient(chainName, chainConfig, rpcOverride) {
553
- const useLight = !rpcOverride && chainConfig.lightClient;
554
- const restoreConsole = suppressWsNoise();
555
- let provider;
556
- if (useLight) {
557
- provider = await createSmoldotProvider(chainName);
558
- } else {
559
- const rpc = rpcOverride ?? chainConfig.rpc;
560
- if (!rpc) {
561
- restoreConsole();
562
- throw new ConnectionError(`No RPC endpoint configured for chain "${chainName}". Use --rpc or configure one with: dot chain add ${chainName} --rpc <url>`);
563
- }
564
- provider = withPolkadotSdkCompat(getWsProvider(rpc, {
565
- timeout: 1e4,
566
- websocketClass: WebSocket
567
- }));
650
+ const restoreConsole = suppressProviderNoise();
651
+ const rpc = rpcOverride ?? chainConfig.rpc;
652
+ if (!rpc) {
653
+ restoreConsole();
654
+ throw new ConnectionError(`No RPC endpoint configured for chain "${chainName}". Use --rpc or configure one with: dot chain add ${chainName} --rpc <url>`);
568
655
  }
656
+ const provider = getWsProvider(rpc, { timeout: 1e4 });
569
657
  const client = createClient(provider, {
570
658
  getMetadata: async () => loadMetadata(chainName),
571
659
  setMetadata: async (_codeHash, metadata) => {
@@ -575,51 +663,25 @@ async function createChainClient(chainName, chainConfig, rpcOverride) {
575
663
  return {
576
664
  client,
577
665
  destroy: () => {
578
- client.destroy();
579
- restoreConsole();
666
+ try {
667
+ client.destroy();
668
+ } catch {}
669
+ setTimeout(restoreConsole, 500);
580
670
  }
581
671
  };
582
672
  }
583
- async function createSmoldotProvider(chainName) {
584
- const { start } = await import("polkadot-api/smoldot");
585
- const { getSmProvider } = await import("polkadot-api/sm-provider");
586
- const entry = KNOWN_CHAIN_SPECS[chainName];
587
- if (!entry) {
588
- throw new ConnectionError(`Light client is only supported for known chains: ${Object.keys(KNOWN_CHAIN_SPECS).join(", ")}. Use --rpc to connect to "${chainName}" instead.`);
589
- }
590
- const { chainSpec } = await import(entry.spec);
591
- const smoldot = start();
592
- if (entry.relay) {
593
- const relayEntry = KNOWN_CHAIN_SPECS[entry.relay];
594
- if (!relayEntry) {
595
- throw new ConnectionError(`Relay chain "${entry.relay}" not found in known chain specs.`);
596
- }
597
- const { chainSpec: relaySpec } = await import(relayEntry.spec);
598
- const relayChain = await smoldot.addChain({ chainSpec: relaySpec, disableJsonRpc: true });
599
- const chain2 = await smoldot.addChain({ chainSpec, potentialRelayChains: [relayChain] });
600
- return getSmProvider(chain2);
601
- }
602
- const chain = await smoldot.addChain({ chainSpec });
603
- return getSmProvider(chain);
604
- }
605
- var KNOWN_CHAIN_SPECS;
673
+ async function getParachainId(clientHandle) {
674
+ try {
675
+ const unsafeApi = clientHandle.client.getUnsafeApi();
676
+ const parachainId = await unsafeApi.query.ParachainInfo.ParachainId.getValue();
677
+ return typeof parachainId === "number" ? parachainId : null;
678
+ } catch {
679
+ return null;
680
+ }
681
+ }
606
682
  var init_client = __esm(() => {
607
683
  init_store();
608
684
  init_errors();
609
- KNOWN_CHAIN_SPECS = {
610
- polkadot: { spec: "polkadot-api/chains/polkadot" },
611
- kusama: { spec: "polkadot-api/chains/ksmcc3" },
612
- westend: { spec: "polkadot-api/chains/westend2" },
613
- paseo: { spec: "polkadot-api/chains/paseo" },
614
- "polkadot-asset-hub": { spec: "polkadot-api/chains/polkadot_asset_hub", relay: "polkadot" },
615
- "polkadot-bridge-hub": { spec: "polkadot-api/chains/polkadot_bridge_hub", relay: "polkadot" },
616
- "polkadot-collectives": { spec: "polkadot-api/chains/polkadot_collectives", relay: "polkadot" },
617
- "polkadot-coretime": { spec: "polkadot-api/chains/polkadot_coretime", relay: "polkadot" },
618
- "polkadot-people": { spec: "polkadot-api/chains/polkadot_people", relay: "polkadot" },
619
- "paseo-asset-hub": { spec: "polkadot-api/chains/paseo_asset_hub", relay: "paseo" },
620
- "paseo-coretime": { spec: "polkadot-api/chains/paseo_coretime", relay: "paseo" },
621
- "paseo-people": { spec: "polkadot-api/chains/paseo_people", relay: "paseo" }
622
- };
623
685
  });
624
686
 
625
687
  // src/core/metadata.ts
@@ -894,42 +956,6 @@ var init_metadata = __esm(() => {
894
956
  v15Arg = toHex(u32.enc(15));
895
957
  });
896
958
 
897
- // src/utils/fuzzy-match.ts
898
- function levenshtein(a, b) {
899
- const la = a.length;
900
- const lb = b.length;
901
- const dp = Array.from({ length: la + 1 }, () => Array(lb + 1).fill(0));
902
- for (let i = 0;i <= la; i++)
903
- dp[i][0] = i;
904
- for (let j = 0;j <= lb; j++)
905
- dp[0][j] = j;
906
- for (let i = 1;i <= la; i++) {
907
- for (let j = 1;j <= lb; j++) {
908
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
909
- dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
910
- }
911
- }
912
- return dp[la][lb];
913
- }
914
- function findClosest(input, candidates, maxDistance = 3) {
915
- const lower = input.toLowerCase();
916
- const exact = candidates.find((c) => c.toLowerCase() === lower);
917
- if (exact)
918
- return [exact];
919
- const scored = candidates.map((c) => ({ name: c, dist: levenshtein(lower, c.toLowerCase()) })).filter((s) => s.dist <= maxDistance).sort((a, b) => a.dist - b.dist);
920
- return scored.slice(0, 3).map((s) => s.name);
921
- }
922
- function suggestMessage(kind, input, candidates) {
923
- const suggestions = findClosest(input, candidates);
924
- if (suggestions.length === 0) {
925
- return `Unknown ${kind} "${input}".`;
926
- }
927
- if (suggestions.length === 1 && suggestions[0]?.toLowerCase() === input.toLowerCase()) {
928
- return suggestions[0];
929
- }
930
- return `Unknown ${kind} "${input}". Did you mean: ${suggestions.join(", ")}?`;
931
- }
932
-
933
959
  // src/core/explorers.ts
934
960
  var pjsAppsLink = (rpc, hash) => `https://polkadot.js.org/apps/?rpc=${encodeURIComponent(rpc)}#/explorer/query/${hash}`, papiLink = (rpc, hash) => `https://dev.papi.how/explorer/${hash}#networkId=custom&endpoint=${encodeURIComponent(rpc)}`;
935
961
 
@@ -955,9 +981,14 @@ async function resolveAccountAddress(input) {
955
981
  return input;
956
982
  }
957
983
  const stored = accountsFile.accounts.map((a) => a.name);
958
- const available = [...DEV_NAMES, ...stored];
959
- throw new Error(`Unknown account or address "${input}".
960
- Available accounts: ${available.join(", ")}`);
984
+ const available = [...DEV_NAMES, ...stored].sort((a, b) => a.localeCompare(b));
985
+ const suggestions = findClosest(input, available);
986
+ const hint = suggestions.length > 0 ? `
987
+ Did you mean: ${suggestions.join(", ")}?` : "";
988
+ const list = available.map((a) => `
989
+ - ${a}`).join("");
990
+ throw new Error(`Unknown account or address "${input}".${hint}
991
+ Available accounts:${list}`);
961
992
  }
962
993
  var init_resolve_address = __esm(() => {
963
994
  init_accounts_store();
@@ -1185,7 +1216,7 @@ async function parseTypedArg(meta, entry, arg) {
1185
1216
  case "enum": {
1186
1217
  if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
1187
1218
  const callCodec = meta.builder.buildDefinition(meta.lookup.call);
1188
- return callCodec.dec(Binary2.fromHex(arg).asBytes());
1219
+ return callCodec.dec(Binary2.fromHex(arg));
1189
1220
  }
1190
1221
  if (arg.startsWith("{")) {
1191
1222
  try {
@@ -1297,6 +1328,7 @@ var init_tx = __esm(() => {
1297
1328
  init_metadata();
1298
1329
  init_output();
1299
1330
  init_resolve_address();
1331
+ init_binary_display();
1300
1332
  init_errors();
1301
1333
  init_focused_inspect();
1302
1334
  PAPI_BUILTIN_EXTENSIONS = new Set([
@@ -1327,6 +1359,13 @@ async function handleApis(target, args, opts) {
1327
1359
  const { name: chainName2, chain: chainConfig2 } = resolveChain(config2, opts.chain);
1328
1360
  const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
1329
1361
  const apis = listRuntimeApis(meta);
1362
+ if (isJsonOutput(opts)) {
1363
+ console.log(formatJson({
1364
+ chain: chainName2,
1365
+ apis: apis.map((a) => ({ name: a.name, methods: a.methods.length }))
1366
+ }));
1367
+ return;
1368
+ }
1330
1369
  printHeading(`Runtime APIs on ${chainName2} (${apis.length})`);
1331
1370
  for (const api of apis) {
1332
1371
  printItem(api.name, `${api.methods.length} methods`);
@@ -1349,7 +1388,24 @@ async function handleApis(target, args, opts) {
1349
1388
  const meta = await loadMeta(chainName, chainConfig, opts.rpc);
1350
1389
  const api = resolveRuntimeApi(meta, apiName);
1351
1390
  if (api.methods.length === 0) {
1352
- console.log(`No methods in ${api.name}.`);
1391
+ if (isJsonOutput(opts)) {
1392
+ console.log(formatJson({ chain: chainName, api: api.name, methods: [] }));
1393
+ } else {
1394
+ console.log(`No methods in ${api.name}.`);
1395
+ }
1396
+ return;
1397
+ }
1398
+ if (isJsonOutput(opts)) {
1399
+ console.log(formatJson({
1400
+ chain: chainName,
1401
+ api: api.name,
1402
+ methods: api.methods.map((m) => ({
1403
+ name: m.name,
1404
+ args: describeRuntimeApiMethodArgs(meta, m),
1405
+ returns: describeType(meta.lookup, m.output),
1406
+ docs: firstSentence(m.docs)
1407
+ }))
1408
+ }));
1353
1409
  return;
1354
1410
  }
1355
1411
  printHeading(`${api.name} Methods`);
@@ -1380,7 +1436,7 @@ async function handleApis(target, args, opts) {
1380
1436
  const parsedArgs = await parseRuntimeApiArgs(meta, method, effectiveArgs);
1381
1437
  const unsafeApi = clientHandle.client.getUnsafeApi();
1382
1438
  const result = await unsafeApi.apis[api.name][method.name](...parsedArgs);
1383
- const format = opts.output ?? "pretty";
1439
+ const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
1384
1440
  printResult(result, format);
1385
1441
  } finally {
1386
1442
  clientHandle.destroy();
@@ -1420,7 +1476,8 @@ async function loadMeta(chainName, chainConfig, rpcOverride) {
1420
1476
  try {
1421
1477
  return await getOrFetchMetadata(chainName);
1422
1478
  } catch {
1423
- console.error(`Fetching metadata from ${chainName}...`);
1479
+ process.stderr.write(`Fetching metadata from ${chainName}...
1480
+ `);
1424
1481
  const clientHandle = await createChainClient(chainName, chainConfig, rpcOverride);
1425
1482
  try {
1426
1483
  return await getOrFetchMetadata(chainName, clientHandle);
@@ -1444,6 +1501,13 @@ async function handleCalls(target, opts) {
1444
1501
  const meta2 = await loadMeta(chainName2, chainConfig2, opts.rpc);
1445
1502
  const pallets = listPallets(meta2);
1446
1503
  const withCalls = pallets.filter((p) => p.calls.length > 0);
1504
+ if (isJsonOutput(opts)) {
1505
+ console.log(formatJson({
1506
+ chain: chainName2,
1507
+ pallets: withCalls.map((p) => ({ name: p.name, calls: p.calls.length }))
1508
+ }));
1509
+ return;
1510
+ }
1447
1511
  printHeading(`Pallets with calls on ${chainName2} (${withCalls.length})`);
1448
1512
  for (const p of withCalls) {
1449
1513
  printItem(p.name, `${p.calls.length} calls`);
@@ -1460,7 +1524,23 @@ async function handleCalls(target, opts) {
1460
1524
  const pallet = resolvePallet(meta, palletName);
1461
1525
  if (!itemName) {
1462
1526
  if (pallet.calls.length === 0) {
1463
- console.log(`No calls in ${pallet.name}.`);
1527
+ if (isJsonOutput(opts)) {
1528
+ console.log(formatJson({ chain: chainName, pallet: pallet.name, calls: [] }));
1529
+ } else {
1530
+ console.log(`No calls in ${pallet.name}.`);
1531
+ }
1532
+ return;
1533
+ }
1534
+ if (isJsonOutput(opts)) {
1535
+ console.log(formatJson({
1536
+ chain: chainName,
1537
+ pallet: pallet.name,
1538
+ calls: pallet.calls.map((c) => ({
1539
+ name: c.name,
1540
+ args: describeCallArgs(meta, pallet.name, c.name),
1541
+ docs: firstSentence(c.docs)
1542
+ }))
1543
+ }));
1464
1544
  return;
1465
1545
  }
1466
1546
  printHeading(`${pallet.name} Calls`);
@@ -1480,6 +1560,17 @@ async function handleCalls(target, opts) {
1480
1560
  const names = pallet.calls.map((c) => c.name);
1481
1561
  throw new Error(suggestMessage(`call in ${pallet.name}`, itemName, names));
1482
1562
  }
1563
+ if (isJsonOutput(opts)) {
1564
+ console.log(formatJson({
1565
+ chain: chainName,
1566
+ pallet: pallet.name,
1567
+ item: callItem.name,
1568
+ category: "call",
1569
+ args: describeCallArgs(meta, pallet.name, callItem.name),
1570
+ docs: callItem.docs
1571
+ }));
1572
+ return;
1573
+ }
1483
1574
  printHeading(`${pallet.name}.${callItem.name} (Call)`);
1484
1575
  const args = describeCallArgs(meta, pallet.name, callItem.name);
1485
1576
  console.log(` ${BOLD}Args:${RESET} ${args}`);
@@ -1496,6 +1587,13 @@ async function handleEvents(target, opts) {
1496
1587
  const meta2 = await loadMeta(chainName2, chainConfig2, opts.rpc);
1497
1588
  const pallets = listPallets(meta2);
1498
1589
  const withEvents = pallets.filter((p) => p.events.length > 0);
1590
+ if (isJsonOutput(opts)) {
1591
+ console.log(formatJson({
1592
+ chain: chainName2,
1593
+ pallets: withEvents.map((p) => ({ name: p.name, events: p.events.length }))
1594
+ }));
1595
+ return;
1596
+ }
1499
1597
  printHeading(`Pallets with events on ${chainName2} (${withEvents.length})`);
1500
1598
  for (const p of withEvents) {
1501
1599
  printItem(p.name, `${p.events.length} events`);
@@ -1512,7 +1610,23 @@ async function handleEvents(target, opts) {
1512
1610
  const pallet = resolvePallet(meta, palletName);
1513
1611
  if (!itemName) {
1514
1612
  if (pallet.events.length === 0) {
1515
- console.log(`No events in ${pallet.name}.`);
1613
+ if (isJsonOutput(opts)) {
1614
+ console.log(formatJson({ chain: chainName, pallet: pallet.name, events: [] }));
1615
+ } else {
1616
+ console.log(`No events in ${pallet.name}.`);
1617
+ }
1618
+ return;
1619
+ }
1620
+ if (isJsonOutput(opts)) {
1621
+ console.log(formatJson({
1622
+ chain: chainName,
1623
+ pallet: pallet.name,
1624
+ events: pallet.events.map((e) => ({
1625
+ name: e.name,
1626
+ fields: describeEventFields(meta, pallet.name, e.name),
1627
+ docs: firstSentence(e.docs)
1628
+ }))
1629
+ }));
1516
1630
  return;
1517
1631
  }
1518
1632
  printHeading(`${pallet.name} Events`);
@@ -1532,6 +1646,17 @@ async function handleEvents(target, opts) {
1532
1646
  const names = pallet.events.map((e) => e.name);
1533
1647
  throw new Error(suggestMessage(`event in ${pallet.name}`, itemName, names));
1534
1648
  }
1649
+ if (isJsonOutput(opts)) {
1650
+ console.log(formatJson({
1651
+ chain: chainName,
1652
+ pallet: pallet.name,
1653
+ item: eventItem.name,
1654
+ category: "event",
1655
+ fields: describeEventFields(meta, pallet.name, eventItem.name),
1656
+ docs: eventItem.docs
1657
+ }));
1658
+ return;
1659
+ }
1535
1660
  printHeading(`${pallet.name}.${eventItem.name} (Event)`);
1536
1661
  const fields = describeEventFields(meta, pallet.name, eventItem.name);
1537
1662
  console.log(` ${BOLD}Fields:${RESET} ${fields}`);
@@ -1548,6 +1673,13 @@ async function handleErrors(target, opts) {
1548
1673
  const meta2 = await loadMeta(chainName2, chainConfig2, opts.rpc);
1549
1674
  const pallets = listPallets(meta2);
1550
1675
  const withErrors = pallets.filter((p) => p.errors.length > 0);
1676
+ if (isJsonOutput(opts)) {
1677
+ console.log(formatJson({
1678
+ chain: chainName2,
1679
+ pallets: withErrors.map((p) => ({ name: p.name, errors: p.errors.length }))
1680
+ }));
1681
+ return;
1682
+ }
1551
1683
  printHeading(`Pallets with errors on ${chainName2} (${withErrors.length})`);
1552
1684
  for (const p of withErrors) {
1553
1685
  printItem(p.name, `${p.errors.length} errors`);
@@ -1564,7 +1696,19 @@ async function handleErrors(target, opts) {
1564
1696
  const pallet = resolvePallet(meta, palletName);
1565
1697
  if (!itemName) {
1566
1698
  if (pallet.errors.length === 0) {
1567
- console.log(`No errors in ${pallet.name}.`);
1699
+ if (isJsonOutput(opts)) {
1700
+ console.log(formatJson({ chain: chainName, pallet: pallet.name, errors: [] }));
1701
+ } else {
1702
+ console.log(`No errors in ${pallet.name}.`);
1703
+ }
1704
+ return;
1705
+ }
1706
+ if (isJsonOutput(opts)) {
1707
+ console.log(formatJson({
1708
+ chain: chainName,
1709
+ pallet: pallet.name,
1710
+ errors: pallet.errors.map((e) => ({ name: e.name, docs: firstSentence(e.docs) }))
1711
+ }));
1568
1712
  return;
1569
1713
  }
1570
1714
  printHeading(`${pallet.name} Errors`);
@@ -1583,6 +1727,16 @@ async function handleErrors(target, opts) {
1583
1727
  const names = pallet.errors.map((e) => e.name);
1584
1728
  throw new Error(suggestMessage(`error in ${pallet.name}`, itemName, names));
1585
1729
  }
1730
+ if (isJsonOutput(opts)) {
1731
+ console.log(formatJson({
1732
+ chain: chainName,
1733
+ pallet: pallet.name,
1734
+ item: errorItem.name,
1735
+ category: "error",
1736
+ docs: errorItem.docs
1737
+ }));
1738
+ return;
1739
+ }
1586
1740
  printHeading(`${pallet.name}.${errorItem.name} (Error)`);
1587
1741
  if (errorItem.docs.length) {
1588
1742
  printDocs(errorItem.docs);
@@ -1596,6 +1750,13 @@ async function handleStorage(target, opts) {
1596
1750
  const meta2 = await loadMeta(chainName2, chainConfig2, opts.rpc);
1597
1751
  const pallets = listPallets(meta2);
1598
1752
  const withStorage = pallets.filter((p) => p.storage.length > 0);
1753
+ if (isJsonOutput(opts)) {
1754
+ console.log(formatJson({
1755
+ chain: chainName2,
1756
+ pallets: withStorage.map((p) => ({ name: p.name, storage: p.storage.length }))
1757
+ }));
1758
+ return;
1759
+ }
1599
1760
  printHeading(`Pallets with storage on ${chainName2} (${withStorage.length})`);
1600
1761
  for (const p of withStorage) {
1601
1762
  printItem(p.name, `${p.storage.length} storage`);
@@ -1612,7 +1773,23 @@ async function handleStorage(target, opts) {
1612
1773
  const pallet = resolvePallet(meta, palletName);
1613
1774
  if (!itemName) {
1614
1775
  if (pallet.storage.length === 0) {
1615
- console.log(`No storage items in ${pallet.name}.`);
1776
+ if (isJsonOutput(opts)) {
1777
+ console.log(formatJson({ chain: chainName, pallet: pallet.name, storage: [] }));
1778
+ } else {
1779
+ console.log(`No storage items in ${pallet.name}.`);
1780
+ }
1781
+ return;
1782
+ }
1783
+ if (isJsonOutput(opts)) {
1784
+ console.log(formatJson({
1785
+ chain: chainName,
1786
+ pallet: pallet.name,
1787
+ storage: pallet.storage.map((s) => {
1788
+ const valueType = describeType(meta.lookup, s.valueTypeId);
1789
+ const keyType = s.keyTypeId != null ? describeType(meta.lookup, s.keyTypeId) : undefined;
1790
+ return { name: s.name, type: s.type, valueType, keyType, docs: firstSentence(s.docs) };
1791
+ })
1792
+ }));
1616
1793
  return;
1617
1794
  }
1618
1795
  printHeading(`${pallet.name} Storage`);
@@ -1639,6 +1816,21 @@ async function handleStorage(target, opts) {
1639
1816
  const names = pallet.storage.map((s) => s.name);
1640
1817
  throw new Error(suggestMessage(`storage item in ${pallet.name}`, itemName, names));
1641
1818
  }
1819
+ if (isJsonOutput(opts)) {
1820
+ const valueType = describeType(meta.lookup, storageItem.valueTypeId);
1821
+ const keyType = storageItem.keyTypeId != null ? describeType(meta.lookup, storageItem.keyTypeId) : undefined;
1822
+ console.log(formatJson({
1823
+ chain: chainName,
1824
+ pallet: pallet.name,
1825
+ item: storageItem.name,
1826
+ category: "storage",
1827
+ type: storageItem.type,
1828
+ valueType,
1829
+ keyType,
1830
+ docs: storageItem.docs
1831
+ }));
1832
+ return;
1833
+ }
1642
1834
  printHeading(`${pallet.name}.${storageItem.name} (Storage)`);
1643
1835
  console.log(` ${BOLD}Type:${RESET} ${storageItem.type}`);
1644
1836
  console.log(` ${BOLD}Value:${RESET} ${describeType(meta.lookup, storageItem.valueTypeId)}`);
@@ -1766,7 +1958,6 @@ async function showItemHelp(category, target, opts) {
1766
1958
  console.log();
1767
1959
  console.log(`${BOLD}Options:${RESET}`);
1768
1960
  console.log(` --dump Dump all entries of a map (required for keyless map queries)`);
1769
- console.log(` --limit <n> Max entries for map queries (0 = unlimited, default: 100)`);
1770
1961
  console.log();
1771
1962
  return;
1772
1963
  }
@@ -1833,7 +2024,7 @@ var init_focused_inspect = __esm(() => {
1833
2024
  });
1834
2025
 
1835
2026
  // src/core/hash.ts
1836
- import { blake2b } from "@noble/hashes/blake2.js";
2027
+ import { blake2b as blake2b2 } from "@noble/hashes/blake2.js";
1837
2028
  import { sha256 } from "@noble/hashes/sha2.js";
1838
2029
  import { keccak_256 } from "@noble/hashes/sha3.js";
1839
2030
  import { bytesToHex, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
@@ -1867,12 +2058,12 @@ var ALGORITHMS;
1867
2058
  var init_hash = __esm(() => {
1868
2059
  ALGORITHMS = {
1869
2060
  blake2b256: {
1870
- compute: (data) => blake2b(data, { dkLen: 32 }),
2061
+ compute: (data) => blake2b2(data, { dkLen: 32 }),
1871
2062
  outputLen: 32,
1872
2063
  description: "BLAKE2b with 256-bit output"
1873
2064
  },
1874
2065
  blake2b128: {
1875
- compute: (data) => blake2b(data, { dkLen: 16 }),
2066
+ compute: (data) => blake2b2(data, { dkLen: 16 }),
1876
2067
  outputLen: 16,
1877
2068
  description: "BLAKE2b with 128-bit output"
1878
2069
  },
@@ -1978,6 +2169,9 @@ async function generateCompletions(currentWord, precedingWords) {
1978
2169
  if (prevWord === "--wait" || prevWord === "-w") {
1979
2170
  return filterPrefix(["broadcast", "best-block", "best", "finalized"], currentWord);
1980
2171
  }
2172
+ if (prevWord === "--relay") {
2173
+ return filterPrefix(knownChains, currentWord);
2174
+ }
1981
2175
  if (currentWord.startsWith("--")) {
1982
2176
  const activeCategory = detectCategory(precedingWords, knownChains);
1983
2177
  const options = [...GLOBAL_OPTIONS];
@@ -1985,10 +2179,19 @@ async function generateCompletions(currentWord, precedingWords) {
1985
2179
  options.push(...TX_OPTIONS);
1986
2180
  if (activeCategory === "query")
1987
2181
  options.push(...QUERY_OPTIONS);
2182
+ const chainIdx = precedingWords.indexOf("chain");
2183
+ if (chainIdx >= 0 && precedingWords[chainIdx + 1] === "add") {
2184
+ options.push("--relay", "--parachain-id");
2185
+ }
1988
2186
  return filterPrefix(options, currentWord);
1989
2187
  }
1990
2188
  const firstArg = precedingWords.find((w) => !w.startsWith("-"));
1991
2189
  if (firstArg === "chain") {
2190
+ const chainSubIdx = precedingWords.indexOf("chain");
2191
+ const subcommand = precedingWords[chainSubIdx + 1];
2192
+ if (subcommand === "add" && currentWord.startsWith("--")) {
2193
+ return filterPrefix(["--rpc", "--relay", "--parachain-id", ...GLOBAL_OPTIONS], currentWord);
2194
+ }
1992
2195
  return filterPrefix(CHAIN_SUBCOMMANDS, currentWord);
1993
2196
  }
1994
2197
  if (firstArg === "account") {
@@ -2166,7 +2369,7 @@ var init_complete = __esm(() => {
2166
2369
  apis: "apis",
2167
2370
  api: "apis"
2168
2371
  };
2169
- NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "parachain", "completions"];
2372
+ NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "sign", "parachain", "completions"];
2170
2373
  CHAIN_SUBCOMMANDS = ["add", "remove", "update", "list", "default"];
2171
2374
  ACCOUNT_SUBCOMMANDS = [
2172
2375
  "add",
@@ -2179,7 +2382,7 @@ var init_complete = __esm(() => {
2179
2382
  "delete",
2180
2383
  "inspect"
2181
2384
  ];
2182
- GLOBAL_OPTIONS = ["--chain", "--rpc", "--light-client", "--output", "--help", "--version"];
2385
+ GLOBAL_OPTIONS = ["--chain", "--rpc", "--output", "--help", "--version"];
2183
2386
  TX_OPTIONS = [
2184
2387
  "--from",
2185
2388
  "--dry-run",
@@ -2191,13 +2394,13 @@ var init_complete = __esm(() => {
2191
2394
  "--mortality",
2192
2395
  "--at"
2193
2396
  ];
2194
- QUERY_OPTIONS = ["--limit"];
2397
+ QUERY_OPTIONS = [];
2195
2398
  });
2196
2399
 
2197
2400
  // src/cli.ts
2198
2401
  import cac from "cac";
2199
2402
  // package.json
2200
- var version = "1.11.0";
2403
+ var version = "1.13.0";
2201
2404
 
2202
2405
  // src/commands/account.ts
2203
2406
  init_accounts_store();
@@ -2238,7 +2441,7 @@ function registerAccountCommands(cli) {
2238
2441
  cli.command("account [action] [...names]", "Manage local accounts (create, import, list, remove)").alias("accounts").option("--secret <value>", "Secret key (mnemonic or hex seed) for import").option("--env <varName>", "Environment variable name holding the secret").option("--path <derivation>", "Derivation path (e.g. //staking, //polkadot//0/wallet)").option("--prefix <number>", "SS58 prefix for address encoding (default: 42)").action(async (action, names, opts) => {
2239
2442
  if (!action) {
2240
2443
  if (process.argv[2] === "accounts")
2241
- return accountList();
2444
+ return accountList(opts);
2242
2445
  console.log(ACCOUNT_HELP);
2243
2446
  return;
2244
2447
  }
@@ -2249,16 +2452,16 @@ function registerAccountCommands(cli) {
2249
2452
  case "add":
2250
2453
  if (opts.secret || opts.env)
2251
2454
  return accountImport(names[0], opts);
2252
- return accountAddWatchOnly(names[0], names[1]);
2455
+ return accountAddWatchOnly(names[0], names[1], opts);
2253
2456
  case "import":
2254
2457
  return accountImport(names[0], opts);
2255
2458
  case "derive":
2256
2459
  return accountDerive(names[0], names[1], opts);
2257
2460
  case "list":
2258
- return accountList();
2461
+ return accountList(opts);
2259
2462
  case "delete":
2260
2463
  case "remove":
2261
- return accountRemove(names);
2464
+ return accountRemove(names, opts);
2262
2465
  case "inspect":
2263
2466
  return accountInspect(names[0], opts);
2264
2467
  default:
@@ -2284,19 +2487,38 @@ async function accountCreate(name, opts) {
2284
2487
  const { mnemonic, publicKey } = createNewAccount(path);
2285
2488
  const hexPub = publicKeyToHex(publicKey);
2286
2489
  const address = toSs58(publicKey);
2490
+ const { deriveBandersnatchMember: deriveBandersnatchMember2 } = await Promise.resolve().then(() => (init_bandersnatch(), exports_bandersnatch));
2491
+ const bandersnatch = {};
2492
+ bandersnatch[""] = publicKeyToHex(deriveBandersnatchMember2(mnemonic));
2493
+ bandersnatch.candidate = publicKeyToHex(deriveBandersnatchMember2(mnemonic, "candidate"));
2287
2494
  accountsFile.accounts.push({
2288
2495
  name,
2289
2496
  secret: mnemonic,
2290
2497
  publicKey: hexPub,
2291
- derivationPath: path
2498
+ derivationPath: path,
2499
+ bandersnatch
2292
2500
  });
2293
2501
  await saveAccounts(accountsFile);
2502
+ if (isJsonOutput(opts)) {
2503
+ console.log(formatJson({
2504
+ name,
2505
+ address,
2506
+ publicKey: hexPub,
2507
+ mnemonic,
2508
+ path: path || undefined,
2509
+ bandersnatch
2510
+ }));
2511
+ console.error(`Save this mnemonic phrase! It is the only way to recover this account.`);
2512
+ return;
2513
+ }
2294
2514
  printHeading("Account Created");
2295
- console.log(` ${BOLD}Name:${RESET} ${name}`);
2515
+ console.log(` ${BOLD}Name:${RESET} ${name}`);
2296
2516
  if (path)
2297
- console.log(` ${BOLD}Path:${RESET} ${path}`);
2298
- console.log(` ${BOLD}Address:${RESET} ${address}`);
2299
- console.log(` ${BOLD}Mnemonic:${RESET} ${mnemonic}`);
2517
+ console.log(` ${BOLD}Path:${RESET} ${path}`);
2518
+ console.log(` ${BOLD}Address:${RESET} ${address}`);
2519
+ console.log(` ${BOLD}Bandersnatch:${RESET} ${bandersnatch[""]}`);
2520
+ console.log(` ${BOLD}(candidate)${RESET} ${bandersnatch.candidate}`);
2521
+ console.log(` ${BOLD}Mnemonic:${RESET} ${mnemonic}`);
2300
2522
  console.log();
2301
2523
  console.log(` ${YELLOW}Save this mnemonic phrase! It is the only way to recover this account.${RESET}`);
2302
2524
  console.log();
@@ -2337,6 +2559,11 @@ async function accountImport(name, opts) {
2337
2559
  derivationPath: path
2338
2560
  });
2339
2561
  await saveAccounts(accountsFile);
2562
+ if (isJsonOutput(opts)) {
2563
+ const address = publicKey ? toSs58(publicKey) : undefined;
2564
+ console.log(formatJson({ name, address, env: opts.env, path: path || undefined }));
2565
+ return;
2566
+ }
2340
2567
  printHeading("Account Imported");
2341
2568
  console.log(` ${BOLD}Name:${RESET} ${name}`);
2342
2569
  if (path)
@@ -2359,6 +2586,10 @@ async function accountImport(name, opts) {
2359
2586
  derivationPath: path
2360
2587
  });
2361
2588
  await saveAccounts(accountsFile);
2589
+ if (isJsonOutput(opts)) {
2590
+ console.log(formatJson({ name, address, publicKey: hexPub, path: path || undefined }));
2591
+ return;
2592
+ }
2362
2593
  printHeading("Account Imported");
2363
2594
  console.log(` ${BOLD}Name:${RESET} ${name}`);
2364
2595
  if (path)
@@ -2367,7 +2598,7 @@ async function accountImport(name, opts) {
2367
2598
  console.log();
2368
2599
  }
2369
2600
  }
2370
- async function accountAddWatchOnly(name, address) {
2601
+ async function accountAddWatchOnly(name, address, opts = {}) {
2371
2602
  if (!name) {
2372
2603
  console.error(`Account name is required.
2373
2604
  `);
@@ -2404,6 +2635,10 @@ async function accountAddWatchOnly(name, address) {
2404
2635
  derivationPath: ""
2405
2636
  });
2406
2637
  await saveAccounts(accountsFile);
2638
+ if (isJsonOutput(opts)) {
2639
+ console.log(formatJson({ name, address: toSs58(hexPub), watchOnly: true }));
2640
+ return;
2641
+ }
2407
2642
  printHeading("Account Added (watch-only)");
2408
2643
  console.log(` ${BOLD}Name:${RESET} ${name}`);
2409
2644
  console.log(` ${BOLD}Address:${RESET} ${toSs58(hexPub)}`);
@@ -2453,6 +2688,11 @@ async function accountDerive(sourceName, newName, opts) {
2453
2688
  derivationPath: path
2454
2689
  });
2455
2690
  await saveAccounts(accountsFile);
2691
+ if (isJsonOutput(opts)) {
2692
+ const address = publicKey ? toSs58(publicKey) : undefined;
2693
+ console.log(formatJson({ name: newName, source: sourceName, path, address, env: sourceSecret.env }));
2694
+ return;
2695
+ }
2456
2696
  printHeading("Account Derived");
2457
2697
  console.log(` ${BOLD}Name:${RESET} ${newName}`);
2458
2698
  console.log(` ${BOLD}Source:${RESET} ${sourceName}`);
@@ -2475,6 +2715,10 @@ async function accountDerive(sourceName, newName, opts) {
2475
2715
  derivationPath: path
2476
2716
  });
2477
2717
  await saveAccounts(accountsFile);
2718
+ if (isJsonOutput(opts)) {
2719
+ console.log(formatJson({ name: newName, source: sourceName, path, address, publicKey: hexPub }));
2720
+ return;
2721
+ }
2478
2722
  printHeading("Account Derived");
2479
2723
  console.log(` ${BOLD}Name:${RESET} ${newName}`);
2480
2724
  console.log(` ${BOLD}Source:${RESET} ${sourceName}`);
@@ -2483,14 +2727,43 @@ async function accountDerive(sourceName, newName, opts) {
2483
2727
  console.log();
2484
2728
  }
2485
2729
  }
2486
- async function accountList() {
2730
+ async function accountList(opts = {}) {
2731
+ const accountsFile = await loadAccounts();
2732
+ if (isJsonOutput(opts)) {
2733
+ const dev = DEV_NAMES.map((name) => ({
2734
+ name: name.charAt(0).toUpperCase() + name.slice(1),
2735
+ address: getDevAddress(name)
2736
+ }));
2737
+ const stored = accountsFile.accounts.map((account) => {
2738
+ let address;
2739
+ if (isWatchOnly(account)) {
2740
+ address = account.publicKey ? toSs58(account.publicKey) : undefined;
2741
+ } else if (account.secret !== undefined && isEnvSecret(account.secret)) {
2742
+ let pubKey = account.publicKey;
2743
+ if (!pubKey) {
2744
+ pubKey = tryDerivePublicKey(account.secret.env, account.derivationPath) ?? "";
2745
+ }
2746
+ address = pubKey ? toSs58(pubKey) : undefined;
2747
+ } else {
2748
+ address = toSs58(account.publicKey);
2749
+ }
2750
+ return {
2751
+ name: account.name,
2752
+ address,
2753
+ derivationPath: account.derivationPath || undefined,
2754
+ watchOnly: isWatchOnly(account),
2755
+ env: account.secret !== undefined && isEnvSecret(account.secret) ? account.secret.env : undefined
2756
+ };
2757
+ });
2758
+ console.log(formatJson({ dev, stored }));
2759
+ return;
2760
+ }
2487
2761
  printHeading("Dev Accounts");
2488
2762
  for (const name of DEV_NAMES) {
2489
2763
  const display = name.charAt(0).toUpperCase() + name.slice(1);
2490
2764
  const address = getDevAddress(name);
2491
2765
  printItem(display, address);
2492
2766
  }
2493
- const accountsFile = await loadAccounts();
2494
2767
  if (accountsFile.accounts.length > 0) {
2495
2768
  printHeading("Stored Accounts");
2496
2769
  for (const account of accountsFile.accounts) {
@@ -2520,7 +2793,7 @@ async function accountList() {
2520
2793
  }
2521
2794
  console.log();
2522
2795
  }
2523
- async function accountRemove(names) {
2796
+ async function accountRemove(names, opts = {}) {
2524
2797
  if (names.length === 0) {
2525
2798
  console.error(`At least one account name is required.
2526
2799
  `);
@@ -2555,6 +2828,10 @@ async function accountRemove(names) {
2555
2828
  accountsFile.accounts.splice(idx, 1);
2556
2829
  }
2557
2830
  await saveAccounts(accountsFile);
2831
+ if (isJsonOutput(opts)) {
2832
+ console.log(formatJson({ removed: names }));
2833
+ return;
2834
+ }
2558
2835
  for (const name of names) {
2559
2836
  console.log(`Account "${name}" removed.`);
2560
2837
  }
@@ -2573,6 +2850,7 @@ async function accountInspect(input, opts) {
2573
2850
  }
2574
2851
  let name;
2575
2852
  let publicKeyHex;
2853
+ let bandersnatch;
2576
2854
  if (isDevAccount(input)) {
2577
2855
  name = input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
2578
2856
  const devAddr = getDevAddress(input);
@@ -2582,6 +2860,7 @@ async function accountInspect(input, opts) {
2582
2860
  const account = findAccount(accountsFile, input);
2583
2861
  if (account) {
2584
2862
  name = account.name;
2863
+ bandersnatch = account.bandersnatch;
2585
2864
  if (account.publicKey) {
2586
2865
  publicKeyHex = account.publicKey;
2587
2866
  } else if (account.secret !== undefined && isEnvSecret(account.secret)) {
@@ -2608,10 +2887,12 @@ async function accountInspect(input, opts) {
2608
2887
  }
2609
2888
  }
2610
2889
  const ss58 = toSs58(publicKeyHex, prefix);
2611
- if (opts.output === "json") {
2890
+ if (isJsonOutput(opts)) {
2612
2891
  const result = { publicKey: publicKeyHex, ss58, prefix };
2613
2892
  if (name)
2614
2893
  result.name = name;
2894
+ if (bandersnatch && Object.keys(bandersnatch).length > 0)
2895
+ result.bandersnatch = bandersnatch;
2615
2896
  console.log(formatJson(result));
2616
2897
  } else {
2617
2898
  printHeading("Account Info");
@@ -2619,6 +2900,18 @@ async function accountInspect(input, opts) {
2619
2900
  console.log(` ${BOLD}Name:${RESET} ${name}`);
2620
2901
  console.log(` ${BOLD}Public Key:${RESET} ${publicKeyHex}`);
2621
2902
  console.log(` ${BOLD}SS58:${RESET} ${ss58}`);
2903
+ if (bandersnatch && Object.keys(bandersnatch).length > 0) {
2904
+ const entries = Object.entries(bandersnatch);
2905
+ for (let i = 0;i < entries.length; i++) {
2906
+ const [key, hex] = entries[i];
2907
+ const label = key ? `(${key})` : "";
2908
+ if (i === 0) {
2909
+ console.log(` ${BOLD}Bandersnatch:${RESET}${label ? ` ${label}` : ""} ${hex}`);
2910
+ } else {
2911
+ console.log(` ${label ? `${label} ` : ""}${hex}`);
2912
+ }
2913
+ }
2914
+ }
2622
2915
  console.log(` ${BOLD}Prefix:${RESET} ${prefix}`);
2623
2916
  console.log();
2624
2917
  }
@@ -2637,6 +2930,13 @@ async function handleApis2(target, args, opts) {
2637
2930
  const { name: chainName2, chain: chainConfig2 } = resolveChain(config2, opts.chain);
2638
2931
  const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
2639
2932
  const apis = listRuntimeApis(meta);
2933
+ if (isJsonOutput(opts)) {
2934
+ console.log(formatJson({
2935
+ chain: chainName2,
2936
+ apis: apis.map((a) => ({ name: a.name, methods: a.methods.length }))
2937
+ }));
2938
+ return;
2939
+ }
2640
2940
  printHeading(`Runtime APIs on ${chainName2} (${apis.length})`);
2641
2941
  for (const api of apis) {
2642
2942
  printItem(api.name, `${api.methods.length} methods`);
@@ -2659,7 +2959,24 @@ async function handleApis2(target, args, opts) {
2659
2959
  const meta = await loadMeta(chainName, chainConfig, opts.rpc);
2660
2960
  const api = resolveRuntimeApi2(meta, apiName);
2661
2961
  if (api.methods.length === 0) {
2662
- console.log(`No methods in ${api.name}.`);
2962
+ if (isJsonOutput(opts)) {
2963
+ console.log(formatJson({ chain: chainName, api: api.name, methods: [] }));
2964
+ } else {
2965
+ console.log(`No methods in ${api.name}.`);
2966
+ }
2967
+ return;
2968
+ }
2969
+ if (isJsonOutput(opts)) {
2970
+ console.log(formatJson({
2971
+ chain: chainName,
2972
+ api: api.name,
2973
+ methods: api.methods.map((m) => ({
2974
+ name: m.name,
2975
+ args: describeRuntimeApiMethodArgs(meta, m),
2976
+ returns: describeType(meta.lookup, m.output),
2977
+ docs: firstSentence(m.docs)
2978
+ }))
2979
+ }));
2663
2980
  return;
2664
2981
  }
2665
2982
  printHeading(`${api.name} Methods`);
@@ -2690,7 +3007,7 @@ async function handleApis2(target, args, opts) {
2690
3007
  const parsedArgs = await parseRuntimeApiArgs2(meta, method, effectiveArgs);
2691
3008
  const unsafeApi = clientHandle.client.getUnsafeApi();
2692
3009
  const result = await unsafeApi.apis[api.name][method.name](...parsedArgs);
2693
- const format = opts.output ?? "pretty";
3010
+ const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
2694
3011
  printResult(result, format);
2695
3012
  } finally {
2696
3013
  clientHandle.destroy();
@@ -2726,7 +3043,6 @@ init_output();
2726
3043
  var CHAIN_HELP = `
2727
3044
  ${BOLD}Usage:${RESET}
2728
3045
  $ dot chain add <name> --rpc <url> Add a chain via WebSocket RPC
2729
- $ dot chain add <name> --light-client Add a chain via Smoldot light client
2730
3046
  $ dot chain remove <name> Remove a chain
2731
3047
  $ dot chain update [name] Re-fetch metadata (default chain if omitted)
2732
3048
  $ dot chain update --all Re-fetch metadata for all configured chains
@@ -2736,7 +3052,8 @@ ${BOLD}Usage:${RESET}
2736
3052
  ${BOLD}Examples:${RESET}
2737
3053
  $ dot chain add kusama --rpc wss://kusama-rpc.polkadot.io
2738
3054
  $ dot chain add kusama --rpc wss://kusama-rpc.polkadot.io --rpc wss://kusama-rpc.dwellir.com
2739
- $ dot chain add westend --light-client
3055
+ $ dot chain add my-para --rpc wss://rpc.example.com --relay polkadot
3056
+ $ dot chain add my-para --rpc wss://rpc.example.com --relay polkadot --parachain-id 2000
2740
3057
  $ dot chain default kusama
2741
3058
  $ dot chain list
2742
3059
  $ dot chain update
@@ -2745,10 +3062,10 @@ ${BOLD}Examples:${RESET}
2745
3062
  $ dot chain remove kusama
2746
3063
  `.trimStart();
2747
3064
  function registerChainCommands(cli) {
2748
- cli.command("chain [action] [name]", "Manage chains (add, remove, update, list, default)").alias("chains").option("--all", "Update all configured chains").action(async (action, name, opts) => {
3065
+ cli.command("chain [action] [name]", "Manage chains (add, remove, update, list, default)").alias("chains").option("--all", "Update all configured chains").option("--relay <name>", "Parent relay chain for this parachain").option("--parachain-id <id>", "Parachain ID (auto-detected if omitted with --relay)").action(async (action, name, opts) => {
2749
3066
  if (!action) {
2750
3067
  if (process.argv[2] === "chains")
2751
- return chainList();
3068
+ return chainList(opts);
2752
3069
  console.log(CHAIN_HELP);
2753
3070
  return;
2754
3071
  }
@@ -2756,13 +3073,13 @@ function registerChainCommands(cli) {
2756
3073
  case "add":
2757
3074
  return chainAdd(name, opts);
2758
3075
  case "remove":
2759
- return chainRemove(name);
3076
+ return chainRemove(name, opts);
2760
3077
  case "list":
2761
- return chainList();
3078
+ return chainList(opts);
2762
3079
  case "update":
2763
3080
  return chainUpdate(name, opts);
2764
3081
  case "default":
2765
- return chainDefault(name);
3082
+ return chainDefault(name, opts);
2766
3083
  default:
2767
3084
  console.error(`Unknown action "${action}".
2768
3085
  `);
@@ -2776,34 +3093,70 @@ async function chainAdd(name, opts) {
2776
3093
  console.error(`Chain name is required.
2777
3094
  `);
2778
3095
  console.error("Usage: dot chain add <name> --rpc <url>");
2779
- console.error(" dot chain add <name> --light-client");
2780
3096
  process.exit(1);
2781
3097
  }
2782
- if (!opts.rpc && !opts.lightClient) {
2783
- console.error(`Must provide either --rpc <url> or --light-client.
3098
+ if (!opts.rpc) {
3099
+ console.error(`Must provide --rpc <url>.
2784
3100
  `);
2785
3101
  console.error("Usage: dot chain add <name> --rpc <url>");
2786
- console.error(" dot chain add <name> --light-client");
2787
3102
  process.exit(1);
2788
3103
  }
2789
- const chainConfig = {
2790
- rpc: opts.rpc ?? "",
2791
- ...opts.lightClient ? { lightClient: true } : {}
2792
- };
2793
- console.error(`Connecting to ${name}...`);
3104
+ const parachainIdRaw = opts.parachainId != null ? Number(opts.parachainId) : undefined;
3105
+ if (parachainIdRaw != null && !opts.relay) {
3106
+ console.error(`Cannot set --parachain-id without --relay.
3107
+ `);
3108
+ console.error("Usage: dot chain add <name> --rpc <url> --relay <relay> --parachain-id <id>");
3109
+ process.exit(1);
3110
+ }
3111
+ const chainConfig = { rpc: opts.rpc };
3112
+ process.stderr.write(`Connecting to ${name}...
3113
+ `);
2794
3114
  const clientHandle = await createChainClient(name, chainConfig, opts.rpc);
2795
3115
  try {
2796
- console.error("Fetching metadata...");
3116
+ process.stderr.write(`Fetching metadata...
3117
+ `);
2797
3118
  await fetchMetadataFromChain(clientHandle, name);
3119
+ if (opts.relay) {
3120
+ const config2 = await loadConfig();
3121
+ const relayResolved = findChainName(config2, opts.relay);
3122
+ if (!relayResolved) {
3123
+ throw new Error(`Relay chain "${opts.relay}" not found. Add it first with: dot chain add ${opts.relay} --rpc <url>`);
3124
+ }
3125
+ chainConfig.relay = relayResolved;
3126
+ if (parachainIdRaw != null) {
3127
+ chainConfig.parachainId = parachainIdRaw;
3128
+ } else {
3129
+ process.stderr.write(`Detecting parachain ID...
3130
+ `);
3131
+ const detected = await getParachainId(clientHandle);
3132
+ if (detected != null) {
3133
+ chainConfig.parachainId = detected;
3134
+ process.stderr.write(`Detected parachain ID: ${detected}
3135
+ `);
3136
+ } else {
3137
+ process.stderr.write(`Could not detect parachain ID. Use --parachain-id to set it manually.
3138
+ `);
3139
+ }
3140
+ }
3141
+ }
2798
3142
  const config = await loadConfig();
2799
3143
  config.chains[name] = chainConfig;
2800
3144
  await saveConfig(config);
2801
- console.log(`Chain "${name}" added successfully.`);
3145
+ const result = { action: "added", chain: name };
3146
+ if (chainConfig.relay)
3147
+ result.relay = chainConfig.relay;
3148
+ if (chainConfig.parachainId != null)
3149
+ result.parachainId = chainConfig.parachainId;
3150
+ if (isJsonOutput(opts)) {
3151
+ console.log(formatJson(result));
3152
+ } else {
3153
+ console.log(`Chain "${name}" added successfully.`);
3154
+ }
2802
3155
  } finally {
2803
3156
  clientHandle.destroy();
2804
3157
  }
2805
3158
  }
2806
- async function chainRemove(name) {
3159
+ async function chainRemove(name, opts = {}) {
2807
3160
  if (!name) {
2808
3161
  console.error("Usage: dot chain remove <name>");
2809
3162
  process.exit(1);
@@ -2816,32 +3169,85 @@ async function chainRemove(name) {
2816
3169
  if (BUILTIN_CHAIN_NAMES.has(resolved)) {
2817
3170
  throw new Error(`Cannot remove the built-in "${resolved}" chain.`);
2818
3171
  }
3172
+ const orphans = Object.entries(config.chains).filter(([, c]) => c.relay === resolved).map(([n]) => n);
3173
+ if (orphans.length > 0) {
3174
+ console.error(`Warning: ${orphans.length} chain(s) reference "${resolved}" as their relay: ${orphans.join(", ")}`);
3175
+ }
2819
3176
  delete config.chains[resolved];
2820
3177
  if (config.defaultChain === resolved) {
2821
3178
  config.defaultChain = "polkadot";
2822
- console.log(`Default chain reset to "polkadot".`);
3179
+ if (!isJsonOutput(opts))
3180
+ console.log(`Default chain reset to "polkadot".`);
2823
3181
  }
2824
3182
  await saveConfig(config);
2825
3183
  await removeChainData(resolved);
2826
- console.log(`Chain "${resolved}" removed.`);
3184
+ if (isJsonOutput(opts)) {
3185
+ console.log(formatJson({ action: "removed", chain: resolved }));
3186
+ } else {
3187
+ console.log(`Chain "${resolved}" removed.`);
3188
+ }
2827
3189
  }
2828
- async function chainList() {
3190
+ async function chainList(opts = {}) {
2829
3191
  const config = await loadConfig();
3192
+ if (isJsonOutput(opts)) {
3193
+ const chains = Object.entries(config.chains).map(([name, chainConfig]) => ({
3194
+ name,
3195
+ default: name === config.defaultChain,
3196
+ rpc: Array.isArray(chainConfig.rpc) ? chainConfig.rpc : [chainConfig.rpc],
3197
+ ...chainConfig.relay && { relay: chainConfig.relay },
3198
+ ...chainConfig.parachainId != null && { parachainId: chainConfig.parachainId }
3199
+ }));
3200
+ console.log(formatJson({ chains }));
3201
+ return;
3202
+ }
2830
3203
  printHeading("Configured Chains");
3204
+ const parachainsByRelay = new Map;
3205
+ const standalone = [];
2831
3206
  for (const [name, chainConfig] of Object.entries(config.chains)) {
2832
- const isDefault = name === config.defaultChain;
2833
- const marker = isDefault ? ` ${BOLD}(default)${RESET}` : "";
2834
- if (chainConfig.lightClient) {
2835
- console.log(` ${CYAN}${name}${RESET}${marker} ${DIM}light-client${RESET}`);
2836
- } else {
2837
- const rpcs = Array.isArray(chainConfig.rpc) ? chainConfig.rpc : [chainConfig.rpc];
2838
- console.log(` ${CYAN}${name}${RESET}${marker} ${DIM}${rpcs[0]}${RESET}`);
2839
- for (let i = 1;i < rpcs.length; i++) {
2840
- console.log(` ${DIM}${rpcs[i]}${RESET}`);
2841
- }
3207
+ if (chainConfig.relay) {
3208
+ const paras = parachainsByRelay.get(chainConfig.relay) ?? [];
3209
+ paras.push([name, chainConfig]);
3210
+ parachainsByRelay.set(chainConfig.relay, paras);
2842
3211
  }
2843
3212
  }
2844
- console.log();
3213
+ const relayNames = new Set(parachainsByRelay.keys());
3214
+ for (const [name, chainConfig] of Object.entries(config.chains)) {
3215
+ if (relayNames.has(name))
3216
+ continue;
3217
+ if (chainConfig.relay)
3218
+ continue;
3219
+ standalone.push([name, chainConfig]);
3220
+ }
3221
+ for (const relayName of relayNames) {
3222
+ const relayConfig = config.chains[relayName];
3223
+ if (relayConfig) {
3224
+ printChainLine(" ", relayName, relayConfig, config.defaultChain);
3225
+ }
3226
+ const paras = parachainsByRelay.get(relayName) ?? [];
3227
+ for (let i = 0;i < paras.length; i++) {
3228
+ const [name, chainConfig] = paras[i];
3229
+ const isLast = i === paras.length - 1;
3230
+ const prefix = isLast ? " └─ " : " ├─ ";
3231
+ const idSuffix = chainConfig.parachainId != null ? ` ${DIM}[${chainConfig.parachainId}]${RESET}` : "";
3232
+ printChainLine(prefix, name, chainConfig, config.defaultChain, idSuffix);
3233
+ }
3234
+ console.log();
3235
+ }
3236
+ for (const [name, chainConfig] of standalone) {
3237
+ printChainLine(" ", name, chainConfig, config.defaultChain);
3238
+ }
3239
+ if (standalone.length > 0)
3240
+ console.log();
3241
+ }
3242
+ function printChainLine(prefix, name, chainConfig, defaultChain, suffix = "") {
3243
+ const isDefault = name === defaultChain;
3244
+ const marker = isDefault ? ` ${BOLD}(default)${RESET}` : "";
3245
+ const rpcs = Array.isArray(chainConfig.rpc) ? chainConfig.rpc : [chainConfig.rpc];
3246
+ console.log(`${prefix}${CYAN}${name}${RESET}${suffix}${marker} ${DIM}${rpcs[0]}${RESET}`);
3247
+ const indent = prefix.replace(/[^\s]/g, " ");
3248
+ for (let i = 1;i < rpcs.length; i++) {
3249
+ console.log(`${indent} ${DIM}${rpcs[i]}${RESET}`);
3250
+ }
2845
3251
  }
2846
3252
  async function chainUpdate(name, opts) {
2847
3253
  const config = await loadConfig();
@@ -2850,19 +3256,26 @@ async function chainUpdate(name, opts) {
2850
3256
  return;
2851
3257
  }
2852
3258
  const { name: chainName, chain: chainConfig } = resolveChain(config, name);
2853
- console.error(`Connecting to ${chainName}...`);
3259
+ process.stderr.write(`Connecting to ${chainName}...
3260
+ `);
2854
3261
  const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
2855
3262
  try {
2856
- console.error("Fetching metadata...");
3263
+ process.stderr.write(`Fetching metadata...
3264
+ `);
2857
3265
  await fetchMetadataFromChain(clientHandle, chainName);
2858
- console.log(`Metadata for "${chainName}" updated.`);
3266
+ if (isJsonOutput(opts)) {
3267
+ console.log(formatJson({ action: "updated", chain: chainName }));
3268
+ } else {
3269
+ console.log(`Metadata for "${chainName}" updated.`);
3270
+ }
2859
3271
  } finally {
2860
3272
  clientHandle.destroy();
2861
3273
  }
2862
3274
  }
2863
3275
  async function chainUpdateAll(config) {
2864
3276
  const chainNames = Object.keys(config.chains).sort();
2865
- console.error(`Updating metadata for ${chainNames.length} chains...
3277
+ process.stderr.write(`Updating metadata for ${chainNames.length} chains...
3278
+
2866
3279
  `);
2867
3280
  const results = await Promise.allSettled(chainNames.map(async (chainName) => {
2868
3281
  const chainConfig = config.chains[chainName];
@@ -2889,7 +3302,7 @@ ${failed} of ${chainNames.length} chains failed to update.`);
2889
3302
  process.exit(1);
2890
3303
  }
2891
3304
  }
2892
- async function chainDefault(name) {
3305
+ async function chainDefault(name, opts = {}) {
2893
3306
  if (!name) {
2894
3307
  console.error("Usage: dot chain default <name>");
2895
3308
  process.exit(1);
@@ -2902,7 +3315,11 @@ async function chainDefault(name) {
2902
3315
  }
2903
3316
  config.defaultChain = resolved;
2904
3317
  await saveConfig(config);
2905
- console.log(`Default chain set to "${resolved}".`);
3318
+ if (isJsonOutput(opts)) {
3319
+ console.log(formatJson({ action: "default", chain: resolved }));
3320
+ } else {
3321
+ console.log(`Default chain set to "${resolved}".`);
3322
+ }
2906
3323
  }
2907
3324
 
2908
3325
  // src/commands/completions.ts
@@ -3002,6 +3419,13 @@ async function handleConst(target, opts) {
3002
3419
  const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
3003
3420
  const pallets = listPallets(meta);
3004
3421
  const withConsts = pallets.filter((p) => p.constants.length > 0);
3422
+ if (isJsonOutput(opts)) {
3423
+ console.log(formatJson({
3424
+ chain: chainName2,
3425
+ pallets: withConsts.map((p) => ({ name: p.name, constants: p.constants.length }))
3426
+ }));
3427
+ return;
3428
+ }
3005
3429
  printHeading(`Pallets with constants on ${chainName2} (${withConsts.length})`);
3006
3430
  for (const p of withConsts) {
3007
3431
  printItem(p.name, `${p.constants.length} constants`);
@@ -3022,7 +3446,23 @@ async function handleConst(target, opts) {
3022
3446
  throw new Error(suggestMessage("pallet", pallet, palletNames));
3023
3447
  }
3024
3448
  if (palletInfo.constants.length === 0) {
3025
- console.log(`No constants in ${palletInfo.name}.`);
3449
+ if (isJsonOutput(opts)) {
3450
+ console.log(formatJson({ chain: chainName, pallet: palletInfo.name, constants: [] }));
3451
+ } else {
3452
+ console.log(`No constants in ${palletInfo.name}.`);
3453
+ }
3454
+ return;
3455
+ }
3456
+ if (isJsonOutput(opts)) {
3457
+ console.log(formatJson({
3458
+ chain: chainName,
3459
+ pallet: palletInfo.name,
3460
+ constants: palletInfo.constants.map((c) => ({
3461
+ name: c.name,
3462
+ type: describeType(meta.lookup, c.typeId),
3463
+ docs: firstSentence(c.docs)
3464
+ }))
3465
+ }));
3026
3466
  return;
3027
3467
  }
3028
3468
  printHeading(`${palletInfo.name} Constants`);
@@ -3051,9 +3491,9 @@ async function handleConst(target, opts) {
3051
3491
  throw new Error(suggestMessage(`constant in ${palletInfo.name}`, item, constNames));
3052
3492
  }
3053
3493
  const unsafeApi = clientHandle.client.getUnsafeApi();
3054
- const runtimeToken = await unsafeApi.runtimeToken;
3055
- const result = unsafeApi.constants[palletInfo.name][constantItem.name](runtimeToken);
3056
- const format = opts.output ?? "pretty";
3494
+ const staticApis = await unsafeApi.getStaticApis();
3495
+ const result = staticApis.constants[palletInfo.name][constantItem.name];
3496
+ const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
3057
3497
  printResult(result, format);
3058
3498
  } finally {
3059
3499
  clientHandle.destroy();
@@ -3069,7 +3509,8 @@ async function loadMeta2(chainName, chainConfig, rpcOverride) {
3069
3509
  try {
3070
3510
  return await getOrFetchMetadata(chainName);
3071
3511
  } catch {
3072
- console.error(`Fetching metadata from ${chainName}...`);
3512
+ process.stderr.write(`Fetching metadata from ${chainName}...
3513
+ `);
3073
3514
  const clientHandle = await createChainClient(chainName, chainConfig, rpcOverride);
3074
3515
  try {
3075
3516
  return await getOrFetchMetadata(chainName, clientHandle);
@@ -3093,6 +3534,13 @@ async function handleCalls2(target, opts) {
3093
3534
  const meta2 = await loadMeta2(chainName2, chainConfig2, opts.rpc);
3094
3535
  const pallets = listPallets(meta2);
3095
3536
  const withCalls = pallets.filter((p) => p.calls.length > 0);
3537
+ if (isJsonOutput(opts)) {
3538
+ console.log(formatJson({
3539
+ chain: chainName2,
3540
+ pallets: withCalls.map((p) => ({ name: p.name, calls: p.calls.length }))
3541
+ }));
3542
+ return;
3543
+ }
3096
3544
  printHeading(`Pallets with calls on ${chainName2} (${withCalls.length})`);
3097
3545
  for (const p of withCalls) {
3098
3546
  printItem(p.name, `${p.calls.length} calls`);
@@ -3109,7 +3557,23 @@ async function handleCalls2(target, opts) {
3109
3557
  const pallet = resolvePallet2(meta, palletName);
3110
3558
  if (!itemName) {
3111
3559
  if (pallet.calls.length === 0) {
3112
- console.log(`No calls in ${pallet.name}.`);
3560
+ if (isJsonOutput(opts)) {
3561
+ console.log(formatJson({ chain: chainName, pallet: pallet.name, calls: [] }));
3562
+ } else {
3563
+ console.log(`No calls in ${pallet.name}.`);
3564
+ }
3565
+ return;
3566
+ }
3567
+ if (isJsonOutput(opts)) {
3568
+ console.log(formatJson({
3569
+ chain: chainName,
3570
+ pallet: pallet.name,
3571
+ calls: pallet.calls.map((c) => ({
3572
+ name: c.name,
3573
+ args: describeCallArgs(meta, pallet.name, c.name),
3574
+ docs: firstSentence(c.docs)
3575
+ }))
3576
+ }));
3113
3577
  return;
3114
3578
  }
3115
3579
  printHeading(`${pallet.name} Calls`);
@@ -3129,6 +3593,17 @@ async function handleCalls2(target, opts) {
3129
3593
  const names = pallet.calls.map((c) => c.name);
3130
3594
  throw new Error(suggestMessage(`call in ${pallet.name}`, itemName, names));
3131
3595
  }
3596
+ if (isJsonOutput(opts)) {
3597
+ console.log(formatJson({
3598
+ chain: chainName,
3599
+ pallet: pallet.name,
3600
+ item: callItem.name,
3601
+ category: "call",
3602
+ args: describeCallArgs(meta, pallet.name, callItem.name),
3603
+ docs: callItem.docs
3604
+ }));
3605
+ return;
3606
+ }
3132
3607
  printHeading(`${pallet.name}.${callItem.name} (Call)`);
3133
3608
  const args = describeCallArgs(meta, pallet.name, callItem.name);
3134
3609
  console.log(` ${BOLD}Args:${RESET} ${args}`);
@@ -3145,6 +3620,13 @@ async function handleEvents2(target, opts) {
3145
3620
  const meta2 = await loadMeta2(chainName2, chainConfig2, opts.rpc);
3146
3621
  const pallets = listPallets(meta2);
3147
3622
  const withEvents = pallets.filter((p) => p.events.length > 0);
3623
+ if (isJsonOutput(opts)) {
3624
+ console.log(formatJson({
3625
+ chain: chainName2,
3626
+ pallets: withEvents.map((p) => ({ name: p.name, events: p.events.length }))
3627
+ }));
3628
+ return;
3629
+ }
3148
3630
  printHeading(`Pallets with events on ${chainName2} (${withEvents.length})`);
3149
3631
  for (const p of withEvents) {
3150
3632
  printItem(p.name, `${p.events.length} events`);
@@ -3161,7 +3643,23 @@ async function handleEvents2(target, opts) {
3161
3643
  const pallet = resolvePallet2(meta, palletName);
3162
3644
  if (!itemName) {
3163
3645
  if (pallet.events.length === 0) {
3164
- console.log(`No events in ${pallet.name}.`);
3646
+ if (isJsonOutput(opts)) {
3647
+ console.log(formatJson({ chain: chainName, pallet: pallet.name, events: [] }));
3648
+ } else {
3649
+ console.log(`No events in ${pallet.name}.`);
3650
+ }
3651
+ return;
3652
+ }
3653
+ if (isJsonOutput(opts)) {
3654
+ console.log(formatJson({
3655
+ chain: chainName,
3656
+ pallet: pallet.name,
3657
+ events: pallet.events.map((e) => ({
3658
+ name: e.name,
3659
+ fields: describeEventFields(meta, pallet.name, e.name),
3660
+ docs: firstSentence(e.docs)
3661
+ }))
3662
+ }));
3165
3663
  return;
3166
3664
  }
3167
3665
  printHeading(`${pallet.name} Events`);
@@ -3181,6 +3679,17 @@ async function handleEvents2(target, opts) {
3181
3679
  const names = pallet.events.map((e) => e.name);
3182
3680
  throw new Error(suggestMessage(`event in ${pallet.name}`, itemName, names));
3183
3681
  }
3682
+ if (isJsonOutput(opts)) {
3683
+ console.log(formatJson({
3684
+ chain: chainName,
3685
+ pallet: pallet.name,
3686
+ item: eventItem.name,
3687
+ category: "event",
3688
+ fields: describeEventFields(meta, pallet.name, eventItem.name),
3689
+ docs: eventItem.docs
3690
+ }));
3691
+ return;
3692
+ }
3184
3693
  printHeading(`${pallet.name}.${eventItem.name} (Event)`);
3185
3694
  const fields = describeEventFields(meta, pallet.name, eventItem.name);
3186
3695
  console.log(` ${BOLD}Fields:${RESET} ${fields}`);
@@ -3197,6 +3706,13 @@ async function handleErrors2(target, opts) {
3197
3706
  const meta2 = await loadMeta2(chainName2, chainConfig2, opts.rpc);
3198
3707
  const pallets = listPallets(meta2);
3199
3708
  const withErrors = pallets.filter((p) => p.errors.length > 0);
3709
+ if (isJsonOutput(opts)) {
3710
+ console.log(formatJson({
3711
+ chain: chainName2,
3712
+ pallets: withErrors.map((p) => ({ name: p.name, errors: p.errors.length }))
3713
+ }));
3714
+ return;
3715
+ }
3200
3716
  printHeading(`Pallets with errors on ${chainName2} (${withErrors.length})`);
3201
3717
  for (const p of withErrors) {
3202
3718
  printItem(p.name, `${p.errors.length} errors`);
@@ -3213,7 +3729,19 @@ async function handleErrors2(target, opts) {
3213
3729
  const pallet = resolvePallet2(meta, palletName);
3214
3730
  if (!itemName) {
3215
3731
  if (pallet.errors.length === 0) {
3216
- console.log(`No errors in ${pallet.name}.`);
3732
+ if (isJsonOutput(opts)) {
3733
+ console.log(formatJson({ chain: chainName, pallet: pallet.name, errors: [] }));
3734
+ } else {
3735
+ console.log(`No errors in ${pallet.name}.`);
3736
+ }
3737
+ return;
3738
+ }
3739
+ if (isJsonOutput(opts)) {
3740
+ console.log(formatJson({
3741
+ chain: chainName,
3742
+ pallet: pallet.name,
3743
+ errors: pallet.errors.map((e) => ({ name: e.name, docs: firstSentence(e.docs) }))
3744
+ }));
3217
3745
  return;
3218
3746
  }
3219
3747
  printHeading(`${pallet.name} Errors`);
@@ -3232,6 +3760,16 @@ async function handleErrors2(target, opts) {
3232
3760
  const names = pallet.errors.map((e) => e.name);
3233
3761
  throw new Error(suggestMessage(`error in ${pallet.name}`, itemName, names));
3234
3762
  }
3763
+ if (isJsonOutput(opts)) {
3764
+ console.log(formatJson({
3765
+ chain: chainName,
3766
+ pallet: pallet.name,
3767
+ item: errorItem.name,
3768
+ category: "error",
3769
+ docs: errorItem.docs
3770
+ }));
3771
+ return;
3772
+ }
3235
3773
  printHeading(`${pallet.name}.${errorItem.name} (Error)`);
3236
3774
  if (errorItem.docs.length) {
3237
3775
  printDocs(errorItem.docs);
@@ -3245,6 +3783,13 @@ async function handleStorage2(target, opts) {
3245
3783
  const meta2 = await loadMeta2(chainName2, chainConfig2, opts.rpc);
3246
3784
  const pallets = listPallets(meta2);
3247
3785
  const withStorage = pallets.filter((p) => p.storage.length > 0);
3786
+ if (isJsonOutput(opts)) {
3787
+ console.log(formatJson({
3788
+ chain: chainName2,
3789
+ pallets: withStorage.map((p) => ({ name: p.name, storage: p.storage.length }))
3790
+ }));
3791
+ return;
3792
+ }
3248
3793
  printHeading(`Pallets with storage on ${chainName2} (${withStorage.length})`);
3249
3794
  for (const p of withStorage) {
3250
3795
  printItem(p.name, `${p.storage.length} storage`);
@@ -3261,7 +3806,23 @@ async function handleStorage2(target, opts) {
3261
3806
  const pallet = resolvePallet2(meta, palletName);
3262
3807
  if (!itemName) {
3263
3808
  if (pallet.storage.length === 0) {
3264
- console.log(`No storage items in ${pallet.name}.`);
3809
+ if (isJsonOutput(opts)) {
3810
+ console.log(formatJson({ chain: chainName, pallet: pallet.name, storage: [] }));
3811
+ } else {
3812
+ console.log(`No storage items in ${pallet.name}.`);
3813
+ }
3814
+ return;
3815
+ }
3816
+ if (isJsonOutput(opts)) {
3817
+ console.log(formatJson({
3818
+ chain: chainName,
3819
+ pallet: pallet.name,
3820
+ storage: pallet.storage.map((s) => {
3821
+ const valueType = describeType(meta.lookup, s.valueTypeId);
3822
+ const keyType = s.keyTypeId != null ? describeType(meta.lookup, s.keyTypeId) : undefined;
3823
+ return { name: s.name, type: s.type, valueType, keyType, docs: firstSentence(s.docs) };
3824
+ })
3825
+ }));
3265
3826
  return;
3266
3827
  }
3267
3828
  printHeading(`${pallet.name} Storage`);
@@ -3288,6 +3849,21 @@ async function handleStorage2(target, opts) {
3288
3849
  const names = pallet.storage.map((s) => s.name);
3289
3850
  throw new Error(suggestMessage(`storage item in ${pallet.name}`, itemName, names));
3290
3851
  }
3852
+ if (isJsonOutput(opts)) {
3853
+ const valueType = describeType(meta.lookup, storageItem.valueTypeId);
3854
+ const keyType = storageItem.keyTypeId != null ? describeType(meta.lookup, storageItem.keyTypeId) : undefined;
3855
+ console.log(formatJson({
3856
+ chain: chainName,
3857
+ pallet: pallet.name,
3858
+ item: storageItem.name,
3859
+ category: "storage",
3860
+ type: storageItem.type,
3861
+ valueType,
3862
+ keyType,
3863
+ docs: storageItem.docs
3864
+ }));
3865
+ return;
3866
+ }
3291
3867
  printHeading(`${pallet.name}.${storageItem.name} (Storage)`);
3292
3868
  console.log(` ${BOLD}Type:${RESET} ${storageItem.type}`);
3293
3869
  console.log(` ${BOLD}Value:${RESET} ${describeType(meta.lookup, storageItem.valueTypeId)}`);
@@ -3415,7 +3991,6 @@ async function showItemHelp2(category, target, opts) {
3415
3991
  console.log();
3416
3992
  console.log(`${BOLD}Options:${RESET}`);
3417
3993
  console.log(` --dump Dump all entries of a map (required for keyless map queries)`);
3418
- console.log(` --limit <n> Max entries for map queries (0 = unlimited, default: 100)`);
3419
3994
  console.log();
3420
3995
  return;
3421
3996
  }
@@ -3532,8 +4107,7 @@ function registerHashCommand(cli) {
3532
4107
  const input = await resolveInput(data, opts);
3533
4108
  const hash = computeHash(algorithm, input);
3534
4109
  const hexHash = toHex2(hash);
3535
- const format = opts.output ?? "pretty";
3536
- if (format === "json") {
4110
+ if (isJsonOutput(opts)) {
3537
4111
  printResult({
3538
4112
  algorithm,
3539
4113
  input: data ?? (opts.file ? `file:${opts.file}` : "stdin"),
@@ -3619,7 +4193,8 @@ function registerInspectCommand(cli) {
3619
4193
  try {
3620
4194
  meta = await getOrFetchMetadata(chainName);
3621
4195
  } catch {
3622
- console.error(`Fetching metadata from ${chainName}...`);
4196
+ process.stderr.write(`Fetching metadata from ${chainName}...
4197
+ `);
3623
4198
  const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
3624
4199
  try {
3625
4200
  meta = await getOrFetchMetadata(chainName, clientHandle);
@@ -3629,6 +4204,20 @@ function registerInspectCommand(cli) {
3629
4204
  }
3630
4205
  if (!target) {
3631
4206
  const pallets = listPallets(meta);
4207
+ if (isJsonOutput(opts)) {
4208
+ console.log(formatJson({
4209
+ chain: chainName,
4210
+ pallets: pallets.map((p) => ({
4211
+ name: p.name,
4212
+ storage: p.storage.length,
4213
+ constants: p.constants.length,
4214
+ calls: p.calls.length,
4215
+ events: p.events.length,
4216
+ errors: p.errors.length
4217
+ }))
4218
+ }));
4219
+ return;
4220
+ }
3632
4221
  printHeading(`Pallets on ${chainName} (${pallets.length})`);
3633
4222
  for (const p of pallets) {
3634
4223
  const counts = [];
@@ -3653,6 +4242,44 @@ function registerInspectCommand(cli) {
3653
4242
  if (!pallet2) {
3654
4243
  throw new Error(suggestMessage("pallet", palletName, palletNames2));
3655
4244
  }
4245
+ if (isJsonOutput(opts)) {
4246
+ console.log(formatJson({
4247
+ chain: chainName,
4248
+ pallet: pallet2.name,
4249
+ docs: pallet2.docs,
4250
+ storage: pallet2.storage.map((s) => {
4251
+ const valueType = describeType(meta.lookup, s.valueTypeId);
4252
+ const keyType = s.keyTypeId != null ? describeType(meta.lookup, s.keyTypeId) : undefined;
4253
+ return {
4254
+ name: s.name,
4255
+ type: s.type,
4256
+ valueType,
4257
+ keyType,
4258
+ docs: firstSentence(s.docs)
4259
+ };
4260
+ }),
4261
+ constants: pallet2.constants.map((c) => ({
4262
+ name: c.name,
4263
+ type: describeType(meta.lookup, c.typeId),
4264
+ docs: firstSentence(c.docs)
4265
+ })),
4266
+ calls: pallet2.calls.map((c) => ({
4267
+ name: c.name,
4268
+ args: describeCallArgs(meta, pallet2.name, c.name),
4269
+ docs: firstSentence(c.docs)
4270
+ })),
4271
+ events: pallet2.events.map((e) => ({
4272
+ name: e.name,
4273
+ fields: describeEventFields(meta, pallet2.name, e.name),
4274
+ docs: firstSentence(e.docs)
4275
+ })),
4276
+ errors: pallet2.errors.map((e) => ({
4277
+ name: e.name,
4278
+ docs: firstSentence(e.docs)
4279
+ }))
4280
+ }));
4281
+ return;
4282
+ }
3656
4283
  printHeading(`${pallet2.name} Pallet`);
3657
4284
  if (pallet2.docs.length) {
3658
4285
  printDocs(pallet2.docs);
@@ -3731,6 +4358,79 @@ function registerInspectCommand(cli) {
3731
4358
  if (!pallet) {
3732
4359
  throw new Error(suggestMessage("pallet", palletName, palletNames));
3733
4360
  }
4361
+ if (isJsonOutput(opts)) {
4362
+ const si = pallet.storage.find((s) => s.name.toLowerCase() === itemName.toLowerCase());
4363
+ if (si) {
4364
+ const valueType = describeType(meta.lookup, si.valueTypeId);
4365
+ const keyType = si.keyTypeId != null ? describeType(meta.lookup, si.keyTypeId) : undefined;
4366
+ console.log(formatJson({
4367
+ chain: chainName,
4368
+ pallet: pallet.name,
4369
+ item: si.name,
4370
+ category: "storage",
4371
+ type: si.type,
4372
+ valueType,
4373
+ keyType,
4374
+ docs: si.docs
4375
+ }));
4376
+ return;
4377
+ }
4378
+ const ci = pallet.constants.find((c) => c.name.toLowerCase() === itemName.toLowerCase());
4379
+ if (ci) {
4380
+ console.log(formatJson({
4381
+ chain: chainName,
4382
+ pallet: pallet.name,
4383
+ item: ci.name,
4384
+ category: "constant",
4385
+ type: describeType(meta.lookup, ci.typeId),
4386
+ docs: ci.docs
4387
+ }));
4388
+ return;
4389
+ }
4390
+ const ca = pallet.calls.find((c) => c.name.toLowerCase() === itemName.toLowerCase());
4391
+ if (ca) {
4392
+ console.log(formatJson({
4393
+ chain: chainName,
4394
+ pallet: pallet.name,
4395
+ item: ca.name,
4396
+ category: "call",
4397
+ args: describeCallArgs(meta, pallet.name, ca.name),
4398
+ docs: ca.docs
4399
+ }));
4400
+ return;
4401
+ }
4402
+ const ev = pallet.events.find((e) => e.name.toLowerCase() === itemName.toLowerCase());
4403
+ if (ev) {
4404
+ console.log(formatJson({
4405
+ chain: chainName,
4406
+ pallet: pallet.name,
4407
+ item: ev.name,
4408
+ category: "event",
4409
+ fields: describeEventFields(meta, pallet.name, ev.name),
4410
+ docs: ev.docs
4411
+ }));
4412
+ return;
4413
+ }
4414
+ const er = pallet.errors.find((e) => e.name.toLowerCase() === itemName.toLowerCase());
4415
+ if (er) {
4416
+ console.log(formatJson({
4417
+ chain: chainName,
4418
+ pallet: pallet.name,
4419
+ item: er.name,
4420
+ category: "error",
4421
+ docs: er.docs
4422
+ }));
4423
+ return;
4424
+ }
4425
+ const allItems2 = [
4426
+ ...pallet.storage.map((s) => s.name),
4427
+ ...pallet.constants.map((c) => c.name),
4428
+ ...pallet.calls.map((c) => c.name),
4429
+ ...pallet.events.map((e) => e.name),
4430
+ ...pallet.errors.map((e) => e.name)
4431
+ ];
4432
+ throw new Error(suggestMessage(`item in ${pallet.name}`, itemName, allItems2));
4433
+ }
3734
4434
  const storageItem = pallet.storage.find((s) => s.name.toLowerCase() === itemName.toLowerCase());
3735
4435
  if (storageItem) {
3736
4436
  printHeading(`${pallet.name}.${storageItem.name} (Storage)`);
@@ -3864,8 +4564,7 @@ function registerParachainCommand(cli) {
3864
4564
  throw new CliError(`Invalid prefix "${opts.prefix}". Must be a non-negative integer.`);
3865
4565
  }
3866
4566
  const types = opts.type ? [validateType(opts.type)] : SOVEREIGN_ACCOUNT_TYPES;
3867
- const format = opts.output ?? "pretty";
3868
- if (format === "json") {
4567
+ if (isJsonOutput(opts)) {
3869
4568
  const result = { paraId, prefix };
3870
4569
  for (const type of types) {
3871
4570
  const accountId = deriveSovereignAccount(paraId, type);
@@ -3906,6 +4605,13 @@ async function handleQuery(target, keys, opts) {
3906
4605
  const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
3907
4606
  const pallets = listPallets(meta);
3908
4607
  const withStorage = pallets.filter((p) => p.storage.length > 0);
4608
+ if (isJsonOutput(opts)) {
4609
+ console.log(formatJson({
4610
+ chain: chainName2,
4611
+ pallets: withStorage.map((p) => ({ name: p.name, storage: p.storage.length }))
4612
+ }));
4613
+ return;
4614
+ }
3909
4615
  printHeading(`Pallets with storage on ${chainName2} (${withStorage.length})`);
3910
4616
  for (const p of withStorage) {
3911
4617
  printItem(p.name, `${p.storage.length} storage items`);
@@ -3920,7 +4626,23 @@ async function handleQuery(target, keys, opts) {
3920
4626
  const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
3921
4627
  const pallet2 = resolvePallet(meta, palletName(target));
3922
4628
  if (pallet2.storage.length === 0) {
3923
- console.log(`No storage items in ${pallet2.name}.`);
4629
+ if (isJsonOutput(opts)) {
4630
+ console.log(formatJson({ chain: chainName2, pallet: pallet2.name, storage: [] }));
4631
+ } else {
4632
+ console.log(`No storage items in ${pallet2.name}.`);
4633
+ }
4634
+ return;
4635
+ }
4636
+ if (isJsonOutput(opts)) {
4637
+ console.log(formatJson({
4638
+ chain: chainName2,
4639
+ pallet: pallet2.name,
4640
+ storage: pallet2.storage.map((s) => {
4641
+ const valueType = describeType(meta.lookup, s.valueTypeId);
4642
+ const keyType = s.keyTypeId != null ? describeType(meta.lookup, s.keyTypeId) : undefined;
4643
+ return { name: s.name, type: s.type, valueType, keyType, docs: firstSentence(s.docs) };
4644
+ })
4645
+ }));
3924
4646
  return;
3925
4647
  }
3926
4648
  printHeading(`${pallet2.name} Storage`);
@@ -3965,7 +4687,7 @@ async function handleQuery(target, keys, opts) {
3965
4687
  typeof opts.parsedArgs === "object" ? JSON.stringify(opts.parsedArgs) : String(opts.parsedArgs)
3966
4688
  ];
3967
4689
  const parsedKeys = await parseStorageKeys(meta, palletInfo.name, storageItem, effectiveKeys);
3968
- const format = opts.output ?? "pretty";
4690
+ const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
3969
4691
  const expectedLen = storageItem.type === "map" && storageItem.keyTypeId != null ? meta.builder.buildStorage(palletInfo.name, storageItem.name).len : 0;
3970
4692
  if (storageItem.type === "map" && parsedKeys.length < expectedLen) {
3971
4693
  if (parsedKeys.length === 0 && !opts.dump) {
@@ -3975,17 +4697,10 @@ async function handleQuery(target, keys, opts) {
3975
4697
  return;
3976
4698
  }
3977
4699
  const entries = await storageApi.getEntries(...parsedKeys);
3978
- const limit = Number(opts.limit);
3979
- const truncated = limit > 0 && entries.length > limit;
3980
- const display = truncated ? entries.slice(0, limit) : entries;
3981
- printResult(display.map((e) => ({
4700
+ printResult(entries.map((e) => ({
3982
4701
  keys: e.keyArgs,
3983
4702
  value: e.value
3984
4703
  })), format);
3985
- if (truncated) {
3986
- console.error(`
3987
- ${DIM}Showing ${limit} of ${entries.length} entries. Use --limit 0 for all.${RESET}`);
3988
- }
3989
4704
  } else {
3990
4705
  const result = await storageApi.getValue(...parsedKeys);
3991
4706
  printResult(result, format);
@@ -4034,6 +4749,100 @@ async function parseStorageKeys(meta, palletName2, storageItem, args) {
4034
4749
  return Promise.all(args.map((arg) => parseTypedArg(meta, keyEntry, arg)));
4035
4750
  }
4036
4751
 
4752
+ // src/commands/sign.ts
4753
+ init_accounts();
4754
+ init_hash();
4755
+ init_output();
4756
+ init_errors();
4757
+ import { readFile as readFile4 } from "node:fs/promises";
4758
+ var SUPPORTED_TYPES = ["sr25519"];
4759
+ function isSupportedType(type) {
4760
+ return SUPPORTED_TYPES.includes(type.toLowerCase());
4761
+ }
4762
+ function variantName(type) {
4763
+ switch (type) {
4764
+ case "sr25519":
4765
+ return "Sr25519";
4766
+ }
4767
+ }
4768
+ async function resolveInput2(data, opts) {
4769
+ const sources = [data !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
4770
+ if (sources > 1) {
4771
+ throw new CliError("Provide only one of: inline data, --file, or --stdin");
4772
+ }
4773
+ if (sources === 0) {
4774
+ throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
4775
+ }
4776
+ if (opts.file) {
4777
+ const buf = await readFile4(opts.file);
4778
+ return new Uint8Array(buf);
4779
+ }
4780
+ if (opts.stdin) {
4781
+ const chunks = [];
4782
+ for await (const chunk of process.stdin) {
4783
+ chunks.push(chunk);
4784
+ }
4785
+ return new Uint8Array(Buffer.concat(chunks));
4786
+ }
4787
+ return parseInputData(data);
4788
+ }
4789
+ function printSignHelp() {
4790
+ console.log(`${BOLD}Usage:${RESET} dot sign <message> --from <account> [options]
4791
+ `);
4792
+ console.log(`${BOLD}Arguments:${RESET}`);
4793
+ console.log(` ${CYAN}message${RESET} ${DIM}Message to sign (text or 0x-prefixed hex)${RESET}`);
4794
+ console.log(`
4795
+ ${BOLD}Options:${RESET}`);
4796
+ console.log(` ${CYAN}--from <name>${RESET} ${DIM}Account to sign with (required)${RESET}`);
4797
+ console.log(` ${CYAN}--type <algo>${RESET} ${DIM}Signature type: sr25519 (default)${RESET}`);
4798
+ console.log(` ${CYAN}--file <path>${RESET} ${DIM}Sign file contents${RESET}`);
4799
+ console.log(` ${CYAN}--stdin${RESET} ${DIM}Read from stdin${RESET}`);
4800
+ console.log(` ${CYAN}--output json${RESET} ${DIM}Output as JSON${RESET}`);
4801
+ console.log(`
4802
+ ${BOLD}Examples:${RESET}`);
4803
+ console.log(` ${DIM}$ dot sign "hello world" --from alice${RESET}`);
4804
+ console.log(` ${DIM}$ dot sign 0xdeadbeef --from alice${RESET}`);
4805
+ console.log(` ${DIM}$ dot sign --file ./payload.bin --from alice${RESET}`);
4806
+ console.log(` ${DIM}$ echo -n "hello" | dot sign --stdin --from alice${RESET}`);
4807
+ console.log(` ${DIM}$ dot sign "hello" --from alice --output json${RESET}`);
4808
+ }
4809
+ function registerSignCommand(cli) {
4810
+ 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) => {
4811
+ if (!message && !opts.file && !opts.stdin) {
4812
+ printSignHelp();
4813
+ return;
4814
+ }
4815
+ if (!opts.from) {
4816
+ throw new CliError("--from is required. Specify the account to sign with.");
4817
+ }
4818
+ const type = opts.type?.toLowerCase() ?? "sr25519";
4819
+ if (!isSupportedType(type)) {
4820
+ throw new CliError(`Unsupported signature type "${opts.type}". Supported: ${SUPPORTED_TYPES.join(", ")}`);
4821
+ }
4822
+ const input = await resolveInput2(message, opts);
4823
+ const keypair = await resolveAccountKeypair(opts.from);
4824
+ const signature = keypair.sign(input);
4825
+ const hexMessage = toHex2(input);
4826
+ const hexSignature = toHex2(signature);
4827
+ const variant = variantName(type);
4828
+ const enumValue = `${variant}(${hexSignature})`;
4829
+ const result = {
4830
+ type: variant,
4831
+ message: hexMessage,
4832
+ signature: hexSignature,
4833
+ enum: enumValue
4834
+ };
4835
+ if (isJsonOutput(opts)) {
4836
+ printResult(result, "json");
4837
+ } else {
4838
+ console.log(` ${BOLD}Type:${RESET} ${result.type}`);
4839
+ console.log(` ${BOLD}Message:${RESET} ${result.message}`);
4840
+ console.log(` ${BOLD}Signature:${RESET} ${result.signature}`);
4841
+ console.log(` ${BOLD}Enum:${RESET} ${result.enum}`);
4842
+ }
4843
+ });
4844
+ }
4845
+
4037
4846
  // src/commands/tx.ts
4038
4847
  init_store();
4039
4848
  init_types();
@@ -4042,6 +4851,7 @@ init_client();
4042
4851
  init_metadata();
4043
4852
  init_output();
4044
4853
  init_resolve_address();
4854
+ init_binary_display();
4045
4855
  init_errors();
4046
4856
  init_focused_inspect();
4047
4857
  import { getViewBuilder as getViewBuilder2 } from "@polkadot-api/view-builder";
@@ -4099,11 +4909,14 @@ function parseMortalityOption(raw) {
4099
4909
  function parseAtOption(raw) {
4100
4910
  if (raw === undefined)
4101
4911
  return;
4102
- if (raw === "best" || raw === "finalized")
4103
- return raw;
4912
+ if (raw === "finalized")
4913
+ return;
4914
+ if (raw === "best") {
4915
+ throw new CliError('"best" is no longer supported for --at in papi v2. Omit --at for finalized, or pass a specific block hash.');
4916
+ }
4104
4917
  if (/^0x[0-9a-fA-F]{64}$/.test(raw))
4105
4918
  return raw;
4106
- throw new CliError(`Invalid --at value "${raw}". Use "best", "finalized", or a 0x-prefixed 32-byte block hash.`);
4919
+ throw new CliError(`Invalid --at value "${raw}". Use a 0x-prefixed 32-byte block hash, or omit for finalized.`);
4107
4920
  }
4108
4921
  async function handleTx(target, args, opts) {
4109
4922
  if (!target) {
@@ -4112,6 +4925,13 @@ async function handleTx(target, args, opts) {
4112
4925
  const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
4113
4926
  const pallets = listPallets(meta);
4114
4927
  const withCalls = pallets.filter((p) => p.calls.length > 0);
4928
+ if (isJsonOutput(opts)) {
4929
+ console.log(formatJson({
4930
+ chain: chainName2,
4931
+ pallets: withCalls.map((p) => ({ name: p.name, calls: p.calls.length }))
4932
+ }));
4933
+ return;
4934
+ }
4115
4935
  printHeading(`Pallets with calls on ${chainName2} (${withCalls.length})`);
4116
4936
  for (const p of withCalls) {
4117
4937
  printItem(p.name, `${p.calls.length} calls`);
@@ -4126,7 +4946,23 @@ async function handleTx(target, args, opts) {
4126
4946
  const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
4127
4947
  const pallet2 = resolvePallet(meta, target);
4128
4948
  if (pallet2.calls.length === 0) {
4129
- console.log(`No calls in ${pallet2.name}.`);
4949
+ if (isJsonOutput(opts)) {
4950
+ console.log(formatJson({ chain: chainName2, pallet: pallet2.name, calls: [] }));
4951
+ } else {
4952
+ console.log(`No calls in ${pallet2.name}.`);
4953
+ }
4954
+ return;
4955
+ }
4956
+ if (isJsonOutput(opts)) {
4957
+ console.log(formatJson({
4958
+ chain: chainName2,
4959
+ pallet: pallet2.name,
4960
+ calls: pallet2.calls.map((c) => ({
4961
+ name: c.name,
4962
+ args: describeCallArgs(meta, pallet2.name, c.name),
4963
+ docs: firstSentence(c.docs)
4964
+ }))
4965
+ }));
4130
4966
  return;
4131
4967
  }
4132
4968
  printHeading(`${pallet2.name} Calls`);
@@ -4141,7 +4977,7 @@ async function handleTx(target, args, opts) {
4141
4977
  console.log();
4142
4978
  return;
4143
4979
  }
4144
- if (!opts.from && !opts.encode && !opts.yaml && !opts.json) {
4980
+ if (!opts.from && !opts.encode && !opts.toYaml && !opts.toJson) {
4145
4981
  if (isRawCall) {
4146
4982
  throw new Error("--from is required (or use --encode to output hex without signing)");
4147
4983
  }
@@ -4154,14 +4990,14 @@ async function handleTx(target, args, opts) {
4154
4990
  if (opts.encode && isRawCall) {
4155
4991
  throw new Error("--encode cannot be used with raw call hex (already encoded)");
4156
4992
  }
4157
- if ((opts.yaml || opts.json) && opts.encode) {
4158
- throw new Error("--yaml/--json and --encode are mutually exclusive");
4993
+ if ((opts.toYaml || opts.toJson) && opts.encode) {
4994
+ throw new Error("--to-yaml/--to-json and --encode are mutually exclusive");
4159
4995
  }
4160
- if ((opts.yaml || opts.json) && opts.dryRun) {
4161
- throw new Error("--yaml/--json and --dry-run are mutually exclusive");
4996
+ if ((opts.toYaml || opts.toJson) && opts.dryRun) {
4997
+ throw new Error("--to-yaml/--to-json and --dry-run are mutually exclusive");
4162
4998
  }
4163
- if (opts.yaml && opts.json) {
4164
- throw new Error("--yaml and --json are mutually exclusive");
4999
+ if (opts.toYaml && opts.toJson) {
5000
+ throw new Error("--to-yaml and --to-json are mutually exclusive");
4165
5001
  }
4166
5002
  const config = await loadConfig();
4167
5003
  const effectiveChain = opts.chain;
@@ -4173,7 +5009,7 @@ async function handleTx(target, args, opts) {
4173
5009
  callName = target.slice(dotIdx + 1);
4174
5010
  }
4175
5011
  const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
4176
- const decodeOnly = opts.encode || opts.yaml || opts.json;
5012
+ const decodeOnly = opts.encode || opts.toYaml || opts.toJson;
4177
5013
  const signer = decodeOnly ? undefined : await resolveAccountSigner(opts.from);
4178
5014
  let clientHandle;
4179
5015
  if (!decodeOnly) {
@@ -4222,9 +5058,9 @@ async function handleTx(target, args, opts) {
4222
5058
  ` + "Usage: dot tx 0x<call_hex> --from <account>");
4223
5059
  }
4224
5060
  callHex = target;
4225
- if (opts.yaml || opts.json) {
5061
+ if (opts.toYaml || opts.toJson) {
4226
5062
  const fileObj = decodeCallToFileFormat(meta, callHex, chainName);
4227
- outputFileFormat(fileObj, !!opts.yaml);
5063
+ outputFileFormat(fileObj, !!opts.toYaml);
4228
5064
  return;
4229
5065
  }
4230
5066
  const callBinary = Binary3.fromHex(target);
@@ -4242,26 +5078,55 @@ async function handleTx(target, args, opts) {
4242
5078
  }
4243
5079
  const effectiveArgs = opts.parsedArgs !== undefined ? fileArgsToStrings(opts.parsedArgs) : args;
4244
5080
  const callData = await parseCallArgs(meta, palletInfo.name, callInfo.name, effectiveArgs);
4245
- if (opts.encode || opts.yaml || opts.json) {
5081
+ if (opts.encode || opts.toYaml || opts.toJson) {
4246
5082
  const { codec, location } = meta.builder.buildCall(palletInfo.name, callInfo.name);
4247
5083
  const encodedArgs = codec.enc(callData);
4248
5084
  const fullCall = new Uint8Array([location[0], location[1], ...encodedArgs]);
4249
- const hex = Binary3.fromBytes(fullCall).asHex();
5085
+ const hex = Binary3.toHex(fullCall);
4250
5086
  if (opts.encode) {
4251
- console.log(hex);
5087
+ if (isJsonOutput(opts)) {
5088
+ console.log(formatJson({ callHex: hex }));
5089
+ } else {
5090
+ console.log(hex);
5091
+ }
4252
5092
  return;
4253
5093
  }
4254
5094
  const fileObj = decodeCallToFileFormat(meta, hex, chainName);
4255
- outputFileFormat(fileObj, !!opts.yaml);
5095
+ outputFileFormat(fileObj, !!opts.toYaml);
4256
5096
  return;
4257
5097
  }
4258
5098
  tx = unsafeApi.tx[palletInfo.name][callInfo.name](callData);
4259
5099
  const encodedCall = await tx.getEncodedData();
4260
- callHex = encodedCall.asHex();
5100
+ callHex = Binary3.toHex(encodedCall);
4261
5101
  }
4262
5102
  const decodedStr = decodeCall(meta, callHex);
4263
5103
  if (opts.dryRun) {
4264
5104
  const signerAddress = toSs58(signer.publicKey);
5105
+ let estimatedFees;
5106
+ try {
5107
+ estimatedFees = String(await tx.getEstimatedFees(signer?.publicKey, txOptions));
5108
+ } catch {
5109
+ estimatedFees = undefined;
5110
+ }
5111
+ if (isJsonOutput(opts)) {
5112
+ const result2 = {
5113
+ chain: chainName,
5114
+ from: { name: opts.from, address: signerAddress },
5115
+ callHex,
5116
+ decoded: decodedStr,
5117
+ estimatedFees
5118
+ };
5119
+ if (nonce !== undefined)
5120
+ result2.nonce = nonce;
5121
+ if (tip !== undefined)
5122
+ result2.tip = String(tip);
5123
+ if (mortality !== undefined)
5124
+ result2.mortality = mortality.mortal ? `mortal (period ${mortality.period})` : "immortal";
5125
+ if (at !== undefined)
5126
+ result2.at = at;
5127
+ console.log(formatJson(result2));
5128
+ return;
5129
+ }
4265
5130
  console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
4266
5131
  console.log(` ${BOLD}From:${RESET} ${opts.from} (${signerAddress})`);
4267
5132
  console.log(` ${BOLD}Call:${RESET} ${callHex}`);
@@ -4274,16 +5139,46 @@ async function handleTx(target, args, opts) {
4274
5139
  console.log(` ${BOLD}Mortality:${RESET} ${mortality.mortal ? `mortal (period ${mortality.period})` : "immortal"}`);
4275
5140
  if (at !== undefined)
4276
5141
  console.log(` ${BOLD}At:${RESET} ${at}`);
4277
- try {
4278
- const fees = await tx.getEstimatedFees(signer?.publicKey, txOptions);
4279
- console.log(` ${BOLD}Estimated fees:${RESET} ${fees}`);
4280
- } catch (err) {
5142
+ if (estimatedFees !== undefined) {
5143
+ console.log(` ${BOLD}Estimated fees:${RESET} ${estimatedFees}`);
5144
+ } else {
4281
5145
  console.log(` ${BOLD}Estimated fees:${RESET} ${YELLOW}unable to estimate${RESET}`);
4282
- console.log(` ${DIM}${err.message ?? err}${RESET}`);
4283
5146
  }
4284
5147
  return;
4285
5148
  }
4286
5149
  const waitLevel = parseWaitLevel(opts.wait);
5150
+ if (isJsonOutput(opts)) {
5151
+ const result2 = await watchTransactionJson(tx.signSubmitAndWatch(signer, txOptions), waitLevel);
5152
+ const rpcUrl2 = primaryRpc(opts.rpc ?? chainConfig.rpc);
5153
+ if (result2.type === "broadcasted") {
5154
+ printJsonLine({ event: "broadcasted", txHash: result2.txHash });
5155
+ return;
5156
+ }
5157
+ const blockHash = result2.block.hash;
5158
+ const explorer = {};
5159
+ if (rpcUrl2) {
5160
+ explorer.polkadotjs = pjsAppsLink(rpcUrl2, blockHash);
5161
+ explorer.papi = papiLink(rpcUrl2, blockHash);
5162
+ }
5163
+ printJsonLine({
5164
+ event: result2.type === "finalized" ? "finalized" : "bestBlock",
5165
+ blockNumber: result2.block.number,
5166
+ blockHash,
5167
+ txHash: result2.txHash,
5168
+ ok: result2.ok,
5169
+ events: result2.events?.map((e) => ({
5170
+ pallet: e.type,
5171
+ name: e.value?.type,
5172
+ fields: e.value?.value
5173
+ })),
5174
+ dispatchError: result2.ok ? null : formatDispatchError(result2.dispatchError),
5175
+ explorer
5176
+ });
5177
+ if (!result2.ok) {
5178
+ throw new CliError(`Transaction dispatch error: ${formatDispatchError(result2.dispatchError)}`);
5179
+ }
5180
+ return;
5181
+ }
4287
5182
  const result = await watchTransaction(tx.signSubmitAndWatch(signer, txOptions), waitLevel);
4288
5183
  console.log();
4289
5184
  console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
@@ -4378,7 +5273,7 @@ function decodeCallFallback(meta, callHex) {
4378
5273
  if (callTypeId == null)
4379
5274
  throw new Error("No RuntimeCall type ID");
4380
5275
  const codec = meta.builder.buildDefinition(callTypeId);
4381
- const decoded = codec.dec(Binary3.fromHex(callHex).asBytes());
5276
+ const decoded = codec.dec(Binary3.fromHex(callHex));
4382
5277
  const palletName2 = decoded.type;
4383
5278
  const call = decoded.value;
4384
5279
  const callName = call.type;
@@ -4394,7 +5289,7 @@ function decodeCallToFileFormat(meta, callHex, chainName) {
4394
5289
  if (callTypeId == null)
4395
5290
  throw new Error("No RuntimeCall type ID in metadata");
4396
5291
  const codec = meta.builder.buildDefinition(callTypeId);
4397
- const decoded = codec.dec(Binary3.fromHex(callHex).asBytes());
5292
+ const decoded = codec.dec(Binary3.fromHex(callHex));
4398
5293
  const palletName2 = decoded.type;
4399
5294
  const call = decoded.value;
4400
5295
  const callName = call.type;
@@ -4411,8 +5306,8 @@ function decodeCallToFileFormat(meta, callHex, chainName) {
4411
5306
  function sanitizeForSerialization(value) {
4412
5307
  if (value === undefined || value === null)
4413
5308
  return null;
4414
- if (value instanceof Binary3)
4415
- return value.asHex();
5309
+ if (value instanceof Uint8Array)
5310
+ return Binary3.toHex(value);
4416
5311
  if (typeof value === "bigint") {
4417
5312
  if (value >= Number.MIN_SAFE_INTEGER && value <= Number.MAX_SAFE_INTEGER) {
4418
5313
  return Number(value);
@@ -4444,8 +5339,8 @@ function outputFileFormat(obj, asYaml) {
4444
5339
  function formatRawDecoded(value) {
4445
5340
  if (value === undefined || value === null)
4446
5341
  return "null";
4447
- if (value instanceof Binary3)
4448
- return value.asHex();
5342
+ if (value instanceof Uint8Array)
5343
+ return Binary3.toHex(value);
4449
5344
  if (typeof value === "bigint")
4450
5345
  return value.toString();
4451
5346
  if (typeof value === "string")
@@ -4566,7 +5461,7 @@ function formatEventValue(v) {
4566
5461
  return v.toString();
4567
5462
  if (v === null || v === undefined)
4568
5463
  return "null";
4569
- if (v instanceof Binary3) {
5464
+ if (v instanceof Uint8Array) {
4570
5465
  return binaryToDisplay(v);
4571
5466
  }
4572
5467
  return JSON.stringify(v, (_k, val) => typeof val === "bigint" ? val.toString() : val);
@@ -4840,7 +5735,7 @@ async function parseTypedArg2(meta, entry, arg) {
4840
5735
  case "enum": {
4841
5736
  if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
4842
5737
  const callCodec = meta.builder.buildDefinition(meta.lookup.call);
4843
- return callCodec.dec(Binary3.fromHex(arg).asBytes());
5738
+ return callCodec.dec(Binary3.fromHex(arg));
4844
5739
  }
4845
5740
  if (arg.startsWith("{")) {
4846
5741
  try {
@@ -5067,10 +5962,151 @@ function watchTransaction(observable, level) {
5067
5962
  });
5068
5963
  });
5069
5964
  }
5965
+ function watchTransactionJson(observable, level) {
5966
+ return new Promise((resolve, reject) => {
5967
+ let settled = false;
5968
+ const subscription = observable.subscribe({
5969
+ next(event) {
5970
+ if (settled)
5971
+ return;
5972
+ switch (event.type) {
5973
+ case "signed":
5974
+ printJsonLine({ event: "signed", txHash: event.txHash });
5975
+ break;
5976
+ case "broadcasted":
5977
+ printJsonLine({ event: "broadcasted", txHash: event.txHash });
5978
+ if (level === "broadcast") {
5979
+ settled = true;
5980
+ subscription.unsubscribe();
5981
+ resolve(event);
5982
+ }
5983
+ break;
5984
+ case "txBestBlocksState":
5985
+ if (event.found) {
5986
+ printJsonLine({ event: "bestBlock", blockNumber: event.block.number, found: true });
5987
+ if (level === "best-block") {
5988
+ settled = true;
5989
+ subscription.unsubscribe();
5990
+ resolve(event);
5991
+ }
5992
+ }
5993
+ break;
5994
+ case "finalized":
5995
+ settled = true;
5996
+ resolve(event);
5997
+ break;
5998
+ }
5999
+ },
6000
+ error(err) {
6001
+ if (settled)
6002
+ return;
6003
+ reject(err);
6004
+ }
6005
+ });
6006
+ });
6007
+ }
6008
+
6009
+ // src/commands/verifiable.ts
6010
+ init_accounts_store();
6011
+ init_accounts();
6012
+ init_bandersnatch();
6013
+ init_output();
6014
+ var DEV_PHRASE2 = "bottom drive obey lake curtain smoke basket hold race lonely fit walk";
6015
+ var VERIFIABLE_HELP = `
6016
+ ${BOLD}Usage:${RESET}
6017
+ $ dot verifiable <account> [--context <value>]
6018
+
6019
+ ${BOLD}Arguments:${RESET}
6020
+ account Account name (stored or dev account)
6021
+
6022
+ ${BOLD}Options:${RESET}
6023
+ --context <value> Blake2b context for keyed derivation (e.g. "candidate")
6024
+
6025
+ ${BOLD}Examples:${RESET}
6026
+ $ dot verifiable alice Unkeyed derivation (lite person)
6027
+ $ dot verifiable alice --context candidate Keyed with "candidate" (full person)
6028
+ $ dot verifiable my-account --context candidate
6029
+
6030
+ ${BOLD}How it works:${RESET}
6031
+
6032
+ Mnemonic (12/24 words)
6033
+ │ mnemonicToEntropy() (raw BIP39 entropy, NOT miniSecret)
6034
+
6035
+ blake2b256(entropy, context?) keyed or unkeyed
6036
+
6037
+ member_from_entropy() verifiablejs WASM (Bandersnatch curve)
6038
+
6039
+ 32-byte member key for on-chain member set registration
6040
+ `.trimStart();
6041
+ function registerVerifiableCommands(cli) {
6042
+ 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) => {
6043
+ if (!account) {
6044
+ console.log(VERIFIABLE_HELP);
6045
+ return;
6046
+ }
6047
+ return deriveVerifiable(account, opts);
6048
+ });
6049
+ }
6050
+ async function deriveVerifiable(account, opts) {
6051
+ const mnemonic = await resolveMnemonic(account);
6052
+ const memberKey = deriveBandersnatchMember(mnemonic, opts.context);
6053
+ const memberKeyHex = publicKeyToHex(memberKey);
6054
+ if (!isDevAccount(account)) {
6055
+ const accountsFile = await loadAccounts();
6056
+ const stored = findAccount(accountsFile, account);
6057
+ if (stored) {
6058
+ if (!stored.bandersnatch)
6059
+ stored.bandersnatch = {};
6060
+ stored.bandersnatch[opts.context ?? ""] = memberKeyHex;
6061
+ await saveAccounts(accountsFile);
6062
+ }
6063
+ }
6064
+ if (isJsonOutput(opts)) {
6065
+ const result = {
6066
+ account,
6067
+ memberKey: memberKeyHex
6068
+ };
6069
+ if (opts.context)
6070
+ result.context = opts.context;
6071
+ console.log(formatJson(result));
6072
+ } else {
6073
+ printHeading("Bandersnatch Member Key");
6074
+ console.log(` ${BOLD}Account:${RESET} ${account}`);
6075
+ if (opts.context)
6076
+ console.log(` ${BOLD}Context:${RESET} ${opts.context}`);
6077
+ console.log(` ${BOLD}Member Key:${RESET} ${memberKeyHex}`);
6078
+ console.log();
6079
+ }
6080
+ }
6081
+ async function resolveMnemonic(account) {
6082
+ if (isDevAccount(account)) {
6083
+ return DEV_PHRASE2;
6084
+ }
6085
+ const accountsFile = await loadAccounts();
6086
+ const stored = findAccount(accountsFile, account);
6087
+ if (!stored) {
6088
+ const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
6089
+ const suggestions = findClosest(account, available);
6090
+ const hint = suggestions.length > 0 ? `
6091
+ Did you mean: ${suggestions.join(", ")}?` : "";
6092
+ const list = available.map((a) => `
6093
+ - ${a}`).join("");
6094
+ throw new Error(`Unknown account "${account}".${hint}
6095
+ Available accounts:${list}`);
6096
+ }
6097
+ if (isWatchOnly(stored)) {
6098
+ throw new Error(`Account "${account}" is watch-only (no secret). Cannot derive Bandersnatch key.`);
6099
+ }
6100
+ const secret = resolveSecret(stored.secret);
6101
+ if (isHexPublicKey(`0x${secret.replace(/^0x/, "")}`)) {
6102
+ throw new Error(`Account "${account}" uses a hex seed. Bandersnatch derivation requires a BIP39 mnemonic.`);
6103
+ }
6104
+ return secret;
6105
+ }
5070
6106
 
5071
6107
  // src/config/store.ts
5072
6108
  init_types();
5073
- import { access as access3, mkdir as mkdir3, readFile as readFile4, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
6109
+ import { access as access3, mkdir as mkdir3, readFile as readFile5, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
5074
6110
  import { homedir as homedir2 } from "node:os";
5075
6111
  import { join as join3 } from "node:path";
5076
6112
  var DOT_DIR2 = join3(homedir2(), ".polkadot");
@@ -5090,11 +6126,17 @@ async function fileExists3(path) {
5090
6126
  async function loadConfig2() {
5091
6127
  await ensureDir3(DOT_DIR2);
5092
6128
  if (await fileExists3(CONFIG_PATH2)) {
5093
- const saved = JSON.parse(await readFile4(CONFIG_PATH2, "utf-8"));
5094
- return {
5095
- ...saved,
5096
- chains: { ...DEFAULT_CONFIG.chains, ...saved.chains }
5097
- };
6129
+ const saved = JSON.parse(await readFile5(CONFIG_PATH2, "utf-8"));
6130
+ const chains = {};
6131
+ for (const [name, defaultConfig] of Object.entries(DEFAULT_CONFIG.chains)) {
6132
+ chains[name] = saved.chains[name] ? { ...defaultConfig, ...saved.chains[name] } : defaultConfig;
6133
+ }
6134
+ for (const [name, config] of Object.entries(saved.chains)) {
6135
+ if (!(name in DEFAULT_CONFIG.chains)) {
6136
+ chains[name] = config;
6137
+ }
6138
+ }
6139
+ return { ...saved, chains };
5098
6140
  }
5099
6141
  await saveConfig2(DEFAULT_CONFIG);
5100
6142
  return DEFAULT_CONFIG;
@@ -5107,7 +6149,7 @@ async function saveConfig2(config) {
5107
6149
 
5108
6150
  // src/core/file-loader.ts
5109
6151
  init_errors();
5110
- import { access as access4, readFile as readFile5 } from "node:fs/promises";
6152
+ import { access as access4, readFile as readFile6 } from "node:fs/promises";
5111
6153
  import { parse as parseYaml } from "yaml";
5112
6154
  var CATEGORIES = ["tx", "query", "const", "apis"];
5113
6155
  var FILE_EXTENSIONS = [".json", ".yaml", ".yml"];
@@ -5172,7 +6214,7 @@ async function loadCommandFile(filePath, cliVars) {
5172
6214
  } catch {
5173
6215
  throw new CliError(`File not found: ${filePath}`);
5174
6216
  }
5175
- const rawText = await readFile5(filePath, "utf-8");
6217
+ const rawText = await readFile6(filePath, "utf-8");
5176
6218
  if (!rawText.trim()) {
5177
6219
  throw new CliError(`File is empty: ${filePath}`);
5178
6220
  }
@@ -5485,21 +6527,24 @@ if (process.argv[2] === "__complete") {
5485
6527
  console.log(" dot apis.Core.version Call a runtime API");
5486
6528
  console.log(" dot polkadot.query.System.Number With chain prefix");
5487
6529
  console.log(" dot ./transfer.yaml --from alice Run from file");
5488
- console.log(" dot tx.0x1f0003... --yaml Decode hex call to YAML");
5489
- console.log(" dot tx.System.remark 0xdead --json Encode & output as JSON file format");
6530
+ console.log(" dot tx.0x1f0003... --to-yaml Decode hex call to YAML");
6531
+ console.log(" dot tx.System.remark 0xdead --to-json Encode & output as JSON file format");
6532
+ console.log(" dot query.System.Number --json Output as JSON");
5490
6533
  console.log();
5491
6534
  console.log("Commands:");
5492
6535
  console.log(" inspect [target] Inspect chain metadata (alias: explore)");
5493
6536
  console.log(" chain Manage chain configurations");
5494
6537
  console.log(" account Manage accounts");
5495
6538
  console.log(" hash Hash utilities");
6539
+ console.log(" sign Sign a message with an account keypair");
5496
6540
  console.log(" parachain Derive parachain sovereign accounts");
6541
+ console.log(" verifiable Derive Bandersnatch member key from mnemonic");
5497
6542
  console.log(" completions <sh> Generate shell completions (zsh, bash, fish)");
5498
6543
  console.log();
5499
6544
  console.log("Global options:");
5500
6545
  console.log(" --chain <name> Target chain (default from config)");
5501
6546
  console.log(" --rpc <url> Override RPC endpoint");
5502
- console.log(" --light-client Use Smoldot light client");
6547
+ console.log(" --json Output as JSON");
5503
6548
  console.log(" --output <format> Output format: pretty or json");
5504
6549
  console.log(" --help, -h Display this message");
5505
6550
  console.log(" --version Show version");
@@ -5508,20 +6553,20 @@ if (process.argv[2] === "__complete") {
5508
6553
  const cli = cac("dot");
5509
6554
  cli.option("--chain <name>", "Target chain (default from config)");
5510
6555
  cli.option("--rpc <url>", "Override RPC endpoint for this call");
5511
- cli.option("--light-client", "Use Smoldot light client instead of WebSocket");
5512
6556
  cli.option("--output <format>", "Output format: pretty or json", {
5513
6557
  default: "pretty"
5514
6558
  });
6559
+ cli.option("--json", "Output as JSON (shorthand for --output json)");
5515
6560
  registerChainCommands(cli);
5516
6561
  registerInspectCommand(cli);
5517
6562
  registerAccountCommands(cli);
5518
6563
  registerHashCommand(cli);
6564
+ registerSignCommand(cli);
5519
6565
  registerParachainCommand(cli);
5520
6566
  registerCompletionsCommand(cli);
5521
- 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("--yaml", "Decode call to YAML file format (for tx)").option("--json", "Decode call to JSON file format (for tx)").option("--ext <json>", "Custom signed extension values as JSON (for tx)").option("-w, --wait <level>", "Resolve at: broadcast, best-block (or best), finalized (for tx)", {
6567
+ registerVerifiableCommands(cli);
6568
+ 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("-w, --wait <level>", "Resolve at: broadcast, best-block (or best), finalized (for tx)", {
5522
6569
  default: "finalized"
5523
- }).option("--limit <n>", "Max entries to return for map queries (0 = unlimited)", {
5524
- default: 100
5525
6570
  }).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 validate against (for tx)').action(async (dotpath, args, opts) => {
5526
6571
  if (!dotpath) {
5527
6572
  printHelp();
@@ -5531,7 +6576,12 @@ if (process.argv[2] === "__complete") {
5531
6576
  const cliVars = collectVarFlags(process.argv);
5532
6577
  const cmd = await loadCommandFile(dotpath, cliVars);
5533
6578
  const effectiveChain2 = opts.chain ?? cmd.chain;
5534
- const handlerOpts2 = { chain: effectiveChain2, rpc: opts.rpc, output: opts.output };
6579
+ const handlerOpts2 = {
6580
+ chain: effectiveChain2,
6581
+ rpc: opts.rpc,
6582
+ output: opts.output,
6583
+ json: opts.json
6584
+ };
5535
6585
  const target2 = `${cmd.pallet}.${cmd.item}`;
5536
6586
  switch (cmd.category) {
5537
6587
  case "tx":
@@ -5540,8 +6590,8 @@ if (process.argv[2] === "__complete") {
5540
6590
  from: opts.from,
5541
6591
  dryRun: opts.dryRun,
5542
6592
  encode: opts.encode,
5543
- yaml: opts.yaml,
5544
- json: opts.json,
6593
+ toYaml: opts.toYaml,
6594
+ toJson: opts.toJson,
5545
6595
  ext: opts.ext,
5546
6596
  wait: opts.wait,
5547
6597
  nonce: opts.nonce,
@@ -5554,7 +6604,6 @@ if (process.argv[2] === "__complete") {
5554
6604
  case "query":
5555
6605
  await handleQuery(target2, args, {
5556
6606
  ...handlerOpts2,
5557
- limit: opts.limit,
5558
6607
  dump: opts.dump,
5559
6608
  parsedArgs: cmd.args
5560
6609
  });
@@ -5589,7 +6638,12 @@ if (process.argv[2] === "__complete") {
5589
6638
  throw new CliError2(`Chain specified both as prefix ("${parsed.chain}") and as --chain flag ("${opts.chain}"). Use one or the other.`);
5590
6639
  }
5591
6640
  const effectiveChain = parsed.chain ?? opts.chain;
5592
- const handlerOpts = { chain: effectiveChain, rpc: opts.rpc, output: opts.output };
6641
+ const handlerOpts = {
6642
+ chain: effectiveChain,
6643
+ rpc: opts.rpc,
6644
+ output: opts.output,
6645
+ json: opts.json
6646
+ };
5593
6647
  const target = parsed.pallet ? parsed.item ? `${parsed.pallet}.${parsed.item}` : parsed.pallet : undefined;
5594
6648
  if (cli.options.help && parsed.pallet && parsed.item) {
5595
6649
  await showItemHelp2(parsed.category, target, handlerOpts);
@@ -5597,7 +6651,7 @@ if (process.argv[2] === "__complete") {
5597
6651
  }
5598
6652
  switch (parsed.category) {
5599
6653
  case "query":
5600
- await handleQuery(target, args, { ...handlerOpts, limit: opts.limit, dump: opts.dump });
6654
+ await handleQuery(target, args, { ...handlerOpts, dump: opts.dump });
5601
6655
  break;
5602
6656
  case "tx": {
5603
6657
  const txOpts = {
@@ -5605,8 +6659,8 @@ if (process.argv[2] === "__complete") {
5605
6659
  from: opts.from,
5606
6660
  dryRun: opts.dryRun,
5607
6661
  encode: opts.encode,
5608
- yaml: opts.yaml,
5609
- json: opts.json,
6662
+ toYaml: opts.toYaml,
6663
+ toJson: opts.toJson,
5610
6664
  ext: opts.ext,
5611
6665
  wait: opts.wait,
5612
6666
  nonce: opts.nonce,