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.
Files changed (3) hide show
  1. package/README.md +216 -13
  2. package/dist/cli.mjs +705 -113
  3. 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 { ...saved, chains };
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 input = chainFlag ?? config.defaultChain;
227
- const name = findChainName(config, input);
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
- const available = Object.keys(config.chains).join(", ");
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
- if (/^0x[0-9a-fA-F]*$/.test(arg))
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, rpcOverride);
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 ?? config.defaultChain;
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 ?? config.defaultChain;
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 ?? config.defaultChain;
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 ?? config.defaultChain;
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", "default"];
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.13.0";
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 [name] Re-fetch metadata (default chain if omitted)
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 default <name> Set the default 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] [name]", "Manage chains (add, remove, update, list, default)").alias("chains").option("--all", "Update all configured chains").option("--relay <name>", "Parent relay chain for this parachain").option("--parachain-id <id>", "Parachain ID (auto-detected if omitted with --relay)").action(async (action, name, opts) => {
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(name, opts);
3276
+ return chainAdd(names[0], opts);
3075
3277
  case "remove":
3076
- return chainRemove(name, opts);
3278
+ return chainRemove(names[0], opts);
3077
3279
  case "list":
3078
3280
  return chainList(opts);
3079
3281
  case "update":
3080
- return chainUpdate(name, opts);
3081
- case "default":
3082
- return chainDefault(name, opts);
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, config.defaultChain);
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, config.defaultChain, idSuffix);
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, config.defaultChain);
3435
+ printChainLine(" ", name, chainConfig);
3238
3436
  }
3239
3437
  if (standalone.length > 0)
3240
3438
  console.log();
3241
3439
  }
3242
- function printChainLine(prefix, name, chainConfig, defaultChain, suffix = "") {
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}${marker} ${DIM}${rpcs[0]}${RESET}`);
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
- async function chainDefault(name, opts = {}) {
3306
- if (!name) {
3307
- console.error("Usage: dot chain default <name>");
3308
- process.exit(1);
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 resolved = findChainName(config, name);
3312
- if (!resolved) {
3313
- const available = Object.keys(config.chains).join(", ");
3314
- throw new Error(`Chain "${name}" not found. Available: ${available}`);
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
- config.defaultChain = resolved;
3317
- await saveConfig(config);
3318
- if (isJsonOutput(opts)) {
3319
- console.log(formatJson({ action: "default", chain: resolved }));
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
- console.log(`Default chain set to "${resolved}".`);
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, rpcOverride);
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 readFile3 } from "node:fs/promises";
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 readFile3(opts.file);
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
- try {
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
- meta = await getOrFetchMetadata(chainName, clientHandle);
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 readFile4 } from "node:fs/promises";
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 readFile4(opts.file);
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 to output hex without signing)");
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 customSignedExtensions = buildCustomSignedExtensions(meta, userExtOverrides);
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
- if (/^0x[0-9a-fA-F]*$/.test(arg))
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 (PAPI_BUILTIN_EXTENSIONS2.has(ext.identifier))
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 watchTransaction(observable, level) {
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
- spinner.succeed("Signed");
5920
- console.log(` ${BOLD}Tx:${RESET} ${event.txHash}`);
5921
- spinner.start("Broadcasting...");
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
- printJsonLine({ event: "signed", txHash: event.txHash });
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 readFile5, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
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 readFile5(CONFIG_PATH2, "utf-8"));
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 { ...saved, chains };
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 writeFile3(CONFIG_PATH2, `${JSON.stringify(config, null, 2)}
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 readFile6 } from "node:fs/promises";
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 readFile6(filePath, "utf-8");
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 writeFile4 } from "node:fs/promises";
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 writeFile4(getCachePath(), `${JSON.stringify(cache)}
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 (default from config)");
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 (default from config)");
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,