polkadot-cli 0.1.1 → 0.2.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 +45 -1
- package/dist/cli.mjs +618 -34
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# polkadot-cli
|
|
2
2
|
|
|
3
|
-
A command-line tool for
|
|
3
|
+
A command-line tool for interacting with Polkadot-ecosystem chains. Query storage, look up constants, inspect metadata, manage accounts, and submit extrinsics — all from your terminal.
|
|
4
4
|
|
|
5
5
|
Ships with Polkadot as the default chain. Add any Substrate-based chain by pointing to its RPC endpoint.
|
|
6
6
|
|
|
@@ -52,6 +52,45 @@ dot inspect System
|
|
|
52
52
|
dot inspect System.Account
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
### Manage accounts
|
|
56
|
+
|
|
57
|
+
Dev accounts (Alice, Bob, Charlie, Dave, Eve, Ferdie) are always available for testnets. Create or import your own accounts for any chain.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# List all accounts (dev + stored)
|
|
61
|
+
dot account list
|
|
62
|
+
|
|
63
|
+
# Create a new account (generates a mnemonic)
|
|
64
|
+
dot account create my-validator
|
|
65
|
+
|
|
66
|
+
# Import from a BIP39 mnemonic
|
|
67
|
+
dot account import treasury --secret "word1 word2 ... word12"
|
|
68
|
+
|
|
69
|
+
# Import from a hex seed
|
|
70
|
+
dot account import raw-key --secret 0xabcdef...
|
|
71
|
+
|
|
72
|
+
# Remove an account
|
|
73
|
+
dot account remove my-validator
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Submit extrinsics
|
|
77
|
+
|
|
78
|
+
Build, sign, and submit transactions. Arguments are parsed from metadata — the CLI knows the expected types for each call.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Simple remark
|
|
82
|
+
dot tx System.remark 0xdeadbeef --from alice
|
|
83
|
+
|
|
84
|
+
# Transfer (amount in plancks)
|
|
85
|
+
dot tx Balances.transferKeepAlive 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty 1000000000000 --from alice
|
|
86
|
+
|
|
87
|
+
# Estimate fees without submitting
|
|
88
|
+
dot tx Balances.transferKeepAlive 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty 1000000000000 --from alice --dry-run
|
|
89
|
+
|
|
90
|
+
# Batch multiple transfers with Utility.batchAll
|
|
91
|
+
dot tx Utility.batchAll '[{"type":"Balances","value":{"type":"transfer_keep_alive","value":{"dest":"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty","value":1000000000000}}},{"type":"Balances","value":{"type":"transfer_keep_alive","value":{"dest":"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y","value":2000000000000}}}]' --from alice
|
|
92
|
+
```
|
|
93
|
+
|
|
55
94
|
### Manage chains
|
|
56
95
|
|
|
57
96
|
```bash
|
|
@@ -62,6 +101,10 @@ dot chain add westend --light-client
|
|
|
62
101
|
# List configured chains
|
|
63
102
|
dot chain list
|
|
64
103
|
|
|
104
|
+
# Re-fetch metadata after a runtime upgrade
|
|
105
|
+
dot chain update # updates default chain
|
|
106
|
+
dot chain update kusama # updates a specific chain
|
|
107
|
+
|
|
65
108
|
# Set default chain
|
|
66
109
|
dot chain default kusama
|
|
67
110
|
|
|
@@ -86,6 +129,7 @@ Config and metadata caches live in `~/.polkadot/`:
|
|
|
86
129
|
```
|
|
87
130
|
~/.polkadot/
|
|
88
131
|
├── config.json # chains and default chain
|
|
132
|
+
├── accounts.json # stored accounts (secrets encrypted at rest — coming soon)
|
|
89
133
|
└── chains/
|
|
90
134
|
└── polkadot/
|
|
91
135
|
└── metadata.bin # cached SCALE-encoded metadata
|
package/dist/cli.mjs
CHANGED
|
@@ -40,6 +40,9 @@ var DEFAULT_CONFIG = {
|
|
|
40
40
|
var DOT_DIR = join(homedir(), ".polkadot");
|
|
41
41
|
var CONFIG_PATH = join(DOT_DIR, "config.json");
|
|
42
42
|
var CHAINS_DIR = join(DOT_DIR, "chains");
|
|
43
|
+
function getConfigDir() {
|
|
44
|
+
return DOT_DIR;
|
|
45
|
+
}
|
|
43
46
|
function getChainDir(chainName) {
|
|
44
47
|
return join(CHAINS_DIR, chainName);
|
|
45
48
|
}
|
|
@@ -131,17 +134,30 @@ var KNOWN_CHAIN_SPECS = {
|
|
|
131
134
|
westend: "polkadot-api/chains/westend2",
|
|
132
135
|
paseo: "polkadot-api/chains/paseo"
|
|
133
136
|
};
|
|
137
|
+
function suppressWsNoise() {
|
|
138
|
+
const orig = console.error;
|
|
139
|
+
console.error = (...args) => {
|
|
140
|
+
if (typeof args[0] === "string" && args[0].includes("Unable to connect"))
|
|
141
|
+
return;
|
|
142
|
+
orig(...args);
|
|
143
|
+
};
|
|
144
|
+
return () => {
|
|
145
|
+
console.error = orig;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
134
148
|
async function createChainClient(chainName, chainConfig, rpcOverride) {
|
|
135
149
|
const useLight = !rpcOverride && chainConfig.lightClient;
|
|
150
|
+
const restoreConsole = suppressWsNoise();
|
|
136
151
|
let provider;
|
|
137
152
|
if (useLight) {
|
|
138
153
|
provider = await createSmoldotProvider(chainName);
|
|
139
154
|
} else {
|
|
140
155
|
const rpc = rpcOverride ?? chainConfig.rpc;
|
|
141
156
|
if (!rpc) {
|
|
157
|
+
restoreConsole();
|
|
142
158
|
throw new ConnectionError(`No RPC endpoint configured for chain "${chainName}". Use --rpc or configure one with: dot chain add ${chainName} --rpc <url>`);
|
|
143
159
|
}
|
|
144
|
-
provider = withPolkadotSdkCompat(getWsProvider(rpc));
|
|
160
|
+
provider = withPolkadotSdkCompat(getWsProvider(rpc, { timeout: 1e4 }));
|
|
145
161
|
}
|
|
146
162
|
const client = createClient(provider, {
|
|
147
163
|
getMetadata: async () => loadMetadata(chainName),
|
|
@@ -151,7 +167,10 @@ async function createChainClient(chainName, chainConfig, rpcOverride) {
|
|
|
151
167
|
});
|
|
152
168
|
return {
|
|
153
169
|
client,
|
|
154
|
-
destroy: () =>
|
|
170
|
+
destroy: () => {
|
|
171
|
+
client.destroy();
|
|
172
|
+
restoreConsole();
|
|
173
|
+
}
|
|
155
174
|
};
|
|
156
175
|
}
|
|
157
176
|
async function createSmoldotProvider(chainName) {
|
|
@@ -173,6 +192,7 @@ import {
|
|
|
173
192
|
unifyMetadata
|
|
174
193
|
} from "@polkadot-api/substrate-bindings";
|
|
175
194
|
import { getLookupFn, getDynamicBuilder } from "@polkadot-api/metadata-builders";
|
|
195
|
+
var METADATA_TIMEOUT_MS = 15000;
|
|
176
196
|
function parseMetadata(raw) {
|
|
177
197
|
const decoded = decAnyMetadata(raw);
|
|
178
198
|
const unified = unifyMetadata(decoded);
|
|
@@ -182,10 +202,19 @@ function parseMetadata(raw) {
|
|
|
182
202
|
}
|
|
183
203
|
async function fetchMetadataFromChain(clientHandle, chainName) {
|
|
184
204
|
const { client } = clientHandle;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
205
|
+
try {
|
|
206
|
+
const hex = await Promise.race([
|
|
207
|
+
client._request("state_getMetadata", []),
|
|
208
|
+
new Promise((_, reject) => setTimeout(() => reject(new ConnectionError(`Timed out fetching metadata for "${chainName}" after ${METADATA_TIMEOUT_MS / 1000}s. ` + "Check that the RPC endpoint is correct and reachable.")), METADATA_TIMEOUT_MS))
|
|
209
|
+
]);
|
|
210
|
+
const bytes = hexToBytes(hex);
|
|
211
|
+
await saveMetadata(chainName, bytes);
|
|
212
|
+
return bytes;
|
|
213
|
+
} catch (err) {
|
|
214
|
+
if (err instanceof ConnectionError)
|
|
215
|
+
throw err;
|
|
216
|
+
throw new ConnectionError(`Failed to fetch metadata for "${chainName}": ${err instanceof Error ? err.message : err}. ` + "Check that the RPC endpoint is correct and reachable.");
|
|
217
|
+
}
|
|
189
218
|
}
|
|
190
219
|
async function getOrFetchMetadata(chainName, clientHandle) {
|
|
191
220
|
let raw = await loadMetadata(chainName);
|
|
@@ -213,9 +242,35 @@ function listPallets(meta) {
|
|
|
213
242
|
name: c.name,
|
|
214
243
|
docs: c.docs ?? [],
|
|
215
244
|
typeId: c.type
|
|
216
|
-
}))
|
|
245
|
+
})),
|
|
246
|
+
calls: extractCalls(meta, p.calls)
|
|
217
247
|
}));
|
|
218
248
|
}
|
|
249
|
+
function extractCalls(meta, callsRef) {
|
|
250
|
+
if (!callsRef)
|
|
251
|
+
return [];
|
|
252
|
+
try {
|
|
253
|
+
const entry = meta.lookup(callsRef.type);
|
|
254
|
+
if (entry.type !== "enum")
|
|
255
|
+
return [];
|
|
256
|
+
return Object.entries(entry.value).map(([name, variant]) => ({
|
|
257
|
+
name,
|
|
258
|
+
docs: variant.docs ?? [],
|
|
259
|
+
typeId: resolveCallTypeId(variant)
|
|
260
|
+
}));
|
|
261
|
+
} catch {
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function resolveCallTypeId(variant) {
|
|
266
|
+
if (variant.type === "lookupEntry")
|
|
267
|
+
return variant.value?.id ?? null;
|
|
268
|
+
if (variant.type === "struct")
|
|
269
|
+
return null;
|
|
270
|
+
if (variant.type === "void" || variant.type === "empty")
|
|
271
|
+
return null;
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
219
274
|
function findPallet(meta, palletName) {
|
|
220
275
|
const pallets = listPallets(meta);
|
|
221
276
|
return pallets.find((p) => p.name.toLowerCase() === palletName.toLowerCase());
|
|
@@ -333,6 +388,7 @@ ${BOLD}Usage:${RESET}
|
|
|
333
388
|
$ dot chain add <name> --rpc <url> Add a chain via WebSocket RPC
|
|
334
389
|
$ dot chain add <name> --light-client Add a chain via Smoldot light client
|
|
335
390
|
$ dot chain remove <name> Remove a chain
|
|
391
|
+
$ dot chain update [name] Re-fetch metadata (default chain if omitted)
|
|
336
392
|
$ dot chain list List configured chains
|
|
337
393
|
$ dot chain default <name> Set the default chain
|
|
338
394
|
|
|
@@ -341,10 +397,12 @@ ${BOLD}Examples:${RESET}
|
|
|
341
397
|
$ dot chain add westend --light-client
|
|
342
398
|
$ dot chain default kusama
|
|
343
399
|
$ dot chain list
|
|
400
|
+
$ dot chain update
|
|
401
|
+
$ dot chain update kusama
|
|
344
402
|
$ dot chain remove kusama
|
|
345
403
|
`.trimStart();
|
|
346
404
|
function registerChainCommands(cli) {
|
|
347
|
-
cli.command("chain [action] [name]", "Manage chains (add, remove, list, default)").action(async (action, name, opts) => {
|
|
405
|
+
cli.command("chain [action] [name]", "Manage chains (add, remove, update, list, default)").action(async (action, name, opts) => {
|
|
348
406
|
if (!action) {
|
|
349
407
|
console.log(CHAIN_HELP);
|
|
350
408
|
return;
|
|
@@ -356,6 +414,8 @@ function registerChainCommands(cli) {
|
|
|
356
414
|
return chainRemove(name);
|
|
357
415
|
case "list":
|
|
358
416
|
return chainList();
|
|
417
|
+
case "update":
|
|
418
|
+
return chainUpdate(name, opts);
|
|
359
419
|
case "default":
|
|
360
420
|
return chainDefault(name);
|
|
361
421
|
default:
|
|
@@ -381,17 +441,18 @@ async function chainAdd(name, opts) {
|
|
|
381
441
|
console.error(" dot chain add <name> --light-client");
|
|
382
442
|
process.exit(1);
|
|
383
443
|
}
|
|
384
|
-
const
|
|
385
|
-
config.chains[name] = {
|
|
444
|
+
const chainConfig = {
|
|
386
445
|
rpc: opts.rpc ?? "",
|
|
387
446
|
...opts.lightClient ? { lightClient: true } : {}
|
|
388
447
|
};
|
|
389
|
-
await saveConfig(config);
|
|
390
448
|
console.log(`Connecting to ${name}...`);
|
|
391
|
-
const clientHandle = await createChainClient(name,
|
|
449
|
+
const clientHandle = await createChainClient(name, chainConfig, opts.rpc);
|
|
392
450
|
try {
|
|
393
451
|
console.log("Fetching metadata...");
|
|
394
452
|
await fetchMetadataFromChain(clientHandle, name);
|
|
453
|
+
const config = await loadConfig();
|
|
454
|
+
config.chains[name] = chainConfig;
|
|
455
|
+
await saveConfig(config);
|
|
395
456
|
console.log(`Chain "${name}" added successfully.`);
|
|
396
457
|
} finally {
|
|
397
458
|
clientHandle.destroy();
|
|
@@ -429,6 +490,19 @@ async function chainList() {
|
|
|
429
490
|
}
|
|
430
491
|
console.log();
|
|
431
492
|
}
|
|
493
|
+
async function chainUpdate(name, opts) {
|
|
494
|
+
const config = await loadConfig();
|
|
495
|
+
const { name: chainName, chain: chainConfig } = resolveChain(config, name);
|
|
496
|
+
console.log(`Connecting to ${chainName}...`);
|
|
497
|
+
const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
498
|
+
try {
|
|
499
|
+
console.log("Fetching metadata...");
|
|
500
|
+
await fetchMetadataFromChain(clientHandle, chainName);
|
|
501
|
+
console.log(`Metadata for "${chainName}" updated.`);
|
|
502
|
+
} finally {
|
|
503
|
+
clientHandle.destroy();
|
|
504
|
+
}
|
|
505
|
+
}
|
|
432
506
|
async function chainDefault(name) {
|
|
433
507
|
if (!name) {
|
|
434
508
|
console.error("Usage: dot chain default <name>");
|
|
@@ -589,6 +663,26 @@ function registerInspectCommand(cli) {
|
|
|
589
663
|
});
|
|
590
664
|
}
|
|
591
665
|
|
|
666
|
+
// src/utils/parse-value.ts
|
|
667
|
+
function parseValue(arg) {
|
|
668
|
+
if (/^\d+$/.test(arg))
|
|
669
|
+
return parseInt(arg, 10);
|
|
670
|
+
if (/^\d{16,}$/.test(arg))
|
|
671
|
+
return BigInt(arg);
|
|
672
|
+
if (/^0x[0-9a-fA-F]+$/.test(arg))
|
|
673
|
+
return arg;
|
|
674
|
+
if (arg === "true")
|
|
675
|
+
return true;
|
|
676
|
+
if (arg === "false")
|
|
677
|
+
return false;
|
|
678
|
+
if (arg.startsWith("{") || arg.startsWith("[")) {
|
|
679
|
+
try {
|
|
680
|
+
return JSON.parse(arg);
|
|
681
|
+
} catch {}
|
|
682
|
+
}
|
|
683
|
+
return arg;
|
|
684
|
+
}
|
|
685
|
+
|
|
592
686
|
// src/commands/query.ts
|
|
593
687
|
var DEFAULT_LIMIT = 100;
|
|
594
688
|
function registerQueryCommand(cli) {
|
|
@@ -625,7 +719,7 @@ function registerQueryCommand(cli) {
|
|
|
625
719
|
}
|
|
626
720
|
const unsafeApi = clientHandle.client.getUnsafeApi();
|
|
627
721
|
const storageApi = unsafeApi.query[palletInfo.name][storageItem.name];
|
|
628
|
-
const parsedKeys = keys.map(
|
|
722
|
+
const parsedKeys = keys.map(parseValue);
|
|
629
723
|
const format = opts.output ?? "pretty";
|
|
630
724
|
if (storageItem.type === "map" && parsedKeys.length === 0) {
|
|
631
725
|
const entries = await storageApi.getEntries();
|
|
@@ -649,24 +743,6 @@ ${DIM}Showing ${limit} of ${entries.length} entries. Use --limit 0 for all.${RES
|
|
|
649
743
|
}
|
|
650
744
|
});
|
|
651
745
|
}
|
|
652
|
-
function parseKeyArg(arg) {
|
|
653
|
-
if (/^\d+$/.test(arg))
|
|
654
|
-
return parseInt(arg, 10);
|
|
655
|
-
if (/^\d{16,}$/.test(arg))
|
|
656
|
-
return BigInt(arg);
|
|
657
|
-
if (/^0x[0-9a-fA-F]+$/.test(arg))
|
|
658
|
-
return arg;
|
|
659
|
-
if (arg === "true")
|
|
660
|
-
return true;
|
|
661
|
-
if (arg === "false")
|
|
662
|
-
return false;
|
|
663
|
-
if (arg.startsWith("{") || arg.startsWith("[")) {
|
|
664
|
-
try {
|
|
665
|
-
return JSON.parse(arg);
|
|
666
|
-
} catch {}
|
|
667
|
-
}
|
|
668
|
-
return arg;
|
|
669
|
-
}
|
|
670
746
|
|
|
671
747
|
// src/commands/const.ts
|
|
672
748
|
function registerConstCommand(cli) {
|
|
@@ -705,6 +781,512 @@ function registerConstCommand(cli) {
|
|
|
705
781
|
});
|
|
706
782
|
}
|
|
707
783
|
|
|
784
|
+
// src/config/accounts-store.ts
|
|
785
|
+
import { join as join2 } from "node:path";
|
|
786
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, access as access2 } from "node:fs/promises";
|
|
787
|
+
var ACCOUNTS_PATH = join2(getConfigDir(), "accounts.json");
|
|
788
|
+
async function ensureDir2(dir) {
|
|
789
|
+
await mkdir2(dir, { recursive: true });
|
|
790
|
+
}
|
|
791
|
+
async function fileExists2(path) {
|
|
792
|
+
try {
|
|
793
|
+
await access2(path);
|
|
794
|
+
return true;
|
|
795
|
+
} catch {
|
|
796
|
+
return false;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
async function loadAccounts() {
|
|
800
|
+
await ensureDir2(getConfigDir());
|
|
801
|
+
if (await fileExists2(ACCOUNTS_PATH)) {
|
|
802
|
+
const data = await readFile2(ACCOUNTS_PATH, "utf-8");
|
|
803
|
+
return JSON.parse(data);
|
|
804
|
+
}
|
|
805
|
+
return { accounts: [] };
|
|
806
|
+
}
|
|
807
|
+
async function saveAccounts(file) {
|
|
808
|
+
await ensureDir2(getConfigDir());
|
|
809
|
+
await writeFile2(ACCOUNTS_PATH, JSON.stringify(file, null, 2) + `
|
|
810
|
+
`);
|
|
811
|
+
}
|
|
812
|
+
function findAccount(file, name) {
|
|
813
|
+
return file.accounts.find((a) => a.name.toLowerCase() === name.toLowerCase());
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// src/core/accounts.ts
|
|
817
|
+
import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
|
|
818
|
+
import {
|
|
819
|
+
DEV_PHRASE,
|
|
820
|
+
mnemonicToEntropy,
|
|
821
|
+
entropyToMiniSecret,
|
|
822
|
+
generateMnemonic,
|
|
823
|
+
validateMnemonic,
|
|
824
|
+
ss58Address
|
|
825
|
+
} from "@polkadot-labs/hdkd-helpers";
|
|
826
|
+
import { getPolkadotSigner } from "polkadot-api/signer";
|
|
827
|
+
var DEV_NAMES = [
|
|
828
|
+
"alice",
|
|
829
|
+
"bob",
|
|
830
|
+
"charlie",
|
|
831
|
+
"dave",
|
|
832
|
+
"eve",
|
|
833
|
+
"ferdie"
|
|
834
|
+
];
|
|
835
|
+
function isDevAccount(name) {
|
|
836
|
+
return DEV_NAMES.includes(name.toLowerCase());
|
|
837
|
+
}
|
|
838
|
+
function devDerivationPath(name) {
|
|
839
|
+
return "//" + name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
|
|
840
|
+
}
|
|
841
|
+
function deriveFromMnemonic(mnemonic, path) {
|
|
842
|
+
const entropy = mnemonicToEntropy(mnemonic);
|
|
843
|
+
const miniSecret = entropyToMiniSecret(entropy);
|
|
844
|
+
const derive = sr25519CreateDerive(miniSecret);
|
|
845
|
+
return derive(path);
|
|
846
|
+
}
|
|
847
|
+
function deriveFromHexSeed(hexSeed, path) {
|
|
848
|
+
const clean = hexSeed.startsWith("0x") ? hexSeed.slice(2) : hexSeed;
|
|
849
|
+
const seed = new Uint8Array(clean.length / 2);
|
|
850
|
+
for (let i = 0;i < clean.length; i += 2) {
|
|
851
|
+
seed[i / 2] = parseInt(clean.substring(i, i + 2), 16);
|
|
852
|
+
}
|
|
853
|
+
const derive = sr25519CreateDerive(seed);
|
|
854
|
+
return derive(path);
|
|
855
|
+
}
|
|
856
|
+
function getDevKeypair(name) {
|
|
857
|
+
const path = devDerivationPath(name);
|
|
858
|
+
return deriveFromMnemonic(DEV_PHRASE, path);
|
|
859
|
+
}
|
|
860
|
+
function getDevAddress(name, prefix = 42) {
|
|
861
|
+
const keypair = getDevKeypair(name);
|
|
862
|
+
return ss58Address(keypair.publicKey, prefix);
|
|
863
|
+
}
|
|
864
|
+
function createNewAccount() {
|
|
865
|
+
const mnemonic = generateMnemonic();
|
|
866
|
+
const entropy = mnemonicToEntropy(mnemonic);
|
|
867
|
+
const miniSecret = entropyToMiniSecret(entropy);
|
|
868
|
+
const derive = sr25519CreateDerive(miniSecret);
|
|
869
|
+
const keypair = derive("");
|
|
870
|
+
return { mnemonic, publicKey: keypair.publicKey };
|
|
871
|
+
}
|
|
872
|
+
function importAccount(secret) {
|
|
873
|
+
const isHexSeed = /^0x[0-9a-fA-F]{64}$/.test(secret);
|
|
874
|
+
if (isHexSeed) {
|
|
875
|
+
const keypair2 = deriveFromHexSeed(secret, "");
|
|
876
|
+
return { publicKey: keypair2.publicKey };
|
|
877
|
+
}
|
|
878
|
+
if (!validateMnemonic(secret)) {
|
|
879
|
+
throw new Error("Invalid secret. Expected a 0x-prefixed 32-byte hex seed or a valid BIP39 mnemonic.");
|
|
880
|
+
}
|
|
881
|
+
const keypair = deriveFromMnemonic(secret, "");
|
|
882
|
+
return { publicKey: keypair.publicKey };
|
|
883
|
+
}
|
|
884
|
+
function publicKeyToHex(publicKey) {
|
|
885
|
+
return "0x" + Array.from(publicKey).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
886
|
+
}
|
|
887
|
+
function toSs58(publicKey, prefix = 42) {
|
|
888
|
+
if (typeof publicKey === "string") {
|
|
889
|
+
const clean = publicKey.startsWith("0x") ? publicKey.slice(2) : publicKey;
|
|
890
|
+
const bytes = new Uint8Array(clean.length / 2);
|
|
891
|
+
for (let i = 0;i < clean.length; i += 2) {
|
|
892
|
+
bytes[i / 2] = parseInt(clean.substring(i, i + 2), 16);
|
|
893
|
+
}
|
|
894
|
+
return ss58Address(bytes, prefix);
|
|
895
|
+
}
|
|
896
|
+
return ss58Address(publicKey, prefix);
|
|
897
|
+
}
|
|
898
|
+
async function resolveAccountSigner(name) {
|
|
899
|
+
if (isDevAccount(name)) {
|
|
900
|
+
const keypair2 = getDevKeypair(name);
|
|
901
|
+
return getPolkadotSigner(keypair2.publicKey, "Sr25519", keypair2.sign);
|
|
902
|
+
}
|
|
903
|
+
const accountsFile = await loadAccounts();
|
|
904
|
+
const account = findAccount(accountsFile, name);
|
|
905
|
+
if (!account) {
|
|
906
|
+
const available = [
|
|
907
|
+
...DEV_NAMES,
|
|
908
|
+
...accountsFile.accounts.map((a) => a.name)
|
|
909
|
+
];
|
|
910
|
+
throw new Error(`Unknown account "${name}". Available accounts: ${available.join(", ")}`);
|
|
911
|
+
}
|
|
912
|
+
const isHexSeed = /^0x[0-9a-fA-F]{64}$/.test(account.secret);
|
|
913
|
+
const keypair = isHexSeed ? deriveFromHexSeed(account.secret, account.derivationPath) : deriveFromMnemonic(account.secret, account.derivationPath);
|
|
914
|
+
return getPolkadotSigner(keypair.publicKey, "Sr25519", keypair.sign);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// src/commands/account.ts
|
|
918
|
+
var ACCOUNT_HELP = `
|
|
919
|
+
${BOLD}Usage:${RESET}
|
|
920
|
+
$ dot account create <name> Create a new account
|
|
921
|
+
$ dot account import <name> --secret <s> Import from mnemonic or hex seed
|
|
922
|
+
$ dot account list List all accounts
|
|
923
|
+
$ dot account remove <name> Remove a stored account
|
|
924
|
+
|
|
925
|
+
${BOLD}Examples:${RESET}
|
|
926
|
+
$ dot account create my-validator
|
|
927
|
+
$ dot account import treasury --secret "word1 word2 ... word12"
|
|
928
|
+
$ dot account import raw-key --secret 0xabcdef...
|
|
929
|
+
$ dot account list
|
|
930
|
+
$ dot account remove my-validator
|
|
931
|
+
`.trimStart();
|
|
932
|
+
function registerAccountCommands(cli) {
|
|
933
|
+
cli.command("account [action] [name]", "Manage local accounts (create, import, list, remove)").option("--secret <value>", "Secret key (mnemonic or hex seed) for import").action(async (action, name, opts) => {
|
|
934
|
+
if (!action) {
|
|
935
|
+
console.log(ACCOUNT_HELP);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
switch (action) {
|
|
939
|
+
case "create":
|
|
940
|
+
return accountCreate(name);
|
|
941
|
+
case "import":
|
|
942
|
+
return accountImport(name, opts);
|
|
943
|
+
case "list":
|
|
944
|
+
return accountList();
|
|
945
|
+
case "remove":
|
|
946
|
+
return accountRemove(name);
|
|
947
|
+
default:
|
|
948
|
+
console.error(`Unknown action "${action}".
|
|
949
|
+
`);
|
|
950
|
+
console.log(ACCOUNT_HELP);
|
|
951
|
+
process.exit(1);
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
async function accountCreate(name) {
|
|
956
|
+
if (!name) {
|
|
957
|
+
console.error(`Account name is required.
|
|
958
|
+
`);
|
|
959
|
+
console.error("Usage: dot account create <name>");
|
|
960
|
+
process.exit(1);
|
|
961
|
+
}
|
|
962
|
+
if (isDevAccount(name)) {
|
|
963
|
+
throw new Error(`"${name}" is a built-in dev account and cannot be used as a custom account name.`);
|
|
964
|
+
}
|
|
965
|
+
const accountsFile = await loadAccounts();
|
|
966
|
+
if (findAccount(accountsFile, name)) {
|
|
967
|
+
throw new Error(`Account "${name}" already exists.`);
|
|
968
|
+
}
|
|
969
|
+
const { mnemonic, publicKey } = createNewAccount();
|
|
970
|
+
const hexPub = publicKeyToHex(publicKey);
|
|
971
|
+
const address = toSs58(publicKey);
|
|
972
|
+
accountsFile.accounts.push({
|
|
973
|
+
name,
|
|
974
|
+
secret: mnemonic,
|
|
975
|
+
publicKey: hexPub,
|
|
976
|
+
derivationPath: ""
|
|
977
|
+
});
|
|
978
|
+
await saveAccounts(accountsFile);
|
|
979
|
+
printHeading("Account Created");
|
|
980
|
+
console.log(` ${BOLD}Name:${RESET} ${name}`);
|
|
981
|
+
console.log(` ${BOLD}Address:${RESET} ${address}`);
|
|
982
|
+
console.log(` ${BOLD}Mnemonic:${RESET} ${mnemonic}`);
|
|
983
|
+
console.log();
|
|
984
|
+
console.log(` ${YELLOW}Save this mnemonic phrase! It is the only way to recover this account.${RESET}`);
|
|
985
|
+
console.log();
|
|
986
|
+
}
|
|
987
|
+
async function accountImport(name, opts) {
|
|
988
|
+
if (!name) {
|
|
989
|
+
console.error(`Account name is required.
|
|
990
|
+
`);
|
|
991
|
+
console.error('Usage: dot account import <name> --secret "mnemonic or hex seed"');
|
|
992
|
+
process.exit(1);
|
|
993
|
+
}
|
|
994
|
+
if (!opts.secret) {
|
|
995
|
+
console.error(`--secret is required.
|
|
996
|
+
`);
|
|
997
|
+
console.error('Usage: dot account import <name> --secret "mnemonic or hex seed"');
|
|
998
|
+
process.exit(1);
|
|
999
|
+
}
|
|
1000
|
+
if (isDevAccount(name)) {
|
|
1001
|
+
throw new Error(`"${name}" is a built-in dev account and cannot be used as a custom account name.`);
|
|
1002
|
+
}
|
|
1003
|
+
const accountsFile = await loadAccounts();
|
|
1004
|
+
if (findAccount(accountsFile, name)) {
|
|
1005
|
+
throw new Error(`Account "${name}" already exists.`);
|
|
1006
|
+
}
|
|
1007
|
+
const { publicKey } = importAccount(opts.secret);
|
|
1008
|
+
const hexPub = publicKeyToHex(publicKey);
|
|
1009
|
+
const address = toSs58(publicKey);
|
|
1010
|
+
accountsFile.accounts.push({
|
|
1011
|
+
name,
|
|
1012
|
+
secret: opts.secret,
|
|
1013
|
+
publicKey: hexPub,
|
|
1014
|
+
derivationPath: ""
|
|
1015
|
+
});
|
|
1016
|
+
await saveAccounts(accountsFile);
|
|
1017
|
+
printHeading("Account Imported");
|
|
1018
|
+
console.log(` ${BOLD}Name:${RESET} ${name}`);
|
|
1019
|
+
console.log(` ${BOLD}Address:${RESET} ${address}`);
|
|
1020
|
+
console.log();
|
|
1021
|
+
}
|
|
1022
|
+
async function accountList() {
|
|
1023
|
+
printHeading("Dev Accounts");
|
|
1024
|
+
for (const name of DEV_NAMES) {
|
|
1025
|
+
const display = name.charAt(0).toUpperCase() + name.slice(1);
|
|
1026
|
+
const address = getDevAddress(name);
|
|
1027
|
+
printItem(display, address);
|
|
1028
|
+
}
|
|
1029
|
+
const accountsFile = await loadAccounts();
|
|
1030
|
+
if (accountsFile.accounts.length > 0) {
|
|
1031
|
+
printHeading("Stored Accounts");
|
|
1032
|
+
for (const account of accountsFile.accounts) {
|
|
1033
|
+
const address = toSs58(account.publicKey);
|
|
1034
|
+
printItem(account.name, address);
|
|
1035
|
+
}
|
|
1036
|
+
} else {
|
|
1037
|
+
printHeading("Stored Accounts");
|
|
1038
|
+
console.log(" (none)");
|
|
1039
|
+
}
|
|
1040
|
+
console.log();
|
|
1041
|
+
}
|
|
1042
|
+
async function accountRemove(name) {
|
|
1043
|
+
if (!name) {
|
|
1044
|
+
console.error(`Account name is required.
|
|
1045
|
+
`);
|
|
1046
|
+
console.error("Usage: dot account remove <name>");
|
|
1047
|
+
process.exit(1);
|
|
1048
|
+
}
|
|
1049
|
+
if (isDevAccount(name)) {
|
|
1050
|
+
throw new Error("Cannot remove built-in dev accounts.");
|
|
1051
|
+
}
|
|
1052
|
+
const accountsFile = await loadAccounts();
|
|
1053
|
+
const idx = accountsFile.accounts.findIndex((a) => a.name.toLowerCase() === name.toLowerCase());
|
|
1054
|
+
if (idx === -1) {
|
|
1055
|
+
throw new Error(`Account "${name}" not found.`);
|
|
1056
|
+
}
|
|
1057
|
+
accountsFile.accounts.splice(idx, 1);
|
|
1058
|
+
await saveAccounts(accountsFile);
|
|
1059
|
+
console.log(`Account "${name}" removed.`);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// src/commands/tx.ts
|
|
1063
|
+
function registerTxCommand(cli) {
|
|
1064
|
+
cli.command("tx [target] [...args]", "Submit an extrinsic (e.g. Balances.transferKeepAlive <dest> <amount>)").option("--from <name>", "Account to sign with (required)").option("--dry-run", "Estimate fees without submitting").action(async (target, args, opts) => {
|
|
1065
|
+
if (!target || !opts.from) {
|
|
1066
|
+
console.log("Usage: dot tx <Pallet.Call> [...args] --from <account> [--dry-run]");
|
|
1067
|
+
console.log("");
|
|
1068
|
+
console.log("Examples:");
|
|
1069
|
+
console.log(" $ dot tx Balances.transferKeepAlive 5FHn... 1000000000000 --from alice");
|
|
1070
|
+
console.log(" $ dot tx System.remark 0xdeadbeef --from alice --dry-run");
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
const config = await loadConfig();
|
|
1074
|
+
const { name: chainName, chain: chainConfig } = resolveChain(config, opts.chain);
|
|
1075
|
+
const { pallet, item: callName } = parseTarget(target);
|
|
1076
|
+
const signer = await resolveAccountSigner(opts.from);
|
|
1077
|
+
const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
1078
|
+
try {
|
|
1079
|
+
const meta = await getOrFetchMetadata(chainName, clientHandle);
|
|
1080
|
+
const palletNames = getPalletNames(meta);
|
|
1081
|
+
const palletInfo = findPallet(meta, pallet);
|
|
1082
|
+
if (!palletInfo) {
|
|
1083
|
+
throw new Error(suggestMessage("pallet", pallet, palletNames));
|
|
1084
|
+
}
|
|
1085
|
+
const callInfo = palletInfo.calls.find((c) => c.name.toLowerCase() === callName.toLowerCase());
|
|
1086
|
+
if (!callInfo) {
|
|
1087
|
+
const callNames = palletInfo.calls.map((c) => c.name);
|
|
1088
|
+
throw new Error(suggestMessage(`call in ${palletInfo.name}`, callName, callNames));
|
|
1089
|
+
}
|
|
1090
|
+
const callData = parseCallArgs(meta, palletInfo.name, callInfo.name, args);
|
|
1091
|
+
const unsafeApi = clientHandle.client.getUnsafeApi();
|
|
1092
|
+
const tx = unsafeApi.tx[palletInfo.name][callInfo.name](callData);
|
|
1093
|
+
if (opts.dryRun) {
|
|
1094
|
+
const signerAddress = toSs58(signer.publicKey);
|
|
1095
|
+
console.log(` ${BOLD}From:${RESET} ${opts.from} (${signerAddress})`);
|
|
1096
|
+
try {
|
|
1097
|
+
const fees = await tx.getEstimatedFees(signer.publicKey);
|
|
1098
|
+
console.log(` ${BOLD}Estimated fees:${RESET} ${fees}`);
|
|
1099
|
+
} catch (err) {
|
|
1100
|
+
console.log(` ${BOLD}Estimated fees:${RESET} ${YELLOW}unable to estimate${RESET}`);
|
|
1101
|
+
console.log(` ${DIM}${err.message ?? err}${RESET}`);
|
|
1102
|
+
}
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
console.log("Signing and submitting...");
|
|
1106
|
+
const result = await tx.signAndSubmit(signer);
|
|
1107
|
+
console.log();
|
|
1108
|
+
console.log(` ${BOLD}Tx:${RESET} ${result.txHash}`);
|
|
1109
|
+
if (result.block) {
|
|
1110
|
+
console.log(` ${BOLD}Block:${RESET} #${result.block.number} (${result.block.hash})`);
|
|
1111
|
+
}
|
|
1112
|
+
if (result.dispatchError) {
|
|
1113
|
+
console.log(` ${BOLD}Status:${RESET} ${YELLOW}dispatch error${RESET}`);
|
|
1114
|
+
console.log(` ${BOLD}Error:${RESET} ${result.dispatchError.type}${result.dispatchError.value ? ": " + JSON.stringify(result.dispatchError.value) : ""}`);
|
|
1115
|
+
} else {
|
|
1116
|
+
console.log(` ${BOLD}Status:${RESET} ${GREEN}ok${RESET}`);
|
|
1117
|
+
}
|
|
1118
|
+
if (result.events && result.events.length > 0) {
|
|
1119
|
+
console.log(` ${BOLD}Events:${RESET}`);
|
|
1120
|
+
for (const event of result.events) {
|
|
1121
|
+
const name = `${CYAN}${event.type}${RESET}.${CYAN}${event.value?.type ?? ""}${RESET}`;
|
|
1122
|
+
const payload = event.value?.value;
|
|
1123
|
+
if (payload && typeof payload === "object") {
|
|
1124
|
+
const fields = Object.entries(payload).map(([k, v]) => `${k}: ${formatEventValue(v)}`).join(", ");
|
|
1125
|
+
console.log(` ${name} { ${fields} }`);
|
|
1126
|
+
} else {
|
|
1127
|
+
console.log(` ${name}`);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
console.log();
|
|
1132
|
+
} finally {
|
|
1133
|
+
clientHandle.destroy();
|
|
1134
|
+
}
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
function formatEventValue(v) {
|
|
1138
|
+
if (typeof v === "bigint")
|
|
1139
|
+
return v.toString();
|
|
1140
|
+
if (typeof v === "string")
|
|
1141
|
+
return v;
|
|
1142
|
+
if (typeof v === "number")
|
|
1143
|
+
return v.toString();
|
|
1144
|
+
if (typeof v === "boolean")
|
|
1145
|
+
return v.toString();
|
|
1146
|
+
if (v === null || v === undefined)
|
|
1147
|
+
return "null";
|
|
1148
|
+
return JSON.stringify(v, (_k, val) => typeof val === "bigint" ? val.toString() : val);
|
|
1149
|
+
}
|
|
1150
|
+
function parseCallArgs(meta, palletName, callName, args) {
|
|
1151
|
+
const palletMeta = meta.unified.pallets.find((p) => p.name === palletName);
|
|
1152
|
+
if (!palletMeta?.calls)
|
|
1153
|
+
return;
|
|
1154
|
+
const callsEntry = meta.lookup(palletMeta.calls.type);
|
|
1155
|
+
if (callsEntry.type !== "enum")
|
|
1156
|
+
return;
|
|
1157
|
+
const variant = callsEntry.value[callName];
|
|
1158
|
+
if (!variant)
|
|
1159
|
+
return;
|
|
1160
|
+
if (variant.type === "void") {
|
|
1161
|
+
if (args.length > 0) {
|
|
1162
|
+
throw new Error(`${palletName}.${callName} takes no arguments, but ${args.length} provided.`);
|
|
1163
|
+
}
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
if (variant.type === "struct") {
|
|
1167
|
+
return parseStructArgs(meta.lookup, variant.value, args, `${palletName}.${callName}`);
|
|
1168
|
+
}
|
|
1169
|
+
if (variant.type === "lookupEntry") {
|
|
1170
|
+
const inner = variant.value;
|
|
1171
|
+
if (inner.type === "struct") {
|
|
1172
|
+
return parseStructArgs(meta.lookup, inner.value, args, `${palletName}.${callName}`);
|
|
1173
|
+
}
|
|
1174
|
+
if (inner.type === "void")
|
|
1175
|
+
return;
|
|
1176
|
+
if (args.length !== 1) {
|
|
1177
|
+
throw new Error(`${palletName}.${callName} takes 1 argument (${describeType(meta.lookup, inner.id)}), but ${args.length} provided.`);
|
|
1178
|
+
}
|
|
1179
|
+
return parseTypedArg(meta.lookup, inner, args[0]);
|
|
1180
|
+
}
|
|
1181
|
+
if (variant.type === "tuple") {
|
|
1182
|
+
const entries = variant.value;
|
|
1183
|
+
if (args.length !== entries.length) {
|
|
1184
|
+
throw new Error(`${palletName}.${callName} takes ${entries.length} arguments, but ${args.length} provided.`);
|
|
1185
|
+
}
|
|
1186
|
+
return entries.map((entry, i) => parseTypedArg(meta.lookup, entry, args[i]));
|
|
1187
|
+
}
|
|
1188
|
+
return args.length === 0 ? undefined : args.map(parseValue);
|
|
1189
|
+
}
|
|
1190
|
+
function parseStructArgs(lookup, fields, args, callLabel) {
|
|
1191
|
+
const fieldNames = Object.keys(fields);
|
|
1192
|
+
if (args.length !== fieldNames.length) {
|
|
1193
|
+
const expected = fieldNames.map((name) => `${name}: ${describeType(lookup, fields[name].id)}`).join(", ");
|
|
1194
|
+
throw new Error(`${callLabel} takes ${fieldNames.length} argument(s): ${expected}
|
|
1195
|
+
` + ` Got ${args.length} argument(s).`);
|
|
1196
|
+
}
|
|
1197
|
+
const result = {};
|
|
1198
|
+
for (let i = 0;i < fieldNames.length; i++) {
|
|
1199
|
+
const name = fieldNames[i];
|
|
1200
|
+
const entry = fields[name];
|
|
1201
|
+
result[name] = parseTypedArg(lookup, entry, args[i]);
|
|
1202
|
+
}
|
|
1203
|
+
return result;
|
|
1204
|
+
}
|
|
1205
|
+
function parseTypedArg(lookup, entry, arg) {
|
|
1206
|
+
switch (entry.type) {
|
|
1207
|
+
case "primitive":
|
|
1208
|
+
return parsePrimitive(entry.value, arg);
|
|
1209
|
+
case "compact":
|
|
1210
|
+
return entry.isBig ? BigInt(arg) : parseInt(arg, 10);
|
|
1211
|
+
case "AccountId32":
|
|
1212
|
+
case "AccountId20":
|
|
1213
|
+
return arg;
|
|
1214
|
+
case "option": {
|
|
1215
|
+
if (arg === "null" || arg === "undefined" || arg === "none") {
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
return parseTypedArg(lookup, entry.value, arg);
|
|
1219
|
+
}
|
|
1220
|
+
case "enum": {
|
|
1221
|
+
if (arg.startsWith("{")) {
|
|
1222
|
+
try {
|
|
1223
|
+
return JSON.parse(arg);
|
|
1224
|
+
} catch {}
|
|
1225
|
+
}
|
|
1226
|
+
const variants = Object.keys(entry.value);
|
|
1227
|
+
const matched = variants.find((v) => v.toLowerCase() === arg.toLowerCase());
|
|
1228
|
+
if (matched) {
|
|
1229
|
+
const variant = entry.value[matched];
|
|
1230
|
+
if (variant.type === "void") {
|
|
1231
|
+
return { type: matched };
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
return parseValue(arg);
|
|
1235
|
+
}
|
|
1236
|
+
case "sequence":
|
|
1237
|
+
case "array":
|
|
1238
|
+
if (arg.startsWith("[")) {
|
|
1239
|
+
try {
|
|
1240
|
+
return JSON.parse(arg);
|
|
1241
|
+
} catch {}
|
|
1242
|
+
}
|
|
1243
|
+
if (/^0x[0-9a-fA-F]*$/.test(arg))
|
|
1244
|
+
return arg;
|
|
1245
|
+
return parseValue(arg);
|
|
1246
|
+
case "struct":
|
|
1247
|
+
if (arg.startsWith("{")) {
|
|
1248
|
+
try {
|
|
1249
|
+
return JSON.parse(arg);
|
|
1250
|
+
} catch {}
|
|
1251
|
+
}
|
|
1252
|
+
return parseValue(arg);
|
|
1253
|
+
case "tuple":
|
|
1254
|
+
if (arg.startsWith("[")) {
|
|
1255
|
+
try {
|
|
1256
|
+
return JSON.parse(arg);
|
|
1257
|
+
} catch {}
|
|
1258
|
+
}
|
|
1259
|
+
return parseValue(arg);
|
|
1260
|
+
default:
|
|
1261
|
+
return parseValue(arg);
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
function parsePrimitive(prim, arg) {
|
|
1265
|
+
switch (prim) {
|
|
1266
|
+
case "bool":
|
|
1267
|
+
return arg === "true";
|
|
1268
|
+
case "char":
|
|
1269
|
+
case "str":
|
|
1270
|
+
return arg;
|
|
1271
|
+
case "u8":
|
|
1272
|
+
case "u16":
|
|
1273
|
+
case "u32":
|
|
1274
|
+
case "i8":
|
|
1275
|
+
case "i16":
|
|
1276
|
+
case "i32":
|
|
1277
|
+
return parseInt(arg, 10);
|
|
1278
|
+
case "u64":
|
|
1279
|
+
case "u128":
|
|
1280
|
+
case "u256":
|
|
1281
|
+
case "i64":
|
|
1282
|
+
case "i128":
|
|
1283
|
+
case "i256":
|
|
1284
|
+
return BigInt(arg);
|
|
1285
|
+
default:
|
|
1286
|
+
return parseValue(arg);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
|
|
708
1290
|
// src/utils/errors.ts
|
|
709
1291
|
class CliError2 extends Error {
|
|
710
1292
|
constructor(message) {
|
|
@@ -725,8 +1307,10 @@ registerChainCommands(cli);
|
|
|
725
1307
|
registerInspectCommand(cli);
|
|
726
1308
|
registerQueryCommand(cli);
|
|
727
1309
|
registerConstCommand(cli);
|
|
1310
|
+
registerAccountCommands(cli);
|
|
1311
|
+
registerTxCommand(cli);
|
|
728
1312
|
cli.help();
|
|
729
|
-
cli.version("0.
|
|
1313
|
+
cli.version("0.2.0");
|
|
730
1314
|
function handleError(err) {
|
|
731
1315
|
if (err instanceof CliError2) {
|
|
732
1316
|
console.error(`Error: ${err.message}`);
|
|
@@ -739,8 +1323,8 @@ function handleError(err) {
|
|
|
739
1323
|
}
|
|
740
1324
|
process.on("unhandledRejection", handleError);
|
|
741
1325
|
try {
|
|
742
|
-
|
|
743
|
-
if (
|
|
1326
|
+
cli.parse();
|
|
1327
|
+
if (!cli.matchedCommandName && !cli.options.help && !cli.options.version) {
|
|
744
1328
|
cli.outputHelp();
|
|
745
1329
|
}
|
|
746
1330
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polkadot-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "CLI tool for querying Polkadot-ecosystem on-chain state",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,11 +32,13 @@
|
|
|
32
32
|
"url": "git+https://github.com/peetzweg/polkadot-cli.git"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"cac": "^6.7.14",
|
|
36
|
-
"polkadot-api": "^1.23.3",
|
|
37
35
|
"@polkadot-api/metadata-builders": "^0.13.9",
|
|
38
36
|
"@polkadot-api/substrate-bindings": "^0.17.0",
|
|
39
|
-
"@polkadot-api/view-builder": "^0.4.17"
|
|
37
|
+
"@polkadot-api/view-builder": "^0.4.17",
|
|
38
|
+
"@polkadot-labs/hdkd": "^0.0.26",
|
|
39
|
+
"@polkadot-labs/hdkd-helpers": "^0.0.27",
|
|
40
|
+
"cac": "^6.7.14",
|
|
41
|
+
"polkadot-api": "^1.23.3"
|
|
40
42
|
},
|
|
41
43
|
"devDependencies": {
|
|
42
44
|
"@changesets/cli": "^2.29.4",
|