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.
Files changed (3) hide show
  1. package/README.md +24 -0
  2. package/dist/cli.mjs +95 -20
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![codecov](https://codecov.io/gh/peetzweg/polkadot-cli/branch/main/graph/badge.svg)](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.7.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 (parts.length !== 2 || !parts[0] || !parts[1]) {
854
- throw new Error(`Invalid target "${input}". Expected format: Pallet.Item (e.g. System.Account)`);
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
- return { pallet: parts[0], item: parts[1] };
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 { name: chainName, chain: chainConfig } = resolveChain(config, opts.chain);
872
- const { pallet, item } = parseTarget(target);
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
- printResult(result, opts.output ?? "pretty");
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 { name: chainName, chain: chainConfig } = resolveChain(config, opts.chain);
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 (!target.includes(".")) {
1120
+ if (!itemName) {
1059
1121
  const palletNames2 = getPalletNames(meta);
1060
- const pallet2 = findPallet(meta, target);
1122
+ const pallet2 = findPallet(meta, palletName);
1061
1123
  if (!pallet2) {
1062
- throw new Error(suggestMessage("pallet", target, palletNames2));
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 { name: chainName, chain: chainConfig } = resolveChain(config, opts.chain);
1415
- const { pallet, item } = parseTarget(target);
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
- const { name: chainName, chain: chainConfig } = resolveChain(config, opts.chain);
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 { pallet, item: callName } = parseTarget(target);
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.7.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",