polkadot-cli 0.5.0 → 0.6.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 +54 -1
  2. package/dist/cli.mjs +262 -25
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # polkadot-cli
2
2
 
3
- A command-line tool for interacting with Polkadot-ecosystem chains. Manage chains and accounts, query storage, look up constants, inspect metadata, and submit extrinsics — all from your terminal.
3
+ 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.
4
4
 
5
5
  Ships with Polkadot as the default chain. Add any Substrate-based chain by pointing to its RPC endpoint.
6
6
 
@@ -115,6 +115,21 @@ dot tx 0x0503008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48
115
115
  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
116
116
  ```
117
117
 
118
+ #### Encode call data
119
+
120
+ Encode a call to hex without signing or submitting. Useful for preparing calls to pass to `Sudo.sudo`, multisig proposals, or governance. Works offline from cached metadata and does not require `--from`.
121
+
122
+ ```bash
123
+ # Encode a remark call
124
+ dot tx System.remark 0xdeadbeef --encode
125
+
126
+ # Encode a transfer (use the hex output in a batch or sudo call)
127
+ dot tx Balances.transfer_keep_alive 5FHneW46... 1000000000000 --encode
128
+
129
+ # Use encoded output with Sudo.sudo
130
+ dot tx Sudo.sudo $(dot tx System.remark 0xcafe --encode) --from alice
131
+ ```
132
+
118
133
  Both dry-run and submission display the encoded call hex and a decoded human-readable form:
119
134
 
120
135
  ```
@@ -139,6 +154,29 @@ For manual override, use `--ext` with a JSON object:
139
154
  dot tx System.remark 0xdeadbeef --from alice --ext '{"MyExtension":{"value":"..."}}'
140
155
  ```
141
156
 
157
+ ### Compute hashes
158
+
159
+ Compute cryptographic hashes commonly used in Substrate. Supports BLAKE2b-256, BLAKE2b-128, Keccak-256, and SHA-256.
160
+
161
+ ```bash
162
+ # Hash hex-encoded data
163
+ dot hash blake2b256 0xdeadbeef
164
+
165
+ # Hash plain text (UTF-8 encoded)
166
+ dot hash sha256 hello
167
+
168
+ # Hash file contents
169
+ dot hash keccak256 --file ./data.bin
170
+
171
+ # Read from stdin
172
+ echo -n "hello" | dot hash sha256 --stdin
173
+
174
+ # JSON output
175
+ dot hash blake2b256 0xdeadbeef --output json
176
+ ```
177
+
178
+ Run `dot hash` with no arguments to see all available algorithms.
179
+
142
180
  ### Global options
143
181
 
144
182
  | Flag | Description |
@@ -149,6 +187,21 @@ dot tx System.remark 0xdeadbeef --from alice --ext '{"MyExtension":{"value":"...
149
187
  | `--output json` | Raw JSON output (default: pretty) |
150
188
  | `--limit <n>` | Max entries for map queries (0 = unlimited, default: 100) |
151
189
 
190
+ ## How it compares
191
+
192
+ | | polkadot-cli | @polkadot/api-cli | subxt-cli | Pop CLI |
193
+ |---|---|---|---|---|
194
+ | **Query storage** | SS58 keys, map iteration | yes (full `--ws` URL required) | yes (keys as SCALE tuples, no SS58) | — |
195
+ | **Read constants** | yes | yes | yes | — |
196
+ | **Submit extrinsics** | yes, with dry-run | yes (via `--seed`) | — | ink! contract calls only |
197
+ | **Inspect metadata** | yes | — | yes (excellent browser) | — |
198
+ | **Chain presets** | built-in aliases (`--chain kusama`) | — (manual `--ws` every call) | — | parachain templates |
199
+ | **Tx tracking + explorer links** | spinner progress, block + explorer link | basic events | — | — |
200
+
201
+ polkadot-cli aims to be the single tool for day-to-day chain interaction: storage reads, constant lookups, transaction submission, and metadata browsing with a polished terminal UX. @polkadot/api-cli covers similar ground but is in maintenance mode and requires verbose flags. subxt-cli has an excellent metadata explorer but cannot sign or submit transactions. Pop CLI targets a different workflow — scaffolding parachains and deploying ink! contracts rather than end-user chain queries.
202
+
203
+ Outside Polkadot, the closest comparable in terms of interactive UX is [near-cli-rs](https://github.com/near/near-cli-rs) (NEAR).
204
+
152
205
  ## Configuration
153
206
 
154
207
  Config and metadata caches live in `~/.polkadot/`:
package/dist/cli.mjs CHANGED
@@ -1092,27 +1092,54 @@ var papiLink = (rpc, hash) => `https://dev.papi.how/explorer/${hash}#networkId=c
1092
1092
 
