polkadot-cli 1.12.0 → 1.14.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 +271 -56
- package/dist/cli.mjs +1770 -353
- package/package.json +8 -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,29 @@ var __export = (target, all) => {
|
|
|
15
14
|
});
|
|
16
15
|
};
|
|
17
16
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
// src/utils/errors.ts
|
|
19
|
+
var CliError, ConnectionError, MetadataError;
|
|
20
|
+
var init_errors = __esm(() => {
|
|
21
|
+
CliError = class CliError extends Error {
|
|
22
|
+
constructor(message) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = "CliError";
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
ConnectionError = class ConnectionError extends CliError {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = "ConnectionError";
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
MetadataError = class MetadataError extends CliError {
|
|
34
|
+
constructor(message) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = "MetadataError";
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
});
|
|
19
40
|
|
|
20
41
|
// src/config/types.ts
|
|
21
42
|
function primaryRpc(rpc) {
|
|
@@ -24,7 +45,6 @@ function primaryRpc(rpc) {
|
|
|
24
45
|
var DEFAULT_CONFIG, BUILTIN_CHAIN_NAMES;
|
|
25
46
|
var init_types = __esm(() => {
|
|
26
47
|
DEFAULT_CONFIG = {
|
|
27
|
-
defaultChain: "polkadot",
|
|
28
48
|
chains: {
|
|
29
49
|
polkadot: {
|
|
30
50
|
rpc: [
|
|
@@ -53,7 +73,9 @@ var init_types = __esm(() => {
|
|
|
53
73
|
"wss://statemint-rpc-tn.dwellir.com",
|
|
54
74
|
"wss://statemint.public.curie.radiumblock.co/ws",
|
|
55
75
|
"wss://asset-hub-polkadot.rpc.permanence.io"
|
|
56
|
-
]
|
|
76
|
+
],
|
|
77
|
+
relay: "polkadot",
|
|
78
|
+
parachainId: 1000
|
|
57
79
|
},
|
|
58
80
|
"polkadot-bridge-hub": {
|
|
59
81
|
rpc: [
|
|
@@ -65,7 +87,9 @@ var init_types = __esm(() => {
|
|
|
65
87
|
"wss://bridgehub-polkadot.api.onfinality.io/public-ws",
|
|
66
88
|
"wss://polkadot-bridge-hub-rpc-tn.dwellir.com",
|
|
67
89
|
"wss://bridgehub-polkadot.public.curie.radiumblock.co/ws"
|
|
68
|
-
]
|
|
90
|
+
],
|
|
91
|
+
relay: "polkadot",
|
|
92
|
+
parachainId: 1002
|
|
69
93
|
},
|
|
70
94
|
"polkadot-collectives": {
|
|
71
95
|
rpc: [
|
|
@@ -77,7 +101,9 @@ var init_types = __esm(() => {
|
|
|
77
101
|
"wss://collectives.api.onfinality.io/public-ws",
|
|
78
102
|
"wss://polkadot-collectives-rpc-tn.dwellir.com",
|
|
79
103
|
"wss://collectives.public.curie.radiumblock.co/ws"
|
|
80
|
-
]
|
|
104
|
+
],
|
|
105
|
+
relay: "polkadot",
|
|
106
|
+
parachainId: 1001
|
|
81
107
|
},
|
|
82
108
|
"polkadot-coretime": {
|
|
83
109
|
rpc: [
|
|
@@ -87,7 +113,9 @@ var init_types = __esm(() => {
|
|
|
87
113
|
"wss://coretime-polkadot-rpc.n.dwellir.com",
|
|
88
114
|
"wss://rpc-coretime-polkadot.luckyfriday.io",
|
|
89
115
|
"wss://coretime-polkadot.api.onfinality.io/public-ws"
|
|
90
|
-
]
|
|
116
|
+
],
|
|
117
|
+
relay: "polkadot",
|
|
118
|
+
parachainId: 1005
|
|
91
119
|
},
|
|
92
120
|
"polkadot-people": {
|
|
93
121
|
rpc: [
|
|
@@ -97,7 +125,9 @@ var init_types = __esm(() => {
|
|
|
97
125
|
"wss://people-polkadot-rpc.n.dwellir.com",
|
|
98
126
|
"wss://rpc-people-polkadot.luckyfriday.io",
|
|
99
127
|
"wss://people-polkadot.api.onfinality.io/public-ws"
|
|
100
|
-
]
|
|
128
|
+
],
|
|
129
|
+
relay: "polkadot",
|
|
130
|
+
parachainId: 1004
|
|
101
131
|
},
|
|
102
132
|
paseo: {
|
|
103
133
|
rpc: [
|
|
@@ -113,23 +143,33 @@ var init_types = __esm(() => {
|
|
|
113
143
|
"wss://asset-hub-paseo.dotters.network",
|
|
114
144
|
"wss://asset-hub-paseo-rpc.n.dwellir.com",
|
|
115
145
|
"wss://sys.turboflakes.io/asset-hub-paseo"
|
|
116
|
-
]
|
|
146
|
+
],
|
|
147
|
+
relay: "paseo",
|
|
148
|
+
parachainId: 1000
|
|
117
149
|
},
|
|
118
150
|
"paseo-bridge-hub": {
|
|
119
|
-
rpc: ["wss://bridge-hub-paseo.ibp.network", "wss://bridge-hub-paseo.dotters.network"]
|
|
151
|
+
rpc: ["wss://bridge-hub-paseo.ibp.network", "wss://bridge-hub-paseo.dotters.network"],
|
|
152
|
+
relay: "paseo",
|
|
153
|
+
parachainId: 1002
|
|
120
154
|
},
|
|
121
155
|
"paseo-collectives": {
|
|
122
|
-
rpc: ["wss://collectives-paseo.ibp.network", "wss://collectives-paseo.dotters.network"]
|
|
156
|
+
rpc: ["wss://collectives-paseo.ibp.network", "wss://collectives-paseo.dotters.network"],
|
|
157
|
+
relay: "paseo",
|
|
158
|
+
parachainId: 1001
|
|
123
159
|
},
|
|
124
160
|
"paseo-coretime": {
|
|
125
|
-
rpc: ["wss://coretime-paseo.ibp.network", "wss://coretime-paseo.dotters.network"]
|
|
161
|
+
rpc: ["wss://coretime-paseo.ibp.network", "wss://coretime-paseo.dotters.network"],
|
|
162
|
+
relay: "paseo",
|
|
163
|
+
parachainId: 1005
|
|
126
164
|
},
|
|
127
165
|
"paseo-people": {
|
|
128
166
|
rpc: [
|
|
129
167
|
"wss://people-paseo.ibp.network",
|
|
130
168
|
"wss://people-paseo.dotters.network",
|
|
131
169
|
"wss://people-paseo.rpc.amforc.com"
|
|
132
|
-
]
|
|
170
|
+
],
|
|
171
|
+
relay: "paseo",
|
|
172
|
+
parachainId: 1004
|
|
133
173
|
}
|
|
134
174
|
}
|
|
135
175
|
};
|
|
@@ -164,10 +204,16 @@ async function loadConfig() {
|
|
|
164
204
|
await ensureDir(DOT_DIR);
|
|
165
205
|
if (await fileExists(CONFIG_PATH)) {
|
|
166
206
|
const saved = JSON.parse(await readFile(CONFIG_PATH, "utf-8"));
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
chains
|
|
170
|
-
}
|
|
207
|
+
const chains = {};
|
|
208
|
+
for (const [name, defaultConfig] of Object.entries(DEFAULT_CONFIG.chains)) {
|
|
209
|
+
chains[name] = saved.chains[name] ? { ...defaultConfig, ...saved.chains[name] } : defaultConfig;
|
|
210
|
+
}
|
|
211
|
+
for (const [name, config] of Object.entries(saved.chains)) {
|
|
212
|
+
if (!(name in DEFAULT_CONFIG.chains)) {
|
|
213
|
+
chains[name] = config;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return { chains };
|
|
171
217
|
}
|
|
172
218
|
await saveConfig(DEFAULT_CONFIG);
|
|
173
219
|
return DEFAULT_CONFIG;
|
|
@@ -199,16 +245,19 @@ function findChainName(config, input) {
|
|
|
199
245
|
return Object.keys(config.chains).find((k) => k.toLowerCase() === input.toLowerCase());
|
|
200
246
|
}
|
|
201
247
|
function resolveChain(config, chainFlag) {
|
|
202
|
-
const
|
|
203
|
-
|
|
248
|
+
const available = Object.keys(config.chains).join(", ");
|
|
249
|
+
if (!chainFlag) {
|
|
250
|
+
throw new CliError(`No chain specified. Pass --chain <name> or use a dotpath prefix (e.g. "dot polkadot.query.System.Number"). Available chains: ${available}`);
|
|
251
|
+
}
|
|
252
|
+
const name = findChainName(config, chainFlag);
|
|
204
253
|
if (!name) {
|
|
205
|
-
|
|
206
|
-
throw new Error(`Unknown chain "${input}". Available chains: ${available}`);
|
|
254
|
+
throw new CliError(`Unknown chain "${chainFlag}". Available chains: ${available}`);
|
|
207
255
|
}
|
|
208
256
|
return { name, chain: config.chains[name] };
|
|
209
257
|
}
|
|
210
258
|
var DOT_DIR, CONFIG_PATH, CHAINS_DIR;
|
|
211
259
|
var init_store = __esm(() => {
|
|
260
|
+
init_errors();
|
|
212
261
|
init_types();
|
|
213
262
|
DOT_DIR = join(homedir(), ".polkadot");
|
|
214
263
|
CONFIG_PATH = join(DOT_DIR, "config.json");
|
|
@@ -259,6 +308,42 @@ function isWatchOnly(account) {
|
|
|
259
308
|
return account.secret === undefined;
|
|
260
309
|
}
|
|
261
310
|
|
|
311
|
+
// src/utils/fuzzy-match.ts
|
|
312
|
+
function levenshtein(a, b) {
|
|
313
|
+
const la = a.length;
|
|
314
|
+
const lb = b.length;
|
|
315
|
+
const dp = Array.from({ length: la + 1 }, () => Array(lb + 1).fill(0));
|
|
316
|
+
for (let i = 0;i <= la; i++)
|
|
317
|
+
dp[i][0] = i;
|
|
318
|
+
for (let j = 0;j <= lb; j++)
|
|
319
|
+
dp[0][j] = j;
|
|
320
|
+
for (let i = 1;i <= la; i++) {
|
|
321
|
+
for (let j = 1;j <= lb; j++) {
|
|
322
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
323
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return dp[la][lb];
|
|
327
|
+
}
|
|
328
|
+
function findClosest(input, candidates, maxDistance = 3) {
|
|
329
|
+
const lower = input.toLowerCase();
|
|
330
|
+
const exact = candidates.find((c) => c.toLowerCase() === lower);
|
|
331
|
+
if (exact)
|
|
332
|
+
return [exact];
|
|
333
|
+
const scored = candidates.map((c) => ({ name: c, dist: levenshtein(lower, c.toLowerCase()) })).filter((s) => s.dist <= maxDistance).sort((a, b) => a.dist - b.dist);
|
|
334
|
+
return scored.slice(0, 3).map((s) => s.name);
|
|
335
|
+
}
|
|
336
|
+
function suggestMessage(kind, input, candidates) {
|
|
337
|
+
const suggestions = findClosest(input, candidates);
|
|
338
|
+
if (suggestions.length === 0) {
|
|
339
|
+
return `Unknown ${kind} "${input}".`;
|
|
340
|
+
}
|
|
341
|
+
if (suggestions.length === 1 && suggestions[0]?.toLowerCase() === input.toLowerCase()) {
|
|
342
|
+
return suggestions[0];
|
|
343
|
+
}
|
|
344
|
+
return `Unknown ${kind} "${input}". Did you mean: ${suggestions.join(", ")}?`;
|
|
345
|
+
}
|
|
346
|
+
|
|
262
347
|
// src/core/accounts.ts
|
|
263
348
|
import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
|
|
264
349
|
import {
|
|
@@ -369,8 +454,14 @@ async function resolveAccountKeypair(name) {
|
|
|
369
454
|
const accountsFile = await loadAccounts();
|
|
370
455
|
const account = findAccount(accountsFile, name);
|
|
371
456
|
if (!account) {
|
|
372
|
-
const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)];
|
|
373
|
-
|
|
457
|
+
const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
|
|
458
|
+
const suggestions = findClosest(name, available);
|
|
459
|
+
const hint = suggestions.length > 0 ? `
|
|
460
|
+
Did you mean: ${suggestions.join(", ")}?` : "";
|
|
461
|
+
const list = available.map((a) => `
|
|
462
|
+
- ${a}`).join("");
|
|
463
|
+
throw new Error(`Unknown account "${name}".${hint}
|
|
464
|
+
Available accounts:${list}`);
|
|
374
465
|
}
|
|
375
466
|
if (account.secret === undefined) {
|
|
376
467
|
throw new Error(`Account "${name}" is watch-only (no secret). Cannot sign. Import with --secret or --env.`);
|
|
@@ -390,6 +481,7 @@ var init_accounts = __esm(() => {
|
|
|
390
481
|
});
|
|
391
482
|
|
|
392
483
|
// src/utils/binary-display.ts
|
|
484
|
+
import { Binary } from "polkadot-api";
|
|
393
485
|
function isReadableText(text) {
|
|
394
486
|
for (let i = 0;i < text.length; i++) {
|
|
395
487
|
const code = text.charCodeAt(i);
|
|
@@ -407,20 +499,17 @@ function isReadableText(text) {
|
|
|
407
499
|
return true;
|
|
408
500
|
}
|
|
409
501
|
function binaryToDisplay(value) {
|
|
410
|
-
const text =
|
|
411
|
-
return isReadableText(text) ? text :
|
|
502
|
+
const text = Binary.toText(value);
|
|
503
|
+
return isReadableText(text) ? text : Binary.toHex(value);
|
|
412
504
|
}
|
|
505
|
+
var init_binary_display = () => {};
|
|
413
506
|
|
|
414
507
|
// src/core/output.ts
|
|
415
|
-
import { Binary } from "polkadot-api";
|
|
416
508
|
function replacer(_key, value) {
|
|
417
509
|
if (typeof value === "bigint")
|
|
418
510
|
return value.toString();
|
|
419
|
-
if (value instanceof Binary) {
|
|
420
|
-
return binaryToDisplay(value);
|
|
421
|
-
}
|
|
422
511
|
if (value instanceof Uint8Array)
|
|
423
|
-
return
|
|
512
|
+
return binaryToDisplay(value);
|
|
424
513
|
return value;
|
|
425
514
|
}
|
|
426
515
|
function formatJson(data) {
|
|
@@ -442,6 +531,12 @@ function printResult(data, format = "pretty") {
|
|
|
442
531
|
console.log(formatPretty(data));
|
|
443
532
|
}
|
|
444
533
|
}
|
|
534
|
+
function isJsonOutput(opts) {
|
|
535
|
+
return opts.json === true || opts.output === "json";
|
|
536
|
+
}
|
|
537
|
+
function printJsonLine(data) {
|
|
538
|
+
console.log(JSON.stringify(data, replacer));
|
|
539
|
+
}
|
|
445
540
|
function printHeading(text) {
|
|
446
541
|
console.log(`
|
|
447
542
|
${BOLD}${text}${RESET}
|
|
@@ -500,6 +595,7 @@ function firstSentence(docs) {
|
|
|
500
595
|
}
|
|
501
596
|
var isTTY, RESET, CYAN, GREEN, RED, YELLOW, MAGENTA, DIM, BOLD, CHECK_MARK = "✓", SPINNER_FRAMES;
|
|
502
597
|
var init_output = __esm(() => {
|
|
598
|
+
init_binary_display();
|
|
503
599
|
isTTY = process.stdout.isTTY ?? false;
|
|
504
600
|
RESET = isTTY ? "\x1B[0m" : "";
|
|
505
601
|
CYAN = isTTY ? "\x1B[36m" : "";
|
|
@@ -531,62 +627,35 @@ function deriveBandersnatchMember(mnemonic, context) {
|
|
|
531
627
|
}
|
|
532
628
|
var init_bandersnatch = () => {};
|
|
533
629
|
|
|
534
|
-
// src/utils/errors.ts
|
|
535
|
-
var CliError, ConnectionError, MetadataError;
|
|
536
|
-
var init_errors = __esm(() => {
|
|
537
|
-
CliError = class CliError extends Error {
|
|
538
|
-
constructor(message) {
|
|
539
|
-
super(message);
|
|
540
|
-
this.name = "CliError";
|
|
541
|
-
}
|
|
542
|
-
};
|
|
543
|
-
ConnectionError = class ConnectionError extends CliError {
|
|
544
|
-
constructor(message) {
|
|
545
|
-
super(message);
|
|
546
|
-
this.name = "ConnectionError";
|
|
547
|
-
}
|
|
548
|
-
};
|
|
549
|
-
MetadataError = class MetadataError extends CliError {
|
|
550
|
-
constructor(message) {
|
|
551
|
-
super(message);
|
|
552
|
-
this.name = "MetadataError";
|
|
553
|
-
}
|
|
554
|
-
};
|
|
555
|
-
});
|
|
556
|
-
|
|
557
630
|
// src/core/client.ts
|
|
558
631
|
import { createClient } from "polkadot-api";
|
|
559
|
-
import {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
const orig = console.error;
|
|
632
|
+
import { getWsProvider } from "polkadot-api/ws";
|
|
633
|
+
function suppressProviderNoise() {
|
|
634
|
+
const origError = console.error;
|
|
635
|
+
const origWarn = console.warn;
|
|
564
636
|
console.error = (...args) => {
|
|
565
637
|
if (typeof args[0] === "string" && args[0].includes("Unable to connect"))
|
|
566
638
|
return;
|
|
567
|
-
|
|
639
|
+
origError(...args);
|
|
640
|
+
};
|
|
641
|
+
console.warn = (...args) => {
|
|
642
|
+
if (typeof args[0] === "string" && args[0].includes("ChainHead"))
|
|
643
|
+
return;
|
|
644
|
+
origWarn(...args);
|
|
568
645
|
};
|
|
569
646
|
return () => {
|
|
570
|
-
console.error =
|
|
647
|
+
console.error = origError;
|
|
648
|
+
console.warn = origWarn;
|
|
571
649
|
};
|
|
572
650
|
}
|
|
573
651
|
async function createChainClient(chainName, chainConfig, rpcOverride) {
|
|
574
|
-
const
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
} else {
|
|
580
|
-
const rpc = rpcOverride ?? chainConfig.rpc;
|
|
581
|
-
if (!rpc) {
|
|
582
|
-
restoreConsole();
|
|
583
|
-
throw new ConnectionError(`No RPC endpoint configured for chain "${chainName}". Use --rpc or configure one with: dot chain add ${chainName} --rpc <url>`);
|
|
584
|
-
}
|
|
585
|
-
provider = withPolkadotSdkCompat(getWsProvider(rpc, {
|
|
586
|
-
timeout: 1e4,
|
|
587
|
-
websocketClass: WebSocket
|
|
588
|
-
}));
|
|
652
|
+
const restoreConsole = suppressProviderNoise();
|
|
653
|
+
const rpc = rpcOverride ?? chainConfig.rpc;
|
|
654
|
+
if (!rpc) {
|
|
655
|
+
restoreConsole();
|
|
656
|
+
throw new ConnectionError(`No RPC endpoint configured for chain "${chainName}". Use --rpc or configure one with: dot chain add ${chainName} --rpc <url>`);
|
|
589
657
|
}
|
|
658
|
+
const provider = getWsProvider(rpc, { timeout: 1e4 });
|
|
590
659
|
const client = createClient(provider, {
|
|
591
660
|
getMetadata: async () => loadMetadata(chainName),
|
|
592
661
|
setMetadata: async (_codeHash, metadata) => {
|
|
@@ -596,51 +665,25 @@ async function createChainClient(chainName, chainConfig, rpcOverride) {
|
|
|
596
665
|
return {
|
|
597
666
|
client,
|
|
598
667
|
destroy: () => {
|
|
599
|
-
|
|
600
|
-
|
|
668
|
+
try {
|
|
669
|
+
client.destroy();
|
|
670
|
+
} catch {}
|
|
671
|
+
setTimeout(restoreConsole, 500);
|
|
601
672
|
}
|
|
602
673
|
};
|
|
603
674
|
}
|
|
604
|
-
async function
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
if (entry.relay) {
|
|
614
|
-
const relayEntry = KNOWN_CHAIN_SPECS[entry.relay];
|
|
615
|
-
if (!relayEntry) {
|
|
616
|
-
throw new ConnectionError(`Relay chain "${entry.relay}" not found in known chain specs.`);
|
|
617
|
-
}
|
|
618
|
-
const { chainSpec: relaySpec } = await import(relayEntry.spec);
|
|
619
|
-
const relayChain = await smoldot.addChain({ chainSpec: relaySpec, disableJsonRpc: true });
|
|
620
|
-
const chain2 = await smoldot.addChain({ chainSpec, potentialRelayChains: [relayChain] });
|
|
621
|
-
return getSmProvider(chain2);
|
|
622
|
-
}
|
|
623
|
-
const chain = await smoldot.addChain({ chainSpec });
|
|
624
|
-
return getSmProvider(chain);
|
|
625
|
-
}
|
|
626
|
-
var KNOWN_CHAIN_SPECS;
|
|
675
|
+
async function getParachainId(clientHandle) {
|
|
676
|
+
try {
|
|
677
|
+
const unsafeApi = clientHandle.client.getUnsafeApi();
|
|
678
|
+
const parachainId = await unsafeApi.query.ParachainInfo.ParachainId.getValue();
|
|
679
|
+
return typeof parachainId === "number" ? parachainId : null;
|
|
680
|
+
} catch {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
627
684
|
var init_client = __esm(() => {
|
|
628
685
|
init_store();
|
|
629
686
|
init_errors();
|
|
630
|
-
KNOWN_CHAIN_SPECS = {
|
|
631
|
-
polkadot: { spec: "polkadot-api/chains/polkadot" },
|
|
632
|
-
kusama: { spec: "polkadot-api/chains/ksmcc3" },
|
|
633
|
-
westend: { spec: "polkadot-api/chains/westend2" },
|
|
634
|
-
paseo: { spec: "polkadot-api/chains/paseo" },
|
|
635
|
-
"polkadot-asset-hub": { spec: "polkadot-api/chains/polkadot_asset_hub", relay: "polkadot" },
|
|
636
|
-
"polkadot-bridge-hub": { spec: "polkadot-api/chains/polkadot_bridge_hub", relay: "polkadot" },
|
|
637
|
-
"polkadot-collectives": { spec: "polkadot-api/chains/polkadot_collectives", relay: "polkadot" },
|
|
638
|
-
"polkadot-coretime": { spec: "polkadot-api/chains/polkadot_coretime", relay: "polkadot" },
|
|
639
|
-
"polkadot-people": { spec: "polkadot-api/chains/polkadot_people", relay: "polkadot" },
|
|
640
|
-
"paseo-asset-hub": { spec: "polkadot-api/chains/paseo_asset_hub", relay: "paseo" },
|
|
641
|
-
"paseo-coretime": { spec: "polkadot-api/chains/paseo_coretime", relay: "paseo" },
|
|
642
|
-
"paseo-people": { spec: "polkadot-api/chains/paseo_people", relay: "paseo" }
|
|
643
|
-
};
|
|
644
687
|
});
|
|
645
688
|
|
|
646
689
|
// src/core/metadata.ts
|
|
@@ -915,42 +958,6 @@ var init_metadata = __esm(() => {
|
|
|
915
958
|
v15Arg = toHex(u32.enc(15));
|
|
916
959
|
});
|
|
917
960
|
|
|
918
|
-
// src/utils/fuzzy-match.ts
|
|
919
|
-
function levenshtein(a, b) {
|
|
920
|
-
const la = a.length;
|
|
921
|
-
const lb = b.length;
|
|
922
|
-
const dp = Array.from({ length: la + 1 }, () => Array(lb + 1).fill(0));
|
|
923
|
-
for (let i = 0;i <= la; i++)
|
|
924
|
-
dp[i][0] = i;
|
|
925
|
-
for (let j = 0;j <= lb; j++)
|
|
926
|
-
dp[0][j] = j;
|
|
927
|
-
for (let i = 1;i <= la; i++) {
|
|
928
|
-
for (let j = 1;j <= lb; j++) {
|
|
929
|
-
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
930
|
-
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
return dp[la][lb];
|
|
934
|
-
}
|
|
935
|
-
function findClosest(input, candidates, maxDistance = 3) {
|
|
936
|
-
const lower = input.toLowerCase();
|
|
937
|
-
const exact = candidates.find((c) => c.toLowerCase() === lower);
|
|
938
|
-
if (exact)
|
|
939
|
-
return [exact];
|
|
940
|
-
const scored = candidates.map((c) => ({ name: c, dist: levenshtein(lower, c.toLowerCase()) })).filter((s) => s.dist <= maxDistance).sort((a, b) => a.dist - b.dist);
|
|
941
|
-
return scored.slice(0, 3).map((s) => s.name);
|
|
942
|
-
}
|
|
943
|
-
function suggestMessage(kind, input, candidates) {
|
|
944
|
-
const suggestions = findClosest(input, candidates);
|
|
945
|
-
if (suggestions.length === 0) {
|
|
946
|
-
return `Unknown ${kind} "${input}".`;
|
|
947
|
-
}
|
|
948
|
-
if (suggestions.length === 1 && suggestions[0]?.toLowerCase() === input.toLowerCase()) {
|
|
949
|
-
return suggestions[0];
|
|
950
|
-
}
|
|
951
|
-
return `Unknown ${kind} "${input}". Did you mean: ${suggestions.join(", ")}?`;
|
|
952
|
-
}
|
|
953
|
-
|
|
954
961
|
// src/core/explorers.ts
|
|
955
962
|
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)}`;
|
|
956
963
|
|
|
@@ -976,9 +983,14 @@ async function resolveAccountAddress(input) {
|
|
|
976
983
|
return input;
|
|
977
984
|
}
|
|
978
985
|
const stored = accountsFile.accounts.map((a) => a.name);
|
|
979
|
-
const available = [...DEV_NAMES, ...stored];
|
|
980
|
-
|
|
981
|
-
|
|
986
|
+
const available = [...DEV_NAMES, ...stored].sort((a, b) => a.localeCompare(b));
|
|
987
|
+
const suggestions = findClosest(input, available);
|
|
988
|
+
const hint = suggestions.length > 0 ? `
|
|
989
|
+
Did you mean: ${suggestions.join(", ")}?` : "";
|
|
990
|
+
const list = available.map((a) => `
|
|
991
|
+
- ${a}`).join("");
|
|
992
|
+
throw new Error(`Unknown account or address "${input}".${hint}
|
|
993
|
+
Available accounts:${list}`);
|
|
982
994
|
}
|
|
983
995
|
var init_resolve_address = __esm(() => {
|
|
984
996
|
init_accounts_store();
|
|
@@ -1006,6 +1018,7 @@ function parseValue(arg) {
|
|
|
1006
1018
|
}
|
|
1007
1019
|
|
|
1008
1020
|
// src/commands/tx.ts
|
|
1021
|
+
import { compact as scaleCompact } from "@polkadot-api/substrate-bindings";
|
|
1009
1022
|
import { getViewBuilder } from "@polkadot-api/view-builder";
|
|
1010
1023
|
import { Binary as Binary2 } from "polkadot-api";
|
|
1011
1024
|
import { stringify as stringifyYaml } from "yaml";
|
|
@@ -1206,7 +1219,7 @@ async function parseTypedArg(meta, entry, arg) {
|
|
|
1206
1219
|
case "enum": {
|
|
1207
1220
|
if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
|
|
1208
1221
|
const callCodec = meta.builder.buildDefinition(meta.lookup.call);
|
|
1209
|
-
return callCodec.dec(Binary2.fromHex(arg)
|
|
1222
|
+
return callCodec.dec(Binary2.fromHex(arg));
|
|
1210
1223
|
}
|
|
1211
1224
|
if (arg.startsWith("{")) {
|
|
1212
1225
|
try {
|
|
@@ -1318,6 +1331,7 @@ var init_tx = __esm(() => {
|
|
|
1318
1331
|
init_metadata();
|
|
1319
1332
|
init_output();
|
|
1320
1333
|
init_resolve_address();
|
|
1334
|
+
init_binary_display();
|
|
1321
1335
|
init_errors();
|
|
1322
1336
|
init_focused_inspect();
|
|
1323
1337
|
PAPI_BUILTIN_EXTENSIONS = new Set([
|
|
@@ -1348,6 +1362,13 @@ async function handleApis(target, args, opts) {
|
|
|
1348
1362
|
const { name: chainName2, chain: chainConfig2 } = resolveChain(config2, opts.chain);
|
|
1349
1363
|
const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
|
|
1350
1364
|
const apis = listRuntimeApis(meta);
|
|
1365
|
+
if (isJsonOutput(opts)) {
|
|
1366
|
+
console.log(formatJson({
|
|
1367
|
+
chain: chainName2,
|
|
1368
|
+
apis: apis.map((a) => ({ name: a.name, methods: a.methods.length }))
|
|
1369
|
+
}));
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1351
1372
|
printHeading(`Runtime APIs on ${chainName2} (${apis.length})`);
|
|
1352
1373
|
for (const api of apis) {
|
|
1353
1374
|
printItem(api.name, `${api.methods.length} methods`);
|
|
@@ -1370,7 +1391,24 @@ async function handleApis(target, args, opts) {
|
|
|
1370
1391
|
const meta = await loadMeta(chainName, chainConfig, opts.rpc);
|
|
1371
1392
|
const api = resolveRuntimeApi(meta, apiName);
|
|
1372
1393
|
if (api.methods.length === 0) {
|
|
1373
|
-
|
|
1394
|
+
if (isJsonOutput(opts)) {
|
|
1395
|
+
console.log(formatJson({ chain: chainName, api: api.name, methods: [] }));
|
|
1396
|
+
} else {
|
|
1397
|
+
console.log(`No methods in ${api.name}.`);
|
|
1398
|
+
}
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
if (isJsonOutput(opts)) {
|
|
1402
|
+
console.log(formatJson({
|
|
1403
|
+
chain: chainName,
|
|
1404
|
+
api: api.name,
|
|
1405
|
+
methods: api.methods.map((m) => ({
|
|
1406
|
+
name: m.name,
|
|
1407
|
+
args: describeRuntimeApiMethodArgs(meta, m),
|
|
1408
|
+
returns: describeType(meta.lookup, m.output),
|
|
1409
|
+
docs: firstSentence(m.docs)
|
|
1410
|
+
}))
|
|
1411
|
+
}));
|
|
1374
1412
|
return;
|
|
1375
1413
|
}
|
|
1376
1414
|
printHeading(`${api.name} Methods`);
|
|
@@ -1401,7 +1439,7 @@ async function handleApis(target, args, opts) {
|
|
|
1401
1439
|
const parsedArgs = await parseRuntimeApiArgs(meta, method, effectiveArgs);
|
|
1402
1440
|
const unsafeApi = clientHandle.client.getUnsafeApi();
|
|
1403
1441
|
const result = await unsafeApi.apis[api.name][method.name](...parsedArgs);
|
|
1404
|
-
const format = opts.output ?? "pretty";
|
|
1442
|
+
const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
|
|
1405
1443
|
printResult(result, format);
|
|
1406
1444
|
} finally {
|
|
1407
1445
|
clientHandle.destroy();
|
|
@@ -1438,11 +1476,23 @@ var init_apis = __esm(() => {
|
|
|
1438
1476
|
|
|
1439
1477
|
// src/commands/focused-inspect.ts
|
|
1440
1478
|
async function loadMeta(chainName, chainConfig, rpcOverride) {
|
|
1479
|
+
if (rpcOverride) {
|
|
1480
|
+
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
1481
|
+
`);
|
|
1482
|
+
const clientHandle = await createChainClient(chainName, chainConfig, rpcOverride);
|
|
1483
|
+
try {
|
|
1484
|
+
const raw = await fetchMetadataFromChain(clientHandle, chainName);
|
|
1485
|
+
return parseMetadata(raw);
|
|
1486
|
+
} finally {
|
|
1487
|
+
clientHandle.destroy();
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1441
1490
|
try {
|
|
1442
1491
|
return await getOrFetchMetadata(chainName);
|
|
1443
1492
|
} catch {
|
|
1444
|
-
|
|
1445
|
-
|
|
1493
|
+
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
1494
|
+
`);
|
|
1495
|
+
const clientHandle = await createChainClient(chainName, chainConfig);
|
|
1446
1496
|
try {
|
|
1447
1497
|
return await getOrFetchMetadata(chainName, clientHandle);
|
|
1448
1498
|
} finally {
|
|
@@ -1465,6 +1515,13 @@ async function handleCalls(target, opts) {
|
|
|
1465
1515
|
const meta2 = await loadMeta(chainName2, chainConfig2, opts.rpc);
|
|
1466
1516
|
const pallets = listPallets(meta2);
|
|
1467
1517
|
const withCalls = pallets.filter((p) => p.calls.length > 0);
|
|
1518
|
+
if (isJsonOutput(opts)) {
|
|
1519
|
+
console.log(formatJson({
|
|
1520
|
+
chain: chainName2,
|
|
1521
|
+
pallets: withCalls.map((p) => ({ name: p.name, calls: p.calls.length }))
|
|
1522
|
+
}));
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1468
1525
|
printHeading(`Pallets with calls on ${chainName2} (${withCalls.length})`);
|
|
1469
1526
|
for (const p of withCalls) {
|
|
1470
1527
|
printItem(p.name, `${p.calls.length} calls`);
|
|
@@ -1481,7 +1538,23 @@ async function handleCalls(target, opts) {
|
|
|
1481
1538
|
const pallet = resolvePallet(meta, palletName);
|
|
1482
1539
|
if (!itemName) {
|
|
1483
1540
|
if (pallet.calls.length === 0) {
|
|
1484
|
-
|
|
1541
|
+
if (isJsonOutput(opts)) {
|
|
1542
|
+
console.log(formatJson({ chain: chainName, pallet: pallet.name, calls: [] }));
|
|
1543
|
+
} else {
|
|
1544
|
+
console.log(`No calls in ${pallet.name}.`);
|
|
1545
|
+
}
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
if (isJsonOutput(opts)) {
|
|
1549
|
+
console.log(formatJson({
|
|
1550
|
+
chain: chainName,
|
|
1551
|
+
pallet: pallet.name,
|
|
1552
|
+
calls: pallet.calls.map((c) => ({
|
|
1553
|
+
name: c.name,
|
|
1554
|
+
args: describeCallArgs(meta, pallet.name, c.name),
|
|
1555
|
+
docs: firstSentence(c.docs)
|
|
1556
|
+
}))
|
|
1557
|
+
}));
|
|
1485
1558
|
return;
|
|
1486
1559
|
}
|
|
1487
1560
|
printHeading(`${pallet.name} Calls`);
|
|
@@ -1501,6 +1574,17 @@ async function handleCalls(target, opts) {
|
|
|
1501
1574
|
const names = pallet.calls.map((c) => c.name);
|
|
1502
1575
|
throw new Error(suggestMessage(`call in ${pallet.name}`, itemName, names));
|
|
1503
1576
|
}
|
|
1577
|
+
if (isJsonOutput(opts)) {
|
|
1578
|
+
console.log(formatJson({
|
|
1579
|
+
chain: chainName,
|
|
1580
|
+
pallet: pallet.name,
|
|
1581
|
+
item: callItem.name,
|
|
1582
|
+
category: "call",
|
|
1583
|
+
args: describeCallArgs(meta, pallet.name, callItem.name),
|
|
1584
|
+
docs: callItem.docs
|
|
1585
|
+
}));
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1504
1588
|
printHeading(`${pallet.name}.${callItem.name} (Call)`);
|
|
1505
1589
|
const args = describeCallArgs(meta, pallet.name, callItem.name);
|
|
1506
1590
|
console.log(` ${BOLD}Args:${RESET} ${args}`);
|
|
@@ -1517,6 +1601,13 @@ async function handleEvents(target, opts) {
|
|
|
1517
1601
|
const meta2 = await loadMeta(chainName2, chainConfig2, opts.rpc);
|
|
1518
1602
|
const pallets = listPallets(meta2);
|
|
1519
1603
|
const withEvents = pallets.filter((p) => p.events.length > 0);
|
|
1604
|
+
if (isJsonOutput(opts)) {
|
|
1605
|
+
console.log(formatJson({
|
|
1606
|
+
chain: chainName2,
|
|
1607
|
+
pallets: withEvents.map((p) => ({ name: p.name, events: p.events.length }))
|
|
1608
|
+
}));
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1520
1611
|
printHeading(`Pallets with events on ${chainName2} (${withEvents.length})`);
|
|
1521
1612
|
for (const p of withEvents) {
|
|
1522
1613
|
printItem(p.name, `${p.events.length} events`);
|
|
@@ -1533,7 +1624,23 @@ async function handleEvents(target, opts) {
|
|
|
1533
1624
|
const pallet = resolvePallet(meta, palletName);
|
|
1534
1625
|
if (!itemName) {
|
|
1535
1626
|
if (pallet.events.length === 0) {
|
|
1536
|
-
|
|
1627
|
+
if (isJsonOutput(opts)) {
|
|
1628
|
+
console.log(formatJson({ chain: chainName, pallet: pallet.name, events: [] }));
|
|
1629
|
+
} else {
|
|
1630
|
+
console.log(`No events in ${pallet.name}.`);
|
|
1631
|
+
}
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
if (isJsonOutput(opts)) {
|
|
1635
|
+
console.log(formatJson({
|
|
1636
|
+
chain: chainName,
|
|
1637
|
+
pallet: pallet.name,
|
|
1638
|
+
events: pallet.events.map((e) => ({
|
|
1639
|
+
name: e.name,
|
|
1640
|
+
fields: describeEventFields(meta, pallet.name, e.name),
|
|
1641
|
+
docs: firstSentence(e.docs)
|
|
1642
|
+
}))
|
|
1643
|
+
}));
|
|
1537
1644
|
return;
|
|
1538
1645
|
}
|
|
1539
1646
|
printHeading(`${pallet.name} Events`);
|
|
@@ -1553,6 +1660,17 @@ async function handleEvents(target, opts) {
|
|
|
1553
1660
|
const names = pallet.events.map((e) => e.name);
|
|
1554
1661
|
throw new Error(suggestMessage(`event in ${pallet.name}`, itemName, names));
|
|
1555
1662
|
}
|
|
1663
|
+
if (isJsonOutput(opts)) {
|
|
1664
|
+
console.log(formatJson({
|
|
1665
|
+
chain: chainName,
|
|
1666
|
+
pallet: pallet.name,
|
|
1667
|
+
item: eventItem.name,
|
|
1668
|
+
category: "event",
|
|
1669
|
+
fields: describeEventFields(meta, pallet.name, eventItem.name),
|
|
1670
|
+
docs: eventItem.docs
|
|
1671
|
+
}));
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1556
1674
|
printHeading(`${pallet.name}.${eventItem.name} (Event)`);
|
|
1557
1675
|
const fields = describeEventFields(meta, pallet.name, eventItem.name);
|
|
1558
1676
|
console.log(` ${BOLD}Fields:${RESET} ${fields}`);
|
|
@@ -1569,6 +1687,13 @@ async function handleErrors(target, opts) {
|
|
|
1569
1687
|
const meta2 = await loadMeta(chainName2, chainConfig2, opts.rpc);
|
|
1570
1688
|
const pallets = listPallets(meta2);
|
|
1571
1689
|
const withErrors = pallets.filter((p) => p.errors.length > 0);
|
|
1690
|
+
if (isJsonOutput(opts)) {
|
|
1691
|
+
console.log(formatJson({
|
|
1692
|
+
chain: chainName2,
|
|
1693
|
+
pallets: withErrors.map((p) => ({ name: p.name, errors: p.errors.length }))
|
|
1694
|
+
}));
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1572
1697
|
printHeading(`Pallets with errors on ${chainName2} (${withErrors.length})`);
|
|
1573
1698
|
for (const p of withErrors) {
|
|
1574
1699
|
printItem(p.name, `${p.errors.length} errors`);
|
|
@@ -1585,7 +1710,19 @@ async function handleErrors(target, opts) {
|
|
|
1585
1710
|
const pallet = resolvePallet(meta, palletName);
|
|
1586
1711
|
if (!itemName) {
|
|
1587
1712
|
if (pallet.errors.length === 0) {
|
|
1588
|
-
|
|
1713
|
+
if (isJsonOutput(opts)) {
|
|
1714
|
+
console.log(formatJson({ chain: chainName, pallet: pallet.name, errors: [] }));
|
|
1715
|
+
} else {
|
|
1716
|
+
console.log(`No errors in ${pallet.name}.`);
|
|
1717
|
+
}
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
if (isJsonOutput(opts)) {
|
|
1721
|
+
console.log(formatJson({
|
|
1722
|
+
chain: chainName,
|
|
1723
|
+
pallet: pallet.name,
|
|
1724
|
+
errors: pallet.errors.map((e) => ({ name: e.name, docs: firstSentence(e.docs) }))
|
|
1725
|
+
}));
|
|
1589
1726
|
return;
|
|
1590
1727
|
}
|
|
1591
1728
|
printHeading(`${pallet.name} Errors`);
|
|
@@ -1604,6 +1741,16 @@ async function handleErrors(target, opts) {
|
|
|
1604
1741
|
const names = pallet.errors.map((e) => e.name);
|
|
1605
1742
|
throw new Error(suggestMessage(`error in ${pallet.name}`, itemName, names));
|
|
1606
1743
|
}
|
|
1744
|
+
if (isJsonOutput(opts)) {
|
|
1745
|
+
console.log(formatJson({
|
|
1746
|
+
chain: chainName,
|
|
1747
|
+
pallet: pallet.name,
|
|
1748
|
+
item: errorItem.name,
|
|
1749
|
+
category: "error",
|
|
1750
|
+
docs: errorItem.docs
|
|
1751
|
+
}));
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1607
1754
|
printHeading(`${pallet.name}.${errorItem.name} (Error)`);
|
|
1608
1755
|
if (errorItem.docs.length) {
|
|
1609
1756
|
printDocs(errorItem.docs);
|
|
@@ -1617,6 +1764,13 @@ async function handleStorage(target, opts) {
|
|
|
1617
1764
|
const meta2 = await loadMeta(chainName2, chainConfig2, opts.rpc);
|
|
1618
1765
|
const pallets = listPallets(meta2);
|
|
1619
1766
|
const withStorage = pallets.filter((p) => p.storage.length > 0);
|
|
1767
|
+
if (isJsonOutput(opts)) {
|
|
1768
|
+
console.log(formatJson({
|
|
1769
|
+
chain: chainName2,
|
|
1770
|
+
pallets: withStorage.map((p) => ({ name: p.name, storage: p.storage.length }))
|
|
1771
|
+
}));
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1620
1774
|
printHeading(`Pallets with storage on ${chainName2} (${withStorage.length})`);
|
|
1621
1775
|
for (const p of withStorage) {
|
|
1622
1776
|
printItem(p.name, `${p.storage.length} storage`);
|
|
@@ -1633,7 +1787,23 @@ async function handleStorage(target, opts) {
|
|
|
1633
1787
|
const pallet = resolvePallet(meta, palletName);
|
|
1634
1788
|
if (!itemName) {
|
|
1635
1789
|
if (pallet.storage.length === 0) {
|
|
1636
|
-
|
|
1790
|
+
if (isJsonOutput(opts)) {
|
|
1791
|
+
console.log(formatJson({ chain: chainName, pallet: pallet.name, storage: [] }));
|
|
1792
|
+
} else {
|
|
1793
|
+
console.log(`No storage items in ${pallet.name}.`);
|
|
1794
|
+
}
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
if (isJsonOutput(opts)) {
|
|
1798
|
+
console.log(formatJson({
|
|
1799
|
+
chain: chainName,
|
|
1800
|
+
pallet: pallet.name,
|
|
1801
|
+
storage: pallet.storage.map((s) => {
|
|
1802
|
+
const valueType = describeType(meta.lookup, s.valueTypeId);
|
|
1803
|
+
const keyType = s.keyTypeId != null ? describeType(meta.lookup, s.keyTypeId) : undefined;
|
|
1804
|
+
return { name: s.name, type: s.type, valueType, keyType, docs: firstSentence(s.docs) };
|
|
1805
|
+
})
|
|
1806
|
+
}));
|
|
1637
1807
|
return;
|
|
1638
1808
|
}
|
|
1639
1809
|
printHeading(`${pallet.name} Storage`);
|
|
@@ -1660,6 +1830,21 @@ async function handleStorage(target, opts) {
|
|
|
1660
1830
|
const names = pallet.storage.map((s) => s.name);
|
|
1661
1831
|
throw new Error(suggestMessage(`storage item in ${pallet.name}`, itemName, names));
|
|
1662
1832
|
}
|
|
1833
|
+
if (isJsonOutput(opts)) {
|
|
1834
|
+
const valueType = describeType(meta.lookup, storageItem.valueTypeId);
|
|
1835
|
+
const keyType = storageItem.keyTypeId != null ? describeType(meta.lookup, storageItem.keyTypeId) : undefined;
|
|
1836
|
+
console.log(formatJson({
|
|
1837
|
+
chain: chainName,
|
|
1838
|
+
pallet: pallet.name,
|
|
1839
|
+
item: storageItem.name,
|
|
1840
|
+
category: "storage",
|
|
1841
|
+
type: storageItem.type,
|
|
1842
|
+
valueType,
|
|
1843
|
+
keyType,
|
|
1844
|
+
docs: storageItem.docs
|
|
1845
|
+
}));
|
|
1846
|
+
return;
|
|
1847
|
+
}
|
|
1663
1848
|
printHeading(`${pallet.name}.${storageItem.name} (Storage)`);
|
|
1664
1849
|
console.log(` ${BOLD}Type:${RESET} ${storageItem.type}`);
|
|
1665
1850
|
console.log(` ${BOLD}Value:${RESET} ${describeType(meta.lookup, storageItem.valueTypeId)}`);
|
|
@@ -1998,6 +2183,9 @@ async function generateCompletions(currentWord, precedingWords) {
|
|
|
1998
2183
|
if (prevWord === "--wait" || prevWord === "-w") {
|
|
1999
2184
|
return filterPrefix(["broadcast", "best-block", "best", "finalized"], currentWord);
|
|
2000
2185
|
}
|
|
2186
|
+
if (prevWord === "--relay") {
|
|
2187
|
+
return filterPrefix(knownChains, currentWord);
|
|
2188
|
+
}
|
|
2001
2189
|
if (currentWord.startsWith("--")) {
|
|
2002
2190
|
const activeCategory = detectCategory(precedingWords, knownChains);
|
|
2003
2191
|
const options = [...GLOBAL_OPTIONS];
|
|
@@ -2005,10 +2193,19 @@ async function generateCompletions(currentWord, precedingWords) {
|
|
|
2005
2193
|
options.push(...TX_OPTIONS);
|
|
2006
2194
|
if (activeCategory === "query")
|
|
2007
2195
|
options.push(...QUERY_OPTIONS);
|
|
2196
|
+
const chainIdx = precedingWords.indexOf("chain");
|
|
2197
|
+
if (chainIdx >= 0 && precedingWords[chainIdx + 1] === "add") {
|
|
2198
|
+
options.push("--relay", "--parachain-id");
|
|
2199
|
+
}
|
|
2008
2200
|
return filterPrefix(options, currentWord);
|
|
2009
2201
|
}
|
|
2010
2202
|
const firstArg = precedingWords.find((w) => !w.startsWith("-"));
|
|
2011
2203
|
if (firstArg === "chain") {
|
|
2204
|
+
const chainSubIdx = precedingWords.indexOf("chain");
|
|
2205
|
+
const subcommand = precedingWords[chainSubIdx + 1];
|
|
2206
|
+
if (subcommand === "add" && currentWord.startsWith("--")) {
|
|
2207
|
+
return filterPrefix(["--rpc", "--relay", "--parachain-id", ...GLOBAL_OPTIONS], currentWord);
|
|
2208
|
+
}
|
|
2012
2209
|
return filterPrefix(CHAIN_SUBCOMMANDS, currentWord);
|
|
2013
2210
|
}
|
|
2014
2211
|
if (firstArg === "account") {
|
|
@@ -2062,7 +2259,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2062
2259
|
return completeApisCategory(first, numComplete, endsWithDot, completeSegments, currentWord, config, chainFromFlag);
|
|
2063
2260
|
}
|
|
2064
2261
|
if (numComplete === 1 && endsWithDot) {
|
|
2065
|
-
const chainName = chainFromFlag
|
|
2262
|
+
const chainName = chainFromFlag;
|
|
2263
|
+
if (!chainName)
|
|
2264
|
+
return [];
|
|
2066
2265
|
const pallets = await loadPallets(config, chainName);
|
|
2067
2266
|
if (!pallets)
|
|
2068
2267
|
return [];
|
|
@@ -2071,7 +2270,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2071
2270
|
return filterPrefix(candidates, currentWord.slice(0, -1));
|
|
2072
2271
|
}
|
|
2073
2272
|
if (numComplete === 1 && !endsWithDot) {
|
|
2074
|
-
const chainName = chainFromFlag
|
|
2273
|
+
const chainName = chainFromFlag;
|
|
2274
|
+
if (!chainName)
|
|
2275
|
+
return [];
|
|
2075
2276
|
const pallets = await loadPallets(config, chainName);
|
|
2076
2277
|
if (!pallets)
|
|
2077
2278
|
return [];
|
|
@@ -2081,7 +2282,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2081
2282
|
}
|
|
2082
2283
|
if (numComplete === 2) {
|
|
2083
2284
|
const palletName2 = completeSegments[1];
|
|
2084
|
-
const chainName = chainFromFlag
|
|
2285
|
+
const chainName = chainFromFlag;
|
|
2286
|
+
if (!chainName)
|
|
2287
|
+
return [];
|
|
2085
2288
|
const pallets = await loadPallets(config, chainName);
|
|
2086
2289
|
if (!pallets)
|
|
2087
2290
|
return [];
|
|
@@ -2143,7 +2346,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2143
2346
|
return [];
|
|
2144
2347
|
}
|
|
2145
2348
|
async function completeApisCategory(prefix, numComplete, endsWithDot, segments, currentWord, config, chainNameOverride) {
|
|
2146
|
-
const chainName = chainNameOverride
|
|
2349
|
+
const chainName = chainNameOverride;
|
|
2350
|
+
if (!chainName)
|
|
2351
|
+
return [];
|
|
2147
2352
|
const apis = await loadRuntimeApiNames(config, chainName);
|
|
2148
2353
|
if (!apis)
|
|
2149
2354
|
return [];
|
|
@@ -2187,7 +2392,7 @@ var init_complete = __esm(() => {
|
|
|
2187
2392
|
api: "apis"
|
|
2188
2393
|
};
|
|
2189
2394
|
NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "sign", "parachain", "completions"];
|
|
2190
|
-
CHAIN_SUBCOMMANDS = ["add", "remove", "update", "list"
|
|
2395
|
+
CHAIN_SUBCOMMANDS = ["add", "remove", "update", "list"];
|
|
2191
2396
|
ACCOUNT_SUBCOMMANDS = [
|
|
2192
2397
|
"add",
|
|
2193
2398
|
"create",
|
|
@@ -2199,9 +2404,10 @@ var init_complete = __esm(() => {
|
|
|
2199
2404
|
"delete",
|
|
2200
2405
|
"inspect"
|
|
2201
2406
|
];
|
|
2202
|
-
GLOBAL_OPTIONS = ["--chain", "--rpc", "--
|
|
2407
|
+
GLOBAL_OPTIONS = ["--chain", "--rpc", "--output", "--help", "--version"];
|
|
2203
2408
|
TX_OPTIONS = [
|
|
2204
2409
|
"--from",
|
|
2410
|
+
"--unsigned",
|
|
2205
2411
|
"--dry-run",
|
|
2206
2412
|
"--encode",
|
|
2207
2413
|
"--ext",
|
|
@@ -2217,12 +2423,13 @@ var init_complete = __esm(() => {
|
|
|
2217
2423
|
// src/cli.ts
|
|
2218
2424
|
import cac from "cac";
|
|
2219
2425
|
// package.json
|
|
2220
|
-
var version = "1.
|
|
2426
|
+
var version = "1.14.0";
|
|
2221
2427
|
|
|
2222
2428
|
// src/commands/account.ts
|
|
2223
2429
|
init_accounts_store();
|
|
2224
2430
|
init_accounts();
|
|
2225
2431
|
init_output();
|
|
2432
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
2226
2433
|
var ACCOUNT_HELP = `
|
|
2227
2434
|
${BOLD}Usage:${RESET}
|
|
2228
2435
|
$ dot account add <name> <ss58|hex> Add a watch-only address (no secret)
|
|
@@ -2231,6 +2438,8 @@ ${BOLD}Usage:${RESET}
|
|
|
2231
2438
|
$ dot account create|new <name> [--path <derivation>] Create a new account
|
|
2232
2439
|
$ dot account import <name> --secret <s> [--path <derivation>] Import from BIP39 mnemonic
|
|
2233
2440
|
$ dot account import <name> --env <VAR> [--path <derivation>] Import account backed by env variable
|
|
2441
|
+
$ dot account import --file <path> Batch-import accounts from a file
|
|
2442
|
+
$ dot account export [names...] Export accounts to stdout
|
|
2234
2443
|
$ dot account derive <source> <new-name> --path <derivation> Derive a child account
|
|
2235
2444
|
$ dot account inspect <input> [--prefix <N>] Inspect an account/address/key
|
|
2236
2445
|
$ dot account list List all accounts
|
|
@@ -2243,6 +2452,13 @@ ${BOLD}Examples:${RESET}
|
|
|
2243
2452
|
$ dot account create multi --path //polkadot//0/wallet
|
|
2244
2453
|
$ dot account import treasury --secret "word1 word2 ... word12"
|
|
2245
2454
|
$ dot account import ci-signer --env MY_SECRET --path //ci
|
|
2455
|
+
$ dot account import --file team-accounts.json
|
|
2456
|
+
$ dot account import --file accounts.json --dry-run
|
|
2457
|
+
$ dot account import --file accounts.json --overwrite
|
|
2458
|
+
$ dot account export
|
|
2459
|
+
$ dot account export treasury my-validator
|
|
2460
|
+
$ dot account export --include-secrets --file backup.json
|
|
2461
|
+
$ dot account export --watch-only
|
|
2246
2462
|
$ dot account derive treasury treasury-staking --path //staking
|
|
2247
2463
|
$ dot account inspect alice
|
|
2248
2464
|
$ dot account inspect 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
|
|
@@ -2255,10 +2471,10 @@ ${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
|
|
|
2255
2471
|
Hex seed import (0x...) is not supported via CLI.${RESET}
|
|
2256
2472
|
`.trimStart();
|
|
2257
2473
|
function registerAccountCommands(cli) {
|
|
2258
|
-
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) => {
|
|
2474
|
+
cli.command("account [action] [...names]", "Manage local accounts (create, import, list, remove, export)").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)").option("--file <path>", "Input/output file for batch import/export").option("--overwrite", "Overwrite existing accounts on batch import").option("--dry-run", "Preview batch import without applying changes").option("--include-secrets", "Include secrets in export (redacted by default)").option("--watch-only", "Export only watch-only accounts").action(async (action, names, opts) => {
|
|
2259
2475
|
if (!action) {
|
|
2260
2476
|
if (process.argv[2] === "accounts")
|
|
2261
|
-
return accountList();
|
|
2477
|
+
return accountList(opts);
|
|
2262
2478
|
console.log(ACCOUNT_HELP);
|
|
2263
2479
|
return;
|
|
2264
2480
|
}
|
|
@@ -2269,16 +2485,20 @@ function registerAccountCommands(cli) {
|
|
|
2269
2485
|
case "add":
|
|
2270
2486
|
if (opts.secret || opts.env)
|
|
2271
2487
|
return accountImport(names[0], opts);
|
|
2272
|
-
return accountAddWatchOnly(names[0], names[1]);
|
|
2488
|
+
return accountAddWatchOnly(names[0], names[1], opts);
|
|
2273
2489
|
case "import":
|
|
2490
|
+
if (opts.file)
|
|
2491
|
+
return accountBatchImport(opts);
|
|
2274
2492
|
return accountImport(names[0], opts);
|
|
2493
|
+
case "export":
|
|
2494
|
+
return accountExport(names, opts);
|
|
2275
2495
|
case "derive":
|
|
2276
2496
|
return accountDerive(names[0], names[1], opts);
|
|
2277
2497
|
case "list":
|
|
2278
|
-
return accountList();
|
|
2498
|
+
return accountList(opts);
|
|
2279
2499
|
case "delete":
|
|
2280
2500
|
case "remove":
|
|
2281
|
-
return accountRemove(names);
|
|
2501
|
+
return accountRemove(names, opts);
|
|
2282
2502
|
case "inspect":
|
|
2283
2503
|
return accountInspect(names[0], opts);
|
|
2284
2504
|
default:
|
|
@@ -2316,6 +2536,18 @@ async function accountCreate(name, opts) {
|
|
|
2316
2536
|
bandersnatch
|
|
2317
2537
|
});
|
|
2318
2538
|
await saveAccounts(accountsFile);
|
|
2539
|
+
if (isJsonOutput(opts)) {
|
|
2540
|
+
console.log(formatJson({
|
|
2541
|
+
name,
|
|
2542
|
+
address,
|
|
2543
|
+
publicKey: hexPub,
|
|
2544
|
+
mnemonic,
|
|
2545
|
+
path: path || undefined,
|
|
2546
|
+
bandersnatch
|
|
2547
|
+
}));
|
|
2548
|
+
console.error(`Save this mnemonic phrase! It is the only way to recover this account.`);
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2319
2551
|
printHeading("Account Created");
|
|
2320
2552
|
console.log(` ${BOLD}Name:${RESET} ${name}`);
|
|
2321
2553
|
if (path)
|
|
@@ -2364,6 +2596,11 @@ async function accountImport(name, opts) {
|
|
|
2364
2596
|
derivationPath: path
|
|
2365
2597
|
});
|
|
2366
2598
|
await saveAccounts(accountsFile);
|
|
2599
|
+
if (isJsonOutput(opts)) {
|
|
2600
|
+
const address = publicKey ? toSs58(publicKey) : undefined;
|
|
2601
|
+
console.log(formatJson({ name, address, env: opts.env, path: path || undefined }));
|
|
2602
|
+
return;
|
|
2603
|
+
}
|
|
2367
2604
|
printHeading("Account Imported");
|
|
2368
2605
|
console.log(` ${BOLD}Name:${RESET} ${name}`);
|
|
2369
2606
|
if (path)
|
|
@@ -2386,6 +2623,10 @@ async function accountImport(name, opts) {
|
|
|
2386
2623
|
derivationPath: path
|
|
2387
2624
|
});
|
|
2388
2625
|
await saveAccounts(accountsFile);
|
|
2626
|
+
if (isJsonOutput(opts)) {
|
|
2627
|
+
console.log(formatJson({ name, address, publicKey: hexPub, path: path || undefined }));
|
|
2628
|
+
return;
|
|
2629
|
+
}
|
|
2389
2630
|
printHeading("Account Imported");
|
|
2390
2631
|
console.log(` ${BOLD}Name:${RESET} ${name}`);
|
|
2391
2632
|
if (path)
|
|
@@ -2394,7 +2635,7 @@ async function accountImport(name, opts) {
|
|
|
2394
2635
|
console.log();
|
|
2395
2636
|
}
|
|
2396
2637
|
}
|
|
2397
|
-
async function accountAddWatchOnly(name, address) {
|
|
2638
|
+
async function accountAddWatchOnly(name, address, opts = {}) {
|
|
2398
2639
|
if (!name) {
|
|
2399
2640
|
console.error(`Account name is required.
|
|
2400
2641
|
`);
|
|
@@ -2431,6 +2672,10 @@ async function accountAddWatchOnly(name, address) {
|
|
|
2431
2672
|
derivationPath: ""
|
|
2432
2673
|
});
|
|
2433
2674
|
await saveAccounts(accountsFile);
|
|
2675
|
+
if (isJsonOutput(opts)) {
|
|
2676
|
+
console.log(formatJson({ name, address: toSs58(hexPub), watchOnly: true }));
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2434
2679
|
printHeading("Account Added (watch-only)");
|
|
2435
2680
|
console.log(` ${BOLD}Name:${RESET} ${name}`);
|
|
2436
2681
|
console.log(` ${BOLD}Address:${RESET} ${toSs58(hexPub)}`);
|
|
@@ -2480,6 +2725,11 @@ async function accountDerive(sourceName, newName, opts) {
|
|
|
2480
2725
|
derivationPath: path
|
|
2481
2726
|
});
|
|
2482
2727
|
await saveAccounts(accountsFile);
|
|
2728
|
+
if (isJsonOutput(opts)) {
|
|
2729
|
+
const address = publicKey ? toSs58(publicKey) : undefined;
|
|
2730
|
+
console.log(formatJson({ name: newName, source: sourceName, path, address, env: sourceSecret.env }));
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2483
2733
|
printHeading("Account Derived");
|
|
2484
2734
|
console.log(` ${BOLD}Name:${RESET} ${newName}`);
|
|
2485
2735
|
console.log(` ${BOLD}Source:${RESET} ${sourceName}`);
|
|
@@ -2502,6 +2752,10 @@ async function accountDerive(sourceName, newName, opts) {
|
|
|
2502
2752
|
derivationPath: path
|
|
2503
2753
|
});
|
|
2504
2754
|
await saveAccounts(accountsFile);
|
|
2755
|
+
if (isJsonOutput(opts)) {
|
|
2756
|
+
console.log(formatJson({ name: newName, source: sourceName, path, address, publicKey: hexPub }));
|
|
2757
|
+
return;
|
|
2758
|
+
}
|
|
2505
2759
|
printHeading("Account Derived");
|
|
2506
2760
|
console.log(` ${BOLD}Name:${RESET} ${newName}`);
|
|
2507
2761
|
console.log(` ${BOLD}Source:${RESET} ${sourceName}`);
|
|
@@ -2510,14 +2764,43 @@ async function accountDerive(sourceName, newName, opts) {
|
|
|
2510
2764
|
console.log();
|
|
2511
2765
|
}
|
|
2512
2766
|
}
|
|
2513
|
-
async function accountList() {
|
|
2767
|
+
async function accountList(opts = {}) {
|
|
2768
|
+
const accountsFile = await loadAccounts();
|
|
2769
|
+
if (isJsonOutput(opts)) {
|
|
2770
|
+
const dev = DEV_NAMES.map((name) => ({
|
|
2771
|
+
name: name.charAt(0).toUpperCase() + name.slice(1),
|
|
2772
|
+
address: getDevAddress(name)
|
|
2773
|
+
}));
|
|
2774
|
+
const stored = accountsFile.accounts.map((account) => {
|
|
2775
|
+
let address;
|
|
2776
|
+
if (isWatchOnly(account)) {
|
|
2777
|
+
address = account.publicKey ? toSs58(account.publicKey) : undefined;
|
|
2778
|
+
} else if (account.secret !== undefined && isEnvSecret(account.secret)) {
|
|
2779
|
+
let pubKey = account.publicKey;
|
|
2780
|
+
if (!pubKey) {
|
|
2781
|
+
pubKey = tryDerivePublicKey(account.secret.env, account.derivationPath) ?? "";
|
|
2782
|
+
}
|
|
2783
|
+
address = pubKey ? toSs58(pubKey) : undefined;
|
|
2784
|
+
} else {
|
|
2785
|
+
address = toSs58(account.publicKey);
|
|
2786
|
+
}
|
|
2787
|
+
return {
|
|
2788
|
+
name: account.name,
|
|
2789
|
+
address,
|
|
2790
|
+
derivationPath: account.derivationPath || undefined,
|
|
2791
|
+
watchOnly: isWatchOnly(account),
|
|
2792
|
+
env: account.secret !== undefined && isEnvSecret(account.secret) ? account.secret.env : undefined
|
|
2793
|
+
};
|
|
2794
|
+
});
|
|
2795
|
+
console.log(formatJson({ dev, stored }));
|
|
2796
|
+
return;
|
|
2797
|
+
}
|
|
2514
2798
|
printHeading("Dev Accounts");
|
|
2515
2799
|
for (const name of DEV_NAMES) {
|
|
2516
2800
|
const display = name.charAt(0).toUpperCase() + name.slice(1);
|
|
2517
2801
|
const address = getDevAddress(name);
|
|
2518
2802
|
printItem(display, address);
|
|
2519
2803
|
}
|
|
2520
|
-
const accountsFile = await loadAccounts();
|
|
2521
2804
|
if (accountsFile.accounts.length > 0) {
|
|
2522
2805
|
printHeading("Stored Accounts");
|
|
2523
2806
|
for (const account of accountsFile.accounts) {
|
|
@@ -2547,7 +2830,7 @@ async function accountList() {
|
|
|
2547
2830
|
}
|
|
2548
2831
|
console.log();
|
|
2549
2832
|
}
|
|
2550
|
-
async function accountRemove(names) {
|
|
2833
|
+
async function accountRemove(names, opts = {}) {
|
|
2551
2834
|
if (names.length === 0) {
|
|
2552
2835
|
console.error(`At least one account name is required.
|
|
2553
2836
|
`);
|
|
@@ -2582,6 +2865,10 @@ async function accountRemove(names) {
|
|
|
2582
2865
|
accountsFile.accounts.splice(idx, 1);
|
|
2583
2866
|
}
|
|
2584
2867
|
await saveAccounts(accountsFile);
|
|
2868
|
+
if (isJsonOutput(opts)) {
|
|
2869
|
+
console.log(formatJson({ removed: names }));
|
|
2870
|
+
return;
|
|
2871
|
+
}
|
|
2585
2872
|
for (const name of names) {
|
|
2586
2873
|
console.log(`Account "${name}" removed.`);
|
|
2587
2874
|
}
|
|
@@ -2637,7 +2924,7 @@ async function accountInspect(input, opts) {
|
|
|
2637
2924
|
}
|
|
2638
2925
|
}
|
|
2639
2926
|
const ss58 = toSs58(publicKeyHex, prefix);
|
|
2640
|
-
if (opts
|
|
2927
|
+
if (isJsonOutput(opts)) {
|
|
2641
2928
|
const result = { publicKey: publicKeyHex, ss58, prefix };
|
|
2642
2929
|
if (name)
|
|
2643
2930
|
result.name = name;
|
|
@@ -2666,6 +2953,162 @@ async function accountInspect(input, opts) {
|
|
|
2666
2953
|
console.log();
|
|
2667
2954
|
}
|
|
2668
2955
|
}
|
|
2956
|
+
async function readStdin() {
|
|
2957
|
+
const chunks = [];
|
|
2958
|
+
for await (const chunk of process.stdin) {
|
|
2959
|
+
chunks.push(chunk);
|
|
2960
|
+
}
|
|
2961
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
2962
|
+
}
|
|
2963
|
+
var REDACTED = "<redacted>";
|
|
2964
|
+
async function accountExport(names, opts) {
|
|
2965
|
+
const accountsFile = await loadAccounts();
|
|
2966
|
+
if (opts.includeSecrets) {
|
|
2967
|
+
process.stderr.write(`${YELLOW}Warning: secrets are included in the export.${RESET}
|
|
2968
|
+
`);
|
|
2969
|
+
}
|
|
2970
|
+
let accounts = accountsFile.accounts;
|
|
2971
|
+
if (names.length > 0) {
|
|
2972
|
+
const filtered = [];
|
|
2973
|
+
for (const input of names) {
|
|
2974
|
+
const account = findAccount(accountsFile, input);
|
|
2975
|
+
if (!account) {
|
|
2976
|
+
throw new Error(`Account "${input}" not found.`);
|
|
2977
|
+
}
|
|
2978
|
+
filtered.push(account);
|
|
2979
|
+
}
|
|
2980
|
+
accounts = filtered;
|
|
2981
|
+
}
|
|
2982
|
+
if (opts.watchOnly) {
|
|
2983
|
+
accounts = accounts.filter((a) => isWatchOnly(a));
|
|
2984
|
+
}
|
|
2985
|
+
const exported = accounts.map((account) => {
|
|
2986
|
+
const entry = {
|
|
2987
|
+
name: account.name,
|
|
2988
|
+
publicKey: account.publicKey,
|
|
2989
|
+
derivationPath: account.derivationPath
|
|
2990
|
+
};
|
|
2991
|
+
if (isWatchOnly(account)) {} else if (account.secret !== undefined && isEnvSecret(account.secret)) {
|
|
2992
|
+
entry.secret = account.secret;
|
|
2993
|
+
} else if (opts.includeSecrets) {
|
|
2994
|
+
entry.secret = account.secret;
|
|
2995
|
+
} else {
|
|
2996
|
+
entry.secret = REDACTED;
|
|
2997
|
+
}
|
|
2998
|
+
if (account.bandersnatch && Object.keys(account.bandersnatch).length > 0) {
|
|
2999
|
+
entry.bandersnatch = account.bandersnatch;
|
|
3000
|
+
}
|
|
3001
|
+
return entry;
|
|
3002
|
+
});
|
|
3003
|
+
const exportData = { accounts: exported };
|
|
3004
|
+
const json = `${JSON.stringify(exportData, null, 2)}
|
|
3005
|
+
`;
|
|
3006
|
+
if (opts.file) {
|
|
3007
|
+
await writeFile3(opts.file, json);
|
|
3008
|
+
if (isJsonOutput(opts)) {
|
|
3009
|
+
console.log(formatJson({ action: "exported", file: opts.file, count: exported.length }));
|
|
3010
|
+
} else {
|
|
3011
|
+
console.log(`Exported ${exported.length} account(s) to ${opts.file}`);
|
|
3012
|
+
}
|
|
3013
|
+
} else {
|
|
3014
|
+
process.stdout.write(json);
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
async function accountBatchImport(opts) {
|
|
3018
|
+
let raw;
|
|
3019
|
+
if (!opts.file || opts.file === "-") {
|
|
3020
|
+
raw = await readStdin();
|
|
3021
|
+
} else {
|
|
3022
|
+
raw = await readFile3(opts.file, "utf-8");
|
|
3023
|
+
}
|
|
3024
|
+
let importData;
|
|
3025
|
+
try {
|
|
3026
|
+
importData = JSON.parse(raw);
|
|
3027
|
+
} catch {
|
|
3028
|
+
throw new Error("Invalid JSON input.");
|
|
3029
|
+
}
|
|
3030
|
+
if (!Array.isArray(importData.accounts)) {
|
|
3031
|
+
throw new Error('Invalid import format: missing "accounts" array.');
|
|
3032
|
+
}
|
|
3033
|
+
const accountsFile = await loadAccounts();
|
|
3034
|
+
const added = [];
|
|
3035
|
+
const skipped = [];
|
|
3036
|
+
const overwritten = [];
|
|
3037
|
+
for (const entry of importData.accounts) {
|
|
3038
|
+
if (!entry.name) {
|
|
3039
|
+
process.stderr.write(`${YELLOW}Skipped entry with missing name.${RESET}
|
|
3040
|
+
`);
|
|
3041
|
+
continue;
|
|
3042
|
+
}
|
|
3043
|
+
if (isDevAccount(entry.name)) {
|
|
3044
|
+
process.stderr.write(`${YELLOW}Skipped "${entry.name}": built-in dev account.${RESET}
|
|
3045
|
+
`);
|
|
3046
|
+
skipped.push(entry.name);
|
|
3047
|
+
continue;
|
|
3048
|
+
}
|
|
3049
|
+
const existing = findAccount(accountsFile, entry.name);
|
|
3050
|
+
if (existing && !opts.overwrite) {
|
|
3051
|
+
skipped.push(entry.name);
|
|
3052
|
+
process.stderr.write(`${YELLOW}Skipped "${entry.name}": already exists (use --overwrite to replace)${RESET}
|
|
3053
|
+
`);
|
|
3054
|
+
continue;
|
|
3055
|
+
}
|
|
3056
|
+
const stored = {
|
|
3057
|
+
name: entry.name,
|
|
3058
|
+
publicKey: entry.publicKey || "",
|
|
3059
|
+
derivationPath: entry.derivationPath || ""
|
|
3060
|
+
};
|
|
3061
|
+
if (entry.secret === undefined || entry.secret === REDACTED) {} else if (typeof entry.secret === "object" && "env" in entry.secret) {
|
|
3062
|
+
stored.secret = entry.secret;
|
|
3063
|
+
if (!stored.publicKey) {
|
|
3064
|
+
stored.publicKey = tryDerivePublicKey(entry.secret.env, stored.derivationPath) ?? "";
|
|
3065
|
+
}
|
|
3066
|
+
} else if (typeof entry.secret === "string") {
|
|
3067
|
+
stored.secret = entry.secret;
|
|
3068
|
+
try {
|
|
3069
|
+
const { publicKey } = importAccount(entry.secret, stored.derivationPath);
|
|
3070
|
+
stored.publicKey = publicKeyToHex(publicKey);
|
|
3071
|
+
} catch {
|
|
3072
|
+
process.stderr.write(`${YELLOW}Warning: "${entry.name}" has an invalid secret, importing as watch-only.${RESET}
|
|
3073
|
+
`);
|
|
3074
|
+
delete stored.secret;
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
if (entry.bandersnatch && Object.keys(entry.bandersnatch).length > 0) {
|
|
3078
|
+
stored.bandersnatch = entry.bandersnatch;
|
|
3079
|
+
}
|
|
3080
|
+
if (existing) {
|
|
3081
|
+
const idx = accountsFile.accounts.findIndex((a) => a.name.toLowerCase() === entry.name.toLowerCase());
|
|
3082
|
+
accountsFile.accounts[idx] = stored;
|
|
3083
|
+
overwritten.push(entry.name);
|
|
3084
|
+
} else {
|
|
3085
|
+
accountsFile.accounts.push(stored);
|
|
3086
|
+
added.push(entry.name);
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
if (!opts.dryRun) {
|
|
3090
|
+
await saveAccounts(accountsFile);
|
|
3091
|
+
}
|
|
3092
|
+
if (isJsonOutput(opts)) {
|
|
3093
|
+
console.log(formatJson({
|
|
3094
|
+
action: opts.dryRun ? "dry-run" : "imported",
|
|
3095
|
+
added,
|
|
3096
|
+
overwritten,
|
|
3097
|
+
skipped
|
|
3098
|
+
}));
|
|
3099
|
+
return;
|
|
3100
|
+
}
|
|
3101
|
+
const prefix = opts.dryRun ? "(dry run) " : "";
|
|
3102
|
+
if (added.length > 0)
|
|
3103
|
+
console.log(`${prefix}Added: ${added.join(", ")}`);
|
|
3104
|
+
if (overwritten.length > 0)
|
|
3105
|
+
console.log(`${prefix}Overwritten: ${overwritten.join(", ")}`);
|
|
3106
|
+
if (skipped.length > 0)
|
|
3107
|
+
console.log(`${prefix}Skipped: ${skipped.join(", ")}`);
|
|
3108
|
+
if (added.length === 0 && overwritten.length === 0) {
|
|
3109
|
+
console.log(`${prefix}No accounts imported.`);
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
2669
3112
|
|
|
2670
3113
|
// src/commands/apis.ts
|
|
2671
3114
|
init_store();
|
|
@@ -2680,6 +3123,13 @@ async function handleApis2(target, args, opts) {
|
|
|
2680
3123
|
const { name: chainName2, chain: chainConfig2 } = resolveChain(config2, opts.chain);
|
|
2681
3124
|
const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
|
|
2682
3125
|
const apis = listRuntimeApis(meta);
|
|
3126
|
+
if (isJsonOutput(opts)) {
|
|
3127
|
+
console.log(formatJson({
|
|
3128
|
+
chain: chainName2,
|
|
3129
|
+
apis: apis.map((a) => ({ name: a.name, methods: a.methods.length }))
|
|
3130
|
+
}));
|
|
3131
|
+
return;
|
|
3132
|
+
}
|
|
2683
3133
|
printHeading(`Runtime APIs on ${chainName2} (${apis.length})`);
|
|
2684
3134
|
for (const api of apis) {
|
|
2685
3135
|
printItem(api.name, `${api.methods.length} methods`);
|
|
@@ -2702,7 +3152,24 @@ async function handleApis2(target, args, opts) {
|
|
|
2702
3152
|
const meta = await loadMeta(chainName, chainConfig, opts.rpc);
|
|
2703
3153
|
const api = resolveRuntimeApi2(meta, apiName);
|
|
2704
3154
|
if (api.methods.length === 0) {
|
|
2705
|
-
|
|
3155
|
+
if (isJsonOutput(opts)) {
|
|
3156
|
+
console.log(formatJson({ chain: chainName, api: api.name, methods: [] }));
|
|
3157
|
+
} else {
|
|
3158
|
+
console.log(`No methods in ${api.name}.`);
|
|
3159
|
+
}
|
|
3160
|
+
return;
|
|
3161
|
+
}
|
|
3162
|
+
if (isJsonOutput(opts)) {
|
|
3163
|
+
console.log(formatJson({
|
|
3164
|
+
chain: chainName,
|
|
3165
|
+
api: api.name,
|
|
3166
|
+
methods: api.methods.map((m) => ({
|
|
3167
|
+
name: m.name,
|
|
3168
|
+
args: describeRuntimeApiMethodArgs(meta, m),
|
|
3169
|
+
returns: describeType(meta.lookup, m.output),
|
|
3170
|
+
docs: firstSentence(m.docs)
|
|
3171
|
+
}))
|
|
3172
|
+
}));
|
|
2706
3173
|
return;
|
|
2707
3174
|
}
|
|
2708
3175
|
printHeading(`${api.name} Methods`);
|
|
@@ -2733,7 +3200,7 @@ async function handleApis2(target, args, opts) {
|
|
|
2733
3200
|
const parsedArgs = await parseRuntimeApiArgs2(meta, method, effectiveArgs);
|
|
2734
3201
|
const unsafeApi = clientHandle.client.getUnsafeApi();
|
|
2735
3202
|
const result = await unsafeApi.apis[api.name][method.name](...parsedArgs);
|
|
2736
|
-
const format = opts.output ?? "pretty";
|
|
3203
|
+
const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
|
|
2737
3204
|
printResult(result, format);
|
|
2738
3205
|
} finally {
|
|
2739
3206
|
clientHandle.destroy();
|
|
@@ -2766,46 +3233,54 @@ init_types();
|
|
|
2766
3233
|
init_client();
|
|
2767
3234
|
init_metadata();
|
|
2768
3235
|
init_output();
|
|
3236
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
2769
3237
|
var CHAIN_HELP = `
|
|
2770
3238
|
${BOLD}Usage:${RESET}
|
|
2771
3239
|
$ dot chain add <name> --rpc <url> Add a chain via WebSocket RPC
|
|
2772
|
-
$ dot chain add <name> --light-client Add a chain via Smoldot light client
|
|
2773
3240
|
$ dot chain remove <name> Remove a chain
|
|
2774
|
-
$ dot chain update
|
|
3241
|
+
$ dot chain update <name> Re-fetch metadata for a chain
|
|
2775
3242
|
$ dot chain update --all Re-fetch metadata for all configured chains
|
|
2776
3243
|
$ dot chain list List configured chains
|
|
2777
|
-
$ dot chain
|
|
3244
|
+
$ dot chain export [names...] Export chain configuration to stdout
|
|
3245
|
+
$ dot chain import <file> Import chain configuration from a file
|
|
2778
3246
|
|
|
2779
3247
|
${BOLD}Examples:${RESET}
|
|
2780
3248
|
$ dot chain add kusama --rpc wss://kusama-rpc.polkadot.io
|
|
2781
3249
|
$ dot chain add kusama --rpc wss://kusama-rpc.polkadot.io --rpc wss://kusama-rpc.dwellir.com
|
|
2782
|
-
$ dot chain add
|
|
2783
|
-
$ dot chain
|
|
3250
|
+
$ dot chain add my-para --rpc wss://rpc.example.com --relay polkadot
|
|
3251
|
+
$ dot chain add my-para --rpc wss://rpc.example.com --relay polkadot --parachain-id 2000
|
|
2784
3252
|
$ dot chain list
|
|
2785
|
-
$ dot chain update
|
|
2786
3253
|
$ dot chain update kusama
|
|
2787
3254
|
$ dot chain update --all
|
|
2788
3255
|
$ dot chain remove kusama
|
|
3256
|
+
$ dot chain export
|
|
3257
|
+
$ dot chain export my-relay my-para --file my-chains.json
|
|
3258
|
+
$ dot chain export --all
|
|
3259
|
+
$ dot chain import my-chains.json
|
|
3260
|
+
$ dot chain import my-chains.json --dry-run
|
|
3261
|
+
$ dot chain import my-chains.json --overwrite
|
|
2789
3262
|
`.trimStart();
|
|
2790
3263
|
function registerChainCommands(cli) {
|
|
2791
|
-
cli.command("chain [action] [
|
|
3264
|
+
cli.command("chain [action] [...names]", "Manage chains (add, remove, update, list, export, import)").alias("chains").option("--all", "Update/export all configured chains").option("--relay <name>", "Parent relay chain for this parachain").option("--parachain-id <id>", "Parachain ID (auto-detected if omitted with --relay)").option("--file <path>", "Output/input file for export/import").option("--overwrite", "Overwrite existing chains on import").option("--dry-run", "Preview import without applying changes").action(async (action, names, opts) => {
|
|
2792
3265
|
if (!action) {
|
|
2793
3266
|
if (process.argv[2] === "chains")
|
|
2794
|
-
return chainList();
|
|
3267
|
+
return chainList(opts);
|
|
2795
3268
|
console.log(CHAIN_HELP);
|
|
2796
3269
|
return;
|
|
2797
3270
|
}
|
|
2798
3271
|
switch (action) {
|
|
2799
3272
|
case "add":
|
|
2800
|
-
return chainAdd(
|
|
3273
|
+
return chainAdd(names[0], opts);
|
|
2801
3274
|
case "remove":
|
|
2802
|
-
return chainRemove(
|
|
3275
|
+
return chainRemove(names[0], opts);
|
|
2803
3276
|
case "list":
|
|
2804
|
-
return chainList();
|
|
3277
|
+
return chainList(opts);
|
|
2805
3278
|
case "update":
|
|
2806
|
-
return chainUpdate(
|
|
2807
|
-
case "
|
|
2808
|
-
return
|
|
3279
|
+
return chainUpdate(names[0], opts);
|
|
3280
|
+
case "export":
|
|
3281
|
+
return chainExport(names, opts);
|
|
3282
|
+
case "import":
|
|
3283
|
+
return chainImport(names[0], opts);
|
|
2809
3284
|
default:
|
|
2810
3285
|
console.error(`Unknown action "${action}".
|
|
2811
3286
|
`);
|
|
@@ -2819,34 +3294,70 @@ async function chainAdd(name, opts) {
|
|
|
2819
3294
|
console.error(`Chain name is required.
|
|
2820
3295
|
`);
|
|
2821
3296
|
console.error("Usage: dot chain add <name> --rpc <url>");
|
|
2822
|
-
console.error(" dot chain add <name> --light-client");
|
|
2823
3297
|
process.exit(1);
|
|
2824
3298
|
}
|
|
2825
|
-
if (!opts.rpc
|
|
2826
|
-
console.error(`Must provide
|
|
3299
|
+
if (!opts.rpc) {
|
|
3300
|
+
console.error(`Must provide --rpc <url>.
|
|
2827
3301
|
`);
|
|
2828
3302
|
console.error("Usage: dot chain add <name> --rpc <url>");
|
|
2829
|
-
console.error(" dot chain add <name> --light-client");
|
|
2830
3303
|
process.exit(1);
|
|
2831
3304
|
}
|
|
2832
|
-
const
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
3305
|
+
const parachainIdRaw = opts.parachainId != null ? Number(opts.parachainId) : undefined;
|
|
3306
|
+
if (parachainIdRaw != null && !opts.relay) {
|
|
3307
|
+
console.error(`Cannot set --parachain-id without --relay.
|
|
3308
|
+
`);
|
|
3309
|
+
console.error("Usage: dot chain add <name> --rpc <url> --relay <relay> --parachain-id <id>");
|
|
3310
|
+
process.exit(1);
|
|
3311
|
+
}
|
|
3312
|
+
const chainConfig = { rpc: opts.rpc };
|
|
3313
|
+
process.stderr.write(`Connecting to ${name}...
|
|
3314
|
+
`);
|
|
2837
3315
|
const clientHandle = await createChainClient(name, chainConfig, opts.rpc);
|
|
2838
3316
|
try {
|
|
2839
|
-
|
|
3317
|
+
process.stderr.write(`Fetching metadata...
|
|
3318
|
+
`);
|
|
2840
3319
|
await fetchMetadataFromChain(clientHandle, name);
|
|
3320
|
+
if (opts.relay) {
|
|
3321
|
+
const config2 = await loadConfig();
|
|
3322
|
+
const relayResolved = findChainName(config2, opts.relay);
|
|
3323
|
+
if (!relayResolved) {
|
|
3324
|
+
throw new Error(`Relay chain "${opts.relay}" not found. Add it first with: dot chain add ${opts.relay} --rpc <url>`);
|
|
3325
|
+
}
|
|
3326
|
+
chainConfig.relay = relayResolved;
|
|
3327
|
+
if (parachainIdRaw != null) {
|
|
3328
|
+
chainConfig.parachainId = parachainIdRaw;
|
|
3329
|
+
} else {
|
|
3330
|
+
process.stderr.write(`Detecting parachain ID...
|
|
3331
|
+
`);
|
|
3332
|
+
const detected = await getParachainId(clientHandle);
|
|
3333
|
+
if (detected != null) {
|
|
3334
|
+
chainConfig.parachainId = detected;
|
|
3335
|
+
process.stderr.write(`Detected parachain ID: ${detected}
|
|
3336
|
+
`);
|
|
3337
|
+
} else {
|
|
3338
|
+
process.stderr.write(`Could not detect parachain ID. Use --parachain-id to set it manually.
|
|
3339
|
+
`);
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
2841
3343
|
const config = await loadConfig();
|
|
2842
3344
|
config.chains[name] = chainConfig;
|
|
2843
3345
|
await saveConfig(config);
|
|
2844
|
-
|
|
3346
|
+
const result = { action: "added", chain: name };
|
|
3347
|
+
if (chainConfig.relay)
|
|
3348
|
+
result.relay = chainConfig.relay;
|
|
3349
|
+
if (chainConfig.parachainId != null)
|
|
3350
|
+
result.parachainId = chainConfig.parachainId;
|
|
3351
|
+
if (isJsonOutput(opts)) {
|
|
3352
|
+
console.log(formatJson(result));
|
|
3353
|
+
} else {
|
|
3354
|
+
console.log(`Chain "${name}" added successfully.`);
|
|
3355
|
+
}
|
|
2845
3356
|
} finally {
|
|
2846
3357
|
clientHandle.destroy();
|
|
2847
3358
|
}
|
|
2848
3359
|
}
|
|
2849
|
-
async function chainRemove(name) {
|
|
3360
|
+
async function chainRemove(name, opts = {}) {
|
|
2850
3361
|
if (!name) {
|
|
2851
3362
|
console.error("Usage: dot chain remove <name>");
|
|
2852
3363
|
process.exit(1);
|
|
@@ -2859,32 +3370,77 @@ async function chainRemove(name) {
|
|
|
2859
3370
|
if (BUILTIN_CHAIN_NAMES.has(resolved)) {
|
|
2860
3371
|
throw new Error(`Cannot remove the built-in "${resolved}" chain.`);
|
|
2861
3372
|
}
|
|
2862
|
-
|
|
2863
|
-
if (
|
|
2864
|
-
|
|
2865
|
-
console.log(`Default chain reset to "polkadot".`);
|
|
3373
|
+
const orphans = Object.entries(config.chains).filter(([, c]) => c.relay === resolved).map(([n]) => n);
|
|
3374
|
+
if (orphans.length > 0) {
|
|
3375
|
+
console.error(`Warning: ${orphans.length} chain(s) reference "${resolved}" as their relay: ${orphans.join(", ")}`);
|
|
2866
3376
|
}
|
|
3377
|
+
delete config.chains[resolved];
|
|
2867
3378
|
await saveConfig(config);
|
|
2868
3379
|
await removeChainData(resolved);
|
|
2869
|
-
|
|
3380
|
+
if (isJsonOutput(opts)) {
|
|
3381
|
+
console.log(formatJson({ action: "removed", chain: resolved }));
|
|
3382
|
+
} else {
|
|
3383
|
+
console.log(`Chain "${resolved}" removed.`);
|
|
3384
|
+
}
|
|
2870
3385
|
}
|
|
2871
|
-
async function chainList() {
|
|
3386
|
+
async function chainList(opts = {}) {
|
|
2872
3387
|
const config = await loadConfig();
|
|
3388
|
+
if (isJsonOutput(opts)) {
|
|
3389
|
+
const chains = Object.entries(config.chains).map(([name, chainConfig]) => ({
|
|
3390
|
+
name,
|
|
3391
|
+
rpc: Array.isArray(chainConfig.rpc) ? chainConfig.rpc : [chainConfig.rpc],
|
|
3392
|
+
...chainConfig.relay && { relay: chainConfig.relay },
|
|
3393
|
+
...chainConfig.parachainId != null && { parachainId: chainConfig.parachainId }
|
|
3394
|
+
}));
|
|
3395
|
+
console.log(formatJson({ chains }));
|
|
3396
|
+
return;
|
|
3397
|
+
}
|
|
2873
3398
|
printHeading("Configured Chains");
|
|
3399
|
+
const parachainsByRelay = new Map;
|
|
3400
|
+
const standalone = [];
|
|
2874
3401
|
for (const [name, chainConfig] of Object.entries(config.chains)) {
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
} else {
|
|
2880
|
-
const rpcs = Array.isArray(chainConfig.rpc) ? chainConfig.rpc : [chainConfig.rpc];
|
|
2881
|
-
console.log(` ${CYAN}${name}${RESET}${marker} ${DIM}${rpcs[0]}${RESET}`);
|
|
2882
|
-
for (let i = 1;i < rpcs.length; i++) {
|
|
2883
|
-
console.log(` ${DIM}${rpcs[i]}${RESET}`);
|
|
2884
|
-
}
|
|
3402
|
+
if (chainConfig.relay) {
|
|
3403
|
+
const paras = parachainsByRelay.get(chainConfig.relay) ?? [];
|
|
3404
|
+
paras.push([name, chainConfig]);
|
|
3405
|
+
parachainsByRelay.set(chainConfig.relay, paras);
|
|
2885
3406
|
}
|
|
2886
3407
|
}
|
|
2887
|
-
|
|
3408
|
+
const relayNames = new Set(parachainsByRelay.keys());
|
|
3409
|
+
for (const [name, chainConfig] of Object.entries(config.chains)) {
|
|
3410
|
+
if (relayNames.has(name))
|
|
3411
|
+
continue;
|
|
3412
|
+
if (chainConfig.relay)
|
|
3413
|
+
continue;
|
|
3414
|
+
standalone.push([name, chainConfig]);
|
|
3415
|
+
}
|
|
3416
|
+
for (const relayName of relayNames) {
|
|
3417
|
+
const relayConfig = config.chains[relayName];
|
|
3418
|
+
if (relayConfig) {
|
|
3419
|
+
printChainLine(" ", relayName, relayConfig);
|
|
3420
|
+
}
|
|
3421
|
+
const paras = parachainsByRelay.get(relayName) ?? [];
|
|
3422
|
+
for (let i = 0;i < paras.length; i++) {
|
|
3423
|
+
const [name, chainConfig] = paras[i];
|
|
3424
|
+
const isLast = i === paras.length - 1;
|
|
3425
|
+
const prefix = isLast ? " └─ " : " ├─ ";
|
|
3426
|
+
const idSuffix = chainConfig.parachainId != null ? ` ${DIM}[${chainConfig.parachainId}]${RESET}` : "";
|
|
3427
|
+
printChainLine(prefix, name, chainConfig, idSuffix);
|
|
3428
|
+
}
|
|
3429
|
+
console.log();
|
|
3430
|
+
}
|
|
3431
|
+
for (const [name, chainConfig] of standalone) {
|
|
3432
|
+
printChainLine(" ", name, chainConfig);
|
|
3433
|
+
}
|
|
3434
|
+
if (standalone.length > 0)
|
|
3435
|
+
console.log();
|
|
3436
|
+
}
|
|
3437
|
+
function printChainLine(prefix, name, chainConfig, suffix = "") {
|
|
3438
|
+
const rpcs = Array.isArray(chainConfig.rpc) ? chainConfig.rpc : [chainConfig.rpc];
|
|
3439
|
+
console.log(`${prefix}${CYAN}${name}${RESET}${suffix} ${DIM}${rpcs[0]}${RESET}`);
|
|
3440
|
+
const indent = prefix.replace(/[^\s]/g, " ");
|
|
3441
|
+
for (let i = 1;i < rpcs.length; i++) {
|
|
3442
|
+
console.log(`${indent} ${DIM}${rpcs[i]}${RESET}`);
|
|
3443
|
+
}
|
|
2888
3444
|
}
|
|
2889
3445
|
async function chainUpdate(name, opts) {
|
|
2890
3446
|
const config = await loadConfig();
|
|
@@ -2892,20 +3448,31 @@ async function chainUpdate(name, opts) {
|
|
|
2892
3448
|
await chainUpdateAll(config);
|
|
2893
3449
|
return;
|
|
2894
3450
|
}
|
|
3451
|
+
if (!name) {
|
|
3452
|
+
console.error("Usage: dot chain update <name> | --all");
|
|
3453
|
+
process.exit(1);
|
|
3454
|
+
}
|
|
2895
3455
|
const { name: chainName, chain: chainConfig } = resolveChain(config, name);
|
|
2896
|
-
|
|
3456
|
+
process.stderr.write(`Connecting to ${chainName}...
|
|
3457
|
+
`);
|
|
2897
3458
|
const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
2898
3459
|
try {
|
|
2899
|
-
|
|
3460
|
+
process.stderr.write(`Fetching metadata...
|
|
3461
|
+
`);
|
|
2900
3462
|
await fetchMetadataFromChain(clientHandle, chainName);
|
|
2901
|
-
|
|
3463
|
+
if (isJsonOutput(opts)) {
|
|
3464
|
+
console.log(formatJson({ action: "updated", chain: chainName }));
|
|
3465
|
+
} else {
|
|
3466
|
+
console.log(`Metadata for "${chainName}" updated.`);
|
|
3467
|
+
}
|
|
2902
3468
|
} finally {
|
|
2903
3469
|
clientHandle.destroy();
|
|
2904
3470
|
}
|
|
2905
3471
|
}
|
|
2906
3472
|
async function chainUpdateAll(config) {
|
|
2907
3473
|
const chainNames = Object.keys(config.chains).sort();
|
|
2908
|
-
|
|
3474
|
+
process.stderr.write(`Updating metadata for ${chainNames.length} chains...
|
|
3475
|
+
|
|
2909
3476
|
`);
|
|
2910
3477
|
const results = await Promise.allSettled(chainNames.map(async (chainName) => {
|
|
2911
3478
|
const chainConfig = config.chains[chainName];
|
|
@@ -2915,37 +3482,150 @@ async function chainUpdateAll(config) {
|
|
|
2915
3482
|
} finally {
|
|
2916
3483
|
clientHandle.destroy();
|
|
2917
3484
|
}
|
|
2918
|
-
}));
|
|
2919
|
-
for (let i = 0;i < chainNames.length; i++) {
|
|
2920
|
-
const result = results[i];
|
|
2921
|
-
const name = chainNames[i];
|
|
2922
|
-
if (result.status === "fulfilled") {
|
|
2923
|
-
console.log(` ${GREEN}${CHECK_MARK}${RESET} ${name}`);
|
|
3485
|
+
}));
|
|
3486
|
+
for (let i = 0;i < chainNames.length; i++) {
|
|
3487
|
+
const result = results[i];
|
|
3488
|
+
const name = chainNames[i];
|
|
3489
|
+
if (result.status === "fulfilled") {
|
|
3490
|
+
console.log(` ${GREEN}${CHECK_MARK}${RESET} ${name}`);
|
|
3491
|
+
} else {
|
|
3492
|
+
console.log(` ${RED}✗${RESET} ${name}${DIM} — ${result.reason?.message ?? "unknown error"}${RESET}`);
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
const failed = results.filter((r) => r.status === "rejected").length;
|
|
3496
|
+
if (failed > 0) {
|
|
3497
|
+
console.error(`
|
|
3498
|
+
${failed} of ${chainNames.length} chains failed to update.`);
|
|
3499
|
+
process.exit(1);
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
function isBuiltinModified(name, config) {
|
|
3503
|
+
const defaultRpc = DEFAULT_CONFIG.chains[name]?.rpc;
|
|
3504
|
+
const currentRpc = config.chains[name]?.rpc;
|
|
3505
|
+
if (!defaultRpc || !currentRpc)
|
|
3506
|
+
return false;
|
|
3507
|
+
return JSON.stringify(currentRpc) !== JSON.stringify(defaultRpc);
|
|
3508
|
+
}
|
|
3509
|
+
async function readStdin2() {
|
|
3510
|
+
const chunks = [];
|
|
3511
|
+
for await (const chunk of process.stdin) {
|
|
3512
|
+
chunks.push(chunk);
|
|
3513
|
+
}
|
|
3514
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
3515
|
+
}
|
|
3516
|
+
async function chainExport(names, opts) {
|
|
3517
|
+
const config = await loadConfig();
|
|
3518
|
+
const exportChains = {};
|
|
3519
|
+
if (names.length > 0) {
|
|
3520
|
+
for (const input of names) {
|
|
3521
|
+
const resolved = findChainName(config, input);
|
|
3522
|
+
if (!resolved) {
|
|
3523
|
+
throw new Error(`Chain "${input}" not found.`);
|
|
3524
|
+
}
|
|
3525
|
+
exportChains[resolved] = config.chains[resolved];
|
|
3526
|
+
}
|
|
3527
|
+
} else if (opts.all) {
|
|
3528
|
+
Object.assign(exportChains, config.chains);
|
|
3529
|
+
} else {
|
|
3530
|
+
for (const [name, chainConfig] of Object.entries(config.chains)) {
|
|
3531
|
+
if (!BUILTIN_CHAIN_NAMES.has(name) || isBuiltinModified(name, config)) {
|
|
3532
|
+
exportChains[name] = chainConfig;
|
|
3533
|
+
}
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
const exportData = {
|
|
3537
|
+
chains: exportChains
|
|
3538
|
+
};
|
|
3539
|
+
const json = `${JSON.stringify(exportData, null, 2)}
|
|
3540
|
+
`;
|
|
3541
|
+
if (opts.file) {
|
|
3542
|
+
await writeFile4(opts.file, json);
|
|
3543
|
+
if (isJsonOutput(opts)) {
|
|
3544
|
+
console.log(formatJson({
|
|
3545
|
+
action: "exported",
|
|
3546
|
+
file: opts.file,
|
|
3547
|
+
count: Object.keys(exportChains).length
|
|
3548
|
+
}));
|
|
3549
|
+
} else {
|
|
3550
|
+
console.log(`Exported ${Object.keys(exportChains).length} chain(s) to ${opts.file}`);
|
|
3551
|
+
}
|
|
3552
|
+
} else {
|
|
3553
|
+
process.stdout.write(json);
|
|
3554
|
+
}
|
|
3555
|
+
}
|
|
3556
|
+
async function chainImport(filePath, opts) {
|
|
3557
|
+
const inputPath = filePath ?? opts.file;
|
|
3558
|
+
let raw;
|
|
3559
|
+
if (inputPath && inputPath !== "-") {
|
|
3560
|
+
raw = await readFile4(inputPath, "utf-8");
|
|
3561
|
+
} else {
|
|
3562
|
+
raw = await readStdin2();
|
|
3563
|
+
}
|
|
3564
|
+
let importData;
|
|
3565
|
+
try {
|
|
3566
|
+
importData = JSON.parse(raw);
|
|
3567
|
+
} catch {
|
|
3568
|
+
throw new Error("Invalid JSON input.");
|
|
3569
|
+
}
|
|
3570
|
+
if (!importData.chains || typeof importData.chains !== "object") {
|
|
3571
|
+
throw new Error('Invalid import format: missing "chains" object.');
|
|
3572
|
+
}
|
|
3573
|
+
const config = await loadConfig();
|
|
3574
|
+
const added = [];
|
|
3575
|
+
const skipped = [];
|
|
3576
|
+
const overwritten = [];
|
|
3577
|
+
const warnings = [];
|
|
3578
|
+
for (const [name, chainConfig] of Object.entries(importData.chains)) {
|
|
3579
|
+
const existing = findChainName(config, name);
|
|
3580
|
+
if (existing && !opts.overwrite) {
|
|
3581
|
+
skipped.push(existing);
|
|
3582
|
+
process.stderr.write(`${YELLOW}Skipped "${existing}": already exists (use --overwrite to replace)${RESET}
|
|
3583
|
+
`);
|
|
3584
|
+
continue;
|
|
3585
|
+
}
|
|
3586
|
+
if (chainConfig.relay) {
|
|
3587
|
+
const relayInImport = Object.keys(importData.chains).some((n) => n.toLowerCase() === chainConfig.relay.toLowerCase());
|
|
3588
|
+
const relayInConfig = findChainName(config, chainConfig.relay);
|
|
3589
|
+
if (!relayInImport && !relayInConfig) {
|
|
3590
|
+
warnings.push(`Chain "${name}" references relay "${chainConfig.relay}" which does not exist.`);
|
|
3591
|
+
process.stderr.write(`${YELLOW}Warning: "${name}" references relay "${chainConfig.relay}" which does not exist.${RESET}
|
|
3592
|
+
`);
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
if (existing) {
|
|
3596
|
+
overwritten.push(existing);
|
|
3597
|
+
config.chains[existing] = chainConfig;
|
|
2924
3598
|
} else {
|
|
2925
|
-
|
|
3599
|
+
added.push(name);
|
|
3600
|
+
config.chains[name] = chainConfig;
|
|
2926
3601
|
}
|
|
2927
3602
|
}
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
console.error(`
|
|
2931
|
-
${failed} of ${chainNames.length} chains failed to update.`);
|
|
2932
|
-
process.exit(1);
|
|
3603
|
+
if (!opts.dryRun) {
|
|
3604
|
+
await saveConfig(config);
|
|
2933
3605
|
}
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
3606
|
+
if (isJsonOutput(opts)) {
|
|
3607
|
+
console.log(formatJson({
|
|
3608
|
+
action: opts.dryRun ? "dry-run" : "imported",
|
|
3609
|
+
added,
|
|
3610
|
+
overwritten,
|
|
3611
|
+
skipped,
|
|
3612
|
+
warnings
|
|
3613
|
+
}));
|
|
3614
|
+
return;
|
|
2939
3615
|
}
|
|
2940
|
-
const
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
3616
|
+
const prefix = opts.dryRun ? "(dry run) " : "";
|
|
3617
|
+
if (added.length > 0)
|
|
3618
|
+
console.log(`${prefix}Added: ${added.join(", ")}`);
|
|
3619
|
+
if (overwritten.length > 0)
|
|
3620
|
+
console.log(`${prefix}Overwritten: ${overwritten.join(", ")}`);
|
|
3621
|
+
if (skipped.length > 0)
|
|
3622
|
+
console.log(`${prefix}Skipped: ${skipped.join(", ")}`);
|
|
3623
|
+
if (added.length === 0 && overwritten.length === 0) {
|
|
3624
|
+
console.log(`${prefix}No chains imported.`);
|
|
3625
|
+
} else if (!opts.dryRun) {
|
|
3626
|
+
console.error(`
|
|
3627
|
+
Run "dot chain update --all" to fetch metadata for imported chains.`);
|
|
2945
3628
|
}
|
|
2946
|
-
config.defaultChain = resolved;
|
|
2947
|
-
await saveConfig(config);
|
|
2948
|
-
console.log(`Default chain set to "${resolved}".`);
|
|
2949
3629
|
}
|
|
2950
3630
|
|
|
2951
3631
|
// src/commands/completions.ts
|
|
@@ -3045,6 +3725,13 @@ async function handleConst(target, opts) {
|
|
|
3045
3725
|
const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
|
|
3046
3726
|
const pallets = listPallets(meta);
|
|
3047
3727
|
const withConsts = pallets.filter((p) => p.constants.length > 0);
|
|
3728
|
+
if (isJsonOutput(opts)) {
|
|
3729
|
+
console.log(formatJson({
|
|
3730
|
+
chain: chainName2,
|
|
3731
|
+
pallets: withConsts.map((p) => ({ name: p.name, constants: p.constants.length }))
|
|
3732
|
+
}));
|
|
3733
|
+
return;
|
|
3734
|
+
}
|
|
3048
3735
|
printHeading(`Pallets with constants on ${chainName2} (${withConsts.length})`);
|
|
3049
3736
|
for (const p of withConsts) {
|
|
3050
3737
|
printItem(p.name, `${p.constants.length} constants`);
|
|
@@ -3065,7 +3752,23 @@ async function handleConst(target, opts) {
|
|
|
3065
3752
|
throw new Error(suggestMessage("pallet", pallet, palletNames));
|
|
3066
3753
|
}
|
|
3067
3754
|
if (palletInfo.constants.length === 0) {
|
|
3068
|
-
|
|
3755
|
+
if (isJsonOutput(opts)) {
|
|
3756
|
+
console.log(formatJson({ chain: chainName, pallet: palletInfo.name, constants: [] }));
|
|
3757
|
+
} else {
|
|
3758
|
+
console.log(`No constants in ${palletInfo.name}.`);
|
|
3759
|
+
}
|
|
3760
|
+
return;
|
|
3761
|
+
}
|
|
3762
|
+
if (isJsonOutput(opts)) {
|
|
3763
|
+
console.log(formatJson({
|
|
3764
|
+
chain: chainName,
|
|
3765
|
+
pallet: palletInfo.name,
|
|
3766
|
+
constants: palletInfo.constants.map((c) => ({
|
|
3767
|
+
name: c.name,
|
|
3768
|
+
type: describeType(meta.lookup, c.typeId),
|
|
3769
|
+
docs: firstSentence(c.docs)
|
|
3770
|
+
}))
|
|
3771
|
+
}));
|
|
3069
3772
|
return;
|
|
3070
3773
|
}
|
|
3071
3774
|
printHeading(`${palletInfo.name} Constants`);
|
|
@@ -3094,9 +3797,9 @@ async function handleConst(target, opts) {
|
|
|
3094
3797
|
throw new Error(suggestMessage(`constant in ${palletInfo.name}`, item, constNames));
|
|
3095
3798
|
}
|
|
3096
3799
|
const unsafeApi = clientHandle.client.getUnsafeApi();
|
|
3097
|
-
const
|
|
3098
|
-
const result =
|
|
3099
|
-
const format = opts.output ?? "pretty";
|
|
3800
|
+
const staticApis = await unsafeApi.getStaticApis();
|
|
3801
|
+
const result = staticApis.constants[palletInfo.name][constantItem.name];
|
|
3802
|
+
const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
|
|
3100
3803
|
printResult(result, format);
|
|
3101
3804
|
} finally {
|
|
3102
3805
|
clientHandle.destroy();
|
|
@@ -3109,11 +3812,23 @@ init_client();
|
|
|
3109
3812
|
init_metadata();
|
|
3110
3813
|
init_output();
|
|
3111
3814
|
async function loadMeta2(chainName, chainConfig, rpcOverride) {
|
|
3815
|
+
if (rpcOverride) {
|
|
3816
|
+
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
3817
|
+
`);
|
|
3818
|
+
const clientHandle = await createChainClient(chainName, chainConfig, rpcOverride);
|
|
3819
|
+
try {
|
|
3820
|
+
const raw = await fetchMetadataFromChain(clientHandle, chainName);
|
|
3821
|
+
return parseMetadata(raw);
|
|
3822
|
+
} finally {
|
|
3823
|
+
clientHandle.destroy();
|
|
3824
|
+
}
|
|
3825
|
+
}
|
|
3112
3826
|
try {
|
|
3113
3827
|
return await getOrFetchMetadata(chainName);
|
|
3114
3828
|
} catch {
|
|
3115
|
-
|
|
3116
|
-
|
|
3829
|
+
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
3830
|
+
`);
|
|
3831
|
+
const clientHandle = await createChainClient(chainName, chainConfig);
|
|
3117
3832
|
try {
|
|
3118
3833
|
return await getOrFetchMetadata(chainName, clientHandle);
|
|
3119
3834
|
} finally {
|
|
@@ -3136,6 +3851,13 @@ async function handleCalls2(target, opts) {
|
|
|
3136
3851
|
const meta2 = await loadMeta2(chainName2, chainConfig2, opts.rpc);
|
|
3137
3852
|
const pallets = listPallets(meta2);
|
|
3138
3853
|
const withCalls = pallets.filter((p) => p.calls.length > 0);
|
|
3854
|
+
if (isJsonOutput(opts)) {
|
|
3855
|
+
console.log(formatJson({
|
|
3856
|
+
chain: chainName2,
|
|
3857
|
+
pallets: withCalls.map((p) => ({ name: p.name, calls: p.calls.length }))
|
|
3858
|
+
}));
|
|
3859
|
+
return;
|
|
3860
|
+
}
|
|
3139
3861
|
printHeading(`Pallets with calls on ${chainName2} (${withCalls.length})`);
|
|
3140
3862
|
for (const p of withCalls) {
|
|
3141
3863
|
printItem(p.name, `${p.calls.length} calls`);
|
|
@@ -3152,7 +3874,23 @@ async function handleCalls2(target, opts) {
|
|
|
3152
3874
|
const pallet = resolvePallet2(meta, palletName);
|
|
3153
3875
|
if (!itemName) {
|
|
3154
3876
|
if (pallet.calls.length === 0) {
|
|
3155
|
-
|
|
3877
|
+
if (isJsonOutput(opts)) {
|
|
3878
|
+
console.log(formatJson({ chain: chainName, pallet: pallet.name, calls: [] }));
|
|
3879
|
+
} else {
|
|
3880
|
+
console.log(`No calls in ${pallet.name}.`);
|
|
3881
|
+
}
|
|
3882
|
+
return;
|
|
3883
|
+
}
|
|
3884
|
+
if (isJsonOutput(opts)) {
|
|
3885
|
+
console.log(formatJson({
|
|
3886
|
+
chain: chainName,
|
|
3887
|
+
pallet: pallet.name,
|
|
3888
|
+
calls: pallet.calls.map((c) => ({
|
|
3889
|
+
name: c.name,
|
|
3890
|
+
args: describeCallArgs(meta, pallet.name, c.name),
|
|
3891
|
+
docs: firstSentence(c.docs)
|
|
3892
|
+
}))
|
|
3893
|
+
}));
|
|
3156
3894
|
return;
|
|
3157
3895
|
}
|
|
3158
3896
|
printHeading(`${pallet.name} Calls`);
|
|
@@ -3172,6 +3910,17 @@ async function handleCalls2(target, opts) {
|
|
|
3172
3910
|
const names = pallet.calls.map((c) => c.name);
|
|
3173
3911
|
throw new Error(suggestMessage(`call in ${pallet.name}`, itemName, names));
|
|
3174
3912
|
}
|
|
3913
|
+
if (isJsonOutput(opts)) {
|
|
3914
|
+
console.log(formatJson({
|
|
3915
|
+
chain: chainName,
|
|
3916
|
+
pallet: pallet.name,
|
|
3917
|
+
item: callItem.name,
|
|
3918
|
+
category: "call",
|
|
3919
|
+
args: describeCallArgs(meta, pallet.name, callItem.name),
|
|
3920
|
+
docs: callItem.docs
|
|
3921
|
+
}));
|
|
3922
|
+
return;
|
|
3923
|
+
}
|
|
3175
3924
|
printHeading(`${pallet.name}.${callItem.name} (Call)`);
|
|
3176
3925
|
const args = describeCallArgs(meta, pallet.name, callItem.name);
|
|
3177
3926
|
console.log(` ${BOLD}Args:${RESET} ${args}`);
|
|
@@ -3188,6 +3937,13 @@ async function handleEvents2(target, opts) {
|
|
|
3188
3937
|
const meta2 = await loadMeta2(chainName2, chainConfig2, opts.rpc);
|
|
3189
3938
|
const pallets = listPallets(meta2);
|
|
3190
3939
|
const withEvents = pallets.filter((p) => p.events.length > 0);
|
|
3940
|
+
if (isJsonOutput(opts)) {
|
|
3941
|
+
console.log(formatJson({
|
|
3942
|
+
chain: chainName2,
|
|
3943
|
+
pallets: withEvents.map((p) => ({ name: p.name, events: p.events.length }))
|
|
3944
|
+
}));
|
|
3945
|
+
return;
|
|
3946
|
+
}
|
|
3191
3947
|
printHeading(`Pallets with events on ${chainName2} (${withEvents.length})`);
|
|
3192
3948
|
for (const p of withEvents) {
|
|
3193
3949
|
printItem(p.name, `${p.events.length} events`);
|
|
@@ -3204,7 +3960,23 @@ async function handleEvents2(target, opts) {
|
|
|
3204
3960
|
const pallet = resolvePallet2(meta, palletName);
|
|
3205
3961
|
if (!itemName) {
|
|
3206
3962
|
if (pallet.events.length === 0) {
|
|
3207
|
-
|
|
3963
|
+
if (isJsonOutput(opts)) {
|
|
3964
|
+
console.log(formatJson({ chain: chainName, pallet: pallet.name, events: [] }));
|
|
3965
|
+
} else {
|
|
3966
|
+
console.log(`No events in ${pallet.name}.`);
|
|
3967
|
+
}
|
|
3968
|
+
return;
|
|
3969
|
+
}
|
|
3970
|
+
if (isJsonOutput(opts)) {
|
|
3971
|
+
console.log(formatJson({
|
|
3972
|
+
chain: chainName,
|
|
3973
|
+
pallet: pallet.name,
|
|
3974
|
+
events: pallet.events.map((e) => ({
|
|
3975
|
+
name: e.name,
|
|
3976
|
+
fields: describeEventFields(meta, pallet.name, e.name),
|
|
3977
|
+
docs: firstSentence(e.docs)
|
|
3978
|
+
}))
|
|
3979
|
+
}));
|
|
3208
3980
|
return;
|
|
3209
3981
|
}
|
|
3210
3982
|
printHeading(`${pallet.name} Events`);
|
|
@@ -3224,6 +3996,17 @@ async function handleEvents2(target, opts) {
|
|
|
3224
3996
|
const names = pallet.events.map((e) => e.name);
|
|
3225
3997
|
throw new Error(suggestMessage(`event in ${pallet.name}`, itemName, names));
|
|
3226
3998
|
}
|
|
3999
|
+
if (isJsonOutput(opts)) {
|
|
4000
|
+
console.log(formatJson({
|
|
4001
|
+
chain: chainName,
|
|
4002
|
+
pallet: pallet.name,
|
|
4003
|
+
item: eventItem.name,
|
|
4004
|
+
category: "event",
|
|
4005
|
+
fields: describeEventFields(meta, pallet.name, eventItem.name),
|
|
4006
|
+
docs: eventItem.docs
|
|
4007
|
+
}));
|
|
4008
|
+
return;
|
|
4009
|
+
}
|
|
3227
4010
|
printHeading(`${pallet.name}.${eventItem.name} (Event)`);
|
|
3228
4011
|
const fields = describeEventFields(meta, pallet.name, eventItem.name);
|
|
3229
4012
|
console.log(` ${BOLD}Fields:${RESET} ${fields}`);
|
|
@@ -3240,6 +4023,13 @@ async function handleErrors2(target, opts) {
|
|
|
3240
4023
|
const meta2 = await loadMeta2(chainName2, chainConfig2, opts.rpc);
|
|
3241
4024
|
const pallets = listPallets(meta2);
|
|
3242
4025
|
const withErrors = pallets.filter((p) => p.errors.length > 0);
|
|
4026
|
+
if (isJsonOutput(opts)) {
|
|
4027
|
+
console.log(formatJson({
|
|
4028
|
+
chain: chainName2,
|
|
4029
|
+
pallets: withErrors.map((p) => ({ name: p.name, errors: p.errors.length }))
|
|
4030
|
+
}));
|
|
4031
|
+
return;
|
|
4032
|
+
}
|
|
3243
4033
|
printHeading(`Pallets with errors on ${chainName2} (${withErrors.length})`);
|
|
3244
4034
|
for (const p of withErrors) {
|
|
3245
4035
|
printItem(p.name, `${p.errors.length} errors`);
|
|
@@ -3256,7 +4046,19 @@ async function handleErrors2(target, opts) {
|
|
|
3256
4046
|
const pallet = resolvePallet2(meta, palletName);
|
|
3257
4047
|
if (!itemName) {
|
|
3258
4048
|
if (pallet.errors.length === 0) {
|
|
3259
|
-
|
|
4049
|
+
if (isJsonOutput(opts)) {
|
|
4050
|
+
console.log(formatJson({ chain: chainName, pallet: pallet.name, errors: [] }));
|
|
4051
|
+
} else {
|
|
4052
|
+
console.log(`No errors in ${pallet.name}.`);
|
|
4053
|
+
}
|
|
4054
|
+
return;
|
|
4055
|
+
}
|
|
4056
|
+
if (isJsonOutput(opts)) {
|
|
4057
|
+
console.log(formatJson({
|
|
4058
|
+
chain: chainName,
|
|
4059
|
+
pallet: pallet.name,
|
|
4060
|
+
errors: pallet.errors.map((e) => ({ name: e.name, docs: firstSentence(e.docs) }))
|
|
4061
|
+
}));
|
|
3260
4062
|
return;
|
|
3261
4063
|
}
|
|
3262
4064
|
printHeading(`${pallet.name} Errors`);
|
|
@@ -3275,6 +4077,16 @@ async function handleErrors2(target, opts) {
|
|
|
3275
4077
|
const names = pallet.errors.map((e) => e.name);
|
|
3276
4078
|
throw new Error(suggestMessage(`error in ${pallet.name}`, itemName, names));
|
|
3277
4079
|
}
|
|
4080
|
+
if (isJsonOutput(opts)) {
|
|
4081
|
+
console.log(formatJson({
|
|
4082
|
+
chain: chainName,
|
|
4083
|
+
pallet: pallet.name,
|
|
4084
|
+
item: errorItem.name,
|
|
4085
|
+
category: "error",
|
|
4086
|
+
docs: errorItem.docs
|
|
4087
|
+
}));
|
|
4088
|
+
return;
|
|
4089
|
+
}
|
|
3278
4090
|
printHeading(`${pallet.name}.${errorItem.name} (Error)`);
|
|
3279
4091
|
if (errorItem.docs.length) {
|
|
3280
4092
|
printDocs(errorItem.docs);
|
|
@@ -3288,6 +4100,13 @@ async function handleStorage2(target, opts) {
|
|
|
3288
4100
|
const meta2 = await loadMeta2(chainName2, chainConfig2, opts.rpc);
|
|
3289
4101
|
const pallets = listPallets(meta2);
|
|
3290
4102
|
const withStorage = pallets.filter((p) => p.storage.length > 0);
|
|
4103
|
+
if (isJsonOutput(opts)) {
|
|
4104
|
+
console.log(formatJson({
|
|
4105
|
+
chain: chainName2,
|
|
4106
|
+
pallets: withStorage.map((p) => ({ name: p.name, storage: p.storage.length }))
|
|
4107
|
+
}));
|
|
4108
|
+
return;
|
|
4109
|
+
}
|
|
3291
4110
|
printHeading(`Pallets with storage on ${chainName2} (${withStorage.length})`);
|
|
3292
4111
|
for (const p of withStorage) {
|
|
3293
4112
|
printItem(p.name, `${p.storage.length} storage`);
|
|
@@ -3304,7 +4123,23 @@ async function handleStorage2(target, opts) {
|
|
|
3304
4123
|
const pallet = resolvePallet2(meta, palletName);
|
|
3305
4124
|
if (!itemName) {
|
|
3306
4125
|
if (pallet.storage.length === 0) {
|
|
3307
|
-
|
|
4126
|
+
if (isJsonOutput(opts)) {
|
|
4127
|
+
console.log(formatJson({ chain: chainName, pallet: pallet.name, storage: [] }));
|
|
4128
|
+
} else {
|
|
4129
|
+
console.log(`No storage items in ${pallet.name}.`);
|
|
4130
|
+
}
|
|
4131
|
+
return;
|
|
4132
|
+
}
|
|
4133
|
+
if (isJsonOutput(opts)) {
|
|
4134
|
+
console.log(formatJson({
|
|
4135
|
+
chain: chainName,
|
|
4136
|
+
pallet: pallet.name,
|
|
4137
|
+
storage: pallet.storage.map((s) => {
|
|
4138
|
+
const valueType = describeType(meta.lookup, s.valueTypeId);
|
|
4139
|
+
const keyType = s.keyTypeId != null ? describeType(meta.lookup, s.keyTypeId) : undefined;
|
|
4140
|
+
return { name: s.name, type: s.type, valueType, keyType, docs: firstSentence(s.docs) };
|
|
4141
|
+
})
|
|
4142
|
+
}));
|
|
3308
4143
|
return;
|
|
3309
4144
|
}
|
|
3310
4145
|
printHeading(`${pallet.name} Storage`);
|
|
@@ -3331,6 +4166,21 @@ async function handleStorage2(target, opts) {
|
|
|
3331
4166
|
const names = pallet.storage.map((s) => s.name);
|
|
3332
4167
|
throw new Error(suggestMessage(`storage item in ${pallet.name}`, itemName, names));
|
|
3333
4168
|
}
|
|
4169
|
+
if (isJsonOutput(opts)) {
|
|
4170
|
+
const valueType = describeType(meta.lookup, storageItem.valueTypeId);
|
|
4171
|
+
const keyType = storageItem.keyTypeId != null ? describeType(meta.lookup, storageItem.keyTypeId) : undefined;
|
|
4172
|
+
console.log(formatJson({
|
|
4173
|
+
chain: chainName,
|
|
4174
|
+
pallet: pallet.name,
|
|
4175
|
+
item: storageItem.name,
|
|
4176
|
+
category: "storage",
|
|
4177
|
+
type: storageItem.type,
|
|
4178
|
+
valueType,
|
|
4179
|
+
keyType,
|
|
4180
|
+
docs: storageItem.docs
|
|
4181
|
+
}));
|
|
4182
|
+
return;
|
|
4183
|
+
}
|
|
3334
4184
|
printHeading(`${pallet.name}.${storageItem.name} (Storage)`);
|
|
3335
4185
|
console.log(` ${BOLD}Type:${RESET} ${storageItem.type}`);
|
|
3336
4186
|
console.log(` ${BOLD}Value:${RESET} ${describeType(meta.lookup, storageItem.valueTypeId)}`);
|
|
@@ -3521,7 +4371,7 @@ async function showItemHelp2(category, target, opts) {
|
|
|
3521
4371
|
init_hash();
|
|
3522
4372
|
init_output();
|
|
3523
4373
|
init_errors();
|
|
3524
|
-
import { readFile as
|
|
4374
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
3525
4375
|
async function resolveInput(data, opts) {
|
|
3526
4376
|
const sources = [data !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
|
|
3527
4377
|
if (sources > 1) {
|
|
@@ -3531,7 +4381,7 @@ async function resolveInput(data, opts) {
|
|
|
3531
4381
|
throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
|
|
3532
4382
|
}
|
|
3533
4383
|
if (opts.file) {
|
|
3534
|
-
const buf = await
|
|
4384
|
+
const buf = await readFile5(opts.file);
|
|
3535
4385
|
return new Uint8Array(buf);
|
|
3536
4386
|
}
|
|
3537
4387
|
if (opts.stdin) {
|
|
@@ -3574,8 +4424,7 @@ function registerHashCommand(cli) {
|
|
|
3574
4424
|
const input = await resolveInput(data, opts);
|
|
3575
4425
|
const hash = computeHash(algorithm, input);
|
|
3576
4426
|
const hexHash = toHex2(hash);
|
|
3577
|
-
|
|
3578
|
-
if (format === "json") {
|
|
4427
|
+
if (isJsonOutput(opts)) {
|
|
3579
4428
|
printResult({
|
|
3580
4429
|
algorithm,
|
|
3581
4430
|
input: data ?? (opts.file ? `file:${opts.file}` : "stdin"),
|
|
@@ -3658,19 +4507,46 @@ function registerInspectCommand(cli) {
|
|
|
3658
4507
|
}
|
|
3659
4508
|
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
3660
4509
|
let meta;
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
console.error(`Fetching metadata from ${chainName}...`);
|
|
4510
|
+
if (opts.rpc) {
|
|
4511
|
+
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
4512
|
+
`);
|
|
3665
4513
|
const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
3666
4514
|
try {
|
|
3667
|
-
|
|
4515
|
+
const raw = await fetchMetadataFromChain(clientHandle, chainName);
|
|
4516
|
+
meta = parseMetadata(raw);
|
|
3668
4517
|
} finally {
|
|
3669
4518
|
clientHandle.destroy();
|
|
3670
4519
|
}
|
|
4520
|
+
} else {
|
|
4521
|
+
try {
|
|
4522
|
+
meta = await getOrFetchMetadata(chainName);
|
|
4523
|
+
} catch {
|
|
4524
|
+
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
4525
|
+
`);
|
|
4526
|
+
const clientHandle = await createChainClient(chainName, chainConfig);
|
|
4527
|
+
try {
|
|
4528
|
+
meta = await getOrFetchMetadata(chainName, clientHandle);
|
|
4529
|
+
} finally {
|
|
4530
|
+
clientHandle.destroy();
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
3671
4533
|
}
|
|
3672
4534
|
if (!target) {
|
|
3673
4535
|
const pallets = listPallets(meta);
|
|
4536
|
+
if (isJsonOutput(opts)) {
|
|
4537
|
+
console.log(formatJson({
|
|
4538
|
+
chain: chainName,
|
|
4539
|
+
pallets: pallets.map((p) => ({
|
|
4540
|
+
name: p.name,
|
|
4541
|
+
storage: p.storage.length,
|
|
4542
|
+
constants: p.constants.length,
|
|
4543
|
+
calls: p.calls.length,
|
|
4544
|
+
events: p.events.length,
|
|
4545
|
+
errors: p.errors.length
|
|
4546
|
+
}))
|
|
4547
|
+
}));
|
|
4548
|
+
return;
|
|
4549
|
+
}
|
|
3674
4550
|
printHeading(`Pallets on ${chainName} (${pallets.length})`);
|
|
3675
4551
|
for (const p of pallets) {
|
|
3676
4552
|
const counts = [];
|
|
@@ -3695,6 +4571,44 @@ function registerInspectCommand(cli) {
|
|
|
3695
4571
|
if (!pallet2) {
|
|
3696
4572
|
throw new Error(suggestMessage("pallet", palletName, palletNames2));
|
|
3697
4573
|
}
|
|
4574
|
+
if (isJsonOutput(opts)) {
|
|
4575
|
+
console.log(formatJson({
|
|
4576
|
+
chain: chainName,
|
|
4577
|
+
pallet: pallet2.name,
|
|
4578
|
+
docs: pallet2.docs,
|
|
4579
|
+
storage: pallet2.storage.map((s) => {
|
|
4580
|
+
const valueType = describeType(meta.lookup, s.valueTypeId);
|
|
4581
|
+
const keyType = s.keyTypeId != null ? describeType(meta.lookup, s.keyTypeId) : undefined;
|
|
4582
|
+
return {
|
|
4583
|
+
name: s.name,
|
|
4584
|
+
type: s.type,
|
|
4585
|
+
valueType,
|
|
4586
|
+
keyType,
|
|
4587
|
+
docs: firstSentence(s.docs)
|
|
4588
|
+
};
|
|
4589
|
+
}),
|
|
4590
|
+
constants: pallet2.constants.map((c) => ({
|
|
4591
|
+
name: c.name,
|
|
4592
|
+
type: describeType(meta.lookup, c.typeId),
|
|
4593
|
+
docs: firstSentence(c.docs)
|
|
4594
|
+
})),
|
|
4595
|
+
calls: pallet2.calls.map((c) => ({
|
|
4596
|
+
name: c.name,
|
|
4597
|
+
args: describeCallArgs(meta, pallet2.name, c.name),
|
|
4598
|
+
docs: firstSentence(c.docs)
|
|
4599
|
+
})),
|
|
4600
|
+
events: pallet2.events.map((e) => ({
|
|
4601
|
+
name: e.name,
|
|
4602
|
+
fields: describeEventFields(meta, pallet2.name, e.name),
|
|
4603
|
+
docs: firstSentence(e.docs)
|
|
4604
|
+
})),
|
|
4605
|
+
errors: pallet2.errors.map((e) => ({
|
|
4606
|
+
name: e.name,
|
|
4607
|
+
docs: firstSentence(e.docs)
|
|
4608
|
+
}))
|
|
4609
|
+
}));
|
|
4610
|
+
return;
|
|
4611
|
+
}
|
|
3698
4612
|
printHeading(`${pallet2.name} Pallet`);
|
|
3699
4613
|
if (pallet2.docs.length) {
|
|
3700
4614
|
printDocs(pallet2.docs);
|
|
@@ -3773,6 +4687,79 @@ function registerInspectCommand(cli) {
|
|
|
3773
4687
|
if (!pallet) {
|
|
3774
4688
|
throw new Error(suggestMessage("pallet", palletName, palletNames));
|
|
3775
4689
|
}
|
|
4690
|
+
if (isJsonOutput(opts)) {
|
|
4691
|
+
const si = pallet.storage.find((s) => s.name.toLowerCase() === itemName.toLowerCase());
|
|
4692
|
+
if (si) {
|
|
4693
|
+
const valueType = describeType(meta.lookup, si.valueTypeId);
|
|
4694
|
+
const keyType = si.keyTypeId != null ? describeType(meta.lookup, si.keyTypeId) : undefined;
|
|
4695
|
+
console.log(formatJson({
|
|
4696
|
+
chain: chainName,
|
|
4697
|
+
pallet: pallet.name,
|
|
4698
|
+
item: si.name,
|
|
4699
|
+
category: "storage",
|
|
4700
|
+
type: si.type,
|
|
4701
|
+
valueType,
|
|
4702
|
+
keyType,
|
|
4703
|
+
docs: si.docs
|
|
4704
|
+
}));
|
|
4705
|
+
return;
|
|
4706
|
+
}
|
|
4707
|
+
const ci = pallet.constants.find((c) => c.name.toLowerCase() === itemName.toLowerCase());
|
|
4708
|
+
if (ci) {
|
|
4709
|
+
console.log(formatJson({
|
|
4710
|
+
chain: chainName,
|
|
4711
|
+
pallet: pallet.name,
|
|
4712
|
+
item: ci.name,
|
|
4713
|
+
category: "constant",
|
|
4714
|
+
type: describeType(meta.lookup, ci.typeId),
|
|
4715
|
+
docs: ci.docs
|
|
4716
|
+
}));
|
|
4717
|
+
return;
|
|
4718
|
+
}
|
|
4719
|
+
const ca = pallet.calls.find((c) => c.name.toLowerCase() === itemName.toLowerCase());
|
|
4720
|
+
if (ca) {
|
|
4721
|
+
console.log(formatJson({
|
|
4722
|
+
chain: chainName,
|
|
4723
|
+
pallet: pallet.name,
|
|
4724
|
+
item: ca.name,
|
|
4725
|
+
category: "call",
|
|
4726
|
+
args: describeCallArgs(meta, pallet.name, ca.name),
|
|
4727
|
+
docs: ca.docs
|
|
4728
|
+
}));
|
|
4729
|
+
return;
|
|
4730
|
+
}
|
|
4731
|
+
const ev = pallet.events.find((e) => e.name.toLowerCase() === itemName.toLowerCase());
|
|
4732
|
+
if (ev) {
|
|
4733
|
+
console.log(formatJson({
|
|
4734
|
+
chain: chainName,
|
|
4735
|
+
pallet: pallet.name,
|
|
4736
|
+
item: ev.name,
|
|
4737
|
+
category: "event",
|
|
4738
|
+
fields: describeEventFields(meta, pallet.name, ev.name),
|
|
4739
|
+
docs: ev.docs
|
|
4740
|
+
}));
|
|
4741
|
+
return;
|
|
4742
|
+
}
|
|
4743
|
+
const er = pallet.errors.find((e) => e.name.toLowerCase() === itemName.toLowerCase());
|
|
4744
|
+
if (er) {
|
|
4745
|
+
console.log(formatJson({
|
|
4746
|
+
chain: chainName,
|
|
4747
|
+
pallet: pallet.name,
|
|
4748
|
+
item: er.name,
|
|
4749
|
+
category: "error",
|
|
4750
|
+
docs: er.docs
|
|
4751
|
+
}));
|
|
4752
|
+
return;
|
|
4753
|
+
}
|
|
4754
|
+
const allItems2 = [
|
|
4755
|
+
...pallet.storage.map((s) => s.name),
|
|
4756
|
+
...pallet.constants.map((c) => c.name),
|
|
4757
|
+
...pallet.calls.map((c) => c.name),
|
|
4758
|
+
...pallet.events.map((e) => e.name),
|
|
4759
|
+
...pallet.errors.map((e) => e.name)
|
|
4760
|
+
];
|
|
4761
|
+
throw new Error(suggestMessage(`item in ${pallet.name}`, itemName, allItems2));
|
|
4762
|
+
}
|
|
3776
4763
|
const storageItem = pallet.storage.find((s) => s.name.toLowerCase() === itemName.toLowerCase());
|
|
3777
4764
|
if (storageItem) {
|
|
3778
4765
|
printHeading(`${pallet.name}.${storageItem.name} (Storage)`);
|
|
@@ -3906,8 +4893,7 @@ function registerParachainCommand(cli) {
|
|
|
3906
4893
|
throw new CliError(`Invalid prefix "${opts.prefix}". Must be a non-negative integer.`);
|
|
3907
4894
|
}
|
|
3908
4895
|
const types = opts.type ? [validateType(opts.type)] : SOVEREIGN_ACCOUNT_TYPES;
|
|
3909
|
-
|
|
3910
|
-
if (format === "json") {
|
|
4896
|
+
if (isJsonOutput(opts)) {
|
|
3911
4897
|
const result = { paraId, prefix };
|
|
3912
4898
|
for (const type of types) {
|
|
3913
4899
|
const accountId = deriveSovereignAccount(paraId, type);
|
|
@@ -3948,6 +4934,13 @@ async function handleQuery(target, keys, opts) {
|
|
|
3948
4934
|
const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
|
|
3949
4935
|
const pallets = listPallets(meta);
|
|
3950
4936
|
const withStorage = pallets.filter((p) => p.storage.length > 0);
|
|
4937
|
+
if (isJsonOutput(opts)) {
|
|
4938
|
+
console.log(formatJson({
|
|
4939
|
+
chain: chainName2,
|
|
4940
|
+
pallets: withStorage.map((p) => ({ name: p.name, storage: p.storage.length }))
|
|
4941
|
+
}));
|
|
4942
|
+
return;
|
|
4943
|
+
}
|
|
3951
4944
|
printHeading(`Pallets with storage on ${chainName2} (${withStorage.length})`);
|
|
3952
4945
|
for (const p of withStorage) {
|
|
3953
4946
|
printItem(p.name, `${p.storage.length} storage items`);
|
|
@@ -3962,7 +4955,23 @@ async function handleQuery(target, keys, opts) {
|
|
|
3962
4955
|
const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
|
|
3963
4956
|
const pallet2 = resolvePallet(meta, palletName(target));
|
|
3964
4957
|
if (pallet2.storage.length === 0) {
|
|
3965
|
-
|
|
4958
|
+
if (isJsonOutput(opts)) {
|
|
4959
|
+
console.log(formatJson({ chain: chainName2, pallet: pallet2.name, storage: [] }));
|
|
4960
|
+
} else {
|
|
4961
|
+
console.log(`No storage items in ${pallet2.name}.`);
|
|
4962
|
+
}
|
|
4963
|
+
return;
|
|
4964
|
+
}
|
|
4965
|
+
if (isJsonOutput(opts)) {
|
|
4966
|
+
console.log(formatJson({
|
|
4967
|
+
chain: chainName2,
|
|
4968
|
+
pallet: pallet2.name,
|
|
4969
|
+
storage: pallet2.storage.map((s) => {
|
|
4970
|
+
const valueType = describeType(meta.lookup, s.valueTypeId);
|
|
4971
|
+
const keyType = s.keyTypeId != null ? describeType(meta.lookup, s.keyTypeId) : undefined;
|
|
4972
|
+
return { name: s.name, type: s.type, valueType, keyType, docs: firstSentence(s.docs) };
|
|
4973
|
+
})
|
|
4974
|
+
}));
|
|
3966
4975
|
return;
|
|
3967
4976
|
}
|
|
3968
4977
|
printHeading(`${pallet2.name} Storage`);
|
|
@@ -4007,7 +5016,7 @@ async function handleQuery(target, keys, opts) {
|
|
|
4007
5016
|
typeof opts.parsedArgs === "object" ? JSON.stringify(opts.parsedArgs) : String(opts.parsedArgs)
|
|
4008
5017
|
];
|
|
4009
5018
|
const parsedKeys = await parseStorageKeys(meta, palletInfo.name, storageItem, effectiveKeys);
|
|
4010
|
-
const format = opts.output ?? "pretty";
|
|
5019
|
+
const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
|
|
4011
5020
|
const expectedLen = storageItem.type === "map" && storageItem.keyTypeId != null ? meta.builder.buildStorage(palletInfo.name, storageItem.name).len : 0;
|
|
4012
5021
|
if (storageItem.type === "map" && parsedKeys.length < expectedLen) {
|
|
4013
5022
|
if (parsedKeys.length === 0 && !opts.dump) {
|
|
@@ -4074,7 +5083,7 @@ init_accounts();
|
|
|
4074
5083
|
init_hash();
|
|
4075
5084
|
init_output();
|
|
4076
5085
|
init_errors();
|
|
4077
|
-
import { readFile as
|
|
5086
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
4078
5087
|
var SUPPORTED_TYPES = ["sr25519"];
|
|
4079
5088
|
function isSupportedType(type) {
|
|
4080
5089
|
return SUPPORTED_TYPES.includes(type.toLowerCase());
|
|
@@ -4094,7 +5103,7 @@ async function resolveInput2(data, opts) {
|
|
|
4094
5103
|
throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
|
|
4095
5104
|
}
|
|
4096
5105
|
if (opts.file) {
|
|
4097
|
-
const buf = await
|
|
5106
|
+
const buf = await readFile6(opts.file);
|
|
4098
5107
|
return new Uint8Array(buf);
|
|
4099
5108
|
}
|
|
4100
5109
|
if (opts.stdin) {
|
|
@@ -4152,8 +5161,7 @@ function registerSignCommand(cli) {
|
|
|
4152
5161
|
signature: hexSignature,
|
|
4153
5162
|
enum: enumValue
|
|
4154
5163
|
};
|
|
4155
|
-
|
|
4156
|
-
if (format === "json") {
|
|
5164
|
+
if (isJsonOutput(opts)) {
|
|
4157
5165
|
printResult(result, "json");
|
|
4158
5166
|
} else {
|
|
4159
5167
|
console.log(` ${BOLD}Type:${RESET} ${result.type}`);
|
|
@@ -4172,8 +5180,10 @@ init_client();
|
|
|
4172
5180
|
init_metadata();
|
|
4173
5181
|
init_output();
|
|
4174
5182
|
init_resolve_address();
|
|
5183
|
+
init_binary_display();
|
|
4175
5184
|
init_errors();
|
|
4176
5185
|
init_focused_inspect();
|
|
5186
|
+
import { compact as scaleCompact2 } from "@polkadot-api/substrate-bindings";
|
|
4177
5187
|
import { getViewBuilder as getViewBuilder2 } from "@polkadot-api/view-builder";
|
|
4178
5188
|
import { Binary as Binary3 } from "polkadot-api";
|
|
4179
5189
|
import { stringify as stringifyYaml2 } from "yaml";
|
|
@@ -4226,14 +5236,31 @@ function parseMortalityOption(raw) {
|
|
|
4226
5236
|
}
|
|
4227
5237
|
return { mortal: true, period: n };
|
|
4228
5238
|
}
|
|
5239
|
+
function parseAssetOption(raw) {
|
|
5240
|
+
if (raw === undefined)
|
|
5241
|
+
return;
|
|
5242
|
+
try {
|
|
5243
|
+
const parsed = JSON.parse(raw);
|
|
5244
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
5245
|
+
throw new Error("must be a JSON object");
|
|
5246
|
+
}
|
|
5247
|
+
return parsed;
|
|
5248
|
+
} catch (err) {
|
|
5249
|
+
throw new CliError(`Invalid --asset value: ${err.message}
|
|
5250
|
+
` + `Expected an XCM location, e.g. '{"parents":0,"interior":{"type":"X2","value":[{"type":"PalletInstance","value":50},{"type":"GeneralIndex","value":"3"}]}}'`);
|
|
5251
|
+
}
|
|
5252
|
+
}
|
|
4229
5253
|
function parseAtOption(raw) {
|
|
4230
5254
|
if (raw === undefined)
|
|
4231
5255
|
return;
|
|
4232
|
-
if (raw === "
|
|
4233
|
-
return
|
|
5256
|
+
if (raw === "finalized")
|
|
5257
|
+
return;
|
|
5258
|
+
if (raw === "best") {
|
|
5259
|
+
throw new CliError('"best" is no longer supported for --at in papi v2. Omit --at for finalized, or pass a specific block hash.');
|
|
5260
|
+
}
|
|
4234
5261
|
if (/^0x[0-9a-fA-F]{64}$/.test(raw))
|
|
4235
5262
|
return raw;
|
|
4236
|
-
throw new CliError(`Invalid --at value "${raw}". Use
|
|
5263
|
+
throw new CliError(`Invalid --at value "${raw}". Use a 0x-prefixed 32-byte block hash, or omit for finalized.`);
|
|
4237
5264
|
}
|
|
4238
5265
|
async function handleTx(target, args, opts) {
|
|
4239
5266
|
if (!target) {
|
|
@@ -4242,6 +5269,13 @@ async function handleTx(target, args, opts) {
|
|
|
4242
5269
|
const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
|
|
4243
5270
|
const pallets = listPallets(meta);
|
|
4244
5271
|
const withCalls = pallets.filter((p) => p.calls.length > 0);
|
|
5272
|
+
if (isJsonOutput(opts)) {
|
|
5273
|
+
console.log(formatJson({
|
|
5274
|
+
chain: chainName2,
|
|
5275
|
+
pallets: withCalls.map((p) => ({ name: p.name, calls: p.calls.length }))
|
|
5276
|
+
}));
|
|
5277
|
+
return;
|
|
5278
|
+
}
|
|
4245
5279
|
printHeading(`Pallets with calls on ${chainName2} (${withCalls.length})`);
|
|
4246
5280
|
for (const p of withCalls) {
|
|
4247
5281
|
printItem(p.name, `${p.calls.length} calls`);
|
|
@@ -4256,7 +5290,23 @@ async function handleTx(target, args, opts) {
|
|
|
4256
5290
|
const meta = await loadMeta(chainName2, chainConfig2, opts.rpc);
|
|
4257
5291
|
const pallet2 = resolvePallet(meta, target);
|
|
4258
5292
|
if (pallet2.calls.length === 0) {
|
|
4259
|
-
|
|
5293
|
+
if (isJsonOutput(opts)) {
|
|
5294
|
+
console.log(formatJson({ chain: chainName2, pallet: pallet2.name, calls: [] }));
|
|
5295
|
+
} else {
|
|
5296
|
+
console.log(`No calls in ${pallet2.name}.`);
|
|
5297
|
+
}
|
|
5298
|
+
return;
|
|
5299
|
+
}
|
|
5300
|
+
if (isJsonOutput(opts)) {
|
|
5301
|
+
console.log(formatJson({
|
|
5302
|
+
chain: chainName2,
|
|
5303
|
+
pallet: pallet2.name,
|
|
5304
|
+
calls: pallet2.calls.map((c) => ({
|
|
5305
|
+
name: c.name,
|
|
5306
|
+
args: describeCallArgs(meta, pallet2.name, c.name),
|
|
5307
|
+
docs: firstSentence(c.docs)
|
|
5308
|
+
}))
|
|
5309
|
+
}));
|
|
4260
5310
|
return;
|
|
4261
5311
|
}
|
|
4262
5312
|
printHeading(`${pallet2.name} Calls`);
|
|
@@ -4271,9 +5321,9 @@ async function handleTx(target, args, opts) {
|
|
|
4271
5321
|
console.log();
|
|
4272
5322
|
return;
|
|
4273
5323
|
}
|
|
4274
|
-
if (!opts.from && !opts.encode && !opts.
|
|
5324
|
+
if (!opts.from && !opts.unsigned && !opts.encode && !opts.toYaml && !opts.toJson) {
|
|
4275
5325
|
if (isRawCall) {
|
|
4276
|
-
throw new Error("--from is required (or use --encode
|
|
5326
|
+
throw new Error("--from is required (or use --unsigned for bare tx, --encode for hex without signing)");
|
|
4277
5327
|
}
|
|
4278
5328
|
await showItemHelp("tx", target, opts);
|
|
4279
5329
|
return;
|
|
@@ -4284,14 +5334,26 @@ async function handleTx(target, args, opts) {
|
|
|
4284
5334
|
if (opts.encode && isRawCall) {
|
|
4285
5335
|
throw new Error("--encode cannot be used with raw call hex (already encoded)");
|
|
4286
5336
|
}
|
|
4287
|
-
if ((opts.
|
|
4288
|
-
throw new Error("--yaml/--json and --encode are mutually exclusive");
|
|
5337
|
+
if ((opts.toYaml || opts.toJson) && opts.encode) {
|
|
5338
|
+
throw new Error("--to-yaml/--to-json and --encode are mutually exclusive");
|
|
5339
|
+
}
|
|
5340
|
+
if ((opts.toYaml || opts.toJson) && opts.dryRun) {
|
|
5341
|
+
throw new Error("--to-yaml/--to-json and --dry-run are mutually exclusive");
|
|
4289
5342
|
}
|
|
4290
|
-
if (
|
|
4291
|
-
throw new Error("--yaml
|
|
5343
|
+
if (opts.toYaml && opts.toJson) {
|
|
5344
|
+
throw new Error("--to-yaml and --to-json are mutually exclusive");
|
|
4292
5345
|
}
|
|
4293
|
-
if (opts.
|
|
4294
|
-
throw new Error("--
|
|
5346
|
+
if (opts.unsigned && opts.from) {
|
|
5347
|
+
throw new Error("--unsigned and --from are mutually exclusive");
|
|
5348
|
+
}
|
|
5349
|
+
if (opts.unsigned && opts.nonce) {
|
|
5350
|
+
throw new Error("--unsigned does not support --nonce");
|
|
5351
|
+
}
|
|
5352
|
+
if (opts.unsigned && opts.tip) {
|
|
5353
|
+
throw new Error("--unsigned does not support --tip");
|
|
5354
|
+
}
|
|
5355
|
+
if (opts.unsigned && opts.mortality) {
|
|
5356
|
+
throw new Error("--unsigned does not support --mortality");
|
|
4295
5357
|
}
|
|
4296
5358
|
const config = await loadConfig();
|
|
4297
5359
|
const effectiveChain = opts.chain;
|
|
@@ -4303,10 +5365,10 @@ async function handleTx(target, args, opts) {
|
|
|
4303
5365
|
callName = target.slice(dotIdx + 1);
|
|
4304
5366
|
}
|
|
4305
5367
|
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
4306
|
-
const decodeOnly = opts.encode || opts.
|
|
4307
|
-
const signer = decodeOnly ? undefined : await resolveAccountSigner(opts.from);
|
|
5368
|
+
const decodeOnly = opts.encode || opts.toYaml || opts.toJson;
|
|
5369
|
+
const signer = decodeOnly || opts.unsigned ? undefined : await resolveAccountSigner(opts.from);
|
|
4308
5370
|
let clientHandle;
|
|
4309
|
-
if (!decodeOnly) {
|
|
5371
|
+
if (!decodeOnly || opts.unsigned) {
|
|
4310
5372
|
clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
4311
5373
|
}
|
|
4312
5374
|
try {
|
|
@@ -4325,11 +5387,18 @@ async function handleTx(target, args, opts) {
|
|
|
4325
5387
|
let txOptions;
|
|
4326
5388
|
const nonce = parseNonceOption(opts.nonce);
|
|
4327
5389
|
const tip = parseTipOption(opts.tip);
|
|
5390
|
+
const asset = parseAssetOption(opts.asset);
|
|
4328
5391
|
const mortality = parseMortalityOption(opts.mortality);
|
|
4329
5392
|
const at = parseAtOption(opts.at);
|
|
4330
|
-
if (!decodeOnly) {
|
|
5393
|
+
if (!decodeOnly || opts.unsigned) {
|
|
4331
5394
|
const userExtOverrides = parseExtOption(opts.ext);
|
|
4332
|
-
const
|
|
5395
|
+
const skipBuiltins = asset !== undefined ? new Set([...PAPI_BUILTIN_EXTENSIONS2].filter((e) => e !== "ChargeAssetTxPayment")) : PAPI_BUILTIN_EXTENSIONS2;
|
|
5396
|
+
if (asset !== undefined) {
|
|
5397
|
+
userExtOverrides.ChargeAssetTxPayment ??= {
|
|
5398
|
+
value: { tip: tip ?? 0n, asset_id: asset }
|
|
5399
|
+
};
|
|
5400
|
+
}
|
|
5401
|
+
const customSignedExtensions = buildCustomSignedExtensions(meta, userExtOverrides, skipBuiltins);
|
|
4333
5402
|
const built = {};
|
|
4334
5403
|
if (Object.keys(customSignedExtensions).length > 0)
|
|
4335
5404
|
built.customSignedExtensions = customSignedExtensions;
|
|
@@ -4352,9 +5421,9 @@ async function handleTx(target, args, opts) {
|
|
|
4352
5421
|
` + "Usage: dot tx 0x<call_hex> --from <account>");
|
|
4353
5422
|
}
|
|
4354
5423
|
callHex = target;
|
|
4355
|
-
if (opts.
|
|
5424
|
+
if (opts.toYaml || opts.toJson) {
|
|
4356
5425
|
const fileObj = decodeCallToFileFormat(meta, callHex, chainName);
|
|
4357
|
-
outputFileFormat(fileObj, !!opts.
|
|
5426
|
+
outputFileFormat(fileObj, !!opts.toYaml);
|
|
4358
5427
|
return;
|
|
4359
5428
|
}
|
|
4360
5429
|
const callBinary = Binary3.fromHex(target);
|
|
@@ -4372,26 +5441,75 @@ async function handleTx(target, args, opts) {
|
|
|
4372
5441
|
}
|
|
4373
5442
|
const effectiveArgs = opts.parsedArgs !== undefined ? fileArgsToStrings(opts.parsedArgs) : args;
|
|
4374
5443
|
const callData = await parseCallArgs(meta, palletInfo.name, callInfo.name, effectiveArgs);
|
|
4375
|
-
if (opts.encode || opts.
|
|
5444
|
+
if (opts.encode && !opts.unsigned || opts.toYaml || opts.toJson) {
|
|
4376
5445
|
const { codec, location } = meta.builder.buildCall(palletInfo.name, callInfo.name);
|
|
4377
5446
|
const encodedArgs = codec.enc(callData);
|
|
4378
5447
|
const fullCall = new Uint8Array([location[0], location[1], ...encodedArgs]);
|
|
4379
|
-
const hex = Binary3.
|
|
5448
|
+
const hex = Binary3.toHex(fullCall);
|
|
4380
5449
|
if (opts.encode) {
|
|
4381
|
-
|
|
5450
|
+
if (isJsonOutput(opts)) {
|
|
5451
|
+
console.log(formatJson({ callHex: hex }));
|
|
5452
|
+
} else {
|
|
5453
|
+
console.log(hex);
|
|
5454
|
+
}
|
|
4382
5455
|
return;
|
|
4383
5456
|
}
|
|
4384
5457
|
const fileObj = decodeCallToFileFormat(meta, hex, chainName);
|
|
4385
|
-
outputFileFormat(fileObj, !!opts.
|
|
5458
|
+
outputFileFormat(fileObj, !!opts.toYaml);
|
|
4386
5459
|
return;
|
|
4387
5460
|
}
|
|
4388
5461
|
tx = unsafeApi.tx[palletInfo.name][callInfo.name](callData);
|
|
4389
5462
|
const encodedCall = await tx.getEncodedData();
|
|
4390
|
-
callHex =
|
|
5463
|
+
callHex = Binary3.toHex(encodedCall);
|
|
4391
5464
|
}
|
|
4392
5465
|
const decodedStr = decodeCall(meta, callHex);
|
|
5466
|
+
if (opts.dryRun && opts.unsigned) {
|
|
5467
|
+
if (isJsonOutput(opts)) {
|
|
5468
|
+
console.log(formatJson({
|
|
5469
|
+
chain: chainName,
|
|
5470
|
+
unsigned: true,
|
|
5471
|
+
callHex,
|
|
5472
|
+
decoded: decodedStr,
|
|
5473
|
+
estimatedFees: null
|
|
5474
|
+
}));
|
|
5475
|
+
return;
|
|
5476
|
+
}
|
|
5477
|
+
console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
|
|
5478
|
+
console.log(` ${BOLD}Type:${RESET} unsigned (bare)`);
|
|
5479
|
+
console.log(` ${BOLD}Call:${RESET} ${callHex}`);
|
|
5480
|
+
console.log(` ${BOLD}Decode:${RESET} ${decodedStr}`);
|
|
5481
|
+
console.log(` ${BOLD}Fees:${RESET} ${DIM}N/A (unsigned transaction)${RESET}`);
|
|
5482
|
+
return;
|
|
5483
|
+
}
|
|
4393
5484
|
if (opts.dryRun) {
|
|
4394
5485
|
const signerAddress = toSs58(signer.publicKey);
|
|
5486
|
+
let estimatedFees;
|
|
5487
|
+
try {
|
|
5488
|
+
estimatedFees = String(await tx.getEstimatedFees(signer?.publicKey, txOptions));
|
|
5489
|
+
} catch {
|
|
5490
|
+
estimatedFees = undefined;
|
|
5491
|
+
}
|
|
5492
|
+
if (isJsonOutput(opts)) {
|
|
5493
|
+
const result2 = {
|
|
5494
|
+
chain: chainName,
|
|
5495
|
+
from: { name: opts.from, address: signerAddress },
|
|
5496
|
+
callHex,
|
|
5497
|
+
decoded: decodedStr,
|
|
5498
|
+
estimatedFees
|
|
5499
|
+
};
|
|
5500
|
+
if (nonce !== undefined)
|
|
5501
|
+
result2.nonce = nonce;
|
|
5502
|
+
if (tip !== undefined)
|
|
5503
|
+
result2.tip = String(tip);
|
|
5504
|
+
if (asset !== undefined)
|
|
5505
|
+
result2.asset = asset;
|
|
5506
|
+
if (mortality !== undefined)
|
|
5507
|
+
result2.mortality = mortality.mortal ? `mortal (period ${mortality.period})` : "immortal";
|
|
5508
|
+
if (at !== undefined)
|
|
5509
|
+
result2.at = at;
|
|
5510
|
+
console.log(formatJson(result2));
|
|
5511
|
+
return;
|
|
5512
|
+
}
|
|
4395
5513
|
console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
|
|
4396
5514
|
console.log(` ${BOLD}From:${RESET} ${opts.from} (${signerAddress})`);
|
|
4397
5515
|
console.log(` ${BOLD}Call:${RESET} ${callHex}`);
|
|
@@ -4400,20 +5518,147 @@ async function handleTx(target, args, opts) {
|
|
|
4400
5518
|
console.log(` ${BOLD}Nonce:${RESET} ${nonce}`);
|
|
4401
5519
|
if (tip !== undefined)
|
|
4402
5520
|
console.log(` ${BOLD}Tip:${RESET} ${tip}`);
|
|
5521
|
+
if (asset !== undefined)
|
|
5522
|
+
console.log(` ${BOLD}Asset:${RESET} ${JSON.stringify(asset)}`);
|
|
4403
5523
|
if (mortality !== undefined)
|
|
4404
5524
|
console.log(` ${BOLD}Mortality:${RESET} ${mortality.mortal ? `mortal (period ${mortality.period})` : "immortal"}`);
|
|
4405
5525
|
if (at !== undefined)
|
|
4406
5526
|
console.log(` ${BOLD}At:${RESET} ${at}`);
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
} catch (err) {
|
|
5527
|
+
if (estimatedFees !== undefined) {
|
|
5528
|
+
console.log(` ${BOLD}Estimated fees:${RESET} ${estimatedFees}`);
|
|
5529
|
+
} else {
|
|
4411
5530
|
console.log(` ${BOLD}Estimated fees:${RESET} ${YELLOW}unable to estimate${RESET}`);
|
|
4412
|
-
console.log(` ${DIM}${err.message ?? err}${RESET}`);
|
|
4413
5531
|
}
|
|
4414
5532
|
return;
|
|
4415
5533
|
}
|
|
4416
5534
|
const waitLevel = parseWaitLevel(opts.wait);
|
|
5535
|
+
if (opts.unsigned) {
|
|
5536
|
+
const callDataBytes = await tx.getEncodedData();
|
|
5537
|
+
const userExtOverrides = parseExtOption(opts.ext);
|
|
5538
|
+
const generalTx = buildGeneralTx(meta, callDataBytes, userExtOverrides);
|
|
5539
|
+
if (opts.encode) {
|
|
5540
|
+
const hex = Binary3.toHex(generalTx);
|
|
5541
|
+
if (isJsonOutput(opts)) {
|
|
5542
|
+
console.log(formatJson({ generalTxHex: hex }));
|
|
5543
|
+
} else {
|
|
5544
|
+
console.log(hex);
|
|
5545
|
+
}
|
|
5546
|
+
return;
|
|
5547
|
+
}
|
|
5548
|
+
const observable = clientHandle.client.submitAndWatch(generalTx, at);
|
|
5549
|
+
if (isJsonOutput(opts)) {
|
|
5550
|
+
const result3 = await watchTransactionJson(observable, waitLevel, { unsigned: true });
|
|
5551
|
+
const rpcUrl3 = primaryRpc(opts.rpc ?? chainConfig.rpc);
|
|
5552
|
+
if (result3.type === "broadcasted") {
|
|
5553
|
+
printJsonLine({ event: "broadcasted", txHash: result3.txHash });
|
|
5554
|
+
return;
|
|
5555
|
+
}
|
|
5556
|
+
const blockHash = result3.block.hash;
|
|
5557
|
+
const explorer = {};
|
|
5558
|
+
if (rpcUrl3) {
|
|
5559
|
+
explorer.polkadotjs = pjsAppsLink(rpcUrl3, blockHash);
|
|
5560
|
+
explorer.papi = papiLink(rpcUrl3, blockHash);
|
|
5561
|
+
}
|
|
5562
|
+
printJsonLine({
|
|
5563
|
+
event: result3.type === "finalized" ? "finalized" : "bestBlock",
|
|
5564
|
+
unsigned: true,
|
|
5565
|
+
blockNumber: result3.block.number,
|
|
5566
|
+
blockHash,
|
|
5567
|
+
txHash: result3.txHash,
|
|
5568
|
+
ok: result3.ok,
|
|
5569
|
+
events: result3.events?.map((e) => ({
|
|
5570
|
+
pallet: e.type,
|
|
5571
|
+
name: e.value?.type,
|
|
5572
|
+
fields: e.value?.value
|
|
5573
|
+
})),
|
|
5574
|
+
dispatchError: result3.ok ? null : formatDispatchError(result3.dispatchError),
|
|
5575
|
+
explorer
|
|
5576
|
+
});
|
|
5577
|
+
if (!result3.ok) {
|
|
5578
|
+
throw new CliError(`Transaction dispatch error: ${formatDispatchError(result3.dispatchError)}`);
|
|
5579
|
+
}
|
|
5580
|
+
return;
|
|
5581
|
+
}
|
|
5582
|
+
const result2 = await watchTransaction(observable, waitLevel, { unsigned: true });
|
|
5583
|
+
console.log();
|
|
5584
|
+
console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
|
|
5585
|
+
console.log(` ${BOLD}Type:${RESET} unsigned (bare)`);
|
|
5586
|
+
console.log(` ${BOLD}Call:${RESET} ${callHex}`);
|
|
5587
|
+
console.log(` ${BOLD}Decode:${RESET} ${decodedStr}`);
|
|
5588
|
+
console.log(` ${BOLD}Tx:${RESET} ${result2.txHash}`);
|
|
5589
|
+
if (result2.type === "broadcasted") {
|
|
5590
|
+
console.log(` ${BOLD}Status:${RESET} ${GREEN}broadcasted${RESET}`);
|
|
5591
|
+
console.log(` ${DIM}Note: tx was broadcast but not yet included in a block${RESET}`);
|
|
5592
|
+
console.log();
|
|
5593
|
+
return;
|
|
5594
|
+
}
|
|
5595
|
+
let dispatchErrorMsg2;
|
|
5596
|
+
if (result2.ok) {
|
|
5597
|
+
const hint = result2.type === "txBestBlocksState" ? ` ${DIM}(best block, not yet finalized)${RESET}` : "";
|
|
5598
|
+
console.log(` ${BOLD}Status:${RESET} ${GREEN}ok${RESET}${hint}`);
|
|
5599
|
+
} else {
|
|
5600
|
+
dispatchErrorMsg2 = formatDispatchError(result2.dispatchError);
|
|
5601
|
+
console.log(` ${BOLD}Status:${RESET} ${RED}dispatch error${RESET}`);
|
|
5602
|
+
console.log(` ${BOLD}Error:${RESET} ${dispatchErrorMsg2}`);
|
|
5603
|
+
}
|
|
5604
|
+
if (result2.events && result2.events.length > 0) {
|
|
5605
|
+
console.log(` ${BOLD}Events:${RESET}`);
|
|
5606
|
+
for (const event of result2.events) {
|
|
5607
|
+
const name = `${CYAN}${event.type}${RESET}.${CYAN}${event.value?.type ?? ""}${RESET}`;
|
|
5608
|
+
const payload = event.value?.value;
|
|
5609
|
+
if (payload && typeof payload === "object") {
|
|
5610
|
+
const fields = Object.entries(payload).map(([k, v]) => `${k}: ${formatEventValue(v)}`).join(", ");
|
|
5611
|
+
console.log(` ${name} { ${fields} }`);
|
|
5612
|
+
} else {
|
|
5613
|
+
console.log(` ${name}`);
|
|
5614
|
+
}
|
|
5615
|
+
}
|
|
5616
|
+
}
|
|
5617
|
+
const rpcUrl2 = primaryRpc(opts.rpc ?? chainConfig.rpc);
|
|
5618
|
+
if (rpcUrl2) {
|
|
5619
|
+
const blockHash = result2.block.hash;
|
|
5620
|
+
console.log(` ${BOLD}Block:${RESET} #${result2.block.number} (${blockHash})`);
|
|
5621
|
+
console.log(` ${DIM}${pjsAppsLink(rpcUrl2, blockHash)}${RESET}`);
|
|
5622
|
+
console.log(` ${DIM}${papiLink(rpcUrl2, blockHash)}${RESET}`);
|
|
5623
|
+
}
|
|
5624
|
+
console.log();
|
|
5625
|
+
if (!result2.ok) {
|
|
5626
|
+
throw new CliError(`Transaction dispatch error: ${dispatchErrorMsg2}`);
|
|
5627
|
+
}
|
|
5628
|
+
return;
|
|
5629
|
+
}
|
|
5630
|
+
if (isJsonOutput(opts)) {
|
|
5631
|
+
const result2 = await watchTransactionJson(tx.signSubmitAndWatch(signer, txOptions), waitLevel);
|
|
5632
|
+
const rpcUrl2 = primaryRpc(opts.rpc ?? chainConfig.rpc);
|
|
5633
|
+
if (result2.type === "broadcasted") {
|
|
5634
|
+
printJsonLine({ event: "broadcasted", txHash: result2.txHash });
|
|
5635
|
+
return;
|
|
5636
|
+
}
|
|
5637
|
+
const blockHash = result2.block.hash;
|
|
5638
|
+
const explorer = {};
|
|
5639
|
+
if (rpcUrl2) {
|
|
5640
|
+
explorer.polkadotjs = pjsAppsLink(rpcUrl2, blockHash);
|
|
5641
|
+
explorer.papi = papiLink(rpcUrl2, blockHash);
|
|
5642
|
+
}
|
|
5643
|
+
printJsonLine({
|
|
5644
|
+
event: result2.type === "finalized" ? "finalized" : "bestBlock",
|
|
5645
|
+
blockNumber: result2.block.number,
|
|
5646
|
+
blockHash,
|
|
5647
|
+
txHash: result2.txHash,
|
|
5648
|
+
ok: result2.ok,
|
|
5649
|
+
events: result2.events?.map((e) => ({
|
|
5650
|
+
pallet: e.type,
|
|
5651
|
+
name: e.value?.type,
|
|
5652
|
+
fields: e.value?.value
|
|
5653
|
+
})),
|
|
5654
|
+
dispatchError: result2.ok ? null : formatDispatchError(result2.dispatchError),
|
|
5655
|
+
explorer
|
|
5656
|
+
});
|
|
5657
|
+
if (!result2.ok) {
|
|
5658
|
+
throw new CliError(`Transaction dispatch error: ${formatDispatchError(result2.dispatchError)}`);
|
|
5659
|
+
}
|
|
5660
|
+
return;
|
|
5661
|
+
}
|
|
4417
5662
|
const result = await watchTransaction(tx.signSubmitAndWatch(signer, txOptions), waitLevel);
|
|
4418
5663
|
console.log();
|
|
4419
5664
|
console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
|
|
@@ -4423,6 +5668,8 @@ async function handleTx(target, args, opts) {
|
|
|
4423
5668
|
console.log(` ${BOLD}Nonce:${RESET} ${nonce}`);
|
|
4424
5669
|
if (tip !== undefined)
|
|
4425
5670
|
console.log(` ${BOLD}Tip:${RESET} ${tip}`);
|
|
5671
|
+
if (asset !== undefined)
|
|
5672
|
+
console.log(` ${BOLD}Asset:${RESET} ${JSON.stringify(asset)}`);
|
|
4426
5673
|
if (mortality !== undefined)
|
|
4427
5674
|
console.log(` ${BOLD}Mortality:${RESET} ${mortality.mortal ? `mortal (period ${mortality.period})` : "immortal"}`);
|
|
4428
5675
|
if (at !== undefined)
|
|
@@ -4508,7 +5755,7 @@ function decodeCallFallback(meta, callHex) {
|
|
|
4508
5755
|
if (callTypeId == null)
|
|
4509
5756
|
throw new Error("No RuntimeCall type ID");
|
|
4510
5757
|
const codec = meta.builder.buildDefinition(callTypeId);
|
|
4511
|
-
const decoded = codec.dec(Binary3.fromHex(callHex)
|
|
5758
|
+
const decoded = codec.dec(Binary3.fromHex(callHex));
|
|
4512
5759
|
const palletName2 = decoded.type;
|
|
4513
5760
|
const call = decoded.value;
|
|
4514
5761
|
const callName = call.type;
|
|
@@ -4524,7 +5771,7 @@ function decodeCallToFileFormat(meta, callHex, chainName) {
|
|
|
4524
5771
|
if (callTypeId == null)
|
|
4525
5772
|
throw new Error("No RuntimeCall type ID in metadata");
|
|
4526
5773
|
const codec = meta.builder.buildDefinition(callTypeId);
|
|
4527
|
-
const decoded = codec.dec(Binary3.fromHex(callHex)
|
|
5774
|
+
const decoded = codec.dec(Binary3.fromHex(callHex));
|
|
4528
5775
|
const palletName2 = decoded.type;
|
|
4529
5776
|
const call = decoded.value;
|
|
4530
5777
|
const callName = call.type;
|
|
@@ -4541,8 +5788,8 @@ function decodeCallToFileFormat(meta, callHex, chainName) {
|
|
|
4541
5788
|
function sanitizeForSerialization(value) {
|
|
4542
5789
|
if (value === undefined || value === null)
|
|
4543
5790
|
return null;
|
|
4544
|
-
if (value instanceof
|
|
4545
|
-
return
|
|
5791
|
+
if (value instanceof Uint8Array)
|
|
5792
|
+
return Binary3.toHex(value);
|
|
4546
5793
|
if (typeof value === "bigint") {
|
|
4547
5794
|
if (value >= Number.MIN_SAFE_INTEGER && value <= Number.MAX_SAFE_INTEGER) {
|
|
4548
5795
|
return Number(value);
|
|
@@ -4574,8 +5821,8 @@ function outputFileFormat(obj, asYaml) {
|
|
|
4574
5821
|
function formatRawDecoded(value) {
|
|
4575
5822
|
if (value === undefined || value === null)
|
|
4576
5823
|
return "null";
|
|
4577
|
-
if (value instanceof
|
|
4578
|
-
return
|
|
5824
|
+
if (value instanceof Uint8Array)
|
|
5825
|
+
return Binary3.toHex(value);
|
|
4579
5826
|
if (typeof value === "bigint")
|
|
4580
5827
|
return value.toString();
|
|
4581
5828
|
if (typeof value === "string")
|
|
@@ -4696,7 +5943,7 @@ function formatEventValue(v) {
|
|
|
4696
5943
|
return v.toString();
|
|
4697
5944
|
if (v === null || v === undefined)
|
|
4698
5945
|
return "null";
|
|
4699
|
-
if (v instanceof
|
|
5946
|
+
if (v instanceof Uint8Array) {
|
|
4700
5947
|
return binaryToDisplay(v);
|
|
4701
5948
|
}
|
|
4702
5949
|
return JSON.stringify(v, (_k, val) => typeof val === "bigint" ? val.toString() : val);
|
|
@@ -4970,7 +6217,7 @@ async function parseTypedArg2(meta, entry, arg) {
|
|
|
4970
6217
|
case "enum": {
|
|
4971
6218
|
if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
|
|
4972
6219
|
const callCodec = meta.builder.buildDefinition(meta.lookup.call);
|
|
4973
|
-
return callCodec.dec(Binary3.fromHex(arg)
|
|
6220
|
+
return callCodec.dec(Binary3.fromHex(arg));
|
|
4974
6221
|
}
|
|
4975
6222
|
if (arg.startsWith("{")) {
|
|
4976
6223
|
try {
|
|
@@ -5104,11 +6351,11 @@ function parseExtOption(ext) {
|
|
|
5104
6351
|
}
|
|
5105
6352
|
}
|
|
5106
6353
|
var NO_DEFAULT2 = Symbol("no-default");
|
|
5107
|
-
function buildCustomSignedExtensions(meta, userOverrides) {
|
|
6354
|
+
function buildCustomSignedExtensions(meta, userOverrides, builtins = PAPI_BUILTIN_EXTENSIONS2) {
|
|
5108
6355
|
const result = {};
|
|
5109
6356
|
const extensions = getSignedExtensions(meta);
|
|
5110
6357
|
for (const ext of extensions) {
|
|
5111
|
-
if (
|
|
6358
|
+
if (builtins.has(ext.identifier))
|
|
5112
6359
|
continue;
|
|
5113
6360
|
if (ext.identifier in userOverrides) {
|
|
5114
6361
|
result[ext.identifier] = userOverrides[ext.identifier];
|
|
@@ -5140,20 +6387,114 @@ function autoDefaultForType(entry) {
|
|
|
5140
6387
|
}
|
|
5141
6388
|
return NO_DEFAULT2;
|
|
5142
6389
|
}
|
|
5143
|
-
function
|
|
6390
|
+
function unsignedDefaultForType(identifier, entry) {
|
|
6391
|
+
switch (identifier) {
|
|
6392
|
+
case "CheckMortality":
|
|
6393
|
+
return { type: "Immortal" };
|
|
6394
|
+
case "CheckNonce":
|
|
6395
|
+
return 0;
|
|
6396
|
+
case "ChargeTransactionPayment":
|
|
6397
|
+
return 0n;
|
|
6398
|
+
case "ChargeAssetTxPayment":
|
|
6399
|
+
return { tip: 0n, asset_id: undefined };
|
|
6400
|
+
}
|
|
6401
|
+
const auto = autoDefaultForType(entry);
|
|
6402
|
+
if (auto !== NO_DEFAULT2)
|
|
6403
|
+
return auto;
|
|
6404
|
+
if (entry.type === "primitive") {
|
|
6405
|
+
switch (entry.value) {
|
|
6406
|
+
case "bool":
|
|
6407
|
+
return false;
|
|
6408
|
+
case "u8":
|
|
6409
|
+
case "u16":
|
|
6410
|
+
case "u32":
|
|
6411
|
+
case "i8":
|
|
6412
|
+
case "i16":
|
|
6413
|
+
case "i32":
|
|
6414
|
+
return 0;
|
|
6415
|
+
case "u64":
|
|
6416
|
+
case "u128":
|
|
6417
|
+
case "u256":
|
|
6418
|
+
case "i64":
|
|
6419
|
+
case "i128":
|
|
6420
|
+
case "i256":
|
|
6421
|
+
return 0n;
|
|
6422
|
+
case "str":
|
|
6423
|
+
case "char":
|
|
6424
|
+
return "";
|
|
6425
|
+
}
|
|
6426
|
+
}
|
|
6427
|
+
if (entry.type === "compact")
|
|
6428
|
+
return 0;
|
|
6429
|
+
if (entry.type === "enum") {
|
|
6430
|
+
const variants = entry.value;
|
|
6431
|
+
if ("Immortal" in variants)
|
|
6432
|
+
return { type: "Immortal" };
|
|
6433
|
+
for (const [name, variant] of Object.entries(variants)) {
|
|
6434
|
+
if (variant.type === "void")
|
|
6435
|
+
return { type: name };
|
|
6436
|
+
}
|
|
6437
|
+
}
|
|
6438
|
+
return NO_DEFAULT2;
|
|
6439
|
+
}
|
|
6440
|
+
function buildGeneralTx(meta, callData, userExtOverrides) {
|
|
6441
|
+
const extensions = getSignedExtensions(meta);
|
|
6442
|
+
const extBytes = [];
|
|
6443
|
+
for (const ext of extensions) {
|
|
6444
|
+
const valueEntry = meta.lookup(ext.type);
|
|
6445
|
+
if (valueEntry.type === "void")
|
|
6446
|
+
continue;
|
|
6447
|
+
let value;
|
|
6448
|
+
if (ext.identifier in userExtOverrides) {
|
|
6449
|
+
const override = userExtOverrides[ext.identifier];
|
|
6450
|
+
value = override.value !== undefined ? override.value : override;
|
|
6451
|
+
} else {
|
|
6452
|
+
value = unsignedDefaultForType(ext.identifier, valueEntry);
|
|
6453
|
+
if (value === NO_DEFAULT2) {
|
|
6454
|
+
throw new CliError(`Cannot determine default unsigned value for extension "${ext.identifier}" ` + `(type: ${valueEntry.type}). Provide it via --ext '{"${ext.identifier}":{"value":...}}'`);
|
|
6455
|
+
}
|
|
6456
|
+
}
|
|
6457
|
+
const codec = meta.builder.buildDefinition(ext.type);
|
|
6458
|
+
extBytes.push(codec.enc(value));
|
|
6459
|
+
}
|
|
6460
|
+
const extVersion = new Uint8Array([0]);
|
|
6461
|
+
const versionByte = new Uint8Array([69]);
|
|
6462
|
+
let payloadLen = 1 + 1;
|
|
6463
|
+
for (const b of extBytes)
|
|
6464
|
+
payloadLen += b.length;
|
|
6465
|
+
payloadLen += callData.length;
|
|
6466
|
+
const lengthPrefix = scaleCompact2.enc(payloadLen);
|
|
6467
|
+
const total = new Uint8Array(lengthPrefix.length + payloadLen);
|
|
6468
|
+
let offset = 0;
|
|
6469
|
+
total.set(lengthPrefix, offset);
|
|
6470
|
+
offset += lengthPrefix.length;
|
|
6471
|
+
total.set(versionByte, offset);
|
|
6472
|
+
offset += 1;
|
|
6473
|
+
total.set(extVersion, offset);
|
|
6474
|
+
offset += 1;
|
|
6475
|
+
for (const b of extBytes) {
|
|
6476
|
+
total.set(b, offset);
|
|
6477
|
+
offset += b.length;
|
|
6478
|
+
}
|
|
6479
|
+
total.set(callData, offset);
|
|
6480
|
+
return total;
|
|
6481
|
+
}
|
|
6482
|
+
function watchTransaction(observable, level, options) {
|
|
5144
6483
|
const spinner = new Spinner;
|
|
5145
6484
|
return new Promise((resolve, reject) => {
|
|
5146
6485
|
let settled = false;
|
|
5147
|
-
spinner.start("Signing...");
|
|
6486
|
+
spinner.start(options?.unsigned ? "Submitting..." : "Signing...");
|
|
5148
6487
|
const subscription = observable.subscribe({
|
|
5149
6488
|
next(event) {
|
|
5150
6489
|
if (settled)
|
|
5151
6490
|
return;
|
|
5152
6491
|
switch (event.type) {
|
|
5153
6492
|
case "signed":
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
6493
|
+
if (!options?.unsigned) {
|
|
6494
|
+
spinner.succeed("Signed");
|
|
6495
|
+
console.log(` ${BOLD}Tx:${RESET} ${event.txHash}`);
|
|
6496
|
+
spinner.start("Broadcasting...");
|
|
6497
|
+
}
|
|
5157
6498
|
break;
|
|
5158
6499
|
case "broadcasted":
|
|
5159
6500
|
if (level === "broadcast") {
|
|
@@ -5197,6 +6538,51 @@ function watchTransaction(observable, level) {
|
|
|
5197
6538
|
});
|
|
5198
6539
|
});
|
|
5199
6540
|
}
|
|
6541
|
+
function watchTransactionJson(observable, level, options) {
|
|
6542
|
+
return new Promise((resolve, reject) => {
|
|
6543
|
+
let settled = false;
|
|
6544
|
+
const subscription = observable.subscribe({
|
|
6545
|
+
next(event) {
|
|
6546
|
+
if (settled)
|
|
6547
|
+
return;
|
|
6548
|
+
switch (event.type) {
|
|
6549
|
+
case "signed":
|
|
6550
|
+
if (!options?.unsigned) {
|
|
6551
|
+
printJsonLine({ event: "signed", txHash: event.txHash });
|
|
6552
|
+
}
|
|
6553
|
+
break;
|
|
6554
|
+
case "broadcasted":
|
|
6555
|
+
printJsonLine({ event: "broadcasted", txHash: event.txHash });
|
|
6556
|
+
if (level === "broadcast") {
|
|
6557
|
+
settled = true;
|
|
6558
|
+
subscription.unsubscribe();
|
|
6559
|
+
resolve(event);
|
|
6560
|
+
}
|
|
6561
|
+
break;
|
|
6562
|
+
case "txBestBlocksState":
|
|
6563
|
+
if (event.found) {
|
|
6564
|
+
printJsonLine({ event: "bestBlock", blockNumber: event.block.number, found: true });
|
|
6565
|
+
if (level === "best-block") {
|
|
6566
|
+
settled = true;
|
|
6567
|
+
subscription.unsubscribe();
|
|
6568
|
+
resolve(event);
|
|
6569
|
+
}
|
|
6570
|
+
}
|
|
6571
|
+
break;
|
|
6572
|
+
case "finalized":
|
|
6573
|
+
settled = true;
|
|
6574
|
+
resolve(event);
|
|
6575
|
+
break;
|
|
6576
|
+
}
|
|
6577
|
+
},
|
|
6578
|
+
error(err) {
|
|
6579
|
+
if (settled)
|
|
6580
|
+
return;
|
|
6581
|
+
reject(err);
|
|
6582
|
+
}
|
|
6583
|
+
});
|
|
6584
|
+
});
|
|
6585
|
+
}
|
|
5200
6586
|
|
|
5201
6587
|
// src/commands/verifiable.ts
|
|
5202
6588
|
init_accounts_store();
|
|
@@ -5253,7 +6639,7 @@ async function deriveVerifiable(account, opts) {
|
|
|
5253
6639
|
await saveAccounts(accountsFile);
|
|
5254
6640
|
}
|
|
5255
6641
|
}
|
|
5256
|
-
if (opts
|
|
6642
|
+
if (isJsonOutput(opts)) {
|
|
5257
6643
|
const result = {
|
|
5258
6644
|
account,
|
|
5259
6645
|
memberKey: memberKeyHex
|
|
@@ -5277,8 +6663,14 @@ async function resolveMnemonic(account) {
|
|
|
5277
6663
|
const accountsFile = await loadAccounts();
|
|
5278
6664
|
const stored = findAccount(accountsFile, account);
|
|
5279
6665
|
if (!stored) {
|
|
5280
|
-
const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)];
|
|
5281
|
-
|
|
6666
|
+
const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
|
|
6667
|
+
const suggestions = findClosest(account, available);
|
|
6668
|
+
const hint = suggestions.length > 0 ? `
|
|
6669
|
+
Did you mean: ${suggestions.join(", ")}?` : "";
|
|
6670
|
+
const list = available.map((a) => `
|
|
6671
|
+
- ${a}`).join("");
|
|
6672
|
+
throw new Error(`Unknown account "${account}".${hint}
|
|
6673
|
+
Available accounts:${list}`);
|
|
5282
6674
|
}
|
|
5283
6675
|
if (isWatchOnly(stored)) {
|
|
5284
6676
|
throw new Error(`Account "${account}" is watch-only (no secret). Cannot derive Bandersnatch key.`);
|
|
@@ -5291,8 +6683,9 @@ async function resolveMnemonic(account) {
|
|
|
5291
6683
|
}
|
|
5292
6684
|
|
|
5293
6685
|
// src/config/store.ts
|
|
6686
|
+
init_errors();
|
|
5294
6687
|
init_types();
|
|
5295
|
-
import { access as access3, mkdir as mkdir3, readFile as
|
|
6688
|
+
import { access as access3, mkdir as mkdir3, readFile as readFile7, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
|
|
5296
6689
|
import { homedir as homedir2 } from "node:os";
|
|
5297
6690
|
import { join as join3 } from "node:path";
|
|
5298
6691
|
var DOT_DIR2 = join3(homedir2(), ".polkadot");
|
|
@@ -5312,24 +6705,30 @@ async function fileExists3(path) {
|
|
|
5312
6705
|
async function loadConfig2() {
|
|
5313
6706
|
await ensureDir3(DOT_DIR2);
|
|
5314
6707
|
if (await fileExists3(CONFIG_PATH2)) {
|
|
5315
|
-
const saved = JSON.parse(await
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
chains
|
|
5319
|
-
}
|
|
6708
|
+
const saved = JSON.parse(await readFile7(CONFIG_PATH2, "utf-8"));
|
|
6709
|
+
const chains = {};
|
|
6710
|
+
for (const [name, defaultConfig] of Object.entries(DEFAULT_CONFIG.chains)) {
|
|
6711
|
+
chains[name] = saved.chains[name] ? { ...defaultConfig, ...saved.chains[name] } : defaultConfig;
|
|
6712
|
+
}
|
|
6713
|
+
for (const [name, config] of Object.entries(saved.chains)) {
|
|
6714
|
+
if (!(name in DEFAULT_CONFIG.chains)) {
|
|
6715
|
+
chains[name] = config;
|
|
6716
|
+
}
|
|
6717
|
+
}
|
|
6718
|
+
return { chains };
|
|
5320
6719
|
}
|
|
5321
6720
|
await saveConfig2(DEFAULT_CONFIG);
|
|
5322
6721
|
return DEFAULT_CONFIG;
|
|
5323
6722
|
}
|
|
5324
6723
|
async function saveConfig2(config) {
|
|
5325
6724
|
await ensureDir3(DOT_DIR2);
|
|
5326
|
-
await
|
|
6725
|
+
await writeFile5(CONFIG_PATH2, `${JSON.stringify(config, null, 2)}
|
|
5327
6726
|
`);
|
|
5328
6727
|
}
|
|
5329
6728
|
|
|
5330
6729
|
// src/core/file-loader.ts
|
|
5331
6730
|
init_errors();
|
|
5332
|
-
import { access as access4, readFile as
|
|
6731
|
+
import { access as access4, readFile as readFile8 } from "node:fs/promises";
|
|
5333
6732
|
import { parse as parseYaml } from "yaml";
|
|
5334
6733
|
var CATEGORIES = ["tx", "query", "const", "apis"];
|
|
5335
6734
|
var FILE_EXTENSIONS = [".json", ".yaml", ".yml"];
|
|
@@ -5394,7 +6793,7 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
5394
6793
|
} catch {
|
|
5395
6794
|
throw new CliError(`File not found: ${filePath}`);
|
|
5396
6795
|
}
|
|
5397
|
-
const rawText = await
|
|
6796
|
+
const rawText = await readFile8(filePath, "utf-8");
|
|
5398
6797
|
if (!rawText.trim()) {
|
|
5399
6798
|
throw new CliError(`File is empty: ${filePath}`);
|
|
5400
6799
|
}
|
|
@@ -5427,6 +6826,7 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
5427
6826
|
}
|
|
5428
6827
|
const doc = parsed;
|
|
5429
6828
|
const chain = doc.chain != null ? String(doc.chain) : undefined;
|
|
6829
|
+
const unsigned = doc.unsigned === true ? true : undefined;
|
|
5430
6830
|
const foundCategories = CATEGORIES.filter((c) => (c in doc));
|
|
5431
6831
|
if (foundCategories.length === 0) {
|
|
5432
6832
|
throw new CliError(`File "${filePath}" must contain exactly one category key: ${CATEGORIES.join(", ")}. None found.`);
|
|
@@ -5457,14 +6857,15 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
5457
6857
|
category,
|
|
5458
6858
|
pallet,
|
|
5459
6859
|
item,
|
|
5460
|
-
args: args ?? undefined
|
|
6860
|
+
args: args ?? undefined,
|
|
6861
|
+
unsigned
|
|
5461
6862
|
};
|
|
5462
6863
|
}
|
|
5463
6864
|
|
|
5464
6865
|
// src/core/update-notifier.ts
|
|
5465
6866
|
init_store();
|
|
5466
6867
|
import { readFileSync } from "node:fs";
|
|
5467
|
-
import { mkdir as mkdir4, writeFile as
|
|
6868
|
+
import { mkdir as mkdir4, writeFile as writeFile6 } from "node:fs/promises";
|
|
5468
6869
|
import { join as join4 } from "node:path";
|
|
5469
6870
|
var CACHE_FILE = "update-check.json";
|
|
5470
6871
|
var STALE_MS = 24 * 60 * 60 * 1000;
|
|
@@ -5534,7 +6935,7 @@ function readCache() {
|
|
|
5534
6935
|
async function writeCache(cache) {
|
|
5535
6936
|
try {
|
|
5536
6937
|
await mkdir4(getConfigDir(), { recursive: true });
|
|
5537
|
-
await
|
|
6938
|
+
await writeFile6(getCachePath(), `${JSON.stringify(cache)}
|
|
5538
6939
|
`);
|
|
5539
6940
|
} catch {}
|
|
5540
6941
|
}
|
|
@@ -5702,13 +7103,15 @@ if (process.argv[2] === "__complete") {
|
|
|
5702
7103
|
console.log(" dot query.System.Account <addr> Query a storage item");
|
|
5703
7104
|
console.log(" dot query.System List storage items in System");
|
|
5704
7105
|
console.log(" dot tx.System.remark 0xdead --from alice");
|
|
7106
|
+
console.log(" dot tx.System.remark 0xdead --unsigned Submit unsigned/bare tx");
|
|
5705
7107
|
console.log(" dot const.Balances.ExistentialDeposit");
|
|
5706
7108
|
console.log(" dot events.Balances List events in Balances");
|
|
5707
7109
|
console.log(" dot apis.Core.version Call a runtime API");
|
|
5708
7110
|
console.log(" dot polkadot.query.System.Number With chain prefix");
|
|
5709
7111
|
console.log(" dot ./transfer.yaml --from alice Run from file");
|
|
5710
|
-
console.log(" dot tx.0x1f0003... --yaml
|
|
5711
|
-
console.log(" dot tx.System.remark 0xdead --json
|
|
7112
|
+
console.log(" dot tx.0x1f0003... --to-yaml Decode hex call to YAML");
|
|
7113
|
+
console.log(" dot tx.System.remark 0xdead --to-json Encode & output as JSON file format");
|
|
7114
|
+
console.log(" dot query.System.Number --json Output as JSON");
|
|
5712
7115
|
console.log();
|
|
5713
7116
|
console.log("Commands:");
|
|
5714
7117
|
console.log(" inspect [target] Inspect chain metadata (alias: explore)");
|
|
@@ -5721,21 +7124,21 @@ if (process.argv[2] === "__complete") {
|
|
|
5721
7124
|
console.log(" completions <sh> Generate shell completions (zsh, bash, fish)");
|
|
5722
7125
|
console.log();
|
|
5723
7126
|
console.log("Global options:");
|
|
5724
|
-
console.log(" --chain <name> Target chain (
|
|
7127
|
+
console.log(" --chain <name> Target chain (required)");
|
|
5725
7128
|
console.log(" --rpc <url> Override RPC endpoint");
|
|
5726
|
-
console.log(" --
|
|
7129
|
+
console.log(" --json Output as JSON");
|
|
5727
7130
|
console.log(" --output <format> Output format: pretty or json");
|
|
5728
7131
|
console.log(" --help, -h Display this message");
|
|
5729
7132
|
console.log(" --version Show version");
|
|
5730
7133
|
};
|
|
5731
7134
|
startBackgroundCheck(version);
|
|
5732
7135
|
const cli = cac("dot");
|
|
5733
|
-
cli.option("--chain <name>", "Target chain (
|
|
7136
|
+
cli.option("--chain <name>", "Target chain (required)");
|
|
5734
7137
|
cli.option("--rpc <url>", "Override RPC endpoint for this call");
|
|
5735
|
-
cli.option("--light-client", "Use Smoldot light client instead of WebSocket");
|
|
5736
7138
|
cli.option("--output <format>", "Output format: pretty or json", {
|
|
5737
7139
|
default: "pretty"
|
|
5738
7140
|
});
|
|
7141
|
+
cli.option("--json", "Output as JSON (shorthand for --output json)");
|
|
5739
7142
|
registerChainCommands(cli);
|
|
5740
7143
|
registerInspectCommand(cli);
|
|
5741
7144
|
registerAccountCommands(cli);
|
|
@@ -5744,9 +7147,9 @@ if (process.argv[2] === "__complete") {
|
|
|
5744
7147
|
registerParachainCommand(cli);
|
|
5745
7148
|
registerCompletionsCommand(cli);
|
|
5746
7149
|
registerVerifiableCommands(cli);
|
|
5747
|
-
cli.command("[dotpath] [...args]").option("--from <name>", "Account to sign with (for tx)").option("--dry-run", "Estimate fees without submitting (for tx)").option("--encode", "Encode call to hex without signing (for tx)").option("--yaml", "Decode call to YAML file format (for tx)").option("--json", "Decode call to JSON file format (for tx)").option("--ext <json>", "Custom signed extension values as JSON (for tx)").option("-w, --wait <level>", "Resolve at: broadcast, best-block (or best), finalized (for tx)", {
|
|
7150
|
+
cli.command("[dotpath] [...args]").option("--from <name>", "Account to sign with (for tx)").option("--dry-run", "Estimate fees without submitting (for tx)").option("--encode", "Encode call to hex without signing (for tx)").option("--to-yaml", "Decode call to YAML file format (for tx)").option("--to-json", "Decode call to JSON file format (for tx)").option("--ext <json>", "Custom signed extension values as JSON (for tx)").option("--asset <json>", "Pay fees in an alternative asset (XCM location JSON, for tx)").option("-w, --wait <level>", "Resolve at: broadcast, best-block (or best), finalized (for tx)", {
|
|
5748
7151
|
default: "finalized"
|
|
5749
|
-
}).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) => {
|
|
7152
|
+
}).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)').option("--unsigned", "Submit as unsigned/bare transaction (no signer required, for tx)").action(async (dotpath, args, opts) => {
|
|
5750
7153
|
if (!dotpath) {
|
|
5751
7154
|
printHelp();
|
|
5752
7155
|
return;
|
|
@@ -5755,18 +7158,25 @@ if (process.argv[2] === "__complete") {
|
|
|
5755
7158
|
const cliVars = collectVarFlags(process.argv);
|
|
5756
7159
|
const cmd = await loadCommandFile(dotpath, cliVars);
|
|
5757
7160
|
const effectiveChain2 = opts.chain ?? cmd.chain;
|
|
5758
|
-
const handlerOpts2 = {
|
|
7161
|
+
const handlerOpts2 = {
|
|
7162
|
+
chain: effectiveChain2,
|
|
7163
|
+
rpc: opts.rpc,
|
|
7164
|
+
output: opts.output,
|
|
7165
|
+
json: opts.json
|
|
7166
|
+
};
|
|
5759
7167
|
const target2 = `${cmd.pallet}.${cmd.item}`;
|
|
5760
7168
|
switch (cmd.category) {
|
|
5761
7169
|
case "tx":
|
|
5762
7170
|
await handleTx(target2, args, {
|
|
5763
7171
|
...handlerOpts2,
|
|
5764
7172
|
from: opts.from,
|
|
7173
|
+
unsigned: opts.unsigned ?? cmd.unsigned,
|
|
5765
7174
|
dryRun: opts.dryRun,
|
|
5766
7175
|
encode: opts.encode,
|
|
5767
|
-
|
|
5768
|
-
|
|
7176
|
+
toYaml: opts.toYaml,
|
|
7177
|
+
toJson: opts.toJson,
|
|
5769
7178
|
ext: opts.ext,
|
|
7179
|
+
asset: opts.asset,
|
|
5770
7180
|
wait: opts.wait,
|
|
5771
7181
|
nonce: opts.nonce,
|
|
5772
7182
|
tip: opts.tip,
|
|
@@ -5812,7 +7222,12 @@ if (process.argv[2] === "__complete") {
|
|
|
5812
7222
|
throw new CliError2(`Chain specified both as prefix ("${parsed.chain}") and as --chain flag ("${opts.chain}"). Use one or the other.`);
|
|
5813
7223
|
}
|
|
5814
7224
|
const effectiveChain = parsed.chain ?? opts.chain;
|
|
5815
|
-
const handlerOpts = {
|
|
7225
|
+
const handlerOpts = {
|
|
7226
|
+
chain: effectiveChain,
|
|
7227
|
+
rpc: opts.rpc,
|
|
7228
|
+
output: opts.output,
|
|
7229
|
+
json: opts.json
|
|
7230
|
+
};
|
|
5816
7231
|
const target = parsed.pallet ? parsed.item ? `${parsed.pallet}.${parsed.item}` : parsed.pallet : undefined;
|
|
5817
7232
|
if (cli.options.help && parsed.pallet && parsed.item) {
|
|
5818
7233
|
await showItemHelp2(parsed.category, target, handlerOpts);
|
|
@@ -5826,11 +7241,13 @@ if (process.argv[2] === "__complete") {
|
|
|
5826
7241
|
const txOpts = {
|
|
5827
7242
|
...handlerOpts,
|
|
5828
7243
|
from: opts.from,
|
|
7244
|
+
unsigned: opts.unsigned,
|
|
5829
7245
|
dryRun: opts.dryRun,
|
|
5830
7246
|
encode: opts.encode,
|
|
5831
|
-
|
|
5832
|
-
|
|
7247
|
+
toYaml: opts.toYaml,
|
|
7248
|
+
toJson: opts.toJson,
|
|
5833
7249
|
ext: opts.ext,
|
|
7250
|
+
asset: opts.asset,
|
|
5834
7251
|
wait: opts.wait,
|
|
5835
7252
|
nonce: opts.nonce,
|
|
5836
7253
|
tip: opts.tip,
|