polkadot-cli 1.18.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 +81 -3
  2. package/dist/cli.mjs +260 -28
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -373,6 +373,11 @@ dot account alice # shorthand — unknown subcommands fall th
373
373
 
374
374
  dot account inspect 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
375
375
  dot account inspect 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
376
+
377
+ # pallet-revive H160 — the 20-byte address shape from EVM tooling. Same
378
+ # input slot as SS58 / hex pubkey; resolved to the deterministic fallback
379
+ # AccountId32 (`H160 || 0xEE * 12`).
380
+ dot account inspect 0x9621DDe636dE098B43Efb0fA9b61fAcFE328F99D
376
381
  ```
377
382
 
378
383
  Use `--prefix` to encode the SS58 address with a specific network prefix (default: 42):
@@ -390,6 +395,7 @@ dot account inspect alice --json
390
395
  # {
391
396
  # "publicKey": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
392
397
  # "ss58": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
398
+ # "h160": "0x9621DDe636dE098B43Efb0fA9b61fAcFE328F99D",
393
399
  # "prefix": 42,
394
400
  # "name": "Alice",
395
401
  # "kind": "dev"
@@ -407,10 +413,38 @@ dot account inspect alice
407
413
  # Kind: dev
408
414
  # Public Key: 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
409
415
  # SS58: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
416
+ # H160: 0x9621DDe636dE098B43Efb0fA9b61fAcFE328F99D
417
+ # Prefix: 42
418
+ ```
419
+
420
+ The `Kind:` line categorises the account: `dev` (built-in), `signer` (has a secret/env), `watch-only` (raw external address), `pallet sovereign` (derived from a `PalletId`), `parachain sovereign (child|sibling)` (derived from a parachain ID), or `revive H160 fallback` (a 20-byte input resolved to its deterministic Substrate AccountId32). For derived sovereigns, an extra `Source:` line shows what the address was derived from. For env-backed signers, an `Env:` line shows the variable; for derived child keys, `Derivation:` shows the path.
421
+
422
+ ##### pallet-revive H160 address
423
+
424
+ Every Substrate account has a corresponding 20-byte H160 address under [pallet-revive](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/revive) (the new EVM-compatible smart-contracts pallet on Polkadot Hub / Asset Hub). `dot account inspect` always shows it, EIP-55 checksummed, on the `H160:` line. The mapping is offline and prefix-independent:
425
+
426
+ - **AccountId32 → H160:** if the last 12 bytes are `0xEE`, strip them (the account originated from an Eth address); otherwise `keccak256(accountId32)` and take the last 20 bytes.
427
+ - **H160 → AccountId32:** deterministic fallback is `H160 || 0xEE * 12`. (The full mapping after a successful `pallet_revive.map_account` extrinsic lives in on-chain `AddressSuffix` storage and isn't recoverable offline — that's a chain-state lookup.)
428
+
429
+ Pass a 20-byte hex value as the inspect input to resolve it back to its fallback Substrate account:
430
+
431
+ ```bash
432
+ dot account inspect 0x9621DDe636dE098B43Efb0fA9b61fAcFE328F99D
433
+ # Output:
434
+ # Account Info
435
+ #
436
+ # Kind: revive H160 fallback
437
+ # Public Key: 0x9621dde636de098b43efb0fa9b61facfe328f99deeeeeeeeeeeeeeeeeeeeeeee
438
+ # SS58: 5FTZ6n1wY3GBqEZ2DWEdspbTarvRnp8DM8x2YXbWubu7JN98
439
+ # H160: 0x9621DDe636dE098B43Efb0fA9b61fAcFE328F99D
410
440
  # Prefix: 42
441
+
442
+ # Script-friendly: just the H160 for a given account
443
+ dot account inspect alice --json | jq -r .h160
444
+ # 0x9621DDe636dE098B43Efb0fA9b61fAcFE328F99D
411
445
  ```
412
446
 
