polkadot-cli 1.1.0 → 1.2.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 +38 -0
  2. package/dist/cli.mjs +135 -38
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -79,6 +79,10 @@ dot accounts # shorthand, same as above
79
79
  # List all accounts (dev + stored)
80
80
  dot account list
81
81
 
82
+ # Add a watch-only address (no secret — for use as tx recipient or query target)
83
+ dot account add treasury 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
84
+ dot account add council 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
85
+
82
86
  # Create a new account (generates a mnemonic)
83
87
  dot account create my-validator
84
88
 
@@ -113,6 +117,36 @@ dot account inspect alice --prefix 0 # Polkadot mainnet prefix
113
117
  dot account inspect alice --output json # JSON output
114
118
  ```
115
119
 
120
+ #### Watch-only accounts
121
+
122
+ Add named addresses without secrets — useful for saving frequently-used recipients, multisig members, or query targets:
123
+
124
+ ```bash
125
+ dot account add treasury 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
126
+ dot account add council 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
127
+ ```
128
+
129
+ Watch-only accounts appear in `dot account list` with a `(watch-only)` badge and can be inspected and removed like any other account. They cannot be used with `--from` (signing) or as a source for `derive`.
130
+
131
+ The `add` subcommand is context-sensitive: bare `add <name> <address>` creates a watch-only entry, while `add --secret` or `add --env` imports a keyed account (same as `import`).
132
+
133
+ #### Named address resolution
134
+
135
+ Named accounts (both watch-only and keyed) resolve automatically everywhere an AccountId32 or MultiAddress is expected — in `dot tx` arguments and `dot query` keys:
136
+
137
+ ```bash
138
+ # Use a named account as transfer recipient
139
+ dot tx Balances.transferKeepAlive treasury 1000000000000 --from alice
140
+
141
+ # Query by account name
142
+ dot query System.Account treasury
143
+
144
+ # Dev accounts also resolve
145
+ dot tx Balances.transferKeepAlive bob 1000000000000 --from alice
146
+ ```
147
+
148
+ Resolution order: dev account name > stored account name > SS58 address > hex public key. If the input doesn't match any, the CLI shows an error listing available account names.
149
+
116
150
  #### Inspect accounts
117
151
 
118
152
  Convert between SS58 addresses, hex public keys, and account names. Accepts any of:
@@ -559,6 +593,10 @@ Config and metadata caches live in `~/.polkadot/`:
559
593
 
560
594
  > **Warning:** `accounts.json` stores secrets (mnemonics and seeds) in **plain text**. Encrypted-at-rest storage is planned but not yet implemented. Keep appropriate file permissions (`chmod 600 ~/.polkadot/accounts.json`) and do not use this for high-value mainnet accounts.
561
595
 
596
+ ## Environment compatibility
597
+
598
+ The CLI works in Node.js, Bun, and sandboxed runtimes (e.g. LLM tool-use / MCP environments) that lack a native `globalThis.WebSocket`. WebSocket connections use the [`ws`](https://github.com/websockets/ws) package explicitly, so no global polyfill is required.
599
+
562
600
  ## Development
563
601
 
564
602
  Requires [Bun](https://bun.sh).
package/dist/cli.mjs CHANGED
@@ -5,7 +5,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
5
5
  // src/cli.ts
6
6
  import cac from "cac";
7
7
  // package.json
8
- var version = "1.1.0";
8
+ var version = "1.2.0";
9
9
 
10
10
  // src/config/accounts-store.ts
11
11
  import { access as access2, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
@@ -238,6 +238,9 @@ function findAccount(file, name) {
238
238
  function isEnvSecret(secret) {
239
239
  return typeof secret === "object" && secret !== null && "env" in secret;
240
240
  }
241
+ function isWatchOnly(account) {
242
+ return account.secret === undefined;
243
+ }
241
244
 
242
245
  // src/core/accounts.ts
243
246
  import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
@@ -354,6 +357,9 @@ async function resolveAccountSigner(name) {
354
357
  const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)];
355
358
  throw new Error(`Unknown account "${name}". Available accounts: ${available.join(", ")}`);
356
359
  }
360
+ if (account.secret === undefined) {
361
+ throw new Error(`Account "${name}" is watch-only (no secret). Cannot sign. Import with --secret or --env.`);
362
+ }
357
363
  const secret = resolveSecret(account.secret);
358
364
  const isHexSeed = /^0x[0-9a-fA-F]{64}$/.test(secret);
359
365
  const keypair = isHexSeed ? deriveFromHexSeed(secret, account.derivationPath) : deriveFromMnemonic(secret, account.derivationPath);
@@ -463,15 +469,19 @@ function firstSentence(docs) {
463
469
  // src/commands/account.ts
464
470
  var ACCOUNT_HELP = `
