polkadot-cli 0.12.0 → 0.13.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 +65 -3
  2. package/dist/cli.mjs +265 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -103,6 +103,45 @@ MY_SECRET="word1 word2 ..." dot tx System.remark 0xdead --from ci-signer
103
103
  # Remove one or more accounts
104
104
  dot account remove my-validator
105
105
  dot account delete my-validator stale-key
106
+
107
+ # Inspect an account — show public key and SS58 address
108
+ dot account inspect alice
109
+ dot account alice # shorthand (same as inspect)
110
+ dot account inspect 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
111
+ dot account inspect 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
112
+ dot account inspect alice --prefix 0 # Polkadot mainnet prefix
113
+ dot account inspect alice --output json # JSON output
114
+ ```
115
+
116
+ #### Inspect accounts
117
+
118
+ Convert between SS58 addresses, hex public keys, and account names. Accepts any of:
119
+
120
+ - **Dev account name** (`alice`, `bob`, etc.) — resolves to public key and SS58
121
+ - **Stored account name** — looks up the public key from the accounts file
122
+ - **SS58 address** — decodes to the underlying public key
123
+ - **Hex public key** (`0x` + 64 hex chars) — encodes to SS58
124
+
125
+ ```bash
126
+ dot account inspect alice
127
+ dot account alice # shorthand — unknown subcommands fall through to inspect
128
+
129
+ dot account inspect 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
130
+ dot account inspect 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
131
+ ```
132
+
133
+ Use `--prefix` to encode the SS58 address with a specific network prefix (default: 42):
134
+
135
+ ```bash
136
+ dot account inspect alice --prefix 0 # Polkadot mainnet (prefix 0, starts with '1')
137
+ dot account inspect alice --prefix 2 # Kusama (prefix 2)
138
+ ```
139
+
140
+ JSON output:
141
+
142
+ ```bash
143
+ dot account inspect alice --output json
144
+ # {"publicKey":"0xd435...a27d","ss58":"5Grw...utQY","prefix":42,"name":"Alice"}
106
145
  ```
107
146
 
108
147
  #### Env-var-backed accounts
@@ -219,20 +258,25 @@ dot const Balances.ExistentialDeposit --output json | jq
219
258
  Works offline from cached metadata after the first fetch.
220
259
 
221
260
  ```bash
222
- # List all pallets
261
+ # List all pallets (shows storage, constants, and calls counts)
223
262
  dot inspect
224
263
 
225
- # List a pallet's storage items and constants
264
+ # List a pallet's storage items, constants, and calls
226
265
  dot inspect System
227
266
 
228
- # Detailed type info for a specific item
267
+ # Detailed type info for a specific storage item or constant
229
268
  dot inspect System.Account
230
269
 
270
+ # Call detail — shows argument signature and docs
271
+ dot inspect Balances.transfer_allow_death
272
+
231
273
  # Inspect a specific chain using chain prefix
232
274
  dot inspect kusama.System
233
275
  dot inspect kusama.System.Account
234
276
  ```
235
277
 
278
+ Use call inspection to discover argument names and types before constructing `dot tx` commands.
279
+
236
280
  ### Submit extrinsics
237
281
 
238
282
  Build, sign, and submit transactions. Pass a `Pallet.Call` with arguments, or a raw SCALE-encoded call hex (e.g. from a multisig proposal or governance). Both forms display a decoded human-readable representation of the call.
@@ -299,6 +343,12 @@ Both dry-run and submission display the encoded call hex and a decoded human-rea
299
343
  Status: ok
300
344
  ```
301
345
 
346
+ Complex calls (e.g. XCM teleports) that the primary decoder cannot handle are automatically decoded via a fallback path:
347
+
348
+ ```
349
+ Decode: PolkadotXcm.limited_teleport_assets { dest: V3 { parents: 1, interior: X1(Parachain(5140)) }, beneficiary: V3 { ... }, assets: V3 [...], fee_asset_item: 0, weight_limit: Unlimited }
350
+ ```
351
+
302
352
  #### Exit codes
303
353
 