413
- The `Kind:` line categorises the account: `dev` (built-in), `signer` (has a secret/env), `watch-only` (raw external address), `pallet sovereign` (derived from a `PalletId`), or `parachain sovereign (child|sibling)` (derived from a parachain ID). For derived sovereigns, an extra `Source:` line shows what the address was derived from. For env-backed signers, an `Env:` line shows the variable; for derived child keys, `Derivation:` shows the path.
447
+ Note: `dot` implements the current `pallet-revive` master variant (keccak fallback). Older `stable2412` runtimes used plain `accountId32[..20]` truncation; if you target one, compute it manually until a `--revive-truncate` flag lands.
414
448
 
415
449
  ##### Stateless sovereign derivation (script-friendly)
416
450
 
@@ -659,6 +693,40 @@ dot polkadot-asset-hub.query.Assets.Metadata 1984
659
693
  # }
660
694
  ```
661
695
 
696
+ #### Historical reads — `--at <block>`
697
+
698
+ Storage queries default to the latest finalized head. Pass `--at` to read
699
+ state at a specific block hash, the chain head (`best`), or `finalized`
700
+ (explicit). Accepted on both `query.*` and `apis.*` runtime calls.
701
+
702
+ ```bash
703
+ # Read at the current best (non-finalized) head — useful for low-latency reads
704
+ dot polkadot.query.System.Number --at best
705
+
706
+ # Read at the last finalized block — same as the default, but explicit
707
+ dot polkadot.query.System.Number --at finalized
708
+
709
+ # Pin a finalized hash and read multiple items at that exact block
710
+ HASH=$(dot polkadot.rpc.chain_getFinalizedHead | tr -d '"')
711
+ dot polkadot.query.System.Number --at "$HASH"
712
+ dot polkadot.apis.Core.version --at "$HASH" --json | jq .spec_version
713
+ ```
714
+
715
+ `--at` accepts a 32-byte `0x…` block hash, `"best"`, or `"finalized"`.
716
+ Anything else errors before any network call. Tx submission rejects `"best"`.
717
+
718
+ > **Archive-only blocks**: papi v2 talks to the `chainHead_v1_*` JSON-RPC API,
719
+ > which only serves *pinned* (recent) blocks. Querying a hash older than a
720
+ > few minutes against a non-archive node fails with a clean error that
721
+ > includes a copy-pasteable `--rpc wss://<archive-endpoint>` hint:
722
+ >
723
+ > ```
724
+ > ⚠ 0x… is not available on the current RPC endpoint.
725
+ > Public nodes serve only recent (pinned) blocks via chainHead_v1_*.
726
+ > For deep historical reads, point --rpc at an archive endpoint, e.g.:
727
+ > dot ... --at 0x… --rpc wss://<archive-endpoint>
728
+ > ```
729
+
662
730
  ### Look up constants
663
731
 
664
732
  ```bash
@@ -1340,7 +1408,7 @@ Override low-level transaction parameters. Useful for rapid-fire submission (cus
1340
1408
  | `--nonce <n>` | non-negative integer | Override the auto-detected nonce |
1341
1409
  | `--tip <amount>` | non-negative integer (planck) | Priority tip for the transaction pool |
1342
1410
  | `--mortality <spec>` | `immortal` or period (min 4) | Transaction mortality window |
1343
- | `--at <block>` | 0x-prefixed block hash | Block hash to validate against (defaults to finalized) |
1411
+ | `--at <block>` | 0x-prefixed block hash, `"best"`, or `"finalized"` | Block to read/validate against (defaults to finalized). Also honored on `query.*` and `apis.*` for historical reads; tx submission rejects `"best"`. |
1344
1412
 
1345
1413
  ```bash
1346
1414
  # Fire-and-forget: submit two txs in rapid succession with manual nonces
@@ -1626,7 +1694,7 @@ All existing flags work with file input — `--chain` overrides the file's `chai
1626
1694
 
1627
1695
  ### Compute hashes
1628
1696
 
