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