polkadot-cli 1.19.0 → 1.20.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 +39 -12
  2. package/dist/cli.mjs +100 -40
  3. package/package.json +7 -7
package/README.md CHANGED
@@ -280,6 +280,10 @@ dot account create my-staking --path //staking
280
280
  # Add a keyed account from a BIP39 mnemonic
281
281
  dot account add treasury --secret "word1 word2 ... word12"
282
282
 
283
+ # Add from a 32-byte hex seed or a 64-byte raw sr25519 private key
284
+ dot account add seeded --secret 0x1111111111111111111111111111111111111111111111111111111111111111
285
+ dot account add raw-key --secret 0x<128-hex-char expanded secret> # no --path
286
+
283
287
  # Add with a derivation path
284
288
  dot account add hot-wallet --secret "word1 word2 ... word12" --path //hot
285
289
 
@@ -313,6 +317,7 @@ dot account inspect 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
313
317
  dot account inspect 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
314
318
  dot account inspect alice --prefix 0 # Polkadot mainnet prefix
315
319
  dot account inspect alice --json # JSON output
320
+ dot account inspect dave --show-secret # reveal mnemonic + sr25519 private key
316
321
  ```
317
322
 
318
323
  #### Watch-only accounts
@@ -484,16 +489,21 @@ dot account inspect --pallet-id py/trsry --json
484
489
 
485
490
  Constraints (will error): cannot combine a positional input with derivation flags; `--pallet-id` and `--parachain` are mutually exclusive; `--parachain` requires `--parachain-type child|sibling`; `--show-secret` doesn't apply (derived sovereigns have no key).
486
491
 
487
- #### Reveal the sr25519 private key
492
+ #### Reveal the mnemonic and sr25519 private key
488
493
 
489
- For provisioning another signer (e.g. a server that expects a raw hex private key in an env var), add `--show-secret` to print the **64-byte sr25519 expanded secret** as `0x`-prefixed hex:
494
+ For provisioning another signer (e.g. a server that expects a raw hex private key in an env var), add `--show-secret` to print the **64-byte sr25519 expanded secret** as `0x`-prefixed hex. It also reveals the **stored mnemonic** (or hex seed) so you can back it up:
490
495
 
491
496
  ```bash