1629
- Compute cryptographic hashes commonly used in Substrate. Supports BLAKE2b-256, BLAKE2b-128, Keccak-256, and SHA-256.
1697
+ Compute cryptographic hashes commonly used in Substrate. Supports BLAKE2b-256, BLAKE2b-128, Keccak-256, SHA-256, and the XXH64-based `twox64` / `twox128` / `twox256` family used to build Substrate storage keys.
1630
1698
 
1631
1699
  ```bash
1632
1700
  # Hash hex-encoded data
@@ -1655,6 +1723,16 @@ dot hash blake2b256 0xdeadbeef --json
1655
1723
  # "input": "0xdeadbeef",
1656
1724
  # "hash": "0xf3e925002fed7cc0ded46842569eb5c90c910c091d8d04a1bdf96e0db719fd91"
1657
1725
  # }
1726
+
1727
+ # Substrate twox128 — pallet/storage prefix used everywhere in Substrate state
1728
+ dot hash twox128 System
1729
+ # Output:
1730
+ # 0x26aa394eea5630e07c48ae0c9558cef7
1731
+
1732
+ # Build a full storage key for `System.Number` and read it raw via JSON-RPC
1733
+ PALLET=$(dot hash twox128 System)
1734
+ ITEM=$(dot hash twox128 Number)
1735
+ dot polkadot.rpc.state_getStorage "${PALLET}${ITEM:2}"
1658
1736
  ```
1659
1737
 
1660
1738
  Run `dot hash` with no arguments to see all available algorithms.
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
@@ -1253,7 +1264,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
1253
1264
  let bytes;
