polkadot-cli 1.13.0 → 1.14.1
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 +216 -13
- package/dist/cli.mjs +705 -113
- 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";
|
|
@@ -1259,7 +1262,10 @@ async function parseTypedArg(meta, entry, arg) {
|
|
|
1259
1262
|
case "array": {
|
|
1260
1263
|
const inner = entry.value;
|
|
1261
1264
|
if (inner.type === "primitive" && inner.value === "u8") {
|
|
1262
|
-
|
|
1265
|
+
const isHex = /^0x[0-9a-fA-F]*$/.test(arg);
|
|
1266
|
+
if (entry.type === "array" && isHex)
|
|
1267
|
+
return arg;
|
|
1268
|
+
if (isHex)
|
|
1263
1269
|
return Binary2.fromHex(arg);
|
|
1264
1270
|
return Binary2.fromText(arg);
|
|
1265
1271
|
}
|
|
@@ -1473,12 +1479,23 @@ var init_apis = __esm(() => {
|
|
|
1473
1479
|
|
|
1474
1480
|
// src/commands/focused-inspect.ts
|
|
1475
1481
|
async function loadMeta(chainName, chainConfig, rpcOverride) {
|
|
1482
|
+
if (rpcOverride) {
|
|
1483
|
+
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
1484
|
+
`);
|
|
1485
|
+
const clientHandle = await createChainClient(chainName, chainConfig, rpcOverride);
|
|
1486
|
+
try {
|
|
1487
|
+
const raw = await fetchMetadataFromChain(clientHandle, chainName);
|
|
1488
|
+
return parseMetadata(raw);
|
|
1489
|
+
} finally {
|
|
1490
|
+
clientHandle.destroy();
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1476
1493
|
try {
|
|
1477
1494
|
return await getOrFetchMetadata(chainName);
|
|
1478
1495
|
} catch {
|
|
1479
1496
|
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
1480
1497
|
`);
|
|
1481
|
-
const clientHandle = await createChainClient(chainName, chainConfig
|
|
1498
|
+
const clientHandle = await createChainClient(chainName, chainConfig);
|
|
1482
1499
|
try {
|
|
1483
1500
|
return await getOrFetchMetadata(chainName, clientHandle);
|
|
1484
1501
|
} finally {
|
|
@@ -2245,7 +2262,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2245
2262
|
return completeApisCategory(first, numComplete, endsWithDot, completeSegments, currentWord, config, chainFromFlag);
|
|
2246
2263
|
}
|
|
2247
2264
|
if (numComplete === 1 && endsWithDot) {
|
|
2248
|
-
const chainName = chainFromFlag
|
|
2265
|
+
const chainName = chainFromFlag;
|
|
2266
|
+
if (!chainName)
|
|
2267
|
+
return [];
|
|
2249
2268
|
const pallets = await loadPallets(config, chainName);
|
|
2250
2269
|
if (!pallets)
|
|
2251
2270
|
return [];
|
|
@@ -2254,7 +2273,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2254
2273
|
return filterPrefix(candidates, currentWord.slice(0, -1));
|
|
2255
2274
|
}
|
|
2256
2275
|
if (numComplete === 1 && !endsWithDot) {
|
|
2257
|
-
const chainName = chainFromFlag
|
|
2276
|
+
const chainName = chainFromFlag;
|
|
2277
|
+
if (!chainName)
|
|
2278
|
+
return [];
|
|
2258
2279
|
const pallets = await loadPallets(config, chainName);
|
|
2259
2280
|
if (!pallets)
|
|
2260
2281
|
return [];
|
|
@@ -2264,7 +2285,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2264
2285
|
}
|
|
2265
2286
|
if (numComplete === 2) {
|
|
2266
2287
|
const palletName2 = completeSegments[1];
|
|
2267
|
-
const chainName = chainFromFlag
|
|
2288
|
+
const chainName = chainFromFlag;
|
|
2289
|
+
if (!chainName)
|
|
2290
|
+
return [];
|
|
2268
2291
|
const pallets = await loadPallets(config, chainName);
|
|
2269
2292
|
if (!pallets)
|
|
2270
2293
|
return [];
|
|
@@ -2326,7 +2349,9 @@ async function completeDotpath(currentWord, config, knownChains, precedingWords)
|
|
|
2326
2349
|
return [];
|
|
2327
2350
|
}
|
|
2328
2351
|
async function completeApisCategory(prefix, numComplete, endsWithDot, segments, currentWord, config, chainNameOverride) {
|
|
2329
|
-
const chainName = chainNameOverride
|
|
2352
|
+
const chainName = chainNameOverride;
|
|
2353
|
+
if (!chainName)
|
|
2354
|
+
return [];
|
|
2330
2355
|
const apis = await loadRuntimeApiNames(config, chainName);
|
|
2331
2356
|
if (!apis)
|
|
2332
2357
|
return [];
|
|
@@ -2370,7 +2395,7 @@ var init_complete = __esm(() => {
|
|
|
2370
2395
|
api: "apis"
|
|
2371
2396
|
};
|
|
2372
2397
|
NAMED_COMMANDS = ["chain", "account", "inspect", "hash", "sign", "parachain", "completions"];
|
|
2373
|
-
CHAIN_SUBCOMMANDS = ["add", "remove", "update", "list"
|
|
2398
|
+
CHAIN_SUBCOMMANDS = ["add", "remove", "update", "list"];
|
|
2374
2399
|
ACCOUNT_SUBCOMMANDS = [
|
|
2375
2400
|
"add",
|
|
2376
2401
|
"create",
|
|
@@ -2385,6 +2410,7 @@ var init_complete = __esm(() => {
|
|
|
2385
2410
|
GLOBAL_OPTIONS = ["--chain", "--rpc", "--output", "--help", "--version"];
|
|
2386
2411
|
TX_OPTIONS = [
|
|
2387
2412
|
"--from",
|
|
2413
|
+
"--unsigned",
|
|
2388
2414
|
"--dry-run",
|
|
2389
2415
|
"--encode",
|
|
2390
2416
|
"--ext",
|
|
@@ -2400,12 +2426,13 @@ var init_complete = __esm(() => {
|
|
|
2400
2426
|
// src/cli.ts
|
|
2401
2427
|
import cac from "cac";
|
|
2402
2428
|
// package.json
|
|
2403
|
-
var version = "1.
|
|
2429
|
+
var version = "1.14.1";
|
|
2404
2430
|
|
|
2405
2431
|
// src/commands/account.ts
|
|
2406
2432
|
init_accounts_store();
|
|
2407
2433
|
init_accounts();
|
|
2408
2434
|
init_output();
|
|
2435
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
2409
2436
|
var ACCOUNT_HELP = `
|
|
2410
2437
|
${BOLD}Usage:${RESET}
|
|
2411
2438
|
$ dot account add <name> <ss58|hex> Add a watch-only address (no secret)
|
|
@@ -2414,6 +2441,8 @@ ${BOLD}Usage:${RESET}
|
|
|
2414
2441
|
$ dot account create|new <name> [--path <derivation>] Create a new account
|
|
2415
2442
|
$ dot account import <name> --secret <s> [--path <derivation>] Import from BIP39 mnemonic
|
|
2416
2443
|
$ dot account import <name> --env <VAR> [--path <derivation>] Import account backed by env variable
|
|
2444
|
+
$ dot account import --file <path> Batch-import accounts from a file
|
|
2445
|
+
$ dot account export [names...] Export accounts to stdout
|
|
2417
2446
|
$ dot account derive <source> <new-name> --path <derivation> Derive a child account
|
|
2418
2447
|
$ dot account inspect <input> [--prefix <N>] Inspect an account/address/key
|
|
2419
2448
|
$ dot account list List all accounts
|
|
@@ -2426,6 +2455,13 @@ ${BOLD}Examples:${RESET}
|
|
|
2426
2455
|
$ dot account create multi --path //polkadot//0/wallet
|
|
2427
2456
|
$ dot account import treasury --secret "word1 word2 ... word12"
|
|
2428
2457
|
$ dot account import ci-signer --env MY_SECRET --path //ci
|
|
2458
|
+
$ dot account import --file team-accounts.json
|
|
2459
|
+
$ dot account import --file accounts.json --dry-run
|
|
2460
|
+
$ dot account import --file accounts.json --overwrite
|
|
2461
|
+
$ dot account export
|
|
2462
|
+
$ dot account export treasury my-validator
|
|
2463
|
+
$ dot account export --include-secrets --file backup.json
|
|
2464
|
+
$ dot account export --watch-only
|
|
2429
2465
|
$ dot account derive treasury treasury-staking --path //staking
|
|
2430
2466
|
$ dot account inspect alice
|
|
2431
2467
|
$ dot account inspect 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
|
|
@@ -2438,7 +2474,7 @@ ${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
|
|
|
2438
2474
|
Hex seed import (0x...) is not supported via CLI.${RESET}
|
|
2439
2475
|
`.trimStart();
|
|
2440
2476
|
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) => {
|
|
2477
|
+
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
2478
|
if (!action) {
|
|
2443
2479
|
if (process.argv[2] === "accounts")
|
|
2444
2480
|
return accountList(opts);
|
|
@@ -2454,7 +2490,11 @@ function registerAccountCommands(cli) {
|
|
|
2454
2490
|
return accountImport(names[0], opts);
|
|
2455
2491
|
return accountAddWatchOnly(names[0], names[1], opts);
|
|
2456
2492
|
case "import":
|
|
2493
|
+
if (opts.file)
|
|
2494
|
+
return accountBatchImport(opts);
|
|
2457
2495
|
return accountImport(names[0], opts);
|
|
2496
|
+
case "export":
|
|
2497
|
+
return accountExport(names, opts);
|
|
2458
2498
|
case "derive":
|
|
2459
2499
|
return accountDerive(names[0], names[1], opts);
|
|
2460
2500
|
case "list":
|
|
@@ -2916,6 +2956,162 @@ async function accountInspect(input, opts) {
|
|
|
2916
2956
|
console.log();
|
|
2917
2957
|
}
|
|
2918
2958
|
}
|
|
2959
|
+
async function readStdin() {
|
|
2960
|
+
const chunks = [];
|
|
2961
|
+
for await (const chunk of process.stdin) {
|
|
2962
|
+
chunks.push(chunk);
|
|
2963
|
+
}
|
|
2964
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
2965
|
+
}
|
|
2966
|
+
var REDACTED = "<redacted>";
|
|
2967
|
+
async function accountExport(names, opts) {
|
|
2968
|
+
const accountsFile = await loadAccounts();
|
|
2969
|
+
if (opts.includeSecrets) {
|
|
2970
|
+
process.stderr.write(`${YELLOW}Warning: secrets are included in the export.${RESET}
|
|
2971
|
+
`);
|
|
2972
|
+
}
|
|
2973
|
+
let accounts = accountsFile.accounts;
|
|
2974
|
+
if (names.length > 0) {
|
|
2975
|
+
const filtered = [];
|
|
2976
|
+
for (const input of names) {
|
|
2977
|
+
const account = findAccount(accountsFile, input);
|
|
2978
|
+
if (!account) {
|
|
2979
|
+
throw new Error(`Account "${input}" not found.`);
|
|
2980
|
+
}
|
|
2981
|
+
filtered.push(account);
|
|
2982
|
+
}
|
|
2983
|
+
accounts = filtered;
|
|
2984
|
+
}
|
|
2985
|
+
if (opts.watchOnly) {
|
|
2986
|
+
accounts = accounts.filter((a) => isWatchOnly(a));
|
|
2987
|
+
}
|
|
2988
|
+
const exported = accounts.map((account) => {
|
|
2989
|
+
const entry = {
|
|
2990
|
+
name: account.name,
|
|
2991
|
+
publicKey: account.publicKey,
|
|
2992
|
+
derivationPath: account.derivationPath
|
|
2993
|
+
};
|
|
2994
|
+
if (isWatchOnly(account)) {} else if (account.secret !== undefined && isEnvSecret(account.secret)) {
|
|
2995
|
+
entry.secret = account.secret;
|
|
2996
|
+
} else if (opts.includeSecrets) {
|
|
2997
|
+
entry.secret = account.secret;
|
|
2998
|
+
} else {
|
|
2999
|
+
entry.secret = REDACTED;
|
|
3000
|
+
}
|
|
3001
|
+
if (account.bandersnatch && Object.keys(account.bandersnatch).length > 0) {
|
|
3002
|
+
entry.bandersnatch = account.bandersnatch;
|
|
3003
|
+
}
|
|
3004
|
+
return entry;
|
|
3005
|
+
});
|
|
3006
|
+
const exportData = { accounts: exported };
|
|
3007
|
+
const json = `${JSON.stringify(exportData, null, 2)}
|
|
3008
|
+
`;
|
|
3009
|
+
if (opts.file) {
|
|
3010
|
+
await writeFile3(opts.file, json);
|
|
3011
|
+
if (isJsonOutput(opts)) {
|
|
3012
|
+
console.log(formatJson({ action: "exported", file: opts.file, count: exported.length }));
|
|
3013
|
+
} else {
|
|
3014
|
+
console.log(`Exported ${exported.length} account(s) to ${opts.file}`);
|
|
3015
|
+
}
|
|
3016
|
+
} else {
|
|
3017
|
+
process.stdout.write(json);
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
async function accountBatchImport(opts) {
|
|
3021
|
+
let raw;
|
|
3022
|
+
if (!opts.file || opts.file === "-") {
|
|
3023
|
+
raw = await readStdin();
|
|
3024
|
+
} else {
|
|
3025
|
+
raw = await readFile3(opts.file, "utf-8");
|
|
3026
|
+
}
|
|
3027
|
+
let importData;
|
|
3028
|
+
try {
|
|
3029
|
+
importData = JSON.parse(raw);
|
|
3030
|
+
} catch {
|
|
3031
|
+
throw new Error("Invalid JSON input.");
|
|
3032
|
+
}
|
|
3033
|
+
if (!Array.isArray(importData.accounts)) {
|
|
3034
|
+
throw new Error('Invalid import format: missing "accounts" array.');
|
|
3035
|
+
}
|
|
3036
|
+
const accountsFile = await loadAccounts();
|
|
3037
|
+
const added = [];
|
|
3038
|
+
const skipped = [];
|
|
3039
|
+
const overwritten = [];
|
|
3040
|
+
for (const entry of importData.accounts) {
|
|
3041
|
+
if (!entry.name) {
|
|
3042
|
+
process.stderr.write(`${YELLOW}Skipped entry with missing name.${RESET}
|
|
3043
|
+
`);
|
|
3044
|
+
continue;
|
|
3045
|
+
}
|
|
3046
|
+
if (isDevAccount(entry.name)) {
|
|
3047
|
+
process.stderr.write(`${YELLOW}Skipped "${entry.name}": built-in dev account.${RESET}
|
|
3048
|
+
`);
|
|
3049
|
+
skipped.push(entry.name);
|
|
3050
|
+
continue;
|
|
3051
|
+
}
|
|
3052
|
+
const existing = findAccount(accountsFile, entry.name);
|
|
3053
|
+
if (existing && !opts.overwrite) {
|
|
3054
|
+
skipped.push(entry.name);
|
|
3055
|
+
process.stderr.write(`${YELLOW}Skipped "${entry.name}": already exists (use --overwrite to replace)${RESET}
|
|
3056
|
+
`);
|
|
3057
|
+
continue;
|
|
3058
|
+
}
|
|
3059
|
+
const stored = {
|
|
3060
|
+
name: entry.name,
|
|
3061
|
+
publicKey: entry.publicKey || "",
|
|
3062
|
+
derivationPath: entry.derivationPath || ""
|
|
3063
|
+
};
|
|
3064
|
+
if (entry.secret === undefined || entry.secret === REDACTED) {} else if (typeof entry.secret === "object" && "env" in entry.secret) {
|
|
3065
|
+
stored.secret = entry.secret;
|
|
3066
|
+
if (!stored.publicKey) {
|
|
3067
|
+
stored.publicKey = tryDerivePublicKey(entry.secret.env, stored.derivationPath) ?? "";
|
|
3068
|
+
}
|
|
3069
|
+
} else if (typeof entry.secret === "string") {
|
|
3070
|
+
stored.secret = entry.secret;
|
|
3071
|
+
try {
|
|
3072
|
+
const { publicKey } = importAccount(entry.secret, stored.derivationPath);
|
|
3073
|
+
stored.publicKey = publicKeyToHex(publicKey);
|
|
3074
|
+
} catch {
|
|
3075
|
+
process.stderr.write(`${YELLOW}Warning: "${entry.name}" has an invalid secret, importing as watch-only.${RESET}
|
|
3076
|
+
`);
|
|
3077
|
+
delete stored.secret;
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
if (entry.bandersnatch && Object.keys(entry.bandersnatch).length > 0) {
|
|
3081
|
+
stored.bandersnatch = entry.bandersnatch;
|
|
3082
|
+
}
|
|
3083
|
+
if (existing) {
|
|
3084
|
+
const idx = accountsFile.accounts.findIndex((a) => a.name.toLowerCase() === entry.name.toLowerCase());
|
|
3085
|
+
accountsFile.accounts[idx] = stored;
|
|
3086
|
+
overwritten.push(entry.name);
|
|
3087
|
+
} else {
|
|
3088
|
+
accountsFile.accounts.push(stored);
|
|
3089
|
+
added.push(entry.name);
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
if (!opts.dryRun) {
|
|
3093
|
+
await saveAccounts(accountsFile);
|
|
3094
|
+
}
|
|
3095
|
+
if (isJsonOutput(opts)) {
|
|
3096
|
+
console.log(formatJson({
|
|
3097
|
+
action: opts.dryRun ? "dry-run" : "imported",
|
|
3098
|
+
added,
|
|
3099
|
+
overwritten,
|
|
3100
|
+
skipped
|
|
3101
|
+
}));
|
|
3102
|
+
return;
|
|
3103
|
+
}
|
|
3104
|
+
const prefix = opts.dryRun ? "(dry run) " : "";
|
|
3105
|
+
if (added.length > 0)
|
|
3106
|
+
console.log(`${prefix}Added: ${added.join(", ")}`);
|
|
3107
|
+
if (overwritten.length > 0)
|
|
3108
|
+
console.log(`${prefix}Overwritten: ${overwritten.join(", ")}`);
|
|
3109
|
+
if (skipped.length > 0)
|
|
3110
|
+
console.log(`${prefix}Skipped: ${skipped.join(", ")}`);
|
|
3111
|
+
if (added.length === 0 && overwritten.length === 0) {
|
|
3112
|
+
console.log(`${prefix}No accounts imported.`);
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
2919
3115
|
|
|
2920
3116
|
// src/commands/apis.ts
|
|
2921
3117
|
init_store();
|
|
@@ -3040,29 +3236,35 @@ init_types();
|
|
|
3040
3236
|
init_client();
|
|
3041
3237
|
init_metadata();
|
|
3042
3238
|
init_output();
|
|
3239
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
3043
3240
|
var CHAIN_HELP = `
|
|
3044
3241
|
${BOLD}Usage:${RESET}
|
|
3045
3242
|
$ dot chain add <name> --rpc <url> Add a chain via WebSocket RPC
|
|
3046
3243
|
$ dot chain remove <name> Remove a chain
|
|
3047
|
-
$ dot chain update
|
|
3244
|
+
$ dot chain update <name> Re-fetch metadata for a chain
|
|
3048
3245
|
$ dot chain update --all Re-fetch metadata for all configured chains
|
|
3049
3246
|
$ dot chain list List configured chains
|
|
3050
|
-
$ dot chain
|
|
3247
|
+
$ dot chain export [names...] Export chain configuration to stdout
|
|
3248
|
+
$ dot chain import <file> Import chain configuration from a file
|
|
3051
3249
|
|
|
3052
3250
|
${BOLD}Examples:${RESET}
|
|
3053
3251
|
$ dot chain add kusama --rpc wss://kusama-rpc.polkadot.io
|
|
3054
3252
|
$ dot chain add kusama --rpc wss://kusama-rpc.polkadot.io --rpc wss://kusama-rpc.dwellir.com
|
|
3055
3253
|
$ dot chain add my-para --rpc wss://rpc.example.com --relay polkadot
|
|
3056
3254
|
$ dot chain add my-para --rpc wss://rpc.example.com --relay polkadot --parachain-id 2000
|
|
3057
|
-
$ dot chain default kusama
|
|
3058
3255
|
$ dot chain list
|
|
3059
|
-
$ dot chain update
|
|
3060
3256
|
$ dot chain update kusama
|
|
3061
3257
|
$ dot chain update --all
|
|
3062
3258
|
$ dot chain remove kusama
|
|
3259
|
+
$ dot chain export
|
|
3260
|
+
$ dot chain export my-relay my-para --file my-chains.json
|
|
3261
|
+
$ dot chain export --all
|
|
3262
|
+
$ dot chain import my-chains.json
|
|
3263
|
+
$ dot chain import my-chains.json --dry-run
|
|
3264
|
+
$ dot chain import my-chains.json --overwrite
|
|
3063
3265
|
`.trimStart();
|
|
3064
3266
|
function registerChainCommands(cli) {
|
|
3065
|
-
cli.command("chain [action] [
|
|
3267
|
+
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
3268
|
if (!action) {
|
|
3067
3269
|
if (process.argv[2] === "chains")
|
|
3068
3270
|
return chainList(opts);
|
|
@@ -3071,15 +3273,17 @@ function registerChainCommands(cli) {
|
|
|
3071
3273
|
}
|
|
3072
3274
|
switch (action) {
|
|
3073
3275
|
case "add":
|
|
3074
|
-
return chainAdd(
|
|
3276
|
+
return chainAdd(names[0], opts);
|
|
3075
3277
|
case "remove":
|
|
3076
|
-
return chainRemove(
|
|
3278
|
+
return chainRemove(names[0], opts);
|
|
3077
3279
|
case "list":
|
|
3078
3280
|
return chainList(opts);
|
|
3079
3281
|
case "update":
|
|
3080
|
-
return chainUpdate(
|
|
3081
|
-
case "
|
|
3082
|
-
return
|
|
3282
|
+
return chainUpdate(names[0], opts);
|
|
3283
|
+
case "export":
|
|
3284
|
+
return chainExport(names, opts);
|
|
3285
|
+
case "import":
|
|
3286
|
+
return chainImport(names[0], opts);
|
|
3083
3287
|
default:
|
|
3084
3288
|
console.error(`Unknown action "${action}".
|
|
3085
3289
|
`);
|
|
@@ -3174,11 +3378,6 @@ async function chainRemove(name, opts = {}) {
|
|
|
3174
3378
|
console.error(`Warning: ${orphans.length} chain(s) reference "${resolved}" as their relay: ${orphans.join(", ")}`);
|
|
3175
3379
|
}
|
|
3176
3380
|
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
3381
|
await saveConfig(config);
|
|
3183
3382
|
await removeChainData(resolved);
|
|
3184
3383
|
if (isJsonOutput(opts)) {
|
|
@@ -3192,7 +3391,6 @@ async function chainList(opts = {}) {
|
|
|
3192
3391
|
if (isJsonOutput(opts)) {
|
|
3193
3392
|
const chains = Object.entries(config.chains).map(([name, chainConfig]) => ({
|
|
3194
3393
|
name,
|
|
3195
|
-
default: name === config.defaultChain,
|
|
3196
3394
|
rpc: Array.isArray(chainConfig.rpc) ? chainConfig.rpc : [chainConfig.rpc],
|
|
3197
3395
|
...chainConfig.relay && { relay: chainConfig.relay },
|
|
3198
3396
|
...chainConfig.parachainId != null && { parachainId: chainConfig.parachainId }
|
|
@@ -3221,7 +3419,7 @@ async function chainList(opts = {}) {
|
|
|
3221
3419
|
for (const relayName of relayNames) {
|
|
3222
3420
|
const relayConfig = config.chains[relayName];
|
|
3223
3421
|
if (relayConfig) {
|
|
3224
|
-
printChainLine(" ", relayName, relayConfig
|
|
3422
|
+
printChainLine(" ", relayName, relayConfig);
|
|
3225
3423
|
}
|
|
3226
3424
|
const paras = parachainsByRelay.get(relayName) ?? [];
|
|
3227
3425
|
for (let i = 0;i < paras.length; i++) {
|
|
@@ -3229,21 +3427,19 @@ async function chainList(opts = {}) {
|
|
|
3229
3427
|
const isLast = i === paras.length - 1;
|
|
3230
3428
|
const prefix = isLast ? " └─ " : " ├─ ";
|
|
3231
3429
|
const idSuffix = chainConfig.parachainId != null ? ` ${DIM}[${chainConfig.parachainId}]${RESET}` : "";
|
|
3232
|
-
printChainLine(prefix, name, chainConfig,
|
|
3430
|
+
printChainLine(prefix, name, chainConfig, idSuffix);
|
|
3233
3431
|
}
|
|
3234
3432
|
console.log();
|
|
3235
3433
|
}
|
|
3236
3434
|
for (const [name, chainConfig] of standalone) {
|
|
3237
|
-
printChainLine(" ", name, chainConfig
|
|
3435
|
+
printChainLine(" ", name, chainConfig);
|
|
3238
3436
|
}
|
|
3239
3437
|
if (standalone.length > 0)
|
|
3240
3438
|
console.log();
|
|
3241
3439
|
}
|
|
3242
|
-
function printChainLine(prefix, name, chainConfig,
|
|
3243
|
-
const isDefault = name === defaultChain;
|
|
3244
|
-
const marker = isDefault ? ` ${BOLD}(default)${RESET}` : "";
|
|
3440
|
+
function printChainLine(prefix, name, chainConfig, suffix = "") {
|
|
3245
3441
|
const rpcs = Array.isArray(chainConfig.rpc) ? chainConfig.rpc : [chainConfig.rpc];
|
|
3246
|
-
console.log(`${prefix}${CYAN}${name}${RESET}${suffix}
|
|
3442
|
+
console.log(`${prefix}${CYAN}${name}${RESET}${suffix} ${DIM}${rpcs[0]}${RESET}`);
|
|
3247
3443
|
const indent = prefix.replace(/[^\s]/g, " ");
|
|
3248
3444
|
for (let i = 1;i < rpcs.length; i++) {
|
|
3249
3445
|
console.log(`${indent} ${DIM}${rpcs[i]}${RESET}`);
|
|
@@ -3255,6 +3451,10 @@ async function chainUpdate(name, opts) {
|
|
|
3255
3451
|
await chainUpdateAll(config);
|
|
3256
3452
|
return;
|
|
3257
3453
|
}
|
|
3454
|
+
if (!name) {
|
|
3455
|
+
console.error("Usage: dot chain update <name> | --all");
|
|
3456
|
+
process.exit(1);
|
|
3457
|
+
}
|
|
3258
3458
|
const { name: chainName, chain: chainConfig } = resolveChain(config, name);
|
|
3259
3459
|
process.stderr.write(`Connecting to ${chainName}...
|
|
3260
3460
|
`);
|
|
@@ -3302,23 +3502,132 @@ ${failed} of ${chainNames.length} chains failed to update.`);
|
|
|
3302
3502
|
process.exit(1);
|
|
3303
3503
|
}
|
|
3304
3504
|
}
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3505
|
+
function isBuiltinModified(name, config) {
|
|
3506
|
+
const defaultRpc = DEFAULT_CONFIG.chains[name]?.rpc;
|
|
3507
|
+
const currentRpc = config.chains[name]?.rpc;
|
|
3508
|
+
if (!defaultRpc || !currentRpc)
|
|
3509
|
+
return false;
|
|
3510
|
+
return JSON.stringify(currentRpc) !== JSON.stringify(defaultRpc);
|
|
3511
|
+
}
|
|
3512
|
+
async function readStdin2() {
|
|
3513
|
+
const chunks = [];
|
|
3514
|
+
for await (const chunk of process.stdin) {
|
|
3515
|
+
chunks.push(chunk);
|
|
3309
3516
|
}
|
|
3517
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
3518
|
+
}
|
|
3519
|
+
async function chainExport(names, opts) {
|
|
3310
3520
|
const config = await loadConfig();
|
|
3311
|
-
const
|
|
3312
|
-
if (
|
|
3313
|
-
const
|
|
3314
|
-
|
|
3521
|
+
const exportChains = {};
|
|
3522
|
+
if (names.length > 0) {
|
|
3523
|
+
for (const input of names) {
|
|
3524
|
+
const resolved = findChainName(config, input);
|
|
3525
|
+
if (!resolved) {
|
|
3526
|
+
throw new Error(`Chain "${input}" not found.`);
|
|
3527
|
+
}
|
|
3528
|
+
exportChains[resolved] = config.chains[resolved];
|
|
3529
|
+
}
|
|
3530
|
+
} else if (opts.all) {
|
|
3531
|
+
Object.assign(exportChains, config.chains);
|
|
3532
|
+
} else {
|
|
3533
|
+
for (const [name, chainConfig] of Object.entries(config.chains)) {
|
|
3534
|
+
if (!BUILTIN_CHAIN_NAMES.has(name) || isBuiltinModified(name, config)) {
|
|
3535
|
+
exportChains[name] = chainConfig;
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3315
3538
|
}
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3539
|
+
const exportData = {
|
|
3540
|
+
chains: exportChains
|
|
3541
|
+
};
|
|
3542
|
+
const json = `${JSON.stringify(exportData, null, 2)}
|
|
3543
|
+
`;
|
|
3544
|
+
if (opts.file) {
|
|
3545
|
+
await writeFile4(opts.file, json);
|
|
3546
|
+
if (isJsonOutput(opts)) {
|
|
3547
|
+
console.log(formatJson({
|
|
3548
|
+
action: "exported",
|
|
3549
|
+
file: opts.file,
|
|
3550
|
+
count: Object.keys(exportChains).length
|
|
3551
|
+
}));
|
|
3552
|
+
} else {
|
|
3553
|
+
console.log(`Exported ${Object.keys(exportChains).length} chain(s) to ${opts.file}`);
|
|
3554
|
+
}
|
|
3320
3555
|
} else {
|
|
3321
|
-
|
|
3556
|
+
process.stdout.write(json);
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
async function chainImport(filePath, opts) {
|
|
3560
|
+
const inputPath = filePath ?? opts.file;
|
|
3561
|
+
let raw;
|
|
3562
|
+
if (inputPath && inputPath !== "-") {
|
|
3563
|
+
raw = await readFile4(inputPath, "utf-8");
|
|
3564
|
+
} else {
|
|
3565
|
+
raw = await readStdin2();
|
|
3566
|
+
}
|
|
3567
|
+
let importData;
|
|
3568
|
+
try {
|
|
3569
|
+
importData = JSON.parse(raw);
|
|
3570
|
+
} catch {
|
|
3571
|
+
throw new Error("Invalid JSON input.");
|
|
3572
|
+
}
|
|
3573
|
+
if (!importData.chains || typeof importData.chains !== "object") {
|
|
3574
|
+
throw new Error('Invalid import format: missing "chains" object.');
|
|
3575
|
+
}
|
|
3576
|
+
const config = await loadConfig();
|
|
3577
|
+
const added = [];
|
|
3578
|
+
const skipped = [];
|
|
3579
|
+
const overwritten = [];
|
|
3580
|
+
const warnings = [];
|
|
3581
|
+
for (const [name, chainConfig] of Object.entries(importData.chains)) {
|
|
3582
|
+
const existing = findChainName(config, name);
|
|
3583
|
+
if (existing && !opts.overwrite) {
|
|
3584
|
+
skipped.push(existing);
|
|
3585
|
+
process.stderr.write(`${YELLOW}Skipped "${existing}": already exists (use --overwrite to replace)${RESET}
|
|
3586
|
+
`);
|
|
3587
|
+
continue;
|
|
3588
|
+
}
|
|
3589
|
+
if (chainConfig.relay) {
|
|
3590
|
+
const relayInImport = Object.keys(importData.chains).some((n) => n.toLowerCase() === chainConfig.relay.toLowerCase());
|
|
3591
|
+
const relayInConfig = findChainName(config, chainConfig.relay);
|
|
3592
|
+
if (!relayInImport && !relayInConfig) {
|
|
3593
|
+
warnings.push(`Chain "${name}" references relay "${chainConfig.relay}" which does not exist.`);
|
|
3594
|
+
process.stderr.write(`${YELLOW}Warning: "${name}" references relay "${chainConfig.relay}" which does not exist.${RESET}
|
|
3595
|
+
`);
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
if (existing) {
|
|
3599
|
+
overwritten.push(existing);
|
|
3600
|
+
config.chains[existing] = chainConfig;
|
|
3601
|
+
} else {
|
|
3602
|
+
added.push(name);
|
|
3603
|
+
config.chains[name] = chainConfig;
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
if (!opts.dryRun) {
|
|
3607
|
+
await saveConfig(config);
|
|
3608
|
+
}
|
|
3609
|
+
if (isJsonOutput(opts)) {
|
|
3610
|
+
console.log(formatJson({
|
|
3611
|
+
action: opts.dryRun ? "dry-run" : "imported",
|
|
3612
|
+
added,
|
|
3613
|
+
overwritten,
|
|
3614
|
+
skipped,
|
|
3615
|
+
warnings
|
|
3616
|
+
}));
|
|
3617
|
+
return;
|
|
3618
|
+
}
|
|
3619
|
+
const prefix = opts.dryRun ? "(dry run) " : "";
|
|
3620
|
+
if (added.length > 0)
|
|
3621
|
+
console.log(`${prefix}Added: ${added.join(", ")}`);
|
|
3622
|
+
if (overwritten.length > 0)
|
|
3623
|
+
console.log(`${prefix}Overwritten: ${overwritten.join(", ")}`);
|
|
3624
|
+
if (skipped.length > 0)
|
|
3625
|
+
console.log(`${prefix}Skipped: ${skipped.join(", ")}`);
|
|
3626
|
+
if (added.length === 0 && overwritten.length === 0) {
|
|
3627
|
+
console.log(`${prefix}No chains imported.`);
|
|
3628
|
+
} else if (!opts.dryRun) {
|
|
3629
|
+
console.error(`
|
|
3630
|
+
Run "dot chain update --all" to fetch metadata for imported chains.`);
|
|
3322
3631
|
}
|
|
3323
3632
|
}
|
|
3324
3633
|
|
|
@@ -3506,12 +3815,23 @@ init_client();
|
|
|
3506
3815
|
init_metadata();
|
|
3507
3816
|
init_output();
|
|
3508
3817
|
async function loadMeta2(chainName, chainConfig, rpcOverride) {
|
|
3818
|
+
if (rpcOverride) {
|
|
3819
|
+
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
3820
|
+
`);
|
|
3821
|
+
const clientHandle = await createChainClient(chainName, chainConfig, rpcOverride);
|
|
3822
|
+
try {
|
|
3823
|
+
const raw = await fetchMetadataFromChain(clientHandle, chainName);
|
|
3824
|
+
return parseMetadata(raw);
|
|
3825
|
+
} finally {
|
|
3826
|
+
clientHandle.destroy();
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3509
3829
|
try {
|
|
3510
3830
|
return await getOrFetchMetadata(chainName);
|
|
3511
3831
|
} catch {
|
|
3512
3832
|
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
3513
3833
|
`);
|
|
3514
|
-
const clientHandle = await createChainClient(chainName, chainConfig
|
|
3834
|
+
const clientHandle = await createChainClient(chainName, chainConfig);
|
|
3515
3835
|
try {
|
|
3516
3836
|
return await getOrFetchMetadata(chainName, clientHandle);
|
|
3517
3837
|
} finally {
|
|
@@ -4054,7 +4374,7 @@ async function showItemHelp2(category, target, opts) {
|
|
|
4054
4374
|
init_hash();
|
|
4055
4375
|
init_output();
|
|
4056
4376
|
init_errors();
|
|
4057
|
-
import { readFile as
|
|
4377
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
4058
4378
|
async function resolveInput(data, opts) {
|
|
4059
4379
|
const sources = [data !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
|
|
4060
4380
|
if (sources > 1) {
|
|
@@ -4064,7 +4384,7 @@ async function resolveInput(data, opts) {
|
|
|
4064
4384
|
throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
|
|
4065
4385
|
}
|
|
4066
4386
|
if (opts.file) {
|
|
4067
|
-
const buf = await
|
|
4387
|
+
const buf = await readFile5(opts.file);
|
|
4068
4388
|
return new Uint8Array(buf);
|
|
4069
4389
|
}
|
|
4070
4390
|
if (opts.stdin) {
|
|
@@ -4190,17 +4510,29 @@ function registerInspectCommand(cli) {
|
|
|
4190
4510
|
}
|
|
4191
4511
|
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
4192
4512
|
let meta;
|
|
4193
|
-
|
|
4194
|
-
meta = await getOrFetchMetadata(chainName);
|
|
4195
|
-
} catch {
|
|
4513
|
+
if (opts.rpc) {
|
|
4196
4514
|
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
4197
4515
|
`);
|
|
4198
4516
|
const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
4199
4517
|
try {
|
|
4200
|
-
|
|
4518
|
+
const raw = await fetchMetadataFromChain(clientHandle, chainName);
|
|
4519
|
+
meta = parseMetadata(raw);
|
|
4201
4520
|
} finally {
|
|
4202
4521
|
clientHandle.destroy();
|
|
4203
4522
|
}
|
|
4523
|
+
} else {
|
|
4524
|
+
try {
|
|
4525
|
+
meta = await getOrFetchMetadata(chainName);
|
|
4526
|
+
} catch {
|
|
4527
|
+
process.stderr.write(`Fetching metadata from ${chainName}...
|
|
4528
|
+
`);
|
|
4529
|
+
const clientHandle = await createChainClient(chainName, chainConfig);
|
|
4530
|
+
try {
|
|
4531
|
+
meta = await getOrFetchMetadata(chainName, clientHandle);
|
|
4532
|
+
} finally {
|
|
4533
|
+
clientHandle.destroy();
|
|
4534
|
+
}
|
|
4535
|
+
}
|
|
4204
4536
|
}
|
|
4205
4537
|
if (!target) {
|
|
4206
4538
|
const pallets = listPallets(meta);
|
|
@@ -4754,7 +5086,7 @@ init_accounts();
|
|
|
4754
5086
|
init_hash();
|
|
4755
5087
|
init_output();
|
|
4756
5088
|
init_errors();
|
|
4757
|
-
import { readFile as
|
|
5089
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
4758
5090
|
var SUPPORTED_TYPES = ["sr25519"];
|
|
4759
5091
|
function isSupportedType(type) {
|
|
4760
5092
|
return SUPPORTED_TYPES.includes(type.toLowerCase());
|
|
@@ -4774,7 +5106,7 @@ async function resolveInput2(data, opts) {
|
|
|
4774
5106
|
throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
|
|
4775
5107
|
}
|
|
4776
5108
|
if (opts.file) {
|
|
4777
|
-
const buf = await
|
|
5109
|
+
const buf = await readFile6(opts.file);
|
|
4778
5110
|
return new Uint8Array(buf);
|
|
4779
5111
|
}
|
|
4780
5112
|
if (opts.stdin) {
|
|
@@ -4854,6 +5186,7 @@ init_resolve_address();
|
|
|
4854
5186
|
init_binary_display();
|
|
4855
5187
|
init_errors();
|
|
4856
5188
|
init_focused_inspect();
|
|
5189
|
+
import { compact as scaleCompact2 } from "@polkadot-api/substrate-bindings";
|
|
4857
5190
|
import { getViewBuilder as getViewBuilder2 } from "@polkadot-api/view-builder";
|
|
4858
5191
|
import { Binary as Binary3 } from "polkadot-api";
|
|
4859
5192
|
import { stringify as stringifyYaml2 } from "yaml";
|
|
@@ -4906,6 +5239,20 @@ function parseMortalityOption(raw) {
|
|
|
4906
5239
|
}
|
|
4907
5240
|
return { mortal: true, period: n };
|
|
4908
5241
|
}
|
|
5242
|
+
function parseAssetOption(raw) {
|
|
5243
|
+
if (raw === undefined)
|
|
5244
|
+
return;
|
|
5245
|
+
try {
|
|
5246
|
+
const parsed = JSON.parse(raw);
|
|
5247
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
5248
|
+
throw new Error("must be a JSON object");
|
|
5249
|
+
}
|
|
5250
|
+
return parsed;
|
|
5251
|
+
} catch (err) {
|
|
5252
|
+
throw new CliError(`Invalid --asset value: ${err.message}
|
|
5253
|
+
` + `Expected an XCM location, e.g. '{"parents":0,"interior":{"type":"X2","value":[{"type":"PalletInstance","value":50},{"type":"GeneralIndex","value":"3"}]}}'`);
|
|
5254
|
+
}
|
|
5255
|
+
}
|
|
4909
5256
|
function parseAtOption(raw) {
|
|
4910
5257
|
if (raw === undefined)
|
|
4911
5258
|
return;
|
|
@@ -4977,9 +5324,9 @@ async function handleTx(target, args, opts) {
|
|
|
4977
5324
|
console.log();
|
|
4978
5325
|
return;
|
|
4979
5326
|
}
|
|
4980
|
-
if (!opts.from && !opts.encode && !opts.toYaml && !opts.toJson) {
|
|
5327
|
+
if (!opts.from && !opts.unsigned && !opts.encode && !opts.toYaml && !opts.toJson) {
|
|
4981
5328
|
if (isRawCall) {
|
|
4982
|
-
throw new Error("--from is required (or use --encode
|
|
5329
|
+
throw new Error("--from is required (or use --unsigned for bare tx, --encode for hex without signing)");
|
|
4983
5330
|
}
|
|
4984
5331
|
await showItemHelp("tx", target, opts);
|
|
4985
5332
|
return;
|
|
@@ -4999,6 +5346,18 @@ async function handleTx(target, args, opts) {
|
|
|
4999
5346
|
if (opts.toYaml && opts.toJson) {
|
|
5000
5347
|
throw new Error("--to-yaml and --to-json are mutually exclusive");
|
|
5001
5348
|
}
|
|
5349
|
+
if (opts.unsigned && opts.from) {
|
|
5350
|
+
throw new Error("--unsigned and --from are mutually exclusive");
|
|
5351
|
+
}
|
|
5352
|
+
if (opts.unsigned && opts.nonce) {
|
|
5353
|
+
throw new Error("--unsigned does not support --nonce");
|
|
5354
|
+
}
|
|
5355
|
+
if (opts.unsigned && opts.tip) {
|
|
5356
|
+
throw new Error("--unsigned does not support --tip");
|
|
5357
|
+
}
|
|
5358
|
+
if (opts.unsigned && opts.mortality) {
|
|
5359
|
+
throw new Error("--unsigned does not support --mortality");
|
|
5360
|
+
}
|
|
5002
5361
|
const config = await loadConfig();
|
|
5003
5362
|
const effectiveChain = opts.chain;
|
|
5004
5363
|
let pallet;
|
|
@@ -5010,9 +5369,9 @@ async function handleTx(target, args, opts) {
|
|
|
5010
5369
|
}
|
|
5011
5370
|
const { name: chainName, chain: chainConfig } = resolveChain(config, effectiveChain);
|
|
5012
5371
|
const decodeOnly = opts.encode || opts.toYaml || opts.toJson;
|
|
5013
|
-
const signer = decodeOnly ? undefined : await resolveAccountSigner(opts.from);
|
|
5372
|
+
const signer = decodeOnly || opts.unsigned ? undefined : await resolveAccountSigner(opts.from);
|
|
5014
5373
|
let clientHandle;
|
|
5015
|
-
if (!decodeOnly) {
|
|
5374
|
+
if (!decodeOnly || opts.unsigned) {
|
|
5016
5375
|
clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
|
|
5017
5376
|
}
|
|
5018
5377
|
try {
|
|
@@ -5031,11 +5390,18 @@ async function handleTx(target, args, opts) {
|
|
|
5031
5390
|
let txOptions;
|
|
5032
5391
|
const nonce = parseNonceOption(opts.nonce);
|
|
5033
5392
|
const tip = parseTipOption(opts.tip);
|
|
5393
|
+
const asset = parseAssetOption(opts.asset);
|
|
5034
5394
|
const mortality = parseMortalityOption(opts.mortality);
|
|
5035
5395
|
const at = parseAtOption(opts.at);
|
|
5036
|
-
if (!decodeOnly) {
|
|
5396
|
+
if (!decodeOnly || opts.unsigned) {
|
|
5037
5397
|
const userExtOverrides = parseExtOption(opts.ext);
|
|
5038
|
-
const
|
|
5398
|
+
const skipBuiltins = asset !== undefined ? new Set([...PAPI_BUILTIN_EXTENSIONS2].filter((e) => e !== "ChargeAssetTxPayment")) : PAPI_BUILTIN_EXTENSIONS2;
|
|
5399
|
+
if (asset !== undefined) {
|
|
5400
|
+
userExtOverrides.ChargeAssetTxPayment ??= {
|
|
5401
|
+
value: { tip: tip ?? 0n, asset_id: asset }
|
|
5402
|
+
};
|
|
5403
|
+
}
|
|
5404
|
+
const customSignedExtensions = buildCustomSignedExtensions(meta, userExtOverrides, skipBuiltins);
|
|
5039
5405
|
const built = {};
|
|
5040
5406
|
if (Object.keys(customSignedExtensions).length > 0)
|
|
5041
5407
|
built.customSignedExtensions = customSignedExtensions;
|
|
@@ -5078,7 +5444,7 @@ async function handleTx(target, args, opts) {
|
|
|
5078
5444
|
}
|
|
5079
5445
|
const effectiveArgs = opts.parsedArgs !== undefined ? fileArgsToStrings(opts.parsedArgs) : args;
|
|
5080
5446
|
const callData = await parseCallArgs(meta, palletInfo.name, callInfo.name, effectiveArgs);
|
|
5081
|
-
if (opts.encode || opts.toYaml || opts.toJson) {
|
|
5447
|
+
if (opts.encode && !opts.unsigned || opts.toYaml || opts.toJson) {
|
|
5082
5448
|
const { codec, location } = meta.builder.buildCall(palletInfo.name, callInfo.name);
|
|
5083
5449
|
const encodedArgs = codec.enc(callData);
|
|
5084
5450
|
const fullCall = new Uint8Array([location[0], location[1], ...encodedArgs]);
|
|
@@ -5100,6 +5466,24 @@ async function handleTx(target, args, opts) {
|
|
|
5100
5466
|
callHex = Binary3.toHex(encodedCall);
|
|
5101
5467
|
}
|
|
5102
5468
|
const decodedStr = decodeCall(meta, callHex);
|
|
5469
|
+
if (opts.dryRun && opts.unsigned) {
|
|
5470
|
+
if (isJsonOutput(opts)) {
|
|
5471
|
+
console.log(formatJson({
|
|
5472
|
+
chain: chainName,
|
|
5473
|
+
unsigned: true,
|
|
5474
|
+
callHex,
|
|
5475
|
+
decoded: decodedStr,
|
|
5476
|
+
estimatedFees: null
|
|
5477
|
+
}));
|
|
5478
|
+
return;
|
|
5479
|
+
}
|
|
5480
|
+
console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
|
|
5481
|
+
console.log(` ${BOLD}Type:${RESET} unsigned (bare)`);
|
|
5482
|
+
console.log(` ${BOLD}Call:${RESET} ${callHex}`);
|
|
5483
|
+
console.log(` ${BOLD}Decode:${RESET} ${decodedStr}`);
|
|
5484
|
+
console.log(` ${BOLD}Fees:${RESET} ${DIM}N/A (unsigned transaction)${RESET}`);
|
|
5485
|
+
return;
|
|
5486
|
+
}
|
|
5103
5487
|
if (opts.dryRun) {
|
|
5104
5488
|
const signerAddress = toSs58(signer.publicKey);
|
|
5105
5489
|
let estimatedFees;
|
|
@@ -5120,6 +5504,8 @@ async function handleTx(target, args, opts) {
|
|
|
5120
5504
|
result2.nonce = nonce;
|
|
5121
5505
|
if (tip !== undefined)
|
|
5122
5506
|
result2.tip = String(tip);
|
|
5507
|
+
if (asset !== undefined)
|
|
5508
|
+
result2.asset = asset;
|
|
5123
5509
|
if (mortality !== undefined)
|
|
5124
5510
|
result2.mortality = mortality.mortal ? `mortal (period ${mortality.period})` : "immortal";
|
|
5125
5511
|
if (at !== undefined)
|
|
@@ -5135,6 +5521,8 @@ async function handleTx(target, args, opts) {
|
|
|
5135
5521
|
console.log(` ${BOLD}Nonce:${RESET} ${nonce}`);
|
|
5136
5522
|
if (tip !== undefined)
|
|
5137
5523
|
console.log(` ${BOLD}Tip:${RESET} ${tip}`);
|
|
5524
|
+
if (asset !== undefined)
|
|
5525
|
+
console.log(` ${BOLD}Asset:${RESET} ${JSON.stringify(asset)}`);
|
|
5138
5526
|
if (mortality !== undefined)
|
|
5139
5527
|
console.log(` ${BOLD}Mortality:${RESET} ${mortality.mortal ? `mortal (period ${mortality.period})` : "immortal"}`);
|
|
5140
5528
|
if (at !== undefined)
|
|
@@ -5147,6 +5535,101 @@ async function handleTx(target, args, opts) {
|
|
|
5147
5535
|
return;
|
|
5148
5536
|
}
|
|
5149
5537
|
const waitLevel = parseWaitLevel(opts.wait);
|
|
5538
|
+
if (opts.unsigned) {
|
|
5539
|
+
const callDataBytes = await tx.getEncodedData();
|
|
5540
|
+
const userExtOverrides = parseExtOption(opts.ext);
|
|
5541
|
+
const generalTx = buildGeneralTx(meta, callDataBytes, userExtOverrides);
|
|
5542
|
+
if (opts.encode) {
|
|
5543
|
+
const hex = Binary3.toHex(generalTx);
|
|
5544
|
+
if (isJsonOutput(opts)) {
|
|
5545
|
+
console.log(formatJson({ generalTxHex: hex }));
|
|
5546
|
+
} else {
|
|
5547
|
+
console.log(hex);
|
|
5548
|
+
}
|
|
5549
|
+
return;
|
|
5550
|
+
}
|
|
5551
|
+
const observable = clientHandle.client.submitAndWatch(generalTx, at);
|
|
5552
|
+
if (isJsonOutput(opts)) {
|
|
5553
|
+
const result3 = await watchTransactionJson(observable, waitLevel, { unsigned: true });
|
|
5554
|
+
const rpcUrl3 = primaryRpc(opts.rpc ?? chainConfig.rpc);
|
|
5555
|
+
if (result3.type === "broadcasted") {
|
|
5556
|
+
printJsonLine({ event: "broadcasted", txHash: result3.txHash });
|
|
5557
|
+
return;
|
|
5558
|
+
}
|
|
5559
|
+
const blockHash = result3.block.hash;
|
|
5560
|
+
const explorer = {};
|
|
5561
|
+
if (rpcUrl3) {
|
|
5562
|
+
explorer.polkadotjs = pjsAppsLink(rpcUrl3, blockHash);
|
|
5563
|
+
explorer.papi = papiLink(rpcUrl3, blockHash);
|
|
5564
|
+
}
|
|
5565
|
+
printJsonLine({
|
|
5566
|
+
event: result3.type === "finalized" ? "finalized" : "bestBlock",
|
|
5567
|
+
unsigned: true,
|
|
5568
|
+
blockNumber: result3.block.number,
|
|
5569
|
+
blockHash,
|
|
5570
|
+
txHash: result3.txHash,
|
|
5571
|
+
ok: result3.ok,
|
|
5572
|
+
events: result3.events?.map((e) => ({
|
|
5573
|
+
pallet: e.type,
|
|
5574
|
+
name: e.value?.type,
|
|
5575
|
+
fields: e.value?.value
|
|
5576
|
+
})),
|
|
5577
|
+
dispatchError: result3.ok ? null : formatDispatchError(result3.dispatchError),
|
|
5578
|
+
explorer
|
|
5579
|
+
});
|
|
5580
|
+
if (!result3.ok) {
|
|
5581
|
+
throw new CliError(`Transaction dispatch error: ${formatDispatchError(result3.dispatchError)}`);
|
|
5582
|
+
}
|
|
5583
|
+
return;
|
|
5584
|
+
}
|
|
5585
|
+
const result2 = await watchTransaction(observable, waitLevel, { unsigned: true });
|
|
5586
|
+
console.log();
|
|
5587
|
+
console.log(` ${BOLD}Chain:${RESET} ${chainName}`);
|
|
5588
|
+
console.log(` ${BOLD}Type:${RESET} unsigned (bare)`);
|
|
5589
|
+
console.log(` ${BOLD}Call:${RESET} ${callHex}`);
|
|
5590
|
+
console.log(` ${BOLD}Decode:${RESET} ${decodedStr}`);
|
|
5591
|
+
console.log(` ${BOLD}Tx:${RESET} ${result2.txHash}`);
|
|
5592
|
+
if (result2.type === "broadcasted") {
|
|
5593
|
+
console.log(` ${BOLD}Status:${RESET} ${GREEN}broadcasted${RESET}`);
|
|
5594
|
+
console.log(` ${DIM}Note: tx was broadcast but not yet included in a block${RESET}`);
|
|
5595
|
+
console.log();
|
|
5596
|
+
return;
|
|
5597
|
+
}
|
|
5598
|
+
let dispatchErrorMsg2;
|
|
5599
|
+
if (result2.ok) {
|
|
5600
|
+
const hint = result2.type === "txBestBlocksState" ? ` ${DIM}(best block, not yet finalized)${RESET}` : "";
|
|
5601
|
+
console.log(` ${BOLD}Status:${RESET} ${GREEN}ok${RESET}${hint}`);
|
|
5602
|
+
} else {
|
|
5603
|
+
dispatchErrorMsg2 = formatDispatchError(result2.dispatchError);
|
|
5604
|
+
console.log(` ${BOLD}Status:${RESET} ${RED}dispatch error${RESET}`);
|
|
5605
|
+
console.log(` ${BOLD}Error:${RESET} ${dispatchErrorMsg2}`);
|
|
5606
|
+
}
|
|
5607
|
+
if (result2.events && result2.events.length > 0) {
|
|
5608
|
+
console.log(` ${BOLD}Events:${RESET}`);
|
|
5609
|
+
for (const event of result2.events) {
|
|
5610
|
+
const name = `${CYAN}${event.type}${RESET}.${CYAN}${event.value?.type ?? ""}${RESET}`;
|
|
5611
|
+
const payload = event.value?.value;
|
|
5612
|
+
if (payload && typeof payload === "object") {
|
|
5613
|
+
const fields = Object.entries(payload).map(([k, v]) => `${k}: ${formatEventValue(v)}`).join(", ");
|
|
5614
|
+
console.log(` ${name} { ${fields} }`);
|
|
5615
|
+
} else {
|
|
5616
|
+
console.log(` ${name}`);
|
|
5617
|
+
}
|
|
5618
|
+
}
|
|
5619
|
+
}
|
|
5620
|
+
const rpcUrl2 = primaryRpc(opts.rpc ?? chainConfig.rpc);
|
|
5621
|
+
if (rpcUrl2) {
|
|
5622
|
+
const blockHash = result2.block.hash;
|
|
5623
|
+
console.log(` ${BOLD}Block:${RESET} #${result2.block.number} (${blockHash})`);
|
|
5624
|
+
console.log(` ${DIM}${pjsAppsLink(rpcUrl2, blockHash)}${RESET}`);
|
|
5625
|
+
console.log(` ${DIM}${papiLink(rpcUrl2, blockHash)}${RESET}`);
|
|
5626
|
+
}
|
|
5627
|
+
console.log();
|
|
5628
|
+
if (!result2.ok) {
|
|
5629
|
+
throw new CliError(`Transaction dispatch error: ${dispatchErrorMsg2}`);
|
|
5630
|
+
}
|
|
5631
|
+
return;
|
|
5632
|
+
}
|
|
5150
5633
|
if (isJsonOutput(opts)) {
|
|
5151
5634
|
const result2 = await watchTransactionJson(tx.signSubmitAndWatch(signer, txOptions), waitLevel);
|
|
5152
5635
|
const rpcUrl2 = primaryRpc(opts.rpc ?? chainConfig.rpc);
|
|
@@ -5188,6 +5671,8 @@ async function handleTx(target, args, opts) {
|
|
|
5188
5671
|
console.log(` ${BOLD}Nonce:${RESET} ${nonce}`);
|
|
5189
5672
|
if (tip !== undefined)
|
|
5190
5673
|
console.log(` ${BOLD}Tip:${RESET} ${tip}`);
|
|
5674
|
+
if (asset !== undefined)
|
|
5675
|
+
console.log(` ${BOLD}Asset:${RESET} ${JSON.stringify(asset)}`);
|
|
5191
5676
|
if (mortality !== undefined)
|
|
5192
5677
|
console.log(` ${BOLD}Mortality:${RESET} ${mortality.mortal ? `mortal (period ${mortality.period})` : "immortal"}`);
|
|
5193
5678
|
if (at !== undefined)
|
|
@@ -5778,7 +6263,10 @@ async function parseTypedArg2(meta, entry, arg) {
|
|
|
5778
6263
|
case "array": {
|
|
5779
6264
|
const inner = entry.value;
|
|
5780
6265
|
if (inner.type === "primitive" && inner.value === "u8") {
|
|
5781
|
-
|
|
6266
|
+
const isHex = /^0x[0-9a-fA-F]*$/.test(arg);
|
|
6267
|
+
if (entry.type === "array" && isHex)
|
|
6268
|
+
return arg;
|
|
6269
|
+
if (isHex)
|
|
5782
6270
|
return Binary3.fromHex(arg);
|
|
5783
6271
|
return Binary3.fromText(arg);
|
|
5784
6272
|
}
|
|
@@ -5869,11 +6357,11 @@ function parseExtOption(ext) {
|
|
|
5869
6357
|
}
|
|
5870
6358
|
}
|
|
5871
6359
|
var NO_DEFAULT2 = Symbol("no-default");
|
|
5872
|
-
function buildCustomSignedExtensions(meta, userOverrides) {
|
|
6360
|
+
function buildCustomSignedExtensions(meta, userOverrides, builtins = PAPI_BUILTIN_EXTENSIONS2) {
|
|
5873
6361
|
const result = {};
|
|
5874
6362
|
const extensions = getSignedExtensions(meta);
|
|
5875
6363
|
for (const ext of extensions) {
|
|
5876
|
-
if (
|
|
6364
|
+
if (builtins.has(ext.identifier))
|
|
5877
6365
|
continue;
|
|
5878
6366
|
if (ext.identifier in userOverrides) {
|
|
5879
6367
|
result[ext.identifier] = userOverrides[ext.identifier];
|
|
@@ -5905,20 +6393,114 @@ function autoDefaultForType(entry) {
|
|
|
5905
6393
|
}
|
|
5906
6394
|
return NO_DEFAULT2;
|
|
5907
6395
|
}
|
|
5908
|
-
function
|
|
6396
|
+
function unsignedDefaultForType(identifier, entry) {
|
|
6397
|
+
switch (identifier) {
|
|
6398
|
+
case "CheckMortality":
|
|
6399
|
+
return { type: "Immortal" };
|
|
6400
|
+
case "CheckNonce":
|
|
6401
|
+
return 0;
|
|
6402
|
+
case "ChargeTransactionPayment":
|
|
6403
|
+
return 0n;
|
|
6404
|
+
case "ChargeAssetTxPayment":
|
|
6405
|
+
return { tip: 0n, asset_id: undefined };
|
|
6406
|
+
}
|
|
6407
|
+
const auto = autoDefaultForType(entry);
|
|
6408
|
+
if (auto !== NO_DEFAULT2)
|
|
6409
|
+
return auto;
|
|
6410
|
+
if (entry.type === "primitive") {
|
|
6411
|
+
switch (entry.value) {
|
|
6412
|
+
case "bool":
|
|
6413
|
+
return false;
|
|
6414
|
+
case "u8":
|
|
6415
|
+
case "u16":
|
|
6416
|
+
case "u32":
|
|
6417
|
+
case "i8":
|
|
6418
|
+
case "i16":
|
|
6419
|
+
case "i32":
|
|
6420
|
+
return 0;
|
|
6421
|
+
case "u64":
|
|
6422
|
+
case "u128":
|
|
6423
|
+
case "u256":
|
|
6424
|
+
case "i64":
|
|
6425
|
+
case "i128":
|
|
6426
|
+
case "i256":
|
|
6427
|
+
return 0n;
|
|
6428
|
+
case "str":
|
|
6429
|
+
case "char":
|
|
6430
|
+
return "";
|
|
6431
|
+
}
|
|
6432
|
+
}
|
|
6433
|
+
if (entry.type === "compact")
|
|
6434
|
+
return 0;
|
|
6435
|
+
if (entry.type === "enum") {
|
|
6436
|
+
const variants = entry.value;
|
|
6437
|
+
if ("Immortal" in variants)
|
|
6438
|
+
return { type: "Immortal" };
|
|
6439
|
+
for (const [name, variant] of Object.entries(variants)) {
|
|
6440
|
+
if (variant.type === "void")
|
|
6441
|
+
return { type: name };
|
|
6442
|
+
}
|
|
6443
|
+
}
|
|
6444
|
+
return NO_DEFAULT2;
|
|
6445
|
+
}
|
|
6446
|
+
function buildGeneralTx(meta, callData, userExtOverrides) {
|
|
6447
|
+
const extensions = getSignedExtensions(meta);
|
|
6448
|
+
const extBytes = [];
|
|
6449
|
+
for (const ext of extensions) {
|
|
6450
|
+
const valueEntry = meta.lookup(ext.type);
|
|
6451
|
+
if (valueEntry.type === "void")
|
|
6452
|
+
continue;
|
|
6453
|
+
let value;
|
|
6454
|
+
if (ext.identifier in userExtOverrides) {
|
|
6455
|
+
const override = userExtOverrides[ext.identifier];
|
|
6456
|
+
value = override.value !== undefined ? override.value : override;
|
|
6457
|
+
} else {
|
|
6458
|
+
value = unsignedDefaultForType(ext.identifier, valueEntry);
|
|
6459
|
+
if (value === NO_DEFAULT2) {
|
|
6460
|
+
throw new CliError(`Cannot determine default unsigned value for extension "${ext.identifier}" ` + `(type: ${valueEntry.type}). Provide it via --ext '{"${ext.identifier}":{"value":...}}'`);
|
|
6461
|
+
}
|
|
6462
|
+
}
|
|
6463
|
+
const codec = meta.builder.buildDefinition(ext.type);
|
|
6464
|
+
extBytes.push(codec.enc(value));
|
|
6465
|
+
}
|
|
6466
|
+
const extVersion = new Uint8Array([0]);
|
|
6467
|
+
const versionByte = new Uint8Array([69]);
|
|
6468
|
+
let payloadLen = 1 + 1;
|
|
6469
|
+
for (const b of extBytes)
|
|
6470
|
+
payloadLen += b.length;
|
|
6471
|
+
payloadLen += callData.length;
|
|
6472
|
+
const lengthPrefix = scaleCompact2.enc(payloadLen);
|
|
6473
|
+
const total = new Uint8Array(lengthPrefix.length + payloadLen);
|
|
6474
|
+
let offset = 0;
|
|
6475
|
+
total.set(lengthPrefix, offset);
|
|
6476
|
+
offset += lengthPrefix.length;
|
|
6477
|
+
total.set(versionByte, offset);
|
|
6478
|
+
offset += 1;
|
|
6479
|
+
total.set(extVersion, offset);
|
|
6480
|
+
offset += 1;
|
|
6481
|
+
for (const b of extBytes) {
|
|
6482
|
+
total.set(b, offset);
|
|
6483
|
+
offset += b.length;
|
|
6484
|
+
}
|
|
6485
|
+
total.set(callData, offset);
|
|
6486
|
+
return total;
|
|
6487
|
+
}
|
|
6488
|
+
function watchTransaction(observable, level, options) {
|
|
5909
6489
|
const spinner = new Spinner;
|
|
5910
6490
|
return new Promise((resolve, reject) => {
|
|
5911
6491
|
let settled = false;
|
|
5912
|
-
spinner.start("Signing...");
|
|
6492
|
+
spinner.start(options?.unsigned ? "Submitting..." : "Signing...");
|
|
5913
6493
|
const subscription = observable.subscribe({
|
|
5914
6494
|
next(event) {
|
|
5915
6495
|
if (settled)
|
|
5916
6496
|
return;
|
|
5917
6497
|
switch (event.type) {
|
|
5918
6498
|
case "signed":
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
6499
|
+
if (!options?.unsigned) {
|
|
6500
|
+
spinner.succeed("Signed");
|
|
6501
|
+
console.log(` ${BOLD}Tx:${RESET} ${event.txHash}`);
|
|
6502
|
+
spinner.start("Broadcasting...");
|
|
6503
|
+
}
|
|
5922
6504
|
break;
|
|
5923
6505
|
case "broadcasted":
|
|
5924
6506
|
if (level === "broadcast") {
|
|
@@ -5962,7 +6544,7 @@ function watchTransaction(observable, level) {
|
|
|
5962
6544
|
});
|
|
5963
6545
|
});
|
|
5964
6546
|
}
|
|
5965
|
-
function watchTransactionJson(observable, level) {
|
|
6547
|
+
function watchTransactionJson(observable, level, options) {
|
|
5966
6548
|
return new Promise((resolve, reject) => {
|
|
5967
6549
|
let settled = false;
|
|
5968
6550
|
const subscription = observable.subscribe({
|
|
@@ -5971,7 +6553,9 @@ function watchTransactionJson(observable, level) {
|
|
|
5971
6553
|
return;
|
|
5972
6554
|
switch (event.type) {
|
|
5973
6555
|
case "signed":
|
|
5974
|
-
|
|
6556
|
+
if (!options?.unsigned) {
|
|
6557
|
+
printJsonLine({ event: "signed", txHash: event.txHash });
|
|
6558
|
+
}
|
|
5975
6559
|
break;
|
|
5976
6560
|
case "broadcasted":
|
|
5977
6561
|
printJsonLine({ event: "broadcasted", txHash: event.txHash });
|
|
@@ -6105,8 +6689,9 @@ async function resolveMnemonic(account) {
|
|
|
6105
6689
|
}
|
|
6106
6690
|
|
|
6107
6691
|
// src/config/store.ts
|
|
6692
|
+
init_errors();
|
|
6108
6693
|
init_types();
|
|
6109
|
-
import { access as access3, mkdir as mkdir3, readFile as
|
|
6694
|
+
import { access as access3, mkdir as mkdir3, readFile as readFile7, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
|
|
6110
6695
|
import { homedir as homedir2 } from "node:os";
|
|
6111
6696
|
import { join as join3 } from "node:path";
|
|
6112
6697
|
var DOT_DIR2 = join3(homedir2(), ".polkadot");
|
|
@@ -6126,7 +6711,7 @@ async function fileExists3(path) {
|
|
|
6126
6711
|
async function loadConfig2() {
|
|
6127
6712
|
await ensureDir3(DOT_DIR2);
|
|
6128
6713
|
if (await fileExists3(CONFIG_PATH2)) {
|
|
6129
|
-
const saved = JSON.parse(await
|
|
6714
|
+
const saved = JSON.parse(await readFile7(CONFIG_PATH2, "utf-8"));
|
|
6130
6715
|
const chains = {};
|
|
6131
6716
|
for (const [name, defaultConfig] of Object.entries(DEFAULT_CONFIG.chains)) {
|
|
6132
6717
|
chains[name] = saved.chains[name] ? { ...defaultConfig, ...saved.chains[name] } : defaultConfig;
|
|
@@ -6136,20 +6721,20 @@ async function loadConfig2() {
|
|
|
6136
6721
|
chains[name] = config;
|
|
6137
6722
|
}
|
|
6138
6723
|
}
|
|
6139
|
-
return {
|
|
6724
|
+
return { chains };
|
|
6140
6725
|
}
|
|
6141
6726
|
await saveConfig2(DEFAULT_CONFIG);
|
|
6142
6727
|
return DEFAULT_CONFIG;
|
|
6143
6728
|
}
|
|
6144
6729
|
async function saveConfig2(config) {
|
|
6145
6730
|
await ensureDir3(DOT_DIR2);
|
|
6146
|
-
await
|
|
6731
|
+
await writeFile5(CONFIG_PATH2, `${JSON.stringify(config, null, 2)}
|
|
6147
6732
|
`);
|
|
6148
6733
|
}
|
|
6149
6734
|
|
|
6150
6735
|
// src/core/file-loader.ts
|
|
6151
6736
|
init_errors();
|
|
6152
|
-
import { access as access4, readFile as
|
|
6737
|
+
import { access as access4, readFile as readFile8 } from "node:fs/promises";
|
|
6153
6738
|
import { parse as parseYaml } from "yaml";
|
|
6154
6739
|
var CATEGORIES = ["tx", "query", "const", "apis"];
|
|
6155
6740
|
var FILE_EXTENSIONS = [".json", ".yaml", ".yml"];
|
|
@@ -6214,7 +6799,7 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
6214
6799
|
} catch {
|
|
6215
6800
|
throw new CliError(`File not found: ${filePath}`);
|
|
6216
6801
|
}
|
|
6217
|
-
const rawText = await
|
|
6802
|
+
const rawText = await readFile8(filePath, "utf-8");
|
|
6218
6803
|
if (!rawText.trim()) {
|
|
6219
6804
|
throw new CliError(`File is empty: ${filePath}`);
|
|
6220
6805
|
}
|
|
@@ -6247,6 +6832,7 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
6247
6832
|
}
|
|
6248
6833
|
const doc = parsed;
|
|
6249
6834
|
const chain = doc.chain != null ? String(doc.chain) : undefined;
|
|
6835
|
+
const unsigned = doc.unsigned === true ? true : undefined;
|
|
6250
6836
|
const foundCategories = CATEGORIES.filter((c) => (c in doc));
|
|
6251
6837
|
if (foundCategories.length === 0) {
|
|
6252
6838
|
throw new CliError(`File "${filePath}" must contain exactly one category key: ${CATEGORIES.join(", ")}. None found.`);
|
|
@@ -6277,14 +6863,15 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
6277
6863
|
category,
|
|
6278
6864
|
pallet,
|
|
6279
6865
|
item,
|
|
6280
|
-
args: args ?? undefined
|
|
6866
|
+
args: args ?? undefined,
|
|
6867
|
+
unsigned
|
|
6281
6868
|
};
|
|
6282
6869
|
}
|
|
6283
6870
|
|
|
6284
6871
|
// src/core/update-notifier.ts
|
|
6285
6872
|
init_store();
|
|
6286
6873
|
import { readFileSync } from "node:fs";
|
|
6287
|
-
import { mkdir as mkdir4, writeFile as
|
|
6874
|
+
import { mkdir as mkdir4, writeFile as writeFile6 } from "node:fs/promises";
|
|
6288
6875
|
import { join as join4 } from "node:path";
|
|
6289
6876
|
var CACHE_FILE = "update-check.json";
|
|
6290
6877
|
var STALE_MS = 24 * 60 * 60 * 1000;
|
|
@@ -6354,7 +6941,7 @@ function readCache() {
|
|
|
6354
6941
|
async function writeCache(cache) {
|
|
6355
6942
|
try {
|
|
6356
6943
|
await mkdir4(getConfigDir(), { recursive: true });
|
|
6357
|
-
await
|
|
6944
|
+
await writeFile6(getCachePath(), `${JSON.stringify(cache)}
|
|
6358
6945
|
`);
|
|
6359
6946
|
} catch {}
|
|
6360
6947
|
}
|
|
@@ -6522,6 +7109,7 @@ if (process.argv[2] === "__complete") {
|
|
|
6522
7109
|
console.log(" dot query.System.Account <addr> Query a storage item");
|
|
6523
7110
|
console.log(" dot query.System List storage items in System");
|
|
6524
7111
|
console.log(" dot tx.System.remark 0xdead --from alice");
|
|
7112
|
+
console.log(" dot tx.System.remark 0xdead --unsigned Submit unsigned/bare tx");
|
|
6525
7113
|
console.log(" dot const.Balances.ExistentialDeposit");
|
|
6526
7114
|
console.log(" dot events.Balances List events in Balances");
|
|
6527
7115
|
console.log(" dot apis.Core.version Call a runtime API");
|
|
@@ -6542,7 +7130,7 @@ if (process.argv[2] === "__complete") {
|
|
|
6542
7130
|
console.log(" completions <sh> Generate shell completions (zsh, bash, fish)");
|
|
6543
7131
|
console.log();
|
|
6544
7132
|
console.log("Global options:");
|
|
6545
|
-
console.log(" --chain <name> Target chain (
|
|
7133
|
+
console.log(" --chain <name> Target chain (required)");
|
|
6546
7134
|
console.log(" --rpc <url> Override RPC endpoint");
|
|
6547
7135
|
console.log(" --json Output as JSON");
|
|
6548
7136
|
console.log(" --output <format> Output format: pretty or json");
|
|
@@ -6551,7 +7139,7 @@ if (process.argv[2] === "__complete") {
|
|
|
6551
7139
|
};
|
|
6552
7140
|
startBackgroundCheck(version);
|
|
6553
7141
|
const cli = cac("dot");
|
|
6554
|
-
cli.option("--chain <name>", "Target chain (
|
|
7142
|
+
cli.option("--chain <name>", "Target chain (required)");
|
|
6555
7143
|
cli.option("--rpc <url>", "Override RPC endpoint for this call");
|
|
6556
7144
|
cli.option("--output <format>", "Output format: pretty or json", {
|
|
6557
7145
|
default: "pretty"
|
|
@@ -6565,9 +7153,9 @@ if (process.argv[2] === "__complete") {
|
|
|
6565
7153
|
registerParachainCommand(cli);
|
|
6566
7154
|
registerCompletionsCommand(cli);
|
|
6567
7155
|
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)", {
|
|
7156
|
+
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
7157
|
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) => {
|
|
7158
|
+
}).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
7159
|
if (!dotpath) {
|
|
6572
7160
|
printHelp();
|
|
6573
7161
|
return;
|
|
@@ -6588,11 +7176,13 @@ if (process.argv[2] === "__complete") {
|
|
|
6588
7176
|
await handleTx(target2, args, {
|
|
6589
7177
|
...handlerOpts2,
|
|
6590
7178
|
from: opts.from,
|
|
7179
|
+
unsigned: opts.unsigned ?? cmd.unsigned,
|
|
6591
7180
|
dryRun: opts.dryRun,
|
|
6592
7181
|
encode: opts.encode,
|
|
6593
7182
|
toYaml: opts.toYaml,
|
|
6594
7183
|
toJson: opts.toJson,
|
|
6595
7184
|
ext: opts.ext,
|
|
7185
|
+
asset: opts.asset,
|
|
6596
7186
|
wait: opts.wait,
|
|
6597
7187
|
nonce: opts.nonce,
|
|
6598
7188
|
tip: opts.tip,
|
|
@@ -6657,11 +7247,13 @@ if (process.argv[2] === "__complete") {
|
|
|
6657
7247
|
const txOpts = {
|
|
6658
7248
|
...handlerOpts,
|
|
6659
7249
|
from: opts.from,
|
|
7250
|
+
unsigned: opts.unsigned,
|
|
6660
7251
|
dryRun: opts.dryRun,
|
|
6661
7252
|
encode: opts.encode,
|
|
6662
7253
|
toYaml: opts.toYaml,
|
|
6663
7254
|
toJson: opts.toJson,
|
|
6664
7255
|
ext: opts.ext,
|
|
7256
|
+
asset: opts.asset,
|
|
6665
7257
|
wait: opts.wait,
|
|
6666
7258
|
nonce: opts.nonce,
|
|
6667
7259
|
tip: opts.tip,
|