304
354
  The CLI exits with code **1** when a finalized transaction has a dispatch error (e.g. insufficient balance, bad origin). The full transaction output (events, explorer links) is still printed before the error so you can debug the failure. Module errors are formatted as `PalletName.ErrorVariant` (e.g. `Balances.InsufficientBalance`).
@@ -310,6 +360,18 @@ dot tx Balances.transferKeepAlive 5FHneW46... 999999999999999999 --from alice
310
360
  echo $? # 1
311
361
  ```
312
362
 
363
+ #### Argument parsing errors
364
+
365
+ When a call argument is invalid, the CLI shows a contextual error message with the argument name, the expected type, and a hint:
366
+
367
+ ```bash
368
+ dot tx Balances.transferKeepAlive 5GrwvaEF... abc --encode
369
+ # Error: Invalid value for argument 'value' (expected Compact<u128>): "abc"
370
+ # Hint: Compact<u128>
371
+ ```
372
+
373
+ For struct-based calls, the error identifies the specific field that failed. For tuple-based calls, it shows the argument index. The original parse error is preserved as the `cause` for programmatic access.
374
+
313
375
  #### Custom signed extensions
314
376
 
315
377
  Chains with non-standard signed extensions (e.g. `people-preview`) are auto-handled:
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.12.0";
8
+ var version = "0.13.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";
@@ -247,6 +247,7 @@ import {
247
247
  generateMnemonic,
248
248
  mnemonicToEntropy,
249
249
  ss58Address,
250
+ ss58Decode,
250
251
  validateMnemonic
251
252
  } from "@polkadot-labs/hdkd-helpers";
252
253
  import { getPolkadotSigner } from "polkadot-api/signer";
@@ -314,6 +315,13 @@ function toSs58(publicKey, prefix = 42) {
314
315
  }
315
316
  return ss58Address(publicKey, prefix);
316
317
  }
318
+ function fromSs58(address) {
319
+ const [payload] = ss58Decode(address);
320
+ return payload;
321
+ }
322
+ function isHexPublicKey(input) {
323
+ return /^0x[0-9a-fA-F]{64}$/.test(input);
324
+ }
317
325
  function resolveSecret(secret) {
318
326
  if (isEnvSecret(secret)) {
319
327
  const value = process.env[secret.env];
@@ -452,6 +460,7 @@ ${BOLD}Usage:${RESET}
452
460
  $ dot account import|add <name> --secret <s> [--path <derivation>] Import from BIP39 mnemonic
453
461
  $ dot account import|add <name> --env <VAR> [--path <derivation>] Import account backed by env variable
454
462
  $ dot account derive <source> <new-name> --path <derivation> Derive a child account
463
+ $ dot account inspect <input> [--prefix <N>] Inspect an account/address/key
455
464
  $ dot account list List all accounts
456
465
  $ dot account remove|delete <name> [name2] ... Remove stored account(s)
457
466
 
@@ -462,6 +471,9 @@ ${BOLD}Examples:${RESET}
462
471
  $ dot account import treasury --secret "word1 word2 ... word12"
463
472
  $ dot account import ci-signer --env MY_SECRET --path //ci
464
473
  $ dot account derive treasury treasury-staking --path //staking
474
+ $ dot account inspect alice
475
+ $ dot account inspect 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
476
+ $ dot account inspect 0xd435...a27d --prefix 0
465
477
  $ dot account list
466
478
  $ dot account remove my-validator stale-key
467
479
 
@@ -470,7 +482,7 @@ ${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
470
482
  Hex seed import (0x...) is not supported via CLI.${RESET}
471
483
  `.trimStart();
472
484
  function registerAccountCommands(cli) {
473
- 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)").action(async (action, names, opts) => {
485
+ 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) => {
474
486
  if (!action) {
475
487
  if (process.argv[2] === "accounts")
476
488
  return accountList();
@@ -491,11 +503,10 @@ function registerAccountCommands(cli) {
491
503
  case "delete":
492
504
  case "remove":
493
505
  return accountRemove(names);
506
+ case "inspect":
507
+ return accountInspect(names[0], opts);
494
508
  default:
495
- console.error(`Unknown action "${action}".
496
- `);
497
- console.log(ACCOUNT_HELP);
498
- process.exit(1);
509
+ return accountInspect(action, opts);
499
510
  }
500
511
  });
