polkadot-cli 0.6.2 → 0.8.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 +50 -2
- package/dist/cli.mjs +546 -74
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
[](https://codecov.io/gh/peetzweg/polkadot-cli)
|
|
2
|
+
|
|
1
3
|
# polkadot-cli
|
|
2
4
|
|
|
3
5
|
A command-line tool for interacting with Polkadot-ecosystem chains. Manage chains and accounts, query storage, look up constants, inspect metadata, submit extrinsics, and compute hashes — all from your terminal.
|
|
@@ -22,7 +24,8 @@ dot chain add kusama --rpc wss://kusama-rpc.polkadot.io
|
|
|
22
24
|
dot chain add westend --light-client
|
|
23
25
|
|
|
24
26
|
# List configured chains
|
|
25
|
-
dot
|
|
27
|
+
dot chains # shorthand
|
|
28
|
+
dot chain list # equivalent
|
|
26
29
|
|
|
27
30
|
# Re-fetch metadata after a runtime upgrade
|
|
28
31
|
dot chain update # updates default chain
|
|
@@ -43,7 +46,8 @@ Dev accounts (Alice, Bob, Charlie, Dave, Eve, Ferdie) are always available for t
|
|
|
43
46
|
|
|
44
47
|
```bash
|
|
45
48
|
# List all accounts (dev + stored)
|
|
46
|
-
dot
|
|
49
|
+
dot accounts # shorthand
|
|
50
|
+
dot account list # equivalent
|
|
47
51
|
|
|
48
52
|
# Create a new account (generates a mnemonic)
|
|
49
53
|
dot account create my-validator
|
|
@@ -64,6 +68,20 @@ dot account remove my-validator
|
|
|
64
68
|
|
|
65
69
|
**Known limitation:** Hex seed import (`--secret 0x...`) does not work from the command line. The CLI argument parser (`cac`) interprets `0x`-prefixed values as JavaScript numbers, which loses precision for 32-byte seeds. Use a BIP39 mnemonic instead. If you need to import a raw seed programmatically, write it directly to `~/.polkadot/accounts.json`.
|
|
66
70
|
|
|
71
|
+
### Chain prefix
|
|
72
|
+
|
|
73
|
+
Instead of the `--chain` flag, you can prefix any target with the chain name using dot notation:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
dot query kusama.System.Account 5GrwvaEF...
|
|
77
|
+
dot const kusama.Balances.ExistentialDeposit
|
|
78
|
+
dot tx kusama.Balances.transferKeepAlive 5FHneW46... 1000000000000 --from alice
|
|
79
|
+
dot inspect kusama.System
|
|
80
|
+
dot inspect kusama.System.Account
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The `--chain` flag and default chain still work as before. If both a chain prefix and `--chain` flag are provided, the CLI errors.
|
|
84
|
+
|
|
67
85
|
### Query storage
|
|
68
86
|
|
|
69
87
|
```bash
|
|
@@ -78,6 +96,9 @@ dot query System.Account --limit 10
|
|
|
78
96
|
|
|
79
97
|
# Pipe to jq (colors disabled automatically)
|
|
80
98
|
dot query System.Account --limit 5 | jq '.[0].value.data.free'
|
|
99
|
+
|
|
100
|
+
# Query a specific chain using chain prefix
|
|
101
|
+
dot query kusama.System.Account 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
|
|
81
102
|
```
|
|
82
103
|
|
|
83
104
|
### Look up constants
|
|
@@ -85,6 +106,7 @@ dot query System.Account --limit 5 | jq '.[0].value.data.free'
|
|
|
85
106
|
```bash
|
|
86
107
|
dot const Balances.ExistentialDeposit
|
|
87
108
|
dot const System.SS58Prefix --chain kusama
|
|
109
|
+
dot const kusama.Balances.ExistentialDeposit
|
|
88
110
|
```
|
|
89
111
|
|
|
90
112
|
### Inspect metadata
|
|
@@ -100,6 +122,10 @@ dot inspect System
|
|
|
100
122
|
|
|
101
123
|
# Detailed type info for a specific item
|
|
102
124
|
dot inspect System.Account
|
|
125
|
+
|
|
126
|
+
# Inspect a specific chain using chain prefix
|
|
127
|
+
dot inspect kusama.System
|
|
128
|
+
dot inspect kusama.System.Account
|
|
103
129
|
```
|
|
104
130
|
|
|
105
131
|
### Submit extrinsics
|
|
@@ -220,6 +246,27 @@ polkadot-cli aims to be the single tool for day-to-day chain interaction: storag
|
|
|
220
246
|
|
|
221
247
|
Outside Polkadot, the closest comparable in terms of interactive UX is [near-cli-rs](https://github.com/near/near-cli-rs) (NEAR).
|
|
222
248
|
|
|
249
|
+
## Update notifications
|
|
250
|
+
|
|
251
|
+
After each command, the CLI checks whether a newer version is available on npm and displays a notification:
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
╭───────────────────────────────────────────────╮
|
|
255
|
+
│ │
|
|
256
|
+
│ Update available! 0.6.2 → 0.7.0 │
|
|
257
|
+
│ Run npm i -g polkadot-cli to update │
|
|
258
|
+
│ │
|
|
259
|
+
╰───────────────────────────────────────────────╯
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The version check runs in the background on startup and caches the result for 24 hours in `~/.polkadot/update-check.json`. It never blocks the CLI.
|
|
263
|
+
|
|
264
|
+
The notification is automatically suppressed when:
|
|
265
|
+
|
|
266
|
+
- `DOT_NO_UPDATE_CHECK=1` is set
|
|
267
|
+
- `CI` environment variable is set (any value)
|
|
268
|
+
- stdout is not a TTY (e.g. piped output)
|
|
269
|
+
|
|
223
270
|
## Configuration
|
|
224
271
|
|
|
225
272
|
Config and metadata caches live in `~/.polkadot/`:
|
|
@@ -228,6 +275,7 @@ Config and metadata caches live in `~/.polkadot/`:
|
|
|
228
275
|
~/.polkadot/
|
|
229
276
|
├── config.json # chains and default chain
|
|
230
277
|
├── accounts.json # stored accounts (⚠️ secrets are NOT encrypted — see below)
|
|
278
|
+
├── update-check.json # cached update check result
|
|
231
279
|
└── chains/
|
|
232
280
|
└── polkadot/
|
|
233
281
|
└── metadata.bin # cached SCALE-encoded metadata
|
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.8.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";
|
|
@@ -317,10 +317,9 @@ ${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
|
|
|
317
317
|
Hex seed import (0x...) is not supported via CLI.${RESET}
|
|
318
318
|
`.trimStart();
|
|
319
319
|
function registerAccountCommands(cli) {
|
|
320
|
-
cli.command("account [action] [name]", "Manage local accounts (create, import, list, remove)").option("--secret <value>", "Secret key (mnemonic or hex seed) for import").action(async (action, name, opts) => {
|
|
320
|
+
cli.command("account [action] [name]", "Manage local accounts (create, import, list, remove)").alias("accounts").option("--secret <value>", "Secret key (mnemonic or hex seed) for import").action(async (action, name, opts) => {
|
|
321
321
|
if (!action) {
|
|
322
|
-
|
|
323
|
-
return;
|
|
322
|
+
return accountList();
|
|
324
323
|
}
|
|
325
324
|
switch (action) {
|
|
326
325
|
case "create":
|
|
@@ -697,10 +696,9 @@ ${BOLD}Examples:${RESET}
|
|
|
697
696
|
$ dot chain remove kusama
|
|
698
697
|
`.trimStart();
|
|
699
698
|
function registerChainCommands(cli) {
|
|
700
|
-
cli.command("chain [action] [name]", "Manage chains (add, remove, update, list, default)").action(async (action, name, opts) => {
|
|
699
|
+
cli.command("chain [action] [name]", "Manage chains (add, remove, update, list, default)").alias("chains").action(async (action, name, opts) => {
|
|
701
700
|
if (!action) {
|
|
702
|
-
|
|
703
|
-
return;
|
|
701
|
+
return chainList();
|
|
704
702
|
}
|
|
705
703
|
switch (action) {
|
|
706
704
|
case "add":
|
|
@@ -850,28 +848,73 @@ function suggestMessage(kind, input, candidates) {
|
|
|
850
848
|
}
|
|
851
849
|
|
|
852
850
|
// src/utils/parse-target.ts
|
|
853
|
-
function parseTarget(input) {
|
|
851
|
+
function parseTarget(input, options) {
|
|
854
852
|
const parts = input.split(".");
|
|
855
|
-
if (
|
|
856
|
-
|
|
853
|
+
if (options?.allowPalletOnly) {
|
|
854
|
+
switch (parts.length) {
|
|
855
|
+
case 1:
|
|
856
|
+
if (!parts[0]) {
|
|
857
|
+
throw new Error(`Invalid target "${input}". Expected format: Pallet or Pallet.Item (e.g. System or System.Account)`);
|
|
858
|
+
}
|
|
859
|
+
return { pallet: parts[0] };
|
|
860
|
+
case 2:
|
|
861
|
+
if (!parts[0] || !parts[1]) {
|
|
862
|
+
throw new Error(`Invalid target "${input}". Expected format: Pallet.Item or Chain.Pallet (e.g. System.Account or kusama.System)`);
|
|
863
|
+
}
|
|
864
|
+
if (options.knownChains?.some((c) => c.toLowerCase() === parts[0].toLowerCase())) {
|
|
865
|
+
return { chain: parts[0], pallet: parts[1] };
|
|
866
|
+
}
|
|
867
|
+
return { pallet: parts[0], item: parts[1] };
|
|
868
|
+
case 3:
|
|
869
|
+
if (!parts[0] || !parts[1] || !parts[2]) {
|
|
870
|
+
throw new Error(`Invalid target "${input}". Expected format: Chain.Pallet.Item (e.g. kusama.System.Account)`);
|
|
871
|
+
}
|
|
872
|
+
return { chain: parts[0], pallet: parts[1], item: parts[2] };
|
|
873
|
+
default:
|
|
874
|
+
throw new Error(`Invalid target "${input}". Expected format: Pallet, Pallet.Item, or Chain.Pallet.Item`);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
switch (parts.length) {
|
|
878
|
+
case 2:
|
|
879
|
+
if (!parts[0] || !parts[1]) {
|
|
880
|
+
throw new Error(`Invalid target "${input}". Expected format: Pallet.Item (e.g. System.Account)`);
|
|
881
|
+
}
|
|
882
|
+
return { pallet: parts[0], item: parts[1] };
|
|
883
|
+
case 3:
|
|
884
|
+
if (!parts[0] || !parts[1] || !parts[2]) {
|
|
885
|
+
throw new Error(`Invalid target "${input}". Expected format: Chain.Pallet.Item (e.g. kusama.System.Account)`);
|
|
886
|
+
}
|
|
887
|
+
return { chain: parts[0], pallet: parts[1], item: parts[2] };
|
|
888
|
+
default:
|
|
889
|
+
throw new Error(`Invalid target "${input}". Expected format: Pallet.Item (e.g. System.Account)`);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
function resolveTargetChain(target, chainFlag) {
|
|
893
|
+
if (target.chain && chainFlag) {
|
|
894
|
+
throw new Error(`Chain specified both as prefix ("${target.chain}") and as --chain flag ("${chainFlag}"). Use one or the other.`);
|
|
857
895
|
}
|
|
858
|
-
return
|
|
896
|
+
return target.chain ?? chainFlag;
|
|
859
897
|
}
|
|
860
898
|
|
|
861
899
|
// src/commands/const.ts
|
|
862
900
|
function registerConstCommand(cli) {
|
|
863
901
|
cli.command("const [target]", "Look up a pallet constant (e.g. Balances.ExistentialDeposit)").action(async (target, opts) => {
|
|
864
902
|
if (!target) {
|
|
865
|
-
console.log("Usage: dot const <Pallet.Constant> [--chain <name>] [--output json]");
|
|
903
|
+
console.log("Usage: dot const <[Chain.]Pallet.Constant> [--chain <name>] [--output json]");
|
|
866
904
|
console.log("");
|
|
867
905
|
console.log("Examples:");
|
|
868
906
|
console.log(" $ dot const Balances.ExistentialDeposit");
|
|
869
907
|
console.log(" $ dot const System.SS58Prefix --chain kusama");
|
|
908
|
+
console.log(" $ dot const kusama.Balances.ExistentialDeposit # chain prefix");
|
|
870
909
|
return;
|
|
871
910
|
}
|
|
872
911
|
const config = await loadConfig();
|
|
873
|
-
const
|
|
874
|
-
const
|
|
912
|
+
const knownChains = Object.keys(config.chains);
|
|
913
|
+
const parsed = parseTarget(target, { knownChains });
|
|
914
|
+
const effectiveChain = resolveTargetChain(parsed, opts.chain);
|
|
915
|
+
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
916
|
+
const pallet = parsed.pallet;
|
|
917
|
+
const item = parsed.item;
|
|
875
918
|
const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
876
919
|
try {
|
|
877
920
|
const meta = await getOrFetchMetadata(chainName, clientHandle);
|
|
@@ -888,7 +931,14 @@ function registerConstCommand(cli) {
|
|
|
888
931
|
const unsafeApi = clientHandle.client.getUnsafeApi();
|
|
889
932
|
const runtimeToken = await unsafeApi.runtimeToken;
|
|
890
933
|
const result = unsafeApi.constants[palletInfo.name][constantItem.name](runtimeToken);
|
|
891
|
-
|
|
934
|
+
const format = opts.output ?? "pretty";
|
|
935
|
+
if (format === "json") {
|
|
936
|
+
console.error(`chain: ${chainName}`);
|
|
937
|
+
} else {
|
|
938
|
+
console.log(`${DIM}chain: ${chainName}${RESET}
|
|
939
|
+
`);
|
|
940
|
+
}
|
|
941
|
+
printResult(result, format);
|
|
892
942
|
} finally {
|
|
893
943
|
clientHandle.destroy();
|
|
894
944
|
}
|
|
@@ -1030,7 +1080,17 @@ function registerHashCommand(cli) {
|
|
|
1030
1080
|
function registerInspectCommand(cli) {
|
|
1031
1081
|
cli.command("inspect [target]", "Inspect chain metadata (pallets, storage, constants)").option("--chain <name>", "Target chain").option("--rpc <url>", "Override RPC endpoint").action(async (target, opts) => {
|
|
1032
1082
|
const config = await loadConfig();
|
|
1033
|
-
const
|
|
1083
|
+
const knownChains = Object.keys(config.chains);
|
|
1084
|
+
let effectiveChain = opts.chain;
|
|
1085
|
+
let palletName;
|
|
1086
|
+
let itemName;
|
|
1087
|
+
if (target) {
|
|
1088
|
+
const parsed = parseTarget(target, { knownChains, allowPalletOnly: true });
|
|
1089
|
+
effectiveChain = resolveTargetChain(parsed, opts.chain);
|
|
1090
|
+
palletName = parsed.pallet;
|
|
1091
|
+
itemName = parsed.item;
|
|
1092
|
+
}
|
|
1093
|
+
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
1034
1094
|
let meta;
|
|
1035
1095
|
try {
|
|
1036
1096
|
meta = await getOrFetchMetadata(chainName);
|
|
@@ -1057,11 +1117,11 @@ function registerInspectCommand(cli) {
|
|
|
1057
1117
|
console.log();
|
|
1058
1118
|
return;
|
|
1059
1119
|
}
|
|
1060
|
-
if (!
|
|
1120
|
+
if (!itemName) {
|
|
1061
1121
|
const palletNames2 = getPalletNames(meta);
|
|
1062
|
-
const pallet2 = findPallet(meta,
|
|
1122
|
+
const pallet2 = findPallet(meta, palletName);
|
|
1063
1123
|
if (!pallet2) {
|
|
1064
|
-
throw new Error(suggestMessage("pallet",
|
|
1124
|
+
throw new Error(suggestMessage("pallet", palletName, palletNames2));
|
|
1065
1125
|
}
|
|
1066
1126
|
printHeading(`${pallet2.name} Pallet`);
|
|
1067
1127
|
if (pallet2.docs.length) {
|
|
@@ -1086,7 +1146,6 @@ function registerInspectCommand(cli) {
|
|
|
1086
1146
|
}
|
|
1087
1147
|
return;
|
|
1088
1148
|
}
|
|
1089
|
-
const { pallet: palletName, item: itemName } = parseTarget(target);
|
|
1090
1149
|
const palletNames = getPalletNames(meta);
|
|
1091
1150
|
const pallet = findPallet(meta, palletName);
|
|
1092
1151
|
if (!pallet) {
|
|
@@ -1146,6 +1205,254 @@ function parseValue(arg) {
|
|
|
1146
1205
|
return arg;
|
|
1147
1206
|
}
|
|
1148
1207
|
|
|
1208
|
+
// src/commands/tx.ts
|
|
1209
|
+
import { getViewBuilder } from "@polkadot-api/view-builder";
|
|
1210
|
+
import { Binary } from "polkadot-api";
|
|
1211
|
+
|
|
1212
|
+
// src/core/explorers.ts
|
|
1213
|
+
var pjsAppsLink = (rpc, hash) => `https://polkadot.js.org/apps/?rpc=${encodeURIComponent(rpc)}#/explorer/query/${hash}`;
|
|
1214
|
+
var papiLink = (rpc, hash) => `https://dev.papi.how/explorer/${hash}#networkId=custom&endpoint=${encodeURIComponent(rpc)}`;
|
|
1215
|
+
|
|
1216
|
+
// src/commands/tx.ts
|
|
1217
|
+
function parseStructArgs(meta, fields, args, callLabel) {
|
|
1218
|
+
const fieldNames = Object.keys(fields);
|
|
1219
|
+
if (args.length !== fieldNames.length) {
|
|
1220
|
+
const expected = fieldNames.map((name) => `${name}: ${describeType(meta.lookup, fields[name].id)}`).join(", ");
|
|
1221
|
+
throw new Error(`${callLabel} takes ${fieldNames.length} argument(s): ${expected}
|
|
1222
|
+
` + ` Got ${args.length} argument(s).`);
|
|
1223
|
+
}
|
|
1224
|
+
const result = {};
|
|
1225
|
+
for (let i = 0;i < fieldNames.length; i++) {
|
|
1226
|
+
const name = fieldNames[i];
|
|
1227
|
+
const entry = fields[name];
|
|
1228
|
+
result[name] = parseTypedArg(meta, entry, args[i]);
|
|
1229
|
+
}
|
|
1230
|
+
return result;
|
|
1231
|
+
}
|
|
1232
|
+
function normalizeValue(lookup, entry, value) {
|
|
1233
|
+
let resolved = entry;
|
|
1234
|
+
while (resolved.type === "lookupEntry") {
|
|
1235
|
+
resolved = resolved.value;
|
|
1236
|
+
}
|
|
1237
|
+
switch (resolved.type) {
|
|
1238
|
+
case "enum": {
|
|
1239
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value) && "type" in value) {
|
|
1240
|
+
const enumValue = value;
|
|
1241
|
+
const variant = resolved.value[enumValue.type];
|
|
1242
|
+
if (variant) {
|
|
1243
|
+
let innerEntry = variant;
|
|
1244
|
+
while (innerEntry.type === "lookupEntry") {
|
|
1245
|
+
innerEntry = innerEntry.value;
|
|
1246
|
+
}
|
|
1247
|
+
let normalizedInner = enumValue.value;
|
|
1248
|
+
if (innerEntry.type !== "array" && innerEntry.type !== "sequence" && innerEntry.type !== "void" && Array.isArray(normalizedInner) && normalizedInner.length === 1) {
|
|
1249
|
+
normalizedInner = normalizedInner[0];
|
|
1250
|
+
}
|
|
1251
|
+
if (normalizedInner !== undefined && innerEntry.type !== "void") {
|
|
1252
|
+
normalizedInner = normalizeValue(lookup, innerEntry, normalizedInner);
|
|
1253
|
+
}
|
|
1254
|
+
return { type: enumValue.type, value: normalizedInner };
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
return value;
|
|
1258
|
+
}
|
|
1259
|
+
case "struct": {
|
|
1260
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
1261
|
+
const fields = resolved.value;
|
|
1262
|
+
const result = {};
|
|
1263
|
+
for (const [key, val] of Object.entries(value)) {
|
|
1264
|
+
if (key in fields) {
|
|
1265
|
+
result[key] = normalizeValue(lookup, fields[key], val);
|
|
1266
|
+
} else {
|
|
1267
|
+
result[key] = val;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
return result;
|
|
1271
|
+
}
|
|
1272
|
+
return value;
|
|
1273
|
+
}
|
|
1274
|
+
case "array":
|
|
1275
|
+
case "sequence": {
|
|
1276
|
+
let innerResolved = resolved.value;
|
|
1277
|
+
while (innerResolved.type === "lookupEntry") {
|
|
1278
|
+
innerResolved = innerResolved.value;
|
|
1279
|
+
}
|
|
1280
|
+
if (innerResolved.type === "primitive" && innerResolved.value === "u8" && typeof value === "string") {
|
|
1281
|
+
if (/^0x[0-9a-fA-F]*$/.test(value))
|
|
1282
|
+
return Binary.fromHex(value);
|
|
1283
|
+
return Binary.fromText(value);
|
|
1284
|
+
}
|
|
1285
|
+
if (Array.isArray(value)) {
|
|
1286
|
+
const innerEntry = resolved.value;
|
|
1287
|
+
return value.map((item) => normalizeValue(lookup, innerEntry, item));
|
|
1288
|
+
}
|
|
1289
|
+
return value;
|
|
1290
|
+
}
|
|
1291
|
+
case "tuple": {
|
|
1292
|
+
if (Array.isArray(value)) {
|
|
1293
|
+
const entries = resolved.value;
|
|
1294
|
+
return value.map((item, i) => i < entries.length ? normalizeValue(lookup, entries[i], item) : item);
|
|
1295
|
+
}
|
|
1296
|
+
return value;
|
|
1297
|
+
}
|
|
1298
|
+
case "option": {
|
|
1299
|
+
if (value !== null && value !== undefined) {
|
|
1300
|
+
return normalizeValue(lookup, resolved.value, value);
|
|
1301
|
+
}
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
case "primitive": {
|
|
1305
|
+
if (typeof value === "string") {
|
|
1306
|
+
const prim = resolved.value;
|
|
1307
|
+
switch (prim) {
|
|
1308
|
+
case "bool":
|
|
1309
|
+
return value === "true";
|
|
1310
|
+
case "u64":
|
|
1311
|
+
case "u128":
|
|
1312
|
+
case "u256":
|
|
1313
|
+
case "i64":
|
|
1314
|
+
case "i128":
|
|
1315
|
+
case "i256":
|
|
1316
|
+
return BigInt(value);
|
|
1317
|
+
case "u8":
|
|
1318
|
+
case "u16":
|
|
1319
|
+
case "u32":
|
|
1320
|
+
case "i8":
|
|
1321
|
+
case "i16":
|
|
1322
|
+
case "i32":
|
|
1323
|
+
return parseInt(value, 10);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
return value;
|
|
1327
|
+
}
|
|
1328
|
+
case "compact": {
|
|
1329
|
+
if (typeof value === "string") {
|
|
1330
|
+
return resolved.isBig ? BigInt(value) : parseInt(value, 10);
|
|
1331
|
+
}
|
|
1332
|
+
return value;
|
|
1333
|
+
}
|
|
1334
|
+
default:
|
|
1335
|
+
return value;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
function parseTypedArg(meta, entry, arg) {
|
|
1339
|
+
switch (entry.type) {
|
|
1340
|
+
case "primitive":
|
|
1341
|
+
return parsePrimitive(entry.value, arg);
|
|
1342
|
+
case "compact":
|
|
1343
|
+
return entry.isBig ? BigInt(arg) : parseInt(arg, 10);
|
|
1344
|
+
case "AccountId32":
|
|
1345
|
+
case "AccountId20":
|
|
1346
|
+
return arg;
|
|
1347
|
+
case "option": {
|
|
1348
|
+
if (arg === "null" || arg === "undefined" || arg === "none") {
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
return parseTypedArg(meta, entry.value, arg);
|
|
1352
|
+
}
|
|
1353
|
+
case "enum": {
|
|
1354
|
+
if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
|
|
1355
|
+
const callCodec = meta.builder.buildDefinition(meta.lookup.call);
|
|
1356
|
+
return callCodec.dec(Binary.fromHex(arg).asBytes());
|
|
1357
|
+
}
|
|
1358
|
+
if (arg.startsWith("{")) {
|
|
1359
|
+
try {
|
|
1360
|
+
return normalizeValue(meta.lookup, entry, JSON.parse(arg));
|
|
1361
|
+
} catch {}
|
|
1362
|
+
}
|
|
1363
|
+
const variants = Object.keys(entry.value);
|
|
1364
|
+
if (variants.includes("Id")) {
|
|
1365
|
+
const idVariant = entry.value.Id;
|
|
1366
|
+
const innerType = idVariant.type === "lookupEntry" ? idVariant.value : idVariant;
|
|
1367
|
+
if (innerType.type === "AccountId32" && !arg.startsWith("{")) {
|
|
1368
|
+
return { type: "Id", value: arg };
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
const matched = variants.find((v) => v.toLowerCase() === arg.toLowerCase());
|
|
1372
|
+
if (matched) {
|
|
1373
|
+
const variant = entry.value[matched];
|
|
1374
|
+
if (variant.type === "void") {
|
|
1375
|
+
return { type: matched };
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
return parseValue(arg);
|
|
1379
|
+
}
|
|
1380
|
+
case "sequence":
|
|
1381
|
+
case "array": {
|
|
1382
|
+
const inner = entry.value;
|
|
1383
|
+
if (inner.type === "primitive" && inner.value === "u8") {
|
|
1384
|
+
if (/^0x[0-9a-fA-F]*$/.test(arg))
|
|
1385
|
+
return Binary.fromHex(arg);
|
|
1386
|
+
return Binary.fromText(arg);
|
|
1387
|
+
}
|
|
1388
|
+
if (arg.startsWith("[")) {
|
|
1389
|
+
try {
|
|
1390
|
+
return normalizeValue(meta.lookup, entry, JSON.parse(arg));
|
|
1391
|
+
} catch {}
|
|
1392
|
+
}
|
|
1393
|
+
if (/^0x[0-9a-fA-F]*$/.test(arg))
|
|
1394
|
+
return Binary.fromHex(arg);
|
|
1395
|
+
return parseValue(arg);
|
|
1396
|
+
}
|
|
1397
|
+
case "struct":
|
|
1398
|
+
if (arg.startsWith("{")) {
|
|
1399
|
+
try {
|
|
1400
|
+
return normalizeValue(meta.lookup, entry, JSON.parse(arg));
|
|
1401
|
+
} catch {}
|
|
1402
|
+
}
|
|
1403
|
+
return parseValue(arg);
|
|
1404
|
+
case "tuple":
|
|
1405
|
+
if (arg.startsWith("[")) {
|
|
1406
|
+
try {
|
|
1407
|
+
return normalizeValue(meta.lookup, entry, JSON.parse(arg));
|
|
1408
|
+
} catch {}
|
|
1409
|
+
}
|
|
1410
|
+
return parseValue(arg);
|
|
1411
|
+
default:
|
|
1412
|
+
return parseValue(arg);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
function parsePrimitive(prim, arg) {
|
|
1416
|
+
switch (prim) {
|
|
1417
|
+
case "bool":
|
|
1418
|
+
return arg === "true";
|
|
1419
|
+
case "char":
|
|
1420
|
+
case "str":
|
|
1421
|
+
return arg;
|
|
1422
|
+
case "u8":
|
|
1423
|
+
case "u16":
|
|
1424
|
+
case "u32":
|
|
1425
|
+
case "i8":
|
|
1426
|
+
case "i16":
|
|
1427
|
+
case "i32":
|
|
1428
|
+
return parseInt(arg, 10);
|
|
1429
|
+
case "u64":
|
|
1430
|
+
case "u128":
|
|
1431
|
+
case "u256":
|
|
1432
|
+
case "i64":
|
|
1433
|
+
case "i128":
|
|
1434
|
+
case "i256":
|
|
1435
|
+
return BigInt(arg);
|
|
1436
|
+
default:
|
|
1437
|
+
return parseValue(arg);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
var PAPI_BUILTIN_EXTENSIONS = new Set([
|
|
1441
|
+
"CheckNonZeroSender",
|
|
1442
|
+
"CheckSpecVersion",
|
|
1443
|
+
"CheckTxVersion",
|
|
1444
|
+
"CheckGenesis",
|
|
1445
|
+
"CheckMortality",
|
|
1446
|
+
"CheckNonce",
|
|
1447
|
+
"CheckWeight",
|
|
1448
|
+
"ChargeTransactionPayment",
|
|
1449
|
+
"ChargeAssetTxPayment",
|
|
1450
|
+
"CheckMetadataHash",
|
|
1451
|
+
"StorageWeightReclaim",
|
|
1452
|
+
"PrevalidateAttests"
|
|
1453
|
+
]);
|
|
1454
|
+
var NO_DEFAULT = Symbol("no-default");
|
|
1455
|
+
|
|
1149
1456
|
// src/commands/query.ts
|
|
1150
1457
|
var DEFAULT_LIMIT = 100;
|
|
1151
1458
|
function registerQueryCommand(cli) {
|
|
@@ -1153,7 +1460,7 @@ function registerQueryCommand(cli) {
|
|
|
1153
1460
|
default: DEFAULT_LIMIT
|
|
1154
1461
|
}).action(async (target, keys, opts) => {
|
|
1155
1462
|
if (!target) {
|
|
1156
|
-
console.log("Usage: dot query <Pallet.Item> [...keys] [--chain <name>] [--output json]");
|
|
1463
|
+
console.log("Usage: dot query <[Chain.]Pallet.Item> [...keys] [--chain <name>] [--output json]");
|
|
1157
1464
|
console.log("");
|
|
1158
1465
|
console.log("Examples:");
|
|
1159
1466
|
console.log(" $ dot query System.Number # plain storage value");
|
|
@@ -1162,11 +1469,16 @@ function registerQueryCommand(cli) {
|
|
|
1162
1469
|
console.log(" $ dot query System.Account --limit 10 # first 10 entries");
|
|
1163
1470
|
console.log(" $ dot query System.Account --limit 0 # all entries (no limit)");
|
|
1164
1471
|
console.log(" $ dot query Assets.Metadata 42 --chain asset-hub");
|
|
1472
|
+
console.log(" $ dot query kusama.System.Account 5Grw... # chain prefix");
|
|
1165
1473
|
return;
|
|
1166
1474
|
}
|
|
1167
1475
|
const config = await loadConfig();
|
|
1168
|
-
const
|
|
1169
|
-
const
|
|
1476
|
+
const knownChains = Object.keys(config.chains);
|
|
1477
|
+
const parsed = parseTarget(target, { knownChains });
|
|
1478
|
+
const effectiveChain = resolveTargetChain(parsed, opts.chain);
|
|
1479
|
+
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
1480
|
+
const pallet = parsed.pallet;
|
|
1481
|
+
const item = parsed.item;
|
|
1170
1482
|
const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
1171
1483
|
try {
|
|
1172
1484
|
const meta = await getOrFetchMetadata(chainName, clientHandle);
|
|
@@ -1182,8 +1494,14 @@ function registerQueryCommand(cli) {
|
|
|
1182
1494
|
}
|
|
1183
1495
|
const unsafeApi = clientHandle.client.getUnsafeApi();
|
|
1184
1496
|
const storageApi = unsafeApi.query[palletInfo.name][storageItem.name];
|
|
1185
|
-
const parsedKeys = keys
|
|
1497
|
+
const parsedKeys = parseStorageKeys(meta, palletInfo.name, storageItem, keys);
|
|
1186
1498
|
const format = opts.output ?? "pretty";
|
|
1499
|
+
if (format === "json") {
|
|
1500
|
+
console.error(`chain: ${chainName}`);
|
|
1501
|
+
} else {
|
|
1502
|
+
console.log(`${DIM}chain: ${chainName}${RESET}
|
|
1503
|
+
`);
|
|
1504
|
+
}
|
|
1187
1505
|
if (storageItem.type === "map" && parsedKeys.length === 0) {
|
|
1188
1506
|
const entries = await storageApi.getEntries();
|
|
1189
1507
|
const limit = Number(opts.limit);
|
|
@@ -1206,26 +1524,57 @@ ${DIM}Showing ${limit} of ${entries.length} entries. Use --limit 0 for all.${RES
|
|
|
1206
1524
|
}
|
|
1207
1525
|
});
|
|
1208
1526
|
}
|
|
1527
|
+
function parseStorageKeys(meta, palletName, storageItem, args) {
|
|
1528
|
+
if (storageItem.type === "plain" || storageItem.keyTypeId == null) {
|
|
1529
|
+
return args.map(parseValue);
|
|
1530
|
+
}
|
|
1531
|
+
if (args.length === 0)
|
|
1532
|
+
return [];
|
|
1533
|
+
const storageEntry = meta.builder.buildStorage(palletName, storageItem.name);
|
|
1534
|
+
const len = storageEntry.len;
|
|
1535
|
+
const keyEntry = meta.lookup(storageItem.keyTypeId);
|
|
1536
|
+
if (len === 1) {
|
|
1537
|
+
if (args.length === 1) {
|
|
1538
|
+
return [parseTypedArg(meta, keyEntry, args[0])];
|
|
1539
|
+
}
|
|
1540
|
+
if (keyEntry.type === "struct") {
|
|
1541
|
+
const label = `${palletName}.${storageItem.name} key`;
|
|
1542
|
+
return [parseStructArgs(meta, keyEntry.value, args, label)];
|
|
1543
|
+
}
|
|
1544
|
+
const typeDesc = describeType(meta.lookup, storageItem.keyTypeId);
|
|
1545
|
+
throw new Error(`${palletName}.${storageItem.name} key expects ${typeDesc}
|
|
1546
|
+
` + ` Pass 1 argument. Got ${args.length}.`);
|
|
1547
|
+
}
|
|
1548
|
+
if (args.length !== len) {
|
|
1549
|
+
let typeDesc;
|
|
1550
|
+
if (keyEntry.type === "tuple") {
|
|
1551
|
+
typeDesc = keyEntry.value.map((e) => describeType(meta.lookup, e.id)).join(", ");
|
|
1552
|
+
} else {
|
|
1553
|
+
typeDesc = describeType(meta.lookup, storageItem.keyTypeId);
|
|
1554
|
+
}
|
|
1555
|
+
throw new Error(`${palletName}.${storageItem.name} expects ${len} key arg(s): (${typeDesc}). Got ${args.length}.`);
|
|
1556
|
+
}
|
|
1557
|
+
if (keyEntry.type === "tuple") {
|
|
1558
|
+
const entries = keyEntry.value;
|
|
1559
|
+
return entries.map((entry, i) => parseTypedArg(meta, entry, args[i]));
|
|
1560
|
+
}
|
|
1561
|
+
return args.map((arg) => parseTypedArg(meta, keyEntry, arg));
|
|
1562
|
+
}
|
|
1209
1563
|
|
|
1210
1564
|
// src/commands/tx.ts
|
|
1211
|
-
import { getViewBuilder } from "@polkadot-api/view-builder";
|
|
1212
|
-
import { Binary } from "polkadot-api";
|
|
1213
|
-
|
|
1214
|
-
// src/core/explorers.ts
|
|
1215
|
-
var pjsAppsLink = (rpc, hash) => `https://polkadot.js.org/apps/?rpc=${encodeURIComponent(rpc)}#/explorer/query/${hash}`;
|
|
1216
|
-
var papiLink = (rpc, hash) => `https://dev.papi.how/explorer/${hash}#networkId=custom&endpoint=${encodeURIComponent(rpc)}`;
|
|
1217
|
-
|
|
1218
|
-
// src/commands/tx.ts
|
|
1565
|
+
import { getViewBuilder as getViewBuilder2 } from "@polkadot-api/view-builder";
|
|
1566
|
+
import { Binary as Binary2 } from "polkadot-api";
|
|
1219
1567
|
function registerTxCommand(cli) {
|
|
1220
1568
|
cli.command("tx [target] [...args]", "Submit an extrinsic (e.g. Balances.transferKeepAlive <dest> <amount>)").option("--from <name>", "Account to sign with (required)").option("--dry-run", "Estimate fees without submitting").option("--encode", "Encode call to hex without signing or submitting").option("--ext <json>", `Custom signed extension values as JSON, e.g. '{"ExtName":{"value":...}}'`).action(async (target, args, opts) => {
|
|
1221
1569
|
if (!target) {
|
|
1222
|
-
console.log("Usage: dot tx <Pallet.Call|0xCallHex> [...args] --from <account> [--dry-run] [--encode]");
|
|
1570
|
+
console.log("Usage: dot tx <[Chain.]Pallet.Call|0xCallHex> [...args] --from <account> [--dry-run] [--encode]");
|
|
1223
1571
|
console.log("");
|
|
1224
1572
|
console.log("Examples:");
|
|
1225
1573
|
console.log(" $ dot tx Balances.transferKeepAlive 5FHn... 1000000000000 --from alice");
|
|
1226
1574
|
console.log(" $ dot tx System.remark 0xdeadbeef --from alice --dry-run");
|
|
1227
1575
|
console.log(" $ dot tx 0x0001076465616462656566 --from alice");
|
|
1228
1576
|
console.log(" $ dot tx Assets.force_create 4 owner true 10 --encode --chain people");
|
|
1577
|
+
console.log(" $ dot tx kusama.Balances.transferKeepAlive 5FHn... 1000000000000 --from alice");
|
|
1229
1578
|
return;
|
|
1230
1579
|
}
|
|
1231
1580
|
if (!opts.from && !opts.encode) {
|
|
@@ -1239,7 +1588,14 @@ function registerTxCommand(cli) {
|
|
|
1239
1588
|
throw new Error("--encode cannot be used with raw call hex (already encoded)");
|
|
1240
1589
|
}
|
|
1241
1590
|
const config = await loadConfig();
|
|
1242
|
-
|
|
1591
|
+
let effectiveChain = opts.chain;
|
|
1592
|
+
let parsedTarget;
|
|
1593
|
+
if (!isRawCall) {
|
|
1594
|
+
const knownChains = Object.keys(config.chains);
|
|
1595
|
+
parsedTarget = parseTarget(target, { knownChains });
|
|
1596
|
+
effectiveChain = resolveTargetChain(parsedTarget, opts.chain);
|
|
1597
|
+
}
|
|
1598
|
+
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
1243
1599
|
const signer = opts.encode ? undefined : await resolveAccountSigner(opts.from);
|
|
1244
1600
|
let clientHandle;
|
|
1245
1601
|
if (!opts.encode) {
|
|
@@ -1272,11 +1628,12 @@ function registerTxCommand(cli) {
|
|
|
1272
1628
|
throw new Error(`Extra arguments are not allowed when submitting a raw call hex.
|
|
1273
1629
|
` + "Usage: dot tx 0x<call_hex> --from <account>");
|
|
1274
1630
|
}
|
|
1275
|
-
const callBinary =
|
|
1631
|
+
const callBinary = Binary2.fromHex(target);
|
|
1276
1632
|
tx = await unsafeApi.txFromCallData(callBinary);
|
|
1277
1633
|
callHex = target;
|
|
1278
1634
|
} else {
|
|
1279
|
-
const
|
|
1635
|
+
const pallet = parsedTarget.pallet;
|
|
1636
|
+
const callName = parsedTarget.item;
|
|
1280
1637
|
const palletNames = getPalletNames(meta);
|
|
1281
1638
|
const palletInfo = findPallet(meta, pallet);
|
|
1282
1639
|
if (!palletInfo) {
|
|
@@ -1292,7 +1649,7 @@ function registerTxCommand(cli) {
|
|
|
1292
1649
|
const { codec, location } = meta.builder.buildCall(palletInfo.name, callInfo.name);
|
|
1293
1650
|
const encodedArgs = codec.enc(callData);
|
|
1294
1651
|
const fullCall = new Uint8Array([location[0], location[1], ...encodedArgs]);
|
|
1295
|
-
console.log(
|
|
1652
|
+
console.log(Binary2.fromBytes(fullCall).asHex());
|
|
1296
1653
|
return;
|
|
1297
1654
|
}
|
|
1298
1655
|
tx = unsafeApi.tx[palletInfo.name][callInfo.name](callData);
|
|
@@ -1377,7 +1734,7 @@ function formatDispatchError(err) {
|
|
|
1377
1734
|
}
|
|
1378
1735
|
function decodeCall(meta, callHex) {
|
|
1379
1736
|
try {
|
|
1380
|
-
const viewBuilder =
|
|
1737
|
+
const viewBuilder = getViewBuilder2(meta.lookup);
|
|
1381
1738
|
const decoded = viewBuilder.callDecoder(callHex);
|
|
1382
1739
|
const palletName = decoded.pallet.value.name;
|
|
1383
1740
|
const callName = decoded.call.value.name;
|
|
@@ -1494,30 +1851,30 @@ function parseCallArgs(meta, palletName, callName, args) {
|
|
|
1494
1851
|
return;
|
|
1495
1852
|
}
|
|
1496
1853
|
if (variant.type === "struct") {
|
|
1497
|
-
return
|
|
1854
|
+
return parseStructArgs2(meta, variant.value, args, `${palletName}.${callName}`);
|
|
1498
1855
|
}
|
|
1499
1856
|
if (variant.type === "lookupEntry") {
|
|
1500
1857
|
const inner = variant.value;
|
|
1501
1858
|
if (inner.type === "struct") {
|
|
1502
|
-
return
|
|
1859
|
+
return parseStructArgs2(meta, inner.value, args, `${palletName}.${callName}`);
|
|
1503
1860
|
}
|
|
1504
1861
|
if (inner.type === "void")
|
|
1505
1862
|
return;
|
|
1506
1863
|
if (args.length !== 1) {
|
|
1507
1864
|
throw new Error(`${palletName}.${callName} takes 1 argument (${describeType(meta.lookup, inner.id)}), but ${args.length} provided.`);
|
|
1508
1865
|
}
|
|
1509
|
-
return
|
|
1866
|
+
return parseTypedArg2(meta, inner, args[0]);
|
|
1510
1867
|
}
|
|
1511
1868
|
if (variant.type === "tuple") {
|
|
1512
1869
|
const entries = variant.value;
|
|
1513
1870
|
if (args.length !== entries.length) {
|
|
1514
1871
|
throw new Error(`${palletName}.${callName} takes ${entries.length} arguments, but ${args.length} provided.`);
|
|
1515
1872
|
}
|
|
1516
|
-
return entries.map((entry, i) =>
|
|
1873
|
+
return entries.map((entry, i) => parseTypedArg2(meta, entry, args[i]));
|
|
1517
1874
|
}
|
|
1518
1875
|
return args.length === 0 ? undefined : args.map(parseValue);
|
|
1519
1876
|
}
|
|
1520
|
-
function
|
|
1877
|
+
function parseStructArgs2(meta, fields, args, callLabel) {
|
|
1521
1878
|
const fieldNames = Object.keys(fields);
|
|
1522
1879
|
if (args.length !== fieldNames.length) {
|
|
1523
1880
|
const expected = fieldNames.map((name) => `${name}: ${describeType(meta.lookup, fields[name].id)}`).join(", ");
|
|
@@ -1528,11 +1885,11 @@ function parseStructArgs(meta, fields, args, callLabel) {
|
|
|
1528
1885
|
for (let i = 0;i < fieldNames.length; i++) {
|
|
1529
1886
|
const name = fieldNames[i];
|
|
1530
1887
|
const entry = fields[name];
|
|
1531
|
-
result[name] =
|
|
1888
|
+
result[name] = parseTypedArg2(meta, entry, args[i]);
|
|
1532
1889
|
}
|
|
1533
1890
|
return result;
|
|
1534
1891
|
}
|
|
1535
|
-
function
|
|
1892
|
+
function normalizeValue2(lookup, entry, value) {
|
|
1536
1893
|
let resolved = entry;
|
|
1537
1894
|
while (resolved.type === "lookupEntry") {
|
|
1538
1895
|
resolved = resolved.value;
|
|
@@ -1552,7 +1909,7 @@ function normalizeValue(lookup, entry, value) {
|
|
|
1552
1909
|
normalizedInner = normalizedInner[0];
|
|
1553
1910
|
}
|
|
1554
1911
|
if (normalizedInner !== undefined && innerEntry.type !== "void") {
|
|
1555
|
-
normalizedInner =
|
|
1912
|
+
normalizedInner = normalizeValue2(lookup, innerEntry, normalizedInner);
|
|
1556
1913
|
}
|
|
1557
1914
|
return { type: enumValue.type, value: normalizedInner };
|
|
1558
1915
|
}
|
|
@@ -1565,7 +1922,7 @@ function normalizeValue(lookup, entry, value) {
|
|
|
1565
1922
|
const result = {};
|
|
1566
1923
|
for (const [key, val] of Object.entries(value)) {
|
|
1567
1924
|
if (key in fields) {
|
|
1568
|
-
result[key] =
|
|
1925
|
+
result[key] = normalizeValue2(lookup, fields[key], val);
|
|
1569
1926
|
} else {
|
|
1570
1927
|
result[key] = val;
|
|
1571
1928
|
}
|
|
@@ -1582,25 +1939,25 @@ function normalizeValue(lookup, entry, value) {
|
|
|
1582
1939
|
}
|
|
1583
1940
|
if (innerResolved.type === "primitive" && innerResolved.value === "u8" && typeof value === "string") {
|
|
1584
1941
|
if (/^0x[0-9a-fA-F]*$/.test(value))
|
|
1585
|
-
return
|
|
1586
|
-
return
|
|
1942
|
+
return Binary2.fromHex(value);
|
|
1943
|
+
return Binary2.fromText(value);
|
|
1587
1944
|
}
|
|
1588
1945
|
if (Array.isArray(value)) {
|
|
1589
1946
|
const innerEntry = resolved.value;
|
|
1590
|
-
return value.map((item) =>
|
|
1947
|
+
return value.map((item) => normalizeValue2(lookup, innerEntry, item));
|
|
1591
1948
|
}
|
|
1592
1949
|
return value;
|
|
1593
1950
|
}
|
|
1594
1951
|
case "tuple": {
|
|
1595
1952
|
if (Array.isArray(value)) {
|
|
1596
1953
|
const entries = resolved.value;
|
|
1597
|
-
return value.map((item, i) => i < entries.length ?
|
|
1954
|
+
return value.map((item, i) => i < entries.length ? normalizeValue2(lookup, entries[i], item) : item);
|
|
1598
1955
|
}
|
|
1599
1956
|
return value;
|
|
1600
1957
|
}
|
|
1601
1958
|
case "option": {
|
|
1602
1959
|
if (value !== null && value !== undefined) {
|
|
1603
|
-
return
|
|
1960
|
+
return normalizeValue2(lookup, resolved.value, value);
|
|
1604
1961
|
}
|
|
1605
1962
|
return;
|
|
1606
1963
|
}
|
|
@@ -1638,10 +1995,10 @@ function normalizeValue(lookup, entry, value) {
|
|
|
1638
1995
|
return value;
|
|
1639
1996
|
}
|
|
1640
1997
|
}
|
|
1641
|
-
function
|
|
1998
|
+
function parseTypedArg2(meta, entry, arg) {
|
|
1642
1999
|
switch (entry.type) {
|
|
1643
2000
|
case "primitive":
|
|
1644
|
-
return
|
|
2001
|
+
return parsePrimitive2(entry.value, arg);
|
|
1645
2002
|
case "compact":
|
|
1646
2003
|
return entry.isBig ? BigInt(arg) : parseInt(arg, 10);
|
|
1647
2004
|
case "AccountId32":
|
|
@@ -1651,16 +2008,16 @@ function parseTypedArg(meta, entry, arg) {
|
|
|
1651
2008
|
if (arg === "null" || arg === "undefined" || arg === "none") {
|
|
1652
2009
|
return;
|
|
1653
2010
|
}
|
|
1654
|
-
return
|
|
2011
|
+
return parseTypedArg2(meta, entry.value, arg);
|
|
1655
2012
|
}
|
|
1656
2013
|
case "enum": {
|
|
1657
2014
|
if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
|
|
1658
2015
|
const callCodec = meta.builder.buildDefinition(meta.lookup.call);
|
|
1659
|
-
return callCodec.dec(
|
|
2016
|
+
return callCodec.dec(Binary2.fromHex(arg).asBytes());
|
|
1660
2017
|
}
|
|
1661
2018
|
if (arg.startsWith("{")) {
|
|
1662
2019
|
try {
|
|
1663
|
-
return
|
|
2020
|
+
return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
|
|
1664
2021
|
} catch {}
|
|
1665
2022
|
}
|
|
1666
2023
|
const variants = Object.keys(entry.value);
|
|
@@ -1685,29 +2042,29 @@ function parseTypedArg(meta, entry, arg) {
|
|
|
1685
2042
|
const inner = entry.value;
|
|
1686
2043
|
if (inner.type === "primitive" && inner.value === "u8") {
|
|
1687
2044
|
if (/^0x[0-9a-fA-F]*$/.test(arg))
|
|
1688
|
-
return
|
|
1689
|
-
return
|
|
2045
|
+
return Binary2.fromHex(arg);
|
|
2046
|
+
return Binary2.fromText(arg);
|
|
1690
2047
|
}
|
|
1691
2048
|
if (arg.startsWith("[")) {
|
|
1692
2049
|
try {
|
|
1693
|
-
return
|
|
2050
|
+
return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
|
|
1694
2051
|
} catch {}
|
|
1695
2052
|
}
|
|
1696
2053
|
if (/^0x[0-9a-fA-F]*$/.test(arg))
|
|
1697
|
-
return
|
|
2054
|
+
return Binary2.fromHex(arg);
|
|
1698
2055
|
return parseValue(arg);
|
|
1699
2056
|
}
|
|
1700
2057
|
case "struct":
|
|
1701
2058
|
if (arg.startsWith("{")) {
|
|
1702
2059
|
try {
|
|
1703
|
-
return
|
|
2060
|
+
return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
|
|
1704
2061
|
} catch {}
|
|
1705
2062
|
}
|
|
1706
2063
|
return parseValue(arg);
|
|
1707
2064
|
case "tuple":
|
|
1708
2065
|
if (arg.startsWith("[")) {
|
|
1709
2066
|
try {
|
|
1710
|
-
return
|
|
2067
|
+
return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
|
|
1711
2068
|
} catch {}
|
|
1712
2069
|
}
|
|
1713
2070
|
return parseValue(arg);
|
|
@@ -1715,7 +2072,7 @@ function parseTypedArg(meta, entry, arg) {
|
|
|
1715
2072
|
return parseValue(arg);
|
|
1716
2073
|
}
|
|
1717
2074
|
}
|
|
1718
|
-
function
|
|
2075
|
+
function parsePrimitive2(prim, arg) {
|
|
1719
2076
|
switch (prim) {
|
|
1720
2077
|
case "bool":
|
|
1721
2078
|
return arg === "true";
|
|
@@ -1740,7 +2097,7 @@ function parsePrimitive(prim, arg) {
|
|
|
1740
2097
|
return parseValue(arg);
|
|
1741
2098
|
}
|
|
1742
2099
|
}
|
|
1743
|
-
var
|
|
2100
|
+
var PAPI_BUILTIN_EXTENSIONS2 = new Set([
|
|
1744
2101
|
"CheckNonZeroSender",
|
|
1745
2102
|
"CheckSpecVersion",
|
|
1746
2103
|
"CheckTxVersion",
|
|
@@ -1770,12 +2127,12 @@ function parseExtOption(ext) {
|
|
|
1770
2127
|
` + `Expected format: '{"ExtName":{"value":...}}'`);
|
|
1771
2128
|
}
|
|
1772
2129
|
}
|
|
1773
|
-
var
|
|
2130
|
+
var NO_DEFAULT2 = Symbol("no-default");
|
|
1774
2131
|
function buildCustomSignedExtensions(meta, userOverrides) {
|
|
1775
2132
|
const result = {};
|
|
1776
2133
|
const extensions = getSignedExtensions(meta);
|
|
1777
2134
|
for (const ext of extensions) {
|
|
1778
|
-
if (
|
|
2135
|
+
if (PAPI_BUILTIN_EXTENSIONS2.has(ext.identifier))
|
|
1779
2136
|
continue;
|
|
1780
2137
|
if (ext.identifier in userOverrides) {
|
|
1781
2138
|
result[ext.identifier] = userOverrides[ext.identifier];
|
|
@@ -1785,10 +2142,10 @@ function buildCustomSignedExtensions(meta, userOverrides) {
|
|
|
1785
2142
|
const addEntry = meta.lookup(ext.additionalSigned);
|
|
1786
2143
|
const value = autoDefaultForType(valueEntry);
|
|
1787
2144
|
const add = autoDefaultForType(addEntry);
|
|
1788
|
-
if (value !==
|
|
2145
|
+
if (value !== NO_DEFAULT2 || add !== NO_DEFAULT2) {
|
|
1789
2146
|
result[ext.identifier] = {
|
|
1790
|
-
...value !==
|
|
1791
|
-
...add !==
|
|
2147
|
+
...value !== NO_DEFAULT2 ? { value } : {},
|
|
2148
|
+
...add !== NO_DEFAULT2 ? { additionalSigned: add } : {}
|
|
1792
2149
|
};
|
|
1793
2150
|
}
|
|
1794
2151
|
}
|
|
@@ -1805,7 +2162,7 @@ function autoDefaultForType(entry) {
|
|
|
1805
2162
|
return { type: "Disabled", value: undefined };
|
|
1806
2163
|
}
|
|
1807
2164
|
}
|
|
1808
|
-
return
|
|
2165
|
+
return NO_DEFAULT2;
|
|
1809
2166
|
}
|
|
1810
2167
|
function watchTransaction(observable) {
|
|
1811
2168
|
const spinner = new Spinner;
|
|
@@ -1845,6 +2202,110 @@ function watchTransaction(observable) {
|
|
|
1845
2202
|
});
|
|
1846
2203
|
}
|
|
1847
2204
|
|
|
2205
|
+
// src/core/update-notifier.ts
|
|
2206
|
+
import { readFileSync } from "node:fs";
|
|
2207
|
+
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
2208
|
+
import { join as join3 } from "node:path";
|
|
2209
|
+
var CACHE_FILE = "update-check.json";
|
|
2210
|
+
var STALE_MS = 24 * 60 * 60 * 1000;
|
|
2211
|
+
var FETCH_TIMEOUT_MS = 5000;
|
|
2212
|
+
var REGISTRY_URL = "https://registry.npmjs.org/polkadot-cli/latest";
|
|
2213
|
+
function parseSemver(v) {
|
|
2214
|
+
const clean = v.replace(/^v/, "").split("-")[0] ?? v;
|
|
2215
|
+
const parts = clean.split(".").map(Number);
|
|
2216
|
+
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
2217
|
+
}
|
|
2218
|
+
function compareSemver(a, b) {
|
|
2219
|
+
const pa = parseSemver(a);
|
|
2220
|
+
const pb = parseSemver(b);
|
|
2221
|
+
for (let i = 0;i < 3; i++) {
|
|
2222
|
+
const left = pa[i] ?? 0;
|
|
2223
|
+
const right = pb[i] ?? 0;
|
|
2224
|
+
if (left < right)
|
|
2225
|
+
return -1;
|
|
2226
|
+
if (left > right)
|
|
2227
|
+
return 1;
|
|
2228
|
+
}
|
|
2229
|
+
return 0;
|
|
2230
|
+
}
|
|
2231
|
+
function isNewerVersion(current, latest) {
|
|
2232
|
+
return compareSemver(current, latest) < 0;
|
|
2233
|
+
}
|
|
2234
|
+
function buildNotificationBox(current, latest) {
|
|
2235
|
+
const YELLOW2 = "\x1B[33m";
|
|
2236
|
+
const GREEN2 = "\x1B[32m";
|
|
2237
|
+
const CYAN2 = "\x1B[36m";
|
|
2238
|
+
const RESET2 = "\x1B[0m";
|
|
2239
|
+
const BOLD2 = "\x1B[1m";
|
|
2240
|
+
const line1 = `Update available! ${YELLOW2}${current}${RESET2} → ${GREEN2}${BOLD2}${latest}${RESET2}`;
|
|
2241
|
+
const line2 = `Run ${CYAN2}npm i -g polkadot-cli${RESET2} to update`;
|
|
2242
|
+
const visibleLine1 = `Update available! ${current} → ${latest}`;
|
|
2243
|
+
const visibleLine2 = `Run npm i -g polkadot-cli to update`;
|
|
2244
|
+
const innerWidth = Math.max(visibleLine1.length, visibleLine2.length) + 4;
|
|
2245
|
+
const pad1 = " ".repeat(innerWidth - visibleLine1.length - 4);
|
|
2246
|
+
const pad2 = " ".repeat(innerWidth - visibleLine2.length - 4);
|
|
2247
|
+
const empty = " ".repeat(innerWidth);
|
|
2248
|
+
const top = `╭${"─".repeat(innerWidth)}╮`;
|
|
2249
|
+
const bot = `╰${"─".repeat(innerWidth)}╯`;
|
|
2250
|
+
return [
|
|
2251
|
+
top,
|
|
2252
|
+
`│${empty}│`,
|
|
2253
|
+
`│ ${line1}${pad1} │`,
|
|
2254
|
+
`│ ${line2}${pad2} │`,
|
|
2255
|
+
`│${empty}│`,
|
|
2256
|
+
bot
|
|
2257
|
+
].join(`
|
|
2258
|
+
`);
|
|
2259
|
+
}
|
|
2260
|
+
function getCachePath() {
|
|
2261
|
+
return join3(getConfigDir(), CACHE_FILE);
|
|
2262
|
+
}
|
|
2263
|
+
function readCache() {
|
|
2264
|
+
try {
|
|
2265
|
+
const data = readFileSync(getCachePath(), "utf-8");
|
|
2266
|
+
return JSON.parse(data);
|
|
2267
|
+
} catch {
|
|
2268
|
+
return null;
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
async function writeCache(cache) {
|
|
2272
|
+
try {
|
|
2273
|
+
await mkdir3(getConfigDir(), { recursive: true });
|
|
2274
|
+
await writeFile3(getCachePath(), `${JSON.stringify(cache)}
|
|
2275
|
+
`);
|
|
2276
|
+
} catch {}
|
|
2277
|
+
}
|
|
2278
|
+
function startBackgroundCheck(_currentVersion) {
|
|
2279
|
+
const cache = readCache();
|
|
2280
|
+
const now = Date.now();
|
|
2281
|
+
if (cache && now - cache.lastCheck < STALE_MS) {
|
|
2282
|
+
return;
|
|
2283
|
+
}
|
|
2284
|
+
fetch(REGISTRY_URL, {
|
|
2285
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
2286
|
+
}).then((res) => res.json()).then((data) => {
|
|
2287
|
+
const latestVersion = data.version;
|
|
2288
|
+
if (typeof latestVersion === "string") {
|
|
2289
|
+
writeCache({ lastCheck: now, latestVersion });
|
|
2290
|
+
}
|
|
2291
|
+
}).catch(() => {});
|
|
2292
|
+
}
|
|
2293
|
+
function getUpdateNotification(currentVersion) {
|
|
2294
|
+
if (process.env.DOT_NO_UPDATE_CHECK === "1")
|
|
2295
|
+
return null;
|
|
2296
|
+
if (process.env.CI)
|
|
2297
|
+
return null;
|
|
2298
|
+
if (!process.stderr.isTTY)
|
|
2299
|
+
return null;
|
|
2300
|
+
const cache = readCache();
|
|
2301
|
+
if (!cache)
|
|
2302
|
+
return null;
|
|
2303
|
+
if (isNewerVersion(currentVersion, cache.latestVersion)) {
|
|
2304
|
+
return buildNotificationBox(currentVersion, cache.latestVersion);
|
|
2305
|
+
}
|
|
2306
|
+
return null;
|
|
2307
|
+
}
|
|
2308
|
+
|
|
1848
2309
|
// src/utils/errors.ts
|
|
1849
2310
|
class CliError2 extends Error {
|
|
1850
2311
|
constructor(message) {
|
|
@@ -1854,6 +2315,7 @@ class CliError2 extends Error {
|
|
|
1854
2315
|
}
|
|
1855
2316
|
|
|
1856
2317
|
// src/cli.ts
|
|
2318
|
+
startBackgroundCheck(version);
|
|
1857
2319
|
var cli = cac("dot");
|
|
1858
2320
|
cli.option("--chain <name>", "Target chain (default from config)");
|
|
1859
2321
|
cli.option("--rpc <url>", "Override RPC endpoint for this call");
|
|
@@ -1870,6 +2332,13 @@ registerTxCommand(cli);
|
|
|
1870
2332
|
registerHashCommand(cli);
|
|
1871
2333
|
cli.help();
|
|
1872
2334
|
cli.version(version);
|
|
2335
|
+
function showUpdateAndExit(code) {
|
|
2336
|
+
const note = getUpdateNotification(version);
|
|
2337
|
+
if (note)
|
|
2338
|
+
process.stderr.write(`${note}
|
|
2339
|
+
`);
|
|
2340
|
+
process.exit(code);
|
|
2341
|
+
}
|
|
1873
2342
|
function handleError(err) {
|
|
1874
2343
|
if (err instanceof CliError2) {
|
|
1875
2344
|
console.error(`Error: ${err.message}`);
|
|
@@ -1878,16 +2347,19 @@ function handleError(err) {
|
|
|
1878
2347
|
} else {
|
|
1879
2348
|
console.error("An unexpected error occurred:", err);
|
|
1880
2349
|
}
|
|
1881
|
-
|
|
2350
|
+
showUpdateAndExit(1);
|
|
1882
2351
|
}
|
|
1883
2352
|
try {
|
|
1884
2353
|
cli.parse(process.argv, { run: false });
|
|
1885
|
-
if (
|
|
2354
|
+
if (cli.options.version || cli.options.help) {
|
|
2355
|
+
showUpdateAndExit(0);
|
|
2356
|
+
} else if (!cli.matchedCommandName) {
|
|
1886
2357
|
cli.outputHelp();
|
|
2358
|
+
showUpdateAndExit(0);
|
|
1887
2359
|
} else {
|
|
1888
2360
|
const result = cli.runMatchedCommand();
|
|
1889
2361
|
if (result && typeof result.then === "function") {
|
|
1890
|
-
result.then(() =>
|
|
2362
|
+
result.then(() => showUpdateAndExit(0), handleError);
|
|
1891
2363
|
}
|
|
1892
2364
|
}
|
|
1893
2365
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polkadot-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "CLI tool for querying Polkadot-ecosystem on-chain state",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"lint": "biome check .",
|
|
19
19
|
"lint:fix": "biome check --write .",
|
|
20
20
|
"test": "bun test",
|
|
21
|
+
"test:coverage": "bun test --coverage --coverage-reporter=lcov --coverage-dir=coverage",
|
|
21
22
|
"prepare": "husky",
|
|
22
23
|
"changeset": "changeset",
|
|
23
24
|
"version": "changeset version",
|