polkadot-cli 1.13.0 → 1.14.0

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