1093
1093
  // src/commands/tx.ts
1094
1094
  function registerTxCommand(cli) {
1095
- 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("--ext <json>", `Custom signed extension values as JSON, e.g. '{"ExtName":{"value":...}}'`).action(async (target, args, opts) => {
1096
- if (!target || !opts.from) {
1097
- console.log("Usage: dot tx <Pallet.Call|0xCallHex> [...args] --from <account> [--dry-run]");
1095
+ 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) => {
1096
+ if (!target) {
1097
+ console.log("Usage: dot tx <Pallet.Call|0xCallHex> [...args] --from <account> [--dry-run] [--encode]");
1098
1098
  console.log("");
1099
1099
  console.log("Examples:");
1100
1100
  console.log(" $ dot tx Balances.transferKeepAlive 5FHn... 1000000000000 --from alice");
1101
1101
  console.log(" $ dot tx System.remark 0xdeadbeef --from alice --dry-run");
1102
1102
  console.log(" $ dot tx 0x0001076465616462656566 --from alice");
1103
+ console.log(" $ dot tx Assets.force_create 4 owner true 10 --encode --chain people");
1103
1104
  return;
1104
1105
  }
1106
+ if (!opts.from && !opts.encode) {
1107
+ throw new Error("--from is required (or use --encode to output hex without signing)");
1108
+ }
1109
+ if (opts.encode && opts.dryRun) {
1110
+ throw new Error("--encode and --dry-run are mutually exclusive");
1111
+ }
1105
1112
  const isRawCall = /^0x[0-9a-fA-F]+$/.test(target);
1113
+ if (opts.encode && isRawCall) {
1114
+ throw new Error("--encode cannot be used with raw call hex (already encoded)");
1115
+ }
1106
1116
  const config = await loadConfig();
1107
1117
  const { name: chainName, chain: chainConfig } = resolveChain(config, opts.chain);
1108
- const signer = await resolveAccountSigner(opts.from);
1109
- const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
1118
+ const signer = opts.encode ? undefined : await resolveAccountSigner(opts.from);
1119
+ let clientHandle;
1120
+ if (!opts.encode) {
1121
+ clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
1122
+ }
1110
1123
  try {
1111
- const meta = await getOrFetchMetadata(chainName, clientHandle);
1112
- const userExtOverrides = parseExtOption(opts.ext);
1113
- const customSignedExtensions = buildCustomSignedExtensions(meta, userExtOverrides);
1114
- const txOptions = Object.keys(customSignedExtensions).length > 0 ? { customSignedExtensions } : undefined;
1115
- const unsafeApi = clientHandle.client.getUnsafeApi();
1124
+ let meta;
1125
+ if (clientHandle) {
1126
+ meta = await getOrFetchMetadata(chainName, clientHandle);
1127
+ } else {
1128
+ try {
1129
+ meta = await getOrFetchMetadata(chainName);
1130
+ } catch {
1131
+ clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
1132
+ meta = await getOrFetchMetadata(chainName, clientHandle);
1133
+ }
1134
+ }
1135
+ let unsafeApi;
1136
+ let txOptions;
1137
+ if (!opts.encode) {
1138
+ const userExtOverrides = parseExtOption(opts.ext);
1139
+ const customSignedExtensions = buildCustomSignedExtensions(meta, userExtOverrides);
1140
+ txOptions = Object.keys(customSignedExtensions).length > 0 ? { customSignedExtensions } : undefined;
1141
+ unsafeApi = clientHandle.client.getUnsafeApi();
1142
+ }
1116
1143
  let tx;
1117
1144
  let callHex;
1118
1145
  if (isRawCall) {
@@ -1136,6 +1163,17 @@ function registerTxCommand(cli) {
1136
1163
  throw new Error(suggestMessage(`call in ${palletInfo.name}`, callName, callNames));
1137
1164
  }
1138
1165
  const callData = parseCallArgs(meta, palletInfo.name, callInfo.name, args);
1166
+ if (opts.encode) {
1167
+ const { codec, location } = meta.builder.buildCall(palletInfo.name, callInfo.name);
1168
+ const encodedArgs = codec.enc(callData);
1169
+ const fullCall = new Uint8Array([
1170
+ location[0],
1171
+ location[1],
1172
+ ...encodedArgs
1173
+ ]);
1174
+ console.log(Binary.fromBytes(fullCall).asHex());
1175
+ return;
1176
+ }
1139
1177
  tx = unsafeApi.tx[palletInfo.name][callInfo.name](callData);
1140
1178
  const encodedCall = await tx.getEncodedData();
1141
1179
  callHex = encodedCall.asHex();
@@ -1189,7 +1227,7 @@ function registerTxCommand(cli) {
1189
1227
  }
1190
1228
  console.log();
1191
1229
  } finally {
1192
- clientHandle.destroy();
1230
+ clientHandle?.destroy();
1193
1231
  }
1194
1232
  });
1195
1233
  }
@@ -1312,33 +1350,33 @@ function parseCallArgs(meta, palletName, callName, args) {
1312
1350
  return;
1313
1351
  }
1314
1352
  if (variant.type === "struct") {
1315
- return parseStructArgs(meta.lookup, variant.value, args, `${palletName}.${callName}`);
1353
+ return parseStructArgs(meta, variant.value, args, `${palletName}.${callName}`);
1316
1354
  }
1317
1355
  if (variant.type === "lookupEntry") {
1318
1356
  const inner = variant.value;
1319
1357
  if (inner.type === "struct") {
1320
- return parseStructArgs(meta.lookup, inner.value, args, `${palletName}.${callName}`);
1358
+ return parseStructArgs(meta, inner.value, args, `${palletName}.${callName}`);
1321
1359
  }
1322
1360
  if (inner.type === "void")
1323
1361
  return;
1324
1362
  if (args.length !== 1) {
1325
1363
  throw new Error(`${palletName}.${callName} takes 1 argument (${describeType(meta.lookup, inner.id)}), but ${args.length} provided.`);
1326
1364
  }
1327
- return parseTypedArg(meta.lookup, inner, args[0]);
1365
+ return parseTypedArg(meta, inner, args[0]);
1328
1366
  }
1329
1367
  if (variant.type === "tuple") {
1330
1368
  const entries = variant.value;
1331
1369
  if (args.length !== entries.length) {
1332
1370
  throw new Error(`${palletName}.${callName} takes ${entries.length} arguments, but ${args.length} provided.`);
1333
1371
  }
1334
- return entries.map((entry, i) => parseTypedArg(meta.lookup, entry, args[i]));
1372
+ return entries.map((entry, i) => parseTypedArg(meta, entry, args[i]));
1335
1373
  }
1336
1374
  return args.length === 0 ? undefined : args.map(parseValue);
1337
1375
  }
1338
- function parseStructArgs(lookup, fields, args, callLabel) {
1376
+ function parseStructArgs(meta, fields, args, callLabel) {
1339
1377
  const fieldNames = Object.keys(fields);
1340
1378
  if (args.length !== fieldNames.length) {
1341
- const expected = fieldNames.map((name) => `${name}: ${describeType(lookup, fields[name].id)}`).join(", ");
1379
+ const expected = fieldNames.map((name) => `${name}: ${describeType(meta.lookup, fields[name].id)}`).join(", ");
1342
1380
  throw new Error(`${callLabel} takes ${fieldNames.length} argument(s): ${expected}
1343
1381
  ` + ` Got ${args.length} argument(s).`);
1344
1382
  }
@@ -1346,11 +1384,78 @@ function parseStructArgs(lookup, fields, args, callLabel) {
1346
1384
  for (let i = 0;i < fieldNames.length; i++) {
1347
1385
  const name = fieldNames[i];
1348
1386
  const entry = fields[name];
1349
- result[name] = parseTypedArg(lookup, entry, args[i]);
1387
+ result[name] = parseTypedArg(meta, entry, args[i]);
1350
1388
  }
1351
1389
  return result;
1352
1390
  }
1353
- function parseTypedArg(lookup, entry, arg) {
1391
+ function normalizeValue(lookup, entry, value) {
1392
+ let resolved = entry;
1393
+ while (resolved.type === "lookupEntry") {
1394
+ resolved = resolved.value;
1395
+ }
1396
+ switch (resolved.type) {
1397
+ case "enum": {
1398
+ if (value !== null && typeof value === "object" && !Array.isArray(value) && "type" in value) {
1399
+ const enumValue = value;
1400
+ const variant = resolved.value[enumValue.type];
1401
+ if (variant) {
1402
+ let innerEntry = variant;
1403
+ while (innerEntry.type === "lookupEntry") {
1404
+ innerEntry = innerEntry.value;
1405
+ }
1406
+ let normalizedInner = enumValue.value;
1407
+ if (innerEntry.type !== "array" && innerEntry.type !== "sequence" && innerEntry.type !== "void" && Array.isArray(normalizedInner) && normalizedInner.length === 1) {
1408
+ normalizedInner = normalizedInner[0];
1409
+ }
1410
+ if (normalizedInner !== undefined && innerEntry.type !== "void") {
1411
+ normalizedInner = normalizeValue(lookup, innerEntry, normalizedInner);
1412
+ }
1413
+ return { type: enumValue.type, value: normalizedInner };
1414
+ }
1415
+ }
1416
+ return value;
1417
+ }
1418
+ case "struct": {
1419
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
1420
+ const fields = resolved.value;
1421
+ const result = {};
1422
+ for (const [key, val] of Object.entries(value)) {
1423
+ if (key in fields) {
1424
+ result[key] = normalizeValue(lookup, fields[key], val);
1425
+ } else {
1426
+ result[key] = val;
1427
+ }
1428
+ }
1429
+ return result;
1430
+ }
1431
+ return value;
1432
+ }
1433
+ case "array":
1434
+ case "sequence": {
1435
+ if (Array.isArray(value)) {
1436
+ const innerEntry = resolved.value;
1437
+ return value.map((item) => normalizeValue(lookup, innerEntry, item));
1438
+ }
1439
+ return value;
1440
+ }
1441
+ case "tuple": {
1442
+ if (Array.isArray(value)) {
1443
+ const entries = resolved.value;
1444
+ return value.map((item, i) => i < entries.length ? normalizeValue(lookup, entries[i], item) : item);
1445
+ }
1446
+ return value;
1447
+ }
1448
+ case "option": {
1449
+ if (value !== null && value !== undefined) {
1450
+ return normalizeValue(lookup, resolved.value, value);
1451
+ }
1452
+ return value;
1453
+ }
1454
+ default:
1455
+ return value;
1456
+ }
1457
+ }
1458
+ function parseTypedArg(meta, entry, arg) {
1354
1459
  switch (entry.type) {
1355
1460
  case "primitive":
1356
1461
  return parsePrimitive(entry.value, arg);
@@ -1363,12 +1468,16 @@ function parseTypedArg(lookup, entry, arg) {
1363
1468
  if (arg === "null" || arg === "undefined" || arg === "none") {
1364
1469
  return;
1365
1470
  }
1366
- return parseTypedArg(lookup, entry.value, arg);
1471
+ return parseTypedArg(meta, entry.value, arg);
1367
1472
  }
1368
1473
  case "enum": {
1474
+ if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
1475
+ const callCodec = meta.builder.buildDefinition(meta.lookup.call);
1476
+ return callCodec.dec(Binary.fromHex(arg).asBytes());
1477
+ }
1369
1478
  if (arg.startsWith("{")) {
1370
1479
  try {
1371
- return JSON.parse(arg);
1480
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1372
1481
  } catch {}
1373
1482
  }
1374
1483
  const variants = Object.keys(entry.value);
@@ -1398,7 +1507,7 @@ function parseTypedArg(lookup, entry, arg) {
1398
1507
  }
1399
1508
  if (arg.startsWith("[")) {
1400
1509
  try {
1401
- return JSON.parse(arg);
1510
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1402
1511
  } catch {}
1403
1512
  }
1404
1513
  if (/^0x[0-9a-fA-F]*$/.test(arg))
@@ -1408,14 +1517,14 @@ function parseTypedArg(lookup, entry, arg) {
1408
1517
  case "struct":
1409
1518
  if (arg.startsWith("{")) {
1410
1519
  try {
1411
- return JSON.parse(arg);
1520
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1412
1521
  } catch {}
1413
1522
  }
1414
1523
  return parseValue(arg);
1415
1524
  case "tuple":
1416
1525
  if (arg.startsWith("[")) {
1417
1526
  try {
1418
- return JSON.parse(arg);
1527
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1419
1528
  } catch {}
1420
1529
  }
1421
1530
  return parseValue(arg);
@@ -1553,6 +1662,133 @@ function watchTransaction(observable) {
1553
1662
  });
1554
1663
  }
1555
1664
 
1665
+ // src/core/hash.ts
1666
+ import { blake2b } from "@noble/hashes/blake2.js";
1667
+ import { keccak_256 } from "@noble/hashes/sha3.js";
1668
+ import { sha256 } from "@noble/hashes/sha2.js";
1669
+ import { bytesToHex, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
1670
+ var ALGORITHMS = {
1671
+ blake2b256: {
1672
+ compute: (data) => blake2b(data, { dkLen: 32 }),
1673
+ outputLen: 32,
1674
+ description: "BLAKE2b with 256-bit output"
1675
+ },
1676
+ blake2b128: {
1677
+ compute: (data) => blake2b(data, { dkLen: 16 }),
1678
+ outputLen: 16,
1679
+ description: "BLAKE2b with 128-bit output"
1680
+ },
1681
+ keccak256: {
1682
+ compute: (data) => keccak_256(data),
1683
+ outputLen: 32,
1684
+ description: "Keccak-256 (Ethereum-compatible)"
1685
+ },
1686
+ sha256: {
1687
+ compute: (data) => sha256(data),
1688
+ outputLen: 32,
1689
+ description: "SHA-256"
1690
+ }
1691
+ };
1692
+ function computeHash(algorithm, data) {
1693
+ const algo = ALGORITHMS[algorithm];
1694
+ if (!algo) {
1695
+ throw new Error(`Unknown algorithm: ${algorithm}`);
1696
+ }
1697
+ return algo.compute(data);
1698
+ }
1699
+ function parseInputData(input) {
1700
+ if (input.startsWith("0x")) {
1701
+ const hex = input.slice(2);
1702
+ if (hex.length % 2 !== 0) {
1703
+ throw new Error(`Invalid hex input: odd number of characters`);
1704
+ }
1705
+ return hexToBytes2(hex);
1706
+ }
1707
+ return new TextEncoder().encode(input);
1708
+ }
1709
+ function toHex(bytes) {
1710
+ return "0x" + bytesToHex(bytes);
1711
+ }
1712
+ function isValidAlgorithm(name) {
1713
+ return name in ALGORITHMS;
1714
+ }
1715
+ function getAlgorithmNames() {
1716
+ return Object.keys(ALGORITHMS);
1717
+ }
1718
+
1719
+ // src/commands/hash.ts
1720
+ async function resolveInput(data, opts) {
1721
+ const sources = [data !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
1722
+ if (sources > 1) {
1723
+ throw new CliError("Provide only one of: inline data, --file, or --stdin");
1724
+ }
1725
+ if (sources === 0) {
1726
+ throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
1727
+ }
1728
+ if (opts.file) {
1729
+ const buf = await Bun.file(opts.file).arrayBuffer();
1730
+ return new Uint8Array(buf);
1731
+ }
1732
+ if (opts.stdin) {
1733
+ const reader = Bun.stdin.stream().getReader();
1734
+ const chunks = [];
1735
+ while (true) {
1736
+ const { done, value } = await reader.read();
1737
+ if (done)
1738
+ break;
1739
+ chunks.push(value);
1740
+ }
1741
+ const totalLen = chunks.reduce((sum, c) => sum + c.length, 0);
1742
+ const result = new Uint8Array(totalLen);
1743
+ let offset = 0;
1744
+ for (const chunk of chunks) {
1745
+ result.set(chunk, offset);
1746
+ offset += chunk.length;
1747
+ }
1748
+ return result;
1749
+ }
1750
+ return parseInputData(data);
1751
+ }
1752
+ function printAlgorithmHelp() {
1753
+ console.log(`${BOLD}Usage:${RESET} dot hash <algorithm> <data> [options]
1754
+ `);
1755
+ console.log(`${BOLD}Algorithms:${RESET}`);
1756
+ for (const [name, algo] of Object.entries(ALGORITHMS)) {
1757
+ console.log(` ${CYAN}${name}${RESET} ${DIM}${algo.description} (${algo.outputLen} bytes)${RESET}`);
1758
+ }
1759
+ console.log(`
1760
+ ${BOLD}Options:${RESET}`);
1761
+ console.log(` ${CYAN}--file <path>${RESET} ${DIM}Hash file contents${RESET}`);
1762
+ console.log(` ${CYAN}--stdin${RESET} ${DIM}Read from stdin${RESET}`);
1763
+ console.log(` ${CYAN}--output json${RESET} ${DIM}Output as JSON${RESET}`);
1764
+ console.log(`
1765
+ ${BOLD}Examples:${RESET}`);
1766
+ console.log(` ${DIM}$ dot hash blake2b256 0xdeadbeef${RESET}`);
1767
+ console.log(` ${DIM}$ dot hash sha256 hello${RESET}`);
1768
+ console.log(` ${DIM}$ dot hash keccak256 --file ./data.bin${RESET}`);
1769
+ console.log(` ${DIM}$ echo -n "hello" | dot hash sha256 --stdin${RESET}`);
1770
+ }
1771
+ function registerHashCommand(cli) {
1772
+ cli.command("hash [algorithm] [data]", "Compute cryptographic hashes").option("--file <path>", "Hash file contents (raw bytes)").option("--stdin", "Read data from stdin").action(async (algorithm, data, opts) => {
1773
+ if (!algorithm) {
1774
+ printAlgorithmHelp();
1775
+ return;
1776
+ }
1777
+ if (!isValidAlgorithm(algorithm)) {
1778
+ throw new CliError(suggestMessage("algorithm", algorithm, getAlgorithmNames()));
1779
+ }
1780
+ const input = await resolveInput(data, opts);
1781
+ const hash = computeHash(algorithm, input);
1782
+ const hexHash = toHex(hash);
1783
+ const format = opts.output ?? "pretty";
1784
+ if (format === "json") {
1785
+ printResult({ algorithm, input: data ?? (opts.file ? `file:${opts.file}` : "stdin"), hash: hexHash }, "json");
1786
+ } else {
1787
+ console.log(hexHash);
1788
+ }
1789
+ });
1790
+ }
1791
+
1556
1792
  // src/utils/errors.ts
1557
1793
  class CliError2 extends Error {
1558
1794
  constructor(message) {
@@ -1561,7 +1797,7 @@ class CliError2 extends Error {
1561
1797
  }
1562
1798
  }
1563
1799
  // package.json
1564
- var version = "0.5.0";
1800
+ var version = "0.6.0";
1565
1801
 
1566
1802
  // src/cli.ts
1567
1803
  var cli = cac("dot");
@@ -1577,6 +1813,7 @@ registerQueryCommand(cli);
1577
1813
  registerConstCommand(cli);
1578
1814
  registerAccountCommands(cli);
1579
1815
  registerTxCommand(cli);
1816
+ registerHashCommand(cli);
1580
1817
  cli.help();
1581
1818
  cli.version(version);
1582
1819
  function handleError(err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polkadot-cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "CLI tool for querying Polkadot-ecosystem on-chain state",
5
5
  "type": "module",
6
6
  "bin": {
@@ -32,6 +32,7 @@
32
32
  "url": "git+https://github.com/peetzweg/polkadot-cli.git"
33
33
  },
34
34
  "dependencies": {
35
+ "@noble/hashes": "^2.0.1",
35
36
  "@polkadot-api/metadata-builders": "^0.13.9",
36
37
  "@polkadot-api/substrate-bindings": "^0.17.0",
37
38
  "@polkadot-api/view-builder": "^0.4.17",