polkadot-cli 1.6.1 → 1.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 +229 -0
- package/dist/cli.mjs +438 -23
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ Ships with Polkadot and all system parachains preconfigured with multiple fallba
|
|
|
20
20
|
- ✅ Named address resolution across all commands
|
|
21
21
|
- ✅ Runtime API calls — `dot apis.Core.version`
|
|
22
22
|
- ✅ Batteries included — all system parachains and testnets already setup to be used
|
|
23
|
+
- ✅ File-based commands — run any command from a YAML/JSON file with variable substitution
|
|
24
|
+
- ✅ Parachain sovereign accounts — derive child and sibling addresses from a parachain ID
|
|
23
25
|
|
|
24
26
|
### Preconfigured chains
|
|
25
27
|
|
|
@@ -495,6 +497,28 @@ dot tx Balances.transfer_keep_alive 5FHneW46... 1000000000000 --encode
|
|
|
495
497
|
dot tx Sudo.sudo $(dot tx System.remark 0xcafe --encode) --from alice
|
|
496
498
|
```
|
|
497
499
|
|
|
500
|
+
#### Decode call data to YAML / JSON
|
|
501
|
+
|
|
502
|
+
Decode a hex-encoded call into a YAML or JSON file that is compatible with [file-based commands](#file-based-commands). This is useful for inspecting opaque call data, sharing human-readable transaction definitions, or editing parameters before re-submitting. Works offline from cached metadata and does not require `--from`.
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
# Decode a raw hex call to YAML
|
|
506
|
+
dot tx.0x0001076465616462656566 --yaml
|
|
507
|
+
|
|
508
|
+
# Decode a raw hex call to JSON
|
|
509
|
+
dot tx.0x0001076465616462656566 --json
|
|
510
|
+
|
|
511
|
+
# Encode a named call and output as YAML
|
|
512
|
+
dot tx.System.remark 0xdeadbeef --yaml
|
|
513
|
+
|
|
514
|
+
# Round-trip: encode to hex, decode to YAML, re-encode from file
|
|
515
|
+
dot tx.System.remark 0xdeadbeef --encode # 0x0001076465616462656566
|
|
516
|
+
dot tx.0x0001076465616462656566 --yaml > remark.yaml
|
|
517
|
+
dot ./remark.yaml --encode # same hex
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
`--yaml` / `--json` are mutually exclusive with each other and with `--encode` and `--dry-run`.
|
|
521
|
+
|
|
498
522
|
Both dry-run and submission display the encoded call hex and a decoded human-readable form:
|
|
499
523
|
|
|
500
524
|
```
|
|
@@ -572,6 +596,185 @@ For manual override, use `--ext` with a JSON object:
|
|
|
572
596
|
dot tx System.remark 0xdeadbeef --from alice --ext '{"MyExtension":{"value":"..."}}'
|
|
573
597
|
```
|
|
574
598
|
|
|
599
|
+
### File-based commands
|
|
600
|
+
|
|
601
|
+
Run any `dot` command from a YAML or JSON file. Especially useful for complex calls like XCM messages that are hard to construct inline.
|
|
602
|
+
|
|
603
|
+
**Teleport DOT** from Asset Hub to the relay chain:
|
|
604
|
+
|
|
605
|
+
```yaml
|
|
606
|
+
# teleport-dot.xcm.yaml
|
|
607
|
+
chain: polkadot-asset-hub
|
|
608
|
+
tx:
|
|
609
|
+
PolkadotXcm:
|
|
610
|
+
limited_teleport_assets:
|
|
611
|
+
dest:
|
|
612
|
+
type: V4
|
|
613
|
+
value:
|
|
614
|
+
parents: 1
|
|
615
|
+
interior:
|
|
616
|
+
type: Here
|
|
617
|
+
beneficiary:
|
|
618
|
+
type: V4
|
|
619
|
+
value:
|
|
620
|
+
parents: 0
|
|
621
|
+
interior:
|
|
622
|
+
type: X1
|
|
623
|
+
value:
|
|
624
|
+
- type: AccountId32
|
|
625
|
+
value:
|
|
626
|
+
network: null
|
|
627
|
+
id: "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
|
|
628
|
+
assets:
|
|
629
|
+
type: V4
|
|
630
|
+
value:
|
|
631
|
+
- id:
|
|
632
|
+
parents: 1
|
|
633
|
+
interior:
|
|
634
|
+
type: Here
|
|
635
|
+
fun:
|
|
636
|
+
type: Fungible
|
|
637
|
+
value: 10000000000
|
|
638
|
+
fee_asset_item: 0
|
|
639
|
+
weight_limit:
|
|
640
|
+
type: Unlimited
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
**Reserve transfer USDC** (asset 1337, 6 decimals) from Asset Hub to Hydration:
|
|
644
|
+
|
|
645
|
+
```yaml
|
|
646
|
+
# reserve-transfer-usdc.xcm.yaml
|
|
647
|
+
chain: polkadot-asset-hub
|
|
648
|
+
tx:
|
|
649
|
+
PolkadotXcm:
|
|
650
|
+
limited_reserve_transfer_assets:
|
|
651
|
+
dest:
|
|
652
|
+
type: V4
|
|
653
|
+
value:
|
|
654
|
+
parents: 1
|
|
655
|
+
interior:
|
|
656
|
+
type: X1
|
|
657
|
+
value:
|
|
658
|
+
- type: Parachain
|
|
659
|
+
value: 2034
|
|
660
|
+
beneficiary:
|
|
661
|
+
type: V4
|
|
662
|
+
value:
|
|
663
|
+
parents: 0
|
|
664
|
+
interior:
|
|
665
|
+
type: X1
|
|
666
|
+
value:
|
|
667
|
+
- type: AccountId32
|
|
668
|
+
value:
|
|
669
|
+
network: null
|
|
670
|
+
id: "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
|
|
671
|
+
assets:
|
|
672
|
+
type: V4
|
|
673
|
+
value:
|
|
674
|
+
- id:
|
|
675
|
+
parents: 0
|
|
676
|
+
interior:
|
|
677
|
+
type: X2
|
|
678
|
+
value:
|
|
679
|
+
- type: PalletInstance
|
|
680
|
+
value: 50
|
|
681
|
+
- type: GeneralIndex
|
|
682
|
+
value: 1337
|
|
683
|
+
fun:
|
|
684
|
+
type: Fungible
|
|
685
|
+
value: 10000000
|
|
686
|
+
fee_asset_item: 0
|
|
687
|
+
weight_limit:
|
|
688
|
+
type: Unlimited
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
The same teleport in JSON:
|
|
692
|
+
|
|
693
|
+
```json
|
|
694
|
+
{
|
|
695
|
+
"chain": "polkadot-asset-hub",
|
|
696
|
+
"tx": {
|
|
697
|
+
"PolkadotXcm": {
|
|
698
|
+
"limited_teleport_assets": {
|
|
699
|
+
"dest": { "type": "V4", "value": { "parents": 1, "interior": { "type": "Here" } } },
|
|
700
|
+
"beneficiary": {
|
|
701
|
+
"type": "V4",
|
|
702
|
+
"value": {
|
|
703
|
+
"parents": 0,
|
|
704
|
+
"interior": {
|
|
705
|
+
"type": "X1",
|
|
706
|
+
"value": [{ "type": "AccountId32", "value": { "network": null, "id": "0xd435...a27d" } }]
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
"assets": {
|
|
711
|
+
"type": "V4",
|
|
712
|
+
"value": [{
|
|
713
|
+
"id": { "parents": 1, "interior": { "type": "Here" } },
|
|
714
|
+
"fun": { "type": "Fungible", "value": 10000000000 }
|
|
715
|
+
}]
|
|
716
|
+
},
|
|
717
|
+
"fee_asset_item": 0,
|
|
718
|
+
"weight_limit": { "type": "Unlimited" }
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
```bash
|
|
726
|
+
# Run from file
|
|
727
|
+
dot ./teleport-dot.xcm.yaml --from alice --dry-run
|
|
728
|
+
|
|
729
|
+
# Encode only
|
|
730
|
+
dot ./reserve-transfer-usdc.xcm.yaml --encode
|
|
731
|
+
|
|
732
|
+
# Override variables
|
|
733
|
+
dot ./transfer.xcm.yaml --var AMOUNT=2000000000000 --from alice
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
The file format uses a required category wrapper (`tx`, `query`, `const`, or `apis`) with the structure `category > Pallet > Item > args`:
|
|
737
|
+
|
|
738
|
+
```yaml
|
|
739
|
+
# Simple transaction
|
|
740
|
+
tx:
|
|
741
|
+
System:
|
|
742
|
+
remark:
|
|
743
|
+
- "0xdeadbeef"
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
```yaml
|
|
747
|
+
# Storage query
|
|
748
|
+
chain: polkadot
|
|
749
|
+
query:
|
|
750
|
+
System:
|
|
751
|
+
Account:
|
|
752
|
+
- "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
```yaml
|
|
756
|
+
# Constant lookup
|
|
757
|
+
chain: polkadot
|
|
758
|
+
const:
|
|
759
|
+
Balances:
|
|
760
|
+
ExistentialDeposit:
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
**Variable substitution** uses shell-style `${VAR}` with optional defaults `${VAR:-default}`. Variables are resolved in order: `--var` flags > environment variables > `vars:` section defaults.
|
|
764
|
+
|
|
765
|
+
```yaml
|
|
766
|
+
chain: ${CHAIN:-polkadot}
|
|
767
|
+
vars:
|
|
768
|
+
AMOUNT: "1000000000000"
|
|
769
|
+
tx:
|
|
770
|
+
Balances:
|
|
771
|
+
transfer_keep_alive:
|
|
772
|
+
dest: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
|
|
773
|
+
value: ${AMOUNT}
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
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
|
+
|
|
575
778
|
### Compute hashes
|
|
576
779
|
|
|
577
780
|
Compute cryptographic hashes commonly used in Substrate. Supports BLAKE2b-256, BLAKE2b-128, Keccak-256, and SHA-256.
|
|
@@ -595,6 +798,32 @@ dot hash blake2b256 0xdeadbeef --output json
|
|
|
595
798
|
|
|
596
799
|
Run `dot hash` with no arguments to see all available algorithms.
|
|
597
800
|
|
|
801
|
+
### Parachain sovereign accounts
|
|
802
|
+
|
|
803
|
+
Derive the sovereign account addresses for a parachain. These are deterministic accounts derived from a parachain ID — no chain connection required.
|
|
804
|
+
|
|
805
|
+
- **Child** accounts represent a parachain on the relay chain (prefix `"para"`)
|
|
806
|
+
- **Sibling** accounts represent a parachain on another parachain (prefix `"sibl"`)
|
|
807
|
+
|
|
808
|
+
```bash
|
|
809
|
+
# Show both child and sibling accounts
|
|
810
|
+
dot parachain 1000
|
|
811
|
+
|
|
812
|
+
# Show only the child (relay chain) account
|
|
813
|
+
dot parachain 2004 --type child
|
|
814
|
+
|
|
815
|
+
# Show only the sibling (parachain-to-parachain) account
|
|
816
|
+
dot parachain 2004 --type sibling
|
|
817
|
+
|
|
818
|
+
# Use Polkadot SS58 prefix (default: 42)
|
|
819
|
+
dot parachain 1000 --prefix 0
|
|
820
|
+
|
|
821
|
+
# JSON output
|
|
822
|
+
dot parachain 1000 --output json
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
Run `dot parachain` with no arguments to see usage and examples.
|
|
826
|
+
|
|
598
827
|
### Getting help
|
|
599
828
|
|
|
600
829
|
Every command supports `--help` to show its detailed usage, available actions, and examples:
|
package/dist/cli.mjs
CHANGED
|
@@ -966,6 +966,7 @@ function parseValue(arg) {
|
|
|
966
966
|
// src/commands/tx.ts
|
|
967
967
|
import { getViewBuilder } from "@polkadot-api/view-builder";
|
|
968
968
|
import { Binary as Binary2 } from "polkadot-api";
|
|
969
|
+
import { stringify as stringifyYaml } from "yaml";
|
|
969
970
|
async function parseStructArgs(meta, fields, args, callLabel) {
|
|
970
971
|
const fieldNames = Object.keys(fields);
|
|
971
972
|
if (args.length !== fieldNames.length) {
|
|
@@ -1080,8 +1081,8 @@ function normalizeValue(lookup, entry, value) {
|
|
|
1080
1081
|
return;
|
|
1081
1082
|
}
|
|
1082
1083
|
case "primitive": {
|
|
1084
|
+
const prim = resolved.value;
|
|
1083
1085
|
if (typeof value === "string") {
|
|
1084
|
-
const prim = resolved.value;
|
|
1085
1086
|
switch (prim) {
|
|
1086
1087
|
case "bool":
|
|
1087
1088
|
return value === "true";
|
|
@@ -1101,12 +1102,26 @@ function normalizeValue(lookup, entry, value) {
|
|
|
1101
1102
|
return parseInt(value, 10);
|
|
1102
1103
|
}
|
|
1103
1104
|
}
|
|
1105
|
+
if (typeof value === "number") {
|
|
1106
|
+
switch (prim) {
|
|
1107
|
+
case "u64":
|
|
1108
|
+
case "u128":
|
|
1109
|
+
case "u256":
|
|
1110
|
+
case "i64":
|
|
1111
|
+
case "i128":
|
|
1112
|
+
case "i256":
|
|
1113
|
+
return BigInt(value);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1104
1116
|
return value;
|
|
1105
1117
|
}
|
|
1106
1118
|
case "compact": {
|
|
1107
1119
|
if (typeof value === "string") {
|
|
1108
1120
|
return resolved.isBig ? BigInt(value) : parseInt(value, 10);
|
|
1109
1121
|
}
|
|
1122
|
+
if (typeof value === "number" && resolved.isBig) {
|
|
1123
|
+
return BigInt(value);
|
|
1124
|
+
}
|
|
1110
1125
|
return value;
|
|
1111
1126
|
}
|
|
1112
1127
|
default:
|
|
@@ -1330,7 +1345,10 @@ async function handleApis(target, args, opts) {
|
|
|
1330
1345
|
const names = api.methods.map((m) => m.name);
|
|
1331
1346
|
throw new Error(suggestMessage(`method in ${api.name}`, methodName, names));
|
|
1332
1347
|
}
|
|
1333
|
-
const
|
|
1348
|
+
const effectiveArgs = args.length > 0 || opts.parsedArgs == null ? args : Array.isArray(opts.parsedArgs) ? opts.parsedArgs.map((v) => typeof v === "object" ? JSON.stringify(v) : String(v)) : [
|
|
1349
|
+
typeof opts.parsedArgs === "object" ? JSON.stringify(opts.parsedArgs) : String(opts.parsedArgs)
|
|
1350
|
+
];
|
|
1351
|
+
const parsedArgs = await parseRuntimeApiArgs(meta, method, effectiveArgs);
|
|
1334
1352
|
const unsafeApi = clientHandle.client.getUnsafeApi();
|
|
1335
1353
|
const result = await unsafeApi.apis[api.name][method.name](...parsedArgs);
|
|
1336
1354
|
const format = opts.output ?? "pretty";
|
|
@@ -1950,6 +1968,12 @@ async function generateCompletions(currentWord, precedingWords) {
|
|
|
1950
1968
|
if (firstArg === "hash") {
|
|
1951
1969
|
return filterPrefix(getAlgorithmNames(), currentWord);
|
|
1952
1970
|
}
|
|
1971
|
+
if (firstArg === "parachain") {
|
|
1972
|
+
if (prevWord === "--type") {
|
|
1973
|
+
return filterPrefix(["child", "sibling"], currentWord);
|
|
1974
|
+
}
|
|
1975
|
+
return [];
|
|
1976
|
+
}
|
|
1953
1977
|
return completeDotpath(currentWord, config, knownChains, precedingWords);
|
|
1954
1978
|
}
|
|
1955
1979
|
function detectCategory(words, _knownChains) {
|
|
@@ -1973,7 +1997,7 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
1973
1997
|
const numComplete = completeSegments.length;
|
|
1974
1998
|
if (numComplete === 0 && !endsWithDot) {
|
|
1975
1999
|
const candidates = [
|
|
1976
|
-
...
|
|
2000
|
+
...CATEGORIES2.map((c) => `${c}.`),
|
|
1977
2001
|
...knownChains.map((c) => `${c}.`),
|
|
1978
2002
|
...NAMED_COMMANDS
|
|
1979
2003
|
];
|
|
@@ -2024,11 +2048,11 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2024
2048
|
if (firstIsChain) {
|
|
2025
2049
|
const chainName = first;
|
|
2026
2050
|
if (numComplete === 1 && endsWithDot) {
|
|
2027
|
-
const candidates =
|
|
2051
|
+
const candidates = CATEGORIES2.map((c) => `${first}.${c}.`);
|
|
2028
2052
|
return filterPrefix(candidates, currentWord.slice(0, -1));
|
|
2029
2053
|
}
|
|
2030
2054
|
if (numComplete === 1 && !endsWithDot) {
|
|
2031
|
-
const candidates =
|
|
2055
|
+
const candidates = CATEGORIES2.map((c) => `${first}.${c}.`);
|
|
2032
2056
|
return filterPrefix(candidates, currentWord);
|
|
2033
2057
|
}
|
|
2034
2058
|
if (numComplete === 2) {
|
|
@@ -2092,14 +2116,14 @@ async function completeApisCategory(prefix, numComplete, endsWithDot, segments,
|
|
|
2092
2116
|
}
|
|
2093
2117
|
return [];
|
|
2094
2118
|
}
|
|
2095
|
-
var
|
|
2119
|
+
var CATEGORIES2, CATEGORY_ALIASES2, NAMED_COMMANDS, CHAIN_SUBCOMMANDS, ACCOUNT_SUBCOMMANDS, GLOBAL_OPTIONS, TX_OPTIONS, QUERY_OPTIONS;
|
|
2096
2120
|
var init_complete = __esm(() => {
|
|
2097
2121
|
init_accounts_store();
|
|
2098
2122
|
init_store();
|
|
2099
2123
|
init_accounts();
|
|
2100
2124
|
init_hash();
|
|
2101
2125
|
init_metadata();
|
|
2102
|
-
|
|
2126
|
+
CATEGORIES2 = ["query", "tx", "const", "events", "errors", "apis"];
|
|
2103
2127
|
CATEGORY_ALIASES2 = {
|
|
2104
2128
|
query: "query",
|
|
2105
2129
|
tx: "tx",
|
|
@@ -2113,7 +2137,7 @@ var init_complete = __esm(() => {
|
|
|
2113
2137
|
apis: "apis",
|
|
2114
2138
|
api: "apis"
|
|
2115
2139
|
};
|
|
2116
|
-
NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "completions"];
|
|
2140
|
+
NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "parachain", "completions"];
|
|
2117
2141
|
CHAIN_SUBCOMMANDS = ["add", "remove", "update", "list", "default"];
|
|
2118
2142
|
ACCOUNT_SUBCOMMANDS = [
|
|
2119
2143
|
"add",
|
|
@@ -2134,7 +2158,7 @@ var init_complete = __esm(() => {
|
|
|
2134
2158
|
// src/cli.ts
|
|
2135
2159
|
import cac from "cac";
|
|
2136
2160
|
// package.json
|
|
2137
|
-
var version = "1.
|
|
2161
|
+
var version = "1.8.0";
|
|
2138
2162
|
|
|
2139
2163
|
// src/commands/account.ts
|
|
2140
2164
|
init_accounts_store();
|
|
@@ -2621,7 +2645,10 @@ async function handleApis2(target, args, opts) {
|
|
|
2621
2645
|
const names = api.methods.map((m) => m.name);
|
|
2622
2646
|
throw new Error(suggestMessage(`method in ${api.name}`, methodName, names));
|
|
2623
2647
|
}
|
|
2624
|
-
const
|
|
2648
|
+
const effectiveArgs = args.length > 0 || opts.parsedArgs == null ? args : Array.isArray(opts.parsedArgs) ? opts.parsedArgs.map((v) => typeof v === "object" ? JSON.stringify(v) : String(v)) : [
|
|
2649
|
+
typeof opts.parsedArgs === "object" ? JSON.stringify(opts.parsedArgs) : String(opts.parsedArgs)
|
|
2650
|
+
];
|
|
2651
|
+
const parsedArgs = await parseRuntimeApiArgs2(meta, method, effectiveArgs);
|
|
2625
2652
|
const unsafeApi = clientHandle.client.getUnsafeApi();
|
|
2626
2653
|
const result = await unsafeApi.apis[api.name][method.name](...parsedArgs);
|
|
2627
2654
|
const format = opts.output ?? "pretty";
|
|
@@ -3745,6 +3772,97 @@ function registerInspectCommand(cli) {
|
|
|
3745
3772
|
});
|
|
3746
3773
|
}
|
|
3747
3774
|
|
|
3775
|
+
// src/commands/parachain.ts
|
|
3776
|
+
init_accounts();
|
|
3777
|
+
init_output();
|
|
3778
|
+
|
|
3779
|
+
// src/core/parachain.ts
|
|
3780
|
+
var SOVEREIGN_ACCOUNT_TYPES = ["child", "sibling"];
|
|
3781
|
+
var PREFIXES = {
|
|
3782
|
+
child: new Uint8Array([112, 97, 114, 97]),
|
|
3783
|
+
sibling: new Uint8Array([115, 105, 98, 108])
|
|
3784
|
+
};
|
|
3785
|
+
function deriveSovereignAccount(paraId, type) {
|
|
3786
|
+
const result = new Uint8Array(32);
|
|
3787
|
+
result.set(PREFIXES[type], 0);
|
|
3788
|
+
new DataView(result.buffer).setUint32(4, paraId, true);
|
|
3789
|
+
return result;
|
|
3790
|
+
}
|
|
3791
|
+
function isValidParaId(value) {
|
|
3792
|
+
return Number.isInteger(value) && value >= 0 && value <= 4294967295;
|
|
3793
|
+
}
|
|
3794
|
+
|
|
3795
|
+
// src/commands/parachain.ts
|
|
3796
|
+
init_errors();
|
|
3797
|
+
function printParachainHelp() {
|
|
3798
|
+
console.log(`${BOLD}Usage:${RESET} dot parachain <paraId> [options]
|
|
3799
|
+
`);
|
|
3800
|
+
console.log(`${BOLD}Description:${RESET}`);
|
|
3801
|
+
console.log(` Derive sovereign account addresses for a parachain.
|
|
3802
|
+
`);
|
|
3803
|
+
console.log(` ${DIM}Child accounts represent a parachain on the relay chain.${RESET}`);
|
|
3804
|
+
console.log(` ${DIM}Sibling accounts represent a parachain on another parachain.${RESET}
|
|
3805
|
+
`);
|
|
3806
|
+
console.log(`${BOLD}Options:${RESET}`);
|
|
3807
|
+
console.log(` ${CYAN}--type <child|sibling>${RESET} ${DIM}Account type (default: both)${RESET}`);
|
|
3808
|
+
console.log(` ${CYAN}--prefix <N>${RESET} ${DIM}SS58 prefix (default: 42)${RESET}`);
|
|
3809
|
+
console.log(` ${CYAN}--output json${RESET} ${DIM}Output as JSON${RESET}`);
|
|
3810
|
+
console.log(`
|
|
3811
|
+
${BOLD}Examples:${RESET}`);
|
|
3812
|
+
console.log(` ${DIM}$ dot parachain 1000${RESET}`);
|
|
3813
|
+
console.log(` ${DIM}$ dot parachain 2004 --prefix 0${RESET}`);
|
|
3814
|
+
console.log(` ${DIM}$ dot parachain 1000 --type sibling${RESET}`);
|
|
3815
|
+
console.log(` ${DIM}$ dot parachain 2000 --output json${RESET}`);
|
|
3816
|
+
}
|
|
3817
|
+
function validateType(type) {
|
|
3818
|
+
const lower = type.toLowerCase();
|
|
3819
|
+
if (lower === "child" || lower === "sibling")
|
|
3820
|
+
return lower;
|
|
3821
|
+
throw new CliError(`Unknown account type "${type}". Valid types: child, sibling.`);
|
|
3822
|
+
}
|
|
3823
|
+
function registerParachainCommand(cli) {
|
|
3824
|
+
cli.command("parachain [paraId]", "Derive parachain sovereign accounts").option("--type <type>", "Account type: child, sibling (default: both)").option("--prefix <number>", "SS58 prefix for address encoding (default: 42)").action(async (paraIdStr, opts) => {
|
|
3825
|
+
if (!paraIdStr) {
|
|
3826
|
+
printParachainHelp();
|
|
3827
|
+
return;
|
|
3828
|
+
}
|
|
3829
|
+
const paraId = Number(paraIdStr);
|
|
3830
|
+
if (!isValidParaId(paraId)) {
|
|
3831
|
+
throw new CliError(`Invalid parachain ID "${paraIdStr}". Must be a non-negative integer (0 to 4294967295).`);
|
|
3832
|
+
}
|
|
3833
|
+
const prefix = opts.prefix != null ? Number(opts.prefix) : 42;
|
|
3834
|
+
if (Number.isNaN(prefix) || prefix < 0) {
|
|
3835
|
+
throw new CliError(`Invalid prefix "${opts.prefix}". Must be a non-negative integer.`);
|
|
3836
|
+
}
|
|
3837
|
+
const types = opts.type ? [validateType(opts.type)] : SOVEREIGN_ACCOUNT_TYPES;
|
|
3838
|
+
const format = opts.output ?? "pretty";
|
|
3839
|
+
if (format === "json") {
|
|
3840
|
+
const result = { paraId, prefix };
|
|
3841
|
+
for (const type of types) {
|
|
3842
|
+
const accountId = deriveSovereignAccount(paraId, type);
|
|
3843
|
+
result[type] = {
|
|
3844
|
+
publicKey: publicKeyToHex(accountId),
|
|
3845
|
+
ss58: toSs58(accountId, prefix)
|
|
3846
|
+
};
|
|
3847
|
+
}
|
|
3848
|
+
console.log(formatJson(result));
|
|
3849
|
+
} else {
|
|
3850
|
+
printHeading(`Parachain ${paraId} — Sovereign Accounts`);
|
|
3851
|
+
for (const type of types) {
|
|
3852
|
+
const accountId = deriveSovereignAccount(paraId, type);
|
|
3853
|
+
const hex = publicKeyToHex(accountId);
|
|
3854
|
+
const ss58 = toSs58(accountId, prefix);
|
|
3855
|
+
const label = type.charAt(0).toUpperCase() + type.slice(1);
|
|
3856
|
+
console.log(` ${BOLD}${label}:${RESET}`);
|
|
3857
|
+
console.log(` ${BOLD}Public Key:${RESET} ${hex}`);
|
|
3858
|
+
console.log(` ${BOLD}SS58:${RESET} ${ss58}`);
|
|
3859
|
+
console.log(` ${BOLD}Prefix:${RESET} ${prefix}`);
|
|
3860
|
+
console.log();
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3863
|
+
});
|
|
3864
|
+
}
|
|
3865
|
+
|
|
3748
3866
|
// src/commands/query.ts
|
|
3749
3867
|
init_store();
|
|
3750
3868
|
init_client();
|
|
@@ -3814,7 +3932,10 @@ async function handleQuery(target, keys, opts) {
|
|
|
3814
3932
|
}
|
|
3815
3933
|
const unsafeApi = clientHandle.client.getUnsafeApi();
|
|
3816
3934
|
const storageApi = unsafeApi.query[palletInfo.name][storageItem.name];
|
|
3817
|
-
const
|
|
3935
|
+
const effectiveKeys = keys.length > 0 || opts.parsedArgs == null ? keys : Array.isArray(opts.parsedArgs) ? opts.parsedArgs.map((v) => typeof v === "object" ? JSON.stringify(v) : String(v)) : [
|
|
3936
|
+
typeof opts.parsedArgs === "object" ? JSON.stringify(opts.parsedArgs) : String(opts.parsedArgs)
|
|
3937
|
+
];
|
|
3938
|
+
const parsedKeys = await parseStorageKeys(meta, palletInfo.name, storageItem, effectiveKeys);
|
|
3818
3939
|
const format = opts.output ?? "pretty";
|
|
3819
3940
|
if (storageItem.type === "map" && parsedKeys.length === 0) {
|
|
3820
3941
|
if (!opts.dump) {
|
|
@@ -3895,6 +4016,7 @@ init_errors();
|
|
|
3895
4016
|
init_focused_inspect();
|
|
3896
4017
|
import { getViewBuilder as getViewBuilder2 } from "@polkadot-api/view-builder";
|
|
3897
4018
|
import { Binary as Binary3 } from "polkadot-api";
|
|
4019
|
+
import { stringify as stringifyYaml2 } from "yaml";
|
|
3898
4020
|
function parseWaitLevel(raw) {
|
|
3899
4021
|
switch (raw) {
|
|
3900
4022
|
case "broadcast":
|
|
@@ -3945,7 +4067,7 @@ async function handleTx(target, args, opts) {
|
|
|
3945
4067
|
console.log();
|
|
3946
4068
|
return;
|
|
3947
4069
|
}
|
|
3948
|
-
if (!opts.from && !opts.encode) {
|
|
4070
|
+
if (!opts.from && !opts.encode && !opts.yaml && !opts.json) {
|
|
3949
4071
|
if (isRawCall) {
|
|
3950
4072
|
throw new Error("--from is required (or use --encode to output hex without signing)");
|
|
3951
4073
|
}
|
|
@@ -3958,6 +4080,15 @@ async function handleTx(target, args, opts) {
|
|
|
3958
4080
|
if (opts.encode && isRawCall) {
|
|
3959
4081
|
throw new Error("--encode cannot be used with raw call hex (already encoded)");
|
|
3960
4082
|
}
|
|
4083
|
+
if ((opts.yaml || opts.json) && opts.encode) {
|
|
4084
|
+
throw new Error("--yaml/--json and --encode are mutually exclusive");
|
|
4085
|
+
}
|
|
4086
|
+
if ((opts.yaml || opts.json) && opts.dryRun) {
|
|
4087
|
+
throw new Error("--yaml/--json and --dry-run are mutually exclusive");
|
|
4088
|
+
}
|
|
4089
|
+
if (opts.yaml && opts.json) {
|
|
4090
|
+
throw new Error("--yaml and --json are mutually exclusive");
|
|
4091
|
+
}
|
|
3961
4092
|
const config = await loadConfig();
|
|
3962
4093
|
const effectiveChain = opts.chain;
|
|
3963
4094
|
let pallet;
|
|
@@ -3968,9 +4099,10 @@ async function handleTx(target, args, opts) {
|
|
|
3968
4099
|
callName = target.slice(dotIdx + 1);
|
|
3969
4100
|
}
|
|
3970
4101
|
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
3971
|
-
const
|
|
4102
|
+
const decodeOnly = opts.encode || opts.yaml || opts.json;
|
|
4103
|
+
const signer = decodeOnly ? undefined : await resolveAccountSigner(opts.from);
|
|
3972
4104
|
let clientHandle;
|
|
3973
|
-
if (!
|
|
4105
|
+
if (!decodeOnly) {
|
|
3974
4106
|
clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
3975
4107
|
}
|
|
3976
4108
|
try {
|
|
@@ -3987,7 +4119,7 @@ async function handleTx(target, args, opts) {
|
|
|
3987
4119
|
}
|
|
3988
4120
|
let unsafeApi;
|
|
3989
4121
|
let txOptions;
|
|
3990
|
-
if (!
|
|
4122
|
+
if (!decodeOnly) {
|
|
3991
4123
|
const userExtOverrides = parseExtOption(opts.ext);
|
|
3992
4124
|
const customSignedExtensions = buildCustomSignedExtensions(meta, userExtOverrides);
|
|
3993
4125
|
txOptions = Object.keys(customSignedExtensions).length > 0 ? { customSignedExtensions } : undefined;
|
|
@@ -4000,9 +4132,14 @@ async function handleTx(target, args, opts) {
|
|
|
4000
4132
|
throw new Error(`Extra arguments are not allowed when submitting a raw call hex.
|
|
4001
4133
|
` + "Usage: dot tx 0x<call_hex> --from <account>");
|
|
4002
4134
|
}
|
|
4135
|
+
callHex = target;
|
|
4136
|
+
if (opts.yaml || opts.json) {
|
|
4137
|
+
const fileObj = decodeCallToFileFormat(meta, callHex, chainName);
|
|
4138
|
+
outputFileFormat(fileObj, !!opts.yaml);
|
|
4139
|
+
return;
|
|
4140
|
+
}
|
|
4003
4141
|
const callBinary = Binary3.fromHex(target);
|
|
4004
4142
|
tx = await unsafeApi.txFromCallData(callBinary);
|
|
4005
|
-
callHex = target;
|
|
4006
4143
|
} else {
|
|
4007
4144
|
const palletNames = getPalletNames(meta);
|
|
4008
4145
|
const palletInfo = findPallet(meta, pallet);
|
|
@@ -4014,12 +4151,19 @@ async function handleTx(target, args, opts) {
|
|
|
4014
4151
|
const callNames = palletInfo.calls.map((c) => c.name);
|
|
4015
4152
|
throw new Error(suggestMessage(`call in ${palletInfo.name}`, callName, callNames));
|
|
4016
4153
|
}
|
|
4017
|
-
const
|
|
4018
|
-
|
|
4154
|
+
const effectiveArgs = opts.parsedArgs !== undefined ? fileArgsToStrings(opts.parsedArgs) : args;
|
|
4155
|
+
const callData = await parseCallArgs(meta, palletInfo.name, callInfo.name, effectiveArgs);
|
|
4156
|
+
if (opts.encode || opts.yaml || opts.json) {
|
|
4019
4157
|
const { codec, location } = meta.builder.buildCall(palletInfo.name, callInfo.name);
|
|
4020
4158
|
const encodedArgs = codec.enc(callData);
|
|
4021
4159
|
const fullCall = new Uint8Array([location[0], location[1], ...encodedArgs]);
|
|
4022
|
-
|
|
4160
|
+
const hex = Binary3.fromBytes(fullCall).asHex();
|
|
4161
|
+
if (opts.encode) {
|
|
4162
|
+
console.log(hex);
|
|
4163
|
+
return;
|
|
4164
|
+
}
|
|
4165
|
+
const fileObj = decodeCallToFileFormat(meta, hex, chainName);
|
|
4166
|
+
outputFileFormat(fileObj, !!opts.yaml);
|
|
4023
4167
|
return;
|
|
4024
4168
|
}
|
|
4025
4169
|
tx = unsafeApi.tx[palletInfo.name][callInfo.name](callData);
|
|
@@ -4140,6 +4284,58 @@ function decodeCallFallback(meta, callHex) {
|
|
|
4140
4284
|
const argsStr = formatRawDecoded(args);
|
|
4141
4285
|
return `${palletName2}.${callName} ${argsStr}`;
|
|
4142
4286
|
}
|
|
4287
|
+
function decodeCallToFileFormat(meta, callHex, chainName) {
|
|
4288
|
+
const callTypeId = meta.lookup.call;
|
|
4289
|
+
if (callTypeId == null)
|
|
4290
|
+
throw new Error("No RuntimeCall type ID in metadata");
|
|
4291
|
+
const codec = meta.builder.buildDefinition(callTypeId);
|
|
4292
|
+
const decoded = codec.dec(Binary3.fromHex(callHex).asBytes());
|
|
4293
|
+
const palletName2 = decoded.type;
|
|
4294
|
+
const call = decoded.value;
|
|
4295
|
+
const callName = call.type;
|
|
4296
|
+
const args = call.value;
|
|
4297
|
+
return {
|
|
4298
|
+
chain: chainName,
|
|
4299
|
+
tx: {
|
|
4300
|
+
[palletName2]: {
|
|
4301
|
+
[callName]: sanitizeForSerialization(args) ?? null
|
|
4302
|
+
}
|
|
4303
|
+
}
|
|
4304
|
+
};
|
|
4305
|
+
}
|
|
4306
|
+
function sanitizeForSerialization(value) {
|
|
4307
|
+
if (value === undefined || value === null)
|
|
4308
|
+
return null;
|
|
4309
|
+
if (value instanceof Binary3)
|
|
4310
|
+
return value.asHex();
|
|
4311
|
+
if (typeof value === "bigint") {
|
|
4312
|
+
if (value >= Number.MIN_SAFE_INTEGER && value <= Number.MAX_SAFE_INTEGER) {
|
|
4313
|
+
return Number(value);
|
|
4314
|
+
}
|
|
4315
|
+
return value.toString();
|
|
4316
|
+
}
|
|
4317
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4318
|
+
return value;
|
|
4319
|
+
}
|
|
4320
|
+
if (Array.isArray(value)) {
|
|
4321
|
+
return value.map(sanitizeForSerialization);
|
|
4322
|
+
}
|
|
4323
|
+
if (typeof value === "object") {
|
|
4324
|
+
const result = {};
|
|
4325
|
+
for (const [k, v] of Object.entries(value)) {
|
|
4326
|
+
result[k] = sanitizeForSerialization(v);
|
|
4327
|
+
}
|
|
4328
|
+
return result;
|
|
4329
|
+
}
|
|
4330
|
+
return String(value);
|
|
4331
|
+
}
|
|
4332
|
+
function outputFileFormat(obj, asYaml) {
|
|
4333
|
+
if (asYaml) {
|
|
4334
|
+
process.stdout.write(stringifyYaml2(obj));
|
|
4335
|
+
} else {
|
|
4336
|
+
console.log(JSON.stringify(obj, null, 2));
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4143
4339
|
function formatRawDecoded(value) {
|
|
4144
4340
|
if (value === undefined || value === null)
|
|
4145
4341
|
return "null";
|
|
@@ -4437,8 +4633,8 @@ function normalizeValue2(lookup, entry, value) {
|
|
|
4437
4633
|
return;
|
|
4438
4634
|
}
|
|
4439
4635
|
case "primitive": {
|
|
4636
|
+
const prim = resolved.value;
|
|
4440
4637
|
if (typeof value === "string") {
|
|
4441
|
-
const prim = resolved.value;
|
|
4442
4638
|
switch (prim) {
|
|
4443
4639
|
case "bool":
|
|
4444
4640
|
return value === "true";
|
|
@@ -4458,18 +4654,52 @@ function normalizeValue2(lookup, entry, value) {
|
|
|
4458
4654
|
return parseInt(value, 10);
|
|
4459
4655
|
}
|
|
4460
4656
|
}
|
|
4657
|
+
if (typeof value === "number") {
|
|
4658
|
+
switch (prim) {
|
|
4659
|
+
case "u64":
|
|
4660
|
+
case "u128":
|
|
4661
|
+
case "u256":
|
|
4662
|
+
case "i64":
|
|
4663
|
+
case "i128":
|
|
4664
|
+
case "i256":
|
|
4665
|
+
return BigInt(value);
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4461
4668
|
return value;
|
|
4462
4669
|
}
|
|
4463
4670
|
case "compact": {
|
|
4464
4671
|
if (typeof value === "string") {
|
|
4465
4672
|
return resolved.isBig ? BigInt(value) : parseInt(value, 10);
|
|
4466
4673
|
}
|
|
4674
|
+
if (typeof value === "number" && resolved.isBig) {
|
|
4675
|
+
return BigInt(value);
|
|
4676
|
+
}
|
|
4467
4677
|
return value;
|
|
4468
4678
|
}
|
|
4469
4679
|
default:
|
|
4470
4680
|
return value;
|
|
4471
4681
|
}
|
|
4472
4682
|
}
|
|
4683
|
+
function fileArgsToStrings(args) {
|
|
4684
|
+
if (args == null)
|
|
4685
|
+
return [];
|
|
4686
|
+
if (typeof args === "object" && !Array.isArray(args)) {
|
|
4687
|
+
return Object.values(args).map(serializeForCli);
|
|
4688
|
+
}
|
|
4689
|
+
if (Array.isArray(args)) {
|
|
4690
|
+
return args.map(serializeForCli);
|
|
4691
|
+
}
|
|
4692
|
+
return [serializeForCli(args)];
|
|
4693
|
+
}
|
|
4694
|
+
function serializeForCli(v) {
|
|
4695
|
+
if (typeof v === "string")
|
|
4696
|
+
return v;
|
|
4697
|
+
if (typeof v === "number" || typeof v === "bigint")
|
|
4698
|
+
return String(v);
|
|
4699
|
+
if (typeof v === "boolean" || v === null)
|
|
4700
|
+
return String(v);
|
|
4701
|
+
return JSON.stringify(v);
|
|
4702
|
+
}
|
|
4473
4703
|
function parseEnumShorthand2(arg) {
|
|
4474
4704
|
if (arg.startsWith("{") || arg.startsWith("[") || arg.startsWith("0x"))
|
|
4475
4705
|
return null;
|
|
@@ -4763,6 +4993,130 @@ async function saveConfig2(config) {
|
|
|
4763
4993
|
`);
|
|
4764
4994
|
}
|
|
4765
4995
|
|
|
4996
|
+
// src/core/file-loader.ts
|
|
4997
|
+
init_errors();
|
|
4998
|
+
import { parse as parseYaml } from "yaml";
|
|
4999
|
+
var CATEGORIES = ["tx", "query", "const", "apis"];
|
|
5000
|
+
var FILE_EXTENSIONS = [".json", ".yaml", ".yml"];
|
|
5001
|
+
function isFilePath(dotpath) {
|
|
5002
|
+
if (FILE_EXTENSIONS.some((ext) => dotpath.endsWith(ext)))
|
|
5003
|
+
return true;
|
|
5004
|
+
if (dotpath.startsWith("./") || dotpath.startsWith("/"))
|
|
5005
|
+
return true;
|
|
5006
|
+
return false;
|
|
5007
|
+
}
|
|
5008
|
+
function parseVarFlags(varFlags) {
|
|
5009
|
+
if (!varFlags)
|
|
5010
|
+
return {};
|
|
5011
|
+
const flags = Array.isArray(varFlags) ? varFlags : [varFlags];
|
|
5012
|
+
const vars = {};
|
|
5013
|
+
for (const flag of flags) {
|
|
5014
|
+
const eqIdx = flag.indexOf("=");
|
|
5015
|
+
if (eqIdx === -1) {
|
|
5016
|
+
throw new CliError(`Invalid --var format "${flag}". Expected KEY=VALUE.`);
|
|
5017
|
+
}
|
|
5018
|
+
const key = flag.slice(0, eqIdx);
|
|
5019
|
+
const value = flag.slice(eqIdx + 1);
|
|
5020
|
+
if (!key) {
|
|
5021
|
+
throw new CliError(`Invalid --var format "${flag}". Key cannot be empty.`);
|
|
5022
|
+
}
|
|
5023
|
+
vars[key] = value;
|
|
5024
|
+
}
|
|
5025
|
+
return vars;
|
|
5026
|
+
}
|
|
5027
|
+
function substituteVars(text, vars) {
|
|
5028
|
+
return text.replace(/\$\{([^}]+)\}/g, (_match, expr) => {
|
|
5029
|
+
const defaultSep = expr.indexOf(":-");
|
|
5030
|
+
let varName;
|
|
5031
|
+
let defaultValue;
|
|
5032
|
+
if (defaultSep !== -1) {
|
|
5033
|
+
varName = expr.slice(0, defaultSep);
|
|
5034
|
+
defaultValue = expr.slice(defaultSep + 2);
|
|
5035
|
+
} else {
|
|
5036
|
+
varName = expr;
|
|
5037
|
+
}
|
|
5038
|
+
if (varName in vars)
|
|
5039
|
+
return vars[varName];
|
|
5040
|
+
const envVal = process.env[varName];
|
|
5041
|
+
if (envVal !== undefined)
|
|
5042
|
+
return envVal;
|
|
5043
|
+
if (defaultValue !== undefined)
|
|
5044
|
+
return defaultValue;
|
|
5045
|
+
throw new CliError(`Undefined variable "\${${varName}}" in file. ` + `Provide it via --var ${varName}=VALUE, as an environment variable, ` + `or add a default with \${${varName}:-default}.`);
|
|
5046
|
+
});
|
|
5047
|
+
}
|
|
5048
|
+
async function loadCommandFile(filePath, cliVars) {
|
|
5049
|
+
const file = Bun.file(filePath);
|
|
5050
|
+
const exists = await file.exists();
|
|
5051
|
+
if (!exists) {
|
|
5052
|
+
throw new CliError(`File not found: ${filePath}`);
|
|
5053
|
+
}
|
|
5054
|
+
const rawText = await file.text();
|
|
5055
|
+
if (!rawText.trim()) {
|
|
5056
|
+
throw new CliError(`File is empty: ${filePath}`);
|
|
5057
|
+
}
|
|
5058
|
+
const isJson = filePath.endsWith(".json");
|
|
5059
|
+
const fileVars = {};
|
|
5060
|
+
try {
|
|
5061
|
+
const preParsed = isJson ? JSON.parse(rawText) : parseYaml(rawText);
|
|
5062
|
+
if (preParsed && typeof preParsed === "object" && !Array.isArray(preParsed) && preParsed.vars) {
|
|
5063
|
+
const varsSection = preParsed.vars;
|
|
5064
|
+
if (typeof varsSection === "object" && !Array.isArray(varsSection)) {
|
|
5065
|
+
for (const [key, val] of Object.entries(varsSection)) {
|
|
5066
|
+
fileVars[key] = String(val);
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
} catch {}
|
|
5071
|
+
const mergedVars = { ...fileVars, ...cliVars };
|
|
5072
|
+
const substituted = substituteVars(rawText, mergedVars);
|
|
5073
|
+
let parsed;
|
|
5074
|
+
try {
|
|
5075
|
+
parsed = isJson ? JSON.parse(substituted) : parseYaml(substituted);
|
|
5076
|
+
} catch (err) {
|
|
5077
|
+
const format = isJson ? "JSON" : "YAML";
|
|
5078
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5079
|
+
throw new CliError(`Failed to parse ${format} file "${filePath}": ${msg}`);
|
|
5080
|
+
}
|
|
5081
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
5082
|
+
throw new CliError(`File "${filePath}" must contain a YAML/JSON object (not an array or scalar).`);
|
|
5083
|
+
}
|
|
5084
|
+
const doc = parsed;
|
|
5085
|
+
const chain = doc.chain != null ? String(doc.chain) : undefined;
|
|
5086
|
+
const foundCategories = CATEGORIES.filter((c) => (c in doc));
|
|
5087
|
+
if (foundCategories.length === 0) {
|
|
5088
|
+
throw new CliError(`File "${filePath}" must contain exactly one category key: ${CATEGORIES.join(", ")}. None found.`);
|
|
5089
|
+
}
|
|
5090
|
+
if (foundCategories.length > 1) {
|
|
5091
|
+
throw new CliError(`File "${filePath}" contains multiple category keys: ${foundCategories.join(", ")}. Only one is allowed.`);
|
|
5092
|
+
}
|
|
5093
|
+
const category = foundCategories[0];
|
|
5094
|
+
const categoryObj = doc[category];
|
|
5095
|
+
if (!categoryObj || typeof categoryObj !== "object" || Array.isArray(categoryObj)) {
|
|
5096
|
+
throw new CliError(`"${category}" in file "${filePath}" must be an object with a pallet name as key.`);
|
|
5097
|
+
}
|
|
5098
|
+
const palletEntries = Object.entries(categoryObj);
|
|
5099
|
+
if (palletEntries.length !== 1) {
|
|
5100
|
+
throw new CliError(`"${category}" in file "${filePath}" must contain exactly one pallet. Found: ${palletEntries.length === 0 ? "none" : palletEntries.map(([k]) => k).join(", ")}.`);
|
|
5101
|
+
}
|
|
5102
|
+
const [pallet, palletObj] = palletEntries[0];
|
|
5103
|
+
if (!palletObj || typeof palletObj !== "object" || Array.isArray(palletObj)) {
|
|
5104
|
+
throw new CliError(`"${category}.${pallet}" in file "${filePath}" must be an object with a call/item name as key.`);
|
|
5105
|
+
}
|
|
5106
|
+
const itemEntries = Object.entries(palletObj);
|
|
5107
|
+
if (itemEntries.length !== 1) {
|
|
5108
|
+
throw new CliError(`"${category}.${pallet}" in file "${filePath}" must contain exactly one item. Found: ${itemEntries.length === 0 ? "none" : itemEntries.map(([k]) => k).join(", ")}.`);
|
|
5109
|
+
}
|
|
5110
|
+
const [item, args] = itemEntries[0];
|
|
5111
|
+
return {
|
|
5112
|
+
chain,
|
|
5113
|
+
category,
|
|
5114
|
+
pallet,
|
|
5115
|
+
item,
|
|
5116
|
+
args: args ?? undefined
|
|
5117
|
+
};
|
|
5118
|
+
}
|
|
5119
|
+
|
|
4766
5120
|
// src/core/update-notifier.ts
|
|
4767
5121
|
init_store();
|
|
4768
5122
|
import { readFileSync } from "node:fs";
|
|
@@ -4973,12 +5327,24 @@ if (process.argv[2] === "__complete") {
|
|
|
4973
5327
|
process.exit(0);
|
|
4974
5328
|
})();
|
|
4975
5329
|
} else {
|
|
4976
|
-
let
|
|
5330
|
+
let collectVarFlags = function(argv) {
|
|
5331
|
+
const vars = [];
|
|
5332
|
+
for (let i = 0;i < argv.length; i++) {
|
|
5333
|
+
if (argv[i] === "--var" && i + 1 < argv.length) {
|
|
5334
|
+
vars.push(argv[i + 1]);
|
|
5335
|
+
i++;
|
|
5336
|
+
} else if (argv[i].startsWith("--var=")) {
|
|
5337
|
+
vars.push(argv[i].slice(6));
|
|
5338
|
+
}
|
|
5339
|
+
}
|
|
5340
|
+
return parseVarFlags(vars);
|
|
5341
|
+
}, printHelp = function() {
|
|
4977
5342
|
console.log(`dot/${version} \u2014 Polkadot CLI`);
|
|
4978
5343
|
console.log();
|
|
4979
5344
|
console.log("Usage:");
|
|
4980
5345
|
console.log(" dot <category>[.Pallet[.Item]] [args] [options]");
|
|
4981
5346
|
console.log(" dot [Chain.]<category>[.Pallet[.Item]] [args] [options]");
|
|
5347
|
+
console.log(" dot <file.yaml|file.json> [options]");
|
|
4982
5348
|
console.log();
|
|
4983
5349
|
console.log("Categories:");
|
|
4984
5350
|
console.log(" query Query on-chain storage");
|
|
@@ -4996,12 +5362,16 @@ if (process.argv[2] === "__complete") {
|
|
|
4996
5362
|
console.log(" dot events.Balances List events in Balances");
|
|
4997
5363
|
console.log(" dot apis.Core.version Call a runtime API");
|
|
4998
5364
|
console.log(" dot polkadot.query.System.Number With chain prefix");
|
|
5365
|
+
console.log(" dot ./transfer.yaml --from alice Run from file");
|
|
5366
|
+
console.log(" dot tx.0x1f0003... --yaml Decode hex call to YAML");
|
|
5367
|
+
console.log(" dot tx.System.remark 0xdead --json Encode & output as JSON file format");
|
|
4999
5368
|
console.log();
|
|
5000
5369
|
console.log("Commands:");
|
|
5001
5370
|
console.log(" inspect [target] Inspect chain metadata (alias: explore)");
|
|
5002
5371
|
console.log(" chain Manage chain configurations");
|
|
5003
5372
|
console.log(" account Manage accounts");
|
|
5004
5373
|
console.log(" hash Hash utilities");
|
|
5374
|
+
console.log(" parachain Derive parachain sovereign accounts");
|
|
5005
5375
|
console.log(" completions <sh> Generate shell completions (zsh, bash, fish)");
|
|
5006
5376
|
console.log();
|
|
5007
5377
|
console.log("Global options:");
|
|
@@ -5024,16 +5394,57 @@ if (process.argv[2] === "__complete") {
|
|
|
5024
5394
|
registerInspectCommand(cli);
|
|
5025
5395
|
registerAccountCommands(cli);
|
|
5026
5396
|
registerHashCommand(cli);
|
|
5397
|
+
registerParachainCommand(cli);
|
|
5027
5398
|
registerCompletionsCommand(cli);
|
|
5028
|
-
cli.command("[dotpath] [...args]").option("--from <name>", "Account to sign with (for tx)").option("--dry-run", "Estimate fees without submitting (for tx)").option("--encode", "Encode call to hex without signing (for tx)").option("--ext <json>", "Custom signed extension values as JSON (for tx)").option("-w, --wait <level>", "Resolve at: broadcast, best-block (or best), finalized (for tx)", {
|
|
5399
|
+
cli.command("[dotpath] [...args]").option("--from <name>", "Account to sign with (for tx)").option("--dry-run", "Estimate fees without submitting (for tx)").option("--encode", "Encode call to hex without signing (for tx)").option("--yaml", "Decode call to YAML file format (for tx)").option("--json", "Decode call to JSON file format (for tx)").option("--ext <json>", "Custom signed extension values as JSON (for tx)").option("-w, --wait <level>", "Resolve at: broadcast, best-block (or best), finalized (for tx)", {
|
|
5029
5400
|
default: "finalized"
|
|
5030
5401
|
}).option("--limit <n>", "Max entries to return for map queries (0 = unlimited)", {
|
|
5031
5402
|
default: 100
|
|
5032
|
-
}).option("--dump", "Dump all entries of a storage map (without specifying a key)").action(async (dotpath, args, opts) => {
|
|
5403
|
+
}).option("--dump", "Dump all entries of a storage map (without specifying a key)").option("--var <kv>", "Template variable for file input (KEY=VALUE, repeatable)").action(async (dotpath, args, opts) => {
|
|
5033
5404
|
if (!dotpath) {
|
|
5034
5405
|
printHelp();
|
|
5035
5406
|
return;
|
|
5036
5407
|
}
|
|
5408
|
+
if (isFilePath(dotpath)) {
|
|
5409
|
+
const cliVars = collectVarFlags(process.argv);
|
|
5410
|
+
const cmd = await loadCommandFile(dotpath, cliVars);
|
|
5411
|
+
const effectiveChain2 = opts.chain ?? cmd.chain;
|
|
5412
|
+
const handlerOpts2 = { chain: effectiveChain2, rpc: opts.rpc, output: opts.output };
|
|
5413
|
+
const target2 = `${cmd.pallet}.${cmd.item}`;
|
|
5414
|
+
switch (cmd.category) {
|
|
5415
|
+
case "tx":
|
|
5416
|
+
await handleTx(target2, args, {
|
|
5417
|
+
...handlerOpts2,
|
|
5418
|
+
from: opts.from,
|
|
5419
|
+
dryRun: opts.dryRun,
|
|
5420
|
+
encode: opts.encode,
|
|
5421
|
+
yaml: opts.yaml,
|
|
5422
|
+
json: opts.json,
|
|
5423
|
+
ext: opts.ext,
|
|
5424
|
+
wait: opts.wait,
|
|
5425
|
+
parsedArgs: cmd.args
|
|
5426
|
+
});
|
|
5427
|
+
break;
|
|
5428
|
+
case "query":
|
|
5429
|
+
await handleQuery(target2, args, {
|
|
5430
|
+
...handlerOpts2,
|
|
5431
|
+
limit: opts.limit,
|
|
5432
|
+
dump: opts.dump,
|
|
5433
|
+
parsedArgs: cmd.args
|
|
5434
|
+
});
|
|
5435
|
+
break;
|
|
5436
|
+
case "const":
|
|
5437
|
+
await handleConst(target2, handlerOpts2);
|
|
5438
|
+
break;
|
|
5439
|
+
case "apis":
|
|
5440
|
+
await handleApis2(target2, args, {
|
|
5441
|
+
...handlerOpts2,
|
|
5442
|
+
parsedArgs: cmd.args
|
|
5443
|
+
});
|
|
5444
|
+
break;
|
|
5445
|
+
}
|
|
5446
|
+
return;
|
|
5447
|
+
}
|
|
5037
5448
|
const config = await loadConfig2();
|
|
5038
5449
|
const knownChains = Object.keys(config.chains);
|
|
5039
5450
|
let parsed;
|
|
@@ -5069,6 +5480,8 @@ if (process.argv[2] === "__complete") {
|
|
|
5069
5480
|
from: opts.from,
|
|
5070
5481
|
dryRun: opts.dryRun,
|
|
5071
5482
|
encode: opts.encode,
|
|
5483
|
+
yaml: opts.yaml,
|
|
5484
|
+
json: opts.json,
|
|
5072
5485
|
ext: opts.ext,
|
|
5073
5486
|
wait: opts.wait
|
|
5074
5487
|
});
|
|
@@ -5078,6 +5491,8 @@ if (process.argv[2] === "__complete") {
|
|
|
5078
5491
|
from: opts.from,
|
|
5079
5492
|
dryRun: opts.dryRun,
|
|
5080
5493
|
encode: opts.encode,
|
|
5494
|
+
yaml: opts.yaml,
|
|
5495
|
+
json: opts.json,
|
|
5081
5496
|
ext: opts.ext,
|
|
5082
5497
|
wait: opts.wait
|
|
5083
5498
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polkadot-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "CLI tool for querying Polkadot-ecosystem on-chain state",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -45,7 +45,8 @@
|
|
|
45
45
|
"@polkadot-labs/hdkd-helpers": "^0.0.27",
|
|
46
46
|
"cac": "^6.7.14",
|
|
47
47
|
"polkadot-api": "^1.23.3",
|
|
48
|
-
"ws": "^8.19.0"
|
|
48
|
+
"ws": "^8.19.0",
|
|
49
|
+
"yaml": "^2.8.3"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@biomejs/biome": "^2.4.5",
|