polkadot-cli 1.14.2 → 1.15.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 +143 -25
- package/dist/cli.mjs +360 -114
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Ships with Polkadot and all system parachains preconfigured with multiple fallba
|
|
|
14
14
|
- ✅ zsh, bash, and fish autocompletion
|
|
15
15
|
- ✅ Exposes all on-chain metadata documentation
|
|
16
16
|
- ✅ Encode, dry-run, and submit extrinsics
|
|
17
|
-
- ✅ Support for custom signed extensions
|
|
17
|
+
- ✅ Support for custom signed extensions — and a `dot <chain>.extensions` inspector to discover them
|
|
18
18
|
- ✅ Built with agent use in mind — structured JSON output on every command (`--json`)
|
|
19
19
|
- ✅ Fuzzy matching with typo suggestions
|
|
20
20
|
- ✅ Account management — BIP39 mnemonics, derivation paths, env-backed secrets, watch-only, dev accounts
|
|
@@ -29,6 +29,7 @@ Ships with Polkadot and all system parachains preconfigured with multiple fallba
|
|
|
29
29
|
- ✅ Non-native fee payment — pay tx fees in any asset the chain accepts via `--asset` (asset-hub-style chains)
|
|
30
30
|
- ✅ Bandersnatch member keys — derive Ring VRF member keys from mnemonics for on-chain member sets
|
|
31
31
|
- ✅ Export/import — portable chain and account configuration for backup, sharing, and CI bootstrapping
|
|
32
|
+
- ✅ Claude Code skill — `dot-cli` skill installable as a plugin marketplace, teaches agents how to drive the CLI
|
|
32
33
|
|
|
33
34
|
### Preconfigured chains
|
|
34
35
|
|
|
@@ -57,6 +58,25 @@ npm install -g polkadot-cli@latest
|
|
|
57
58
|
|
|
58
59
|
This installs the `dot` command globally.
|
|
59
60
|
|
|
61
|
+
## Claude Code skill
|
|
62
|
+
|
|
63
|
+
This repo ships a [Claude Code](https://claude.com/claude-code) skill that teaches Claude how to drive the `dot` CLI — query patterns, tx encoding, runtime API calls, and bash scripting gotchas.
|
|
64
|
+
|
|
65
|
+
Register the marketplace and install the skill:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
/plugin marketplace add peetzweg/polkadot-cli
|
|
69
|
+
/plugin install dot-cli@polkadot-cli
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The skill auto-triggers when you ask Claude about `dot`, Substrate storage queries, extrinsic submission, runtime APIs, or XCM. You can also invoke it directly with `/dot-cli`.
|
|
73
|
+
|
|
74
|
+
To pull the latest skill updates:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
/plugin marketplace update polkadot-cli
|
|
78
|
+
```
|
|
79
|
+
|
|
60
80
|
## Usage
|
|
61
81
|
|
|
62
82
|
### Manage chains
|
|
@@ -133,7 +153,7 @@ Chain names are case-insensitive (`Polkadot.query.System.Number` works the same)
|
|
|
133
153
|
|
|
134
154
|
#### Export/import chain configuration
|
|
135
155
|
|
|
136
|
-
Export and import chain configurations for backup, sharing across machines, or team collaboration.
|
|
156
|
+
Export and import chain configurations for backup, sharing across machines, or team collaboration.
|
|
137
157
|
|
|
138
158
|
```bash
|
|
139
159
|
# Export custom chains to stdout (pipe-friendly JSON)
|
|
@@ -157,12 +177,36 @@ dot chain import my-chains.json --dry-run
|
|
|
157
177
|
# Overwrite existing chains
|
|
158
178
|
dot chain import my-chains.json --overwrite
|
|
159
179
|
|
|
180
|
+
# Skip automatic metadata fetch (faster for offline/CI bootstrap)
|
|
181
|
+
dot chain import my-chains.json --no-metadata
|
|
182
|
+
|
|
160
183
|
# Pipe between machines
|
|
161
184
|
ssh remote-dev "dot chain export" | dot chain import -
|
|
162
185
|
```
|
|
163
186
|
|
|
164
187
|
By default, `export` only includes user-added chains and built-ins with modified RPCs. Use `--all` to include everything. Import skips existing chains unless `--overwrite` is passed, and validates relay references with warnings for missing relays.
|
|
165
188
|
|
|
189
|
+
After a non-dry-run import, metadata is fetched automatically for each newly added or overwritten chain so tab completion and metadata-dependent commands work immediately. Pass `--no-metadata` to skip the fetch — you can always backfill later with `dot chain update --all`.
|
|
190
|
+
|
|
191
|
+
Output shows one line per chain with a status glyph and a terse summary:
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
✓ preview
|
|
195
|
+
✓ preview-people
|
|
196
|
+
⟳ polkadot (overwritten)
|
|
197
|
+
- paseo (skipped)
|
|
198
|
+
|
|
199
|
+
2 added, 1 overwritten, 1 skipped
|
|
200
|
+
|
|
201
|
+
Updating metadata for 3 chain(s)...
|
|
202
|
+
|
|
203
|
+
✓ preview
|
|
204
|
+
✓ preview-people
|
|
205
|
+
✓ polkadot
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Running `dot chain import` with no file path prints the subcommand help instead of blocking on stdin.
|
|
209
|
+
|
|
166
210
|
### Manage accounts
|
|
167
211
|
|
|
168
212
|
Dev accounts (Alice, Bob, Charlie, Dave, Eve, Ferdie) are always available for testnets. Create or import your own accounts for any chain.
|
|
@@ -187,14 +231,14 @@ dot account create my-validator
|
|
|
187
231
|
# Create with a derivation path
|
|
188
232
|
dot account create my-staking --path //staking
|
|
189
233
|
|
|
190
|
-
#
|
|
191
|
-
dot account
|
|
234
|
+
# Add a keyed account from a BIP39 mnemonic
|
|
235
|
+
dot account add treasury --secret "word1 word2 ... word12"
|
|
192
236
|
|
|
193
|
-
#
|
|
194
|
-
dot account
|
|
237
|
+
# Add with a derivation path
|
|
238
|
+
dot account add hot-wallet --secret "word1 word2 ... word12" --path //hot
|
|
195
239
|
|
|
196
|
-
#
|
|
197
|
-
dot account
|
|
240
|
+
# Add an env-var-backed account (secret stays off disk)
|
|
241
|
+
dot account add ci-signer --env MY_SECRET
|
|
198
242
|
|
|
199
243
|
# Derive a child account from an existing one
|
|
200
244
|
dot account derive treasury treasury-staking --path //staking
|
|
@@ -212,9 +256,9 @@ dot account export --include-secrets --file backup.json
|
|
|
212
256
|
dot account export --watch-only
|
|
213
257
|
|
|
214
258
|
# Batch-import accounts from a file
|
|
215
|
-
dot account import
|
|
216
|
-
dot account import
|
|
217
|
-
dot account import
|
|
259
|
+
dot account import team-accounts.json
|
|
260
|
+
dot account import accounts.json --dry-run
|
|
261
|
+
dot account import accounts.json --overwrite
|
|
218
262
|
|
|
219
263
|
# Inspect an account — show public key and SS58 address
|
|
220
264
|
dot account inspect alice
|
|
@@ -236,7 +280,7 @@ dot account add council 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684
|
|
|
236
280
|
|
|
237
281
|
Watch-only accounts appear in `dot account list` with a `(watch-only)` badge and can be inspected and removed like any other account. They cannot be used with `--from` (signing) or as a source for `derive`.
|
|
238
282
|
|
|
239
|
-
The `add` subcommand is context-sensitive: bare `add <name> <address>` creates a watch-only entry, while `add --secret` or `add --env` imports a keyed account
|
|
283
|
+
The `add` subcommand is context-sensitive: bare `add <name> <address>` creates a watch-only entry, while `add --secret` or `add --env` imports a keyed account. `dot account import` is reserved for file-based batch import.
|
|
240
284
|
|
|
241
285
|
#### Named address resolution
|
|
242
286
|
|
|
@@ -297,21 +341,32 @@ dot account inspect alice --json
|
|
|
297
341
|
# {"publicKey":"0xd435...a27d","ss58":"5Grw...utQY","prefix":42,"name":"Alice"}
|
|
298
342
|
```
|
|
299
343
|
|
|
344
|
+
#### Reveal the sr25519 private key
|
|
345
|
+
|
|
346
|
+
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:
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
dot account inspect dave --show-secret
|
|
350
|
+
# Private Key: 0x<128 hex chars> (sr25519 expanded, 64 bytes — never share)
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
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.
|
|
354
|
+
|
|
300
355
|
#### Env-var-backed accounts
|
|
301
356
|
|
|
302
357
|
For CI/CD and security-conscious workflows, store a reference to an environment variable instead of the secret itself:
|
|
303
358
|
|
|
304
359
|
```bash
|
|
305
|
-
dot account
|
|
360
|
+
dot account add ci-signer --env MY_SECRET
|
|
306
361
|
```
|
|
307
362
|
|
|
308
|
-
`--secret` and `--env` are mutually exclusive. `add` is
|
|
363
|
+
`--secret` and `--env` are mutually exclusive. Use `dot account add` for single-account imports; `dot account import` is reserved for file-based batch import.
|
|
309
364
|
|
|
310
365
|
The secret is never written to disk. At signing time, the CLI reads `$MY_SECRET` and derives the keypair. If the variable is not set, the CLI errors with a clear message. `account list` shows an `(env: MY_SECRET)` badge and resolves the address live when the variable is available.
|
|
311
366
|
|
|
312
367
|
#### Derivation paths
|
|
313
368
|
|
|
314
|
-
Use `--path` with `create`, `
|
|
369
|
+
Use `--path` with `create`, `add`, or the `derive` action to derive child keys from the same secret. Different paths produce different keypairs, enabling key separation (e.g. staking vs. governance) without managing multiple mnemonics.
|
|
315
370
|
|
|
316
371
|
```bash
|
|
317
372
|
# Create with a derivation path
|
|
@@ -320,8 +375,8 @@ dot account create my-staking --path //staking
|
|
|
320
375
|
# Multi-segment path (hard + soft junctions)
|
|
321
376
|
dot account create multi --path //polkadot//0/wallet
|
|
322
377
|
|
|
323
|
-
#
|
|
324
|
-
dot account
|
|
378
|
+
# Add with a path
|
|
379
|
+
dot account add hot --secret "word1 word2 ..." --path //hot
|
|
325
380
|
|
|
326
381
|
# Derive a child from an existing account
|
|
327
382
|
dot account derive treasury treasury-staking --path //staking
|
|
@@ -362,20 +417,24 @@ dot account export --include-secrets --file backup.json
|
|
|
362
417
|
# Export only watch-only accounts (always safe)
|
|
363
418
|
dot account export --watch-only
|
|
364
419
|
|
|
365
|
-
# Batch-import accounts from a file
|
|
366
|
-
dot account import
|
|
420
|
+
# Batch-import accounts from a file (positional path, like `dot chain import`)
|
|
421
|
+
dot account import team-accounts.json
|
|
367
422
|
|
|
368
423
|
# Preview without applying
|
|
369
|
-
dot account import
|
|
424
|
+
dot account import accounts.json --dry-run
|
|
370
425
|
|
|
371
426
|
# Overwrite existing accounts
|
|
372
|
-
dot account import
|
|
427
|
+
dot account import accounts.json --overwrite
|
|
373
428
|
|
|
374
429
|
# Pipe from another machine
|
|
375
|
-
ssh remote-dev "dot account export --watch-only" | dot account import
|
|
430
|
+
ssh remote-dev "dot account export --watch-only" | dot account import -
|
|
376
431
|
```
|
|
377
432
|
|
|
378
|
-
|
|
433
|
+
Output mirrors `dot chain import` — one line per account with a status glyph (`✓` added, `⟳` overwritten, `-` skipped) and a terse count summary at the end. Running `dot account import` with no file path prints the subcommand help instead of blocking on stdin.
|
|
434
|
+
|
|
435
|
+
`dot account import` is file-only. For a single-account import from a mnemonic or env variable, use `dot account add <name> --secret "..."` or `dot account add <name> --env VAR`.
|
|
436
|
+
|
|
437
|
+
Security: default export replaces mnemonic/seed with `"<redacted>"`. `--include-secrets` is required for actual secrets. Env-backed accounts export the variable *name* (e.g. `{"env": "MY_SECRET"}`), never the value. Redacted accounts import as watch-only (public key preserved, no signing capability).
|
|
379
438
|
|
|
380
439
|
### Chain prefix
|
|
381
440
|
|
|
@@ -409,7 +468,7 @@ dot polkadot.apis.Core
|
|
|
409
468
|
dot apis Core --chain polkadot
|
|
410
469
|
```
|
|
411
470
|
|
|
412
|
-
This works for all categories (`query`, `tx`, `const`, `events`, `errors`, `apis`). When passing positional method arguments, keep `Pallet` and `Item` either fully dot-joined (`query.System.Account 5Grw...`) or fully space-separated (`query System Account 5Grw...`) — mixing the two (`query System.Account 5Grw...`) does not work because the second arg gets parsed as a pallet name.
|
|
471
|
+
This works for all categories (`query`, `tx`, `const`, `events`, `errors`, `apis`, `extensions`). When passing positional method arguments, keep `Pallet` and `Item` either fully dot-joined (`query.System.Account 5Grw...`) or fully space-separated (`query System Account 5Grw...`) — mixing the two (`query System.Account 5Grw...`) does not work because the second arg gets parsed as a pallet name.
|
|
413
472
|
|
|
414
473
|
### Query storage
|
|
415
474
|
|
|
@@ -637,10 +696,44 @@ dot polkadot.const.Balances.ExistentialDeposit # look up value (connects to cha
|
|
|
637
696
|
# Runtime APIs
|
|
638
697
|
dot polkadot.apis # all runtime APIs
|
|
639
698
|
dot polkadot.apis.Core # methods in Core
|
|
699
|
+
|
|
700
|
+
# Transaction extensions (flat — no pallet sub-level)
|
|
701
|
+
dot polkadot.extensions # all transaction extensions
|
|
702
|
+
dot polkadot.extensions.CheckMortality # extension detail
|
|
640
703
|
```
|
|
641
704
|
|
|
642
705
|
`--chain <name>` works as an alternative to the prefix in every form (e.g. `dot tx.Balances --chain polkadot`). To browse pallets across all categories at once, use `dot inspect` (see [Inspect metadata](#inspect-metadata)).
|
|
643
706
|
|
|
707
|
+
### Transaction extensions
|
|
708
|
+
|
|
709
|
+
List the transaction extensions (also known as signed extensions) a chain declares in its runtime, with types and a marker indicating whether `polkadot-api` handles the extension automatically or whether you need to provide a value via `--ext` when building a transaction (see [Submit extrinsics](#submit-extrinsics)).
|
|
710
|
+
|
|
711
|
+
```bash
|
|
712
|
+
# List all transaction extensions on a chain
|
|
713
|
+
dot polkadot.extensions
|
|
714
|
+
|
|
715
|
+
# Detail view for a single extension
|
|
716
|
+
dot polkadot.extensions.CheckMortality
|
|
717
|
+
|
|
718
|
+
# --chain flag form is equivalent
|
|
719
|
+
dot extensions.ChargeTransactionPayment --chain polkadot
|
|
720
|
+
|
|
721
|
+
# Space-separated syntax also works
|
|
722
|
+
dot extensions CheckMortality --chain polkadot
|
|
723
|
+
|
|
724
|
+
# Structured output for scripts
|
|
725
|
+
dot polkadot.extensions --json
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
`extension` and `ext` are aliases for `extensions`. Shell completion suggests identifiers after `dot polkadot.extensions.<Tab>`.
|
|
729
|
+
|
|
730
|
+
The list view tags each entry:
|
|
731
|
+
|
|
732
|
+
- `[builtin]` — `polkadot-api` fills this in for you (e.g. `CheckMortality`, `CheckNonce`, `ChargeTransactionPayment`, `CheckMetadataHash`)
|
|
733
|
+
- `[custom]` — you must provide a value with `--ext` when signing, for example `--ext '{"<Identifier>":{"value":<v>}}'`
|
|
734
|
+
|
|
735
|
+
The detail view shows the extension's value type, its `additionalSigned` type, and a ready-to-adapt `--ext` snippet for custom extensions. Use this to discover what `--ext` payload a chain expects before submitting a `dot tx` command.
|
|
736
|
+
|
|
644
737
|
### Submit extrinsics
|
|
645
738
|
|
|
646
739
|
Build, sign, and submit transactions. Pass a `Pallet.Call` with arguments, or a raw SCALE-encoded call hex (e.g. from a multisig proposal or governance). Both forms display a decoded human-readable representation of the call.
|
|
@@ -806,6 +899,8 @@ For manual override, use `--ext` with a JSON object:
|
|
|
806
899
|
dot tx.System.remark 0xdeadbeef --from alice --chain polkadot --ext '{"MyExtension":{"value":"..."}}'
|
|
807
900
|
```
|
|
808
901
|
|
|
902
|
+
Not sure which extensions a chain exposes? Run `dot <chain>.extensions` (see [Transaction extensions](#transaction-extensions)) to list them all with value types and a `[builtin]` / `[custom]` marker.
|
|
903
|
+
|
|
809
904
|
#### Transaction options
|
|
810
905
|
|
|
811
906
|
Override low-level transaction parameters. Useful for rapid-fire submission (custom nonce), priority fees (tip), or controlling transaction lifetime (mortality).
|
|
@@ -1346,7 +1441,7 @@ The notification is automatically suppressed when:
|
|
|
1346
1441
|
|
|
1347
1442
|
## Configuration
|
|
1348
1443
|
|
|
1349
|
-
Config and metadata caches live in `~/.polkadot
|
|
1444
|
+
Config and metadata caches live in `~/.polkadot/` by default:
|
|
1350
1445
|
|
|
1351
1446
|
```
|
|
1352
1447
|
~/.polkadot/
|
|
@@ -1360,6 +1455,29 @@ Config and metadata caches live in `~/.polkadot/`:
|
|
|
1360
1455
|
|
|
1361
1456
|
> **Warning:** `accounts.json` stores secrets (mnemonics and seeds) in **plain text**. Encrypted-at-rest storage is planned but not yet implemented. Keep appropriate file permissions (`chmod 600 ~/.polkadot/accounts.json`) and do not use this for high-value mainnet accounts.
|
|
1362
1457
|
|
|
1458
|
+
### `DOT_HOME` — redirect the config directory
|
|
1459
|
+
|
|
1460
|
+
Set the `DOT_HOME` environment variable to point at a different directory. When set, the CLI reads and writes **everything** (config, accounts, metadata, update cache) under that path — no `.polkadot` suffix is appended.
|
|
1461
|
+
|
|
1462
|
+
```bash
|
|
1463
|
+
# Use a scratch directory for experimentation
|
|
1464
|
+
DOT_HOME=/tmp/dot-scratch dot account create throwaway
|
|
1465
|
+
|
|
1466
|
+
# Isolated per-project state (e.g. in a repo-local shell)
|
|
1467
|
+
export DOT_HOME="$PWD/.dot"
|
|
1468
|
+
dot chain add local --rpc ws://localhost:9944
|
|
1469
|
+
|
|
1470
|
+
# Unset or empty DOT_HOME falls back to $HOME/.polkadot
|
|
1471
|
+
```
|
|
1472
|
+
|
|
1473
|
+
Typical uses:
|
|
1474
|
+
|
|
1475
|
+
- **Run throwaway commands without touching your real accounts.** Point `DOT_HOME` at a tmpdir so `dot account create`, `dot chain add`, and similar never modify `~/.polkadot/`.
|
|
1476
|
+
- **CI and test harnesses.** Give each job its own `DOT_HOME` so parallel runs don't share state. The project's own test fixture (`runCli`) uses this mechanism.
|
|
1477
|
+
- **Multiple profiles on one machine.** Switch between environments (e.g. a mainnet profile and a local-dev profile) by changing `DOT_HOME`.
|
|
1478
|
+
|
|
1479
|
+
Empty-string `DOT_HOME=""` is treated as unset and falls back to `$HOME/.polkadot` — so a shell-quoting slip can't accidentally send writes to `/`.
|
|
1480
|
+
|
|
1363
1481
|
## Environment compatibility
|
|
1364
1482
|
|
|
1365
1483
|
The CLI works in Node.js (v22+), Bun, and sandboxed runtimes (e.g. LLM tool-use / MCP environments). WebSocket connections use the native `WebSocket` implementation provided by the runtime — no external WebSocket package is required.
|
package/dist/cli.mjs
CHANGED
|
@@ -181,13 +181,20 @@ import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
|
181
181
|
import { homedir } from "node:os";
|
|
182
182
|
import { join } from "node:path";
|
|
183
183
|
function getConfigDir() {
|
|
184
|
-
|
|
184
|
+
const override = process.env.DOT_HOME;
|
|
185
|
+
return override && override.length > 0 ? override : join(homedir(), ".polkadot");
|
|
186
|
+
}
|
|
187
|
+
function getChainsDir() {
|
|
188
|
+
return join(getConfigDir(), "chains");
|
|
185
189
|
}
|
|
186
190
|
function getChainDir(chainName) {
|
|
187
|
-
return join(
|
|
191
|
+
return join(getChainsDir(), chainName);
|
|
188
192
|
}
|
|
189
193
|
function getMetadataPath(chainName) {
|
|
190
|
-
return join(
|
|
194
|
+
return join(getChainDir(chainName), "metadata.bin");
|
|
195
|
+
}
|
|
196
|
+
function getConfigPath() {
|
|
197
|
+
return join(getConfigDir(), "config.json");
|
|
191
198
|
}
|
|
192
199
|
async function ensureDir(dir) {
|
|
193
200
|
await mkdir(dir, { recursive: true });
|
|
@@ -201,9 +208,10 @@ async function fileExists(path) {
|
|
|
201
208
|
}
|
|
202
209
|
}
|
|
203
210
|
async function loadConfig() {
|
|
204
|
-
await ensureDir(
|
|
205
|
-
|
|
206
|
-
|
|
211
|
+
await ensureDir(getConfigDir());
|
|
212
|
+
const configPath = getConfigPath();
|
|
213
|
+
if (await fileExists(configPath)) {
|
|
214
|
+
const saved = JSON.parse(await readFile(configPath, "utf-8"));
|
|
207
215
|
const chains = {};
|
|
208
216
|
for (const [name, defaultConfig] of Object.entries(DEFAULT_CONFIG.chains)) {
|
|
209
217
|
chains[name] = saved.chains[name] ? { ...defaultConfig, ...saved.chains[name] } : defaultConfig;
|
|
@@ -219,8 +227,8 @@ async function loadConfig() {
|
|
|
219
227
|
return DEFAULT_CONFIG;
|
|
220
228
|
}
|
|
221
229
|
async function saveConfig(config) {
|
|
222
|
-
await ensureDir(
|
|
223
|
-
await writeFile(
|
|
230
|
+
await ensureDir(getConfigDir());
|
|
231
|
+
await writeFile(getConfigPath(), `${JSON.stringify(config, null, 2)}
|
|
224
232
|
`);
|
|
225
233
|
}
|
|
226
234
|
async function loadMetadata(chainName) {
|
|
@@ -255,18 +263,17 @@ function resolveChain(config, chainFlag) {
|
|
|
255
263
|
}
|
|
256
264
|
return { name, chain: config.chains[name] };
|
|
257
265
|
}
|
|
258
|
-
var DOT_DIR, CONFIG_PATH, CHAINS_DIR;
|
|
259
266
|
var init_store = __esm(() => {
|
|
260
267
|
init_errors();
|
|
261
268
|
init_types();
|
|
262
|
-
DOT_DIR = join(homedir(), ".polkadot");
|
|
263
|
-
CONFIG_PATH = join(DOT_DIR, "config.json");
|
|
264
|
-
CHAINS_DIR = join(DOT_DIR, "chains");
|
|
265
269
|
});
|
|
266
270
|
|
|
267
271
|
// src/config/accounts-store.ts
|
|
268
272
|
import { access as access2, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
269
273
|
import { join as join2 } from "node:path";
|
|
274
|
+
function getAccountsPath() {
|
|
275
|
+
return join2(getConfigDir(), "accounts.json");
|
|
276
|
+
}
|
|
270
277
|
async function ensureDir2(dir) {
|
|
271
278
|
await mkdir2(dir, { recursive: true });
|
|
272
279
|
}
|
|
@@ -280,24 +287,23 @@ async function fileExists2(path) {
|
|
|
280
287
|
}
|
|
281
288
|
async function loadAccounts() {
|
|
282
289
|
await ensureDir2(getConfigDir());
|
|
283
|
-
|
|
284
|
-
|
|
290
|
+
const path = getAccountsPath();
|
|
291
|
+
if (await fileExists2(path)) {
|
|
292
|
+
const data = await readFile2(path, "utf-8");
|
|
285
293
|
return JSON.parse(data);
|
|
286
294
|
}
|
|
287
295
|
return { accounts: [] };
|
|
288
296
|
}
|
|
289
297
|
async function saveAccounts(file) {
|
|
290
298
|
await ensureDir2(getConfigDir());
|
|
291
|
-
await writeFile2(
|
|
299
|
+
await writeFile2(getAccountsPath(), `${JSON.stringify(file, null, 2)}
|
|
292
300
|
`);
|
|
293
301
|
}
|
|
294
302
|
function findAccount(file, name) {
|
|
295
303
|
return file.accounts.find((a) => a.name.toLowerCase() === name.toLowerCase());
|
|
296
304
|
}
|
|
297
|
-
var ACCOUNTS_PATH;
|
|
298
305
|
var init_accounts_store = __esm(() => {
|
|
299
306
|
init_store();
|
|
300
|
-
ACCOUNTS_PATH = join2(getConfigDir(), "accounts.json");
|
|
301
307
|
});
|
|
302
308
|
|
|
303
309
|
// src/config/accounts-types.ts
|
|
@@ -355,6 +361,7 @@ import {
|
|
|
355
361
|
ss58Decode,
|
|
356
362
|
validateMnemonic
|
|
357
363
|
} from "@polkadot-labs/hdkd-helpers";
|
|
364
|
+
import { HDKD, secretFromSeed } from "@scure/sr25519";
|
|
358
365
|
import { getPolkadotSigner } from "polkadot-api/signer";
|
|
359
366
|
function isDevAccount(name) {
|
|
360
367
|
return DEV_NAMES.includes(name.toLowerCase());
|
|
@@ -381,6 +388,50 @@ function getDevKeypair(name) {
|
|
|
381
388
|
const path = devDerivationPath(name);
|
|
382
389
|
return deriveFromMnemonic(DEV_PHRASE, path);
|
|
383
390
|
}
|
|
391
|
+
function parseDerivations(path) {
|
|
392
|
+
const out = [];
|
|
393
|
+
for (const [, type, code] of path.matchAll(DERIVATION_RE)) {
|
|
394
|
+
out.push([type === "//" ? "hard" : "soft", code]);
|
|
395
|
+
}
|
|
396
|
+
return out;
|
|
397
|
+
}
|
|
398
|
+
function createChainCode(code) {
|
|
399
|
+
const chainCode = new Uint8Array(32);
|
|
400
|
+
const asNumber = +code;
|
|
401
|
+
if (Number.isNaN(asNumber)) {
|
|
402
|
+
const bytes = new TextEncoder().encode(code);
|
|
403
|
+
if (bytes.length >= 32) {
|
|
404
|
+
throw new Error(`Derivation component "${code}" is too long (max 31 bytes)`);
|
|
405
|
+
}
|
|
406
|
+
chainCode[0] = bytes.length << 2;
|
|
407
|
+
chainCode.set(bytes, 1);
|
|
408
|
+
} else {
|
|
409
|
+
const n = asNumber >>> 0;
|
|
410
|
+
chainCode[0] = n & 255;
|
|
411
|
+
chainCode[1] = n >>> 8 & 255;
|
|
412
|
+
chainCode[2] = n >>> 16 & 255;
|
|
413
|
+
chainCode[3] = n >>> 24 & 255;
|
|
414
|
+
}
|
|
415
|
+
return chainCode;
|
|
416
|
+
}
|
|
417
|
+
function deriveExpandedSecret(miniSecret, path) {
|
|
418
|
+
return parseDerivations(path).reduce((sk, [type, code]) => type === "hard" ? HDKD.secretHard(sk, createChainCode(code)) : HDKD.secretSoft(sk, createChainCode(code)), secretFromSeed(miniSecret));
|
|
419
|
+
}
|
|
420
|
+
function miniSecretFromSecret(secret) {
|
|
421
|
+
const isHexSeed = /^0x[0-9a-fA-F]{64}$/.test(secret);
|
|
422
|
+
if (isHexSeed) {
|
|
423
|
+
const clean = secret.slice(2);
|
|
424
|
+
const bytes = new Uint8Array(32);
|
|
425
|
+
for (let i = 0;i < clean.length; i += 2) {
|
|
426
|
+
bytes[i / 2] = parseInt(clean.substring(i, i + 2), 16);
|
|
427
|
+
}
|
|
428
|
+
return bytes;
|
|
429
|
+
}
|
|
430
|
+
if (!validateMnemonic(secret)) {
|
|
431
|
+
throw new Error("Invalid secret. Expected a 0x-prefixed 32-byte hex seed or a valid BIP39 mnemonic.");
|
|
432
|
+
}
|
|
433
|
+
return entropyToMiniSecret(mnemonicToEntropy(secret));
|
|
434
|
+
}
|
|
384
435
|
function getDevAddress(name, prefix = 42) {
|
|
385
436
|
const keypair = getDevKeypair(name);
|
|
386
437
|
return ss58Address(keypair.publicKey, prefix);
|
|
@@ -474,10 +525,37 @@ async function resolveAccountSigner(name) {
|
|
|
474
525
|
const keypair = await resolveAccountKeypair(name);
|
|
475
526
|
return getPolkadotSigner(keypair.publicKey, "Sr25519", keypair.sign);
|
|
476
527
|
}
|
|
477
|
-
|
|
528
|
+
async function resolveAccountExpandedSecret(name) {
|
|
529
|
+
if (isDevAccount(name)) {
|
|
530
|
+
const miniSecret2 = entropyToMiniSecret(mnemonicToEntropy(DEV_PHRASE));
|
|
531
|
+
return deriveExpandedSecret(miniSecret2, devDerivationPath(name));
|
|
532
|
+
}
|
|
533
|
+
const accountsFile = await loadAccounts();
|
|
534
|
+
const account = findAccount(accountsFile, name);
|
|
535
|
+
if (!account) {
|
|
536
|
+
const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
|
|
537
|
+
const suggestions = findClosest(name, available);
|
|
538
|
+
const hint = suggestions.length > 0 ? `
|
|
539
|
+
Did you mean: ${suggestions.join(", ")}?` : "";
|
|
540
|
+
const list = available.map((a) => `
|
|
541
|
+
- ${a}`).join("");
|
|
542
|
+
throw new Error(`Unknown account "${name}".${hint}
|
|
543
|
+
Available accounts:${list}`);
|
|
544
|
+
}
|
|
545
|
+
if (account.secret === undefined) {
|
|
546
|
+
throw new Error(`Account "${name}" is watch-only (no secret). Cannot derive private key. Import with --secret or --env.`);
|
|
547
|
+
}
|
|
548
|
+
const miniSecret = miniSecretFromSecret(resolveSecret(account.secret));
|
|
549
|
+
return deriveExpandedSecret(miniSecret, account.derivationPath);
|
|
550
|
+
}
|
|
551
|
+
function bytesToHex(bytes) {
|
|
552
|
+
return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
553
|
+
}
|
|
554
|
+
var DEV_NAMES, DERIVATION_RE;
|
|
478
555
|
var init_accounts = __esm(() => {
|
|
479
556
|
init_accounts_store();
|
|
480
557
|
DEV_NAMES = ["alice", "bob", "charlie", "dave", "eve", "ferdie"];
|
|
558
|
+
DERIVATION_RE = /(\/{1,2})([^/]+)/g;
|
|
481
559
|
});
|
|
482
560
|
|
|
483
561
|
// src/utils/binary-display.ts
|
|
@@ -556,6 +634,33 @@ function printDocs(docs) {
|
|
|
556
634
|
console.log(` ${DIM}${text}${RESET}`);
|
|
557
635
|
}
|
|
558
636
|
}
|
|
637
|
+
function printImportResults(params) {
|
|
638
|
+
const { added, overwritten, skipped, dryRun, noun } = params;
|
|
639
|
+
for (const name of added) {
|
|
640
|
+
console.log(` ${GREEN}${CHECK_MARK}${RESET} ${name}`);
|
|
641
|
+
}
|
|
642
|
+
for (const name of overwritten) {
|
|
643
|
+
console.log(` ${YELLOW}⟳${RESET} ${name}${DIM} (overwritten)${RESET}`);
|
|
644
|
+
}
|
|
645
|
+
for (const name of skipped) {
|
|
646
|
+
console.log(` ${DIM}- ${name} (skipped)${RESET}`);
|
|
647
|
+
}
|
|
648
|
+
if (added.length === 0 && overwritten.length === 0 && skipped.length === 0) {
|
|
649
|
+
const prefix = dryRun ? "(dry run) " : "";
|
|
650
|
+
console.log(`${prefix}No ${noun}s imported.`);
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
const parts = [];
|
|
654
|
+
if (added.length > 0)
|
|
655
|
+
parts.push(`${added.length} added`);
|
|
656
|
+
if (overwritten.length > 0)
|
|
657
|
+
parts.push(`${overwritten.length} overwritten`);
|
|
658
|
+
if (skipped.length > 0)
|
|
659
|
+
parts.push(`${skipped.length} skipped`);
|
|
660
|
+
const suffix = dryRun ? " (dry run)" : "";
|
|
661
|
+
console.log();
|
|
662
|
+
console.log(`${parts.join(", ")}${suffix}`);
|
|
663
|
+
}
|
|
559
664
|
|
|
560
665
|
class Spinner {
|
|
561
666
|
timer = null;
|
|
@@ -802,6 +907,22 @@ function getSignedExtensions(meta) {
|
|
|
802
907
|
return [];
|
|
803
908
|
return byVersion[Number(versionKeys[0])] ?? [];
|
|
804
909
|
}
|
|
910
|
+
function getSignedExtensionNames(meta) {
|
|
911
|
+
return getSignedExtensions(meta).map((e) => e.identifier).sort((a, b) => a.localeCompare(b));
|
|
912
|
+
}
|
|
913
|
+
function findSignedExtension(meta, identifier) {
|
|
914
|
+
return getSignedExtensions(meta).find((e) => e.identifier.toLowerCase() === identifier.toLowerCase());
|
|
915
|
+
}
|
|
916
|
+
function describeSignedExtension(meta, info) {
|
|
917
|
+
return {
|
|
918
|
+
identifier: info.identifier,
|
|
919
|
+
valueType: describeType(meta.lookup, info.type),
|
|
920
|
+
additionalSignedType: describeType(meta.lookup, info.additionalSigned),
|
|
921
|
+
valueTypeId: info.type,
|
|
922
|
+
additionalSignedTypeId: info.additionalSigned,
|
|
923
|
+
isBuiltin: PAPI_BUILTIN_EXTENSIONS.has(info.identifier)
|
|
924
|
+
};
|
|
925
|
+
}
|
|
805
926
|
function getPalletNames(meta) {
|
|
806
927
|
return meta.unified.pallets.map((p) => p.name).sort((a, b) => a.localeCompare(b));
|
|
807
928
|
}
|
|
@@ -950,12 +1071,26 @@ function hexToBytes(hex) {
|
|
|
950
1071
|
}
|
|
951
1072
|
return bytes;
|
|
952
1073
|
}
|
|
953
|
-
var METADATA_TIMEOUT_MS = 15000, optionalOpaqueBytes, v15Arg;
|
|
1074
|
+
var METADATA_TIMEOUT_MS = 15000, optionalOpaqueBytes, v15Arg, PAPI_BUILTIN_EXTENSIONS;
|
|
954
1075
|
var init_metadata = __esm(() => {
|
|
955
1076
|
init_store();
|
|
956
1077
|
init_errors();
|
|
957
1078
|
optionalOpaqueBytes = Option(Bytes());
|
|
958
1079
|
v15Arg = toHex(u32.enc(15));
|
|
1080
|
+
PAPI_BUILTIN_EXTENSIONS = new Set([
|
|
1081
|
+
"CheckNonZeroSender",
|
|
1082
|
+
"CheckSpecVersion",
|
|
1083
|
+
"CheckTxVersion",
|
|
1084
|
+
"CheckGenesis",
|
|
1085
|
+
"CheckMortality",
|
|
1086
|
+
"CheckNonce",
|
|
1087
|
+
"CheckWeight",
|
|
1088
|
+
"ChargeTransactionPayment",
|
|
1089
|
+
"ChargeAssetTxPayment",
|
|
1090
|
+
"CheckMetadataHash",
|
|
1091
|
+
"StorageWeightReclaim",
|
|
1092
|
+
"PrevalidateAttests"
|
|
1093
|
+
]);
|
|
959
1094
|
});
|
|
960
1095
|
|
|
961
1096
|
// src/core/explorers.ts
|
|
@@ -1325,7 +1460,7 @@ function parsePrimitive(prim, arg) {
|
|
|
1325
1460
|
return parseValue(arg);
|
|
1326
1461
|
}
|
|
1327
1462
|
}
|
|
1328
|
-
var
|
|
1463
|
+
var NO_DEFAULT;
|
|
1329
1464
|
var init_tx = __esm(() => {
|
|
1330
1465
|
init_store();
|
|
1331
1466
|
init_types();
|
|
@@ -1337,20 +1472,6 @@ var init_tx = __esm(() => {
|
|
|
1337
1472
|
init_binary_display();
|
|
1338
1473
|
init_errors();
|
|
1339
1474
|
init_focused_inspect();
|
|
1340
|
-
PAPI_BUILTIN_EXTENSIONS = new Set([
|
|
1341
|
-
"CheckNonZeroSender",
|
|
1342
|
-
"CheckSpecVersion",
|
|
1343
|
-
"CheckTxVersion",
|
|
1344
|
-
"CheckGenesis",
|
|
1345
|
-
"CheckMortality",
|
|
1346
|
-
"CheckNonce",
|
|
1347
|
-
"CheckWeight",
|
|
1348
|
-
"ChargeTransactionPayment",
|
|
1349
|
-
"ChargeAssetTxPayment",
|
|
1350
|
-
"CheckMetadataHash",
|
|
1351
|
-
"StorageWeightReclaim",
|
|
1352
|
-
"PrevalidateAttests"
|
|
1353
|
-
]);
|
|
1354
1475
|
NO_DEFAULT = Symbol("no-default");
|
|
1355
1476
|
});
|
|
1356
1477
|
|
|
@@ -2046,7 +2167,7 @@ var init_focused_inspect = __esm(() => {
|
|
|
2046
2167
|
import { blake2b as blake2b2 } from "@noble/hashes/blake2.js";
|
|
2047
2168
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
2048
2169
|
import { keccak_256 } from "@noble/hashes/sha3.js";
|
|
2049
|
-
import { bytesToHex, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
|
|
2170
|
+
import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
|
|
2050
2171
|
function computeHash(algorithm, data) {
|
|
2051
2172
|
const algo = ALGORITHMS[algorithm];
|
|
2052
2173
|
if (!algo) {
|
|
@@ -2065,7 +2186,7 @@ function parseInputData(input) {
|
|
|
2065
2186
|
return new TextEncoder().encode(input);
|
|
2066
2187
|
}
|
|
2067
2188
|
function toHex2(bytes) {
|
|
2068
|
-
return `0x${
|
|
2189
|
+
return `0x${bytesToHex2(bytes)}`;
|
|
2069
2190
|
}
|
|
2070
2191
|
function isValidAlgorithm(name) {
|
|
2071
2192
|
return name in ALGORITHMS;
|
|
@@ -2124,6 +2245,12 @@ async function loadRuntimeApiNames(_config, chainName) {
|
|
|
2124
2245
|
methodNames: a.methods.map((m) => m.name)
|
|
2125
2246
|
}));
|
|
2126
2247
|
}
|
|
2248
|
+
async function loadExtensionIdentifiers(_config, chainName) {
|
|
2249
|
+
const raw = await loadMetadata(chainName);
|
|
2250
|
+
if (!raw)
|
|
2251
|
+
return null;
|
|
2252
|
+
return getSignedExtensionNames(parseMetadata(raw));
|
|
2253
|
+
}
|
|
2127
2254
|
function filterPallets(pallets, category) {
|
|
2128
2255
|
switch (category) {
|
|
2129
2256
|
case "query":
|
|
@@ -2263,6 +2390,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2263
2390
|
if (category === "apis") {
|
|
2264
2391
|
return completeApisCategory(first, numComplete, endsWithDot, completeSegments, currentWord, config, chainFromFlag);
|
|
2265
2392
|
}
|
|
2393
|
+
if (category === "extensions") {
|
|
2394
|
+
return completeExtensionsCategory(first, numComplete, endsWithDot, currentWord, config, chainFromFlag);
|
|
2395
|
+
}
|
|
2266
2396
|
if (numComplete === 1 && endsWithDot) {
|
|
2267
2397
|
const chainName = chainFromFlag;
|
|
2268
2398
|
if (!chainName)
|
|
@@ -2319,6 +2449,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2319
2449
|
if (category === "apis") {
|
|
2320
2450
|
return completeApisCategory(`${first}.${completeSegments[1]}`, numComplete - 1, endsWithDot, completeSegments.slice(1), currentWord, config, chainName);
|
|
2321
2451
|
}
|
|
2452
|
+
if (category === "extensions") {
|
|
2453
|
+
return completeExtensionsCategory(`${first}.${completeSegments[1]}`, numComplete - 1, endsWithDot, currentWord, config, chainName);
|
|
2454
|
+
}
|
|
2322
2455
|
const pallets = await loadPallets(config, chainName);
|
|
2323
2456
|
if (!pallets)
|
|
2324
2457
|
return [];
|
|
@@ -2375,6 +2508,23 @@ async function completeApisCategory(prefix, numComplete, endsWithDot, segments,
|
|
|
2375
2508
|
}
|
|
2376
2509
|
return [];
|
|
2377
2510
|
}
|
|
2511
|
+
async function completeExtensionsCategory(prefix, numComplete, endsWithDot, currentWord, config, chainNameOverride) {
|
|
2512
|
+
const chainName = chainNameOverride;
|
|
2513
|
+
if (!chainName)
|
|
2514
|
+
return [];
|
|
2515
|
+
const names = await loadExtensionIdentifiers(config, chainName);
|
|
2516
|
+
if (!names)
|
|
2517
|
+
return [];
|
|
2518
|
+
if (numComplete === 1 && endsWithDot) {
|
|
2519
|
+
const candidates = names.map((n) => `${prefix}.${n}`);
|
|
2520
|
+
return filterPrefix(candidates, currentWord.slice(0, -1));
|
|
2521
|
+
}
|
|
2522
|
+
if (numComplete === 1 && !endsWithDot) {
|
|
2523
|
+
const candidates = names.map((n) => `${prefix}.${n}`);
|
|
2524
|
+
return filterPrefix(candidates, currentWord);
|
|
2525
|
+
}
|
|
2526
|
+
return [];
|
|
2527
|
+
}
|
|
2378
2528
|
var CATEGORIES2, CATEGORY_ALIASES2, NAMED_COMMANDS, CHAIN_SUBCOMMANDS, ACCOUNT_SUBCOMMANDS, GLOBAL_OPTIONS, TX_OPTIONS, QUERY_OPTIONS;
|
|
2379
2529
|
var init_complete = __esm(() => {
|
|
2380
2530
|
init_accounts_store();
|
|
@@ -2382,7 +2532,7 @@ var init_complete = __esm(() => {
|
|
|
2382
2532
|
init_accounts();
|
|
2383
2533
|
init_hash();
|
|
2384
2534
|
init_metadata();
|
|
2385
|
-
CATEGORIES2 = ["query", "tx", "const", "events", "errors", "apis"];
|
|
2535
|
+
CATEGORIES2 = ["query", "tx", "const", "events", "errors", "apis", "extensions"];
|
|
2386
2536
|
CATEGORY_ALIASES2 = {
|
|
2387
2537
|
query: "query",
|
|
2388
2538
|
tx: "tx",
|
|
@@ -2394,7 +2544,10 @@ var init_complete = __esm(() => {
|
|
|
2394
2544
|
errors: "errors",
|
|
2395
2545
|
error: "errors",
|
|
2396
2546
|
apis: "apis",
|
|
2397
|
-
api: "apis"
|
|
2547
|
+
api: "apis",
|
|
2548
|
+
extensions: "extensions",
|
|
2549
|
+
extension: "extensions",
|
|
2550
|
+
ext: "extensions"
|
|
2398
2551
|
};
|
|
2399
2552
|
NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "sign", "parachain", "completions"];
|
|
2400
2553
|
CHAIN_SUBCOMMANDS = ["add", "remove", "update", "list"];
|
|
@@ -2428,7 +2581,7 @@ var init_complete = __esm(() => {
|
|
|
2428
2581
|
// src/cli.ts
|
|
2429
2582
|
import cac from "cac";
|
|
2430
2583
|
// package.json
|
|
2431
|
-
var version = "1.
|
|
2584
|
+
var version = "1.15.0";
|
|
2432
2585
|
|
|
2433
2586
|
// src/commands/account.ts
|
|
2434
2587
|
init_accounts_store();
|
|
@@ -2441,31 +2594,30 @@ ${BOLD}Usage:${RESET}
|
|
|
2441
2594
|
$ dot account add <name> --secret <s> [--path <derivation>] Import from BIP39 mnemonic
|
|
2442
2595
|
$ dot account add <name> --env <VAR> [--path <derivation>] Import account backed by env variable
|
|
2443
2596
|
$ dot account create|new <name> [--path <derivation>] Create a new account
|
|
2444
|
-
$ dot account import <
|
|
2445
|
-
$ dot account import <name> --env <VAR> [--path <derivation>] Import account backed by env variable
|
|
2446
|
-
$ dot account import --file <path> Batch-import accounts from a file
|
|
2597
|
+
$ dot account import <file> Batch-import accounts from a file
|
|
2447
2598
|
$ dot account export [names...] Export accounts to stdout
|
|
2448
2599
|
$ dot account derive <source> <new-name> --path <derivation> Derive a child account
|
|
2449
|
-
$ dot account inspect <input> [--prefix <N>]
|
|
2600
|
+
$ dot account inspect <input> [--prefix <N>] [--show-secret] Inspect an account/address/key
|
|
2450
2601
|
$ dot account list List all accounts
|
|
2451
2602
|
$ dot account remove|delete <name> [name2] ... Remove stored account(s)
|
|
2452
2603
|
|
|
2453
2604
|
${BOLD}Examples:${RESET}
|
|
2454
2605
|
$ dot account add treasury 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
|
|
2606
|
+
$ dot account add treasury --secret "word1 word2 ... word12"
|
|
2607
|
+
$ dot account add ci-signer --env MY_SECRET --path //ci
|
|
2455
2608
|
$ dot account create my-validator
|
|
2456
2609
|
$ dot account create my-staking --path //staking
|
|
2457
2610
|
$ dot account create multi --path //polkadot//0/wallet
|
|
2458
|
-
$ dot account import
|
|
2459
|
-
$ dot account import
|
|
2460
|
-
$ dot account import
|
|
2461
|
-
$ dot account import --file accounts.json --dry-run
|
|
2462
|
-
$ dot account import --file accounts.json --overwrite
|
|
2611
|
+
$ dot account import team-accounts.json
|
|
2612
|
+
$ dot account import accounts.json --dry-run
|
|
2613
|
+
$ dot account import accounts.json --overwrite
|
|
2463
2614
|
$ dot account export
|
|
2464
2615
|
$ dot account export treasury my-validator
|
|
2465
2616
|
$ dot account export --include-secrets --file backup.json
|
|
2466
2617
|
$ dot account export --watch-only
|
|
2467
2618
|
$ dot account derive treasury treasury-staking --path //staking
|
|
2468
2619
|
$ dot account inspect alice
|
|
2620
|
+
$ dot account inspect dave --show-secret
|
|
2469
2621
|
$ dot account inspect 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
|
|
2470
2622
|
$ dot account inspect 0xd435...a27d --prefix 0
|
|
2471
2623
|
$ dot account list
|
|
@@ -2476,7 +2628,7 @@ ${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
|
|
|
2476
2628
|
Hex seed import (0x...) is not supported via CLI.${RESET}
|
|
2477
2629
|
`.trimStart();
|
|
2478
2630
|
function registerAccountCommands(cli) {
|
|
2479
|
-
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("--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").action(async (action, names, opts) => {
|
|
2631
|
+
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("--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) => {
|
|
2480
2632
|
if (!action) {
|
|
2481
2633
|
if (process.argv[2] === "accounts")
|
|
2482
2634
|
return accountList(opts);
|
|
@@ -2492,9 +2644,7 @@ function registerAccountCommands(cli) {
|
|
|
2492
2644
|
return accountImport(names[0], opts);
|
|
2493
2645
|
return accountAddWatchOnly(names[0], names[1], opts);
|
|
2494
2646
|
case "import":
|
|
2495
|
-
|
|
2496
|
-
return accountBatchImport(opts);
|
|
2497
|
-
return accountImport(names[0], opts);
|
|
2647
|
+
return accountBatchImport(names[0], opts);
|
|
2498
2648
|
case "export":
|
|
2499
2649
|
return accountExport(names, opts);
|
|
2500
2650
|
case "derive":
|
|
@@ -2893,16 +3043,19 @@ async function accountInspect(input, opts) {
|
|
|
2893
3043
|
let name;
|
|
2894
3044
|
let publicKeyHex;
|
|
2895
3045
|
let bandersnatch;
|
|
3046
|
+
let hasSecret = false;
|
|
2896
3047
|
if (isDevAccount(input)) {
|
|
2897
3048
|
name = input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
|
|
2898
3049
|
const devAddr = getDevAddress(input);
|
|
2899
3050
|
publicKeyHex = publicKeyToHex(fromSs58(devAddr));
|
|
3051
|
+
hasSecret = true;
|
|
2900
3052
|
} else {
|
|
2901
3053
|
const accountsFile = await loadAccounts();
|
|
2902
3054
|
const account = findAccount(accountsFile, input);
|
|
2903
3055
|
if (account) {
|
|
2904
3056
|
name = account.name;
|
|
2905
3057
|
bandersnatch = account.bandersnatch;
|
|
3058
|
+
hasSecret = account.secret !== undefined;
|
|
2906
3059
|
if (account.publicKey) {
|
|
2907
3060
|
publicKeyHex = account.publicKey;
|
|
2908
3061
|
} else if (account.secret !== undefined && isEnvSecret(account.secret)) {
|
|
@@ -2929,12 +3082,31 @@ async function accountInspect(input, opts) {
|
|
|
2929
3082
|
}
|
|
2930
3083
|
}
|
|
2931
3084
|
const ss58 = toSs58(publicKeyHex, prefix);
|
|
3085
|
+
let privateKeyHex;
|
|
3086
|
+
if (opts.showSecret) {
|
|
3087
|
+
if (!name) {
|
|
3088
|
+
console.error("--show-secret requires an account name; raw addresses and hex keys have no secret to reveal.");
|
|
3089
|
+
process.exit(1);
|
|
3090
|
+
}
|
|
3091
|
+
if (!hasSecret) {
|
|
3092
|
+
console.error(`Account "${name}" is watch-only (no secret). Cannot reveal private key.`);
|
|
3093
|
+
process.exit(1);
|
|
3094
|
+
}
|
|
3095
|
+
try {
|
|
3096
|
+
privateKeyHex = bytesToHex(await resolveAccountExpandedSecret(input));
|
|
3097
|
+
} catch (err) {
|
|
3098
|
+
console.error(err.message);
|
|
3099
|
+
process.exit(1);
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
2932
3102
|
if (isJsonOutput(opts)) {
|
|
2933
3103
|
const result = { publicKey: publicKeyHex, ss58, prefix };
|
|
2934
3104
|
if (name)
|
|
2935
3105
|
result.name = name;
|
|
2936
3106
|
if (bandersnatch && Object.keys(bandersnatch).length > 0)
|
|
2937
3107
|
result.bandersnatch = bandersnatch;
|
|
3108
|
+
if (privateKeyHex)
|
|
3109
|
+
result.privateKey = privateKeyHex;
|
|
2938
3110
|
console.log(formatJson(result));
|
|
2939
3111
|
} else {
|
|
2940
3112
|
printHeading("Account Info");
|
|
@@ -2955,6 +3127,10 @@ async function accountInspect(input, opts) {
|
|
|
2955
3127
|
}
|
|
2956
3128
|
}
|
|
2957
3129
|
console.log(` ${BOLD}Prefix:${RESET} ${prefix}`);
|
|
3130
|
+
if (privateKeyHex) {
|
|
3131
|
+
console.log(` ${BOLD}Private Key:${RESET} ${privateKeyHex}`);
|
|
3132
|
+
console.log(` ${YELLOW}(sr25519 expanded, 64 bytes — never share)${RESET}`);
|
|
3133
|
+
}
|
|
2958
3134
|
console.log();
|
|
2959
3135
|
}
|
|
2960
3136
|
}
|
|
@@ -3019,12 +3195,17 @@ async function accountExport(names, opts) {
|
|
|
3019
3195
|
process.stdout.write(json);
|
|
3020
3196
|
}
|
|
3021
3197
|
}
|
|
3022
|
-
async function accountBatchImport(opts) {
|
|
3198
|
+
async function accountBatchImport(filePath, opts) {
|
|
3199
|
+
const inputPath = filePath ?? opts.file;
|
|
3023
3200
|
let raw;
|
|
3024
|
-
if (!
|
|
3201
|
+
if (!inputPath || inputPath === "-") {
|
|
3202
|
+
if (process.stdin.isTTY) {
|
|
3203
|
+
console.log(ACCOUNT_HELP);
|
|
3204
|
+
return;
|
|
3205
|
+
}
|
|
3025
3206
|
raw = await readStdin();
|
|
3026
3207
|
} else {
|
|
3027
|
-
raw = await readFile3(
|
|
3208
|
+
raw = await readFile3(inputPath, "utf-8");
|
|
3028
3209
|
}
|
|
3029
3210
|
let importData;
|
|
3030
3211
|
try {
|
|
@@ -3103,16 +3284,13 @@ async function accountBatchImport(opts) {
|
|
|
3103
3284
|
}));
|
|
3104
3285
|
return;
|
|
3105
3286
|
}
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
if (added.length === 0 && overwritten.length === 0) {
|
|
3114
|
-
console.log(`${prefix}No accounts imported.`);
|
|
3115
|
-
}
|
|
3287
|
+
printImportResults({
|
|
3288
|
+
added,
|
|
3289
|
+
overwritten,
|
|
3290
|
+
skipped,
|
|
3291
|
+
dryRun: opts.dryRun ?? false,
|
|
3292
|
+
noun: "account"
|
|
3293
|
+
});
|
|
3116
3294
|
}
|
|
3117
3295
|
|
|
3118
3296
|
// src/commands/apis.ts
|
|
@@ -3264,9 +3442,10 @@ ${BOLD}Examples:${RESET}
|
|
|
3264
3442
|
$ dot chain import my-chains.json
|
|
3265
3443
|
$ dot chain import my-chains.json --dry-run
|
|
3266
3444
|
$ dot chain import my-chains.json --overwrite
|
|
3445
|
+
$ dot chain import my-chains.json --no-metadata
|
|
3267
3446
|
`.trimStart();
|
|
3268
3447
|
function registerChainCommands(cli) {
|
|
3269
|
-
cli.command("chain [action] [...names]", "Manage chains (add, remove, update, list, export, import)").alias("chains").option("--all", "Update/export all configured chains").option("--relay <name>", "Parent relay chain for this parachain").option("--parachain-id <id>", "Parachain ID (auto-detected if omitted with --relay)").option("--file <path>", "Output/input file for export/import").option("--overwrite", "Overwrite existing chains on import").option("--dry-run", "Preview import without applying changes").action(async (action, names, opts) => {
|
|
3448
|
+
cli.command("chain [action] [...names]", "Manage chains (add, remove, update, list, export, import)").alias("chains").option("--all", "Update/export all configured chains").option("--relay <name>", "Parent relay chain for this parachain").option("--parachain-id <id>", "Parachain ID (auto-detected if omitted with --relay)").option("--file <path>", "Output/input file for export/import").option("--overwrite", "Overwrite existing chains on import").option("--dry-run", "Preview import without applying changes").option("--no-metadata", "Skip automatic metadata fetch after import").action(async (action, names, opts) => {
|
|
3270
3449
|
if (!action) {
|
|
3271
3450
|
if (process.argv[2] === "chains")
|
|
3272
3451
|
return chainList(opts);
|
|
@@ -3476,7 +3655,17 @@ async function chainUpdate(name, opts) {
|
|
|
3476
3655
|
}
|
|
3477
3656
|
async function chainUpdateAll(config) {
|
|
3478
3657
|
const chainNames = Object.keys(config.chains).sort();
|
|
3479
|
-
|
|
3658
|
+
const failed = await updateChainsMetadata(config, chainNames);
|
|
3659
|
+
if (failed > 0) {
|
|
3660
|
+
console.error(`
|
|
3661
|
+
${failed} of ${chainNames.length} chains failed to update.`);
|
|
3662
|
+
process.exit(1);
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
async function updateChainsMetadata(config, chainNames) {
|
|
3666
|
+
if (chainNames.length === 0)
|
|
3667
|
+
return 0;
|
|
3668
|
+
process.stderr.write(`Updating metadata for ${chainNames.length} chain(s)...
|
|
3480
3669
|
|
|
3481
3670
|
`);
|
|
3482
3671
|
const results = await Promise.allSettled(chainNames.map(async (chainName) => {
|
|
@@ -3497,12 +3686,7 @@ async function chainUpdateAll(config) {
|
|
|
3497
3686
|
console.log(` ${RED}✗${RESET} ${name}${DIM} — ${result.reason?.message ?? "unknown error"}${RESET}`);
|
|
3498
3687
|
}
|
|
3499
3688
|
}
|
|
3500
|
-
|
|
3501
|
-
if (failed > 0) {
|
|
3502
|
-
console.error(`
|
|
3503
|
-
${failed} of ${chainNames.length} chains failed to update.`);
|
|
3504
|
-
process.exit(1);
|
|
3505
|
-
}
|
|
3689
|
+
return results.filter((r) => r.status === "rejected").length;
|
|
3506
3690
|
}
|
|
3507
3691
|
function isBuiltinModified(name, config) {
|
|
3508
3692
|
const defaultRpc = DEFAULT_CONFIG.chains[name]?.rpc;
|
|
@@ -3561,10 +3745,14 @@ async function chainExport(names, opts) {
|
|
|
3561
3745
|
async function chainImport(filePath, opts) {
|
|
3562
3746
|
const inputPath = filePath ?? opts.file;
|
|
3563
3747
|
let raw;
|
|
3564
|
-
if (inputPath
|
|
3565
|
-
|
|
3566
|
-
|
|
3748
|
+
if (!inputPath || inputPath === "-") {
|
|
3749
|
+
if (process.stdin.isTTY) {
|
|
3750
|
+
console.log(CHAIN_HELP);
|
|
3751
|
+
return;
|
|
3752
|
+
}
|
|
3567
3753
|
raw = await readStdin2();
|
|
3754
|
+
} else {
|
|
3755
|
+
raw = await readFile4(inputPath, "utf-8");
|
|
3568
3756
|
}
|
|
3569
3757
|
let importData;
|
|
3570
3758
|
try {
|
|
@@ -3618,18 +3806,16 @@ async function chainImport(filePath, opts) {
|
|
|
3618
3806
|
}));
|
|
3619
3807
|
return;
|
|
3620
3808
|
}
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
if (added.length
|
|
3629
|
-
console.log(
|
|
3630
|
-
|
|
3631
|
-
console.error(`
|
|
3632
|
-
Run "dot chain update --all" to fetch metadata for imported chains.`);
|
|
3809
|
+
printImportResults({
|
|
3810
|
+
added,
|
|
3811
|
+
overwritten,
|
|
3812
|
+
skipped,
|
|
3813
|
+
dryRun: opts.dryRun ?? false,
|
|
3814
|
+
noun: "chain"
|
|
3815
|
+
});
|
|
3816
|
+
if (!opts.dryRun && opts.metadata !== false && (added.length > 0 || overwritten.length > 0)) {
|
|
3817
|
+
console.log();
|
|
3818
|
+
await updateChainsMetadata(config, [...added, ...overwritten]);
|
|
3633
3819
|
}
|
|
3634
3820
|
}
|
|
3635
3821
|
|
|
@@ -4373,6 +4559,61 @@ async function showItemHelp2(category, target, opts) {
|
|
|
4373
4559
|
}
|
|
4374
4560
|
}
|
|
4375
4561
|
}
|
|
4562
|
+
async function handleExtensions(target, opts) {
|
|
4563
|
+
const config = await loadConfig();
|
|
4564
|
+
const { name: chainName, chain: chainConfig } = resolveChain(config, opts.chain);
|
|
4565
|
+
const meta = await loadMeta2(chainName, chainConfig, opts.rpc);
|
|
4566
|
+
if (!target) {
|
|
4567
|
+
const extensions = getSignedExtensions(meta).map((e) => describeSignedExtension(meta, e)).sort((a, b) => a.identifier.localeCompare(b.identifier));
|
|
4568
|
+
if (isJsonOutput(opts)) {
|
|
4569
|
+
console.log(formatJson({
|
|
4570
|
+
chain: chainName,
|
|
4571
|
+
extensions: extensions.map((e) => ({
|
|
4572
|
+
identifier: e.identifier,
|
|
4573
|
+
valueType: e.valueType,
|
|
4574
|
+
additionalSignedType: e.additionalSignedType,
|
|
4575
|
+
isBuiltin: e.isBuiltin
|
|
4576
|
+
}))
|
|
4577
|
+
}));
|
|
4578
|
+
return;
|
|
4579
|
+
}
|
|
4580
|
+
printHeading(`Transaction extensions on ${chainName} (${extensions.length})`);
|
|
4581
|
+
for (const e of extensions) {
|
|
4582
|
+
const tag = e.isBuiltin ? `${DIM}[builtin]${RESET}` : `${CYAN}[custom]${RESET}`;
|
|
4583
|
+
printItem(e.identifier, `${e.valueType} ${tag}`);
|
|
4584
|
+
}
|
|
4585
|
+
console.log();
|
|
4586
|
+
return;
|
|
4587
|
+
}
|
|
4588
|
+
const info = findSignedExtension(meta, target);
|
|
4589
|
+
if (!info) {
|
|
4590
|
+
const names = getSignedExtensionNames(meta);
|
|
4591
|
+
throw new Error(suggestMessage("transaction extension", target, names));
|
|
4592
|
+
}
|
|
4593
|
+
const described = describeSignedExtension(meta, info);
|
|
4594
|
+
if (isJsonOutput(opts)) {
|
|
4595
|
+
console.log(formatJson({
|
|
4596
|
+
chain: chainName,
|
|
4597
|
+
identifier: described.identifier,
|
|
4598
|
+
valueType: described.valueType,
|
|
4599
|
+
additionalSignedType: described.additionalSignedType,
|
|
4600
|
+
valueTypeId: described.valueTypeId,
|
|
4601
|
+
additionalSignedTypeId: described.additionalSignedTypeId,
|
|
4602
|
+
isBuiltin: described.isBuiltin
|
|
4603
|
+
}));
|
|
4604
|
+
return;
|
|
4605
|
+
}
|
|
4606
|
+
printHeading(`${described.identifier} (Transaction Extension)`);
|
|
4607
|
+
console.log(` ${BOLD}Value type:${RESET} ${described.valueType}`);
|
|
4608
|
+
console.log(` ${BOLD}AdditionalSigned:${RESET} ${described.additionalSignedType}`);
|
|
4609
|
+
console.log(` ${BOLD}Handled by:${RESET} ${described.isBuiltin ? "polkadot-api (builtin)" : "user (custom — provide via --ext)"}`);
|
|
4610
|
+
if (!described.isBuiltin) {
|
|
4611
|
+
console.log();
|
|
4612
|
+
console.log(`${BOLD}Usage:${RESET}`);
|
|
4613
|
+
console.log(` dot ${chainName}.tx.<Pallet>.<Call> --from <acc> --ext '{"${described.identifier}":{"value":<v>}}'`);
|
|
4614
|
+
}
|
|
4615
|
+
console.log();
|
|
4616
|
+
}
|
|
4376
4617
|
|
|
4377
4618
|
// src/commands/hash.ts
|
|
4378
4619
|
init_hash();
|
|
@@ -5399,7 +5640,7 @@ async function handleTx(target, args, opts) {
|
|
|
5399
5640
|
const at = parseAtOption(opts.at);
|
|
5400
5641
|
if (!decodeOnly || opts.unsigned) {
|
|
5401
5642
|
const userExtOverrides = parseExtOption(opts.ext);
|
|
5402
|
-
const skipBuiltins = asset !== undefined ? new Set([...
|
|
5643
|
+
const skipBuiltins = asset !== undefined ? new Set([...PAPI_BUILTIN_EXTENSIONS].filter((e) => e !== "ChargeAssetTxPayment")) : PAPI_BUILTIN_EXTENSIONS;
|
|
5403
5644
|
if (asset !== undefined) {
|
|
5404
5645
|
userExtOverrides.ChargeAssetTxPayment ??= {
|
|
5405
5646
|
value: { tip: tip ?? 0n, asset_id: asset }
|
|
@@ -6330,20 +6571,6 @@ function parsePrimitive2(prim, arg) {
|
|
|
6330
6571
|
return parseValue(arg);
|
|
6331
6572
|
}
|
|
6332
6573
|
}
|
|
6333
|
-
var PAPI_BUILTIN_EXTENSIONS2 = new Set([
|
|
6334
|
-
"CheckNonZeroSender",
|
|
6335
|
-
"CheckSpecVersion",
|
|
6336
|
-
"CheckTxVersion",
|
|
6337
|
-
"CheckGenesis",
|
|
6338
|
-
"CheckMortality",
|
|
6339
|
-
"CheckNonce",
|
|
6340
|
-
"CheckWeight",
|
|
6341
|
-
"ChargeTransactionPayment",
|
|
6342
|
-
"ChargeAssetTxPayment",
|
|
6343
|
-
"CheckMetadataHash",
|
|
6344
|
-
"StorageWeightReclaim",
|
|
6345
|
-
"PrevalidateAttests"
|
|
6346
|
-
]);
|
|
6347
6574
|
function parseExtOption(ext) {
|
|
6348
6575
|
if (!ext)
|
|
6349
6576
|
return {};
|
|
@@ -6361,7 +6588,7 @@ function parseExtOption(ext) {
|
|
|
6361
6588
|
}
|
|
6362
6589
|
}
|
|
6363
6590
|
var NO_DEFAULT2 = Symbol("no-default");
|
|
6364
|
-
function buildCustomSignedExtensions(meta, userOverrides, builtins =
|
|
6591
|
+
function buildCustomSignedExtensions(meta, userOverrides, builtins = PAPI_BUILTIN_EXTENSIONS) {
|
|
6365
6592
|
const result = {};
|
|
6366
6593
|
const extensions = getSignedExtensions(meta);
|
|
6367
6594
|
for (const ext of extensions) {
|
|
@@ -6698,9 +6925,13 @@ init_types();
|
|
|
6698
6925
|
import { access as access3, mkdir as mkdir3, readFile as readFile7, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
|
|
6699
6926
|
import { homedir as homedir2 } from "node:os";
|
|
6700
6927
|
import { join as join3 } from "node:path";
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6928
|
+
function getConfigDir2() {
|
|
6929
|
+
const override = process.env.DOT_HOME;
|
|
6930
|
+
return override && override.length > 0 ? override : join3(homedir2(), ".polkadot");
|
|
6931
|
+
}
|
|
6932
|
+
function getConfigPath2() {
|
|
6933
|
+
return join3(getConfigDir2(), "config.json");
|
|
6934
|
+
}
|
|
6704
6935
|
async function ensureDir3(dir) {
|
|
6705
6936
|
await mkdir3(dir, { recursive: true });
|
|
6706
6937
|
}
|
|
@@ -6713,9 +6944,10 @@ async function fileExists3(path) {
|
|
|
6713
6944
|
}
|
|
6714
6945
|
}
|
|
6715
6946
|
async function loadConfig2() {
|
|
6716
|
-
await ensureDir3(
|
|
6717
|
-
|
|
6718
|
-
|
|
6947
|
+
await ensureDir3(getConfigDir2());
|
|
6948
|
+
const configPath = getConfigPath2();
|
|
6949
|
+
if (await fileExists3(configPath)) {
|
|
6950
|
+
const saved = JSON.parse(await readFile7(configPath, "utf-8"));
|
|
6719
6951
|
const chains = {};
|
|
6720
6952
|
for (const [name, defaultConfig] of Object.entries(DEFAULT_CONFIG.chains)) {
|
|
6721
6953
|
chains[name] = saved.chains[name] ? { ...defaultConfig, ...saved.chains[name] } : defaultConfig;
|
|
@@ -6731,8 +6963,8 @@ async function loadConfig2() {
|
|
|
6731
6963
|
return DEFAULT_CONFIG;
|
|
6732
6964
|
}
|
|
6733
6965
|
async function saveConfig2(config) {
|
|
6734
|
-
await ensureDir3(
|
|
6735
|
-
await writeFile5(
|
|
6966
|
+
await ensureDir3(getConfigDir2());
|
|
6967
|
+
await writeFile5(getConfigPath2(), `${JSON.stringify(config, null, 2)}
|
|
6736
6968
|
`);
|
|
6737
6969
|
}
|
|
6738
6970
|
|
|
@@ -7012,7 +7244,10 @@ var CATEGORY_ALIASES = {
|
|
|
7012
7244
|
errors: "errors",
|
|
7013
7245
|
error: "errors",
|
|
7014
7246
|
apis: "apis",
|
|
7015
|
-
api: "apis"
|
|
7247
|
+
api: "apis",
|
|
7248
|
+
extensions: "extensions",
|
|
7249
|
+
extension: "extensions",
|
|
7250
|
+
ext: "extensions"
|
|
7016
7251
|
};
|
|
7017
7252
|
function matchCategory(segment) {
|
|
7018
7253
|
return CATEGORY_ALIASES[segment.toLowerCase()];
|
|
@@ -7027,7 +7262,7 @@ function parseDotPath(input, knownChains = []) {
|
|
|
7027
7262
|
const cat = matchCategory(parts[0]);
|
|
7028
7263
|
if (cat)
|
|
7029
7264
|
return { category: cat };
|
|
7030
|
-
throw new Error(`Unknown command "${parts[0]}". Expected a category (query, tx, const, events, errors, apis) or a named command.`);
|
|
7265
|
+
throw new Error(`Unknown command "${parts[0]}". Expected a category (query, tx, const, events, errors, apis, extensions) or a named command.`);
|
|
7031
7266
|
}
|
|
7032
7267
|
case 2: {
|
|
7033
7268
|
const cat = matchCategory(parts[0]);
|
|
@@ -7108,6 +7343,7 @@ if (process.argv[2] === "__complete") {
|
|
|
7108
7343
|
console.log(" events List or inspect pallet events");
|
|
7109
7344
|
console.log(" errors List or inspect pallet errors");
|
|
7110
7345
|
console.log(" apis Browse and call runtime APIs");
|
|
7346
|
+
console.log(" extensions List transaction extensions on a chain");
|
|
7111
7347
|
console.log();
|
|
7112
7348
|
console.log("Examples:");
|
|
7113
7349
|
console.log(" dot polkadot.query.System.Account <addr> Query a storage item");
|
|
@@ -7117,6 +7353,8 @@ if (process.argv[2] === "__complete") {
|
|
|
7117
7353
|
console.log(" dot polkadot.const.Balances.ExistentialDeposit");
|
|
7118
7354
|
console.log(" dot polkadot.events.Balances List events in Balances");
|
|
7119
7355
|
console.log(" dot polkadot.apis.Core.version Call a runtime API");
|
|
7356
|
+
console.log(" dot polkadot.extensions List transaction extensions");
|
|
7357
|
+
console.log(" dot polkadot.extensions.CheckMortality Inspect one extension");
|
|
7120
7358
|
console.log(" dot query.System.Number --chain polkadot --chain flag form");
|
|
7121
7359
|
console.log(" dot ./transfer.yaml --from alice Run from file (chain in YAML)");
|
|
7122
7360
|
console.log(" dot tx.0x1f0003... --to-yaml --chain polkadot Decode hex call to YAML");
|
|
@@ -7283,6 +7521,14 @@ if (process.argv[2] === "__complete") {
|
|
|
7283
7521
|
case "apis":
|
|
7284
7522
|
await handleApis2(target, args, handlerOpts);
|
|
7285
7523
|
break;
|
|
7524
|
+
case "extensions": {
|
|
7525
|
+
if (parsed.item) {
|
|
7526
|
+
const suggestion = parsed.chain ? `dot ${parsed.chain}.extensions.${parsed.pallet}` : opts.chain ? `dot extensions.${parsed.pallet} --chain ${opts.chain}` : `dot extensions.${parsed.pallet} --chain <chain>`;
|
|
7527
|
+
throw new CliError2(`Transaction extensions have no sub-items. Try "${suggestion}".`);
|
|
7528
|
+
}
|
|
7529
|
+
await handleExtensions(parsed.pallet, handlerOpts);
|
|
7530
|
+
break;
|
|
7531
|
+
}
|
|
7286
7532
|
}
|
|
7287
7533
|
});
|
|
7288
7534
|
cli.option("--help, -h", "Display this message");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polkadot-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "CLI tool for querying Polkadot-ecosystem on-chain state",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"@polkadot-api/view-builder": "^0.5.1",
|
|
44
44
|
"@polkadot-labs/hdkd": "^0.0.28",
|
|
45
45
|
"@polkadot-labs/hdkd-helpers": "^0.0.29",
|
|
46
|
+
"@scure/sr25519": "^1.0.0",
|
|
46
47
|
"cac": "^6.7.14",
|
|
47
48
|
"polkadot-api": "^2.0.1",
|
|
48
49
|
"verifiablejs": "^1.2.0",
|