492
- dot account inspect dave --show-secret
493
- # Private Key: 0x<128 hex chars> (sr25519 expanded, 64 bytes never share)
497
+ dot account inspect my-validator --show-secret
498
+ # Mnemonic: word1 word2 ... word12 (only for accounts stored as a phrase)
499
+ # Private Key: 0x<128 hex chars> (sr25519 expanded, 64 bytes — never share)
494
500
  ```
495
501
 
496
- Works for dev accounts (derived on-the-fly from the standard dev mnemonic) and for stored accounts that have a secret (mnemonic or hex seed). Refuses on watch-only accounts, bare SS58 addresses, or hex public keys. The hex is the final secret after any derivation path is applied, so it can be fed directly to signers that don't accept a mnemonic+path (e.g. `@scure/sr25519`'s `sign`, or services like identity-backend that read a `PROXY_PRIVATE_KEY`). Combine with `--json` to include it under the `privateKey` field.
502
+ Works for dev accounts (derived on-the-fly from the standard dev mnemonic) and for stored accounts that have a secret. Refuses on watch-only accounts, bare SS58 addresses, or hex public keys. The revealed line depends on how the account was stored: a phrase shows under `Mnemonic`, a 32-byte hex seed under `Seed`, and a raw private key shows only the `Private Key` (the stored secret already _is_ the expanded key). **Env-backed secrets are never resolved to disk output** — only the `$VAR` reference is shown. The expanded `Private Key` is the final secret after any derivation path is applied, so it can be fed directly to signers that don't accept a mnemonic+path (e.g. `@scure/sr25519`'s `sign`, or services like identity-backend that read a `PROXY_PRIVATE_KEY`). Combine with `--json` to include the values under the `mnemonic`/`seed` and `privateKey` fields.
503
+
504
+ The revealed `Private Key` round-trips: you can re-import it as a usable account (see [Import a raw private key](#import-a-raw-private-key) below).
505
+
506
+ > **Note:** if the account was stored with a derivation path, the revealed `Mnemonic`/`Seed` reproduces the original address **only when re-imported with the same `--path`** (shown on the `Derivation` line). The `Private Key` already bakes in the path, so it round-trips on its own.
497
507
 
498
508
  #### Env-var-backed accounts
499
509
 
@@ -540,12 +550,29 @@ Signers
540
550
 
541
551
  **Supported secret formats for import:**
542
552
 
543
- | Format | Example | Status |
544
- |--------|---------|--------|
545
- | BIP39 mnemonic (12/24 words) | `"abandon abandon ... about"` | Supported |
546
- | Hex seed (`0x` + 64 hex chars) | `0xabcdef0123...` | Not supported via CLI (see below) |
553
+ | Format | Example | `--path`? |
554
+ |--------|---------|-----------|
555
+ | BIP39 mnemonic (12/24 words) | `"abandon abandon ... about"` | Yes |
556
+ | Hex seed (`0x` + 64 hex chars = 32 bytes) | `0x1111...1111` | Yes |
557
+ | Raw private key (`0x` + 128 hex chars = 64-byte sr25519 expanded secret) | `0x20e0...5568` | No (cannot be HD-derived) |
558
+
559
+ All three formats work directly from the command line via `--secret` or via `--env`.
560
+
561
+ #### Import a raw private key
562
+
563
+ The 64-byte expanded secret that `--show-secret` prints can be re-imported as a fully usable, signing-capable account. This is the round-trip companion to `--show-secret` — handy when a key only exists in expanded form (e.g. exported from another tool or read from a `PROXY_PRIVATE_KEY` env var):
564
+
565
+ ```bash
566
+ # Round-trip: reveal dave's expanded secret, then import it under a new name
567
+ SECRET=$(dot account inspect dave --show-secret --json | jq -r .privateKey)
568
+ dot account add raw-dave --secret "$SECRET"
569
+ # raw-dave now has the same address as dave and can sign
570
+
571
+ # Or from an environment variable (secret stays off disk)
572
+ dot account add server-signer --env PROXY_PRIVATE_KEY
573
+ ```
547
574
 
548
- **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`.
575
+ A raw private key cannot be HD-derived, so `--path` is rejected for this format. Imported expanded-secret accounts sign exactly like mnemonic-backed ones.
549
576
 
550
577
  #### Export/import accounts
551
578
 
@@ -1871,7 +1898,7 @@ dot verifiable alice
1871
1898
  # Bandersnatch Member Key
1872
1899
  #
1873
1900
  # Account: alice
1874
- # Member Key: 0x66813b70ba616b374c90ac92edff6e3be95e12adbc93ea7a6c37cbf334ab87e2
1901
+ # Member Key: 0xbb6ee099b568f1844d62fc00e6305c2e83aa8da30ce59e664ef39e089204d43c
1875
1902
 
1876
1903
  # Derive keyed member key (full person — "candidate" context)
1877
1904
  dot verifiable alice --context candidate
@@ -1880,7 +1907,7 @@ dot verifiable alice --context candidate
1880
1907
  #
1881
1908
  # Account: alice
1882
1909
  # Context: candidate
1883
- # Member Key: 0x2fd5b74033d904cf5575b932507939c5d43811e488223229eaf5596565f15ae6
1910
+ # Member Key: 0x5f915576987547d3e55bb4129ac8cae1d338f8933073dc74272b4c825f738592
1884
1911
 
1885
1912
  # Arbitrary context string
1886
1913
  dot verifiable alice --context pps