1254
1265
  try {
1255
1266
  const hex = await withTimeout(client._request("state_call", ["Metadata_metadata_at_version", v15Arg]), chainName);
1256
- const raw = hexToBytes(hex);
1267
+ const raw = hexToBytes2(hex);
1257
1268
  const decoded = optionalOpaqueBytes.dec(raw);
1258
1269
  if (decoded !== undefined) {
1259
1270
  bytes = new Uint8Array(decoded);
@@ -1262,7 +1273,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
1262
1273
  if (!bytes) {
1263
1274
  try {
1264
1275
  const hex = await withTimeout(client._request("state_getMetadata", []), chainName);
1265
- bytes = hexToBytes(hex);
1276
+ bytes = hexToBytes2(hex);
1266
1277
  } catch (err) {
1267
1278
  if (err instanceof ConnectionError)
1268
1279
  throw err;
@@ -1311,6 +1322,24 @@ async function withStalenessSuggestion(chainName, clientHandle, task) {
1311
1322
  ` + ` Run: dot chain update ${chainName}`);
1312
1323
  }
1313
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
+ }
1314
1343
  async function getOrFetchMetadata(chainName, clientHandle) {
1315
1344
  let raw = await loadMetadata(chainName);
1316
1345
  if (!raw) {
@@ -1440,7 +1469,7 @@ function describeCallArgs(meta, palletName, callName) {
1440
1469
  function describeEventFields(meta, palletName, eventName) {
1441
1470
  return compactArgsString(getEventFields(meta, palletName, eventName));
1442
1471
  }
1443
- function hexToBytes(hex) {
1472
+ function hexToBytes2(hex) {
1444
1473
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
1445
1474
  const bytes = new Uint8Array(clean.length / 2);
1446
1475
  for (let i = 0;i < clean.length; i += 2) {
@@ -1535,6 +1564,15 @@ import { compact as scaleCompact } from "@polkadot-api/substrate-bindings";
1535
1564
  import { getViewBuilder } from "@polkadot-api/view-builder";
1536
1565
  import { Binary as Binary2 } from "polkadot-api";
1537
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
+ }
1538
1576
  async function parseStructArgs(meta, fields, args, callLabel) {
1539
1577
  const fieldNames = Object.keys(fields);
1540
1578
  if (args.length !== fieldNames.length) {
@@ -1943,8 +1981,10 @@ async function handleApis(target, args, opts) {
1943
1981
  typeof opts.parsedArgs === "object" ? JSON.stringify(opts.parsedArgs) : String(opts.parsedArgs)
1944
1982
  ];
1945
1983
  const parsedArgs = await parseRuntimeApiArgs(meta, method, effectiveArgs);
1984
+ const atArg = parseAtForRead(opts.at);
1985
+ const pullOpts = atArg !== undefined ? [{ at: atArg }] : [];
1946
1986
  const unsafeApi = clientHandle.client.getUnsafeApi();
1947
- 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));
1948
1988
  const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
1949
1989
  printResult(result, format);
1950
1990
  } finally {
@@ -3067,11 +3107,98 @@ var init_rpc_registry = __esm(() => {
3067
3107
  };
3068
3108
  });
3069
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
+
3070
3197
  // src/core/hash.ts
3071
3198
  import { blake2b as blake2b2 } from "@noble/hashes/blake2.js";
3072
3199
  import { sha256 } from "@noble/hashes/sha2.js";
3073
- import { keccak_256 } from "@noble/hashes/sha3.js";
3074
- 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";
3075
3202
  function computeHash(algorithm, data) {
3076
3203
  const algo = ALGORITHMS[algorithm];
3077
3204
  if (!algo) {
@@ -3085,12 +3212,12 @@ function parseInputData(input) {
3085
3212
  if (hex.length % 2 !== 0) {
3086
3213
  throw new Error(`Invalid hex input: odd number of characters`);
3087
3214
  }
3088
- return hexToBytes2(hex);
3215
+ return hexToBytes3(hex);
3089
3216
  }
3090
3217
  return new TextEncoder().encode(input);
3091
3218
  }
3092
3219
  function toHex2(bytes) {
3093
- return `0x${bytesToHex2(bytes)}`;
3220
+ return `0x${bytesToHex3(bytes)}`;
3094
3221
  }
3095
3222
  function isValidAlgorithm(name) {
3096
3223
  return name in ALGORITHMS;
@@ -3100,6 +3227,7 @@ function getAlgorithmNames() {
3100
3227
  }
3101
3228
  var ALGORITHMS;
3102
3229
  var init_hash = __esm(() => {
3230
+ init_xxh64();
3103
3231
  ALGORITHMS = {
3104
3232
  blake2b256: {
3105
3233
  compute: (data) => blake2b2(data, { dkLen: 32 }),
@@ -3112,7 +3240,7 @@ var init_hash = __esm(() => {
3112
3240
  description: "BLAKE2b with 128-bit output"
3113
3241
  },
3114
3242
  keccak256: {
3115
- compute: (data) => keccak_256(data),
3243
+ compute: (data) => keccak_2562(data),
3116
3244
  outputLen: 32,
3117
3245
  description: "Keccak-256 (Ethereum-compatible)"
3118
3246
  },
@@ -3120,6 +3248,21 @@ var init_hash = __esm(() => {
3120
3248
  compute: (data) => sha256(data),
3121
3249
  outputLen: 32,
3122
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)"
3123
3266
  }
3124
3267
  };
3125
3268
  });
@@ -3517,13 +3660,73 @@ var init_complete = __esm(() => {
3517
3660
  // src/cli.ts
3518
3661
  import cac from "cac";
3519
3662
  // package.json
3520
- var version = "1.18.0";
3663
+ var version = "1.19.0";
3521
3664
 
3522
3665
  // src/commands/account.ts
3523
3666
  init_accounts_store();
3524
3667
  init_accounts();
3525
- init_output();
3526
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();
3527
3730
 
3528
3731
  // src/core/pallet.ts
3529
3732
  init_errors();
@@ -4226,6 +4429,7 @@ async function accountInspect(input, opts) {
4226
4429
  let hasSecret = false;
4227
4430
  let storedAccount;
4228
4431
  let isDev = false;
4432
+ let isH160Fallback = false;
4229
4433
  let virtualSource;
4230
4434
  if (sovereignSource) {
4231
4435
  if (sovereignSource.kind === "pallet") {
@@ -4273,17 +4477,22 @@ async function accountInspect(input, opts) {
4273
4477
  }
4274
4478
  } else if (isHexPublicKey(input)) {
4275
4479
  publicKeyHex = input;
4480
+ } else if (isH160Hex(input)) {
4481
+ const fallback = h160ToFallbackAccountId(h160FromHex(input));
4482
+ publicKeyHex = publicKeyToHex(fallback);
4483
+ isH160Fallback = true;
4276
4484
  } else {
4277
4485
  try {
4278
4486
  const decoded = fromSs58(input);
4279
4487
  publicKeyHex = publicKeyToHex(decoded);
4280
4488
  } catch {
4281
- 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.`);
4282
4490
  process.exit(1);
4283
4491
  }
4284
4492
  }
4285
4493
  }
4286
4494
  const ss58 = toSs58(publicKeyHex, prefix);
4495
+ const h160Hex = toEip55(accountIdToH160(nobleHexToBytes(publicKeyHex.slice(2))));
4287
4496
  let privateKeyHex;
4288
4497
  if (opts.showSecret) {
4289
4498
  if (!name) {
@@ -4314,6 +4523,8 @@ async function accountInspect(input, opts) {
4314
4523
  sourceLine = `parachain ${virtualSource.paraId}`;
4315
4524
  } else if (isDev) {
4316
4525
  kindLabel = "dev";
4526
+ } else if (isH160Fallback) {
4527
+ kindLabel = "revive H160 fallback";
4317
4528
  } else if (storedAccount) {
4318
4529
  const k = classifyAccount(storedAccount);
4319
4530
  if (k === "pallet" && storedAccount.source?.kind === "pallet") {
@@ -4335,7 +4546,12 @@ async function accountInspect(input, opts) {
4335
4546
  }
4336
4547
  }
4337
4548
  if (isJsonOutput(opts)) {
4338
- const result = { publicKey: publicKeyHex, ss58, prefix };
4549
+ const result = {
4550
+ publicKey: publicKeyHex,
4551
+ ss58,
4552
+ h160: h160Hex,
4553
+ prefix
4554
+ };
4339
4555
  if (name)
4340
4556
  result.name = name;
4341
4557
  if (kindLabel)
@@ -4382,6 +4598,7 @@ async function accountInspect(input, opts) {
4382
4598
  console.log(` ${BOLD}Kind:${RESET} ${kindLabel}`);
4383
4599
  console.log(` ${BOLD}Public Key:${RESET} ${publicKeyHex}`);
4384
4600
  console.log(` ${BOLD}SS58:${RESET} ${ss58}`);
4601
+ console.log(` ${BOLD}H160:${RESET} ${h160Hex}`);
4385
4602
  if (sourceLine)
4386
4603
  console.log(` ${BOLD}Source:${RESET} ${sourceLine}`);
4387
4604
  if (derivationLine)
@@ -4655,8 +4872,10 @@ async function handleApis2(target, args, opts) {
4655
4872
  typeof opts.parsedArgs === "object" ? JSON.stringify(opts.parsedArgs) : String(opts.parsedArgs)
4656
4873
  ];
4657
4874
  const parsedArgs = await parseRuntimeApiArgs2(meta, method, effectiveArgs);
4875
+ const atArg = parseAtForRead(opts.at);
4876
+ const pullOpts = atArg !== undefined ? [{ at: atArg }] : [];
4658
4877
  const unsafeApi = clientHandle.client.getUnsafeApi();
4659
- 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));
4660
4879
  const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
4661
4880
  printResult(result, format);
4662
4881
  } finally {
@@ -6826,6 +7045,8 @@ async function handleQuery(target, keys, opts) {
6826
7045
  typeof opts.parsedArgs === "object" ? JSON.stringify(opts.parsedArgs) : String(opts.parsedArgs)
6827
7046
  ];
6828
7047
  const parsedKeys = await parseStorageKeys(meta, palletInfo.name, storageItem, effectiveKeys);
7048
+ const atArg = parseAtForRead(opts.at);
7049
+ const pullOpts = atArg !== undefined ? [{ at: atArg }] : [];
6829
7050
  const format = isJsonOutput(opts) ? "json" : opts.output ?? "pretty";
6830
7051
  const expectedLen = storageItem.type === "map" && storageItem.keyTypeId != null ? meta.builder.buildStorage(palletInfo.name, storageItem.name).len : 0;
6831
7052
  if (storageItem.type === "map" && parsedKeys.length < expectedLen) {
@@ -6835,7 +7056,7 @@ async function handleQuery(target, keys, opts) {
6835
7056
  console.log(`${DIM}Hint: use --dump to fetch all entries${RESET}`);
6836
7057
  return;
6837
7058
  }
6838
- const entries = await withStalenessSuggestion(chainName, clientHandle, () => storageApi.getEntries(...parsedKeys));
7059
+ const entries = await withStalenessSuggestion(chainName, clientHandle, () => withBlockAvailabilityHint(opts.at, () => storageApi.getEntries(...parsedKeys, ...pullOpts)));
6839
7060
  const rows = entries.map((e) => ({
6840
7061
  keys: e.keyArgs,
6841
7062
  value: e.value
@@ -6844,7 +7065,7 @@ async function handleQuery(target, keys, opts) {
6844
7065
  await writeStdout(`${text}
6845
7066
  `);
6846
7067
  } else {
6847
- const result = await withStalenessSuggestion(chainName, clientHandle, () => storageApi.getValue(...parsedKeys));
7068
+ const result = await withStalenessSuggestion(chainName, clientHandle, () => withBlockAvailabilityHint(opts.at, () => storageApi.getValue(...parsedKeys, ...pullOpts)));
6848
7069
  const text = format === "json" ? formatJson(result) : formatPretty(result);
6849
7070
  await writeStdout(`${text}
6850
7071
  `);
@@ -7468,7 +7689,7 @@ async function handleTx(target, args, opts) {
7468
7689
  let estimatedFees;
7469
7690
  let estimationError;
7470
7691
  try {
7471
- 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))));
7472
7693
  } catch (err) {
7473
7694
  estimationError = err instanceof Error ? err.message : String(err);
7474
7695
  }
@@ -7538,7 +7759,7 @@ async function handleTx(target, args, opts) {
7538
7759
  }
7539
7760
  const observable = clientHandle.client.submitAndWatch(generalTx, at);
7540
7761
  if (isJsonOutput(opts)) {
7541
- 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 })));
7542
7763
  const rpcUrl3 = primaryRpc(opts.rpc ?? chainConfig.rpc);
7543
7764
  if (result3.type === "broadcasted") {
7544
7765
  printJsonLine({ event: "broadcasted", txHash: result3.txHash });
@@ -7570,7 +7791,7 @@ async function handleTx(target, args, opts) {
7570
7791
  }
7571
7792
  return;
7572
7793
  }
7573
- 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 })));
7574
7795
  console.log();
