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.
- package/README.md +38 -0
- package/dist/cli.mjs +135 -38
- 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.
|
|
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
|
|
468
|
-
$ dot account import
|
|
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
|
-
|
|
653
|
-
|
|
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:
|
|
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} ${
|
|
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 $${
|
|
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(
|
|
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:
|
|
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 (
|
|
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, {
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
}
|