501
512
  }
@@ -743,6 +754,70 @@ async function accountRemove(names) {
743
754
  console.log(`Account "${name}" removed.`);
744
755
  }
745
756
  }
757
+ async function accountInspect(input, opts) {
758
+ if (!input) {
759
+ console.error(`Input is required.
760
+ `);
761
+ console.error("Usage: dot account inspect <name|ss58-address|0x-public-key> [--prefix <N>]");
762
+ process.exit(1);
763
+ }
764
+ const prefix = opts.prefix != null ? Number(opts.prefix) : 42;
765
+ if (Number.isNaN(prefix) || prefix < 0) {
766
+ console.error(`Invalid prefix "${opts.prefix}". Must be a non-negative integer.`);
767
+ process.exit(1);
768
+ }
769
+ let name;
770
+ let publicKeyHex;
771
+ if (isDevAccount(input)) {
772
+ name = input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
773
+ const devAddr = getDevAddress(input);
774
+ publicKeyHex = publicKeyToHex(fromSs58(devAddr));
775
+ } else {
776
+ const accountsFile = await loadAccounts();
777
+ const account = findAccount(accountsFile, input);
778
+ if (account) {
779
+ name = account.name;
780
+ if (account.publicKey) {
781
+ publicKeyHex = account.publicKey;
782
+ } else if (isEnvSecret(account.secret)) {
783
+ const derived = tryDerivePublicKey(account.secret.env, account.derivationPath);
784
+ if (!derived) {
785
+ console.error(`Cannot derive public key for "${account.name}": $${account.secret.env} is not set.`);
786
+ process.exit(1);
787
+ }
788
+ publicKeyHex = derived;
789
+ } else {
790
+ console.error(`Account "${account.name}" has no public key.`);
791
+ process.exit(1);
792
+ }
793
+ } else if (isHexPublicKey(input)) {
794
+ publicKeyHex = input;
795
+ } else {
796
+ try {
797
+ const decoded = fromSs58(input);
798
+ publicKeyHex = publicKeyToHex(decoded);
799
+ } catch {
800
+ console.error(`Cannot identify "${input}" as an account name, SS58 address, or hex public key.`);
801
+ process.exit(1);
802
+ }
803
+ }
804
+ }
805
+ const ss58 = toSs58(publicKeyHex, prefix);
806
+ if (opts.output === "json") {
807
+ const result = { publicKey: publicKeyHex, ss58, prefix };
808
+ if (name)
809
+ result.name = name;
810
+ console.log(formatJson(result));
811
+ } else {
812
+ printHeading("Account Info");
813
+ if (name)
814
+ console.log(` ${BOLD}Name:${RESET} ${name}`);
815
+ console.log(` ${BOLD}Public Key:${RESET} ${publicKeyHex}`);
816
+ console.log(` ${BOLD}SS58:${RESET} ${ss58}`);
817
+ console.log(` ${BOLD}Prefix:${RESET} ${prefix}`);
818
+ console.log();
819
+ }
820
+ }
746
821
 
747
822
  // src/core/client.ts
748
823
  import { createClient } from "polkadot-api";
@@ -984,6 +1059,42 @@ function formatLookupEntry(entry) {
984
1059
  return "unknown";
985
1060
  }
986
1061
  }
