polkadot-cli 0.7.0 → 0.8.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 +24 -0
- package/dist/cli.mjs +95 -20
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
[](https://codecov.io/gh/peetzweg/polkadot-cli)
|
|
2
|
+
|
|
1
3
|
# polkadot-cli
|
|
2
4
|
|
|
3
5
|
A command-line tool for interacting with Polkadot-ecosystem chains. Manage chains and accounts, query storage, look up constants, inspect metadata, submit extrinsics, and compute hashes — all from your terminal.
|
|
@@ -66,6 +68,20 @@ dot account remove my-validator
|
|
|
66
68
|
|
|
67
69
|
**Known limitation:** Hex seed import (`--secret 0x...`) does not work from the command line. The CLI argument parser (`cac`) interprets `0x`-prefixed values as JavaScript numbers, which loses precision for 32-byte seeds. Use a BIP39 mnemonic instead. If you need to import a raw seed programmatically, write it directly to `~/.polkadot/accounts.json`.
|
|
68
70
|
|
|
71
|
+
### Chain prefix
|
|
72
|
+
|
|
73
|
+
Instead of the `--chain` flag, you can prefix any target with the chain name using dot notation:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
dot query kusama.System.Account 5GrwvaEF...
|
|
77
|
+
dot const kusama.Balances.ExistentialDeposit
|
|
78
|
+
dot tx kusama.Balances.transferKeepAlive 5FHneW46... 1000000000000 --from alice
|
|
79
|
+
dot inspect kusama.System
|
|
80
|
+
dot inspect kusama.System.Account
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The `--chain` flag and default chain still work as before. If both a chain prefix and `--chain` flag are provided, the CLI errors.
|
|
84
|
+
|
|
69
85
|
### Query storage
|
|
70
86
|
|
|
71
87
|
```bash
|
|
@@ -80,6 +96,9 @@ dot query System.Account --limit 10
|
|
|
80
96
|
|
|
81
97
|
# Pipe to jq (colors disabled automatically)
|
|
82
98
|
dot query System.Account --limit 5 | jq '.[0].value.data.free'
|
|
99
|
+
|
|
100
|
+
# Query a specific chain using chain prefix
|
|
101
|
+
dot query kusama.System.Account 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
|
|
83
102
|
```
|
|
84
103
|
|
|
85
104
|
### Look up constants
|
|
@@ -87,6 +106,7 @@ dot query System.Account --limit 5 | jq '.[0].value.data.free'
|
|
|
87
106
|
```bash
|
|
88
107
|
dot const Balances.ExistentialDeposit
|
|
89
108
|
dot const System.SS58Prefix --chain kusama
|
|
109
|
+
dot const kusama.Balances.ExistentialDeposit
|
|
90
110
|
```
|
|
91
111
|
|
|
92
112
|
### Inspect metadata
|
|
@@ -102,6 +122,10 @@ dot inspect System
|
|
|
102
122
|
|
|
103
123
|
# Detailed type info for a specific item
|
|
104
124
|
dot inspect System.Account
|
|
125
|
+
|
|
126
|
+
# Inspect a specific chain using chain prefix
|
|
127
|
+
dot inspect kusama.System
|
|
128
|
+
dot inspect kusama.System.Account
|
|
105
129
|
```
|
|
106
130
|
|
|
107
131
|
### Submit extrinsics
|
package/dist/cli.mjs
CHANGED
|
@@ -5,7 +5,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
5
5
|
// src/cli.ts
|
|
6
6
|
import cac from "cac";
|
|
7
7
|
// package.json
|
|
8
|
-
var version = "0.
|
|
8
|
+
var version = "0.8.0";
|
|
9
9
|
|
|
10
10
|
// src/config/accounts-store.ts
|
|
11
11
|
import { access as access2, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
@@ -848,28 +848,73 @@ function suggestMessage(kind, input, candidates) {
|
|
|
848
848
|
}
|
|
849
849
|
|
|
850
850
|
// src/utils/parse-target.ts
|
|
851
|
-
function parseTarget(input) {
|
|
851
|
+
function parseTarget(input, options) {
|
|
852
852
|
const parts = input.split(".");
|
|
853
|
-
if (
|
|
854
|
-
|
|
853
|
+
if (options?.allowPalletOnly) {
|
|
854
|
+
switch (parts.length) {
|
|
855
|
+
case 1:
|
|
856
|
+
if (!parts[0]) {
|
|
857
|
+
throw new Error(`Invalid target "${input}". Expected format: Pallet or Pallet.Item (e.g. System or System.Account)`);
|
|
858
|
+
}
|
|
859
|
+
return { pallet: parts[0] };
|
|
860
|
+
case 2:
|
|
861
|
+
if (!parts[0] || !parts[1]) {
|
|
862
|
+
throw new Error(`Invalid target "${input}". Expected format: Pallet.Item or Chain.Pallet (e.g. System.Account or kusama.System)`);
|
|
863
|
+
}
|
|
864
|
+
if (options.knownChains?.some((c) => c.toLowerCase() === parts[0].toLowerCase())) {
|
|
865
|
+
return { chain: parts[0], pallet: parts[1] };
|
|
866
|
+
}
|
|
867
|
+
return { pallet: parts[0], item: parts[1] };
|
|
868
|
+
case 3:
|
|
869
|
+
if (!parts[0] || !parts[1] || !parts[2]) {
|
|
870
|
+
throw new Error(`Invalid target "${input}". Expected format: Chain.Pallet.Item (e.g. kusama.System.Account)`);
|
|
871
|
+
}
|
|
872
|
+
return { chain: parts[0], pallet: parts[1], item: parts[2] };
|
|
873
|
+
default:
|
|
874
|
+
throw new Error(`Invalid target "${input}". Expected format: Pallet, Pallet.Item, or Chain.Pallet.Item`);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
switch (parts.length) {
|
|
878
|
+
case 2:
|
|
879
|
+
if (!parts[0] || !parts[1]) {
|
|
880
|
+
throw new Error(`Invalid target "${input}". Expected format: Pallet.Item (e.g. System.Account)`);
|
|
881
|
+
}
|
|
882
|
+
return { pallet: parts[0], item: parts[1] };
|
|
883
|
+
case 3:
|
|
884
|
+
if (!parts[0] || !parts[1] || !parts[2]) {
|
|
885
|
+
throw new Error(`Invalid target "${input}". Expected format: Chain.Pallet.Item (e.g. kusama.System.Account)`);
|
|
886
|
+
}
|
|
887
|
+
return { chain: parts[0], pallet: parts[1], item: parts[2] };
|
|
888
|
+
default:
|
|
889
|
+
throw new Error(`Invalid target "${input}". Expected format: Pallet.Item (e.g. System.Account)`);
|
|
855
890
|
}
|
|
856
|
-
|
|
891
|
+
}
|
|
892
|
+
function resolveTargetChain(target, chainFlag) {
|
|
893
|
+
if (target.chain && chainFlag) {
|
|
894
|
+
throw new Error(`Chain specified both as prefix ("${target.chain}") and as --chain flag ("${chainFlag}"). Use one or the other.`);
|
|
895
|
+
}
|
|
896
|
+
return target.chain ?? chainFlag;
|
|
857
897
|
}
|
|
858
898
|
|
|
859
899
|
// src/commands/const.ts
|
|
860
900
|
function registerConstCommand(cli) {
|
|
861
901
|
cli.command("const [target]", "Look up a pallet constant (e.g. Balances.ExistentialDeposit)").action(async (target, opts) => {
|
|
862
902
|
if (!target) {
|
|
863
|
-
console.log("Usage: dot const <Pallet.Constant> [--chain <name>] [--output json]");
|
|
903
|
+
console.log("Usage: dot const <[Chain.]Pallet.Constant> [--chain <name>] [--output json]");
|
|
864
904
|
console.log("");
|
|
865
905
|
console.log("Examples:");
|
|
866
906
|
console.log(" $ dot const Balances.ExistentialDeposit");
|
|
867
907
|
console.log(" $ dot const System.SS58Prefix --chain kusama");
|
|
908
|
+
console.log(" $ dot const kusama.Balances.ExistentialDeposit # chain prefix");
|
|
868
909
|
return;
|
|
869
910
|
}
|
|
870
911
|
const config = await loadConfig();
|
|
871
|
-
const
|
|
872
|
-
const
|
|
912
|
+
const knownChains = Object.keys(config.chains);
|
|
913
|
+
const parsed = parseTarget(target, { knownChains });
|
|
914
|
+
const effectiveChain = resolveTargetChain(parsed, opts.chain);
|
|
915
|
+
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
916
|
+
const pallet = parsed.pallet;
|
|
917
|
+
const item = parsed.item;
|
|
873
918
|
const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
874
919
|
try {
|
|
875
920
|
const meta = await getOrFetchMetadata(chainName, clientHandle);
|
|
@@ -886,7 +931,14 @@ function registerConstCommand(cli) {
|
|
|
886
931
|
const unsafeApi = clientHandle.client.getUnsafeApi();
|
|
887
932
|
const runtimeToken = await unsafeApi.runtimeToken;
|
|
888
933
|
const result = unsafeApi.constants[palletInfo.name][constantItem.name](runtimeToken);
|
|
889
|
-
|
|
934
|
+
const format = opts.output ?? "pretty";
|
|
935
|
+
if (format === "json") {
|
|
936
|
+
console.error(`chain: ${chainName}`);
|
|
937
|
+
} else {
|
|
938
|
+
console.log(`${DIM}chain: ${chainName}${RESET}
|
|
939
|
+
`);
|
|
940
|
+
}
|
|
941
|
+
printResult(result, format);
|
|
890
942
|
} finally {
|
|
891
943
|
clientHandle.destroy();
|
|
892
944
|
}
|
|
@@ -1028,7 +1080,17 @@ function registerHashCommand(cli) {
|
|
|
1028
1080
|
function registerInspectCommand(cli) {
|
|
1029
1081
|
cli.command("inspect [target]", "Inspect chain metadata (pallets, storage, constants)").option("--chain <name>", "Target chain").option("--rpc <url>", "Override RPC endpoint").action(async (target, opts) => {
|
|
1030
1082
|
const config = await loadConfig();
|
|
1031
|
-
const
|
|
1083
|
+
const knownChains = Object.keys(config.chains);
|
|
1084
|
+
let effectiveChain = opts.chain;
|
|
1085
|
+
let palletName;
|
|
1086
|
+
let itemName;
|
|
1087
|
+
if (target) {
|
|
1088
|
+
const parsed = parseTarget(target, { knownChains, allowPalletOnly: true });
|
|
1089
|
+
effectiveChain = resolveTargetChain(parsed, opts.chain);
|
|
1090
|
+
palletName = parsed.pallet;
|
|
1091
|
+
itemName = parsed.item;
|
|
1092
|
+
}
|
|
1093
|
+
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
1032
1094
|
let meta;
|
|
1033
1095
|
try {
|
|
1034
1096
|
meta = await getOrFetchMetadata(chainName);
|
|
@@ -1055,11 +1117,11 @@ function registerInspectCommand(cli) {
|
|
|
1055
1117
|
console.log();
|
|
1056
1118
|
return;
|
|
1057
1119
|
}
|
|
1058
|
-
if (!
|
|
1120
|
+
if (!itemName) {
|
|
1059
1121
|
const palletNames2 = getPalletNames(meta);
|
|
1060
|
-
const pallet2 = findPallet(meta,
|
|
1122
|
+
const pallet2 = findPallet(meta, palletName);
|
|
1061
1123
|
if (!pallet2) {
|
|
1062
|
-
throw new Error(suggestMessage("pallet",
|
|
1124
|
+
throw new Error(suggestMessage("pallet", palletName, palletNames2));
|
|
1063
1125
|
}
|
|
1064
1126
|
printHeading(`${pallet2.name} Pallet`);
|
|
1065
1127
|
if (pallet2.docs.length) {
|
|
@@ -1084,7 +1146,6 @@ function registerInspectCommand(cli) {
|
|
|
1084
1146
|
}
|
|
1085
1147
|
return;
|
|
1086
1148
|
}
|
|
1087
|
-
const { pallet: palletName, item: itemName } = parseTarget(target);
|
|
1088
1149
|
const palletNames = getPalletNames(meta);
|
|
1089
1150
|
const pallet = findPallet(meta, palletName);
|
|
1090
1151
|
if (!pallet) {
|
|
@@ -1399,7 +1460,7 @@ function registerQueryCommand(cli) {
|
|
|
1399
1460
|
default: DEFAULT_LIMIT
|
|
1400
1461
|
}).action(async (target, keys, opts) => {
|
|
1401
1462
|
if (!target) {
|
|
1402
|
-
console.log("Usage: dot query <Pallet.Item> [...keys] [--chain <name>] [--output json]");
|
|
1463
|
+
console.log("Usage: dot query <[Chain.]Pallet.Item> [...keys] [--chain <name>] [--output json]");
|
|
1403
1464
|
console.log("");
|
|
1404
1465
|
console.log("Examples:");
|
|
1405
1466
|
console.log(" $ dot query System.Number # plain storage value");
|
|
@@ -1408,11 +1469,16 @@ function registerQueryCommand(cli) {
|
|
|
1408
1469
|
console.log(" $ dot query System.Account --limit 10 # first 10 entries");
|
|
1409
1470
|
console.log(" $ dot query System.Account --limit 0 # all entries (no limit)");
|
|
1410
1471
|
console.log(" $ dot query Assets.Metadata 42 --chain asset-hub");
|
|
1472
|
+
console.log(" $ dot query kusama.System.Account 5Grw... # chain prefix");
|
|
1411
1473
|
return;
|
|
1412
1474
|
}
|
|
1413
1475
|
const config = await loadConfig();
|
|
1414
|
-
const
|
|
1415
|
-
const
|
|
1476
|
+
const knownChains = Object.keys(config.chains);
|
|
1477
|
+
const parsed = parseTarget(target, { knownChains });
|
|
1478
|
+
const effectiveChain = resolveTargetChain(parsed, opts.chain);
|
|
1479
|
+
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
1480
|
+
const pallet = parsed.pallet;
|
|
1481
|
+
const item = parsed.item;
|
|
1416
1482
|
const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
1417
1483
|
try {
|
|
1418
1484
|
const meta = await getOrFetchMetadata(chainName, clientHandle);
|
|
@@ -1501,13 +1567,14 @@ import { Binary as Binary2 } from "polkadot-api";
|
|
|
1501
1567
|
function registerTxCommand(cli) {
|
|
1502
1568
|
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").option("--encode", "Encode call to hex without signing or submitting").option("--ext <json>", `Custom signed extension values as JSON, e.g. '{"ExtName":{"value":...}}'`).action(async (target, args, opts) => {
|
|
1503
1569
|
if (!target) {
|
|
1504
|
-
console.log("Usage: dot tx <Pallet.Call|0xCallHex> [...args] --from <account> [--dry-run] [--encode]");
|
|
1570
|
+
console.log("Usage: dot tx <[Chain.]Pallet.Call|0xCallHex> [...args] --from <account> [--dry-run] [--encode]");
|
|
1505
1571
|
console.log("");
|
|
1506
1572
|
console.log("Examples:");
|
|
1507
1573
|
console.log(" $ dot tx Balances.transferKeepAlive 5FHn... 1000000000000 --from alice");
|
|
1508
1574
|
console.log(" $ dot tx System.remark 0xdeadbeef --from alice --dry-run");
|
|
1509
1575
|
console.log(" $ dot tx 0x0001076465616462656566 --from alice");
|
|
1510
1576
|
console.log(" $ dot tx Assets.force_create 4 owner true 10 --encode --chain people");
|
|
1577
|
+
console.log(" $ dot tx kusama.Balances.transferKeepAlive 5FHn... 1000000000000 --from alice");
|
|
1511
1578
|
return;
|
|
1512
1579
|
}
|
|
1513
1580
|
if (!opts.from && !opts.encode) {
|
|
@@ -1521,7 +1588,14 @@ function registerTxCommand(cli) {
|
|
|
1521
1588
|
throw new Error("--encode cannot be used with raw call hex (already encoded)");
|
|
1522
1589
|
}
|
|
1523
1590
|
const config = await loadConfig();
|
|
1524
|
-
|
|
1591
|
+
let effectiveChain = opts.chain;
|
|
1592
|
+
let parsedTarget;
|
|
1593
|
+
if (!isRawCall) {
|
|
1594
|
+
const knownChains = Object.keys(config.chains);
|
|
1595
|
+
parsedTarget = parseTarget(target, { knownChains });
|
|
1596
|
+
effectiveChain = resolveTargetChain(parsedTarget, opts.chain);
|
|
1597
|
+
}
|
|
1598
|
+
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
1525
1599
|
const signer = opts.encode ? undefined : await resolveAccountSigner(opts.from);
|
|
1526
1600
|
let clientHandle;
|
|
1527
1601
|
if (!opts.encode) {
|
|
@@ -1558,7 +1632,8 @@ function registerTxCommand(cli) {
|
|
|
1558
1632
|
tx = await unsafeApi.txFromCallData(callBinary);
|
|
1559
1633
|
callHex = target;
|
|
1560
1634
|
} else {
|
|
1561
|
-
const
|
|
1635
|
+
const pallet = parsedTarget.pallet;
|
|
1636
|
+
const callName = parsedTarget.item;
|
|
1562
1637
|
const palletNames = getPalletNames(meta);
|
|
1563
1638
|
const palletInfo = findPallet(meta, pallet);
|
|
1564
1639
|
if (!palletInfo) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polkadot-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "CLI tool for querying Polkadot-ecosystem on-chain state",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"lint": "biome check .",
|
|
19
19
|
"lint:fix": "biome check --write .",
|
|
20
20
|
"test": "bun test",
|
|
21
|
+
"test:coverage": "bun test --coverage --coverage-reporter=lcov --coverage-dir=coverage",
|
|
21
22
|
"prepare": "husky",
|
|
22
23
|
"changeset": "changeset",
|
|
23
24
|
"version": "changeset version",
|