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.
- package/README.md +174 -51
- package/dist/cli.mjs +1334 -280
- 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
|
-
|
|
168
|
-
|
|
169
|
-
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
|
|
425
|
+
async function resolveAccountKeypair(name) {
|
|
366
426
|
if (isDevAccount(name)) {
|
|
367
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
409
|
-
return isReadableText(text) ? text :
|
|
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
|
|
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 {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
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
|
-
|
|
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 =
|
|
645
|
+
console.error = origError;
|
|
646
|
+
console.warn = origWarn;
|
|
550
647
|
};
|
|
551
648
|
}
|
|
552
649
|
async function createChainClient(chainName, chainConfig, rpcOverride) {
|
|
553
|
-
const
|
|
554
|
-
const
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
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
|
-
|
|
579
|
-
|
|
666
|
+
try {
|
|
667
|
+
client.destroy();
|
|
668
|
+
} catch {}
|
|
669
|
+
setTimeout(restoreConsole, 500);
|
|
580
670
|
}
|
|
581
671
|
};
|
|
582
672
|
}
|
|
583
|
-
async function
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
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
|
-
|
|
960
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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) =>
|
|
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", "--
|
|
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 = [
|
|
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.
|
|
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}
|
|
2515
|
+
console.log(` ${BOLD}Name:${RESET} ${name}`);
|
|
2296
2516
|
if (path)
|
|
2297
|
-
console.log(` ${BOLD}Path:${RESET}
|
|
2298
|
-
console.log(` ${BOLD}Address:${RESET}
|
|
2299
|
-
console.log(` ${BOLD}
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
2783
|
-
console.error(`Must provide
|
|
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
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3259
|
+
process.stderr.write(`Connecting to ${chainName}...
|
|
3260
|
+
`);
|
|
2854
3261
|
const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
2855
3262
|
try {
|
|
2856
|
-
|
|
3263
|
+
process.stderr.write(`Fetching metadata...
|
|
3264
|
+
`);
|
|
2857
3265
|
await fetchMetadataFromChain(clientHandle, chainName);
|
|
2858
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3055
|
-
const result =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 === "
|
|
4103
|
-
return
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
5061
|
+
if (opts.toYaml || opts.toJson) {
|
|
4226
5062
|
const fileObj = decodeCallToFileFormat(meta, callHex, chainName);
|
|
4227
|
-
outputFileFormat(fileObj, !!opts.
|
|
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.
|
|
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.
|
|
5085
|
+
const hex = Binary3.toHex(fullCall);
|
|
4250
5086
|
if (opts.encode) {
|
|
4251
|
-
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
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)
|
|
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)
|
|
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
|
|
4415
|
-
return
|
|
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
|
|
4448
|
-
return
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
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
|
|
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
|
|
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
|
|
5489
|
-
console.log(" dot tx.System.remark 0xdead --json
|
|
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(" --
|
|
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
|
|
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 = {
|
|
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
|
-
|
|
5544
|
-
|
|
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 = {
|
|
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,
|
|
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
|
-
|
|
5609
|
-
|
|
6662
|
+
toYaml: opts.toYaml,
|
|
6663
|
+
toJson: opts.toJson,
|
|
5610
6664
|
ext: opts.ext,
|
|
5611
6665
|
wait: opts.wait,
|
|
5612
6666
|
nonce: opts.nonce,
|