465
471
  ${BOLD}Usage:${RESET}
472
+ $ dot account add <name> <ss58|hex> Add a watch-only address (no secret)
473
+ $ dot account add <name> --secret <s> [--path <derivation>] Import from BIP39 mnemonic
474
+ $ dot account add <name> --env <VAR> [--path <derivation>] Import account backed by env variable
466
475
  $ dot account create|new <name> [--path <derivation>] Create a new account
467
- $ dot account import|add <name> --secret <s> [--path <derivation>] Import from BIP39 mnemonic
468
- $ dot account import|add <name> --env <VAR> [--path <derivation>] Import account backed by env variable
476
+ $ dot account import <name> --secret <s> [--path <derivation>] Import from BIP39 mnemonic
477
+ $ dot account import <name> --env <VAR> [--path <derivation>] Import account backed by env variable
469
478
  $ dot account derive <source> <new-name> --path <derivation> Derive a child account
470
479
  $ dot account inspect <input> [--prefix <N>] Inspect an account/address/key
471
480
  $ dot account list List all accounts
472
481
  $ dot account remove|delete <name> [name2] ... Remove stored account(s)
473
482
 
474
483
  ${BOLD}Examples:${RESET}
484
+ $ dot account add treasury 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
475
485
  $ dot account create my-validator
476
486
  $ dot account create my-staking --path //staking
477
487
  $ dot account create multi --path //polkadot//0/wallet
@@ -500,8 +510,11 @@ function registerAccountCommands(cli) {
500
510
  case "new":
501
511
  case "create":
502
512
  return accountCreate(names[0], opts);
503
- case "import":
504
513
  case "add":
514
+ if (opts.secret || opts.env)
515
+ return accountImport(names[0], opts);
516
+ return accountAddWatchOnly(names[0], names[1]);
517
+ case "import":
505
518
  return accountImport(names[0], opts);
506
519
  case "derive":
507
520
  return accountDerive(names[0], names[1], opts);
@@ -618,6 +631,48 @@ async function accountImport(name, opts) {
618
631
  console.log();
619
632
  }
620
633
  }