1062
+ function describeCallArgs(meta, palletName, callName) {
1063
+ try {
1064
+ const palletMeta = meta.unified.pallets.find((p) => p.name === palletName);
1065
+ if (!palletMeta?.calls)
1066
+ return "";
1067
+ const callsEntry = meta.lookup(palletMeta.calls.type);
1068
+ if (callsEntry.type !== "enum")
1069
+ return "";
1070
+ const variant = callsEntry.value[callName];
1071
+ if (!variant)
1072
+ return "";
1073
+ if (variant.type === "void")
1074
+ return "()";
1075
+ if (variant.type === "struct") {
1076
+ const fields = Object.entries(variant.value).map(([k, v]) => `${k}: ${formatLookupEntry(v)}`).join(", ");
1077
+ return `(${fields})`;
1078
+ }
1079
+ if (variant.type === "lookupEntry") {
1080
+ const inner = variant.value;
1081
+ if (inner.type === "void")
1082
+ return "()";
1083
+ if (inner.type === "struct") {
1084
+ const fields = Object.entries(inner.value).map(([k, v]) => `${k}: ${formatLookupEntry(v)}`).join(", ");
1085
+ return `(${fields})`;
1086
+ }
1087
+ return `(${formatLookupEntry(inner)})`;
1088
+ }
1089
+ if (variant.type === "tuple") {
1090
+ const types = variant.value.map(formatLookupEntry).join(", ");
1091
+ return `(${types})`;
1092
+ }
1093
+ return "";
1094
+ } catch {
1095
+ return "";
1096
+ }
1097
+ }
987
1098
  function hexToBytes(hex) {
988
1099
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
989
1100
  const bytes = new Uint8Array(clean.length / 2);
@@ -1436,6 +1547,8 @@ function registerInspectCommand(cli) {
1436
1547
  counts.push(`${p.storage.length} storage`);
1437
1548
  if (p.constants.length)
1438
1549
  counts.push(`${p.constants.length} constants`);
1550
+ if (p.calls.length)
1551
+ counts.push(`${p.calls.length} calls`);
1439
1552
  printItem(p.name, counts.join(", "));
1440
1553
  }
1441
1554
  console.log();
@@ -1468,6 +1581,14 @@ function registerInspectCommand(cli) {
1468
1581
  }
1469
1582
  console.log();
1470
1583
  }
1584
+ if (pallet2.calls.length) {
1585
+ console.log(` ${BOLD}Calls:${RESET}`);
1586
+ for (const c of pallet2.calls) {
1587
+ const doc = c.docs[0] ? ` — ${c.docs[0].slice(0, 80)}` : "";
1588
+ console.log(` ${CYAN}${c.name}${RESET}${DIM}${doc}${RESET}`);
1589
+ }
1590
+ console.log();
1591
+ }
1471
1592
  return;
1472
1593
  }
1473
1594
  const palletNames = getPalletNames(meta);
@@ -1501,9 +1622,22 @@ function registerInspectCommand(cli) {
1501
1622
  console.log();
1502
1623
  return;
1503
1624
  }
1625
+ const callItem = pallet.calls.find((c) => c.name.toLowerCase() === itemName.toLowerCase());
1626
+ if (callItem) {
1627
+ printHeading(`${pallet.name}.${callItem.name} (Call)`);
1628
+ const args = describeCallArgs(meta, pallet.name, callItem.name);
1629
+ console.log(` ${BOLD}Args:${RESET} ${args}`);
1630
+ if (callItem.docs.length) {
1631
+ console.log();
1632
+ printDocs(callItem.docs);
1633
+ }
1634
+ console.log();
1635
+ return;
1636
+ }
1504
1637
  const allItems = [
1505
1638
  ...pallet.storage.map((s) => s.name),
1506
- ...pallet.constants.map((c) => c.name)
1639
+ ...pallet.constants.map((c) => c.name),
1640
+ ...pallet.calls.map((c) => c.name)
1507
1641
  ];
1508
1642
  throw new Error(suggestMessage(`item in ${pallet.name}`, itemName, allItems));
1509
1643
  });
@@ -1549,10 +1683,36 @@ function parseStructArgs(meta, fields, args, callLabel) {
1549
1683
  for (let i = 0;i < fieldNames.length; i++) {
1550
1684
  const name = fieldNames[i];
1551
1685
  const entry = fields[name];
1552
- result[name] = parseTypedArg(meta, entry, args[i]);
1686
+ try {
1687
+ result[name] = parseTypedArg(meta, entry, args[i]);
1688
+ } catch (err) {
1689
+ const typeDesc = describeType(meta.lookup, entry.id);
1690
+ throw new Error(`Invalid value for argument '${name}' (expected ${typeDesc}): ${JSON.stringify(args[i])}
1691
+ ` + ` Hint: ${typeHint(entry, meta)}`, { cause: err });
1692
+ }
1553
1693
  }
1554
1694
  return result;
1555
1695
  }
