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.
- package/README.md +39 -12
- package/dist/cli.mjs +100 -40
- 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
|
|
493
|
-
#
|
|
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
|
|
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 |
|
|
544
|
-
|
|
545
|
-
| BIP39 mnemonic (12/24 words) | `"abandon abandon ... about"` |
|
|
546
|
-
| Hex seed (`0x` + 64 hex chars) | `
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
|
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
|
-
|
|
532
|
-
|
|
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
|
|
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
|
-
|
|
559
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
641
|
-
return deriveExpandedSecret(
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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.
|
|
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.
|
|
42
|
-
"@polkadot-api/substrate-bindings": "^0.20.
|
|
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.
|
|
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.
|
|
50
|
-
"verifiablejs": "1.3.0
|
|
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.
|
|
56
|
+
"@polkadot-api/metadata-compatibility": "^0.6.3",
|
|
57
57
|
"@types/bun": "^1.3.9",
|
|
58
58
|
"husky": "^9.1.7"
|
|
59
59
|
}
|