polkadot-cli 1.8.0 → 1.9.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 +30 -1
- package/dist/cli.mjs +55 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/polkadot-cli)
|
|
1
2
|
[](https://codecov.io/gh/peetzweg/polkadot-cli)
|
|
2
3
|
|
|
3
4
|
# polkadot-cli
|
|
@@ -309,12 +310,32 @@ dot query System.Number --output json | jq '.+1'
|
|
|
309
310
|
dot query kusama.System.Account 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
|
|
310
311
|
```
|
|
311
312
|
|
|
313
|
+
#### Partial key queries
|
|
314
|
+
|
|
315
|
+
For storage maps with multiple keys (NMaps), you can provide fewer keys than
|
|
316
|
+
expected to retrieve all entries matching that prefix. This uses the chain's
|
|
317
|
+
prefix-based iteration and does not require `--dump`.
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
# Full key — returns a single value
|
|
321
|
+
dot query Staking.ErasStakers 100 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
|
|
322
|
+
|
|
323
|
+
# Partial key — returns all entries matching the first key
|
|
324
|
+
dot query Staking.ErasStakers 100
|
|
325
|
+
|
|
326
|
+
# No keys — requires --dump (safety net for large maps)
|
|
327
|
+
dot query Staking.ErasStakers --dump --limit 10
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The `--limit` option applies to partial key results just like it does for
|
|
331
|
+
`--dump` (default: 100, use `--limit 0` for unlimited).
|
|
332
|
+
|
|
312
333
|
#### Output formatting
|
|
313
334
|
|
|
314
335
|
Query results automatically convert on-chain types for readability:
|
|
315
336
|
|
|
316
337
|
- **BigInt** values (e.g. balances) render as decimal strings
|
|
317
|
-
- **Binary** fields (e.g. token `name`, `symbol`) render as text when
|
|
338
|
+
- **Binary** fields (e.g. token `name`, `symbol`) render as text when the value contains only printable characters, or as `0x`-prefixed hex otherwise (values containing control characters, Private Use Area code points, or invalid UTF-8 sequences always fall back to hex)
|
|
318
339
|
- **Uint8Array** values render as `0x`-prefixed hex
|
|
319
340
|
|
|
320
341
|
```bash
|
|
@@ -773,6 +794,14 @@ tx:
|
|
|
773
794
|
value: ${AMOUNT}
|
|
774
795
|
```
|
|
775
796
|
|
|
797
|
+
Hex values passed via `--var` are preserved as-is, including leading zeros. This is important for encoded call data in XCM `Transact` instructions or similar byte-array fields:
|
|
798
|
+
|
|
799
|
+
```bash
|
|
800
|
+
# Encode a remark, then embed it in an XCM Transact via --var
|
|
801
|
+
CALL=$(dot tx.System.remark 0xdead --encode)
|
|
802
|
+
dot ./xcm-transact.yaml --var CALL=$CALL --encode
|
|
803
|
+
```
|
|
804
|
+
|
|
776
805
|
All existing flags work with file input — `--chain` overrides the file's `chain:` field, `--from`, `--dry-run`, `--encode`, `--yaml`, `--json`, `--output`, etc. behave identically to inline commands.
|
|
777
806
|
|
|
778
807
|
### Compute hashes
|
package/dist/cli.mjs
CHANGED
|
@@ -387,14 +387,35 @@ var init_accounts = __esm(() => {
|
|
|
387
387
|
DEV_NAMES = ["alice", "bob", "charlie", "dave", "eve", "ferdie"];
|
|
388
388
|
});
|
|
389
389
|
|
|
390
|
+
// src/utils/binary-display.ts
|
|
391
|
+
function isReadableText(text) {
|
|
392
|
+
for (let i = 0;i < text.length; i++) {
|
|
393
|
+
const code = text.charCodeAt(i);
|
|
394
|
+
if (code <= 31 && code !== 9 && code !== 10 && code !== 13)
|
|
395
|
+
return false;
|
|
396
|
+
if (code === 127)
|
|
397
|
+
return false;
|
|
398
|
+
if (code >= 128 && code <= 159)
|
|
399
|
+
return false;
|
|
400
|
+
if (code === 65533)
|
|
401
|
+
return false;
|
|
402
|
+
if (code >= 57344 && code <= 63743)
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
function binaryToDisplay(value) {
|
|
408
|
+
const text = value.asText();
|
|
409
|
+
return isReadableText(text) ? text : value.asHex();
|
|
410
|
+
}
|
|
411
|
+
|
|
390
412
|
// src/core/output.ts
|
|
391
413
|
import { Binary } from "polkadot-api";
|
|
392
414
|
function replacer(_key, value) {
|
|
393
415
|
if (typeof value === "bigint")
|
|
394
416
|
return value.toString();
|
|
395
417
|
if (value instanceof Binary) {
|
|
396
|
-
|
|
397
|
-
return text.includes("�") ? value.asHex() : text;
|
|
418
|
+
return binaryToDisplay(value);
|
|
398
419
|
}
|
|
399
420
|
if (value instanceof Uint8Array)
|
|
400
421
|
return `0x${Buffer.from(value).toString("hex")}`;
|
|
@@ -2158,7 +2179,7 @@ var init_complete = __esm(() => {
|
|
|
2158
2179
|
// src/cli.ts
|
|
2159
2180
|
import cac from "cac";
|
|
2160
2181
|
// package.json
|
|
2161
|
-
var version = "1.
|
|
2182
|
+
var version = "1.9.0";
|
|
2162
2183
|
|
|
2163
2184
|
// src/commands/account.ts
|
|
2164
2185
|
init_accounts_store();
|
|
@@ -3440,6 +3461,7 @@ async function showItemHelp2(category, target, opts) {
|
|
|
3440
3461
|
init_hash();
|
|
3441
3462
|
init_output();
|
|
3442
3463
|
init_errors();
|
|
3464
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
3443
3465
|
async function resolveInput(data, opts) {
|
|
3444
3466
|
const sources = [data !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
|
|
3445
3467
|
if (sources > 1) {
|
|
@@ -3449,26 +3471,15 @@ async function resolveInput(data, opts) {
|
|
|
3449
3471
|
throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
|
|
3450
3472
|
}
|
|
3451
3473
|
if (opts.file) {
|
|
3452
|
-
const buf = await
|
|
3474
|
+
const buf = await readFile3(opts.file);
|
|
3453
3475
|
return new Uint8Array(buf);
|
|
3454
3476
|
}
|
|
3455
3477
|
if (opts.stdin) {
|
|
3456
|
-
const reader = Bun.stdin.stream().getReader();
|
|
3457
3478
|
const chunks = [];
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
if (done)
|
|
3461
|
-
break;
|
|
3462
|
-
chunks.push(value);
|
|
3479
|
+
for await (const chunk of process.stdin) {
|
|
3480
|
+
chunks.push(chunk);
|
|
3463
3481
|
}
|
|
3464
|
-
|
|
3465
|
-
const result = new Uint8Array(totalLen);
|
|
3466
|
-
let offset = 0;
|
|
3467
|
-
for (const chunk of chunks) {
|
|
3468
|
-
result.set(chunk, offset);
|
|
3469
|
-
offset += chunk.length;
|
|
3470
|
-
}
|
|
3471
|
-
return result;
|
|
3482
|
+
return new Uint8Array(Buffer.concat(chunks));
|
|
3472
3483
|
}
|
|
3473
3484
|
return parseInputData(data);
|
|
3474
3485
|
}
|
|
@@ -3937,14 +3948,15 @@ async function handleQuery(target, keys, opts) {
|
|
|
3937
3948
|
];
|
|
3938
3949
|
const parsedKeys = await parseStorageKeys(meta, palletInfo.name, storageItem, effectiveKeys);
|
|
3939
3950
|
const format = opts.output ?? "pretty";
|
|
3940
|
-
|
|
3941
|
-
|
|
3951
|
+
const expectedLen = storageItem.type === "map" && storageItem.keyTypeId != null ? meta.builder.buildStorage(palletInfo.name, storageItem.name).len : 0;
|
|
3952
|
+
if (storageItem.type === "map" && parsedKeys.length < expectedLen) {
|
|
3953
|
+
if (parsedKeys.length === 0 && !opts.dump) {
|
|
3942
3954
|
clientHandle.destroy();
|
|
3943
3955
|
await showItemHelp("query", target, { chain: opts.chain, rpc: opts.rpc });
|
|
3944
3956
|
console.log(`${DIM}Hint: use --dump to fetch all entries${RESET}`);
|
|
3945
3957
|
return;
|
|
3946
3958
|
}
|
|
3947
|
-
const entries = await storageApi.getEntries();
|
|
3959
|
+
const entries = await storageApi.getEntries(...parsedKeys);
|
|
3948
3960
|
const limit = Number(opts.limit);
|
|
3949
3961
|
const truncated = limit > 0 && entries.length > limit;
|
|
3950
3962
|
const display = truncated ? entries.slice(0, limit) : entries;
|
|
@@ -3988,18 +4000,18 @@ async function parseStorageKeys(meta, palletName2, storageItem, args) {
|
|
|
3988
4000
|
throw new Error(`${palletName2}.${storageItem.name} key expects ${typeDesc}
|
|
3989
4001
|
` + ` Pass 1 argument. Got ${args.length}.`);
|
|
3990
4002
|
}
|
|
3991
|
-
if (args.length
|
|
4003
|
+
if (args.length > len) {
|
|
3992
4004
|
let typeDesc;
|
|
3993
4005
|
if (keyEntry.type === "tuple") {
|
|
3994
4006
|
typeDesc = keyEntry.value.map((e) => describeType(meta.lookup, e.id)).join(", ");
|
|
3995
4007
|
} else {
|
|
3996
4008
|
typeDesc = describeType(meta.lookup, storageItem.keyTypeId);
|
|
3997
4009
|
}
|
|
3998
|
-
throw new Error(`${palletName2}.${storageItem.name} expects ${len} key arg(s): (${typeDesc}). Got ${args.length}.`);
|
|
4010
|
+
throw new Error(`${palletName2}.${storageItem.name} expects at most ${len} key arg(s): (${typeDesc}). Got ${args.length}.`);
|
|
3999
4011
|
}
|
|
4000
4012
|
if (keyEntry.type === "tuple") {
|
|
4001
4013
|
const entries = keyEntry.value;
|
|
4002
|
-
return Promise.all(entries.map((entry, i) => parseTypedArg(meta, entry, args[i])));
|
|
4014
|
+
return Promise.all(entries.slice(0, args.length).map((entry, i) => parseTypedArg(meta, entry, args[i])));
|
|
4003
4015
|
}
|
|
4004
4016
|
return Promise.all(args.map((arg) => parseTypedArg(meta, keyEntry, arg)));
|
|
4005
4017
|
}
|
|
@@ -4462,8 +4474,7 @@ function formatEventValue(v) {
|
|
|
4462
4474
|
if (v === null || v === undefined)
|
|
4463
4475
|
return "null";
|
|
4464
4476
|
if (v instanceof Binary3) {
|
|
4465
|
-
|
|
4466
|
-
return text.includes("�") ? v.asHex() : text;
|
|
4477
|
+
return binaryToDisplay(v);
|
|
4467
4478
|
}
|
|
4468
4479
|
return JSON.stringify(v, (_k, val) => typeof val === "bigint" ? val.toString() : val);
|
|
4469
4480
|
}
|
|
@@ -4958,7 +4969,7 @@ function watchTransaction(observable, level) {
|
|
|
4958
4969
|
|
|
4959
4970
|
// src/config/store.ts
|
|
4960
4971
|
init_types();
|
|
4961
|
-
import { access as access3, mkdir as mkdir3, readFile as
|
|
4972
|
+
import { access as access3, mkdir as mkdir3, readFile as readFile4, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
|
|
4962
4973
|
import { homedir as homedir2 } from "node:os";
|
|
4963
4974
|
import { join as join3 } from "node:path";
|
|
4964
4975
|
var DOT_DIR2 = join3(homedir2(), ".polkadot");
|
|
@@ -4978,7 +4989,7 @@ async function fileExists3(path) {
|
|
|
4978
4989
|
async function loadConfig2() {
|
|
4979
4990
|
await ensureDir3(DOT_DIR2);
|
|
4980
4991
|
if (await fileExists3(CONFIG_PATH2)) {
|
|
4981
|
-
const saved = JSON.parse(await
|
|
4992
|
+
const saved = JSON.parse(await readFile4(CONFIG_PATH2, "utf-8"));
|
|
4982
4993
|
return {
|
|
4983
4994
|
...saved,
|
|
4984
4995
|
chains: { ...DEFAULT_CONFIG.chains, ...saved.chains }
|
|
@@ -4995,6 +5006,7 @@ async function saveConfig2(config) {
|
|
|
4995
5006
|
|
|
4996
5007
|
// src/core/file-loader.ts
|
|
4997
5008
|
init_errors();
|
|
5009
|
+
import { access as access4, readFile as readFile5 } from "node:fs/promises";
|
|
4998
5010
|
import { parse as parseYaml } from "yaml";
|
|
4999
5011
|
var CATEGORIES = ["tx", "query", "const", "apis"];
|
|
5000
5012
|
var FILE_EXTENSIONS = [".json", ".yaml", ".yml"];
|
|
@@ -5042,16 +5054,24 @@ function substituteVars(text, vars) {
|
|
|
5042
5054
|
return envVal;
|
|
5043
5055
|
if (defaultValue !== undefined)
|
|
5044
5056
|
return defaultValue;
|
|
5045
|
-
throw new CliError(`Undefined variable "\${${varName}}" in file.
|
|
5057
|
+
throw new CliError(`Undefined variable "\${${varName}}" in file.
|
|
5058
|
+
|
|
5059
|
+
` + ` Provide it using one of:
|
|
5060
|
+
` + ` --var ${varName}=VALUE
|
|
5061
|
+
` + ` ${varName}=VALUE dot ... (environment variable)
|
|
5062
|
+
` + ` \${${varName}:-default} (inline default in file)`);
|
|
5046
5063
|
});
|
|
5047
5064
|
}
|
|
5065
|
+
function quoteYamlHexValues(text) {
|
|
5066
|
+
return text.replace(/^(\s*(?:[^:]+:\s+|-\s+))(0x[0-9a-fA-F]+)\s*$/gm, '$1"$2"');
|
|
5067
|
+
}
|
|
5048
5068
|
async function loadCommandFile(filePath, cliVars) {
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5069
|
+
try {
|
|
5070
|
+
await access4(filePath);
|
|
5071
|
+
} catch {
|
|
5052
5072
|
throw new CliError(`File not found: ${filePath}`);
|
|
5053
5073
|
}
|
|
5054
|
-
const rawText = await
|
|
5074
|
+
const rawText = await readFile5(filePath, "utf-8");
|
|
5055
5075
|
if (!rawText.trim()) {
|
|
5056
5076
|
throw new CliError(`File is empty: ${filePath}`);
|
|
5057
5077
|
}
|
|
@@ -5070,9 +5090,10 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
5070
5090
|
} catch {}
|
|
5071
5091
|
const mergedVars = { ...fileVars, ...cliVars };
|
|
5072
5092
|
const substituted = substituteVars(rawText, mergedVars);
|
|
5093
|
+
const textToParse = isJson ? substituted : quoteYamlHexValues(substituted);
|
|
5073
5094
|
let parsed;
|
|
5074
5095
|
try {
|
|
5075
|
-
parsed = isJson ? JSON.parse(
|
|
5096
|
+
parsed = isJson ? JSON.parse(textToParse) : parseYaml(textToParse);
|
|
5076
5097
|
} catch (err) {
|
|
5077
5098
|
const format = isJson ? "JSON" : "YAML";
|
|
5078
5099
|
const msg = err instanceof Error ? err.message : String(err);
|