package/dist/cli.mjs CHANGED
@@ -461,6 +461,7 @@ function suggestMessage(kind, input, candidates) {
461
461
  }
462
462
 
463
463
  // src/core/accounts.ts
464
+ import { hexToBytes as nobleHexToBytes } from "@noble/hashes/utils.js";
464
465
  import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
465
466
  import {
466
467
  DEV_PHRASE,
@@ -471,7 +472,7 @@ import {
471
472
  ss58Decode,
472
473
  validateMnemonic
473
474
  } from "@polkadot-labs/hdkd-helpers";
474
- import { HDKD, secretFromSeed } from "@scure/sr25519";
475
+ import { getPublicKey, HDKD, secretFromSeed, sign } from "@scure/sr25519";
475
476
  import { getPolkadotSigner } from "polkadot-api/signer";
476
477
  function isDevAccount(name) {
477
478
  return DEV_NAMES.includes(name.toLowerCase());
@@ -486,18 +487,54 @@ function deriveFromMnemonic(mnemonic, path) {
486
487
  return derive(path);
487
488
  }
488
489
  function deriveFromHexSeed(hexSeed, path) {
489
- const clean = hexSeed.startsWith("0x") ? hexSeed.slice(2) : hexSeed;
490
- const seed = new Uint8Array(clean.length / 2);
491
- for (let i = 0;i < clean.length; i += 2) {
492
- seed[i / 2] = parseInt(clean.substring(i, i + 2), 16);
493
- }
494
- const derive = sr25519CreateDerive(seed);
490
+ const derive = sr25519CreateDerive(hexToBytes(hexSeed));
495
491
  return derive(path);
496
492
  }
497
493
  function getDevKeypair(name) {
498
494
  const path = devDerivationPath(name);
499
495
  return deriveFromMnemonic(DEV_PHRASE, path);
500
496
  }
497
+ function hexToBytes(hex) {
498
+ return nobleHexToBytes(hex.startsWith("0x") ? hex.slice(2) : hex);
499
+ }
500
+ function isExpandedSecret(secret) {
501
+ return EXPANDED_SECRET_RE.test(secret);
502
+ }
503
+ function isHexSeed(secret) {
504
+ return HEX_SEED_RE.test(secret);
505
+ }
506
+ function secretKind(secret) {
507
+ if (isExpandedSecret(secret))
508
+ return "expanded";
509
+ if (isHexSeed(secret))
510
+ return "seed";
511
+ return "mnemonic";
512
+ }
513
+ function assertNoPathForExpandedSecret(derivationPath) {
514
+ if (derivationPath) {
515
+ throw new Error("Stored account has a 64-byte expanded secret with a derivation path, which cannot be applied. An expanded secret cannot be HD-derived; remove the derivationPath from accounts.json.");
516
+ }
517
+ }
518
+ function keypairFromExpandedSecret(secret) {
519
+ return {
520
+ publicKey: getPublicKey(secret),
521
+ sign: (msg) => sign(secret, msg)
522
+ };
523
+ }
524
+ function keypairFromSecret(secret, derivationPath = "") {
525
+ if (isExpandedSecret(secret)) {
526
+ assertNoPathForExpandedSecret(derivationPath);
527
+ return keypairFromExpandedSecret(hexToBytes(secret));
528
+ }
529
+ return isHexSeed(secret) ? deriveFromHexSeed(secret, derivationPath) : deriveFromMnemonic(secret, derivationPath);
530
+ }
531
+ function expandedSecretFromStored(secret, derivationPath = "") {
532
+ if (isExpandedSecret(secret)) {
533
+ assertNoPathForExpandedSecret(derivationPath);
534
+ return hexToBytes(secret);
535
+ }
536
+ return deriveExpandedSecret(miniSecretFromSecret(secret), derivationPath);
537
+ }
501
538
  function parseDerivations(path) {
502
539
  const out = [];
503
540
  for (const [, type, code] of path.matchAll(DERIVATION_RE)) {
@@ -528,17 +565,11 @@ function deriveExpandedSecret(miniSecret, path) {
528
565
  return parseDerivations(path).reduce((sk, [type, code]) => type === "hard" ? HDKD.secretHard(sk, createChainCode(code)) : HDKD.secretSoft(sk, createChainCode(code)), secretFromSeed(miniSecret));
529
566
  }
530
567
  function miniSecretFromSecret(secret) {
531
- const isHexSeed = /^0x[0-9a-fA-F]{64}$/.test(secret);
532
- if (isHexSeed) {
533
- const clean = secret.slice(2);
534
- const bytes = new Uint8Array(32);
535
- for (let i = 0;i < clean.length; i += 2) {
536
- bytes[i / 2] = parseInt(clean.substring(i, i + 2), 16);
537
- }
538
- return bytes;
568
+ if (isHexSeed(secret)) {
569
+ return hexToBytes(secret);
539
570
  }
540
571
  if (!validateMnemonic(secret)) {
541
- throw new Error("Invalid secret. Expected a 0x-prefixed 32-byte hex seed or a valid BIP39 mnemonic.");
572
+ throw new Error("Invalid secret. Expected a BIP39 mnemonic or a 0x-prefixed 32-byte hex seed.");
542
573
  }
543
574
  return entropyToMiniSecret(mnemonicToEntropy(secret));
544
575
  }
@@ -555,13 +586,19 @@ function createNewAccount(path = "") {
555
586
  return { mnemonic, publicKey: keypair.publicKey };
556
587
  }
557
588
  function importAccount(secret, path = "") {
558
- const isHexSeed = /^0x[0-9a-fA-F]{64}$/.test(secret);
559
- if (isHexSeed) {
589
+ if (isExpandedSecret(secret)) {
590
+ if (path) {
591
+ throw new Error("Derivation paths are not supported for raw private key (64-byte expanded secret) import. An expanded secret cannot be HD-derived; omit --path.");
592
+ }
593
+ const keypair2 = keypairFromExpandedSecret(hexToBytes(secret));
594
+ return { publicKey: keypair2.publicKey };
595
+ }
596
+ if (isHexSeed(secret)) {
560
597
  const keypair2 = deriveFromHexSeed(secret, path);
561
598
  return { publicKey: keypair2.publicKey };
562
599
  }
563
600
  if (!validateMnemonic(secret)) {
564
- throw new Error("Invalid secret. Expected a 0x-prefixed 32-byte hex seed or a valid BIP39 mnemonic.");
601
+ throw new Error("Invalid secret. Expected a BIP39 mnemonic, a 0x-prefixed 32-byte hex seed, or a 0x-prefixed 64-byte sr25519 expanded secret.");
565
602
  }
566
603
  const keypair = deriveFromMnemonic(secret, path);
567
604
  return { publicKey: keypair.publicKey };
@@ -627,9 +664,7 @@ async function resolveAccountKeypair(name) {
627
664
  if (account.secret === undefined) {
628
665
  throw new Error(`Account "${name}" is watch-only (no secret). Cannot sign. Import with --secret or --env.`);
629
666
  }
630
- const secret = resolveSecret(account.secret);
631
- const isHexSeed = /^0x[0-9a-fA-F]{64}$/.test(secret);
632
- return isHexSeed ? deriveFromHexSeed(secret, account.derivationPath) : deriveFromMnemonic(secret, account.derivationPath);
667
+ return keypairFromSecret(resolveSecret(account.secret), account.derivationPath);
633
668
  }
634
669
  async function resolveAccountSigner(name) {
635
670
  const keypair = await resolveAccountKeypair(name);
@@ -637,8 +672,8 @@ async function resolveAccountSigner(name) {
637
672
  }
638
673
  async function resolveAccountExpandedSecret(name) {
639
674
  if (isDevAccount(name)) {
640
- const miniSecret2 = entropyToMiniSecret(mnemonicToEntropy(DEV_PHRASE));
641
- return deriveExpandedSecret(miniSecret2, devDerivationPath(name));
675
+ const miniSecret = entropyToMiniSecret(mnemonicToEntropy(DEV_PHRASE));
676
+ return deriveExpandedSecret(miniSecret, devDerivationPath(name));
642
677
  }
643
678
  const accountsFile = await loadAccounts();
644
679
  const account = findAccount(accountsFile, name);
@@ -655,16 +690,17 @@ async function resolveAccountExpandedSecret(name) {
655
690
  if (account.secret === undefined) {
656
691
  throw new Error(`Account "${name}" is watch-only (no secret). Cannot derive private key. Import with --secret or --env.`);
657
692
  }
658
- const miniSecret = miniSecretFromSecret(resolveSecret(account.secret));
659
- return deriveExpandedSecret(miniSecret, account.derivationPath);
693
+ return expandedSecretFromStored(resolveSecret(account.secret), account.derivationPath);
660
694
  }
661
695
  function bytesToHex(bytes) {
662
696
  return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
663
697
  }
664
- var DEV_NAMES, DERIVATION_RE;
698
+ var DEV_NAMES, EXPANDED_SECRET_RE, HEX_SEED_RE, DERIVATION_RE;
665
699
  var init_accounts = __esm(() => {
666
700
  init_accounts_store();
667
701
  DEV_NAMES = ["alice", "bob", "charlie", "dave", "eve", "ferdie"];
702
+ EXPANDED_SECRET_RE = /^0x[0-9a-fA-F]{128}$/;
703
+ HEX_SEED_RE = /^0x[0-9a-fA-F]{64}$/;
668
704
  DERIVATION_RE = /(\/{1,2})([^/]+)/g;
669
705
  });
670
706
 
@@ -1264,7 +1300,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
1264
1300
  let bytes;
1265
1301
  try {
1266
1302
  const hex = await withTimeout(client._request("state_call", ["Metadata_metadata_at_version", v15Arg]), chainName);
1267
- const raw = hexToBytes2(hex);
1303
+ const raw = hexToBytes3(hex);
1268
1304
  const decoded = optionalOpaqueBytes.dec(raw);
1269
1305
  if (decoded !== undefined) {
1270
1306
  bytes = new Uint8Array(decoded);
@@ -1273,7 +1309,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
1273
1309
  if (!bytes) {
1274
1310
  try {
1275
1311
  const hex = await withTimeout(client._request("state_getMetadata", []), chainName);
1276
- bytes = hexToBytes2(hex);
1312
+ bytes = hexToBytes3(hex);
1277
1313
  } catch (err) {
1278
1314
  if (err instanceof ConnectionError)
1279
1315
  throw err;
@@ -1469,7 +1505,7 @@ function describeCallArgs(meta, palletName, callName) {
1469
1505
  function describeEventFields(meta, palletName, eventName) {
1470
1506
  return compactArgsString(getEventFields(meta, palletName, eventName));
1471
1507
  }
1472
- function hexToBytes2(hex) {
1508
+ function hexToBytes3(hex) {
1473
1509
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
1474
1510
  const bytes = new Uint8Array(clean.length / 2);
1475
1511
  for (let i = 0;i < clean.length; i += 2) {
@@ -3198,7 +3234,7 @@ var init_xxh64 = __esm(() => {
3198
3234
  import { blake2b as blake2b2 } from "@noble/hashes/blake2.js";
3199
3235
  import { sha256 } from "@noble/hashes/sha2.js";
3200
3236
  import { keccak_256 as keccak_2562 } from "@noble/hashes/sha3.js";
3201
- import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes3 } from "@noble/hashes/utils.js";
3237
+ import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes4 } from "@noble/hashes/utils.js";
3202
3238
  function computeHash(algorithm, data) {
3203
3239
  const algo = ALGORITHMS[algorithm];
3204
3240
  if (!algo) {
@@ -3212,7 +3248,7 @@ function parseInputData(input) {
3212
3248
  if (hex.length % 2 !== 0) {
3213
3249
  throw new Error(`Invalid hex input: odd number of characters`);
3214
3250
  }
3215
- return hexToBytes3(hex);
3251
+ return hexToBytes4(hex);
3216
3252
  }
3217
3253
  return new TextEncoder().encode(input);
3218
3254
  }
@@ -3660,17 +3696,17 @@ var init_complete = __esm(() => {
3660
3696
  // src/cli.ts
3661
3697
  import cac from "cac";
3662
3698
  // package.json
3663
- var version = "1.19.0";
3699
+ var version = "1.20.0";
3664
3700
 
3665
3701
  // src/commands/account.ts
3666
3702
  init_accounts_store();
3667
3703
  init_accounts();
3668
3704
  import { readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
3669
- import { hexToBytes as nobleHexToBytes } from "@noble/hashes/utils.js";
3705
+ import { hexToBytes as nobleHexToBytes2 } from "@noble/hashes/utils.js";
3670
3706
 
3671
3707
  // src/core/h160.ts
3672
3708
  import { keccak_256 } from "@noble/hashes/sha3.js";
3673
- import { bytesToHex as bytesToHex2, hexToBytes } from "@noble/hashes/utils.js";
3709
+ import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
3674
3710
  var ETH_DERIVED_SUFFIX_BYTE = 238;
3675
3711
  var H160_LEN = 20;
3676
3712
  var ACCOUNT_ID_LEN = 32;
@@ -3722,7 +3758,7 @@ function h160FromHex(input) {
3722
3758
  if (!isH160Hex(input)) {
3723
3759
  throw new Error(`Not a valid 0x-prefixed 20-byte hex string: ${input}`);
3724
3760
  }
3725
- return hexToBytes(input.slice(2));
3761
+ return hexToBytes2(input.slice(2));
3726
3762
  }
3727
3763
 
3728
3764
  // src/commands/account.ts
@@ -3797,7 +3833,8 @@ function isValidParaId(value) {
3797
3833
  var ACCOUNT_HELP = `
3798
3834
  ${BOLD}Usage:${RESET}
3799
3835
  $ dot account add <name> <ss58|hex> Add a watch-only address (no secret)
3800
- $ dot account add <name> --secret <s> [--path <derivation>] Import from BIP39 mnemonic
3836
+ $ dot account add <name> --secret <s> [--path <derivation>] Import from BIP39 mnemonic or 32-byte hex seed
3837
+ $ dot account add <name> --secret 0x<128 hex> Import a raw 64-byte sr25519 private key (no --path)
3801
3838
  $ dot account add <name> --env <VAR> [--path <derivation>] Import account backed by env variable
3802
3839
  $ dot account add <name> --parachain <id> --parachain-type <t> Derive a parachain sovereign (t = child|sibling)
3803
3840
  $ dot account add <name> --pallet-id <8 chars or 0x hex> Derive a pallet sovereign (e.g. py/trsry)
@@ -3814,6 +3851,7 @@ ${BOLD}Usage:${RESET}
3814
3851
  ${BOLD}Examples:${RESET}
3815
3852
  $ dot account add treasury 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
3816
3853
  $ dot account add treasury --secret "word1 word2 ... word12"
3854
+ $ dot account add raw-key --secret 0x<128-hex-char sr25519 expanded secret>
3817
3855
  $ dot account add ci-signer --env MY_SECRET --path //ci
3818
3856
  $ dot account add Treasury --pallet-id py/trsry
3819
3857
  $ dot account add Bounties --pallet-id 0x70792f626f756e74
@@ -3841,16 +3879,21 @@ ${BOLD}Examples:${RESET}
3841
3879
 
3842
3880
  ${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
3843
3881
  Use --env to keep secrets off disk entirely.
3844
- Hex seed import (0x...) is not supported via CLI.${RESET}
3882
+ --secret accepts a BIP39 mnemonic, a 0x 32-byte hex seed, or a
3883
+ 0x 64-byte raw sr25519 private key (the value --show-secret prints).
3884
+ Raw private keys cannot be HD-derived, so --path is rejected for them.${RESET}
3845
3885
  `.trimStart();
3846
3886
  function registerAccountCommands(cli) {
3847
- cli.command("account [action] [...names]", "Manage local accounts (create, import, list, remove, export)").alias("accounts").option("--secret <value>", "Secret key (mnemonic or hex seed) for import").option("--env <varName>", "Environment variable name holding the secret").option("--path <derivation>", "Derivation path (e.g. //staking, //polkadot//0/wallet)").option("--parachain <id>", "Derive a parachain sovereign account (requires --parachain-type)").option("--parachain-type <type>", "Parachain sovereign type: child or sibling").option("--pallet-id <id>", "Derive a pallet sovereign account from an 8-byte PalletId").option("--prefix <number>", "SS58 prefix for address encoding (default: 42)").option("--file <path>", "Input/output file for batch import/export").option("--overwrite", "Overwrite existing accounts on batch import").option("--dry-run", "Preview batch import without applying changes").option("--include-secrets", "Include secrets in export (redacted by default)").option("--watch-only", "Export only watch-only accounts").option("--show-secret", "Reveal the 64-byte sr25519 expanded private key (inspect only)").action(async (action, names, opts) => {
3887
+ cli.command("account [action] [...names]", "Manage local accounts (create, import, list, remove, export)").alias("accounts").option("--secret <value>", "Secret for import: BIP39 mnemonic, 0x 32-byte hex seed, or 0x 64-byte raw private key").option("--env <varName>", "Environment variable name holding the secret").option("--path <derivation>", "Derivation path (e.g. //staking, //polkadot//0/wallet)").option("--parachain <id>", "Derive a parachain sovereign account (requires --parachain-type)").option("--parachain-type <type>", "Parachain sovereign type: child or sibling").option("--pallet-id <id>", "Derive a pallet sovereign account from an 8-byte PalletId").option("--prefix <number>", "SS58 prefix for address encoding (default: 42)").option("--file <path>", "Input/output file for batch import/export").option("--overwrite", "Overwrite existing accounts on batch import").option("--dry-run", "Preview batch import without applying changes").option("--include-secrets", "Include secrets in export (redacted by default)").option("--watch-only", "Export only watch-only accounts").option("--show-secret", "Reveal the 64-byte sr25519 expanded private key (inspect only)").action(async (action, names, opts) => {
3848
3888
  if (!action) {
3849
3889
  if (process.argv[2] === "accounts")
3850
3890
  return accountList(opts);
3851
3891
  console.log(ACCOUNT_HELP);
3852
3892
  return;
3853
3893
  }
3894
+ const rawSecret = rawArgValue("--secret");
3895
+ if (rawSecret != null)
3896
+ opts.secret = rawSecret;
3854
3897
  switch (action) {
3855
3898
  case "new":
3856
3899
  case "create":
@@ -4492,8 +4535,9 @@ async function accountInspect(input, opts) {
4492
4535
  }
4493
4536
  }
4494
4537
  const ss58 = toSs58(publicKeyHex, prefix);
4495
- const h160Hex = toEip55(accountIdToH160(nobleHexToBytes(publicKeyHex.slice(2))));
4538
+ const h160Hex = toEip55(accountIdToH160(nobleHexToBytes2(publicKeyHex.slice(2))));
4496
4539
  let privateKeyHex;
4540
+ let revealedSecret;
4497
4541
  if (opts.showSecret) {
4498
4542
  if (!name) {
4499
4543
  console.error("--show-secret requires an account name; raw addresses and hex keys have no secret to reveal.");
@@ -4509,6 +4553,14 @@ async function accountInspect(input, opts) {
4509
4553
  console.error(err.message);
4510
4554
  process.exit(1);
4511
4555
  }
4556
+ if (storedAccount?.secret !== undefined && !isEnvSecret(storedAccount.secret)) {
4557
+ const kind = secretKind(storedAccount.secret);
4558
+ if (kind === "mnemonic") {
4559
+ revealedSecret = { label: "Mnemonic", field: "mnemonic", value: storedAccount.secret };
4560
+ } else if (kind === "seed") {
4561
+ revealedSecret = { label: "Seed", field: "seed", value: storedAccount.secret };
4562
+ }
4563
+ }
4512
4564
  }
4513
4565
  let kindLabel;
4514
4566
  let sourceLine;
@@ -4587,6 +4639,8 @@ async function accountInspect(input, opts) {
4587
4639
  result.env = envLine.replace(/^\$/, "");
4588
4640
  if (bandersnatch && Object.keys(bandersnatch).length > 0)
4589
4641
  result.bandersnatch = bandersnatch;
4642
+ if (revealedSecret)
4643
+ result[revealedSecret.field] = revealedSecret.value;
4590
4644
  if (privateKeyHex)
4591
4645
  result.privateKey = privateKeyHex;
4592
4646
  console.log(formatJson(result));
@@ -4618,6 +4672,12 @@ async function accountInspect(input, opts) {
4618
4672
  }
4619
4673
  }
4620
4674
  console.log(` ${BOLD}Prefix:${RESET} ${prefix}`);
4675
+ if (revealedSecret) {
4676
+ console.log(` ${BOLD}${`${revealedSecret.label}:`.padEnd(13)}${RESET}${revealedSecret.value}`);
4677
+ if (derivationLine) {
4678
+ console.log(` ${YELLOW}(derived with ${derivationLine} — re-import needs --path ${derivationLine})${RESET}`);
4679
+ }
4680
+ }
4621
4681
  if (privateKeyHex) {
4622
4682
  console.log(` ${BOLD}Private Key:${RESET} ${privateKeyHex}`);
4623
4683
  console.log(` ${YELLOW}(sr25519 expanded, 64 bytes — never share)${RESET}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polkadot-cli",
3
- "version": "1.19.0",
3
+ "version": "1.20.0",
4
4
  "description": "CLI tool for querying Polkadot-ecosystem on-chain state",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,22 +38,22 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@noble/hashes": "^2.0.1",
41
- "@polkadot-api/metadata-builders": "^0.14.1",
42
- "@polkadot-api/substrate-bindings": "^0.20.1",
41
+ "@polkadot-api/metadata-builders": "^0.14.3",
42
+ "@polkadot-api/substrate-bindings": "^0.20.3",
43
43
  "@polkadot-api/substrate-client": "^0.7.0",
44
- "@polkadot-api/view-builder": "^0.5.1",
44
+ "@polkadot-api/view-builder": "^0.5.3",
45
45
  "@polkadot-labs/hdkd": "^0.0.28",
46
46
  "@polkadot-labs/hdkd-helpers": "^0.0.29",
47
47
  "@scure/sr25519": "^1.0.0",
48
48
  "cac": "^6.7.14",
49
- "polkadot-api": "^2.0.1",
50
- "verifiablejs": "1.3.0-beta.2",
49
+ "polkadot-api": "^2.1.4",
50
+ "verifiablejs": "1.3.0",
51
51
  "yaml": "^2.8.3"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@biomejs/biome": "^2.4.5",
55
55
  "@changesets/cli": "^2.29.4",
56
- "@polkadot-api/metadata-compatibility": "^0.6.1",
56
+ "@polkadot-api/metadata-compatibility": "^0.6.3",
57
57
  "@types/bun": "^1.3.9",
58
58
  "husky": "^9.1.7"
59
59
  }