7575
7796
  console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
7576
7797
  console.log(` ${BOLD}Type:${RESET} unsigned (bare)`);
@@ -7619,7 +7840,7 @@ async function handleTx(target, args, opts) {
7619
7840
  return;
7620
7841
  }
7621
7842
  if (isJsonOutput(opts)) {
7622
- 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)));
7623
7844
  const rpcUrl2 = primaryRpc(opts.rpc ?? chainConfig.rpc);
7624
7845
  if (result2.type === "broadcasted") {
7625
7846
  printJsonLine({ event: "broadcasted", txHash: result2.txHash });
@@ -7650,7 +7871,7 @@ async function handleTx(target, args, opts) {
7650
7871
  }
7651
7872
  return;
7652
7873
  }
7653
- 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)));
7654
7875
  console.log();
7655
7876
  console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
7656
7877
  console.log(` ${BOLD}Call:${RESET} ${callHex}`);
@@ -9125,7 +9346,15 @@ if (process.argv[2] === "__complete") {
9125
9346
  process.exit(0);
9126
9347
  })();
9127
9348
  } else {
9128
- 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) {
9129
9358
  const vars = [];
9130
9359
  for (let i = 0;i < argv.length; i++) {
9131
9360
  if (argv[i] === "--var" && i + 1 < argv.length) {
@@ -9211,11 +9440,12 @@ if (process.argv[2] === "__complete") {
9211
9440
  registerVerifiableCommands(cli);
9212
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)", {
9213
9442
  default: "finalized"
9214
- }).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)").option("--refresh", "Refresh the cached RPC method list from the node (for rpc)").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) => {
9215
9444
  if (!dotpath) {
9216
9445
  printHelp();
9217
9446
  return;
9218
9447
  }