1696
+ function typeHint(entry, meta) {
1697
+ const resolved = entry.type === "lookupEntry" ? entry.value : entry;
1698
+ switch (resolved.type) {
1699
+ case "enum": {
1700
+ const variants = Object.keys(resolved.value);
1701
+ if (variants.length <= 6)
1702
+ return `a variant: ${variants.join(" | ")}`;
1703
+ return `one of ${variants.length} variants (e.g. ${variants.slice(0, 3).join(", ")})`;
1704
+ }
1705
+ case "struct":
1706
+ return `a JSON object with fields: ${Object.keys(resolved.value).join(", ")}`;
1707
+ case "tuple":
1708
+ return "a JSON array";
1709
+ case "sequence":
1710
+ case "array":
1711
+ return "a JSON array or hex-encoded bytes";
1712
+ default:
1713
+ return describeType(meta.lookup, entry.id);
1714
+ }
1715
+ }
1556
1716
  function normalizeValue(lookup, entry, value) {
1557
1717
  let resolved = entry;
1558
1718
  while (resolved.type === "lookupEntry") {
@@ -2084,10 +2244,66 @@ function decodeCall(meta, callHex) {
2084
2244
  const callName = decoded.call.value.name;
2085
2245
  const argsStr = formatDecodedArgs(decoded.args.value);
2086
2246
  return `${palletName}.${callName}${argsStr}`;
2247
+ } catch {}
2248
+ try {
2249
+ return decodeCallFallback(meta, callHex);
2087
2250
  } catch {
2088
2251
  return "(unable to decode)";
2089
2252
  }
2090
2253
  }
2254
+ function decodeCallFallback(meta, callHex) {
2255
+ const callTypeId = meta.lookup.call;
2256
+ if (callTypeId == null)
2257
+ throw new Error("No RuntimeCall type ID");
2258
+ const codec = meta.builder.buildDefinition(callTypeId);
2259
+ const decoded = codec.dec(Binary3.fromHex(callHex).asBytes());
2260
+ const palletName = decoded.type;
2261
+ const call = decoded.value;
2262
+ const callName = call.type;
2263
+ const args = call.value;
2264
+ if (args === undefined || args === null) {
2265
+ return `${palletName}.${callName}`;
2266
+ }
2267
+ const argsStr = formatRawDecoded(args);
2268
+ return `${palletName}.${callName} ${argsStr}`;
2269
+ }
2270
+ function formatRawDecoded(value) {
2271
+ if (value === undefined || value === null)
2272
+ return "null";
2273
+ if (value instanceof Binary3)
2274
+ return value.asHex();
2275
+ if (typeof value === "bigint")
2276
+ return value.toString();
2277
+ if (typeof value === "string")
2278
+ return value;
2279
+ if (typeof value === "number")
2280
+ return value.toString();
2281
+ if (typeof value === "boolean")
2282
+ return value.toString();
2283
+ if (Array.isArray(value)) {
2284
+ if (value.length === 0)
2285
+ return "[]";
2286
+ return `[${value.map(formatRawDecoded).join(", ")}]`;
2287
+ }
2288
+ if (typeof value === "object") {
2289
+ const obj = value;
2290
+ if ("type" in obj && typeof obj.type === "string") {
2291
+ const inner = obj.value;
2292
+ if (inner === undefined || inner === null)
2293
+ return obj.type;
2294
+ const innerStr = formatRawDecoded(inner);
2295
+ if (innerStr.startsWith("{"))
2296
+ return `${obj.type} ${innerStr}`;
2297
+ return `${obj.type}(${innerStr})`;
2298
+ }
2299
+ const entries = Object.entries(obj);
2300
+ if (entries.length === 0)
2301
+ return "{}";
2302
+ const fields = entries.map(([k, v]) => `${k}: ${formatRawDecoded(v)}`).join(", ");
2303
+ return `{ ${fields} }`;
2304
+ }
2305
+ return String(value);
2306
+ }
2091
2307
  function formatDecodedArgs(decoded) {
2092
2308
  return formatDecoded(decoded);
2093
2309
  }
@@ -2211,14 +2427,26 @@ function parseCallArgs(meta, palletName, callName, args) {
2211
2427
  if (args.length !== 1) {
2212
2428
  throw new Error(`${palletName}.${callName} takes 1 argument (${describeType(meta.lookup, inner.id)}), but ${args.length} provided.`);
2213
2429
  }
2214
- return parseTypedArg2(meta, inner, args[0]);
2430
+ try {
2431
+ return parseTypedArg2(meta, inner, args[0]);
2432
+ } catch (err) {
2433
+ const typeDesc = describeType(meta.lookup, inner.id);
2434
+ throw new Error(`Invalid value for argument 0 (expected ${typeDesc}): ${JSON.stringify(args[0])}`, { cause: err });
2435
+ }
2215
2436
  }
2216
2437
  if (variant.type === "tuple") {
2217
2438
  const entries = variant.value;
2218
2439
  if (args.length !== entries.length) {
2219
2440
  throw new Error(`${palletName}.${callName} takes ${entries.length} arguments, but ${args.length} provided.`);
2220
2441
  }
2221
- return entries.map((entry, i) => parseTypedArg2(meta, entry, args[i]));
2442
+ return entries.map((entry, i) => {
2443
+ try {
2444
+ return parseTypedArg2(meta, entry, args[i]);
2445
+ } catch (err) {
2446
+ const typeDesc = describeType(meta.lookup, entry.id);
2447
+ throw new Error(`Invalid value for argument ${i} (expected ${typeDesc}): ${JSON.stringify(args[i])}`, { cause: err });
2448
+ }
2449
+ });
2222
2450
  }
2223
2451
  return args.length === 0 ? undefined : args.map(parseValue);
2224
2452
  }
