polkadot-cli 1.17.0 → 1.19.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 +276 -40
  2. package/dist/cli.mjs +1472 -115
  3. package/package.json +3 -2
package/dist/cli.mjs CHANGED
@@ -22,6 +22,12 @@ function isLikelyStaleMetadataError(err) {
22
22
  return false;
23
23
  return STALE_METADATA_PATTERNS.some((re) => re.test(msg));
24
24
  }
25
+ function isBlockUnavailableError(err) {
26
+ const msg = err instanceof Error ? err.message : typeof err === "string" ? err : "";
27
+ if (!msg)
28
+ return false;
29
+ return BLOCK_UNAVAILABLE_PATTERNS.some((re) => re.test(msg));
30
+ }
25
31
  function formatRuntimeError(err) {
26
32
  const msg = err instanceof Error ? err.message : String(err);
27
33
  if (/wasm trap|wasm `?unreachable`? instruction|Execution aborted due to trap/i.test(msg)) {
@@ -40,7 +46,7 @@ function formatRuntimeError(err) {
40
46
  }
41
47
  return msg;
42
48
  }
43
- var CliError, ConnectionError, MetadataError, STALE_METADATA_PATTERNS;
49
+ var CliError, ConnectionError, MetadataError, STALE_METADATA_PATTERNS, BLOCK_UNAVAILABLE_PATTERNS;
44
50
  var init_errors = __esm(() => {
45
51
  CliError = class CliError extends Error {
46
52
  constructor(message) {
@@ -69,6 +75,11 @@ var init_errors = __esm(() => {
69
75
  /Lookup failed/i,
70
76
  /metadata.*mismatch/i
71
77
  ];
78
+ BLOCK_UNAVAILABLE_PATTERNS = [
79
+ /is not pinned/i,
80
+ /Invalid BlockHash/i,
81
+ /UnknownBlock.*Header was not found/i
82
+ ];
72
83
  });
73
84
 
74
85
  // src/utils/runtime-fingerprint.ts
@@ -240,6 +251,9 @@ function getMetadataPath(chainName) {
240
251
  function getMetadataFingerprintPath(chainName) {
241
252
  return join(getChainDir(chainName), "metadata.fingerprint.json");
242
253
  }
254
+ function getRpcMethodsPath(chainName) {
255
+ return join(getChainDir(chainName), "rpc-methods.json");
256
+ }
243
257
  function getConfigPath() {
244
258
  return join(getConfigDir(), "config.json");
245
259
  }
@@ -305,6 +319,31 @@ async function loadMetadataFingerprint(chainName) {
305
319
  return null;
306
320
  }
307
321
  }
322
+ async function loadRpcMethods(chainName) {
323
+ const path = getRpcMethodsPath(chainName);
324
+ if (!await fileExists(path))
325
+ return null;
326
+ try {
327
+ const parsed = JSON.parse(await readFile(path, "utf-8"));
328
+ if (parsed && Array.isArray(parsed.methods) && typeof parsed.version === "number" && typeof parsed.fetchedAt === "string") {
329
+ return parsed;
330
+ }
331
+ return null;
332
+ } catch {
333
+ return null;
334
+ }
335
+ }
336
+ async function saveRpcMethods(chainName, methods, version2) {
337
+ const dir = getChainDir(chainName);
338
+ await ensureDir(dir);
339
+ const cache = {
340
+ methods,
341
+ version: version2,
342
+ fetchedAt: new Date().toISOString()
343
+ };
344
+ await writeFile(getRpcMethodsPath(chainName), `${JSON.stringify(cache, null, 2)}
345
+ `);
346
+ }
308
347
  async function removeChainData(chainName) {
309
348
  const dir = getChainDir(chainName);
310
349
  await rm(dir, { recursive: true, force: true });
@@ -375,6 +414,15 @@ function isEnvSecret(secret) {
375
414
  function isWatchOnly(account) {
376
415
  return account.secret === undefined;
377
416
  }
417
+ function classifyAccount(account) {
418
+ if (account.source?.kind === "pallet")
419
+ return "pallet";
420
+ if (account.source?.kind === "parachain")
421
+ return "parachain";
422
+ if (account.secret !== undefined)
423
+ return "signer";
424
+ return "watch-only";
425
+ }
378
426
 
379
427
  // src/utils/fuzzy-match.ts
380
428
  function levenshtein(a, b) {
@@ -1216,7 +1264,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
1216
1264
  let bytes;
1217
1265
  try {
1218
1266
  const hex = await withTimeout(client._request("state_call", ["Metadata_metadata_at_version", v15Arg]), chainName);
1219
- const raw = hexToBytes(hex);
1267
+ const raw = hexToBytes2(hex);
1220
1268
  const decoded = optionalOpaqueBytes.dec(raw);
1221
1269
  if (decoded !== undefined) {
1222
1270
  bytes = new Uint8Array(decoded);
@@ -1225,7 +1273,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
1225
1273
  if (!bytes) {
1226
1274
  try {
1227
1275
  const hex = await withTimeout(client._request("state_getMetadata", []), chainName);
1228
- bytes = hexToBytes(hex);
1276
+ bytes = hexToBytes2(hex);
1229
1277
  } catch (err) {
1230
1278
  if (err instanceof ConnectionError)
1231
1279
  throw err;
@@ -1274,6 +1322,24 @@ async function withStalenessSuggestion(chainName, clientHandle, task) {
1274
1322
  ` + ` Run: dot chain update ${chainName}`);
1275
1323
  }
1276
1324
  }
1325
+ async function withBlockAvailabilityHint(atRaw, task) {
1326
+ try {
1327
+ return await task();
1328
+ } catch (err) {
1329
+ if (!isBlockUnavailableError(err))
1330
+ throw err;
1331
+ const original = err instanceof Error ? err.message : String(err);
1332
+ const isExplicitHash = atRaw && atRaw !== "best" && atRaw !== "finalized";
1333
+ const target = isExplicitHash ? atRaw : "the requested block";
1334
+ const exampleAt = isExplicitHash ? atRaw : "<hash>";
1335
+ throw new CliError(`${original}
1336
+
1337
+ ` + `⚠ ${target} is not available on the current RPC endpoint.
1338
+ ` + ` Public nodes serve only recent (pinned) blocks via chainHead_v1_*.
1339
+ ` + ` For deep historical reads, point --rpc at an archive endpoint, e.g.:
1340
+ ` + ` dot ... --at ${exampleAt} --rpc wss://<archive-endpoint>`);
1341
+ }
1342
+ }
1277
1343
  async function getOrFetchMetadata(chainName, clientHandle) {
1278
1344
  let raw = await loadMetadata(chainName);
1279
1345
  if (!raw) {
@@ -1403,7 +1469,7 @@ function describeCallArgs(meta, palletName, callName) {
1403
1469
  function describeEventFields(meta, palletName, eventName) {
1404
1470
  return compactArgsString(getEventFields(meta, palletName, eventName));
1405
1471
  }
1406
- function hexToBytes(hex) {
1472
+ function hexToBytes2(hex) {
1407
1473
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
1408
1474
  const bytes = new Uint8Array(clean.length / 2);
1409
1475
  for (let i = 0;i < clean.length; i += 2) {
@@ -1498,6 +1564,15 @@ import { compact as scaleCompact } from "@polkadot-api/substrate-bindings";
1498
1564
  import { getViewBuilder } from "@polkadot-api/view-builder";
1499
1565
  import { Binary as Binary2 } from "polkadot-api";
1500
1566
  import { stringify as stringifyYaml } from "yaml";
1567
+ function parseAtForRead(raw) {
1568
+ if (raw === undefined)
1569
+ return;
1570
+ if (raw === "best" || raw === "finalized")
1571
+ return raw;
1572
+ if (/^0x[0-9a-fA-F]{64}$/.test(raw))
1573
+ return raw;
1574
+ throw new CliError(`Invalid --at value "${raw}". Use "best", "finalized", or a 0x-prefixed 32-byte block hash.`);
1575
+ }
1501
1576
  async function parseStructArgs(meta, fields, args, callLabel) {
1502
1577
  const fieldNames = Object.keys(fields);
1503
1578
  if (args.length !== fieldNames.length) {
@@ -1906,8 +1981,10 @@ async function handleApis(target, args, opts) {
1906
1981
  typeof opts.parsedArgs === "object" ? JSON.stringify(opts.parsedArgs) : String(opts.parsedArgs)
1907
1982
  ];
1908
1983
  const parsedArgs = await parseRuntimeApiArgs(meta, method, effectiveArgs);
1984
+ const atArg = parseAtForRead(opts.at);
1985
+ const pullOpts = atArg !== undefined ? [{ at: atArg }] : [];
1909
1986
  const unsafeApi = clientHandle.client.getUnsafeApi();
1910
- const result = await unsafeApi.apis[api.name][method.name](...parsedArgs);
1987
+ const result = await withBlockAvailabilityHint(opts.at, () => unsafeApi.apis[api.name][method.name](...parsedArgs, ...pullOpts));
1911
1988
  const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
1912
1989
  printResult(result, format);
1913
1990
  } finally {
@@ -2559,11 +2636,569 @@ var init_focused_inspect = __esm(() => {
2559
2636
  init_pretty_type();
2560
2637
  });
2561
2638
 
2639
+ // src/data/rpc-registry.ts
2640
+ function inferFamily(method) {
2641
+ const prefix = method.split("_")[0];
2642
+ switch (prefix) {
2643
+ case "system":
2644
+ case "chain":
2645
+ case "state":
2646
+ case "author":
2647
+ case "payment":
2648
+ case "babe":
2649
+ case "grandpa":
2650
+ case "beefy":
2651
+ case "mmr":
2652
+ case "offchain":
2653
+ case "dev":
2654
+ return prefix;
2655
+ case "rpc":
2656
+ return "spec";
2657
+ case "chainHead":
2658
+ return "chainHead";
2659
+ case "chainSpec":
2660
+ return "chainSpec";
2661
+ case "transaction":
2662
+ return "transaction";
2663
+ case "archive":
2664
+ return "archive";
2665
+ default:
2666
+ return "other";
2667
+ }
2668
+ }
2669
+ var RPC_REGISTRY;
2670
+ var init_rpc_registry = __esm(() => {
2671
+ RPC_REGISTRY = {
2672
+ system_health: {
2673
+ description: "Node sync state (peers, isSyncing, shouldHavePeers).",
2674
+ family: "system",
2675
+ args: []
2676
+ },
2677
+ system_syncState: {
2678
+ description: "Block-level sync progress (startingBlock, currentBlock, highestBlock).",
2679
+ family: "system",
2680
+ args: []
2681
+ },
2682
+ system_version: {
2683
+ description: "Node software version string.",
2684
+ family: "system",
2685
+ args: []
2686
+ },
2687
+ system_name: {
2688
+ description: "Node implementation name.",
2689
+ family: "system",
2690
+ args: []
2691
+ },
2692
+ system_chain: {
2693
+ description: "Chain name as reported by the node.",
2694
+ family: "system",
2695
+ args: []
2696
+ },
2697
+ system_chainType: {
2698
+ description: "Chain type (Live, Development, Local).",
2699
+ family: "system",
2700
+ args: []
2701
+ },
2702
+ system_properties: {
2703
+ description: "Chain properties (ss58Format, tokenDecimals, tokenSymbol).",
2704
+ family: "system",
2705
+ args: []
2706
+ },
2707
+ system_peers: {
2708
+ description: "Connected peer details (peerId, roles, bestHash, bestNumber).",
2709
+ family: "system",
2710
+ args: []
2711
+ },
2712
+ system_localPeerId: {
2713
+ description: "Base58 PeerId of this node.",
2714
+ family: "system",
2715
+ args: []
2716
+ },
2717
+ system_localListenAddresses: {
2718
+ description: "Multiaddrs this node listens on.",
2719
+ family: "system",
2720
+ args: []
2721
+ },
2722
+ system_nodeRoles: {
2723
+ description: "Role(s) of the node (Authority, Full, Light, …).",
2724
+ family: "system",
2725
+ args: []
2726
+ },
2727
+ system_addLogFilter: {
2728
+ description: "Add a tracing/log filter directive at runtime.",
2729
+ family: "system",
2730
+ args: [{ name: "directives", type: "string" }],
2731
+ dangerous: true
2732
+ },
2733
+ system_resetLogFilter: {
2734
+ description: "Reset log filter to the default set at startup.",
2735
+ family: "system",
2736
+ args: [],
2737
+ dangerous: true
2738
+ },
2739
+ system_accountNextIndex: {
2740
+ description: "Next nonce for an account, including pending mempool extrinsics.",
2741
+ family: "system",
2742
+ args: [{ name: "address", type: "AccountId" }]
2743
+ },
2744
+ system_dryRun: {
2745
+ description: "Dry-run an extrinsic, returning its ApplyExtrinsicResult.",
2746
+ family: "system",
2747
+ args: [
2748
+ { name: "extrinsic", type: "hex" },
2749
+ { name: "at", type: "H256", optional: true }
2750
+ ]
2751
+ },
2752
+ chain_getBlock: {
2753
+ description: "Full block (header + extrinsics) by hash.",
2754
+ family: "chain",
2755
+ args: [{ name: "blockHash", type: "H256", optional: true, description: "latest if omitted" }]
2756
+ },
2757
+ chain_getBlockHash: {
2758
+ description: "Block hash by number, or latest if omitted.",
2759
+ family: "chain",
2760
+ args: [{ name: "blockNumber", type: "u32", optional: true }]
2761
+ },
2762
+ chain_getFinalizedHead: {
2763
+ description: "Hash of the latest finalized head.",
2764
+ family: "chain",
2765
+ args: []
2766
+ },
2767
+ chain_getHeader: {
2768
+ description: "Block header by hash, or latest if omitted.",
2769
+ family: "chain",
2770
+ args: [{ name: "blockHash", type: "H256", optional: true }]
2771
+ },
2772
+ chain_subscribeAllHeads: {
2773
+ description: "Subscribe to all imported headers.",
2774
+ family: "chain",
2775
+ args: [],
2776
+ subscription: true
2777
+ },
2778
+ chain_subscribeFinalizedHeads: {
2779
+ description: "Subscribe to finalized-head changes.",
2780
+ family: "chain",
2781
+ args: [],
2782
+ subscription: true
2783
+ },
2784
+ chain_subscribeNewHeads: {
2785
+ description: "Subscribe to best-block-head changes.",
2786
+ family: "chain",
2787
+ args: [],
2788
+ subscription: true
2789
+ },
2790
+ state_call: {
2791
+ description: "Invoke a runtime API method by name with raw SCALE-encoded arguments.",
2792
+ family: "state",
2793
+ args: [
2794
+ { name: "method", type: "string", description: "e.g. Core_version" },
2795
+ { name: "data", type: "hex", description: "SCALE-encoded args (0x for none)" },
2796
+ { name: "at", type: "H256", optional: true }
2797
+ ]
2798
+ },
2799
+ state_getMetadata: {
2800
+ description: "Raw SCALE-encoded runtime metadata at a block.",
2801
+ family: "state",
2802
+ args: [{ name: "at", type: "H256", optional: true }]
2803
+ },
2804
+ state_getRuntimeVersion: {
2805
+ description: "Runtime version at a block.",
2806
+ family: "state",
2807
+ args: [{ name: "at", type: "H256", optional: true }]
2808
+ },
2809
+ state_getStorage: {
2810
+ description: "Raw SCALE-encoded storage value at a key.",
2811
+ family: "state",
2812
+ args: [
2813
+ { name: "key", type: "StorageKey" },
2814
+ { name: "at", type: "H256", optional: true }
2815
+ ]
2816
+ },
2817
+ state_getStorageHash: {
2818
+ description: "Blake2 hash of the value at a storage key.",
2819
+ family: "state",
2820
+ args: [
2821
+ { name: "key", type: "StorageKey" },
2822
+ { name: "at", type: "H256", optional: true }
2823
+ ]
2824
+ },
2825
+ state_getStorageSize: {
2826
+ description: "Byte length of the value at a storage key.",
2827
+ family: "state",
2828
+ args: [
2829
+ { name: "key", type: "StorageKey" },
2830
+ { name: "at", type: "H256", optional: true }
2831
+ ]
2832
+ },
2833
+ state_getKeysPaged: {
2834
+ description: "Paginated key iteration under a prefix.",
2835
+ family: "state",
2836
+ args: [
2837
+ { name: "prefix", type: "StorageKey" },
2838
+ { name: "count", type: "u32" },
2839
+ { name: "startKey", type: "StorageKey", optional: true },
2840
+ { name: "at", type: "H256", optional: true }
2841
+ ]
2842
+ },
2843
+ state_queryStorageAt: {
2844
+ description: "Read multiple keys at a single block.",
2845
+ family: "state",
2846
+ args: [
2847
+ { name: "keys", type: "StorageKey[]" },
2848
+ { name: "at", type: "H256", optional: true }
2849
+ ]
2850
+ },
2851
+ state_traceBlock: {
2852
+ description: "Per-block storage access trace (heavy: archival/debug nodes only).",
2853
+ family: "state",
2854
+ args: [
2855
+ { name: "blockHash", type: "H256" },
2856
+ { name: "targets", type: "string", optional: true },
2857
+ { name: "storageKeys", type: "string", optional: true },
2858
+ { name: "methods", type: "string", optional: true }
2859
+ ]
2860
+ },
2861
+ state_subscribeRuntimeVersion: {
2862
+ description: "Subscribe to runtime upgrades.",
2863
+ family: "state",
2864
+ args: [],
2865
+ subscription: true
2866
+ },
2867
+ state_subscribeStorage: {
2868
+ description: "Subscribe to changes for a set of keys.",
2869
+ family: "state",
2870
+ args: [{ name: "keys", type: "StorageKey[]" }],
2871
+ subscription: true
2872
+ },
2873
+ author_pendingExtrinsics: {
2874
+ description: "Mempool snapshot — encoded extrinsics waiting to be included.",
2875
+ family: "author",
2876
+ args: []
2877
+ },
2878
+ author_submitExtrinsic: {
2879
+ description: "Submit a signed extrinsic to the mempool. Returns the tx hash.",
2880
+ family: "author",
2881
+ args: [{ name: "extrinsic", type: "hex" }],
2882
+ dangerous: true
2883
+ },
2884
+ author_removeExtrinsic: {
2885
+ description: "Remove specific extrinsics from the mempool by hash.",
2886
+ family: "author",
2887
+ args: [{ name: "bytesOrHash", type: "ExtrinsicOrHash[]" }],
2888
+ dangerous: true
2889
+ },
2890
+ author_hasKey: {
2891
+ description: "Whether the keystore has the public key for a given key type.",
2892
+ family: "author",
2893
+ args: [
2894
+ { name: "publicKey", type: "hex" },
2895
+ { name: "keyType", type: "string", description: "e.g. babe, gran, imon" }
2896
+ ]
2897
+ },
2898
+ author_hasSessionKeys: {
2899
+ description: "Whether the keystore has all keys for a session-keys blob.",
2900
+ family: "author",
2901
+ args: [{ name: "sessionKeys", type: "hex" }]
2902
+ },
2903
+ author_rotateKeys: {
2904
+ description: "Generate a new set of session keys and return the public-keys blob.",
2905
+ family: "author",
2906
+ args: [],
2907
+ dangerous: true
2908
+ },
2909
+ author_insertKey: {
2910
+ description: "Insert a key into the node keystore.",
2911
+ family: "author",
2912
+ args: [
2913
+ { name: "keyType", type: "string" },
2914
+ { name: "suri", type: "string" },
2915
+ { name: "publicKey", type: "hex" }
2916
+ ],
2917
+ dangerous: true
2918
+ },
2919
+ author_submitAndWatchExtrinsic: {
2920
+ description: "Submit an extrinsic and subscribe to its status updates.",
2921
+ family: "author",
2922
+ args: [{ name: "extrinsic", type: "hex" }],
2923
+ dangerous: true,
2924
+ subscription: true
2925
+ },
2926
+ payment_queryInfo: {
2927
+ description: "Pre-submission fee estimate (weight, partialFee, class).",
2928
+ family: "payment",
2929
+ args: [
2930
+ { name: "extrinsic", type: "hex" },
2931
+ { name: "at", type: "H256", optional: true }
2932
+ ]
2933
+ },
2934
+ payment_queryFeeDetails: {
2935
+ description: "Fee breakdown (baseFee, lenFee, adjustedWeightFee, tip).",
2936
+ family: "payment",
2937
+ args: [
2938
+ { name: "extrinsic", type: "hex" },
2939
+ { name: "at", type: "H256", optional: true }
2940
+ ]
2941
+ },
2942
+ babe_epochAuthorship: {
2943
+ description: "Slots this node is allowed to author in the current epoch.",
2944
+ family: "babe",
2945
+ args: []
2946
+ },
2947
+ grandpa_proveFinality: {
2948
+ description: "Finality proof up to a block (for light clients).",
2949
+ family: "grandpa",
2950
+ args: [{ name: "blockNumber", type: "u32" }]
2951
+ },
2952
+ grandpa_roundState: {
2953
+ description: "GRANDPA round state (best round, total weight, voters).",
2954
+ family: "grandpa",
2955
+ args: []
2956
+ },
2957
+ grandpa_subscribeJustifications: {
2958
+ description: "Subscribe to GRANDPA justifications.",
2959
+ family: "grandpa",
2960
+ args: [],
2961
+ subscription: true
2962
+ },
2963
+ beefy_getFinalizedHead: {
2964
+ description: "Latest BEEFY-finalized block hash.",
2965
+ family: "beefy",
2966
+ args: []
2967
+ },
2968
+ beefy_subscribeJustifications: {
2969
+ description: "Subscribe to BEEFY signed commitments.",
2970
+ family: "beefy",
2971
+ args: [],
2972
+ subscription: true
2973
+ },
2974
+ mmr_root: {
2975
+ description: "MMR root at a block.",
2976
+ family: "mmr",
2977
+ args: [{ name: "at", type: "H256", optional: true }]
2978
+ },
2979
+ mmr_generateProof: {
2980
+ description: "Generate an MMR proof for given leaf indices.",
2981
+ family: "mmr",
2982
+ args: [
2983
+ { name: "blockNumbers", type: "u32[]" },
2984
+ { name: "bestKnownBlockNumber", type: "u32", optional: true },
2985
+ { name: "at", type: "H256", optional: true }
2986
+ ]
2987
+ },
2988
+ mmr_verifyProof: {
2989
+ description: "Verify an MMR proof.",
2990
+ family: "mmr",
2991
+ args: [{ name: "proof", type: "MmrLeavesProof" }]
2992
+ },
2993
+ offchain_localStorageGet: {
2994
+ description: "Read from offchain-worker local storage (PERSISTENT or LOCAL kind).",
2995
+ family: "offchain",
2996
+ args: [
2997
+ { name: "kind", type: "string", description: "PERSISTENT or LOCAL" },
2998
+ { name: "key", type: "hex" }
2999
+ ]
3000
+ },
3001
+ offchain_localStorageSet: {
3002
+ description: "Write to offchain-worker local storage.",
3003
+ family: "offchain",
3004
+ args: [
3005
+ { name: "kind", type: "string" },
3006
+ { name: "key", type: "hex" },
3007
+ { name: "value", type: "hex" }
3008
+ ],
3009
+ dangerous: true
3010
+ },
3011
+ dev_newBlock: {
3012
+ description: "Manually trigger block production (manual-seal dev nodes).",
3013
+ family: "dev",
3014
+ args: [
3015
+ {
3016
+ name: "params",
3017
+ type: "json",
3018
+ optional: true,
3019
+ description: "{ create_empty?: bool, finalize?: bool }"
3020
+ }
3021
+ ],
3022
+ dangerous: true
3023
+ },
3024
+ dev_setHead: {
3025
+ description: "Set the chain head to a specific block (manual-seal dev nodes).",
3026
+ family: "dev",
3027
+ args: [{ name: "blockHash", type: "H256" }],
3028
+ dangerous: true
3029
+ },
3030
+ rpc_methods: {
3031
+ description: "List of JSON-RPC methods this node exposes.",
3032
+ family: "spec",
3033
+ args: []
3034
+ },
3035
+ chainSpec_v1_chainName: {
3036
+ description: "Chain name from the chain spec (no SCALE).",
3037
+ family: "chainSpec",
3038
+ args: []
3039
+ },
3040
+ chainSpec_v1_genesisHash: {
3041
+ description: "Genesis block hash.",
3042
+ family: "chainSpec",
3043
+ args: []
3044
+ },
3045
+ chainSpec_v1_properties: {
3046
+ description: "Chain properties from the chain spec.",
3047
+ family: "chainSpec",
3048
+ args: []
3049
+ },
3050
+ archive_v1_finalizedHeight: {
3051
+ description: "Latest finalized block number this archive node has.",
3052
+ family: "archive",
3053
+ args: []
3054
+ },
3055
+ archive_v1_genesisHash: {
3056
+ description: "Genesis block hash from the archive node.",
3057
+ family: "archive",
3058
+ args: []
3059
+ },
3060
+ archive_v1_hashByHeight: {
3061
+ description: "Block hashes at a height (canonical + forks).",
3062
+ family: "archive",
3063
+ args: [{ name: "height", type: "u32" }]
3064
+ },
3065
+ archive_v1_header: {
3066
+ description: "Header for an archived block.",
3067
+ family: "archive",
3068
+ args: [{ name: "blockHash", type: "H256" }]
3069
+ },
3070
+ archive_v1_body: {
3071
+ description: "Block body (extrinsics) for an archived block.",
3072
+ family: "archive",
3073
+ args: [{ name: "blockHash", type: "H256" }]
3074
+ },
3075
+ archive_v1_call: {
3076
+ description: "Invoke a runtime API at an archived block.",
3077
+ family: "archive",
3078
+ args: [
3079
+ { name: "blockHash", type: "H256" },
3080
+ { name: "function", type: "string" },
3081
+ { name: "callParameters", type: "hex" }
3082
+ ]
3083
+ },
3084
+ archive_v1_storage: {
3085
+ description: "Read storage at an archived block.",
3086
+ family: "archive",
3087
+ args: [
3088
+ { name: "blockHash", type: "H256" },
3089
+ { name: "items", type: "StorageItemInput[]" },
3090
+ { name: "childTrie", type: "string", optional: true }
3091
+ ],
3092
+ subscription: true
3093
+ },
3094
+ chainHead_v1_follow: {
3095
+ description: "Pin chainHead and stream block events. Requires a follow session.",
3096
+ family: "chainHead",
3097
+ args: [{ name: "withRuntime", type: "bool" }],
3098
+ subscription: true
3099
+ },
3100
+ transaction_v1_broadcast: {
3101
+ description: "Broadcast a signed extrinsic and watch its status.",
3102
+ family: "transaction",
3103
+ args: [{ name: "extrinsic", type: "hex" }],
3104
+ dangerous: true,
3105
+ subscription: true
3106
+ }
3107
+ };
3108
+ });
3109
+
3110
+ // src/core/xxh64.ts
3111
+ function rotl(v, r) {
3112
+ return (v << r | v >> 64n - r) & MASK;
3113
+ }
3114
+ function round(acc, val) {
3115
+ acc = acc + val * P2 & MASK;
3116
+ acc = rotl(acc, 31n);
3117
+ return acc * P1 & MASK;
3118
+ }
3119
+ function mergeRound(h, val) {
3120
+ const k = round(0n, val);
3121
+ return (h ^ k) * P1 + P4 & MASK;
3122
+ }
3123
+ function readU64LE(data, p) {
3124
+ return BigInt(data[p]) | BigInt(data[p + 1]) << 8n | BigInt(data[p + 2]) << 16n | BigInt(data[p + 3]) << 24n | BigInt(data[p + 4]) << 32n | BigInt(data[p + 5]) << 40n | BigInt(data[p + 6]) << 48n | BigInt(data[p + 7]) << 56n;
3125
+ }
3126
+ function readU32LE(data, p) {
3127
+ return BigInt(data[p]) | BigInt(data[p + 1]) << 8n | BigInt(data[p + 2]) << 16n | BigInt(data[p + 3]) << 24n;
3128
+ }
3129
+ function xxh64(input, seed = 0n) {
3130
+ const len = input.length;
3131
+ let p = 0;
3132
+ let h;
3133
+ if (len >= 32) {
3134
+ let v1 = seed + P1 + P2 & MASK;
3135
+ let v2 = seed + P2 & MASK;
3136
+ let v3 = seed & MASK;
3137
+ let v4 = seed - P1 & MASK;
3138
+ while (p <= len - 32) {
3139
+ v1 = round(v1, readU64LE(input, p));
3140
+ p += 8;
3141
+ v2 = round(v2, readU64LE(input, p));
3142
+ p += 8;
3143
+ v3 = round(v3, readU64LE(input, p));
3144
+ p += 8;
3145
+ v4 = round(v4, readU64LE(input, p));
3146
+ p += 8;
3147
+ }
3148
+ h = rotl(v1, 1n) + rotl(v2, 7n) + rotl(v3, 12n) + rotl(v4, 18n) & MASK;
3149
+ h = mergeRound(h, v1);
3150
+ h = mergeRound(h, v2);
3151
+ h = mergeRound(h, v3);
3152
+ h = mergeRound(h, v4);
3153
+ } else {
3154
+ h = seed + P5 & MASK;
3155
+ }
3156
+ h = h + BigInt(len) & MASK;
3157
+ while (p + 8 <= len) {
3158
+ const k = round(0n, readU64LE(input, p));
3159
+ h = rotl(h ^ k, 27n) * P1 + P4 & MASK;
3160
+ p += 8;
3161
+ }
3162
+ if (p + 4 <= len) {
3163
+ const k = readU32LE(input, p) * P1 & MASK;
3164
+ h = rotl(h ^ k, 23n) * P2 + P3 & MASK;
3165
+ p += 4;
3166
+ }
3167
+ while (p < len) {
3168
+ const k = BigInt(input[p]) * P5 & MASK;
3169
+ h = rotl(h ^ k, 11n) * P1 & MASK;
3170
+ p++;
3171
+ }
3172
+ h ^= h >> 33n;
3173
+ h = h * P2 & MASK;
3174
+ h ^= h >> 29n;
3175
+ h = h * P3 & MASK;
3176
+ h ^= h >> 32n;
3177
+ return h;
3178
+ }
3179
+ function u64ToLEBytes(v, out, offset) {
3180
+ for (let i = 0;i < 8; i++) {
3181
+ out[offset + i] = Number(v >> BigInt(i * 8) & 0xffn);
3182
+ }
3183
+ }
3184
+ function twox(input, bits) {
3185
+ const lanes = bits / 64;
3186
+ const out = new Uint8Array(bits / 8);
3187
+ for (let i = 0;i < lanes; i++) {
3188
+ u64ToLEBytes(xxh64(input, BigInt(i)), out, i * 8);
3189
+ }
3190
+ return out;
3191
+ }
3192
+ var MASK, P1 = 11400714785074694791n, P2 = 14029467366897019727n, P3 = 1609587929392839161n, P4 = 9650029242287828579n, P5 = 2870177450012600261n;
3193
+ var init_xxh64 = __esm(() => {
3194
+ MASK = (1n << 64n) - 1n;
3195
+ });
3196
+
2562
3197
  // src/core/hash.ts
2563
3198
  import { blake2b as blake2b2 } from "@noble/hashes/blake2.js";
2564
3199
  import { sha256 } from "@noble/hashes/sha2.js";
2565
- import { keccak_256 } from "@noble/hashes/sha3.js";
2566
- import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
3200
+ import { keccak_256 as keccak_2562 } from "@noble/hashes/sha3.js";
3201
+ import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes3 } from "@noble/hashes/utils.js";
2567
3202
  function computeHash(algorithm, data) {
2568
3203
  const algo = ALGORITHMS[algorithm];
2569
3204
  if (!algo) {
@@ -2577,12 +3212,12 @@ function parseInputData(input) {
2577
3212
  if (hex.length % 2 !== 0) {
2578
3213
  throw new Error(`Invalid hex input: odd number of characters`);
2579
3214
  }
2580
- return hexToBytes2(hex);
3215
+ return hexToBytes3(hex);
2581
3216
  }
2582
3217
  return new TextEncoder().encode(input);
2583
3218
  }
2584
3219
  function toHex2(bytes) {
2585
- return `0x${bytesToHex2(bytes)}`;
3220
+ return `0x${bytesToHex3(bytes)}`;
2586
3221
  }
2587
3222
  function isValidAlgorithm(name) {
2588
3223
  return name in ALGORITHMS;
@@ -2592,6 +3227,7 @@ function getAlgorithmNames() {
2592
3227
  }
2593
3228
  var ALGORITHMS;
2594
3229
  var init_hash = __esm(() => {
3230
+ init_xxh64();
2595
3231
  ALGORITHMS = {
2596
3232
  blake2b256: {
2597
3233
  compute: (data) => blake2b2(data, { dkLen: 32 }),
@@ -2604,7 +3240,7 @@ var init_hash = __esm(() => {
2604
3240
  description: "BLAKE2b with 128-bit output"
2605
3241
  },
2606
3242
  keccak256: {
2607
- compute: (data) => keccak_256(data),
3243
+ compute: (data) => keccak_2562(data),
2608
3244
  outputLen: 32,
2609
3245
  description: "Keccak-256 (Ethereum-compatible)"
2610
3246
  },
@@ -2612,6 +3248,21 @@ var init_hash = __esm(() => {
2612
3248
  compute: (data) => sha256(data),
2613
3249
  outputLen: 32,
2614
3250
  description: "SHA-256"
3251
+ },
3252
+ twox64: {
3253
+ compute: (data) => twox(data, 64),
3254
+ outputLen: 8,
3255
+ description: "XXH64 with seed 0 (Substrate twox64)"
3256
+ },
3257
+ twox128: {
3258
+ compute: (data) => twox(data, 128),
3259
+ outputLen: 16,
3260
+ description: "XXH64 with seeds 0,1 (Substrate twox128, pallet/storage prefix)"
3261
+ },
3262
+ twox256: {
3263
+ compute: (data) => twox(data, 256),
3264
+ outputLen: 32,
3265
+ description: "XXH64 with seeds 0,1,2,3 (Substrate twox256)"
2615
3266
  }
2616
3267
  };
2617
3268
  });
@@ -2742,11 +3393,8 @@ async function generateCompletions(currentWord, precedingWords) {
2742
3393
  if (firstArg === "hash") {
2743
3394
  return filterPrefix(getAlgorithmNames(), currentWord);
2744
3395
  }
2745
- if (firstArg === "parachain") {
2746
- if (prevWord === "--type") {
2747
- return filterPrefix(["child", "sibling"], currentWord);
2748
- }
2749
- return [];
3396
+ if (firstArg === "account" && prevWord === "--parachain-type") {
3397
+ return filterPrefix(["child", "sibling"], currentWord);
2750
3398
  }
2751
3399
  return completeDotpath(currentWord, config, knownChains, precedingWords);
2752
3400
  }
@@ -2789,6 +3437,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
2789
3437
  if (category === "extensions") {
2790
3438
  return completeExtensionsCategory(first, numComplete, endsWithDot, currentWord, config, chainFromFlag);
2791
3439
  }
3440
+ if (category === "rpc") {
3441
+ return completeRpcCategory(first, numComplete, endsWithDot, currentWord, chainFromFlag);
3442
+ }
2792
3443
  if (numComplete === 1 && endsWithDot) {
2793
3444
  const chainName = chainFromFlag;
2794
3445
  if (!chainName)
@@ -2848,6 +3499,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
2848
3499
  if (category === "extensions") {
2849
3500
  return completeExtensionsCategory(`${first}.${completeSegments[1]}`, numComplete - 1, endsWithDot, currentWord, config, chainName);
2850
3501
  }
3502
+ if (category === "rpc") {
3503
+ return completeRpcCategory(`${first}.${completeSegments[1]}`, numComplete - 1, endsWithDot, currentWord, chainName);
3504
+ }
2851
3505
  const pallets = await loadPallets(config, chainName);
2852
3506
  if (!pallets)
2853
3507
  return [];
@@ -2921,6 +3575,24 @@ async function completeExtensionsCategory(prefix, numComplete, endsWithDot, curr
2921
3575
  }
2922
3576
  return [];
2923
3577
  }
3578
+ async function completeRpcCategory(prefix, numComplete, endsWithDot, currentWord, chainNameOverride) {
3579
+ let names;
3580
+ if (chainNameOverride) {
3581
+ const cached = await loadRpcMethods(chainNameOverride);
3582
+ names = cached?.methods ?? Object.keys(RPC_REGISTRY);
3583
+ } else {
3584
+ names = Object.keys(RPC_REGISTRY);
3585
+ }
3586
+ if (numComplete === 1 && endsWithDot) {
3587
+ const candidates = names.map((n) => `${prefix}.${n}`);
3588
+ return filterPrefix(candidates, currentWord.slice(0, -1));
3589
+ }
3590
+ if (numComplete === 1 && !endsWithDot) {
3591
+ const candidates = names.map((n) => `${prefix}.${n}`);
3592
+ return filterPrefix(candidates, currentWord);
3593
+ }
3594
+ return [];
3595
+ }
2924
3596
  var CATEGORIES2, CATEGORY_ALIASES2, NAMED_COMMANDS, CHAIN_SUBCOMMANDS, ACCOUNT_SUBCOMMANDS, GLOBAL_OPTIONS, TX_OPTIONS, QUERY_OPTIONS;
2925
3597
  var init_complete = __esm(() => {
2926
3598
  init_accounts_store();
@@ -2928,7 +3600,17 @@ var init_complete = __esm(() => {
2928
3600
  init_accounts();
2929
3601
  init_hash();
2930
3602
  init_metadata();
2931
- CATEGORIES2 = ["query", "tx", "const", "events", "errors", "apis", "extensions"];
3603
+ init_rpc_registry();
3604
+ CATEGORIES2 = [
3605
+ "query",
3606
+ "tx",
3607
+ "const",
3608
+ "events",
3609
+ "errors",
3610
+ "apis",
3611
+ "extensions",
3612
+ "rpc"
3613
+ ];
2932
3614
  CATEGORY_ALIASES2 = {
2933
3615
  query: "query",
2934
3616
  tx: "tx",
@@ -2943,9 +3625,10 @@ var init_complete = __esm(() => {
2943
3625
  api: "apis",
2944
3626
  extensions: "extensions",
2945
3627
  extension: "extensions",
2946
- ext: "extensions"
3628
+ ext: "extensions",
3629
+ rpc: "rpc"
2947
3630
  };
2948
- NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "sign", "parachain", "completions"];
3631
+ NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "sign", "completions"];
2949
3632
  CHAIN_SUBCOMMANDS = ["add", "info", "list", "remove", "update"];
2950
3633
  ACCOUNT_SUBCOMMANDS = [
2951
3634
  "add",
@@ -2977,23 +3660,154 @@ var init_complete = __esm(() => {
2977
3660
  // src/cli.ts
2978
3661
  import cac from "cac";
2979
3662
  // package.json
2980
- var version = "1.17.0";
3663
+ var version = "1.19.0";
2981
3664
 
2982
3665
  // src/commands/account.ts
2983
3666
  init_accounts_store();
2984
3667
  init_accounts();
2985
- init_output();
2986
3668
  import { readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
3669
+ import { hexToBytes as nobleHexToBytes } from "@noble/hashes/utils.js";
3670
+
3671
+ // src/core/h160.ts
3672
+ import { keccak_256 } from "@noble/hashes/sha3.js";
3673
+ import { bytesToHex as bytesToHex2, hexToBytes } from "@noble/hashes/utils.js";
3674
+ var ETH_DERIVED_SUFFIX_BYTE = 238;
3675
+ var H160_LEN = 20;
3676
+ var ACCOUNT_ID_LEN = 32;
3677
+ function hasEthDerivedSuffix(accountId) {
3678
+ if (accountId.length !== ACCOUNT_ID_LEN)
3679
+ return false;
3680
+ for (let i = H160_LEN;i < ACCOUNT_ID_LEN; i++) {
3681
+ if (accountId[i] !== ETH_DERIVED_SUFFIX_BYTE)
3682
+ return false;
3683
+ }
3684
+ return true;
3685
+ }
3686
+ function accountIdToH160(accountId) {
3687
+ if (accountId.length !== ACCOUNT_ID_LEN) {
3688
+ throw new Error(`accountIdToH160 expects 32 bytes, got ${accountId.length}`);
3689
+ }
3690
+ if (hasEthDerivedSuffix(accountId)) {
3691
+ return accountId.slice(0, H160_LEN);
3692
+ }
3693
+ return keccak_256(accountId).slice(ACCOUNT_ID_LEN - H160_LEN);
3694
+ }
3695
+ function h160ToFallbackAccountId(h160) {
3696
+ if (h160.length !== H160_LEN) {
3697
+ throw new Error(`h160ToFallbackAccountId expects 20 bytes, got ${h160.length}`);
3698
+ }
3699
+ const out = new Uint8Array(ACCOUNT_ID_LEN);
3700
+ out.set(h160, 0);
3701
+ out.fill(ETH_DERIVED_SUFFIX_BYTE, H160_LEN);
3702
+ return out;
3703
+ }
3704
+ function toEip55(h160) {
3705
+ if (h160.length !== H160_LEN) {
3706
+ throw new Error(`toEip55 expects 20 bytes, got ${h160.length}`);
3707
+ }
3708
+ const lowerHex = bytesToHex2(h160);
3709
+ const hashBytes = keccak_256(new TextEncoder().encode(lowerHex));
3710
+ let out = "0x";
3711
+ for (let i = 0;i < lowerHex.length; i++) {
3712
+ const c = lowerHex[i];
3713
+ const nibble = i % 2 === 0 ? hashBytes[i >> 1] >> 4 : hashBytes[i >> 1] & 15;
3714
+ out += nibble >= 8 ? c.toUpperCase() : c;
3715
+ }
3716
+ return out;
3717
+ }
3718
+ function isH160Hex(input) {
3719
+ return /^0x[0-9a-fA-F]{40}$/.test(input);
3720
+ }
3721
+ function h160FromHex(input) {
3722
+ if (!isH160Hex(input)) {
3723
+ throw new Error(`Not a valid 0x-prefixed 20-byte hex string: ${input}`);
3724
+ }
3725
+ return hexToBytes(input.slice(2));
3726
+ }
3727
+
3728
+ // src/commands/account.ts
3729
+ init_output();
3730
+
3731
+ // src/core/pallet.ts
3732
+ init_errors();
3733
+ var MODL_PREFIX = new Uint8Array([109, 111, 100, 108]);
3734
+ var PALLET_ID_BYTES = 8;
3735
+ function derivePalletAccount(palletId) {
3736
+ if (palletId.length !== PALLET_ID_BYTES) {
3737
+ throw new CliError(`PalletId must be exactly ${PALLET_ID_BYTES} bytes (got ${palletId.length}).`);
3738
+ }
3739
+ const result = new Uint8Array(32);
3740
+ result.set(MODL_PREFIX, 0);
3741
+ result.set(palletId, 4);
3742
+ return result;
3743
+ }
3744
+ function parsePalletId(input) {
3745
+ if (input.startsWith("0x") || input.startsWith("0X")) {
3746
+ const hex = input.slice(2);
3747
+ if (hex.length !== PALLET_ID_BYTES * 2) {
3748
+ throw new CliError(`Invalid PalletId hex "${input}". Must be 0x followed by exactly ${PALLET_ID_BYTES * 2} hex characters.`);
3749
+ }
3750
+ if (!/^[0-9a-fA-F]+$/.test(hex)) {
3751
+ throw new CliError(`Invalid PalletId hex "${input}". Contains non-hex characters.`);
3752
+ }
3753
+ const bytes2 = new Uint8Array(PALLET_ID_BYTES);
3754
+ for (let i = 0;i < PALLET_ID_BYTES; i++) {
3755
+ bytes2[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
3756
+ }
3757
+ return bytes2;
3758
+ }
3759
+ if (input.length !== PALLET_ID_BYTES) {
3760
+ throw new CliError(`Invalid PalletId "${input}". Must be ${PALLET_ID_BYTES} ASCII characters or 0x-prefixed hex.`);
3761
+ }
3762
+ const bytes = new Uint8Array(PALLET_ID_BYTES);
3763
+ for (let i = 0;i < PALLET_ID_BYTES; i++) {
3764
+ const code = input.charCodeAt(i);
3765
+ if (code > 127) {
3766
+ throw new CliError(`Invalid PalletId "${input}". ASCII form must contain only ASCII bytes.`);
3767
+ }
3768
+ bytes[i] = code;
3769
+ }
3770
+ return bytes;
3771
+ }
3772
+ function formatPalletId(palletId) {
3773
+ const allPrintable = palletId.every((b) => b >= 32 && b <= 126);
3774
+ if (allPrintable) {
3775
+ return Array.from(palletId, (b) => String.fromCharCode(b)).join("");
3776
+ }
3777
+ return `0x${Array.from(palletId, (b) => b.toString(16).padStart(2, "0")).join("")}`;
3778
+ }
3779
+
3780
+ // src/core/parachain.ts
3781
+ var SOVEREIGN_ACCOUNT_TYPES = ["child", "sibling"];
3782
+ var PREFIXES = {
3783
+ child: new Uint8Array([112, 97, 114, 97]),
3784
+ sibling: new Uint8Array([115, 105, 98, 108])
3785
+ };
3786
+ function deriveSovereignAccount(paraId, type) {
3787
+ const result = new Uint8Array(32);
3788
+ result.set(PREFIXES[type], 0);
3789
+ new DataView(result.buffer).setUint32(4, paraId, true);
3790
+ return result;
3791
+ }
3792
+ function isValidParaId(value) {
3793
+ return Number.isInteger(value) && value >= 0 && value <= 4294967295;
3794
+ }
3795
+
3796
+ // src/commands/account.ts
2987
3797
  var ACCOUNT_HELP = `
2988
3798
  ${BOLD}Usage:${RESET}
2989
3799
  $ dot account add <name> <ss58|hex> Add a watch-only address (no secret)
2990
3800
  $ dot account add <name> --secret <s> [--path <derivation>] Import from BIP39 mnemonic
2991
3801
  $ dot account add <name> --env <VAR> [--path <derivation>] Import account backed by env variable
3802
+ $ dot account add <name> --parachain <id> --parachain-type <t> Derive a parachain sovereign (t = child|sibling)
3803
+ $ dot account add <name> --pallet-id <8 chars or 0x hex> Derive a pallet sovereign (e.g. py/trsry)
2992
3804
  $ dot account create|new <name> [--path <derivation>] Create a new account
2993
3805
  $ dot account import <file> Batch-import accounts from a file
2994
3806
  $ dot account export [names...] Export accounts to stdout
2995
3807
  $ dot account derive <source> <new-name> --path <derivation> Derive a child account
2996
3808
  $ dot account inspect <input> [--prefix <N>] [--show-secret] Inspect an account/address/key
3809
+ $ dot account inspect --pallet-id <id> [--prefix <N>] Derive a pallet sovereign (no save — script-friendly)
3810
+ $ dot account inspect --parachain <id> --parachain-type <t> Derive a parachain sovereign (no save — script-friendly)
2997
3811
  $ dot account list List all accounts
2998
3812
  $ dot account remove|delete <name> [name2] ... Remove stored account(s)
2999
3813
 
@@ -3001,6 +3815,10 @@ ${BOLD}Examples:${RESET}
3001
3815
  $ dot account add treasury 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
3002
3816
  $ dot account add treasury --secret "word1 word2 ... word12"
3003
3817
  $ dot account add ci-signer --env MY_SECRET --path //ci
3818
+ $ dot account add Treasury --pallet-id py/trsry
3819
+ $ dot account add Bounties --pallet-id 0x70792f626f756e74
3820
+ $ dot account add People --parachain 1004 --parachain-type child
3821
+ $ dot account add People-Sibling --parachain 1004 --parachain-type sibling
3004
3822
  $ dot account create my-validator
3005
3823
  $ dot account create my-staking --path //staking
3006
3824
  $ dot account create multi --path //polkadot//0/wallet
@@ -3016,6 +3834,8 @@ ${BOLD}Examples:${RESET}
3016
3834
  $ dot account inspect dave --show-secret
3017
3835
  $ dot account inspect 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
3018
3836
  $ dot account inspect 0xd435...a27d --prefix 0
3837
+ $ dot account inspect --pallet-id py/trsry --prefix 0
3838
+ $ dot account inspect --parachain 1004 --parachain-type child
3019
3839
  $ dot account list
3020
3840
  $ dot account remove my-validator stale-key
3021
3841
 
@@ -3024,7 +3844,7 @@ ${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
3024
3844
  Hex seed import (0x...) is not supported via CLI.${RESET}
3025
3845
  `.trimStart();
3026
3846
  function registerAccountCommands(cli) {
3027
- cli.command("account [action] [...names]", "Manage local accounts (create, import, list, remove, export)").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)").option("--file <path>", "Input/output file for batch import/export").option("--overwrite", "Overwrite existing accounts on batch import").option("--dry-run", "Preview batch import without applying changes").option("--include-secrets", "Include secrets in export (redacted by default)").option("--watch-only", "Export only watch-only accounts").option("--show-secret", "Reveal the 64-byte sr25519 expanded private key (inspect only)").action(async (action, names, opts) => {
3847
+ cli.command("account [action] [...names]", "Manage local accounts (create, import, list, remove, export)").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("--parachain <id>", "Derive a parachain sovereign account (requires --parachain-type)").option("--parachain-type <type>", "Parachain sovereign type: child or sibling").option("--pallet-id <id>", "Derive a pallet sovereign account from an 8-byte PalletId").option("--prefix <number>", "SS58 prefix for address encoding (default: 42)").option("--file <path>", "Input/output file for batch import/export").option("--overwrite", "Overwrite existing accounts on batch import").option("--dry-run", "Preview batch import without applying changes").option("--include-secrets", "Include secrets in export (redacted by default)").option("--watch-only", "Export only watch-only accounts").option("--show-secret", "Reveal the 64-byte sr25519 expanded private key (inspect only)").action(async (action, names, opts) => {
3028
3848
  if (!action) {
3029
3849
  if (process.argv[2] === "accounts")
3030
3850
  return accountList(opts);
@@ -3036,8 +3856,13 @@ function registerAccountCommands(cli) {
3036
3856
  case "create":
3037
3857
  return accountCreate(names[0], opts);
3038
3858
  case "add":
3039
- if (opts.secret || opts.env)
3859
+ if (opts.secret || opts.env) {
3860
+ const hasDerivation = opts.parachain != null || opts.palletId != null || process.argv.includes("--parachain") || process.argv.includes("--pallet-id") || process.argv.some((a) => a.startsWith("--parachain=") || a.startsWith("--pallet-id="));
3861
+ if (hasDerivation) {
3862
+ throw new Error("Derivation flags (--parachain, --pallet-id) cannot be combined with --secret or --env. A derived sovereign account has no secret.");
3863
+ }
3040
3864
  return accountImport(names[0], opts);
3865
+ }
3041
3866
  return accountAddWatchOnly(names[0], names[1], opts);
3042
3867
  case "import":
3043
3868
  return accountBatchImport(names[0], opts);
@@ -3193,10 +4018,16 @@ async function accountAddWatchOnly(name, address, opts = {}) {
3193
4018
  console.error("Usage: dot account add <name> <ss58-address|0x-public-key>");
3194
4019
  process.exit(1);
3195
4020
  }
3196
- if (!address) {
4021
+ const sovereignSource = resolveSovereignSource(opts);
4022
+ if (sovereignSource && address) {
4023
+ throw new Error("Cannot combine a positional address with --parachain or --pallet-id. Pass either an address OR a derivation flag, not both.");
4024
+ }
4025
+ if (!sovereignSource && !address) {
3197
4026
  console.error(`Address is required.
3198
4027
  `);
3199
4028
  console.error("Usage: dot account add <name> <ss58-address|0x-public-key>");
4029
+ console.error(" dot account add <name> --parachain <id> --parachain-type <child|sibling>");
4030
+ console.error(" dot account add <name> --pallet-id <8 chars or 0x hex>");
3200
4031
  process.exit(1);
3201
4032
  }
3202
4033
  if (isDevAccount(name)) {
@@ -3207,7 +4038,10 @@ async function accountAddWatchOnly(name, address, opts = {}) {
3207
4038
  throw new Error(`Account "${name}" already exists.`);
3208
4039
  }
3209
4040
  let hexPub;
3210
- if (isHexPublicKey(address)) {
4041
+ if (sovereignSource) {
4042
+ const accountId = sovereignSource.kind === "parachain" ? deriveSovereignAccount(sovereignSource.paraId, sovereignSource.type) : derivePalletAccount(sovereignSource.palletId);
4043
+ hexPub = publicKeyToHex(accountId);
4044
+ } else if (isHexPublicKey(address)) {
3211
4045
  hexPub = address;
3212
4046
  } else {
3213
4047
  try {
@@ -3217,21 +4051,99 @@ async function accountAddWatchOnly(name, address, opts = {}) {
3217
4051
  throw new Error(`Invalid address "${address}". Expected an SS58 address or 0x-prefixed 32-byte hex public key.`);
3218
4052
  }
3219
4053
  }
4054
+ let persistedSource;
4055
+ if (sovereignSource?.kind === "pallet") {
4056
+ persistedSource = {
4057
+ kind: "pallet",
4058
+ palletId: `0x${Array.from(sovereignSource.palletId, (b) => b.toString(16).padStart(2, "0")).join("")}`
4059
+ };
4060
+ } else if (sovereignSource?.kind === "parachain") {
4061
+ persistedSource = {
4062
+ kind: "parachain",
4063
+ paraId: sovereignSource.paraId,
4064
+ type: sovereignSource.type
4065
+ };
4066
+ }
3220
4067
  accountsFile.accounts.push({
3221
4068
  name,
3222
4069
  publicKey: hexPub,
3223
- derivationPath: ""
4070
+ derivationPath: "",
4071
+ ...persistedSource ? { source: persistedSource } : {}
3224
4072
  });
3225
4073
  await saveAccounts(accountsFile);
3226
4074
  if (isJsonOutput(opts)) {
3227
- console.log(formatJson({ name, address: toSs58(hexPub), watchOnly: true }));
4075
+ const payload = {
4076
+ name,
4077
+ address: toSs58(hexPub),
4078
+ watchOnly: true
4079
+ };
4080
+ if (sovereignSource?.kind === "parachain") {
4081
+ payload.derivation = {
4082
+ kind: "parachain",
4083
+ paraId: sovereignSource.paraId,
4084
+ type: sovereignSource.type
4085
+ };
4086
+ } else if (sovereignSource?.kind === "pallet") {
4087
+ payload.derivation = {
4088
+ kind: "pallet",
4089
+ palletId: formatPalletId(sovereignSource.palletId),
4090
+ palletIdHex: `0x${Array.from(sovereignSource.palletId, (b) => b.toString(16).padStart(2, "0")).join("")}`
4091
+ };
4092
+ }
4093
+ console.log(formatJson(payload));
3228
4094
  return;
3229
4095
  }
3230
4096
  printHeading("Account Added (watch-only)");
3231
4097
  console.log(` ${BOLD}Name:${RESET} ${name}`);
3232
4098
  console.log(` ${BOLD}Address:${RESET} ${toSs58(hexPub)}`);
4099
+ if (sovereignSource?.kind === "parachain") {
4100
+ console.log(` ${BOLD}Source:${RESET} parachain ${sovereignSource.paraId} (${sovereignSource.type} sovereign)`);
4101
+ } else if (sovereignSource?.kind === "pallet") {
4102
+ const display = formatPalletId(sovereignSource.palletId);
4103
+ const hex = `0x${Array.from(sovereignSource.palletId, (b) => b.toString(16).padStart(2, "0")).join("")}`;
4104
+ console.log(` ${BOLD}Source:${RESET} pallet ${display} (${hex})`);
4105
+ }
3233
4106
  console.log();
3234
4107
  }
4108
+ function rawArgValue(flag) {
4109
+ const argv = process.argv;
4110
+ for (let i = 0;i < argv.length; i++) {
4111
+ if (argv[i] === flag && i + 1 < argv.length)
4112
+ return argv[i + 1];
4113
+ const prefix = `${flag}=`;
4114
+ if (argv[i]?.startsWith(prefix))
4115
+ return argv[i].slice(prefix.length);
4116
+ }
4117
+ return;
4118
+ }
4119
+ function resolveSovereignSource(opts) {
4120
+ const rawPalletId = rawArgValue("--pallet-id") ?? opts.palletId;
4121
+ const rawParachain = rawArgValue("--parachain") ?? opts.parachain;
4122
+ if (rawParachain != null && rawPalletId != null) {
4123
+ throw new Error("--parachain and --pallet-id are mutually exclusive. Pass only one derivation flag.");
4124
+ }
4125
+ if (rawParachain != null) {
4126
+ const paraId = Number(rawParachain);
4127
+ if (!isValidParaId(paraId)) {
4128
+ throw new Error(`Invalid parachain ID "${rawParachain}". Must be a non-negative integer (0 to 4294967295).`);
4129
+ }
4130
+ if (!opts.parachainType) {
4131
+ throw new Error("--parachain-type is required when --parachain is given. Use --parachain-type child or --parachain-type sibling.");
4132
+ }
4133
+ const type = opts.parachainType.toLowerCase();
4134
+ if (type !== "child" && type !== "sibling") {
4135
+ throw new Error(`Unknown parachain account type "${opts.parachainType}". Valid types: child, sibling.`);
4136
+ }
4137
+ return { kind: "parachain", paraId, type };
4138
+ }
4139
+ if (rawPalletId != null) {
4140
+ return { kind: "pallet", palletId: parsePalletId(String(rawPalletId)) };
4141
+ }
4142
+ if (opts.parachainType != null) {
4143
+ throw new Error("--parachain-type requires --parachain.");
4144
+ }
4145
+ return;
4146
+ }
3235
4147
  async function accountDerive(sourceName, newName, opts) {
3236
4148
  if (!sourceName) {
3237
4149
  console.error(`Source account name is required.
@@ -3315,69 +4227,134 @@ async function accountDerive(sourceName, newName, opts) {
3315
4227
  console.log();
3316
4228
  }
3317
4229
  }
4230
+ function resolveAddress(account) {
4231
+ if (isWatchOnly(account)) {
4232
+ return account.publicKey ? toSs58(account.publicKey) : "n/a";
4233
+ }
4234
+ if (account.secret !== undefined && isEnvSecret(account.secret)) {
4235
+ const pubKey = account.publicKey || tryDerivePublicKey(account.secret.env, account.derivationPath) || "";
4236
+ return pubKey ? toSs58(pubKey) : "n/a";
4237
+ }
4238
+ return toSs58(account.publicKey);
4239
+ }
4240
+ function buildAttributes(account) {
4241
+ const attrs = [];
4242
+ if (account.derivationPath)
4243
+ attrs.push({ label: "path", value: account.derivationPath });
4244
+ if (account.secret !== undefined && isEnvSecret(account.secret)) {
4245
+ attrs.push({ label: "env", value: `$${account.secret.env}` });
4246
+ }
4247
+ if (account.source) {
4248
+ if (account.source.kind === "pallet") {
4249
+ const bytes = parsePalletId(account.source.palletId);
4250
+ attrs.push({
4251
+ label: "pallet-id",
4252
+ value: `${formatPalletId(bytes)} (${account.source.palletId})`
4253
+ });
4254
+ } else {
4255
+ attrs.push({ label: "parachain", value: String(account.source.paraId) });
4256
+ attrs.push({ label: "parachain-type", value: account.source.type });
4257
+ }
4258
+ }
4259
+ return attrs;
4260
+ }
4261
+ function buildRow(account) {
4262
+ return {
4263
+ account,
4264
+ kind: classifyAccount(account),
4265
+ address: resolveAddress(account),
4266
+ attributes: buildAttributes(account)
4267
+ };
4268
+ }
4269
+ var SECTION_ORDER = [
4270
+ { kind: "signer", title: "Signers" },
4271
+ { kind: "watch-only", title: "Watch-only" },
4272
+ { kind: "pallet", title: "Pallet Sovereigns" },
4273
+ { kind: "parachain", title: "Parachain Sovereigns" }
4274
+ ];
4275
+ function printAccountSection(title, rows) {
4276
+ if (rows.length === 0)
4277
+ return;
4278
+ const nameWidth = Math.max(...rows.map((r) => r.name.length));
4279
+ printHeading(title);
4280
+ for (const row of rows) {
4281
+ const namePad = row.name.padEnd(nameWidth);
4282
+ console.log(` ${CYAN}${namePad}${RESET} ${row.address}`);
4283
+ if (row.attributes.length === 0)
4284
+ continue;
4285
+ const labelWidth = Math.max(...row.attributes.map((a) => a.label.length)) + 1;
4286
+ for (let i = 0;i < row.attributes.length; i++) {
4287
+ const isLast = i === row.attributes.length - 1;
4288
+ const connector = isLast ? "└─" : "├─";
4289
+ const labelText = `${row.attributes[i].label}:`.padEnd(labelWidth + 1);
4290
+ console.log(` ${DIM}${connector}${RESET} ${DIM}${labelText}${RESET}${row.attributes[i].value}`);
4291
+ }
4292
+ }
4293
+ }
3318
4294
  async function accountList(opts = {}) {
3319
4295
  const accountsFile = await loadAccounts();
3320
4296
  if (isJsonOutput(opts)) {
3321
4297
  const dev = DEV_NAMES.map((name) => ({
3322
4298
  name: name.charAt(0).toUpperCase() + name.slice(1),
3323
- address: getDevAddress(name)
4299
+ address: getDevAddress(name),
4300
+ kind: "dev"
3324
4301
  }));
3325
4302
  const stored = accountsFile.accounts.map((account) => {
3326
- let address;
3327
- if (isWatchOnly(account)) {
3328
- address = account.publicKey ? toSs58(account.publicKey) : undefined;
3329
- } else if (account.secret !== undefined && isEnvSecret(account.secret)) {
3330
- let pubKey = account.publicKey;
3331
- if (!pubKey) {
3332
- pubKey = tryDerivePublicKey(account.secret.env, account.derivationPath) ?? "";
3333
- }
3334
- address = pubKey ? toSs58(pubKey) : undefined;
3335
- } else {
3336
- address = toSs58(account.publicKey);
3337
- }
3338
- return {
4303
+ const kind = classifyAccount(account);
4304
+ const entry = {
3339
4305
  name: account.name,
3340
- address,
3341
- derivationPath: account.derivationPath || undefined,
3342
- watchOnly: isWatchOnly(account),
3343
- env: account.secret !== undefined && isEnvSecret(account.secret) ? account.secret.env : undefined
4306
+ address: resolveAddress(account),
4307
+ kind,
4308
+ watchOnly: isWatchOnly(account)
3344
4309
  };
4310
+ if (account.derivationPath)
4311
+ entry.derivationPath = account.derivationPath;
4312
+ if (account.secret !== undefined && isEnvSecret(account.secret)) {
4313
+ entry.env = account.secret.env;
4314
+ }
4315
+ if (account.source) {
4316
+ if (account.source.kind === "pallet") {
4317
+ const bytes = parsePalletId(account.source.palletId);
4318
+ entry.source = {
4319
+ kind: "pallet",
4320
+ palletId: formatPalletId(bytes),
4321
+ palletIdHex: account.source.palletId
4322
+ };
4323
+ } else {
4324
+ entry.source = account.source;
4325
+ }
4326
+ }
4327
+ return entry;
3345
4328
  });
3346
4329
  console.log(formatJson({ dev, stored }));
3347
4330
  return;
3348
4331
  }
3349
- printHeading("Dev Accounts");
3350
- for (const name of DEV_NAMES) {
3351
- const display = name.charAt(0).toUpperCase() + name.slice(1);
3352
- const address = getDevAddress(name);
3353
- printItem(display, address);
4332
+ const devRows = DEV_NAMES.map((name) => ({
4333
+ name: name.charAt(0).toUpperCase() + name.slice(1),
4334
+ address: getDevAddress(name),
4335
+ attributes: []
4336
+ }));
4337
+ printAccountSection("Dev Accounts", devRows);
4338
+ const buckets = new Map;
4339
+ for (const account of accountsFile.accounts) {
4340
+ const row = buildRow(account);
4341
+ const arr = buckets.get(row.kind) ?? [];
4342
+ arr.push(row);
4343
+ buckets.set(row.kind, arr);
4344
+ }
4345
+ for (const { kind, title } of SECTION_ORDER) {
4346
+ const rows = buckets.get(kind);
4347
+ if (!rows || rows.length === 0)
4348
+ continue;
4349
+ printAccountSection(title, rows.map((r) => ({
4350
+ name: r.account.name,
4351
+ address: r.address,
4352
+ attributes: r.attributes
4353
+ })));
3354
4354
  }
3355
- if (accountsFile.accounts.length > 0) {
3356
- printHeading("Stored Accounts");
3357
- for (const account of accountsFile.accounts) {
3358
- let displayName = account.name;
3359
- if (account.derivationPath) {
3360
- displayName += ` (${account.derivationPath})`;
3361
- }
3362
- let address;
3363
- if (isWatchOnly(account)) {
3364
- displayName += " (watch-only)";
3365
- address = account.publicKey ? toSs58(account.publicKey) : "n/a";
3366
- } else if (account.secret !== undefined && isEnvSecret(account.secret)) {
3367
- displayName += ` (env: ${account.secret.env})`;
3368
- let pubKey = account.publicKey;
3369
- if (!pubKey) {
3370
- pubKey = tryDerivePublicKey(account.secret.env, account.derivationPath) ?? "";
3371
- }
3372
- address = pubKey ? toSs58(pubKey) : "n/a";
3373
- } else {
3374
- address = toSs58(account.publicKey);
3375
- }
3376
- printItem(displayName, address);
3377
- }
3378
- } else {
4355
+ if (accountsFile.accounts.length === 0) {
3379
4356
  printHeading("Stored Accounts");
3380
- console.log(" (none)");
4357
+ console.log(` ${DIM}(none)${RESET}`);
3381
4358
  }
3382
4359
  console.log();
3383
4360
  }
@@ -3425,10 +4402,20 @@ async function accountRemove(names, opts = {}) {
3425
4402
  }
3426
4403
  }
3427
4404
  async function accountInspect(input, opts) {
3428
- if (!input) {
4405
+ const sovereignSource = resolveSovereignSource({
4406
+ parachain: opts.parachain,
4407
+ parachainType: opts.parachainType,
4408
+ palletId: opts.palletId
4409
+ });
4410
+ if (sovereignSource && input) {
4411
+ throw new Error("Cannot combine a positional input with --parachain or --pallet-id. Pass either an existing-account input OR a derivation flag, not both.");
4412
+ }
4413
+ if (!sovereignSource && !input) {
3429
4414
  console.error(`Input is required.
3430
4415
  `);
3431
4416
  console.error("Usage: dot account inspect <name|ss58-address|0x-public-key> [--prefix <N>]");
4417
+ console.error(" dot account inspect --pallet-id <id> [--prefix <N>]");
4418
+ console.error(" dot account inspect --parachain <id> --parachain-type <child|sibling> [--prefix <N>]");
3432
4419
  process.exit(1);
3433
4420
  }
3434
4421
  const prefix = opts.prefix != null ? Number(opts.prefix) : 42;
@@ -3440,11 +4427,33 @@ async function accountInspect(input, opts) {
3440
4427
  let publicKeyHex;
3441
4428
  let bandersnatch;
3442
4429
  let hasSecret = false;
3443
- if (isDevAccount(input)) {
4430
+ let storedAccount;
4431
+ let isDev = false;
4432
+ let isH160Fallback = false;
4433
+ let virtualSource;
4434
+ if (sovereignSource) {
4435
+ if (sovereignSource.kind === "pallet") {
4436
+ const accountId = derivePalletAccount(sovereignSource.palletId);
4437
+ publicKeyHex = publicKeyToHex(accountId);
4438
+ virtualSource = {
4439
+ kind: "pallet",
4440
+ palletIdHex: `0x${Array.from(sovereignSource.palletId, (b) => b.toString(16).padStart(2, "0")).join("")}`
4441
+ };
4442
+ } else {
4443
+ const accountId = deriveSovereignAccount(sovereignSource.paraId, sovereignSource.type);
4444
+ publicKeyHex = publicKeyToHex(accountId);
4445
+ virtualSource = {
4446
+ kind: "parachain",
4447
+ paraId: sovereignSource.paraId,
4448
+ type: sovereignSource.type
4449
+ };
4450
+ }
4451
+ } else if (isDevAccount(input)) {
3444
4452
  name = input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
3445
4453
  const devAddr = getDevAddress(input);
3446
4454
  publicKeyHex = publicKeyToHex(fromSs58(devAddr));
3447
4455
  hasSecret = true;
4456
+ isDev = true;
3448
4457
  } else {
3449
4458
  const accountsFile = await loadAccounts();
3450
4459
  const account = findAccount(accountsFile, input);
@@ -3452,6 +4461,7 @@ async function accountInspect(input, opts) {
3452
4461
  name = account.name;
3453
4462
  bandersnatch = account.bandersnatch;
3454
4463
  hasSecret = account.secret !== undefined;
4464
+ storedAccount = account;
3455
4465
  if (account.publicKey) {
3456
4466
  publicKeyHex = account.publicKey;
3457
4467
  } else if (account.secret !== undefined && isEnvSecret(account.secret)) {
@@ -3467,17 +4477,22 @@ async function accountInspect(input, opts) {
3467
4477
  }
3468
4478
  } else if (isHexPublicKey(input)) {
3469
4479
  publicKeyHex = input;
4480
+ } else if (isH160Hex(input)) {
4481
+ const fallback = h160ToFallbackAccountId(h160FromHex(input));
4482
+ publicKeyHex = publicKeyToHex(fallback);
4483
+ isH160Fallback = true;
3470
4484
  } else {
3471
4485
  try {
3472
4486
  const decoded = fromSs58(input);
3473
4487
  publicKeyHex = publicKeyToHex(decoded);
3474
4488
  } catch {
3475
- console.error(`Cannot identify "${input}" as an account name, SS58 address, or hex public key.`);
4489
+ console.error(`Cannot identify "${input}" as an account name, SS58 address, hex public key, or H160.`);
3476
4490
  process.exit(1);
3477
4491
  }
3478
4492
  }
3479
4493
  }
3480
4494
  const ss58 = toSs58(publicKeyHex, prefix);
4495
+ const h160Hex = toEip55(accountIdToH160(nobleHexToBytes(publicKeyHex.slice(2))));
3481
4496
  let privateKeyHex;
3482
4497
  if (opts.showSecret) {
3483
4498
  if (!name) {
@@ -3495,10 +4510,81 @@ async function accountInspect(input, opts) {
3495
4510
  process.exit(1);
3496
4511
  }
3497
4512
  }
4513
+ let kindLabel;
4514
+ let sourceLine;
4515
+ let derivationLine;
4516
+ let envLine;
4517
+ if (virtualSource?.kind === "pallet") {
4518
+ kindLabel = "pallet sovereign";
4519
+ const bytes = parsePalletId(virtualSource.palletIdHex);
4520
+ sourceLine = `PalletId ${formatPalletId(bytes)} (${virtualSource.palletIdHex})`;
4521
+ } else if (virtualSource?.kind === "parachain") {
4522
+ kindLabel = `parachain sovereign (${virtualSource.type})`;
4523
+ sourceLine = `parachain ${virtualSource.paraId}`;
4524
+ } else if (isDev) {
4525
+ kindLabel = "dev";
4526
+ } else if (isH160Fallback) {
4527
+ kindLabel = "revive H160 fallback";
4528
+ } else if (storedAccount) {
4529
+ const k = classifyAccount(storedAccount);
4530
+ if (k === "pallet" && storedAccount.source?.kind === "pallet") {
4531
+ kindLabel = "pallet sovereign";
4532
+ const bytes = parsePalletId(storedAccount.source.palletId);
4533
+ sourceLine = `PalletId ${formatPalletId(bytes)} (${storedAccount.source.palletId})`;
4534
+ } else if (k === "parachain" && storedAccount.source?.kind === "parachain") {
4535
+ kindLabel = `parachain sovereign (${storedAccount.source.type})`;
4536
+ sourceLine = `parachain ${storedAccount.source.paraId}`;
4537
+ } else if (k === "signer") {
4538
+ kindLabel = "signer";
4539
+ } else {
4540
+ kindLabel = "watch-only";
4541
+ }
4542
+ if (storedAccount.derivationPath)
4543
+ derivationLine = storedAccount.derivationPath;
4544
+ if (storedAccount.secret !== undefined && isEnvSecret(storedAccount.secret)) {
4545
+ envLine = `$${storedAccount.secret.env}`;
4546
+ }
4547
+ }
3498
4548
  if (isJsonOutput(opts)) {
3499
- const result = { publicKey: publicKeyHex, ss58, prefix };
4549
+ const result = {
4550
+ publicKey: publicKeyHex,
4551
+ ss58,
4552
+ h160: h160Hex,
4553
+ prefix
4554
+ };
3500
4555
  if (name)
3501
4556
  result.name = name;
4557
+ if (kindLabel)
4558
+ result.kind = kindLabel;
4559
+ if (virtualSource?.kind === "pallet") {
4560
+ const bytes = parsePalletId(virtualSource.palletIdHex);
4561
+ result.source = {
4562
+ kind: "pallet",
4563
+ palletId: formatPalletId(bytes),
4564
+ palletIdHex: virtualSource.palletIdHex
4565
+ };
4566
+ } else if (virtualSource?.kind === "parachain") {
4567
+ result.source = {
4568
+ kind: "parachain",
4569
+ paraId: virtualSource.paraId,
4570
+ type: virtualSource.type
4571
+ };
4572
+ } else if (storedAccount?.source) {
4573
+ if (storedAccount.source.kind === "pallet") {
4574
+ const bytes = parsePalletId(storedAccount.source.palletId);
4575
+ result.source = {
4576
+ kind: "pallet",
4577
+ palletId: formatPalletId(bytes),
4578
+ palletIdHex: storedAccount.source.palletId
4579
+ };
4580
+ } else {
4581
+ result.source = storedAccount.source;
4582
+ }
4583
+ }
4584
+ if (derivationLine)
4585
+ result.derivationPath = derivationLine;
4586
+ if (envLine)
4587
+ result.env = envLine.replace(/^\$/, "");
3502
4588
  if (bandersnatch && Object.keys(bandersnatch).length > 0)
3503
4589
  result.bandersnatch = bandersnatch;
3504
4590
  if (privateKeyHex)
@@ -3508,8 +4594,17 @@ async function accountInspect(input, opts) {
3508
4594
  printHeading("Account Info");
3509
4595
  if (name)
3510
4596
  console.log(` ${BOLD}Name:${RESET} ${name}`);
4597
+ if (kindLabel)
4598
+ console.log(` ${BOLD}Kind:${RESET} ${kindLabel}`);
3511
4599
  console.log(` ${BOLD}Public Key:${RESET} ${publicKeyHex}`);
3512
4600
  console.log(` ${BOLD}SS58:${RESET} ${ss58}`);
4601
+ console.log(` ${BOLD}H160:${RESET} ${h160Hex}`);
4602
+ if (sourceLine)
4603
+ console.log(` ${BOLD}Source:${RESET} ${sourceLine}`);
4604
+ if (derivationLine)
4605
+ console.log(` ${BOLD}Derivation:${RESET} ${derivationLine}`);
4606
+ if (envLine)
4607
+ console.log(` ${BOLD}Env:${RESET} ${envLine}`);
3513
4608
  if (bandersnatch && Object.keys(bandersnatch).length > 0) {
3514
4609
  const entries = Object.entries(bandersnatch);
3515
4610
  for (let i = 0;i < entries.length; i++) {
@@ -3777,8 +4872,10 @@ async function handleApis2(target, args, opts) {
3777
4872
  typeof opts.parsedArgs === "object" ? JSON.stringify(opts.parsedArgs) : String(opts.parsedArgs)
3778
4873
  ];
3779
4874
  const parsedArgs = await parseRuntimeApiArgs2(meta, method, effectiveArgs);
4875
+ const atArg = parseAtForRead(opts.at);
4876
+ const pullOpts = atArg !== undefined ? [{ at: atArg }] : [];
3780
4877
  const unsafeApi = clientHandle.client.getUnsafeApi();
3781
- const result = await unsafeApi.apis[api.name][method.name](...parsedArgs);
4878
+ const result = await withBlockAvailabilityHint(opts.at, () => unsafeApi.apis[api.name][method.name](...parsedArgs, ...pullOpts));
3782
4879
  const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
3783
4880
  printResult(result, format);
3784
4881
  } finally {
@@ -3813,6 +4910,53 @@ init_client();
3813
4910
  init_metadata();
3814
4911
  init_output();
3815
4912
  import { readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
4913
+
4914
+ // src/core/rpc.ts
4915
+ init_errors();
4916
+ import { createClient as createSubstrateClient } from "@polkadot-api/substrate-client";
4917
+ import { getWsProvider as getWsProvider2 } from "polkadot-api/ws";
4918
+ function suppressProviderNoise2() {
4919
+ const origError = console.error;
4920
+ console.error = (...args) => {
4921
+ if (typeof args[0] === "string" && args[0].includes("Unable to connect"))
4922
+ return;
4923
+ origError(...args);
4924
+ };
4925
+ return () => {
4926
+ console.error = origError;
4927
+ };
4928
+ }
4929
+ async function rpcRequest(rpcUrl, method, params, timeoutMs = 30000) {
4930
+ const restoreConsole = suppressProviderNoise2();
4931
+ const provider = getWsProvider2(rpcUrl, { timeout: 1e4 });
4932
+ const client = createSubstrateClient(provider);
4933
+ const controller = new AbortController;
4934
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
4935
+ try {
4936
+ return await client.request(method, params, controller.signal);
4937
+ } catch (err) {
4938
+ if (err instanceof Error && /Unable to connect/i.test(err.message)) {
4939
+ throw new ConnectionError(`Could not reach RPC ${Array.isArray(rpcUrl) ? rpcUrl.join(", ") : rpcUrl}: ${err.message}`);
4940
+ }
4941
+ throw err;
4942
+ } finally {
4943
+ clearTimeout(timer);
4944
+ try {
4945
+ client.destroy();
4946
+ } catch {}
4947
+ setTimeout(restoreConsole, 100);
4948
+ }
4949
+ }
4950
+ async function fetchRpcMethods(rpcUrl) {
4951
+ const result = await rpcRequest(rpcUrl, "rpc_methods", []);
4952
+ if (!result || !Array.isArray(result.methods)) {
4953
+ throw new ConnectionError("Node returned an unexpected response for rpc_methods (no methods array).");
4954
+ }
4955
+ return { methods: result.methods, version: result.version ?? 1 };
4956
+ }
4957
+
4958
+ // src/commands/chain.ts
4959
+ init_rpc_registry();
3816
4960
  var CHAIN_HELP = `
3817
4961
  ${BOLD}Usage:${RESET}
3818
4962
  $ dot chain add <name> --rpc <url> Add a chain via WebSocket RPC
@@ -3881,6 +5025,18 @@ function registerChainCommands(cli) {
3881
5025
  }
3882
5026
  });
3883
5027
  }
5028
+ async function refreshRpcMethods(chainName, rpcUrl, silent = false) {
5029
+ try {
5030
+ const { methods, version: version2 } = await fetchRpcMethods(rpcUrl);
5031
+ await saveRpcMethods(chainName, methods, version2);
5032
+ } catch (err) {
5033
+ if (!silent) {
5034
+ const msg = err instanceof Error ? err.message : String(err);
5035
+ process.stderr.write(`${YELLOW}Warning:${RESET} could not fetch rpc_methods — ${msg}
5036
+ `);
5037
+ }
5038
+ }
5039
+ }
3884
5040
  async function chainAdd(name, opts) {
3885
5041
  if (!name) {
3886
5042
  console.error(`Chain name is required.
@@ -3909,6 +5065,7 @@ async function chainAdd(name, opts) {
3909
5065
  process.stderr.write(`Fetching metadata...
3910
5066
  `);
3911
5067
  await fetchMetadataFromChain(clientHandle, name);
5068
+ await refreshRpcMethods(name, opts.rpc);
3912
5069
  if (opts.relay) {
3913
5070
  const config2 = await loadConfig();
3914
5071
  const relayResolved = findChainName(config2, opts.relay);
@@ -4049,6 +5206,12 @@ async function chainInfo(name, opts = {}) {
4049
5206
  const rpcs = Array.isArray(chain.rpc) ? chain.rpc : [chain.rpc];
4050
5207
  const parachains = Object.entries(config.chains).filter(([, c]) => c.relay === resolved).map(([n, c]) => ({ name: n, parachainId: c.parachainId }));
4051
5208
  const fingerprint = await loadMetadataFingerprint(resolved);
5209
+ const rpcCache = await loadRpcMethods(resolved);
5210
+ const rpcSummary = rpcCache ? {
5211
+ count: rpcCache.methods.length,
5212
+ fetchedAt: rpcCache.fetchedAt,
5213
+ byFamily: countByFamily(rpcCache.methods)
5214
+ } : null;
4052
5215
  if (isJsonOutput(opts)) {
4053
5216
  console.log(formatJson({
4054
5217
  name: resolved,
@@ -4060,7 +5223,8 @@ async function chainInfo(name, opts = {}) {
4060
5223
  specName: fingerprint.specName,
4061
5224
  specVersion: fingerprint.specVersion,
4062
5225
  fetchedAt: fingerprint.fetchedAt
4063
- } : null
5226
+ } : null,
5227
+ rpcMethods: rpcSummary
4064
5228
  }));
4065
5229
  return;
4066
5230
  }
@@ -4088,6 +5252,22 @@ async function chainInfo(name, opts = {}) {
4088
5252
  } else {
4089
5253
  console.log(` ${DIM}not cached — run \`dot chain update ${resolved}\`${RESET}`);
4090
5254
  }
5255
+ console.log(` ${CYAN}rpc methods:${RESET}`);
5256
+ if (rpcSummary) {
5257
+ const families = Object.entries(rpcSummary.byFamily).map(([f, n]) => `${f}: ${n}`).join(", ");
5258
+ console.log(` ${rpcSummary.count} ${DIM}(${families})${RESET}`);
5259
+ console.log(` ${DIM}cached ${rpcSummary.fetchedAt}${RESET}`);
5260
+ } else {
5261
+ console.log(` ${DIM}not cached — run \`dot chain update ${resolved}\`${RESET}`);
5262
+ }
5263
+ }
5264
+ function countByFamily(methods) {
5265
+ const counts = {};
5266
+ for (const m of methods) {
5267
+ const family = RPC_REGISTRY[m]?.family ?? inferFamily(m);
5268
+ counts[family] = (counts[family] ?? 0) + 1;
5269
+ }
5270
+ return counts;
4091
5271
  }
4092
5272
  async function chainUpdate(name, opts) {
4093
5273
  const config = await loadConfig();
@@ -4107,6 +5287,7 @@ async function chainUpdate(name, opts) {
4107
5287
  process.stderr.write(`Fetching metadata...
4108
5288
  `);
4109
5289
  await fetchMetadataFromChain(clientHandle, chainName);
5290
+ await refreshRpcMethods(chainName, opts.rpc ?? chainConfig.rpc);
4110
5291
  if (isJsonOutput(opts)) {
4111
5292
  console.log(formatJson({ action: "updated", chain: chainName }));
4112
5293
  } else {
@@ -4136,6 +5317,7 @@ async function updateChainsMetadata(config, chainNames) {
4136
5317
  const clientHandle = await createChainClient(chainName, chainConfig);
4137
5318
  try {
4138
5319
  await fetchMetadataFromChain(clientHandle, chainName);
5320
+ await refreshRpcMethods(chainName, chainConfig.rpc, true);
4139
5321
  } finally {
4140
5322
  clientHandle.destroy();
4141
5323
  }
@@ -5687,24 +6869,6 @@ function registerMetadataCommand(cli) {
5687
6869
  // src/commands/parachain.ts
5688
6870
  init_accounts();
5689
6871
  init_output();
5690
-
5691
- // src/core/parachain.ts
5692
- var SOVEREIGN_ACCOUNT_TYPES = ["child", "sibling"];
5693
- var PREFIXES = {
5694
- child: new Uint8Array([112, 97, 114, 97]),
5695
- sibling: new Uint8Array([115, 105, 98, 108])
5696
- };
5697
- function deriveSovereignAccount(paraId, type) {
5698
- const result = new Uint8Array(32);
5699
- result.set(PREFIXES[type], 0);
5700
- new DataView(result.buffer).setUint32(4, paraId, true);
5701
- return result;
5702
- }
5703
- function isValidParaId(value) {
5704
- return Number.isInteger(value) && value >= 0 && value <= 4294967295;
5705
- }
5706
-
5707
- // src/commands/parachain.ts
5708
6872
  init_errors();
5709
6873
  function printParachainHelp() {
5710
6874
  console.log(`${BOLD}Usage:${RESET} dot parachain <paraId> [options]
@@ -5734,6 +6898,7 @@ function validateType(type) {
5734
6898
  }
5735
6899
  function registerParachainCommand(cli) {
5736
6900
  cli.command("parachain [paraId]", "Derive parachain sovereign accounts").option("--type <type>", "Account type: child, sibling (default: both)").option("--prefix <number>", "SS58 prefix for address encoding (default: 42)").action(async (paraIdStr, opts) => {
6901
+ console.error("Warning: `dot parachain` is deprecated; use `dot account inspect --parachain <id> --parachain-type <child|sibling>` instead. Will be removed in a future release.");
5737
6902
  if (!paraIdStr) {
5738
6903
  printParachainHelp();
5739
6904
  return;
@@ -5880,6 +7045,8 @@ async function handleQuery(target, keys, opts) {
5880
7045
  typeof opts.parsedArgs === "object" ? JSON.stringify(opts.parsedArgs) : String(opts.parsedArgs)
5881
7046
  ];
5882
7047
  const parsedKeys = await parseStorageKeys(meta, palletInfo.name, storageItem, effectiveKeys);
7048
+ const atArg = parseAtForRead(opts.at);
7049
+ const pullOpts = atArg !== undefined ? [{ at: atArg }] : [];
5883
7050
  const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
5884
7051
  const expectedLen = storageItem.type === "map" && storageItem.keyTypeId != null ? meta.builder.buildStorage(palletInfo.name, storageItem.name).len : 0;
5885
7052
  if (storageItem.type === "map" && parsedKeys.length < expectedLen) {
@@ -5889,7 +7056,7 @@ async function handleQuery(target, keys, opts) {
5889
7056
  console.log(`${DIM}Hint: use --dump to fetch all entries${RESET}`);
5890
7057
  return;
5891
7058
  }
5892
- const entries = await withStalenessSuggestion(chainName, clientHandle, () => storageApi.getEntries(...parsedKeys));
7059
+ const entries = await withStalenessSuggestion(chainName, clientHandle, () => withBlockAvailabilityHint(opts.at, () => storageApi.getEntries(...parsedKeys, ...pullOpts)));
5893
7060
  const rows = entries.map((e) => ({
5894
7061
  keys: e.keyArgs,
5895
7062
  value: e.value
@@ -5898,7 +7065,7 @@ async function handleQuery(target, keys, opts) {
5898
7065
  await writeStdout(`${text}
5899
7066
  `);
5900
7067
  } else {
5901
- const result = await withStalenessSuggestion(chainName, clientHandle, () => storageApi.getValue(...parsedKeys));
7068
+ const result = await withStalenessSuggestion(chainName, clientHandle, () => withBlockAvailabilityHint(opts.at, () => storageApi.getValue(...parsedKeys, ...pullOpts)));
5902
7069
  const text = format === "json" ? formatJson(result) : formatPretty(result);
5903
7070
  await writeStdout(`${text}
5904
7071
  `);
@@ -5947,6 +7114,168 @@ async function parseStorageKeys(meta, palletName2, storageItem, args) {
5947
7114
  return Promise.all(args.map((arg) => parseTypedArg(meta, keyEntry, arg)));
5948
7115
  }
5949
7116
 
7117
+ // src/commands/rpc.ts
7118
+ init_store();
7119
+ init_output();
7120
+ init_rpc_registry();
7121
+ init_errors();
7122
+ var FAMILY_ORDER = [
7123
+ "system",
7124
+ "chain",
7125
+ "state",
7126
+ "author",
7127
+ "payment",
7128
+ "babe",
7129
+ "grandpa",
7130
+ "beefy",
7131
+ "mmr",
7132
+ "offchain",
7133
+ "dev",
7134
+ "spec",
7135
+ "chainHead",
7136
+ "chainSpec",
7137
+ "transaction",
7138
+ "archive",
7139
+ "other"
7140
+ ];
7141
+ async function getMethodList(chainName, rpcUrl, refresh) {
7142
+ if (!refresh) {
7143
+ const cached = await loadRpcMethods(chainName);
7144
+ if (cached) {
7145
+ return { methods: cached.methods, version: cached.version, fromCache: true };
7146
+ }
7147
+ }
7148
+ const fresh = await fetchRpcMethods(rpcUrl);
7149
+ await saveRpcMethods(chainName, fresh.methods, fresh.version);
7150
+ return { methods: fresh.methods, version: fresh.version, fromCache: false };
7151
+ }
7152
+ function groupByFamily(methods) {
7153
+ const groups = new Map;
7154
+ for (const m of methods) {
7155
+ const family = RPC_REGISTRY[m]?.family ?? inferFamily(m);
7156
+ const list = groups.get(family) ?? [];
7157
+ list.push(m);
7158
+ groups.set(family, list);
7159
+ }
7160
+ for (const list of groups.values())
7161
+ list.sort();
7162
+ return groups;
7163
+ }
7164
+ function formatArgs(info) {
7165
+ if (info.args.length === 0)
7166
+ return "(no args)";
7167
+ return info.args.map((a) => `<${a.name}: ${a.type}${a.optional ? "?" : ""}>`).join(" ");
7168
+ }
7169
+ function printMethodHelp(method, info) {
7170
+ console.log();
7171
+ console.log(`${BOLD}${method}${RESET}`);
7172
+ if (info) {
7173
+ if (info.dangerous) {
7174
+ console.log(` ${YELLOW}⚠️ WRITE / state-changing${RESET}`);
7175
+ }
7176
+ if (info.subscription) {
7177
+ console.log(` ${DIM}subscription — not callable as a one-shot${RESET}`);
7178
+ }
7179
+ console.log(` ${info.description}`);
7180
+ console.log();
7181
+ console.log(` ${DIM}Family:${RESET} ${info.family}`);
7182
+ console.log(` ${DIM}Args:${RESET} ${formatArgs(info)}`);
7183
+ if (info.args.some((a) => a.description)) {
7184
+ console.log();
7185
+ for (const a of info.args) {
7186
+ if (a.description) {
7187
+ console.log(` ${CYAN}${a.name}${RESET} ${DIM}${a.description}${RESET}`);
7188
+ }
7189
+ }
7190
+ }
7191
+ } else {
7192
+ console.log(` ${DIM}No curated metadata. Args are passed through as raw JSON-RPC params.${RESET}`);
7193
+ console.log(` ${DIM}Family:${RESET} ${inferFamily(method)} (inferred)`);
7194
+ }
7195
+ console.log();
7196
+ }
7197
+ async function listMethods(chainName, rpcUrl, opts) {
7198
+ const { methods, version: version2, fromCache } = await getMethodList(chainName, rpcUrl, opts.refresh ?? false);
7199
+ if (isJsonOutput(opts)) {
7200
+ console.log(formatJson({
7201
+ chain: chainName,
7202
+ version: version2,
7203
+ fromCache,
7204
+ methods: methods.map((m) => {
7205
+ const info = RPC_REGISTRY[m];
7206
+ return {
7207
+ method: m,
7208
+ family: info?.family ?? inferFamily(m),
7209
+ description: info?.description,
7210
+ dangerous: info?.dangerous ?? false,
7211
+ subscription: info?.subscription ?? false
7212
+ };
7213
+ })
7214
+ }));
7215
+ return;
7216
+ }
7217
+ printHeading(`RPC methods on ${chainName} (${methods.length})`);
7218
+ const groups = groupByFamily(methods);
7219
+ for (const family of FAMILY_ORDER) {
7220
+ const list = groups.get(family);
7221
+ if (!list || list.length === 0)
7222
+ continue;
7223
+ console.log(`${BOLD}${family}${RESET} ${DIM}(${list.length})${RESET}`);
7224
+ for (const m of list) {
7225
+ const info = RPC_REGISTRY[m];
7226
+ const tags = [];
7227
+ if (info?.dangerous)
7228
+ tags.push(`${YELLOW}⚠ write${RESET}`);
7229
+ if (info?.subscription)
7230
+ tags.push(`${DIM}sub${RESET}`);
7231
+ const suffix = tags.length > 0 ? ` ${tags.join(" ")}` : "";
7232
+ const desc = info?.description ? ` ${DIM}${info.description}${RESET}` : "";
7233
+ console.log(` ${CYAN}${m}${RESET}${suffix}${desc}`);
7234
+ }
7235
+ console.log();
7236
+ }
7237
+ if (!fromCache) {
7238
+ console.log(`${DIM}(fetched from node — cached for next run)${RESET}`);
7239
+ }
7240
+ }
7241
+ async function handleRpc(method, args, opts) {
7242
+ const config = await loadConfig();
7243
+ const { name: chainName, chain: chainConfig } = resolveChain(config, opts.chain);
7244
+ const rpcUrl = opts.rpc ?? chainConfig.rpc;
7245
+ if (!rpcUrl) {
7246
+ throw new CliError(`No RPC endpoint for chain "${chainName}". Pass --rpc or run \`dot chain add ${chainName} --rpc <url>\`.`);
7247
+ }
7248
+ if (!method) {
7249
+ await listMethods(chainName, rpcUrl, opts);
7250
+ return;
7251
+ }
7252
+ const info = RPC_REGISTRY[method];
7253
+ if (opts.help) {
7254
+ printMethodHelp(method, info);
7255
+ return;
7256
+ }
7257
+ const { methods: knownMethods } = await getMethodList(chainName, rpcUrl, opts.refresh ?? false);
7258
+ if (!knownMethods.includes(method)) {
7259
+ const hint = suggestMessage("method", method, knownMethods);
7260
+ throw new CliError(`Method "${method}" is not exposed by the node for "${chainName}".${hint ? ` ${hint}` : ""} ` + `Run \`dot ${chainName}.rpc --refresh\` if the node has been upgraded.`);
7261
+ }
7262
+ if (info?.subscription) {
7263
+ throw new CliError(`"${method}" is a subscription method (requires a follow session) and is not callable as a one-shot. ` + `Use a long-running client for streaming RPC.`);
7264
+ }
7265
+ if (!info && /(_subscribe|_unsubscribe)/.test(method)) {
7266
+ throw new CliError(`"${method}" looks like a subscription/unsubscription method and isn't supported as a one-shot. ` + `Pass --help to see what we know about it.`);
7267
+ }
7268
+ if (info) {
7269
+ const required = info.args.filter((a) => !a.optional).length;
7270
+ if (args.length < required) {
7271
+ throw new CliError(`"${method}" expects at least ${required} argument(s) (${formatArgs(info)}); got ${args.length}.`);
7272
+ }
7273
+ }
7274
+ const params = args.map(parseValue);
7275
+ const result = await rpcRequest(rpcUrl, method, params);
7276
+ printResult(result, isJsonOutput(opts) ? "json" : opts.output);
7277
+ }
7278
+
5950
7279
  // src/commands/sign.ts
5951
7280
  init_accounts();
5952
7281
  init_hash();
@@ -6360,7 +7689,7 @@ async function handleTx(target, args, opts) {
6360
7689
  let estimatedFees;
6361
7690
  let estimationError;
6362
7691
  try {
6363
- estimatedFees = String(await withStalenessSuggestion(chainName, clientHandle, () => tx.getEstimatedFees(signer?.publicKey, txOptions)));
7692
+ estimatedFees = String(await withStalenessSuggestion(chainName, clientHandle, () => withBlockAvailabilityHint(opts.at, () => tx.getEstimatedFees(signer?.publicKey, txOptions))));
6364
7693
  } catch (err) {
6365
7694
  estimationError = err instanceof Error ? err.message : String(err);
6366
7695
  }
@@ -6430,7 +7759,7 @@ async function handleTx(target, args, opts) {
6430
7759
  }
6431
7760
  const observable = clientHandle.client.submitAndWatch(generalTx, at);
6432
7761
  if (isJsonOutput(opts)) {
6433
- const result3 = await withStalenessSuggestion(chainName, clientHandle, () => watchTransactionJson(observable, waitLevel, { unsigned: true }));
7762
+ const result3 = await withStalenessSuggestion(chainName, clientHandle, () => withBlockAvailabilityHint(opts.at, () => watchTransactionJson(observable, waitLevel, { unsigned: true })));
6434
7763
  const rpcUrl3 = primaryRpc(opts.rpc ?? chainConfig.rpc);
6435
7764
  if (result3.type === "broadcasted") {
6436
7765
  printJsonLine({ event: "broadcasted", txHash: result3.txHash });
@@ -6462,7 +7791,7 @@ async function handleTx(target, args, opts) {
6462
7791
  }
6463
7792
  return;
6464
7793
  }
6465
- const result2 = await withStalenessSuggestion(chainName, clientHandle, () => watchTransaction(observable, waitLevel, { unsigned: true }));
7794
+ const result2 = await withStalenessSuggestion(chainName, clientHandle, () => withBlockAvailabilityHint(opts.at, () => watchTransaction(observable, waitLevel, { unsigned: true })));
6466
7795
  console.log();
6467
7796
  console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
6468
7797
  console.log(` ${BOLD}Type:${RESET} unsigned (bare)`);
@@ -6511,7 +7840,7 @@ async function handleTx(target, args, opts) {
6511
7840
  return;
6512
7841
  }
6513
7842
  if (isJsonOutput(opts)) {
6514
- const result2 = await withStalenessSuggestion(chainName, clientHandle, () => watchTransactionJson(tx.signSubmitAndWatch(signer, txOptions), waitLevel));
7843
+ const result2 = await withStalenessSuggestion(chainName, clientHandle, () => withBlockAvailabilityHint(opts.at, () => watchTransactionJson(tx.signSubmitAndWatch(signer, txOptions), waitLevel)));
6515
7844
  const rpcUrl2 = primaryRpc(opts.rpc ?? chainConfig.rpc);
6516
7845
  if (result2.type === "broadcasted") {
6517
7846
  printJsonLine({ event: "broadcasted", txHash: result2.txHash });
@@ -6542,7 +7871,7 @@ async function handleTx(target, args, opts) {
6542
7871
  }
6543
7872
  return;
6544
7873
  }
6545
- const result = await withStalenessSuggestion(chainName, clientHandle, () => watchTransaction(tx.signSubmitAndWatch(signer, txOptions), waitLevel));
7874
+ const result = await withStalenessSuggestion(chainName, clientHandle, () => withBlockAvailabilityHint(opts.at, () => watchTransaction(tx.signSubmitAndWatch(signer, txOptions), waitLevel)));
6546
7875
  console.log();
6547
7876
  console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
6548
7877
  console.log(` ${BOLD}Call:${RESET} ${callHex}`);
@@ -7946,7 +9275,8 @@ var CATEGORY_ALIASES = {
7946
9275
  api: "apis",
7947
9276
  extensions: "extensions",
7948
9277
  extension: "extensions",
7949
- ext: "extensions"
9278
+ ext: "extensions",
9279
+ rpc: "rpc"
7950
9280
  };
7951
9281
  function matchCategory(segment) {
7952
9282
  return CATEGORY_ALIASES[segment.toLowerCase()];
@@ -7961,7 +9291,7 @@ function parseDotPath(input, knownChains = []) {
7961
9291
  const cat = matchCategory(parts[0]);
7962
9292
  if (cat)
7963
9293
  return { category: cat };
7964
- throw new Error(`Unknown command "${parts[0]}". Expected a category (query, tx, const, events, errors, apis, extensions) or a named command.`);
9294
+ throw new Error(`Unknown command "${parts[0]}". Expected a category (query, tx, const, events, errors, apis, extensions, rpc) or a named command.`);
7965
9295
  }
7966
9296
  case 2: {
7967
9297
  const cat = matchCategory(parts[0]);
@@ -8016,7 +9346,15 @@ if (process.argv[2] === "__complete") {
8016
9346
  process.exit(0);
8017
9347
  })();
8018
9348
  } else {
8019
- let collectVarFlags = function(argv) {
9349
+ let readAtFromArgv = function(argv) {
9350
+ for (let i = 0;i < argv.length; i++) {
9351
+ if (argv[i] === "--at" && i + 1 < argv.length)
9352
+ return argv[i + 1];
9353
+ if (argv[i].startsWith("--at="))
9354
+ return argv[i].slice(5);
9355
+ }
9356
+ return;
9357
+ }, collectVarFlags = function(argv) {
8020
9358
  const vars = [];
8021
9359
  for (let i = 0;i < argv.length; i++) {
8022
9360
  if (argv[i] === "--var" && i + 1 < argv.length) {
@@ -8043,6 +9381,7 @@ if (process.argv[2] === "__complete") {
8043
9381
  console.log(" errors List or inspect pallet errors");
8044
9382
  console.log(" apis Browse and call runtime APIs");
8045
9383
  console.log(" extensions List transaction extensions on a chain");
9384
+ console.log(" rpc Call raw JSON-RPC methods on a node");
8046
9385
  console.log();
8047
9386
  console.log("Examples:");
8048
9387
  console.log(" dot polkadot.query.System.Account <addr> Query a storage item");
@@ -8055,6 +9394,8 @@ if (process.argv[2] === "__complete") {
8055
9394
  console.log(" dot metadata polkadot Dump runtime metadata as JSON");
8056
9395
  console.log(" dot polkadot.extensions List transaction extensions");
8057
9396
  console.log(" dot polkadot.extensions.CheckMortality Inspect one extension");
9397
+ console.log(" dot polkadot.rpc List RPC methods on the node");
9398
+ console.log(" dot polkadot.rpc.system_health Call a JSON-RPC method");
8058
9399
  console.log(" dot query.System.Number --chain polkadot --chain flag form");
8059
9400
  console.log(" dot ./transfer.yaml --from alice Run from file (chain in YAML)");
8060
9401
  console.log(" dot tx.0x1f0003... --to-yaml --chain polkadot Decode hex call to YAML");
@@ -8068,7 +9409,7 @@ if (process.argv[2] === "__complete") {
8068
9409
  console.log(" account Manage accounts");
8069
9410
  console.log(" hash Hash utilities");
8070
9411
  console.log(" sign Sign a message with an account keypair");
8071
- console.log(" parachain Derive parachain sovereign accounts");
9412
+ console.log(" parachain Derive parachain sovereign accounts (deprecated \u2014 use `account inspect --parachain`)");
8072
9413
  console.log(" verifiable Derive Bandersnatch member key from mnemonic");
8073
9414
  console.log(" completions <sh> Generate shell completions (zsh, bash, fish)");
8074
9415
  console.log();
@@ -8099,11 +9440,12 @@ if (process.argv[2] === "__complete") {
8099
9440
  registerVerifiableCommands(cli);
8100
9441
  cli.command("[dotpath] [...args]").option("--from <name>", "Account to sign with (for tx)").option("--dry-run", "Estimate fees without submitting (for tx)").option("--encode", "Encode call to hex without signing (for tx)").option("--to-yaml", "Decode call to YAML file format (for tx)").option("--to-json", "Decode call to JSON file format (for tx)").option("--ext <json>", "Custom signed extension values as JSON (for tx)").option("--asset <json>", "Pay fees in an alternative asset (XCM location JSON, for tx)").option("-w, --wait <level>", "Resolve at: broadcast, best-block (or best), finalized (for tx)", {
8101
9442
  default: "finalized"
8102
- }).option("--dump", "Dump all entries of a storage map (without specifying a key)").option("--var <kv>", "Template variable for file input (KEY=VALUE, repeatable)").option("--nonce <n>", "Custom nonce for manual tx sequencing (for tx)").option("--tip <amount>", "Tip to prioritize transaction (for tx)").option("--mortality <spec>", '"immortal" or period number (for tx)').option("--at <block>", 'Block hash, "best", or "finalized" to validate against (for tx)').option("--unsigned", "Submit as unsigned/bare transaction (no signer required, for tx)").action(async (dotpath, args, opts) => {
9443
+ }).option("--dump", "Dump all entries of a storage map (without specifying a key)").option("--var <kv>", "Template variable for file input (KEY=VALUE, repeatable)").option("--nonce <n>", "Custom nonce for manual tx sequencing (for tx)").option("--tip <amount>", "Tip to prioritize transaction (for tx)").option("--mortality <spec>", '"immortal" or period number (for tx)').option("--at <block>", 'Block hash, "best", or "finalized" to read/validate against (tx, query, apis)').option("--unsigned", "Submit as unsigned/bare transaction (no signer required, for tx)").option("--refresh", "Refresh the cached RPC method list from the node (for rpc)").action(async (dotpath, args, opts) => {
8103
9444
  if (!dotpath) {
8104
9445
  printHelp();
8105
9446
  return;
8106
9447
  }
9448
+ const atRaw = readAtFromArgv(process.argv);
8107
9449
  if (isFilePath(dotpath)) {
8108
9450
  const cliVars = collectVarFlags(process.argv);
8109
9451
  const cmd = await loadCommandFile(dotpath, cliVars);
@@ -8131,7 +9473,7 @@ if (process.argv[2] === "__complete") {
8131
9473
  nonce: opts.nonce,
8132
9474
  tip: opts.tip,
8133
9475
  mortality: opts.mortality,
8134
- at: opts.at,
9476
+ at: atRaw,
8135
9477
  parsedArgs: cmd.args
8136
9478
  });
8137
9479
  break;
@@ -8139,6 +9481,7 @@ if (process.argv[2] === "__complete") {
8139
9481
  await handleQuery(target2, args, {
8140
9482
  ...handlerOpts2,
8141
9483
  dump: opts.dump,
9484
+ at: atRaw,
8142
9485
  parsedArgs: cmd.args
8143
9486
  });
8144
9487
  break;
@@ -8148,6 +9491,7 @@ if (process.argv[2] === "__complete") {
8148
9491
  case "apis":
8149
9492
  await handleApis2(target2, args, {
8150
9493
  ...handlerOpts2,
9494
+ at: atRaw,
8151
9495
  parsedArgs: cmd.args
8152
9496
  });
8153
9497
  break;
@@ -8162,9 +9506,10 @@ if (process.argv[2] === "__complete") {
8162
9506
  } catch {
8163
9507
  throw new CliError2(`Unknown command "${dotpath}". Run "dot --help" for available commands.`);
8164
9508
  }
9509
+ const isFlatCategory = parsed.category === "rpc" || parsed.category === "extensions";
8165
9510
  if (!parsed.pallet && args.length > 0) {
8166
9511
  parsed.pallet = args.shift();
8167
- if (!parsed.item && args.length > 0) {
9512
+ if (!isFlatCategory && !parsed.item && args.length > 0) {
8168
9513
  parsed.item = args.shift();
8169
9514
  }
8170
9515
  }
@@ -8185,7 +9530,7 @@ if (process.argv[2] === "__complete") {
8185
9530
  }
8186
9531
  switch (parsed.category) {
8187
9532
  case "query":
8188
- await handleQuery(target, args, { ...handlerOpts, dump: opts.dump });
9533
+ await handleQuery(target, args, { ...handlerOpts, dump: opts.dump, at: atRaw });
8189
9534
  break;
8190
9535
  case "tx": {
8191
9536
  const txOpts = {
@@ -8202,7 +9547,7 @@ if (process.argv[2] === "__complete") {
8202
9547
  nonce: opts.nonce,
8203
9548
  tip: opts.tip,
8204
9549
  mortality: opts.mortality,
8205
- at: opts.at
9550
+ at: atRaw
8206
9551
  };
8207
9552
  if (parsed.pallet && /^0x[0-9a-fA-F]+$/.test(parsed.pallet)) {
8208
9553
  await handleTx(parsed.pallet, args, txOpts);
@@ -8221,7 +9566,7 @@ if (process.argv[2] === "__complete") {
8221
9566
  await handleErrors2(target, handlerOpts);
8222
9567
  break;
8223
9568
  case "apis":
8224
- await handleApis2(target, args, handlerOpts);
9569
+ await handleApis2(target, args, { ...handlerOpts, at: atRaw });
8225
9570
  break;
8226
9571
  case "extensions": {
8227
9572
  if (parsed.item) {
@@ -8231,6 +9576,18 @@ if (process.argv[2] === "__complete") {
8231
9576
  await handleExtensions(parsed.pallet, handlerOpts);
8232
9577
  break;
8233
9578
  }
9579
+ case "rpc": {
9580
+ if (parsed.item) {
9581
+ const suggestion = parsed.chain ? `dot ${parsed.chain}.rpc.${parsed.pallet}` : opts.chain ? `dot rpc.${parsed.pallet} --chain ${opts.chain}` : `dot rpc.${parsed.pallet} --chain <chain>`;
9582
+ throw new CliError2(`RPC methods have no sub-items. Try "${suggestion}".`);
9583
+ }
9584
+ await handleRpc(parsed.pallet, args, {
9585
+ ...handlerOpts,
9586
+ help: cli.options.help,
9587
+ refresh: opts.refresh
9588
+ });
9589
+ break;
9590
+ }
8234
9591
  }
8235
9592
  });
8236
9593
  cli.option("--help, -h", "Display this message");