9448
+ const atRaw = readAtFromArgv(process.argv);
9219
9449
  if (isFilePath(dotpath)) {
9220
9450
  const cliVars = collectVarFlags(process.argv);
9221
9451
  const cmd = await loadCommandFile(dotpath, cliVars);
@@ -9243,7 +9473,7 @@ if (process.argv[2] === "__complete") {
9243
9473
  nonce: opts.nonce,
9244
9474
  tip: opts.tip,
9245
9475
  mortality: opts.mortality,
9246
- at: opts.at,
9476
+ at: atRaw,
9247
9477
  parsedArgs: cmd.args
9248
9478
  });
9249
9479
  break;
@@ -9251,6 +9481,7 @@ if (process.argv[2] === "__complete") {
9251
9481
  await handleQuery(target2, args, {
9252
9482
  ...handlerOpts2,
9253
9483
  dump: opts.dump,
9484
+ at: atRaw,
9254
9485
  parsedArgs: cmd.args
9255
9486
  });
9256
9487
  break;
@@ -9260,6 +9491,7 @@ if (process.argv[2] === "__complete") {
9260
9491
  case "apis":
9261
9492
  await handleApis2(target2, args, {
9262
9493
  ...handlerOpts2,
9494
+ at: atRaw,
9263
9495
  parsedArgs: cmd.args
9264
9496
  });
9265
9497
  break;
@@ -9298,7 +9530,7 @@ if (process.argv[2] === "__complete") {
9298
9530
  }
9299
9531
  switch (parsed.category) {
9300
9532
  case "query":
9301
- await handleQuery(target, args, { ...handlerOpts, dump: opts.dump });
9533
+ await handleQuery(target, args, { ...handlerOpts, dump: opts.dump, at: atRaw });
9302
9534
  break;
9303
9535
  case "tx": {
9304
9536
  const txOpts = {
@@ -9315,7 +9547,7 @@ if (process.argv[2] === "__complete") {
9315
9547
  nonce: opts.nonce,
9316
9548
  tip: opts.tip,
9317
9549
  mortality: opts.mortality,
9318
- at: opts.at
9550
+ at: atRaw
9319
9551
  };
9320
9552
  if (parsed.pallet && /^0x[0-9a-fA-F]+$/.test(parsed.pallet)) {
9321
9553
  await handleTx(parsed.pallet, args, txOpts);
@@ -9334,7 +9566,7 @@ if (process.argv[2] === "__complete") {
9334
9566
  await handleErrors2(target, handlerOpts);
9335
9567
  break;
9336
9568
  case "apis":
9337
- await handleApis2(target, args, handlerOpts);
9569
+ await handleApis2(target, args, { ...handlerOpts, at: atRaw });
9338
9570
  break;
9339
9571
  case "extensions": {
9340
9572
  if (parsed.item) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polkadot-cli",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "description": "CLI tool for querying Polkadot-ecosystem on-chain state",
5
5
  "type": "module",
6
6
  "bin": {
@@ -47,7 +47,7 @@
47
47
  "@scure/sr25519": "^1.0.0",
48
48
  "cac": "^6.7.14",
49
49
  "polkadot-api": "^2.0.1",
50
- "verifiablejs": "^1.2.0",
50
+ "verifiablejs": "1.3.0-beta.2",
51
51
  "yaml": "^2.8.3"
52
52
  },
53
53
  "devDependencies": {