634
+ async function accountAddWatchOnly(name, address) {
635
+ if (!name) {
636
+ console.error(`Account name is required.
637
+ `);
638
+ console.error("Usage: dot account add <name> <ss58-address|0x-public-key>");
639
+ process.exit(1);
640
+ }
641
+ if (!address) {
642
+ console.error(`Address is required.
643
+ `);
644
+ console.error("Usage: dot account add <name> <ss58-address|0x-public-key>");
645
+ process.exit(1);
646
+ }
647
+ if (isDevAccount(name)) {
648
+ throw new Error(`"${name}" is a built-in dev account and cannot be used as a custom account name.`);
649
+ }
650
+ const accountsFile = await loadAccounts();
651
+ if (findAccount(accountsFile, name)) {
652
+ throw new Error(`Account "${name}" already exists.`);
653
+ }
654
+ let hexPub;
655
+ if (isHexPublicKey(address)) {
656
+ hexPub = address;
657
+ } else {
658
+ try {
659
+ const decoded = fromSs58(address);
660
+ hexPub = publicKeyToHex(decoded);
661
+ } catch {
662
+ throw new Error(`Invalid address "${address}". Expected an SS58 address or 0x-prefixed 32-byte hex public key.`);
663
+ }
664
+ }
665
+ accountsFile.accounts.push({
666
+ name,
667
+ publicKey: hexPub,
668
+ derivationPath: ""
669
+ });
670
+ await saveAccounts(accountsFile);
671
+ printHeading("Account Added (watch-only)");
672
+ console.log(` ${BOLD}Name:${RESET} ${name}`);
673
+ console.log(` ${BOLD}Address:${RESET} ${toSs58(hexPub)}`);
674
+ console.log();
675
+ }
621
676
  async function accountDerive(sourceName, newName, opts) {
622
677
  if (!sourceName) {
623
678
  console.error(`Source account name is required.
@@ -645,15 +700,19 @@ async function accountDerive(sourceName, newName, opts) {
645
700
  if (!source) {
646
701
  throw new Error(`Source account "${sourceName}" not found.`);
647
702
  }
703
+ if (isWatchOnly(source)) {
704
+ throw new Error(`Cannot derive from "${sourceName}": watch-only, no secret.`);
705
+ }
648
706
  if (findAccount(accountsFile, newName)) {
649
707
  throw new Error(`Account "${newName}" already exists.`);
650
708
  }
651
709
  const path = opts.path;
652
- if (isEnvSecret(source.secret)) {
653
- const publicKey = tryDerivePublicKey(source.secret.env, path) ?? "";
710
+ const sourceSecret = source.secret;
711
+ if (isEnvSecret(sourceSecret)) {
712
+ const publicKey = tryDerivePublicKey(sourceSecret.env, path) ?? "";
654
713
  accountsFile.accounts.push({
655
714
  name: newName,
656
- secret: source.secret,
715
+ secret: sourceSecret,
657
716
  publicKey,
658
717
  derivationPath: path
659
718
  });
@@ -662,20 +721,20 @@ async function accountDerive(sourceName, newName, opts) {
662
721
  console.log(` ${BOLD}Name:${RESET} ${newName}`);
663
722
  console.log(` ${BOLD}Source:${RESET} ${sourceName}`);
664
723
  console.log(` ${BOLD}Path:${RESET} ${path}`);
665
- console.log(` ${BOLD}Env:${RESET} ${source.secret.env}`);
724
+ console.log(` ${BOLD}Env:${RESET} ${sourceSecret.env}`);
666
725
  if (publicKey) {
667
726
  console.log(` ${BOLD}Address:${RESET} ${toSs58(publicKey)}`);
668
727
  } else {
669
- console.log(` ${YELLOW}Address will resolve when $${source.secret.env} is set.${RESET}`);
728
+ console.log(` ${YELLOW}Address will resolve when $${sourceSecret.env} is set.${RESET}`);
670
729
  }
671
730
  console.log();
672
731
  } else {
673
- const { publicKey } = importAccount(source.secret, path);
732
+ const { publicKey } = importAccount(sourceSecret, path);
674
733
  const hexPub = publicKeyToHex(publicKey);
675
734
  const address = toSs58(publicKey);
676
735
  accountsFile.accounts.push({
677
736
  name: newName,
678
- secret: source.secret,
737
+ secret: sourceSecret,
679
738
  publicKey: hexPub,
680
739
  derivationPath: path
681
740
  });
@@ -704,7 +763,10 @@ async function accountList() {
704
763
  displayName += ` (${account.derivationPath})`;
705
764
  }
706
765
  let address;
707
- if (isEnvSecret(account.secret)) {
766
+ if (isWatchOnly(account)) {
767
+ displayName += " (watch-only)";
768
+ address = account.publicKey ? toSs58(account.publicKey) : "n/a";
769
+ } else if (account.secret !== undefined && isEnvSecret(account.secret)) {
708
770
  displayName += ` (env: ${account.secret.env})`;
709
771
  let pubKey = account.publicKey;
710
772
  if (!pubKey) {
@@ -786,7 +848,7 @@ async function accountInspect(input, opts) {
786
848
  name = account.name;
787
849
  if (account.publicKey) {
788
850
  publicKeyHex = account.publicKey;
789
- } else if (isEnvSecret(account.secret)) {
851
+ } else if (account.secret !== undefined && isEnvSecret(account.secret)) {
790
852
  const derived = tryDerivePublicKey(account.secret.env, account.derivationPath);
791
853
  if (!derived) {
792
854
  console.error(`Cannot derive public key for "${account.name}": $${account.secret.env} is not set.`);
@@ -830,6 +892,7 @@ async function accountInspect(input, opts) {
830
892
  import { createClient } from "polkadot-api";
831
893
  import { withPolkadotSdkCompat } from "polkadot-api/polkadot-sdk-compat";
832
894
  import { getWsProvider } from "polkadot-api/ws-provider";
895
+ import { WebSocket } from "ws";
833
896
 
834
897
  // src/utils/errors.ts
835
898
  class CliError extends Error {
@@ -891,7 +954,10 @@ async function createChainClient(chainName, chainConfig, rpcOverride) {
891
954
  restoreConsole();
892
955
  throw new ConnectionError(`No RPC endpoint configured for chain "${chainName}". Use --rpc or configure one with: dot chain add ${chainName} --rpc <url>`);
893
956
  }
894
- provider = withPolkadotSdkCompat(getWsProvider(rpc, { timeout: 1e4 }));
957
+ provider = withPolkadotSdkCompat(getWsProvider(rpc, {
958
+ timeout: 1e4,
959
+ websocketClass: WebSocket
960
+ }));
895
961
  }
896
962
  const client = createClient(provider, {
897
963
  getMetadata: async () => loadMetadata(chainName),
@@ -2560,8 +2626,35 @@ import { Binary as Binary2 } from "polkadot-api";
2560
2626
  var pjsAppsLink = (rpc, hash) => `https://polkadot.js.org/apps/?rpc=${encodeURIComponent(rpc)}#/explorer/query/${hash}`;
2561
2627
  var papiLink = (rpc, hash) => `https://dev.papi.how/explorer/${hash}#networkId=custom&endpoint=${encodeURIComponent(rpc)}`;
2562
2628
 
2629
+ // src/core/resolve-address.ts
2630
+ async function resolveAccountAddress(input) {
2631
+ if (isDevAccount(input)) {
2632
+ return getDevAddress(input);
2633
+ }
2634
+ const accountsFile = await loadAccounts();
2635
+ const account = findAccount(accountsFile, input);
2636
+ if (account) {
2637
+ if (account.publicKey) {
2638
+ return toSs58(account.publicKey);
2639
+ }
2640
+ throw new Error(`Account "${account.name}" has no public key resolved yet.`);
2641
+ }
2642
+ try {
2643
+ const decoded = fromSs58(input);
2644
+ publicKeyToHex(decoded);
2645
+ return input;
2646
+ } catch {}
2647
+ if (isHexPublicKey(input)) {
2648
+ return input;
2649
+ }
2650
+ const stored = accountsFile.accounts.map((a) => a.name);
2651
+ const available = [...DEV_NAMES, ...stored];
2652
+ throw new Error(`Unknown account or address "${input}".
2653
+ Available accounts: ${available.join(", ")}`);
2654
+ }
2655
+
2563
2656
  // src/commands/tx.ts
2564
- function parseStructArgs(meta, fields, args, callLabel) {
2657
+ async function parseStructArgs(meta, fields, args, callLabel) {
2565
2658
  const fieldNames = Object.keys(fields);
2566
2659
  if (args.length !== fieldNames.length) {
2567
2660
  const expected = fieldNames.map((name) => `${name}: ${describeType(meta.lookup, fields[name].id)}`).join(", ");
@@ -2573,7 +2666,7 @@ function parseStructArgs(meta, fields, args, callLabel) {
2573
2666
  const name = fieldNames[i];
2574
2667
  const entry = fields[name];
2575
2668
  try {
2576
- result[name] = parseTypedArg(meta, entry, args[i]);
2669
+ result[name] = await parseTypedArg(meta, entry, args[i]);
2577
2670
  } catch (err) {
2578
2671
  const typeDesc = describeType(meta.lookup, entry.id);
2579
2672
  throw new Error(`Invalid value for argument '${name}' (expected ${typeDesc}): ${JSON.stringify(args[i])}
@@ -2719,7 +2812,7 @@ function parseEnumShorthand(arg) {
2719
2812
  return null;
2720
2813
  return { variant, inner: arg.slice(firstParen + 1, -1) };
2721
2814
  }
2722
- function parseTypedArg(meta, entry, arg) {
2815
+ async function parseTypedArg(meta, entry, arg) {
2723
2816
  if (entry.type === "lookupEntry")
2724
2817
  return parseTypedArg(meta, entry.value, arg);
2725
2818
  switch (entry.type) {
@@ -2728,6 +2821,7 @@ function parseTypedArg(meta, entry, arg) {
2728
2821
  case "compact":
2729
2822
  return entry.isBig ? BigInt(arg) : parseInt(arg, 10);
2730
2823
  case "AccountId32":
2824
+ return resolveAccountAddress(arg);
2731
2825
  case "AccountId20":
2732
2826
  return arg;
2733
2827
  case "option": {
@@ -2756,7 +2850,7 @@ function parseTypedArg(meta, entry, arg) {
2756
2850
  if (resolvedDef.type === "void" || shorthand.inner === "") {
2757
2851
  return { type: matched2 };
2758
2852
  }
2759
- const innerValue = parseTypedArg(meta, variantDef, shorthand.inner);
2853
+ const innerValue = await parseTypedArg(meta, variantDef, shorthand.inner);
2760
2854
  return normalizeValue(meta.lookup, entry, { type: matched2, value: innerValue });
2761
2855
  }
2762
2856
  }
@@ -2764,7 +2858,8 @@ function parseTypedArg(meta, entry, arg) {
2764
2858
  const idVariant = entry.value.Id;
2765
2859
  const innerType = idVariant.type === "lookupEntry" ? idVariant.value : idVariant;
2766
2860
  if (innerType.type === "AccountId32" && !arg.startsWith("{")) {
2767
- return { type: "Id", value: arg };
2861
+ const resolved = await resolveAccountAddress(arg);
2862
+ return { type: "Id", value: resolved };
2768
2863
  }
2769
2864
  }
2770
2865
  const matched = variants.find((v) => v.toLowerCase() === arg.toLowerCase());
@@ -2915,7 +3010,7 @@ async function handleQuery(target, keys, opts) {
2915
3010
  }
2916
3011
  const unsafeApi = clientHandle.client.getUnsafeApi();
2917
3012
  const storageApi = unsafeApi.query[palletInfo.name][storageItem.name];
2918
- const parsedKeys = parseStorageKeys(meta, palletInfo.name, storageItem, keys);
3013
+ const parsedKeys = await parseStorageKeys(meta, palletInfo.name, storageItem, keys);
2919
3014
  const format = opts.output ?? "pretty";
2920
3015
  if (storageItem.type === "map" && parsedKeys.length === 0) {
2921
3016
  const entries = await storageApi.getEntries();
@@ -2941,7 +3036,7 @@ ${DIM}Showing ${limit} of ${entries.length} entries. Use --limit 0 for all.${RES
2941
3036
  function palletName(name) {
2942
3037
  return name;
2943
3038
  }
2944
- function parseStorageKeys(meta, palletName2, storageItem, args) {
3039
+ async function parseStorageKeys(meta, palletName2, storageItem, args) {
2945
3040
  if (storageItem.type === "plain" || storageItem.keyTypeId == null) {
2946
3041
  return args.map(parseValue);
2947
3042
  }
@@ -2952,11 +3047,11 @@ function parseStorageKeys(meta, palletName2, storageItem, args) {
2952
3047
  const keyEntry = meta.lookup(storageItem.keyTypeId);
2953
3048
  if (len === 1) {
2954
3049
  if (args.length === 1) {
2955
- return [parseTypedArg(meta, keyEntry, args[0])];
3050
+ return [await parseTypedArg(meta, keyEntry, args[0])];
2956
3051
  }
2957
3052
  if (keyEntry.type === "struct") {
2958
3053
  const label = `${palletName2}.${storageItem.name} key`;
2959
- return [parseStructArgs(meta, keyEntry.value, args, label)];
3054
+ return [await parseStructArgs(meta, keyEntry.value, args, label)];
2960
3055
  }
2961
3056
  const typeDesc = describeType(meta.lookup, storageItem.keyTypeId);
2962
3057
  throw new Error(`${palletName2}.${storageItem.name} key expects ${typeDesc}
@@ -2973,9 +3068,9 @@ function parseStorageKeys(meta, palletName2, storageItem, args) {
2973
3068
  }
2974
3069
  if (keyEntry.type === "tuple") {
2975
3070
  const entries = keyEntry.value;
2976
- return entries.map((entry, i) => parseTypedArg(meta, entry, args[i]));
3071
+ return Promise.all(entries.map((entry, i) => parseTypedArg(meta, entry, args[i])));
2977
3072
  }
2978
- return args.map((arg) => parseTypedArg(meta, keyEntry, arg));
3073
+ return Promise.all(args.map((arg) => parseTypedArg(meta, keyEntry, arg)));
2979
3074
  }
2980
3075
 
2981
3076
  // src/commands/tx.ts
@@ -3086,7 +3181,7 @@ async function handleTx(target, args, opts) {
3086
3181
  const callNames = palletInfo.calls.map((c) => c.name);
3087
3182
  throw new Error(suggestMessage(`call in ${palletInfo.name}`, callName, callNames));
3088
3183
  }
3089
- const callData = parseCallArgs(meta, palletInfo.name, callInfo.name, args);
3184
+ const callData = await parseCallArgs(meta, palletInfo.name, callInfo.name, args);
3090
3185
  if (opts.encode) {
3091
3186
  const { codec, location } = meta.builder.buildCall(palletInfo.name, callInfo.name);
3092
3187
  const encodedArgs = codec.enc(callData);
@@ -3335,7 +3430,7 @@ function formatEventValue(v) {
3335
3430
  }
3336
3431
  return JSON.stringify(v, (_k, val) => typeof val === "bigint" ? val.toString() : val);
3337
3432
  }
3338
- function parseCallArgs(meta, palletName2, callName, args) {
3433
+ async function parseCallArgs(meta, palletName2, callName, args) {
3339
3434
  const palletMeta = meta.unified.pallets.find((p) => p.name === palletName2);
3340
3435
  if (!palletMeta?.calls)
3341
3436
  return;
@@ -3352,12 +3447,12 @@ function parseCallArgs(meta, palletName2, callName, args) {
3352
3447
  return;
3353
3448
  }
3354
3449
  if (variant.type === "struct") {
3355
- return parseStructArgs2(meta, variant.value, args, `${palletName2}.${callName}`);
3450
+ return await parseStructArgs2(meta, variant.value, args, `${palletName2}.${callName}`);
3356
3451
  }
3357
3452
  if (variant.type === "lookupEntry") {
3358
3453
  const inner = variant.value;
3359
3454
  if (inner.type === "struct") {
3360
- return parseStructArgs2(meta, inner.value, args, `${palletName2}.${callName}`);
3455
+ return await parseStructArgs2(meta, inner.value, args, `${palletName2}.${callName}`);
3361
3456
  }
3362
3457
  if (inner.type === "void")
3363
3458
  return;
@@ -3365,7 +3460,7 @@ function parseCallArgs(meta, palletName2, callName, args) {
3365
3460
  throw new Error(`${palletName2}.${callName} takes 1 argument (${describeType(meta.lookup, inner.id)}), but ${args.length} provided.`);
3366
3461
  }
3367
3462
  try {
3368
- return parseTypedArg2(meta, inner, args[0]);
3463
+ return await parseTypedArg2(meta, inner, args[0]);
3369
3464
  } catch (err) {
3370
3465
  const typeDesc = describeType(meta.lookup, inner.id);
3371
3466
  throw new Error(`Invalid value for argument 0 (expected ${typeDesc}): ${JSON.stringify(args[0])}`, { cause: err });
@@ -3376,18 +3471,18 @@ function parseCallArgs(meta, palletName2, callName, args) {
3376
3471
  if (args.length !== entries.length) {
3377
3472
  throw new Error(`${palletName2}.${callName} takes ${entries.length} arguments, but ${args.length} provided.`);
3378
3473
  }
3379
- return entries.map((entry, i) => {
3474
+ return Promise.all(entries.map(async (entry, i) => {
3380
3475
  try {
3381
- return parseTypedArg2(meta, entry, args[i]);
3476
+ return await parseTypedArg2(meta, entry, args[i]);
3382
3477
  } catch (err) {
3383
3478
  const typeDesc = describeType(meta.lookup, entry.id);
3384
3479
  throw new Error(`Invalid value for argument ${i} (expected ${typeDesc}): ${JSON.stringify(args[i])}`, { cause: err });
3385
3480
  }
3386
- });
3481
+ }));
3387
3482
  }
3388
3483
  return args.length === 0 ? undefined : args.map(parseValue);
3389
3484
  }
3390
- function parseStructArgs2(meta, fields, args, callLabel) {
3485
+ async function parseStructArgs2(meta, fields, args, callLabel) {
3391
3486
  const fieldNames = Object.keys(fields);
3392
3487
  if (args.length !== fieldNames.length) {
3393
3488
  const expected = fieldNames.map((name) => `${name}: ${describeType(meta.lookup, fields[name].id)}`).join(", ");
@@ -3399,7 +3494,7 @@ function parseStructArgs2(meta, fields, args, callLabel) {
3399
3494
  const name = fieldNames[i];
3400
3495
  const entry = fields[name];
3401
3496
  try {
3402
- result[name] = parseTypedArg2(meta, entry, args[i]);
3497
+ result[name] = await parseTypedArg2(meta, entry, args[i]);
3403
3498
  } catch (err) {
3404
3499
  const typeDesc = describeType(meta.lookup, entry.id);
3405
3500
  throw new Error(`Invalid value for argument '${name}' (expected ${typeDesc}): ${JSON.stringify(args[i])}
@@ -3545,7 +3640,7 @@ function parseEnumShorthand2(arg) {
3545
3640
  return null;
3546
3641
  return { variant, inner: arg.slice(firstParen + 1, -1) };
3547
3642
  }
3548
- function parseTypedArg2(meta, entry, arg) {
3643
+ async function parseTypedArg2(meta, entry, arg) {
3549
3644
  if (entry.type === "lookupEntry")
3550
3645
  return parseTypedArg2(meta, entry.value, arg);
3551
3646
  switch (entry.type) {
@@ -3554,6 +3649,7 @@ function parseTypedArg2(meta, entry, arg) {
3554
3649
  case "compact":
3555
3650
  return entry.isBig ? BigInt(arg) : parseInt(arg, 10);
3556
3651
  case "AccountId32":
3652
+ return resolveAccountAddress(arg);
3557
3653
  case "AccountId20":
3558
3654
  return arg;
3559
3655
  case "option": {
@@ -3582,7 +3678,7 @@ function parseTypedArg2(meta, entry, arg) {
3582
3678
  if (resolvedDef.type === "void" || shorthand.inner === "") {
3583
3679
  return { type: matched2 };
3584
3680
  }
3585
- const innerValue = parseTypedArg2(meta, variantDef, shorthand.inner);
3681
+ const innerValue = await parseTypedArg2(meta, variantDef, shorthand.inner);
3586
3682
  return normalizeValue2(meta.lookup, entry, { type: matched2, value: innerValue });
3587
3683
  }
3588
3684
  }
@@ -3590,7 +3686,8 @@ function parseTypedArg2(meta, entry, arg) {
3590
3686
  const idVariant = entry.value.Id;
3591
3687
  const innerType = idVariant.type === "lookupEntry" ? idVariant.value : idVariant;
3592
3688
  if (innerType.type === "AccountId32" && !arg.startsWith("{")) {
3593
- return { type: "Id", value: arg };
3689
+ const resolved = await resolveAccountAddress(arg);
3690
+ return { type: "Id", value: resolved };
3594
3691
  }
3595
3692
  }
3596
3693
  const matched = variants.find((v) => v.toLowerCase() === arg.toLowerCase());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polkadot-cli",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "CLI tool for querying Polkadot-ecosystem on-chain state",
5
5
  "type": "module",
6
6
  "bin": {
@@ -44,12 +44,14 @@
44
44
  "@polkadot-labs/hdkd": "^0.0.26",
45
45
  "@polkadot-labs/hdkd-helpers": "^0.0.27",
46
46
  "cac": "^6.7.14",
47
- "polkadot-api": "^1.23.3"
47
+ "polkadot-api": "^1.23.3",
48
+ "ws": "^8.19.0"
48
49
  },
49
50
  "devDependencies": {
50
51
  "@biomejs/biome": "^2.4.5",
51
52
  "@changesets/cli": "^2.29.4",
52
53
  "@types/bun": "latest",
54
+ "@types/ws": "^8.18.1",
53
55
  "husky": "^9.1.7"
54
56
  }
55
57
  }