polkadot-cli 1.13.0 → 1.14.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 +160 -13
- package/dist/cli.mjs +697 -111
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -15,6 +15,29 @@ var __export = (target, all) => {
|
|
|
15
15
|
};
|
|
16
16
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
17
|
|
|
18
|
+
// src/utils/errors.ts
|
|
19
|
+
var CliError, ConnectionError, MetadataError;
|
|
20
|
+
var init_errors = __esm(() => {
|
|
21
|
+
CliError = class CliError extends Error {
|
|
22
|
+
constructor(message) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = "CliError";
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
ConnectionError = class ConnectionError extends CliError {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = "ConnectionError";
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
MetadataError = class MetadataError extends CliError {
|
|
34
|
+
constructor(message) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = "MetadataError";
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
|
|
18
41
|
// src/config/types.ts
|
|
19
42
|
function primaryRpc(rpc) {
|
|
20
43
|
return Array.isArray(rpc) ? rpc[0] : rpc;
|
|
@@ -22,7 +45,6 @@ function primaryRpc(rpc) {
|
|
|
22
45
|
var DEFAULT_CONFIG, BUILTIN_CHAIN_NAMES;
|
|
23
46
|
var init_types = __esm(() => {
|
|
24
47
|
DEFAULT_CONFIG = {
|
|
25
|
-
defaultChain: "polkadot",
|
|
26
48
|
chains: {
|
|
27
49
|
polkadot: {
|
|
28
50
|
rpc: [
|
|
@@ -191,7 +213,7 @@ async function loadConfig() {
|
|
|
191
213
|
chains[name] = config;
|
|
192
214
|
}
|
|
193
215
|
}
|
|
194
|
-
return {
|
|
216
|
+
return { chains };
|
|
195
217
|
}
|
|
196
218
|
await saveConfig(DEFAULT_CONFIG);
|
|
197
219
|
return DEFAULT_CONFIG;
|
|
@@ -223,16 +245,19 @@ function findChainName(config, input) {
|
|
|
223
245
|
return Object.keys(config.chains).find((k) => k.toLowerCase() === input.toLowerCase());
|
|
224
246
|
}
|
|
225
247
|
function resolveChain(config, chainFlag) {
|
|
226
|
-
const
|
|
227
|
-
|
|
248
|
+
const available = Object.keys(config.chains).join(", ");
|
|
249
|
+
if (!chainFlag) {
|
|
250
|
+
throw new CliError(`No chain specified. Pass --chain <name> or use a dotpath prefix (e.g. "dot polkadot.query.System.Number"). Available chains: ${available}`);
|
|
251
|
+
}
|
|
252
|
+
const name = findChainName(config, chainFlag);
|
|
228
253
|
if (!name) {
|
|
229
|
-
|
|
230
|
-
throw new Error(`Unknown chain "${input}". Available chains: ${available}`);
|
|
254
|
+
throw new CliError(`Unknown chain "${chainFlag}". Available chains: ${available}`);
|
|
231
255
|
}
|
|
232
256
|
return { name, chain: config.chains[name] };
|
|
233
257
|
}
|
|
234
258
|
var DOT_DIR, CONFIG_PATH, CHAINS_DIR;
|
|
235
259
|
var init_store = __esm(() => {
|
|
260
|
+
init_errors();
|
|
236
261
|
init_types();
|
|
237
262
|
DOT_DIR = join(homedir(), ".polkadot");
|
|
238
263
|
CONFIG_PATH = join(DOT_DIR, "config.json");
|
|
@@ -602,29 +627,6 @@ function deriveBandersnatchMember(mnemonic, context) {
|
|
|
602
627
|
}
|
|
603
628
|
var init_bandersnatch = () => {};
|
|
604
629
|
|
|
605
|
-
// src/utils/errors.ts
|
|
606
|
-
var CliError, ConnectionError, MetadataError;
|
|
607
|
-
var init_errors = __esm(() => {
|
|
608
|
-
CliError = class CliError extends Error {
|
|
609
|
-
constructor(message) {
|
|
610
|
-
super(message);
|
|
611
|
-
this.name = "CliError";
|
|
612
|
-
}
|
|
613
|
-
};
|
|
614
|
-
ConnectionError = class ConnectionError extends CliError {
|
|
615
|
-
constructor(message) {
|
|
616
|
-
super(message);
|
|
617
|
-
this.name = "ConnectionError";
|
|
618
|
-
}
|
|
619
|
-
};
|
|
620
|
-
MetadataError = class MetadataError extends CliError {
|
|
621
|
-
constructor(message) {
|
|
622
|
-
super(message);
|
|
623
|
-
this.name = "MetadataError";
|
|
624
|
-
}
|
|
625
|
-
};
|
|
626
|
-
});
|
|
627
|
-
|
|
628
630
|
// src/core/client.ts
|
|
629
631
|
import { createClient } from "polkadot-api";
|
|
630
632
|
import { getWsProvider } from "polkadot-api/ws";
|
|
@@ -1016,6 +1018,7 @@ function parseValue(arg) {
|
|
|
1016
1018
|
}
|
|
1017
1019
|
|
|
1018
1020
|
// src/commands/tx.ts
|
|
1021
|
+
import { compact as scaleCompact } from "@polkadot-api/substrate-bindings";
|
|
1019
1022
|
import { getViewBuilder } from "@polkadot-api/view-builder";
|
|
1020
1023
|
import { Binary as Binary2 } from "polkadot-api";
|
|
1021
1024
|
import { stringify as stringifyYaml } from "yaml";
|
|
@@ -1473,12 +1476,23 @@ var init_apis = __esm(() => {
|
|
|
1473
1476
|
|
|
1474
1477
|
// src/commands/focused-inspect.ts
|
|
1475
1478
|
async function loadMeta(chainName, chainConfig, rpcOverride) {
|
|
1479
|
+
if (rpcOverride) {
|
|
1480
|
+
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
1481
|
+
`);
|
|
1482
|
+
const clientHandle = await createChainClient(chainName, chainConfig, rpcOverride);
|
|
1483
|
+
try {
|
|
1484
|
+
const raw = await fetchMetadataFromChain(clientHandle, chainName);
|
|
1485
|
+
return parseMetadata(raw);
|
|
1486
|
+
} finally {
|
|
1487
|
+
clientHandle.destroy();
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1476
1490
|
try {
|
|
1477
1491
|
return await getOrFetchMetadata(chainName);
|
|
1478
1492
|
} catch {
|
|
1479
1493
|
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
1480
1494
|
`);
|
|
1481
|
-
const clientHandle = await createChainClient(chainName, chainConfig
|
|
1495
|
+
const clientHandle = await createChainClient(chainName, chainConfig);
|
|
1482
1496
|
try {
|
|
1483
1497
|
return await getOrFetchMetadata(chainName, clientHandle);
|
|
1484
1498
|
} finally {
|
|
@@ -2245,7 +2259,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2245
2259
|
return completeApisCategory(first, numComplete, endsWithDot, completeSegments, currentWord, config, chainFromFlag);
|
|
2246
2260
|
}
|
|
2247
2261
|
if (numComplete === 1 && endsWithDot) {
|
|
2248
|
-
const chainName = chainFromFlag
|
|
2262
|
+
const chainName = chainFromFlag;
|
|
2263
|
+
if (!chainName)
|
|
2264
|
+
return [];
|
|
2249
2265
|
const pallets = await loadPallets(config, chainName);
|
|
2250
2266
|
if (!pallets)
|
|
2251
2267
|
return [];
|
|
@@ -2254,7 +2270,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2254
2270
|
return filterPrefix(candidates, currentWord.slice(0, -1));
|
|
2255
2271
|
}
|
|
2256
2272
|
if (numComplete === 1 && !endsWithDot) {
|
|
2257
|
-
const chainName = chainFromFlag
|
|
2273
|
+
const chainName = chainFromFlag;
|
|
2274
|
+
if (!chainName)
|
|
2275
|
+
return [];
|
|
2258
2276
|
const pallets = await loadPallets(config, chainName);
|
|
2259
2277
|
if (!pallets)
|
|
2260
2278
|
return [];
|
|
@@ -2264,7 +2282,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2264
2282
|
}
|
|
2265
2283
|
if (numComplete === 2) {
|
|
2266
2284
|
const palletName2 = completeSegments[1];
|
|
2267
|
-
const chainName = chainFromFlag
|
|
2285
|
+
const chainName = chainFromFlag;
|
|
2286
|
+
if (!chainName)
|
|
2287
|
+
return [];
|
|
2268
2288
|
const pallets = await loadPallets(config, chainName);
|
|
2269
2289
|
if (!pallets)
|
|
2270
2290
|
return [];
|
|
@@ -2326,7 +2346,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2326
2346
|
return [];
|
|
2327
2347
|
}
|
|
2328
2348
|
async function completeApisCategory(prefix, numComplete, endsWithDot, segments, currentWord, config, chainNameOverride) {
|
|
2329
|
-
const chainName = chainNameOverride
|
|
2349
|
+
const chainName = chainNameOverride;
|
|
2350
|
+
if (!chainName)
|
|
2351
|
+
return [];
|
|
2330
2352
|
const apis = await loadRuntimeApiNames(config, chainName);
|
|
2331
2353
|
if (!apis)
|
|
2332
2354
|
return [];
|
|
@@ -2370,7 +2392,7 @@ var init_complete = __esm(() => {
|
|
|
2370
2392
|
api: "apis"
|
|
2371
2393
|
};
|
|
2372
2394
|
NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "sign", "parachain", "completions"];
|
|
2373
|
-
CHAIN_SUBCOMMANDS = ["add", "remove", "update", "list"
|
|
2395
|
+
CHAIN_SUBCOMMANDS = ["add", "remove", "update", "list"];
|
|
2374
2396
|
ACCOUNT_SUBCOMMANDS = [
|
|
2375
2397
|
"add",
|
|
2376
2398
|
"create",
|
|
@@ -2385,6 +2407,7 @@ var init_complete = __esm(() => {
|
|
|
2385
2407
|
GLOBAL_OPTIONS = ["--chain", "--rpc", "--output", "--help", "--version"];
|
|
2386
2408
|
TX_OPTIONS = [
|
|
2387
2409
|
"--from",
|
|
2410
|
+
"--unsigned",
|
|
2388
2411
|
"--dry-run",
|
|
2389
2412
|
"--encode",
|
|
2390
2413
|
"--ext",
|
|
@@ -2400,12 +2423,13 @@ var init_complete = __esm(() => {
|
|
|
2400
2423
|
// src/cli.ts
|
|
2401
2424
|
import cac from "cac";
|
|
2402
2425
|
// package.json
|
|
2403
|
-
var version = "1.
|
|
2426
|
+
var version = "1.14.0";
|
|
2404
2427
|
|
|
2405
2428
|
// src/commands/account.ts
|
|
2406
2429
|
init_accounts_store();
|
|
2407
2430
|
init_accounts();
|
|
2408
2431
|
init_output();
|
|
2432
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
2409
2433
|
var ACCOUNT_HELP = `
|
|
2410
2434
|
${BOLD}Usage:${RESET}
|
|
2411
2435
|
$ dot account add <name> <ss58|hex> Add a watch-only address (no secret)
|
|
@@ -2414,6 +2438,8 @@ ${BOLD}Usage:${RESET}
|
|
|
2414
2438
|
$ dot account create|new <name> [--path <derivation>] Create a new account
|
|
2415
2439
|
$ dot account import <name> --secret <s> [--path <derivation>] Import from BIP39 mnemonic
|
|
2416
2440
|
$ dot account import <name> --env <VAR> [--path <derivation>] Import account backed by env variable
|
|
2441
|
+
$ dot account import --file <path> Batch-import accounts from a file
|
|
2442
|
+
$ dot account export [names...] Export accounts to stdout
|
|
2417
2443
|
$ dot account derive <source> <new-name> --path <derivation> Derive a child account
|
|
2418
2444
|
$ dot account inspect <input> [--prefix <N>] Inspect an account/address/key
|
|
2419
2445
|
$ dot account list List all accounts
|
|
@@ -2426,6 +2452,13 @@ ${BOLD}Examples:${RESET}
|
|
|
2426
2452
|
$ dot account create multi --path //polkadot//0/wallet
|
|
2427
2453
|
$ dot account import treasury --secret "word1 word2 ... word12"
|
|
2428
2454
|
$ dot account import ci-signer --env MY_SECRET --path //ci
|
|
2455
|
+
$ dot account import --file team-accounts.json
|
|
2456
|
+
$ dot account import --file accounts.json --dry-run
|
|
2457
|
+
$ dot account import --file accounts.json --overwrite
|
|
2458
|
+
$ dot account export
|
|
2459
|
+
$ dot account export treasury my-validator
|
|
2460
|
+
$ dot account export --include-secrets --file backup.json
|
|
2461
|
+
$ dot account export --watch-only
|
|
2429
2462
|
$ dot account derive treasury treasury-staking --path //staking
|
|
2430
2463
|
$ dot account inspect alice
|
|
2431
2464
|
$ dot account inspect 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
|
|
@@ -2438,7 +2471,7 @@ ${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
|
|
|
2438
2471
|
Hex seed import (0x...) is not supported via CLI.${RESET}
|
|
2439
2472
|
`.trimStart();
|
|
2440
2473
|
function registerAccountCommands(cli) {
|
|
2441
|
-
cli.command("account [action] [...names]", "Manage local accounts (create, import, list, remove)").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)").action(async (action, names, opts) => {
|
|
2474
|
+
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) => {
|
|
2442
2475
|
if (!action) {
|
|
2443
2476
|
if (process.argv[2] === "accounts")
|
|
2444
2477
|
return accountList(opts);
|
|
@@ -2454,7 +2487,11 @@ function registerAccountCommands(cli) {
|
|
|
2454
2487
|
return accountImport(names[0], opts);
|
|
2455
2488
|
return accountAddWatchOnly(names[0], names[1], opts);
|
|
2456
2489
|
case "import":
|
|
2490
|
+
if (opts.file)
|
|
2491
|
+
return accountBatchImport(opts);
|
|
2457
2492
|
return accountImport(names[0], opts);
|
|
2493
|
+
case "export":
|
|
2494
|
+
return accountExport(names, opts);
|
|
2458
2495
|
case "derive":
|
|
2459
2496
|
return accountDerive(names[0], names[1], opts);
|
|
2460
2497
|
case "list":
|
|
@@ -2916,6 +2953,162 @@ async function accountInspect(input, opts) {
|
|
|
2916
2953
|
console.log();
|
|
2917
2954
|
}
|
|
2918
2955
|
}
|
|
2956
|
+
async function readStdin() {
|
|
2957
|
+
const chunks = [];
|
|
2958
|
+
for await (const chunk of process.stdin) {
|
|
2959
|
+
chunks.push(chunk);
|
|
2960
|
+
}
|
|
2961
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
2962
|
+
}
|
|
2963
|
+
var REDACTED = "<redacted>";
|
|
2964
|
+
async function accountExport(names, opts) {
|
|
2965
|
+
const accountsFile = await loadAccounts();
|
|
2966
|
+
if (opts.includeSecrets) {
|
|
2967
|
+
process.stderr.write(`${YELLOW}Warning: secrets are included in the export.${RESET}
|
|
2968
|
+
`);
|
|
2969
|
+
}
|
|
2970
|
+
let accounts = accountsFile.accounts;
|
|
2971
|
+
if (names.length > 0) {
|
|
2972
|
+
const filtered = [];
|
|
2973
|
+
for (const input of names) {
|
|
2974
|
+
const account = findAccount(accountsFile, input);
|
|
2975
|
+
if (!account) {
|
|
2976
|
+
throw new Error(`Account "${input}" not found.`);
|
|
2977
|
+
}
|
|
2978
|
+
filtered.push(account);
|
|
2979
|
+
}
|
|
2980
|
+
accounts = filtered;
|
|
2981
|
+
}
|
|
2982
|
+
if (opts.watchOnly) {
|
|
2983
|
+
accounts = accounts.filter((a) => isWatchOnly(a));
|
|
2984
|
+
}
|
|
2985
|
+
const exported = accounts.map((account) => {
|
|
2986
|
+
const entry = {
|
|
2987
|
+
name: account.name,
|
|
2988
|
+
publicKey: account.publicKey,
|
|
2989
|
+
derivationPath: account.derivationPath
|
|
2990
|
+
};
|
|
2991
|
+
if (isWatchOnly(account)) {} else if (account.secret !== undefined && isEnvSecret(account.secret)) {
|
|
2992
|
+
entry.secret = account.secret;
|
|
2993
|
+
} else if (opts.includeSecrets) {
|
|
2994
|
+
entry.secret = account.secret;
|
|
2995
|
+
} else {
|
|
2996
|
+
entry.secret = REDACTED;
|
|
2997
|
+
}
|
|
2998
|
+
if (account.bandersnatch && Object.keys(account.bandersnatch).length > 0) {
|
|
2999
|
+
entry.bandersnatch = account.bandersnatch;
|
|
3000
|
+
}
|
|
3001
|
+
return entry;
|
|
3002
|
+
});
|
|
3003
|
+
const exportData = { accounts: exported };
|
|
3004
|
+
const json = `${JSON.stringify(exportData, null, 2)}
|
|
3005
|
+
`;
|
|
3006
|
+
if (opts.file) {
|
|
3007
|
+
await writeFile3(opts.file, json);
|
|
3008
|
+
if (isJsonOutput(opts)) {
|
|
3009
|
+
console.log(formatJson({ action: "exported", file: opts.file, count: exported.length }));
|
|
3010
|
+
} else {
|
|
3011
|
+
console.log(`Exported ${exported.length} account(s) to ${opts.file}`);
|
|
3012
|
+
}
|
|
3013
|
+
} else {
|
|
3014
|
+
process.stdout.write(json);
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
async function accountBatchImport(opts) {
|
|
3018
|
+
let raw;
|
|
3019
|
+
if (!opts.file || opts.file === "-") {
|
|
3020
|
+
raw = await readStdin();
|
|
3021
|
+
} else {
|
|
3022
|
+
raw = await readFile3(opts.file, "utf-8");
|
|
3023
|
+
}
|
|
3024
|
+
let importData;
|
|
3025
|
+
try {
|
|
3026
|
+
importData = JSON.parse(raw);
|
|
3027
|
+
} catch {
|
|
3028
|
+
throw new Error("Invalid JSON input.");
|
|
3029
|
+
}
|
|
3030
|
+
if (!Array.isArray(importData.accounts)) {
|
|
3031
|
+
throw new Error('Invalid import format: missing "accounts" array.');
|
|
3032
|
+
}
|
|
3033
|
+
const accountsFile = await loadAccounts();
|
|
3034
|
+
const added = [];
|
|
3035
|
+
const skipped = [];
|
|
3036
|
+
const overwritten = [];
|
|
3037
|
+
for (const entry of importData.accounts) {
|
|
3038
|
+
if (!entry.name) {
|
|
3039
|
+
process.stderr.write(`${YELLOW}Skipped entry with missing name.${RESET}
|
|
3040
|
+
`);
|
|
3041
|
+
continue;
|
|
3042
|
+
}
|
|
3043
|
+
if (isDevAccount(entry.name)) {
|
|
3044
|
+
process.stderr.write(`${YELLOW}Skipped "${entry.name}": built-in dev account.${RESET}
|
|
3045
|
+
`);
|
|
3046
|
+
skipped.push(entry.name);
|
|
3047
|
+
continue;
|
|
3048
|
+
}
|
|
3049
|
+
const existing = findAccount(accountsFile, entry.name);
|
|
3050
|
+
if (existing && !opts.overwrite) {
|
|
3051
|
+
skipped.push(entry.name);
|
|
3052
|
+
process.stderr.write(`${YELLOW}Skipped "${entry.name}": already exists (use --overwrite to replace)${RESET}
|
|
3053
|
+
`);
|
|
3054
|
+
continue;
|
|
3055
|
+
}
|
|
3056
|
+
const stored = {
|
|
3057
|
+
name: entry.name,
|
|
3058
|
+
publicKey: entry.publicKey || "",
|
|
3059
|
+
derivationPath: entry.derivationPath || ""
|
|
3060
|
+
};
|
|
3061
|
+
if (entry.secret === undefined || entry.secret === REDACTED) {} else if (typeof entry.secret === "object" && "env" in entry.secret) {
|
|
3062
|
+
stored.secret = entry.secret;
|
|
3063
|
+
if (!stored.publicKey) {
|
|
3064
|
+
stored.publicKey = tryDerivePublicKey(entry.secret.env, stored.derivationPath) ?? "";
|
|
3065
|
+
}
|
|
3066
|
+
} else if (typeof entry.secret === "string") {
|
|
3067
|
+
stored.secret = entry.secret;
|
|
3068
|
+
try {
|
|
3069
|
+
const { publicKey } = importAccount(entry.secret, stored.derivationPath);
|
|
3070
|
+
stored.publicKey = publicKeyToHex(publicKey);
|
|
3071
|
+
} catch {
|
|
3072
|
+
process.stderr.write(`${YELLOW}Warning: "${entry.name}" has an invalid secret, importing as watch-only.${RESET}
|
|
3073
|
+
`);
|
|
3074
|
+
delete stored.secret;
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
if (entry.bandersnatch && Object.keys(entry.bandersnatch).length > 0) {
|
|
3078
|
+
stored.bandersnatch = entry.bandersnatch;
|
|
3079
|
+
}
|
|
3080
|
+
if (existing) {
|
|
3081
|
+
const idx = accountsFile.accounts.findIndex((a) => a.name.toLowerCase() === entry.name.toLowerCase());
|
|
3082
|
+
accountsFile.accounts[idx] = stored;
|
|
3083
|
+
overwritten.push(entry.name);
|
|
3084
|
+
} else {
|
|
3085
|
+
accountsFile.accounts.push(stored);
|
|
3086
|
+
added.push(entry.name);
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
if (!opts.dryRun) {
|
|
3090
|
+
await saveAccounts(accountsFile);
|
|
3091
|
+
}
|
|
3092
|
+
if (isJsonOutput(opts)) {
|
|
3093
|
+
console.log(formatJson({
|
|
3094
|
+
action: opts.dryRun ? "dry-run" : "imported",
|
|
3095
|
+
added,
|
|
3096
|
+
overwritten,
|
|
3097
|
+
skipped
|
|
3098
|
+
}));
|
|
3099
|
+
return;
|
|
3100
|
+
}
|
|
3101
|
+
const prefix = opts.dryRun ? "(dry run) " : "";
|
|
3102
|
+
if (added.length > 0)
|
|
3103
|
+
console.log(`${prefix}Added: ${added.join(", ")}`);
|
|
3104
|
+
if (overwritten.length > 0)
|
|
3105
|
+
console.log(`${prefix}Overwritten: ${overwritten.join(", ")}`);
|
|
3106
|
+
if (skipped.length > 0)
|
|
3107
|
+
console.log(`${prefix}Skipped: ${skipped.join(", ")}`);
|
|
3108
|
+
if (added.length === 0 && overwritten.length === 0) {
|
|
3109
|
+
console.log(`${prefix}No accounts imported.`);
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
2919
3112
|
|
|
2920
3113
|
// src/commands/apis.ts
|
|
2921
3114
|
init_store();
|
|
@@ -3040,29 +3233,35 @@ init_types();
|
|
|
3040
3233
|
init_client();
|
|
3041
3234
|
init_metadata();
|
|
3042
3235
|
init_output();
|
|
3236
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
3043
3237
|
var CHAIN_HELP = `
|
|
3044
3238
|
${BOLD}Usage:${RESET}
|
|
3045
3239
|
$ dot chain add <name> --rpc <url> Add a chain via WebSocket RPC
|
|
3046
3240
|
$ dot chain remove <name> Remove a chain
|
|
3047
|
-
$ dot chain update
|
|
3241
|
+
$ dot chain update <name> Re-fetch metadata for a chain
|
|
3048
3242
|
$ dot chain update --all Re-fetch metadata for all configured chains
|
|
3049
3243
|
$ dot chain list List configured chains
|
|
3050
|
-
$ dot chain
|
|
3244
|
+
$ dot chain export [names...] Export chain configuration to stdout
|
|
3245
|
+
$ dot chain import <file> Import chain configuration from a file
|
|
3051
3246
|
|
|
3052
3247
|
${BOLD}Examples:${RESET}
|
|
3053
3248
|
$ dot chain add kusama --rpc wss://kusama-rpc.polkadot.io
|
|
3054
3249
|
$ dot chain add kusama --rpc wss://kusama-rpc.polkadot.io --rpc wss://kusama-rpc.dwellir.com
|
|
3055
3250
|
$ dot chain add my-para --rpc wss://rpc.example.com --relay polkadot
|
|
3056
3251
|
$ dot chain add my-para --rpc wss://rpc.example.com --relay polkadot --parachain-id 2000
|
|
3057
|
-
$ dot chain default kusama
|
|
3058
3252
|
$ dot chain list
|
|
3059
|
-
$ dot chain update
|
|
3060
3253
|
$ dot chain update kusama
|
|
3061
3254
|
$ dot chain update --all
|
|
3062
3255
|
$ dot chain remove kusama
|
|
3256
|
+
$ dot chain export
|
|
3257
|
+
$ dot chain export my-relay my-para --file my-chains.json
|
|
3258
|
+
$ dot chain export --all
|
|
3259
|
+
$ dot chain import my-chains.json
|
|
3260
|
+
$ dot chain import my-chains.json --dry-run
|
|
3261
|
+
$ dot chain import my-chains.json --overwrite
|
|
3063
3262
|
`.trimStart();
|
|
3064
3263
|
function registerChainCommands(cli) {
|
|
3065
|
-
cli.command("chain [action] [
|
|
3264
|
+
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) => {
|
|
3066
3265
|
if (!action) {
|
|
3067
3266
|
if (process.argv[2] === "chains")
|
|
3068
3267
|
return chainList(opts);
|
|
@@ -3071,15 +3270,17 @@ function registerChainCommands(cli) {
|
|
|
3071
3270
|
}
|
|
3072
3271
|
switch (action) {
|
|
3073
3272
|
case "add":
|
|
3074
|
-
return chainAdd(
|
|
3273
|
+
return chainAdd(names[0], opts);
|
|
3075
3274
|
case "remove":
|
|
3076
|
-
return chainRemove(
|
|
3275
|
+
return chainRemove(names[0], opts);
|
|
3077
3276
|
case "list":
|
|
3078
3277
|
return chainList(opts);
|
|
3079
3278
|
case "update":
|
|
3080
|
-
return chainUpdate(
|
|
3081
|
-
case "
|
|
3082
|
-
return
|
|
3279
|
+
return chainUpdate(names[0], opts);
|
|
3280
|
+
case "export":
|
|
3281
|
+
return chainExport(names, opts);
|
|
3282
|
+
case "import":
|
|
3283
|
+
return chainImport(names[0], opts);
|
|
3083
3284
|
default:
|
|
3084
3285
|
console.error(`Unknown action "${action}".
|
|
3085
3286
|
`);
|
|
@@ -3174,11 +3375,6 @@ async function chainRemove(name, opts = {}) {
|
|
|
3174
3375
|
console.error(`Warning: ${orphans.length} chain(s) reference "${resolved}" as their relay: ${orphans.join(", ")}`);
|
|
3175
3376
|
}
|
|
3176
3377
|
delete config.chains[resolved];
|
|
3177
|
-
if (config.defaultChain === resolved) {
|
|
3178
|
-
config.defaultChain = "polkadot";
|
|
3179
|
-
if (!isJsonOutput(opts))
|
|
3180
|
-
console.log(`Default chain reset to "polkadot".`);
|
|
3181
|
-
}
|
|
3182
3378
|
await saveConfig(config);
|
|
3183
3379
|
await removeChainData(resolved);
|
|
3184
3380
|
if (isJsonOutput(opts)) {
|
|
@@ -3192,7 +3388,6 @@ async function chainList(opts = {}) {
|
|
|
3192
3388
|
if (isJsonOutput(opts)) {
|
|
3193
3389
|
const chains = Object.entries(config.chains).map(([name, chainConfig]) => ({
|
|
3194
3390
|
name,
|
|
3195
|
-
default: name === config.defaultChain,
|
|
3196
3391
|
rpc: Array.isArray(chainConfig.rpc) ? chainConfig.rpc : [chainConfig.rpc],
|
|
3197
3392
|
...chainConfig.relay && { relay: chainConfig.relay },
|
|
3198
3393
|
...chainConfig.parachainId != null && { parachainId: chainConfig.parachainId }
|
|
@@ -3221,7 +3416,7 @@ async function chainList(opts = {}) {
|
|
|
3221
3416
|
for (const relayName of relayNames) {
|
|
3222
3417
|
const relayConfig = config.chains[relayName];
|
|
3223
3418
|
if (relayConfig) {
|
|
3224
|
-
printChainLine(" ", relayName, relayConfig
|
|
3419
|
+
printChainLine(" ", relayName, relayConfig);
|
|
3225
3420
|
}
|
|
3226
3421
|
const paras = parachainsByRelay.get(relayName) ?? [];
|
|
3227
3422
|
for (let i = 0;i < paras.length; i++) {
|
|
@@ -3229,21 +3424,19 @@ async function chainList(opts = {}) {
|
|
|
3229
3424
|
const isLast = i === paras.length - 1;
|
|
3230
3425
|
const prefix = isLast ? " └─ " : " ├─ ";
|
|
3231
3426
|
const idSuffix = chainConfig.parachainId != null ? ` ${DIM}[${chainConfig.parachainId}]${RESET}` : "";
|
|
3232
|
-
printChainLine(prefix, name, chainConfig,
|
|
3427
|
+
printChainLine(prefix, name, chainConfig, idSuffix);
|
|
3233
3428
|
}
|
|
3234
3429
|
console.log();
|
|
3235
3430
|
}
|
|
3236
3431
|
for (const [name, chainConfig] of standalone) {
|
|
3237
|
-
printChainLine(" ", name, chainConfig
|
|
3432
|
+
printChainLine(" ", name, chainConfig);
|
|
3238
3433
|
}
|
|
3239
3434
|
if (standalone.length > 0)
|
|
3240
3435
|
console.log();
|
|
3241
3436
|
}
|
|
3242
|
-
function printChainLine(prefix, name, chainConfig,
|
|
3243
|
-
const isDefault = name === defaultChain;
|
|
3244
|
-
const marker = isDefault ? ` ${BOLD}(default)${RESET}` : "";
|
|
3437
|
+
function printChainLine(prefix, name, chainConfig, suffix = "") {
|
|
3245
3438
|
const rpcs = Array.isArray(chainConfig.rpc) ? chainConfig.rpc : [chainConfig.rpc];
|
|
3246
|
-
console.log(`${prefix}${CYAN}${name}${RESET}${suffix}
|
|
3439
|
+
console.log(`${prefix}${CYAN}${name}${RESET}${suffix} ${DIM}${rpcs[0]}${RESET}`);
|
|
3247
3440
|
const indent = prefix.replace(/[^\s]/g, " ");
|
|
3248
3441
|
for (let i = 1;i < rpcs.length; i++) {
|
|
3249
3442
|
console.log(`${indent} ${DIM}${rpcs[i]}${RESET}`);
|
|
@@ -3255,6 +3448,10 @@ async function chainUpdate(name, opts) {
|
|
|
3255
3448
|
await chainUpdateAll(config);
|
|
3256
3449
|
return;
|
|
3257
3450
|
}
|
|
3451
|
+
if (!name) {
|
|
3452
|
+
console.error("Usage: dot chain update <name> | --all");
|
|
3453
|
+
process.exit(1);
|
|
3454
|
+
}
|
|
3258
3455
|
const { name: chainName, chain: chainConfig } = resolveChain(config, name);
|
|
3259
3456
|
process.stderr.write(`Connecting to ${chainName}...
|
|
3260
3457
|
`);
|
|
@@ -3302,23 +3499,132 @@ ${failed} of ${chainNames.length} chains failed to update.`);
|
|
|
3302
3499
|
process.exit(1);
|
|
3303
3500
|
}
|
|
3304
3501
|
}
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3502
|
+
function isBuiltinModified(name, config) {
|
|
3503
|
+
const defaultRpc = DEFAULT_CONFIG.chains[name]?.rpc;
|
|
3504
|
+
const currentRpc = config.chains[name]?.rpc;
|
|
3505
|
+
if (!defaultRpc || !currentRpc)
|
|
3506
|
+
return false;
|
|
3507
|
+
return JSON.stringify(currentRpc) !== JSON.stringify(defaultRpc);
|
|
3508
|
+
}
|
|
3509
|
+
async function readStdin2() {
|
|
3510
|
+
const chunks = [];
|
|
3511
|
+
for await (const chunk of process.stdin) {
|
|
3512
|
+
chunks.push(chunk);
|
|
3309
3513
|
}
|
|
3514
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
3515
|
+
}
|
|
3516
|
+
async function chainExport(names, opts) {
|
|
3310
3517
|
const config = await loadConfig();
|
|
3311
|
-
const
|
|
3312
|
-
if (
|
|
3313
|
-
const
|
|
3314
|
-
|
|
3518
|
+
const exportChains = {};
|
|
3519
|
+
if (names.length > 0) {
|
|
3520
|
+
for (const input of names) {
|
|
3521
|
+
const resolved = findChainName(config, input);
|
|
3522
|
+
if (!resolved) {
|
|
3523
|
+
throw new Error(`Chain "${input}" not found.`);
|
|
3524
|
+
}
|
|
3525
|
+
exportChains[resolved] = config.chains[resolved];
|
|
3526
|
+
}
|
|
3527
|
+
} else if (opts.all) {
|
|
3528
|
+
Object.assign(exportChains, config.chains);
|
|
3529
|
+
} else {
|
|
3530
|
+
for (const [name, chainConfig] of Object.entries(config.chains)) {
|
|
3531
|
+
if (!BUILTIN_CHAIN_NAMES.has(name) || isBuiltinModified(name, config)) {
|
|
3532
|
+
exportChains[name] = chainConfig;
|
|
3533
|
+
}
|
|
3534
|
+
}
|
|
3315
3535
|
}
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3536
|
+
const exportData = {
|
|
3537
|
+
chains: exportChains
|
|
3538
|
+
};
|
|
3539
|
+
const json = `${JSON.stringify(exportData, null, 2)}
|
|
3540
|
+
`;
|
|
3541
|
+
if (opts.file) {
|
|
3542
|
+
await writeFile4(opts.file, json);
|
|
3543
|
+
if (isJsonOutput(opts)) {
|
|
3544
|
+
console.log(formatJson({
|
|
3545
|
+
action: "exported",
|
|
3546
|
+
file: opts.file,
|
|
3547
|
+
count: Object.keys(exportChains).length
|
|
3548
|
+
}));
|
|
3549
|
+
} else {
|
|
3550
|
+
console.log(`Exported ${Object.keys(exportChains).length} chain(s) to ${opts.file}`);
|
|
3551
|
+
}
|
|
3320
3552
|
} else {
|
|
3321
|
-
|
|
3553
|
+
process.stdout.write(json);
|
|
3554
|
+
}
|
|
3555
|
+
}
|
|
3556
|
+
async function chainImport(filePath, opts) {
|
|
3557
|
+
const inputPath = filePath ?? opts.file;
|
|
3558
|
+
let raw;
|
|
3559
|
+
if (inputPath && inputPath !== "-") {
|
|
3560
|
+
raw = await readFile4(inputPath, "utf-8");
|
|
3561
|
+
} else {
|
|
3562
|
+
raw = await readStdin2();
|
|
3563
|
+
}
|
|
3564
|
+
let importData;
|
|
3565
|
+
try {
|
|
3566
|
+
importData = JSON.parse(raw);
|
|
3567
|
+
} catch {
|
|
3568
|
+
throw new Error("Invalid JSON input.");
|
|
3569
|
+
}
|
|
3570
|
+
if (!importData.chains || typeof importData.chains !== "object") {
|
|
3571
|
+
throw new Error('Invalid import format: missing "chains" object.');
|
|
3572
|
+
}
|
|
3573
|
+
const config = await loadConfig();
|
|
3574
|
+
const added = [];
|
|
3575
|
+
const skipped = [];
|
|
3576
|
+
const overwritten = [];
|
|
3577
|
+
const warnings = [];
|
|
3578
|
+
for (const [name, chainConfig] of Object.entries(importData.chains)) {
|
|
3579
|
+
const existing = findChainName(config, name);
|
|
3580
|
+
if (existing && !opts.overwrite) {
|
|
3581
|
+
skipped.push(existing);
|
|
3582
|
+
process.stderr.write(`${YELLOW}Skipped "${existing}": already exists (use --overwrite to replace)${RESET}
|
|
3583
|
+
`);
|
|
3584
|
+
continue;
|
|
3585
|
+
}
|
|
3586
|
+
if (chainConfig.relay) {
|
|
3587
|
+
const relayInImport = Object.keys(importData.chains).some((n) => n.toLowerCase() === chainConfig.relay.toLowerCase());
|
|
3588
|
+
const relayInConfig = findChainName(config, chainConfig.relay);
|
|
3589
|
+
if (!relayInImport && !relayInConfig) {
|
|
3590
|
+
warnings.push(`Chain "${name}" references relay "${chainConfig.relay}" which does not exist.`);
|
|
3591
|
+
process.stderr.write(`${YELLOW}Warning: "${name}" references relay "${chainConfig.relay}" which does not exist.${RESET}
|
|
3592
|
+
`);
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
if (existing) {
|
|
3596
|
+
overwritten.push(existing);
|
|
3597
|
+
config.chains[existing] = chainConfig;
|
|
3598
|
+
} else {
|
|
3599
|
+
added.push(name);
|
|
3600
|
+
config.chains[name] = chainConfig;
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
if (!opts.dryRun) {
|
|
3604
|
+
await saveConfig(config);
|
|
3605
|
+
}
|
|
3606
|
+
if (isJsonOutput(opts)) {
|
|
3607
|
+
console.log(formatJson({
|
|
3608
|
+
action: opts.dryRun ? "dry-run" : "imported",
|
|
3609
|
+
added,
|
|
3610
|
+
overwritten,
|
|
3611
|
+
skipped,
|
|
3612
|
+
warnings
|
|
3613
|
+
}));
|
|
3614
|
+
return;
|
|
3615
|
+
}
|
|
3616
|
+
const prefix = opts.dryRun ? "(dry run) " : "";
|
|
3617
|
+
if (added.length > 0)
|
|
3618
|
+
console.log(`${prefix}Added: ${added.join(", ")}`);
|
|
3619
|
+
if (overwritten.length > 0)
|
|
3620
|
+
console.log(`${prefix}Overwritten: ${overwritten.join(", ")}`);
|
|
3621
|
+
if (skipped.length > 0)
|
|
3622
|
+
console.log(`${prefix}Skipped: ${skipped.join(", ")}`);
|
|
3623
|
+
if (added.length === 0 && overwritten.length === 0) {
|
|
3624
|
+
console.log(`${prefix}No chains imported.`);
|
|
3625
|
+
} else if (!opts.dryRun) {
|
|
3626
|
+
console.error(`
|
|
3627
|
+
Run "dot chain update --all" to fetch metadata for imported chains.`);
|
|
3322
3628
|
}
|
|
3323
3629
|
}
|
|
3324
3630
|
|
|
@@ -3506,12 +3812,23 @@ init_client();
|
|
|
3506
3812
|
init_metadata();
|
|
3507
3813
|
init_output();
|
|
3508
3814
|
async function loadMeta2(chainName, chainConfig, rpcOverride) {
|
|
3815
|
+
if (rpcOverride) {
|
|
3816
|
+
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
3817
|
+
`);
|
|
3818
|
+
const clientHandle = await createChainClient(chainName, chainConfig, rpcOverride);
|
|
3819
|
+
try {
|
|
3820
|
+
const raw = await fetchMetadataFromChain(clientHandle, chainName);
|
|
3821
|
+
return parseMetadata(raw);
|
|
3822
|
+
} finally {
|
|
3823
|
+
clientHandle.destroy();
|
|
3824
|
+
}
|
|
3825
|
+
}
|
|
3509
3826
|
try {
|
|
3510
3827
|
return await getOrFetchMetadata(chainName);
|
|
3511
3828
|
} catch {
|
|
3512
3829
|
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
3513
3830
|
`);
|
|
3514
|
-
const clientHandle = await createChainClient(chainName, chainConfig
|
|
3831
|
+
const clientHandle = await createChainClient(chainName, chainConfig);
|
|
3515
3832
|
try {
|
|
3516
3833
|
return await getOrFetchMetadata(chainName, clientHandle);
|
|
3517
3834
|
} finally {
|
|
@@ -4054,7 +4371,7 @@ async function showItemHelp2(category, target, opts) {
|
|
|
4054
4371
|
init_hash();
|
|
4055
4372
|
init_output();
|
|
4056
4373
|
init_errors();
|
|
4057
|
-
import { readFile as
|
|
4374
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
4058
4375
|
async function resolveInput(data, opts) {
|
|
4059
4376
|
const sources = [data !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
|
|
4060
4377
|
if (sources > 1) {
|
|
@@ -4064,7 +4381,7 @@ async function resolveInput(data, opts) {
|
|
|
4064
4381
|
throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
|
|
4065
4382
|
}
|
|
4066
4383
|
if (opts.file) {
|
|
4067
|
-
const buf = await
|
|
4384
|
+
const buf = await readFile5(opts.file);
|
|
4068
4385
|
return new Uint8Array(buf);
|
|
4069
4386
|
}
|
|
4070
4387
|
if (opts.stdin) {
|
|
@@ -4190,17 +4507,29 @@ function registerInspectCommand(cli) {
|
|
|
4190
4507
|
}
|
|
4191
4508
|
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
4192
4509
|
let meta;
|
|
4193
|
-
|
|
4194
|
-
meta = await getOrFetchMetadata(chainName);
|
|
4195
|
-
} catch {
|
|
4510
|
+
if (opts.rpc) {
|
|
4196
4511
|
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
4197
4512
|
`);
|
|
4198
4513
|
const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
4199
4514
|
try {
|
|
4200
|
-
|
|
4515
|
+
const raw = await fetchMetadataFromChain(clientHandle, chainName);
|
|
4516
|
+
meta = parseMetadata(raw);
|
|
4201
4517
|
} finally {
|
|
4202
4518
|
clientHandle.destroy();
|
|
4203
4519
|
}
|
|
4520
|
+
} else {
|
|
4521
|
+
try {
|
|
4522
|
+
meta = await getOrFetchMetadata(chainName);
|
|
4523
|
+
} catch {
|
|
4524
|
+
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
4525
|
+
`);
|
|
4526
|
+
const clientHandle = await createChainClient(chainName, chainConfig);
|
|
4527
|
+
try {
|
|
4528
|
+
meta = await getOrFetchMetadata(chainName, clientHandle);
|
|
4529
|
+
} finally {
|
|
4530
|
+
clientHandle.destroy();
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4204
4533
|
}
|
|
4205
4534
|
if (!target) {
|
|
4206
4535
|
const pallets = listPallets(meta);
|
|
@@ -4754,7 +5083,7 @@ init_accounts();
|
|
|
4754
5083
|
init_hash();
|
|
4755
5084
|
init_output();
|
|
4756
5085
|
init_errors();
|
|
4757
|
-
import { readFile as
|
|
5086
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
4758
5087
|
var SUPPORTED_TYPES = ["sr25519"];
|
|
4759
5088
|
function isSupportedType(type) {
|
|
4760
5089
|
return SUPPORTED_TYPES.includes(type.toLowerCase());
|
|
@@ -4774,7 +5103,7 @@ async function resolveInput2(data, opts) {
|
|
|
4774
5103
|
throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
|
|
4775
5104
|
}
|
|
4776
5105
|
if (opts.file) {
|
|
4777
|
-
const buf = await
|
|
5106
|
+
const buf = await readFile6(opts.file);
|
|
4778
5107
|
return new Uint8Array(buf);
|
|
4779
5108
|
}
|
|
4780
5109
|
if (opts.stdin) {
|
|
@@ -4854,6 +5183,7 @@ init_resolve_address();
|
|
|
4854
5183
|
init_binary_display();
|
|
4855
5184
|
init_errors();
|
|
4856
5185
|
init_focused_inspect();
|
|
5186
|
+
import { compact as scaleCompact2 } from "@polkadot-api/substrate-bindings";
|
|
4857
5187
|
import { getViewBuilder as getViewBuilder2 } from "@polkadot-api/view-builder";
|
|
4858
5188
|
import { Binary as Binary3 } from "polkadot-api";
|
|
4859
5189
|
import { stringify as stringifyYaml2 } from "yaml";
|
|
@@ -4906,6 +5236,20 @@ function parseMortalityOption(raw) {
|
|
|
4906
5236
|
}
|
|
4907
5237
|
return { mortal: true, period: n };
|
|
4908
5238
|
}
|
|
5239
|
+
function parseAssetOption(raw) {
|
|
5240
|
+
if (raw === undefined)
|
|
5241
|
+
return;
|
|
5242
|
+
try {
|
|
5243
|
+
const parsed = JSON.parse(raw);
|
|
5244
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
5245
|
+
throw new Error("must be a JSON object");
|
|
5246
|
+
}
|
|
5247
|
+
return parsed;
|
|
5248
|
+
} catch (err) {
|
|
5249
|
+
throw new CliError(`Invalid --asset value: ${err.message}
|
|
5250
|
+
` + `Expected an XCM location, e.g. '{"parents":0,"interior":{"type":"X2","value":[{"type":"PalletInstance","value":50},{"type":"GeneralIndex","value":"3"}]}}'`);
|
|
5251
|
+
}
|
|
5252
|
+
}
|
|
4909
5253
|
function parseAtOption(raw) {
|
|
4910
5254
|
if (raw === undefined)
|
|
4911
5255
|
return;
|
|
@@ -4977,9 +5321,9 @@ async function handleTx(target, args, opts) {
|
|
|
4977
5321
|
console.log();
|
|
4978
5322
|
return;
|
|
4979
5323
|
}
|
|
4980
|
-
if (!opts.from && !opts.encode && !opts.toYaml && !opts.toJson) {
|
|
5324
|
+
if (!opts.from && !opts.unsigned && !opts.encode && !opts.toYaml && !opts.toJson) {
|
|
4981
5325
|
if (isRawCall) {
|
|
4982
|
-
throw new Error("--from is required (or use --encode
|
|
5326
|
+
throw new Error("--from is required (or use --unsigned for bare tx, --encode for hex without signing)");
|
|
4983
5327
|
}
|
|
4984
5328
|
await showItemHelp("tx", target, opts);
|
|
4985
5329
|
return;
|
|
@@ -4999,6 +5343,18 @@ async function handleTx(target, args, opts) {
|
|
|
4999
5343
|
if (opts.toYaml && opts.toJson) {
|
|
5000
5344
|
throw new Error("--to-yaml and --to-json are mutually exclusive");
|
|
5001
5345
|
}
|
|
5346
|
+
if (opts.unsigned && opts.from) {
|
|
5347
|
+
throw new Error("--unsigned and --from are mutually exclusive");
|
|
5348
|
+
}
|
|
5349
|
+
if (opts.unsigned && opts.nonce) {
|
|
5350
|
+
throw new Error("--unsigned does not support --nonce");
|
|
5351
|
+
}
|
|
5352
|
+
if (opts.unsigned && opts.tip) {
|
|
5353
|
+
throw new Error("--unsigned does not support --tip");
|
|
5354
|
+
}
|
|
5355
|
+
if (opts.unsigned && opts.mortality) {
|
|
5356
|
+
throw new Error("--unsigned does not support --mortality");
|
|
5357
|
+
}
|
|
5002
5358
|
const config = await loadConfig();
|
|
5003
5359
|
const effectiveChain = opts.chain;
|
|
5004
5360
|
let pallet;
|
|
@@ -5010,9 +5366,9 @@ async function handleTx(target, args, opts) {
|
|
|
5010
5366
|
}
|
|
5011
5367
|
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
5012
5368
|
const decodeOnly = opts.encode || opts.toYaml || opts.toJson;
|
|
5013
|
-
const signer = decodeOnly ? undefined : await resolveAccountSigner(opts.from);
|
|
5369
|
+
const signer = decodeOnly || opts.unsigned ? undefined : await resolveAccountSigner(opts.from);
|
|
5014
5370
|
let clientHandle;
|
|
5015
|
-
if (!decodeOnly) {
|
|
5371
|
+
if (!decodeOnly || opts.unsigned) {
|
|
5016
5372
|
clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
5017
5373
|
}
|
|
5018
5374
|
try {
|
|
@@ -5031,11 +5387,18 @@ async function handleTx(target, args, opts) {
|
|
|
5031
5387
|
let txOptions;
|
|
5032
5388
|
const nonce = parseNonceOption(opts.nonce);
|
|
5033
5389
|
const tip = parseTipOption(opts.tip);
|
|
5390
|
+
const asset = parseAssetOption(opts.asset);
|
|
5034
5391
|
const mortality = parseMortalityOption(opts.mortality);
|
|
5035
5392
|
const at = parseAtOption(opts.at);
|
|
5036
|
-
if (!decodeOnly) {
|
|
5393
|
+
if (!decodeOnly || opts.unsigned) {
|
|
5037
5394
|
const userExtOverrides = parseExtOption(opts.ext);
|
|
5038
|
-
const
|
|
5395
|
+
const skipBuiltins = asset !== undefined ? new Set([...PAPI_BUILTIN_EXTENSIONS2].filter((e) => e !== "ChargeAssetTxPayment")) : PAPI_BUILTIN_EXTENSIONS2;
|
|
5396
|
+
if (asset !== undefined) {
|
|
5397
|
+
userExtOverrides.ChargeAssetTxPayment ??= {
|
|
5398
|
+
value: { tip: tip ?? 0n, asset_id: asset }
|
|
5399
|
+
};
|
|
5400
|
+
}
|
|
5401
|
+
const customSignedExtensions = buildCustomSignedExtensions(meta, userExtOverrides, skipBuiltins);
|
|
5039
5402
|
const built = {};
|
|
5040
5403
|
if (Object.keys(customSignedExtensions).length > 0)
|
|
5041
5404
|
built.customSignedExtensions = customSignedExtensions;
|
|
@@ -5078,7 +5441,7 @@ async function handleTx(target, args, opts) {
|
|
|
5078
5441
|
}
|
|
5079
5442
|
const effectiveArgs = opts.parsedArgs !== undefined ? fileArgsToStrings(opts.parsedArgs) : args;
|
|
5080
5443
|
const callData = await parseCallArgs(meta, palletInfo.name, callInfo.name, effectiveArgs);
|
|
5081
|
-
if (opts.encode || opts.toYaml || opts.toJson) {
|
|
5444
|
+
if (opts.encode && !opts.unsigned || opts.toYaml || opts.toJson) {
|
|
5082
5445
|
const { codec, location } = meta.builder.buildCall(palletInfo.name, callInfo.name);
|
|
5083
5446
|
const encodedArgs = codec.enc(callData);
|
|
5084
5447
|
const fullCall = new Uint8Array([location[0], location[1], ...encodedArgs]);
|
|
@@ -5100,6 +5463,24 @@ async function handleTx(target, args, opts) {
|
|
|
5100
5463
|
callHex = Binary3.toHex(encodedCall);
|
|
5101
5464
|
}
|
|
5102
5465
|
const decodedStr = decodeCall(meta, callHex);
|
|
5466
|
+
if (opts.dryRun && opts.unsigned) {
|
|
5467
|
+
if (isJsonOutput(opts)) {
|
|
5468
|
+
console.log(formatJson({
|
|
5469
|
+
chain: chainName,
|
|
5470
|
+
unsigned: true,
|
|
5471
|
+
callHex,
|
|
5472
|
+
decoded: decodedStr,
|
|
5473
|
+
estimatedFees: null
|
|
5474
|
+
}));
|
|
5475
|
+
return;
|
|
5476
|
+
}
|
|
5477
|
+
console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
|
|
5478
|
+
console.log(` ${BOLD}Type:${RESET} unsigned (bare)`);
|
|
5479
|
+
console.log(` ${BOLD}Call:${RESET} ${callHex}`);
|
|
5480
|
+
console.log(` ${BOLD}Decode:${RESET} ${decodedStr}`);
|
|
5481
|
+
console.log(` ${BOLD}Fees:${RESET} ${DIM}N/A (unsigned transaction)${RESET}`);
|
|
5482
|
+
return;
|
|
5483
|
+
}
|
|
5103
5484
|
if (opts.dryRun) {
|
|
5104
5485
|
const signerAddress = toSs58(signer.publicKey);
|
|
5105
5486
|
let estimatedFees;
|
|
@@ -5120,6 +5501,8 @@ async function handleTx(target, args, opts) {
|
|
|
5120
5501
|
result2.nonce = nonce;
|
|
5121
5502
|
if (tip !== undefined)
|
|
5122
5503
|
result2.tip = String(tip);
|
|
5504
|
+
if (asset !== undefined)
|
|
5505
|
+
result2.asset = asset;
|
|
5123
5506
|
if (mortality !== undefined)
|
|
5124
5507
|
result2.mortality = mortality.mortal ? `mortal (period ${mortality.period})` : "immortal";
|
|
5125
5508
|
if (at !== undefined)
|
|
@@ -5135,6 +5518,8 @@ async function handleTx(target, args, opts) {
|
|
|
5135
5518
|
console.log(` ${BOLD}Nonce:${RESET} ${nonce}`);
|
|
5136
5519
|
if (tip !== undefined)
|
|
5137
5520
|
console.log(` ${BOLD}Tip:${RESET} ${tip}`);
|
|
5521
|
+
if (asset !== undefined)
|
|
5522
|
+
console.log(` ${BOLD}Asset:${RESET} ${JSON.stringify(asset)}`);
|
|
5138
5523
|
if (mortality !== undefined)
|
|
5139
5524
|
console.log(` ${BOLD}Mortality:${RESET} ${mortality.mortal ? `mortal (period ${mortality.period})` : "immortal"}`);
|
|
5140
5525
|
if (at !== undefined)
|
|
@@ -5147,6 +5532,101 @@ async function handleTx(target, args, opts) {
|
|
|
5147
5532
|
return;
|
|
5148
5533
|
}
|
|
5149
5534
|
const waitLevel = parseWaitLevel(opts.wait);
|
|
5535
|
+
if (opts.unsigned) {
|
|
5536
|
+
const callDataBytes = await tx.getEncodedData();
|
|
5537
|
+
const userExtOverrides = parseExtOption(opts.ext);
|
|
5538
|
+
const generalTx = buildGeneralTx(meta, callDataBytes, userExtOverrides);
|
|
5539
|
+
if (opts.encode) {
|
|
5540
|
+
const hex = Binary3.toHex(generalTx);
|
|
5541
|
+
if (isJsonOutput(opts)) {
|
|
5542
|
+
console.log(formatJson({ generalTxHex: hex }));
|
|
5543
|
+
} else {
|
|
5544
|
+
console.log(hex);
|
|
5545
|
+
}
|
|
5546
|
+
return;
|
|
5547
|
+
}
|
|
5548
|
+
const observable = clientHandle.client.submitAndWatch(generalTx, at);
|
|
5549
|
+
if (isJsonOutput(opts)) {
|
|
5550
|
+
const result3 = await watchTransactionJson(observable, waitLevel, { unsigned: true });
|
|
5551
|
+
const rpcUrl3 = primaryRpc(opts.rpc ?? chainConfig.rpc);
|
|
5552
|
+
if (result3.type === "broadcasted") {
|
|
5553
|
+
printJsonLine({ event: "broadcasted", txHash: result3.txHash });
|
|
5554
|
+
return;
|
|
5555
|
+
}
|
|
5556
|
+
const blockHash = result3.block.hash;
|
|
5557
|
+
const explorer = {};
|
|
5558
|
+
if (rpcUrl3) {
|
|
5559
|
+
explorer.polkadotjs = pjsAppsLink(rpcUrl3, blockHash);
|
|
5560
|
+
explorer.papi = papiLink(rpcUrl3, blockHash);
|
|
5561
|
+
}
|
|
5562
|
+
printJsonLine({
|
|
5563
|
+
event: result3.type === "finalized" ? "finalized" : "bestBlock",
|
|
5564
|
+
unsigned: true,
|
|
5565
|
+
blockNumber: result3.block.number,
|
|
5566
|
+
blockHash,
|
|
5567
|
+
txHash: result3.txHash,
|
|
5568
|
+
ok: result3.ok,
|
|
5569
|
+
events: result3.events?.map((e) => ({
|
|
5570
|
+
pallet: e.type,
|
|
5571
|
+
name: e.value?.type,
|
|
5572
|
+
fields: e.value?.value
|
|
5573
|
+
})),
|
|
5574
|
+
dispatchError: result3.ok ? null : formatDispatchError(result3.dispatchError),
|
|
5575
|
+
explorer
|
|
5576
|
+
});
|
|
5577
|
+
if (!result3.ok) {
|
|
5578
|
+
throw new CliError(`Transaction dispatch error: ${formatDispatchError(result3.dispatchError)}`);
|
|
5579
|
+
}
|
|
5580
|
+
return;
|
|
5581
|
+
}
|
|
5582
|
+
const result2 = await watchTransaction(observable, waitLevel, { unsigned: true });
|
|
5583
|
+
console.log();
|
|
5584
|
+
console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
|
|
5585
|
+
console.log(` ${BOLD}Type:${RESET} unsigned (bare)`);
|
|
5586
|
+
console.log(` ${BOLD}Call:${RESET} ${callHex}`);
|
|
5587
|
+
console.log(` ${BOLD}Decode:${RESET} ${decodedStr}`);
|
|
5588
|
+
console.log(` ${BOLD}Tx:${RESET} ${result2.txHash}`);
|
|
5589
|
+
if (result2.type === "broadcasted") {
|
|
5590
|
+
console.log(` ${BOLD}Status:${RESET} ${GREEN}broadcasted${RESET}`);
|
|
5591
|
+
console.log(` ${DIM}Note: tx was broadcast but not yet included in a block${RESET}`);
|
|
5592
|
+
console.log();
|
|
5593
|
+
return;
|
|
5594
|
+
}
|
|
5595
|
+
let dispatchErrorMsg2;
|
|
5596
|
+
if (result2.ok) {
|
|
5597
|
+
const hint = result2.type === "txBestBlocksState" ? ` ${DIM}(best block, not yet finalized)${RESET}` : "";
|
|
5598
|
+
console.log(` ${BOLD}Status:${RESET} ${GREEN}ok${RESET}${hint}`);
|
|
5599
|
+
} else {
|
|
5600
|
+
dispatchErrorMsg2 = formatDispatchError(result2.dispatchError);
|
|
5601
|
+
console.log(` ${BOLD}Status:${RESET} ${RED}dispatch error${RESET}`);
|
|
5602
|
+
console.log(` ${BOLD}Error:${RESET} ${dispatchErrorMsg2}`);
|
|
5603
|
+
}
|
|
5604
|
+
if (result2.events && result2.events.length > 0) {
|
|
5605
|
+
console.log(` ${BOLD}Events:${RESET}`);
|
|
5606
|
+
for (const event of result2.events) {
|
|
5607
|
+
const name = `${CYAN}${event.type}${RESET}.${CYAN}${event.value?.type ?? ""}${RESET}`;
|
|
5608
|
+
const payload = event.value?.value;
|
|
5609
|
+
if (payload && typeof payload === "object") {
|
|
5610
|
+
const fields = Object.entries(payload).map(([k, v]) => `${k}: ${formatEventValue(v)}`).join(", ");
|
|
5611
|
+
console.log(` ${name} { ${fields} }`);
|
|
5612
|
+
} else {
|
|
5613
|
+
console.log(` ${name}`);
|
|
5614
|
+
}
|
|
5615
|
+
}
|
|
5616
|
+
}
|
|
5617
|
+
const rpcUrl2 = primaryRpc(opts.rpc ?? chainConfig.rpc);
|
|
5618
|
+
if (rpcUrl2) {
|
|
5619
|
+
const blockHash = result2.block.hash;
|
|
5620
|
+
console.log(` ${BOLD}Block:${RESET} #${result2.block.number} (${blockHash})`);
|
|
5621
|
+
console.log(` ${DIM}${pjsAppsLink(rpcUrl2, blockHash)}${RESET}`);
|
|
5622
|
+
console.log(` ${DIM}${papiLink(rpcUrl2, blockHash)}${RESET}`);
|
|
5623
|
+
}
|
|
5624
|
+
console.log();
|
|
5625
|
+
if (!result2.ok) {
|
|
5626
|
+
throw new CliError(`Transaction dispatch error: ${dispatchErrorMsg2}`);
|
|
5627
|
+
}
|
|
5628
|
+
return;
|
|
5629
|
+
}
|
|
5150
5630
|
if (isJsonOutput(opts)) {
|
|
5151
5631
|
const result2 = await watchTransactionJson(tx.signSubmitAndWatch(signer, txOptions), waitLevel);
|
|
5152
5632
|
const rpcUrl2 = primaryRpc(opts.rpc ?? chainConfig.rpc);
|
|
@@ -5188,6 +5668,8 @@ async function handleTx(target, args, opts) {
|
|
|
5188
5668
|
console.log(` ${BOLD}Nonce:${RESET} ${nonce}`);
|
|
5189
5669
|
if (tip !== undefined)
|
|
5190
5670
|
console.log(` ${BOLD}Tip:${RESET} ${tip}`);
|
|
5671
|
+
if (asset !== undefined)
|
|
5672
|
+
console.log(` ${BOLD}Asset:${RESET} ${JSON.stringify(asset)}`);
|
|
5191
5673
|
if (mortality !== undefined)
|
|
5192
5674
|
console.log(` ${BOLD}Mortality:${RESET} ${mortality.mortal ? `mortal (period ${mortality.period})` : "immortal"}`);
|
|
5193
5675
|
if (at !== undefined)
|
|
@@ -5869,11 +6351,11 @@ function parseExtOption(ext) {
|
|
|
5869
6351
|
}
|
|
5870
6352
|
}
|
|
5871
6353
|
var NO_DEFAULT2 = Symbol("no-default");
|
|
5872
|
-
function buildCustomSignedExtensions(meta, userOverrides) {
|
|
6354
|
+
function buildCustomSignedExtensions(meta, userOverrides, builtins = PAPI_BUILTIN_EXTENSIONS2) {
|
|
5873
6355
|
const result = {};
|
|
5874
6356
|
const extensions = getSignedExtensions(meta);
|
|
5875
6357
|
for (const ext of extensions) {
|
|
5876
|
-
if (
|
|
6358
|
+
if (builtins.has(ext.identifier))
|
|
5877
6359
|
continue;
|
|
5878
6360
|
if (ext.identifier in userOverrides) {
|
|
5879
6361
|
result[ext.identifier] = userOverrides[ext.identifier];
|
|
@@ -5905,20 +6387,114 @@ function autoDefaultForType(entry) {
|
|
|
5905
6387
|
}
|
|
5906
6388
|
return NO_DEFAULT2;
|
|
5907
6389
|
}
|
|
5908
|
-
function
|
|
6390
|
+
function unsignedDefaultForType(identifier, entry) {
|
|
6391
|
+
switch (identifier) {
|
|
6392
|
+
case "CheckMortality":
|
|
6393
|
+
return { type: "Immortal" };
|
|
6394
|
+
case "CheckNonce":
|
|
6395
|
+
return 0;
|
|
6396
|
+
case "ChargeTransactionPayment":
|
|
6397
|
+
return 0n;
|
|
6398
|
+
case "ChargeAssetTxPayment":
|
|
6399
|
+
return { tip: 0n, asset_id: undefined };
|
|
6400
|
+
}
|
|
6401
|
+
const auto = autoDefaultForType(entry);
|
|
6402
|
+
if (auto !== NO_DEFAULT2)
|
|
6403
|
+
return auto;
|
|
6404
|
+
if (entry.type === "primitive") {
|
|
6405
|
+
switch (entry.value) {
|
|
6406
|
+
case "bool":
|
|
6407
|
+
return false;
|
|
6408
|
+
case "u8":
|
|
6409
|
+
case "u16":
|
|
6410
|
+
case "u32":
|
|
6411
|
+
case "i8":
|
|
6412
|
+
case "i16":
|
|
6413
|
+
case "i32":
|
|
6414
|
+
return 0;
|
|
6415
|
+
case "u64":
|
|
6416
|
+
case "u128":
|
|
6417
|
+
case "u256":
|
|
6418
|
+
case "i64":
|
|
6419
|
+
case "i128":
|
|
6420
|
+
case "i256":
|
|
6421
|
+
return 0n;
|
|
6422
|
+
case "str":
|
|
6423
|
+
case "char":
|
|
6424
|
+
return "";
|
|
6425
|
+
}
|
|
6426
|
+
}
|
|
6427
|
+
if (entry.type === "compact")
|
|
6428
|
+
return 0;
|
|
6429
|
+
if (entry.type === "enum") {
|
|
6430
|
+
const variants = entry.value;
|
|
6431
|
+
if ("Immortal" in variants)
|
|
6432
|
+
return { type: "Immortal" };
|
|
6433
|
+
for (const [name, variant] of Object.entries(variants)) {
|
|
6434
|
+
if (variant.type === "void")
|
|
6435
|
+
return { type: name };
|
|
6436
|
+
}
|
|
6437
|
+
}
|
|
6438
|
+
return NO_DEFAULT2;
|
|
6439
|
+
}
|
|
6440
|
+
function buildGeneralTx(meta, callData, userExtOverrides) {
|
|
6441
|
+
const extensions = getSignedExtensions(meta);
|
|
6442
|
+
const extBytes = [];
|
|
6443
|
+
for (const ext of extensions) {
|
|
6444
|
+
const valueEntry = meta.lookup(ext.type);
|
|
6445
|
+
if (valueEntry.type === "void")
|
|
6446
|
+
continue;
|
|
6447
|
+
let value;
|
|
6448
|
+
if (ext.identifier in userExtOverrides) {
|
|
6449
|
+
const override = userExtOverrides[ext.identifier];
|
|
6450
|
+
value = override.value !== undefined ? override.value : override;
|
|
6451
|
+
} else {
|
|
6452
|
+
value = unsignedDefaultForType(ext.identifier, valueEntry);
|
|
6453
|
+
if (value === NO_DEFAULT2) {
|
|
6454
|
+
throw new CliError(`Cannot determine default unsigned value for extension "${ext.identifier}" ` + `(type: ${valueEntry.type}). Provide it via --ext '{"${ext.identifier}":{"value":...}}'`);
|
|
6455
|
+
}
|
|
6456
|
+
}
|
|
6457
|
+
const codec = meta.builder.buildDefinition(ext.type);
|
|
6458
|
+
extBytes.push(codec.enc(value));
|
|
6459
|
+
}
|
|
6460
|
+
const extVersion = new Uint8Array([0]);
|
|
6461
|
+
const versionByte = new Uint8Array([69]);
|
|
6462
|
+
let payloadLen = 1 + 1;
|
|
6463
|
+
for (const b of extBytes)
|
|
6464
|
+
payloadLen += b.length;
|
|
6465
|
+
payloadLen += callData.length;
|
|
6466
|
+
const lengthPrefix = scaleCompact2.enc(payloadLen);
|
|
6467
|
+
const total = new Uint8Array(lengthPrefix.length + payloadLen);
|
|
6468
|
+
let offset = 0;
|
|
6469
|
+
total.set(lengthPrefix, offset);
|
|
6470
|
+
offset += lengthPrefix.length;
|
|
6471
|
+
total.set(versionByte, offset);
|
|
6472
|
+
offset += 1;
|
|
6473
|
+
total.set(extVersion, offset);
|
|
6474
|
+
offset += 1;
|
|
6475
|
+
for (const b of extBytes) {
|
|
6476
|
+
total.set(b, offset);
|
|
6477
|
+
offset += b.length;
|
|
6478
|
+
}
|
|
6479
|
+
total.set(callData, offset);
|
|
6480
|
+
return total;
|
|
6481
|
+
}
|
|
6482
|
+
function watchTransaction(observable, level, options) {
|
|
5909
6483
|
const spinner = new Spinner;
|
|
5910
6484
|
return new Promise((resolve, reject) => {
|
|
5911
6485
|
let settled = false;
|
|
5912
|
-
spinner.start("Signing...");
|
|
6486
|
+
spinner.start(options?.unsigned ? "Submitting..." : "Signing...");
|
|
5913
6487
|
const subscription = observable.subscribe({
|
|
5914
6488
|
next(event) {
|
|
5915
6489
|
if (settled)
|
|
5916
6490
|
return;
|
|
5917
6491
|
switch (event.type) {
|
|
5918
6492
|
case "signed":
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
6493
|
+
if (!options?.unsigned) {
|
|
6494
|
+
spinner.succeed("Signed");
|
|
6495
|
+
console.log(` ${BOLD}Tx:${RESET} ${event.txHash}`);
|
|
6496
|
+
spinner.start("Broadcasting...");
|
|
6497
|
+
}
|
|
5922
6498
|
break;
|
|
5923
6499
|
case "broadcasted":
|
|
5924
6500
|
if (level === "broadcast") {
|
|
@@ -5962,7 +6538,7 @@ function watchTransaction(observable, level) {
|
|
|
5962
6538
|
});
|
|
5963
6539
|
});
|
|
5964
6540
|
}
|
|
5965
|
-
function watchTransactionJson(observable, level) {
|
|
6541
|
+
function watchTransactionJson(observable, level, options) {
|
|
5966
6542
|
return new Promise((resolve, reject) => {
|
|
5967
6543
|
let settled = false;
|
|
5968
6544
|
const subscription = observable.subscribe({
|
|
@@ -5971,7 +6547,9 @@ function watchTransactionJson(observable, level) {
|
|
|
5971
6547
|
return;
|
|
5972
6548
|
switch (event.type) {
|
|
5973
6549
|
case "signed":
|
|
5974
|
-
|
|
6550
|
+
if (!options?.unsigned) {
|
|
6551
|
+
printJsonLine({ event: "signed", txHash: event.txHash });
|
|
6552
|
+
}
|
|
5975
6553
|
break;
|
|
5976
6554
|
case "broadcasted":
|
|
5977
6555
|
printJsonLine({ event: "broadcasted", txHash: event.txHash });
|
|
@@ -6105,8 +6683,9 @@ async function resolveMnemonic(account) {
|
|
|
6105
6683
|
}
|
|
6106
6684
|
|
|
6107
6685
|
// src/config/store.ts
|
|
6686
|
+
init_errors();
|
|
6108
6687
|
init_types();
|
|
6109
|
-
import { access as access3, mkdir as mkdir3, readFile as
|
|
6688
|
+
import { access as access3, mkdir as mkdir3, readFile as readFile7, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
|
|
6110
6689
|
import { homedir as homedir2 } from "node:os";
|
|
6111
6690
|
import { join as join3 } from "node:path";
|
|
6112
6691
|
var DOT_DIR2 = join3(homedir2(), ".polkadot");
|
|
@@ -6126,7 +6705,7 @@ async function fileExists3(path) {
|
|
|
6126
6705
|
async function loadConfig2() {
|
|
6127
6706
|
await ensureDir3(DOT_DIR2);
|
|
6128
6707
|
if (await fileExists3(CONFIG_PATH2)) {
|
|
6129
|
-
const saved = JSON.parse(await
|
|
6708
|
+
const saved = JSON.parse(await readFile7(CONFIG_PATH2, "utf-8"));
|
|
6130
6709
|
const chains = {};
|
|
6131
6710
|
for (const [name, defaultConfig] of Object.entries(DEFAULT_CONFIG.chains)) {
|
|
6132
6711
|
chains[name] = saved.chains[name] ? { ...defaultConfig, ...saved.chains[name] } : defaultConfig;
|
|
@@ -6136,20 +6715,20 @@ async function loadConfig2() {
|
|
|
6136
6715
|
chains[name] = config;
|
|
6137
6716
|
}
|
|
6138
6717
|
}
|
|
6139
|
-
return {
|
|
6718
|
+
return { chains };
|
|
6140
6719
|
}
|
|
6141
6720
|
await saveConfig2(DEFAULT_CONFIG);
|
|
6142
6721
|
return DEFAULT_CONFIG;
|
|
6143
6722
|
}
|
|
6144
6723
|
async function saveConfig2(config) {
|
|
6145
6724
|
await ensureDir3(DOT_DIR2);
|
|
6146
|
-
await
|
|
6725
|
+
await writeFile5(CONFIG_PATH2, `${JSON.stringify(config, null, 2)}
|
|
6147
6726
|
`);
|
|
6148
6727
|
}
|
|
6149
6728
|
|
|
6150
6729
|
// src/core/file-loader.ts
|
|
6151
6730
|
init_errors();
|
|
6152
|
-
import { access as access4, readFile as
|
|
6731
|
+
import { access as access4, readFile as readFile8 } from "node:fs/promises";
|
|
6153
6732
|
import { parse as parseYaml } from "yaml";
|
|
6154
6733
|
var CATEGORIES = ["tx", "query", "const", "apis"];
|
|
6155
6734
|
var FILE_EXTENSIONS = [".json", ".yaml", ".yml"];
|
|
@@ -6214,7 +6793,7 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
6214
6793
|
} catch {
|
|
6215
6794
|
throw new CliError(`File not found: ${filePath}`);
|
|
6216
6795
|
}
|
|
6217
|
-
const rawText = await
|
|
6796
|
+
const rawText = await readFile8(filePath, "utf-8");
|
|
6218
6797
|
if (!rawText.trim()) {
|
|
6219
6798
|
throw new CliError(`File is empty: ${filePath}`);
|
|
6220
6799
|
}
|
|
@@ -6247,6 +6826,7 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
6247
6826
|
}
|
|
6248
6827
|
const doc = parsed;
|
|
6249
6828
|
const chain = doc.chain != null ? String(doc.chain) : undefined;
|
|
6829
|
+
const unsigned = doc.unsigned === true ? true : undefined;
|
|
6250
6830
|
const foundCategories = CATEGORIES.filter((c) => (c in doc));
|
|
6251
6831
|
if (foundCategories.length === 0) {
|
|
6252
6832
|
throw new CliError(`File "${filePath}" must contain exactly one category key: ${CATEGORIES.join(", ")}. None found.`);
|
|
@@ -6277,14 +6857,15 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
6277
6857
|
category,
|
|
6278
6858
|
pallet,
|
|
6279
6859
|
item,
|
|
6280
|
-
args: args ?? undefined
|
|
6860
|
+
args: args ?? undefined,
|
|
6861
|
+
unsigned
|
|
6281
6862
|
};
|
|
6282
6863
|
}
|
|
6283
6864
|
|
|
6284
6865
|
// src/core/update-notifier.ts
|
|
6285
6866
|
init_store();
|
|
6286
6867
|
import { readFileSync } from "node:fs";
|
|
6287
|
-
import { mkdir as mkdir4, writeFile as
|
|
6868
|
+
import { mkdir as mkdir4, writeFile as writeFile6 } from "node:fs/promises";
|
|
6288
6869
|
import { join as join4 } from "node:path";
|
|
6289
6870
|
var CACHE_FILE = "update-check.json";
|
|
6290
6871
|
var STALE_MS = 24 * 60 * 60 * 1000;
|
|
@@ -6354,7 +6935,7 @@ function readCache() {
|
|
|
6354
6935
|
async function writeCache(cache) {
|
|
6355
6936
|
try {
|
|
6356
6937
|
await mkdir4(getConfigDir(), { recursive: true });
|
|
6357
|
-
await
|
|
6938
|
+
await writeFile6(getCachePath(), `${JSON.stringify(cache)}
|
|
6358
6939
|
`);
|
|
6359
6940
|
} catch {}
|
|
6360
6941
|
}
|
|
@@ -6522,6 +7103,7 @@ if (process.argv[2] === "__complete") {
|
|
|
6522
7103
|
console.log(" dot query.System.Account <addr> Query a storage item");
|
|
6523
7104
|
console.log(" dot query.System List storage items in System");
|
|
6524
7105
|
console.log(" dot tx.System.remark 0xdead --from alice");
|
|
7106
|
+
console.log(" dot tx.System.remark 0xdead --unsigned Submit unsigned/bare tx");
|
|
6525
7107
|
console.log(" dot const.Balances.ExistentialDeposit");
|
|
6526
7108
|
console.log(" dot events.Balances List events in Balances");
|
|
6527
7109
|
console.log(" dot apis.Core.version Call a runtime API");
|
|
@@ -6542,7 +7124,7 @@ if (process.argv[2] === "__complete") {
|
|
|
6542
7124
|
console.log(" completions <sh> Generate shell completions (zsh, bash, fish)");
|
|
6543
7125
|
console.log();
|
|
6544
7126
|
console.log("Global options:");
|
|
6545
|
-
console.log(" --chain <name> Target chain (
|
|
7127
|
+
console.log(" --chain <name> Target chain (required)");
|
|
6546
7128
|
console.log(" --rpc <url> Override RPC endpoint");
|
|
6547
7129
|
console.log(" --json Output as JSON");
|
|
6548
7130
|
console.log(" --output <format> Output format: pretty or json");
|
|
@@ -6551,7 +7133,7 @@ if (process.argv[2] === "__complete") {
|
|
|
6551
7133
|
};
|
|
6552
7134
|
startBackgroundCheck(version);
|
|
6553
7135
|
const cli = cac("dot");
|
|
6554
|
-
cli.option("--chain <name>", "Target chain (
|
|
7136
|
+
cli.option("--chain <name>", "Target chain (required)");
|
|
6555
7137
|
cli.option("--rpc <url>", "Override RPC endpoint for this call");
|
|
6556
7138
|
cli.option("--output <format>", "Output format: pretty or json", {
|
|
6557
7139
|
default: "pretty"
|
|
@@ -6565,9 +7147,9 @@ if (process.argv[2] === "__complete") {
|
|
|
6565
7147
|
registerParachainCommand(cli);
|
|
6566
7148
|
registerCompletionsCommand(cli);
|
|
6567
7149
|
registerVerifiableCommands(cli);
|
|
6568
|
-
cli.command("[dotpath] [...args]").option("--from <name>", "Account to sign with (for tx)").option("--dry-run", "Estimate fees without submitting (for tx)").option("--encode", "Encode call to hex without signing (for tx)").option("--to-yaml", "Decode call to YAML file format (for tx)").option("--to-json", "Decode call to JSON file format (for tx)").option("--ext <json>", "Custom signed extension values as JSON (for tx)").option("-w, --wait <level>", "Resolve at: broadcast, best-block (or best), finalized (for tx)", {
|
|
7150
|
+
cli.command("[dotpath] [...args]").option("--from <name>", "Account to sign with (for tx)").option("--dry-run", "Estimate fees without submitting (for tx)").option("--encode", "Encode call to hex without signing (for tx)").option("--to-yaml", "Decode call to YAML file format (for tx)").option("--to-json", "Decode call to JSON file format (for tx)").option("--ext <json>", "Custom signed extension values as JSON (for tx)").option("--asset <json>", "Pay fees in an alternative asset (XCM location JSON, for tx)").option("-w, --wait <level>", "Resolve at: broadcast, best-block (or best), finalized (for tx)", {
|
|
6569
7151
|
default: "finalized"
|
|
6570
|
-
}).option("--dump", "Dump all entries of a storage map (without specifying a key)").option("--var <kv>", "Template variable for file input (KEY=VALUE, repeatable)").option("--nonce <n>", "Custom nonce for manual tx sequencing (for tx)").option("--tip <amount>", "Tip to prioritize transaction (for tx)").option("--mortality <spec>", '"immortal" or period number (for tx)').option("--at <block>", 'Block hash, "best", or "finalized" to validate against (for tx)').action(async (dotpath, args, opts) => {
|
|
7152
|
+
}).option("--dump", "Dump all entries of a storage map (without specifying a key)").option("--var <kv>", "Template variable for file input (KEY=VALUE, repeatable)").option("--nonce <n>", "Custom nonce for manual tx sequencing (for tx)").option("--tip <amount>", "Tip to prioritize transaction (for tx)").option("--mortality <spec>", '"immortal" or period number (for tx)').option("--at <block>", 'Block hash, "best", or "finalized" to validate against (for tx)').option("--unsigned", "Submit as unsigned/bare transaction (no signer required, for tx)").action(async (dotpath, args, opts) => {
|
|
6571
7153
|
if (!dotpath) {
|
|
6572
7154
|
printHelp();
|
|
6573
7155
|
return;
|
|
@@ -6588,11 +7170,13 @@ if (process.argv[2] === "__complete") {
|
|
|
6588
7170
|
await handleTx(target2, args, {
|
|
6589
7171
|
...handlerOpts2,
|
|
6590
7172
|
from: opts.from,
|
|
7173
|
+
unsigned: opts.unsigned ?? cmd.unsigned,
|
|
6591
7174
|
dryRun: opts.dryRun,
|
|
6592
7175
|
encode: opts.encode,
|
|
6593
7176
|
toYaml: opts.toYaml,
|
|
6594
7177
|
toJson: opts.toJson,
|
|
6595
7178
|
ext: opts.ext,
|
|
7179
|
+
asset: opts.asset,
|
|
6596
7180
|
wait: opts.wait,
|
|
6597
7181
|
nonce: opts.nonce,
|
|
6598
7182
|
tip: opts.tip,
|
|
@@ -6657,11 +7241,13 @@ if (process.argv[2] === "__complete") {
|
|
|
6657
7241
|
const txOpts = {
|
|
6658
7242
|
...handlerOpts,
|
|
6659
7243
|
from: opts.from,
|
|
7244
|
+
unsigned: opts.unsigned,
|
|
6660
7245
|
dryRun: opts.dryRun,
|
|
6661
7246
|
encode: opts.encode,
|
|
6662
7247
|
toYaml: opts.toYaml,
|
|
6663
7248
|
toJson: opts.toJson,
|
|
6664
7249
|
ext: opts.ext,
|
|
7250
|
+
asset: opts.asset,
|
|
6665
7251
|
wait: opts.wait,
|
|
6666
7252
|
nonce: opts.nonce,
|
|
6667
7253
|
tip: opts.tip,
|