polkadot-cli 1.7.0 → 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.
Files changed (3) hide show
  1. package/README.md +50 -1
  2. package/dist/cli.mjs +192 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -21,6 +21,7 @@ Ships with Polkadot and all system parachains preconfigured with multiple fallba
21
21
  - ✅ Runtime API calls — `dot apis.Core.version`
22
22
  - ✅ Batteries included — all system parachains and testnets already setup to be used
23
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
24
25
 
25
26
  ### Preconfigured chains
26
27
 
@@ -496,6 +497,28 @@ dot tx Balances.transfer_keep_alive 5FHneW46... 1000000000000 --encode
496
497
  dot tx Sudo.sudo $(dot tx System.remark 0xcafe --encode) --from alice
497
498
  ```
498
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
+
499
522
  Both dry-run and submission display the encoded call hex and a decoded human-readable form:
500
523
 
501
524
  ```
@@ -750,7 +773,7 @@ tx:
750
773
  value: ${AMOUNT}
751
774
  ```
752
775
 
753
- All existing flags work with file input — `--chain` overrides the file's `chain:` field, `--from`, `--dry-run`, `--encode`, `--output`, etc. behave identically to inline commands.
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.
754
777
 
755
778
  ### Compute hashes
756
779
 
@@ -775,6 +798,32 @@ dot hash blake2b256 0xdeadbeef --output json
775
798
 
776
799
  Run `dot hash` with no arguments to see all available algorithms.
777
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
+
778
827
  ### Getting help
779
828
 
780
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) {
@@ -1967,6 +1968,12 @@ async function generateCompletions(currentWord, precedingWords) {
1967
1968
  if (firstArg === "hash") {
1968
1969
  return filterPrefix(getAlgorithmNames(), currentWord);
1969
1970
  }
1971
+ if (firstArg === "parachain") {
1972
+ if (prevWord === "--type") {
1973
+ return filterPrefix(["child", "sibling"], currentWord);
1974
+ }
1975
+ return [];
1976
+ }
1970
1977
  return completeDotpath(currentWord, config, knownChains, precedingWords);
1971
1978
  }
1972
1979
  function detectCategory(words, _knownChains) {
@@ -2130,7 +2137,7 @@ var init_complete = __esm(() => {
2130
2137
  apis: "apis",
2131
2138
  api: "apis"
2132
2139
  };
2133
- NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "completions"];
2140
+ NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "parachain", "completions"];
2134
2141
  CHAIN_SUBCOMMANDS = ["add", "remove", "update", "list", "default"];
2135
2142
  ACCOUNT_SUBCOMMANDS = [
2136
2143
  "add",
@@ -2151,7 +2158,7 @@ var init_complete = __esm(() => {
2151
2158
  // src/cli.ts
2152
2159
  import cac from "cac";
2153
2160
  // package.json
2154
- var version = "1.7.0";
2161
+ var version = "1.8.0";
2155
2162
 
2156
2163
  // src/commands/account.ts
2157
2164
  init_accounts_store();
@@ -3765,6 +3772,97 @@ function registerInspectCommand(cli) {
3765
3772
  });
3766
3773
  }
3767
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
+
3768
3866
  // src/commands/query.ts
3769
3867
  init_store();
3770
3868
  init_client();
@@ -3918,6 +4016,7 @@ init_errors();
3918
4016
  init_focused_inspect();
3919
4017
  import { getViewBuilder as getViewBuilder2 } from "@polkadot-api/view-builder";
3920
4018
  import { Binary as Binary3 } from "polkadot-api";
4019
+ import { stringify as stringifyYaml2 } from "yaml";
3921
4020
  function parseWaitLevel(raw) {
3922
4021
  switch (raw) {
3923
4022
  case "broadcast":
@@ -3968,7 +4067,7 @@ async function handleTx(target, args, opts) {
3968
4067
  console.log();
3969
4068
  return;
3970
4069
  }
3971
- if (!opts.from && !opts.encode) {
4070
+ if (!opts.from && !opts.encode && !opts.yaml && !opts.json) {
3972
4071
  if (isRawCall) {
3973
4072
  throw new Error("--from is required (or use --encode to output hex without signing)");
3974
4073
  }
@@ -3981,6 +4080,15 @@ async function handleTx(target, args, opts) {
3981
4080
  if (opts.encode && isRawCall) {
3982
4081
  throw new Error("--encode cannot be used with raw call hex (already encoded)");
3983
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
+ }
3984
4092
  const config = await loadConfig();
3985
4093
  const effectiveChain = opts.chain;
3986
4094
  let pallet;
@@ -3991,9 +4099,10 @@ async function handleTx(target, args, opts) {
3991
4099
  callName = target.slice(dotIdx + 1);
3992
4100
  }
3993
4101
  const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
3994
- const signer = opts.encode ? undefined : await resolveAccountSigner(opts.from);
4102
+ const decodeOnly = opts.encode || opts.yaml || opts.json;
4103
+ const signer = decodeOnly ? undefined : await resolveAccountSigner(opts.from);
3995
4104
  let clientHandle;
3996
- if (!opts.encode) {
4105
+ if (!decodeOnly) {
3997
4106
  clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
3998
4107
  }
3999
4108
  try {
@@ -4010,7 +4119,7 @@ async function handleTx(target, args, opts) {
4010
4119
  }
4011
4120
  let unsafeApi;
4012
4121
  let txOptions;
4013
- if (!opts.encode) {
4122
+ if (!decodeOnly) {
4014
4123
  const userExtOverrides = parseExtOption(opts.ext);
4015
4124
  const customSignedExtensions = buildCustomSignedExtensions(meta, userExtOverrides);
4016
4125
  txOptions = Object.keys(customSignedExtensions).length > 0 ? { customSignedExtensions } : undefined;
@@ -4023,9 +4132,14 @@ async function handleTx(target, args, opts) {
4023
4132
  throw new Error(`Extra arguments are not allowed when submitting a raw call hex.