@@ -2233,10 +2461,36 @@ function parseStructArgs2(meta, fields, args, callLabel) {
2233
2461
  for (let i = 0;i < fieldNames.length; i++) {
2234
2462
  const name = fieldNames[i];
2235
2463
  const entry = fields[name];
2236
- result[name] = parseTypedArg2(meta, entry, args[i]);
2464
+ try {
2465
+ result[name] = parseTypedArg2(meta, entry, args[i]);
2466
+ } catch (err) {
2467
+ const typeDesc = describeType(meta.lookup, entry.id);
2468
+ throw new Error(`Invalid value for argument '${name}' (expected ${typeDesc}): ${JSON.stringify(args[i])}
2469
+ ` + ` Hint: ${typeHint2(entry, meta)}`, { cause: err });
2470
+ }
2237
2471
  }
2238
2472
  return result;
2239
2473
  }
2474
+ function typeHint2(entry, meta) {
2475
+ const resolved = entry.type === "lookupEntry" ? entry.value : entry;
2476
+ switch (resolved.type) {
2477
+ case "enum": {
2478
+ const variants = Object.keys(resolved.value);
2479
+ if (variants.length <= 6)
2480
+ return `a variant: ${variants.join(" | ")}`;
2481
+ return `one of ${variants.length} variants (e.g. ${variants.slice(0, 3).join(", ")})`;
2482
+ }
2483
+ case "struct":
2484
+ return `a JSON object with fields: ${Object.keys(resolved.value).join(", ")}`;
2485
+ case "tuple":
2486
+ return "a JSON array";
2487
+ case "sequence":
2488
+ case "array":
2489
+ return "a JSON array or hex-encoded bytes";
2490
+ default:
2491
+ return describeType(meta.lookup, entry.id);
2492
+ }
2493
+ }
2240
2494
  function normalizeValue2(lookup, entry, value) {
2241
2495
  let resolved = entry;
2242
2496
  while (resolved.type === "lookupEntry") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polkadot-cli",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "CLI tool for querying Polkadot-ecosystem on-chain state",
5
5
  "type": "module",
6
6
  "bin": {