4024
4133
  ` + "Usage: dot tx 0x<call_hex> --from <account>");
4025
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
+ }
4026
4141
  const callBinary = Binary3.fromHex(target);
4027
4142
  tx = await unsafeApi.txFromCallData(callBinary);
4028
- callHex = target;
4029
4143
  } else {
4030
4144
  const palletNames = getPalletNames(meta);
4031
4145
  const palletInfo = findPallet(meta, pallet);
@@ -4039,11 +4153,17 @@ async function handleTx(target, args, opts) {
4039
4153
  }
4040
4154
  const effectiveArgs = opts.parsedArgs !== undefined ? fileArgsToStrings(opts.parsedArgs) : args;
4041
4155
  const callData = await parseCallArgs(meta, palletInfo.name, callInfo.name, effectiveArgs);
4042
- if (opts.encode) {
4156
+ if (opts.encode || opts.yaml || opts.json) {
4043
4157
  const { codec, location } = meta.builder.buildCall(palletInfo.name, callInfo.name);
4044
4158
  const encodedArgs = codec.enc(callData);
4045
4159
  const fullCall = new Uint8Array([location[0], location[1], ...encodedArgs]);
4046
- console.log(Binary3.fromBytes(fullCall).asHex());
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);
4047
4167
  return;
4048
4168
  }
4049
4169
  tx = unsafeApi.tx[palletInfo.name][callInfo.name](callData);
@@ -4164,6 +4284,58 @@ function decodeCallFallback(meta, callHex) {
4164
4284
  const argsStr = formatRawDecoded(args);
4165
4285
  return `${palletName2}.${callName} ${argsStr}`;
4166
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
+ }
4167
4339
  function formatRawDecoded(value) {
4168
4340
  if (value === undefined || value === null)
4169
4341
  return "null";
@@ -5191,12 +5363,15 @@ if (process.argv[2] === "__complete") {
5191
5363
  console.log(" dot apis.Core.version Call a runtime API");
5192
5364
  console.log(" dot polkadot.query.System.Number With chain prefix");
5193
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");
5194
5368
  console.log();
5195
5369
  console.log("Commands:");
5196
5370
  console.log(" inspect [target] Inspect chain metadata (alias: explore)");
5197
5371
  console.log(" chain Manage chain configurations");
5198
5372
  console.log(" account Manage accounts");
5199
5373
  console.log(" hash Hash utilities");
5374
+ console.log(" parachain Derive parachain sovereign accounts");
5200
5375
  console.log(" completions <sh> Generate shell completions (zsh, bash, fish)");
5201
5376
  console.log();
5202
5377
  console.log("Global options:");
@@ -5219,8 +5394,9 @@ if (process.argv[2] === "__complete") {
5219
5394
  registerInspectCommand(cli);
5220
5395
  registerAccountCommands(cli);
5221
5396
  registerHashCommand(cli);
5397
+ registerParachainCommand(cli);
5222
5398
  registerCompletionsCommand(cli);
5223
- 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)", {
5224
5400
  default: "finalized"
5225
5401
  }).option("--limit <n>", "Max entries to return for map queries (0 = unlimited)", {
5226
5402
  default: 100
@@ -5242,6 +5418,8 @@ if (process.argv[2] === "__complete") {
5242
5418
  from: opts.from,
5243
5419
  dryRun: opts.dryRun,
5244
5420
  encode: opts.encode,
5421
+ yaml: opts.yaml,
5422
+ json: opts.json,
5245
5423
  ext: opts.ext,
5246
5424
  wait: opts.wait,
5247
5425
  parsedArgs: cmd.args
@@ -5302,6 +5480,8 @@ if (process.argv[2] === "__complete") {
5302
5480
  from: opts.from,
5303
5481
  dryRun: opts.dryRun,
5304
5482
  encode: opts.encode,
5483
+ yaml: opts.yaml,
5484
+ json: opts.json,
5305
5485
  ext: opts.ext,
5306
5486
  wait: opts.wait
5307
5487
  });
@@ -5311,6 +5491,8 @@ if (process.argv[2] === "__complete") {
5311
5491
  from: opts.from,
5312
5492
  dryRun: opts.dryRun,
5313
5493
  encode: opts.encode,
5494
+ yaml: opts.yaml,
5495
+ json: opts.json,
5314
5496
  ext: opts.ext,
5315
5497
  wait: opts.wait
5316
5498
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polkadot-cli",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "CLI tool for querying Polkadot-ecosystem on-chain state",
5
5
  "type": "module",
6
6
  "bin": {