@vultisig/cli 0.2.0 → 0.4.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 (4) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/README.md +112 -29
  3. package/dist/index.js +543 -168
  4. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1103,9 +1103,10 @@ var require_main = __commonJS({
1103
1103
 
1104
1104
  // src/index.ts
1105
1105
  import "dotenv/config";
1106
- import { Vultisig as Vultisig4 } from "@vultisig/sdk";
1107
- import chalk12 from "chalk";
1106
+ import { parseKeygenQR, Vultisig as Vultisig4 } from "@vultisig/sdk";
1107
+ import chalk13 from "chalk";
1108
1108
  import { program } from "commander";
1109
+ import { promises as fs3 } from "fs";
1109
1110
  import inquirer8 from "inquirer";
1110
1111
 
1111
1112
  // src/core/command-context.ts
@@ -1445,25 +1446,26 @@ import { fiatCurrencies, fiatCurrencyNameRecord as fiatCurrencyNameRecord2 } fro
1445
1446
  import { fiatCurrencyNameRecord, Vultisig } from "@vultisig/sdk";
1446
1447
  import chalk2 from "chalk";
1447
1448
  import inquirer2 from "inquirer";
1448
- function displayBalance(chain, balance) {
1449
+ function displayBalance(chain, balance, raw = false) {
1449
1450
  printResult(chalk2.cyan(`
1450
1451
  ${chain} Balance:`));
1451
- printResult(` Amount: ${balance.amount} ${balance.symbol}`);
1452
+ const displayAmount = raw ? balance.amount : formatBalanceAmount(balance.amount, balance.decimals);
1453
+ printResult(` Amount: ${displayAmount} ${balance.symbol}`);
1452
1454
  if (balance.fiatValue && balance.fiatCurrency) {
1453
1455
  printResult(` Value: ${balance.fiatValue.toFixed(2)} ${balance.fiatCurrency}`);
1454
1456
  }
1455
1457
  }
1456
- function displayBalancesTable(balances) {
1458
+ function displayBalancesTable(balances, raw = false) {
1457
1459
  printResult(chalk2.cyan("\nPortfolio Balances:\n"));
1458
1460
  const tableData = Object.entries(balances).map(([chain, balance]) => ({
1459
1461
  Chain: chain,
1460
- Amount: balance.amount,
1462
+ Amount: raw ? balance.amount : formatBalanceAmount(balance.amount, balance.decimals),
1461
1463
  Symbol: balance.symbol,
1462
1464
  Value: balance.fiatValue && balance.fiatCurrency ? `${balance.fiatValue.toFixed(2)} ${balance.fiatCurrency}` : "N/A"
1463
1465
  }));
1464
1466
  printTable(tableData);
1465
1467
  }
1466
- function displayPortfolio(portfolio, currency) {
1468
+ function displayPortfolio(portfolio, currency, raw = false) {
1467
1469
  const currencyName = fiatCurrencyNameRecord[currency];
1468
1470
  printResult(chalk2.cyan("\n+----------------------------------------+"));
1469
1471
  printResult(chalk2.cyan(`| Portfolio Total Value (${currencyName}) |`));
@@ -1474,7 +1476,7 @@ function displayPortfolio(portfolio, currency) {
1474
1476
  printResult(chalk2.bold("Chain Breakdown:\n"));
1475
1477
  const table = portfolio.chainBalances.map(({ chain, balance, value }) => ({
1476
1478
  Chain: chain,
1477
- Amount: balance.amount,
1479
+ Amount: raw ? balance.amount : formatBalanceAmount(balance.amount, balance.decimals),
1478
1480
  Symbol: balance.symbol,
1479
1481
  Value: value ? `${value.amount} ${value.currency.toUpperCase()}` : "N/A"
1480
1482
  }));
@@ -1610,6 +1612,14 @@ function formatBigintAmount(amount, decimals) {
1610
1612
  const trimmed = fractionStr.replace(/0+$/, "");
1611
1613
  return `${whole}.${trimmed}`;
1612
1614
  }
1615
+ function formatBalanceAmount(amount, decimals) {
1616
+ if (!amount || amount === "0") return "0";
1617
+ try {
1618
+ return formatBigintAmount(BigInt(amount), decimals);
1619
+ } catch {
1620
+ return amount;
1621
+ }
1622
+ }
1613
1623
  function displaySwapPreview(quote, fromAmount, fromSymbol, toSymbol, options) {
1614
1624
  const estimatedOutputFormatted = formatBigintAmount(quote.estimatedOutput, options.toDecimals);
1615
1625
  printResult(chalk2.cyan("\nSwap Preview:"));
@@ -1633,6 +1643,10 @@ function displaySwapPreview(quote, fromAmount, fromSymbol, toSymbol, options) {
1633
1643
  if (quote.feesFiat?.affiliate) {
1634
1644
  printResult(` (~$${quote.feesFiat.affiliate.toFixed(2)})`);
1635
1645
  }
1646
+ if (options.discountTier) {
1647
+ const tierDisplay = options.discountTier.charAt(0).toUpperCase() + options.discountTier.slice(1);
1648
+ printResult(chalk2.green(` (${tierDisplay} tier discount applied)`));
1649
+ }
1636
1650
  }
1637
1651
  printResult(` Total: ${totalFeeFormatted} ${options.feeSymbol}`);
1638
1652
  if (quote.feesFiat) {
@@ -1687,6 +1701,7 @@ async function confirmSwap() {
1687
1701
  async function executeBalance(ctx2, options = {}) {
1688
1702
  const vault = await ctx2.ensureActiveVault();
1689
1703
  const spinner = createSpinner("Loading balances...");
1704
+ const raw = options.raw ?? false;
1690
1705
  if (options.chain) {
1691
1706
  const balance = await vault.balance(options.chain);
1692
1707
  spinner.succeed("Balance loaded");
@@ -1694,7 +1709,7 @@ async function executeBalance(ctx2, options = {}) {
1694
1709
  outputJson({ chain: options.chain, balance });
1695
1710
  return;
1696
1711
  }
1697
- displayBalance(options.chain, balance);
1712
+ displayBalance(options.chain, balance, raw);
1698
1713
  } else {
1699
1714
  const balances = await vault.balances(void 0, options.includeTokens);
1700
1715
  spinner.succeed("Balances loaded");
@@ -1702,7 +1717,7 @@ async function executeBalance(ctx2, options = {}) {
1702
1717
  outputJson({ balances });
1703
1718
  return;
1704
1719
  }
1705
- displayBalancesTable(balances);
1720
+ displayBalancesTable(balances, raw);
1706
1721
  }
1707
1722
  }
1708
1723
  async function executePortfolio(ctx2, options = {}) {
@@ -1737,13 +1752,27 @@ async function executePortfolio(ctx2, options = {}) {
1737
1752
  outputJson({ portfolio, currency });
1738
1753
  return;
1739
1754
  }
1740
- displayPortfolio(portfolio, currency);
1755
+ displayPortfolio(portfolio, currency, options.raw ?? false);
1741
1756
  }
1742
1757
 
1743
1758
  // src/commands/chains.ts
1759
+ import { SUPPORTED_CHAINS } from "@vultisig/sdk";
1744
1760
  import chalk3 from "chalk";
1745
1761
  async function executeChains(ctx2, options = {}) {
1746
1762
  const vault = await ctx2.ensureActiveVault();
1763
+ if (options.addAll) {
1764
+ const currentCount = vault.chains.length;
1765
+ const spinner = createSpinner(`Adding all ${SUPPORTED_CHAINS.length} supported chains...`);
1766
+ await vault.setChains([...SUPPORTED_CHAINS]);
1767
+ const addedCount = SUPPORTED_CHAINS.length - currentCount;
1768
+ spinner.succeed(`Added ${addedCount} chains (${SUPPORTED_CHAINS.length} total)`);
1769
+ if (isJsonOutput()) {
1770
+ outputJson({ chains: [...vault.chains], added: addedCount, total: SUPPORTED_CHAINS.length });
1771
+ return;
1772
+ }
1773
+ info(chalk3.gray("\nAll supported chains are now enabled."));
1774
+ return;
1775
+ }
1747
1776
  if (options.add) {
1748
1777
  await vault.addChain(options.add);
1749
1778
  success(`
@@ -1764,7 +1793,9 @@ async function executeChains(ctx2, options = {}) {
1764
1793
  chains.forEach((chain) => {
1765
1794
  printResult(` - ${chain}`);
1766
1795
  });
1767
- info(chalk3.gray("\nUse --add <chain> to add a chain or --remove <chain> to remove one"));
1796
+ info(chalk3.gray(`
1797
+ ${chains.length} of ${SUPPORTED_CHAINS.length} chains enabled`));
1798
+ info(chalk3.gray("Use --add <chain>, --add-all, or --remove <chain>"));
1768
1799
  }
1769
1800
  }
1770
1801
  async function executeAddresses(ctx2) {
@@ -2506,8 +2537,8 @@ async function executeInfo(ctx2) {
2506
2537
  }
2507
2538
  displayVaultInfo(vault);
2508
2539
  }
2509
- async function executeImportSeedphraseFast(ctx2, options) {
2510
- const { mnemonic, name, password, email, discoverChains, chains, signal } = options;
2540
+ async function executeCreateFromSeedphraseFast(ctx2, options) {
2541
+ const { mnemonic, name, password, email, discoverChains, chains, signal, usePhantomSolanaPath } = options;
2511
2542
  const validateSpinner = createSpinner("Validating seedphrase...");
2512
2543
  const validation = await ctx2.sdk.validateSeedphrase(mnemonic);
2513
2544
  if (!validation.valid) {
@@ -2518,19 +2549,26 @@ async function executeImportSeedphraseFast(ctx2, options) {
2518
2549
  throw new Error(validation.error || "Invalid mnemonic phrase");
2519
2550
  }
2520
2551
  validateSpinner.succeed(`Valid ${validation.wordCount}-word seedphrase`);
2552
+ let detectedUsePhantomSolanaPath = usePhantomSolanaPath;
2521
2553
  if (discoverChains) {
2522
2554
  const discoverSpinner = createSpinner("Discovering chains with balances...");
2523
2555
  try {
2524
- const discovered = await ctx2.sdk.discoverChainsFromSeedphrase(mnemonic, chains, (p) => {
2556
+ const { results: discovered, usePhantomSolanaPath: detectedPhantomPath } = await ctx2.sdk.discoverChainsFromSeedphrase(mnemonic, chains, (p) => {
2525
2557
  discoverSpinner.text = `Discovering: ${p.chain || "scanning"} (${p.chainsProcessed}/${p.chainsTotal})`;
2526
2558
  });
2527
2559
  const chainsWithBalance = discovered.filter((c) => c.hasBalance);
2528
2560
  discoverSpinner.succeed(`Found ${chainsWithBalance.length} chains with balances`);
2561
+ if (usePhantomSolanaPath === void 0) {
2562
+ detectedUsePhantomSolanaPath = detectedPhantomPath;
2563
+ }
2529
2564
  if (chainsWithBalance.length > 0 && !isSilent()) {
2530
2565
  info("\nChains with balances:");
2531
2566
  for (const result of chainsWithBalance) {
2532
2567
  info(` ${result.chain}: ${result.balance} ${result.symbol}`);
2533
2568
  }
2569
+ if (detectedUsePhantomSolanaPath) {
2570
+ info(" (Using Phantom wallet derivation path for Solana)");
2571
+ }
2534
2572
  info("");
2535
2573
  }
2536
2574
  } catch {
@@ -2539,13 +2577,14 @@ async function executeImportSeedphraseFast(ctx2, options) {
2539
2577
  }
2540
2578
  const importSpinner = createSpinner("Importing seedphrase...");
2541
2579
  const vaultId = await withAbortSignal(
2542
- ctx2.sdk.importSeedphraseAsFastVault({
2580
+ ctx2.sdk.createFastVaultFromSeedphrase({
2543
2581
  mnemonic,
2544
2582
  name,
2545
2583
  password,
2546
2584
  email,
2547
2585
  // Don't pass discoverChains - CLI handles discovery above
2548
2586
  chains,
2587
+ usePhantomSolanaPath: detectedUsePhantomSolanaPath,
2549
2588
  onProgress: (step) => {
2550
2589
  importSpinner.text = `${step.message} (${step.progress}%)`;
2551
2590
  }
@@ -2573,7 +2612,7 @@ async function executeImportSeedphraseFast(ctx2, options) {
2573
2612
  verifySpinner.succeed("Email verified successfully!");
2574
2613
  setupVaultEvents(vault);
2575
2614
  await ctx2.setActiveVault(vault);
2576
- success("\n+ Vault imported from seedphrase!");
2615
+ success("\n+ Vault created from seedphrase!");
2577
2616
  info("\nYour vault is ready. Run the following commands:");
2578
2617
  printResult(chalk5.cyan(" vultisig balance ") + "- View balances");
2579
2618
  printResult(chalk5.cyan(" vultisig addresses ") + "- View addresses");
@@ -2623,8 +2662,18 @@ async function executeImportSeedphraseFast(ctx2, options) {
2623
2662
  }
2624
2663
  throw new Error("Verification loop exited unexpectedly");
2625
2664
  }
2626
- async function executeImportSeedphraseSecure(ctx2, options) {
2627
- const { mnemonic, name, password, threshold, shares: totalShares, discoverChains, chains, signal } = options;
2665
+ async function executeCreateFromSeedphraseSecure(ctx2, options) {
2666
+ const {
2667
+ mnemonic,
2668
+ name,
2669
+ password,
2670
+ threshold,
2671
+ shares: totalShares,
2672
+ discoverChains,
2673
+ chains,
2674
+ signal,
2675
+ usePhantomSolanaPath
2676
+ } = options;
2628
2677
  const validateSpinner = createSpinner("Validating seedphrase...");
2629
2678
  const validation = await ctx2.sdk.validateSeedphrase(mnemonic);
2630
2679
  if (!validation.valid) {
@@ -2635,19 +2684,26 @@ async function executeImportSeedphraseSecure(ctx2, options) {
2635
2684
  throw new Error(validation.error || "Invalid mnemonic phrase");
2636
2685
  }
2637
2686
  validateSpinner.succeed(`Valid ${validation.wordCount}-word seedphrase`);
2687
+ let detectedUsePhantomSolanaPath = usePhantomSolanaPath;
2638
2688
  if (discoverChains) {
2639
2689
  const discoverSpinner = createSpinner("Discovering chains with balances...");
2640
2690
  try {
2641
- const discovered = await ctx2.sdk.discoverChainsFromSeedphrase(mnemonic, chains, (p) => {
2691
+ const { results: discovered, usePhantomSolanaPath: detectedPhantomPath } = await ctx2.sdk.discoverChainsFromSeedphrase(mnemonic, chains, (p) => {
2642
2692
  discoverSpinner.text = `Discovering: ${p.chain || "scanning"} (${p.chainsProcessed}/${p.chainsTotal})`;
2643
2693
  });
2644
2694
  const chainsWithBalance = discovered.filter((c) => c.hasBalance);
2645
2695
  discoverSpinner.succeed(`Found ${chainsWithBalance.length} chains with balances`);
2696
+ if (usePhantomSolanaPath === void 0) {
2697
+ detectedUsePhantomSolanaPath = detectedPhantomPath;
2698
+ }
2646
2699
  if (chainsWithBalance.length > 0 && !isSilent()) {
2647
2700
  info("\nChains with balances:");
2648
2701
  for (const result of chainsWithBalance) {
2649
2702
  info(` ${result.chain}: ${result.balance} ${result.symbol}`);
2650
2703
  }
2704
+ if (detectedUsePhantomSolanaPath) {
2705
+ info(" (Using Phantom wallet derivation path for Solana)");
2706
+ }
2651
2707
  info("");
2652
2708
  }
2653
2709
  } catch {
@@ -2657,7 +2713,7 @@ async function executeImportSeedphraseSecure(ctx2, options) {
2657
2713
  const importSpinner = createSpinner("Importing seedphrase as secure vault...");
2658
2714
  try {
2659
2715
  const result = await withAbortSignal(
2660
- ctx2.sdk.importSeedphraseAsSecureVault({
2716
+ ctx2.sdk.createSecureVaultFromSeedphrase({
2661
2717
  mnemonic,
2662
2718
  name,
2663
2719
  password,
@@ -2665,6 +2721,7 @@ async function executeImportSeedphraseSecure(ctx2, options) {
2665
2721
  threshold,
2666
2722
  // Don't pass discoverChains - CLI handles discovery above
2667
2723
  chains,
2724
+ usePhantomSolanaPath: detectedUsePhantomSolanaPath,
2668
2725
  onProgress: (step) => {
2669
2726
  importSpinner.text = `${step.message} (${step.progress}%)`;
2670
2727
  },
@@ -2713,16 +2770,138 @@ Or use this URL: ${qrPayload}
2713
2770
  warn(`
2714
2771
  Important: Save your vault backup file (.vult) in a secure location.`);
2715
2772
  warn(`This is a ${threshold}-of-${totalShares} vault. You'll need ${threshold} devices to sign transactions.`);
2716
- success("\n+ Vault imported from seedphrase!");
2773
+ success("\n+ Vault created from seedphrase!");
2717
2774
  return result.vault;
2718
2775
  } catch (err) {
2719
- importSpinner.fail("Secure vault import failed");
2776
+ importSpinner.fail("Secure vault creation failed");
2720
2777
  if (err.message?.includes("not implemented")) {
2721
- warn("\nSecure vault seedphrase import is not yet implemented in the SDK");
2778
+ warn("\nSecure vault creation from seedphrase is not yet implemented in the SDK");
2779
+ }
2780
+ throw err;
2781
+ }
2782
+ }
2783
+ async function executeJoinSecure(ctx2, options) {
2784
+ const { qrPayload, mnemonic, password, devices, signal } = options;
2785
+ if (!devices || devices < 2) {
2786
+ throw new Error("devices is required when joining a SecureVault (minimum 2)");
2787
+ }
2788
+ const spinner = createSpinner("Joining SecureVault session...");
2789
+ try {
2790
+ const result = await withAbortSignal(
2791
+ ctx2.sdk.joinSecureVault(qrPayload, {
2792
+ mnemonic,
2793
+ password,
2794
+ devices,
2795
+ onProgress: (step) => {
2796
+ spinner.text = `${step.message} (${step.progress}%)`;
2797
+ },
2798
+ onDeviceJoined: (deviceId, totalJoined, required) => {
2799
+ if (!isSilent()) {
2800
+ spinner.text = `Device joined: ${totalJoined}/${required} (${deviceId})`;
2801
+ } else if (!isJsonOutput()) {
2802
+ printResult(`Device joined: ${totalJoined}/${required}`);
2803
+ }
2804
+ }
2805
+ }),
2806
+ signal
2807
+ );
2808
+ setupVaultEvents(result.vault);
2809
+ await ctx2.setActiveVault(result.vault);
2810
+ spinner.succeed(`Joined SecureVault: ${result.vault.name}`);
2811
+ if (isJsonOutput()) {
2812
+ outputJson({
2813
+ vault: {
2814
+ id: result.vaultId,
2815
+ name: result.vault.name,
2816
+ type: "secure"
2817
+ }
2818
+ });
2819
+ return result.vault;
2722
2820
  }
2821
+ warn("\nImportant: Save your vault backup file (.vult) in a secure location.");
2822
+ success("\n+ Successfully joined vault!");
2823
+ return result.vault;
2824
+ } catch (err) {
2825
+ spinner.fail("Failed to join SecureVault session");
2723
2826
  throw err;
2724
2827
  }
2725
2828
  }
2829
+ async function executeDelete(ctx2, options = {}) {
2830
+ let vault;
2831
+ if (options.vaultId) {
2832
+ const vaults = await ctx2.sdk.listVaults();
2833
+ vault = findVaultByIdOrName({ vaults, idOrName: options.vaultId });
2834
+ } else {
2835
+ vault = await ctx2.ensureActiveVault();
2836
+ }
2837
+ if (isJsonOutput()) {
2838
+ const spinner2 = createSpinner("Deleting vault...");
2839
+ await ctx2.sdk.deleteVault(vault);
2840
+ spinner2.succeed("Vault deleted");
2841
+ outputJson({
2842
+ deleted: true,
2843
+ vault: {
2844
+ id: vault.id,
2845
+ name: vault.name,
2846
+ type: vault.type
2847
+ }
2848
+ });
2849
+ return;
2850
+ }
2851
+ info("\n" + chalk5.bold("Vault to delete:"));
2852
+ info(` Name: ${chalk5.cyan(vault.name)}`);
2853
+ info(` Type: ${chalk5.yellow(vault.type)}`);
2854
+ info(` ID: ${vault.id.slice(0, 16)}...`);
2855
+ info(` Chains: ${vault.chains.length}`);
2856
+ info("");
2857
+ if (!options.skipConfirmation) {
2858
+ warn(chalk5.red.bold("WARNING: This action cannot be undone!"));
2859
+ warn("Make sure you have a backup of your vault (.vult file) before proceeding.");
2860
+ info("");
2861
+ const { confirmed } = await inquirer4.prompt([
2862
+ {
2863
+ type: "confirm",
2864
+ name: "confirmed",
2865
+ message: `Are you sure you want to delete vault "${vault.name}"?`,
2866
+ default: false
2867
+ }
2868
+ ]);
2869
+ if (!confirmed) {
2870
+ info("Deletion cancelled.");
2871
+ return;
2872
+ }
2873
+ }
2874
+ const spinner = createSpinner("Deleting vault...");
2875
+ await ctx2.sdk.deleteVault(vault);
2876
+ spinner.succeed(`Vault deleted: ${vault.name}`);
2877
+ success("\n+ Vault deleted successfully");
2878
+ info(chalk5.gray('\nTip: Run "vultisig vaults" to see remaining vaults'));
2879
+ }
2880
+ function findVaultByIdOrName({ vaults, idOrName }) {
2881
+ const byId = vaults.find((v) => v.id === idOrName);
2882
+ if (byId) return byId;
2883
+ const byName = vaults.filter((v) => v.name.toLowerCase() === idOrName.toLowerCase());
2884
+ if (byName.length === 1) return byName[0];
2885
+ if (byName.length > 1) {
2886
+ const matches = byName.map((v) => ` - ${v.name} (${v.id.slice(0, 8)}...)`).join("\n");
2887
+ throw new Error(
2888
+ `Multiple vaults match "${idOrName}":
2889
+ ${matches}
2890
+ Please specify the full vault ID to be more specific.`
2891
+ );
2892
+ }
2893
+ const byPartialId = vaults.filter((v) => v.id.startsWith(idOrName));
2894
+ if (byPartialId.length === 1) return byPartialId[0];
2895
+ if (byPartialId.length > 1) {
2896
+ const matches = byPartialId.map((v) => ` - ${v.name} (${v.id.slice(0, 8)}...)`).join("\n");
2897
+ throw new Error(
2898
+ `Multiple vaults match prefix "${idOrName}":
2899
+ ${matches}
2900
+ Please specify more characters of the vault ID.`
2901
+ );
2902
+ }
2903
+ throw new Error(`Vault not found: "${idOrName}"`);
2904
+ }
2726
2905
 
2727
2906
  // src/commands/swap.ts
2728
2907
  async function executeSwapChains(ctx2) {
@@ -2765,11 +2944,13 @@ async function executeSwapQuote(ctx2, options) {
2765
2944
  return quote;
2766
2945
  }
2767
2946
  const feeBalance = await vault.balance(options.fromChain);
2947
+ const discountTier = await vault.getDiscountTier();
2768
2948
  displaySwapPreview(quote, String(options.amount), quote.fromCoin.ticker, quote.toCoin.ticker, {
2769
2949
  fromDecimals: quote.fromCoin.decimals,
2770
2950
  toDecimals: quote.toCoin.decimals,
2771
2951
  feeDecimals: feeBalance.decimals,
2772
- feeSymbol: feeBalance.symbol
2952
+ feeSymbol: feeBalance.symbol,
2953
+ discountTier
2773
2954
  });
2774
2955
  info('\nTo execute this swap, use the "swap" command');
2775
2956
  return quote;
@@ -2793,12 +2974,14 @@ async function executeSwap(ctx2, options) {
2793
2974
  });
2794
2975
  quoteSpinner.succeed("Quote received");
2795
2976
  const feeBalance = await vault.balance(options.fromChain);
2977
+ const discountTier = await vault.getDiscountTier();
2796
2978
  if (!isJsonOutput()) {
2797
2979
  displaySwapPreview(quote, String(options.amount), quote.fromCoin.ticker, quote.toCoin.ticker, {
2798
2980
  fromDecimals: quote.fromCoin.decimals,
2799
2981
  toDecimals: quote.toCoin.decimals,
2800
2982
  feeDecimals: feeBalance.decimals,
2801
- feeSymbol: feeBalance.symbol
2983
+ feeSymbol: feeBalance.symbol,
2984
+ discountTier
2802
2985
  });
2803
2986
  }
2804
2987
  if (!options.yes && !isJsonOutput()) {
@@ -3031,6 +3214,104 @@ Address Book${options.chain ? ` (${options.chain})` : ""}:
3031
3214
  return allEntries;
3032
3215
  }
3033
3216
 
3217
+ // src/commands/discount.ts
3218
+ import {
3219
+ baseAffiliateBps,
3220
+ vultDiscountTierBps,
3221
+ vultDiscountTierMinBalances
3222
+ } from "@vultisig/sdk";
3223
+ import chalk7 from "chalk";
3224
+ var TIER_CONFIG = {
3225
+ none: { bps: baseAffiliateBps, discount: 0 },
3226
+ ...Object.fromEntries(
3227
+ Object.entries(vultDiscountTierMinBalances).map(([tier, minVult]) => [
3228
+ tier,
3229
+ {
3230
+ bps: baseAffiliateBps - vultDiscountTierBps[tier],
3231
+ discount: vultDiscountTierBps[tier],
3232
+ minVult
3233
+ }
3234
+ ])
3235
+ )
3236
+ };
3237
+ function getTierColor(tier) {
3238
+ const colors = {
3239
+ none: chalk7.gray,
3240
+ bronze: chalk7.hex("#CD7F32"),
3241
+ silver: chalk7.hex("#C0C0C0"),
3242
+ gold: chalk7.hex("#FFD700"),
3243
+ platinum: chalk7.hex("#E5E4E2"),
3244
+ diamond: chalk7.hex("#B9F2FF"),
3245
+ ultimate: chalk7.hex("#FF00FF")
3246
+ };
3247
+ return colors[tier] || chalk7.white;
3248
+ }
3249
+ function getNextTier(currentTier) {
3250
+ const tierOrder = ["none", "bronze", "silver", "gold", "platinum", "diamond", "ultimate"];
3251
+ const currentIndex = tierOrder.indexOf(currentTier);
3252
+ if (currentIndex === -1 || currentIndex >= tierOrder.length - 1) {
3253
+ return null;
3254
+ }
3255
+ const nextTierName = tierOrder[currentIndex + 1];
3256
+ const config = TIER_CONFIG[nextTierName];
3257
+ if ("minVult" in config) {
3258
+ return { name: nextTierName, vultRequired: config.minVult };
3259
+ }
3260
+ return null;
3261
+ }
3262
+ async function executeDiscount(ctx2, options = {}) {
3263
+ const vault = await ctx2.ensureActiveVault();
3264
+ const spinner = createSpinner(options.refresh ? "Refreshing discount tier..." : "Loading discount tier...");
3265
+ const tierResult = options.refresh ? await vault.updateDiscountTier() : await vault.getDiscountTier();
3266
+ const tier = tierResult || "none";
3267
+ const config = TIER_CONFIG[tier];
3268
+ const nextTier = getNextTier(tier);
3269
+ const tierInfo = {
3270
+ tier,
3271
+ feeBps: config.bps,
3272
+ discountBps: config.discount,
3273
+ nextTier
3274
+ };
3275
+ spinner.succeed("Discount tier loaded");
3276
+ if (isJsonOutput()) {
3277
+ outputJson({
3278
+ tier: tierInfo.tier,
3279
+ feeBps: tierInfo.feeBps,
3280
+ discountBps: tierInfo.discountBps,
3281
+ nextTier: tierInfo.nextTier
3282
+ });
3283
+ return tierInfo;
3284
+ }
3285
+ displayDiscountTier(tierInfo);
3286
+ return tierInfo;
3287
+ }
3288
+ function displayDiscountTier(tierInfo) {
3289
+ const tierColor = getTierColor(tierInfo.tier);
3290
+ printResult(chalk7.cyan("\n+----------------------------------------+"));
3291
+ printResult(chalk7.cyan("| VULT Discount Tier |"));
3292
+ printResult(chalk7.cyan("+----------------------------------------+\n"));
3293
+ const tierDisplay = tierInfo.tier === "none" ? chalk7.gray("No Tier") : tierColor(tierInfo.tier.charAt(0).toUpperCase() + tierInfo.tier.slice(1));
3294
+ printResult(` Current Tier: ${tierDisplay}`);
3295
+ if (tierInfo.tier === "none") {
3296
+ printResult(` Swap Fee: ${chalk7.gray("50 bps (0.50%)")}`);
3297
+ printResult(` Discount: ${chalk7.gray("None")}`);
3298
+ } else {
3299
+ printResult(` Swap Fee: ${chalk7.green(`${tierInfo.feeBps} bps (${(tierInfo.feeBps / 100).toFixed(2)}%)`)}`);
3300
+ printResult(` Discount: ${chalk7.green(`${tierInfo.discountBps} bps saved`)}`);
3301
+ }
3302
+ if (tierInfo.nextTier) {
3303
+ const nextTierColor = getTierColor(tierInfo.nextTier.name);
3304
+ printResult(chalk7.bold("\n Next Tier:"));
3305
+ printResult(
3306
+ ` ${nextTierColor(tierInfo.nextTier.name.charAt(0).toUpperCase() + tierInfo.nextTier.name.slice(1))} - requires ${tierInfo.nextTier.vultRequired.toLocaleString()} VULT`
3307
+ );
3308
+ } else if (tierInfo.tier === "ultimate") {
3309
+ printResult(chalk7.bold("\n ") + chalk7.magenta("You have the highest tier! 0% swap fees."));
3310
+ }
3311
+ info(chalk7.gray("\n Tip: Thorguard NFT holders get +1 tier upgrade (up to gold)"));
3312
+ printResult("");
3313
+ }
3314
+
3034
3315
  // src/interactive/completer.ts
3035
3316
  import { Chain as Chain5 } from "@vultisig/sdk";
3036
3317
  import fs2 from "fs";
@@ -3040,8 +3321,10 @@ var COMMANDS = [
3040
3321
  "vaults",
3041
3322
  "vault",
3042
3323
  "import",
3043
- "import-seedphrase",
3324
+ "delete",
3325
+ "create-from-seedphrase",
3044
3326
  "create",
3327
+ "join",
3045
3328
  "info",
3046
3329
  "export",
3047
3330
  // Wallet operations
@@ -3086,26 +3369,36 @@ function createCompleter(ctx2) {
3086
3369
  return completeVaultName(ctx2, partial);
3087
3370
  }
3088
3371
  if (command === "chains" && parts.length >= 2) {
3372
+ const lastPart = parts[parts.length - 1] || "";
3373
+ const lastPartLower = lastPart.toLowerCase();
3374
+ if (lastPartLower.startsWith("-")) {
3375
+ const flags = ["--add", "--add-all", "--remove"];
3376
+ const matches = flags.filter((f) => f.startsWith(lastPartLower));
3377
+ return [matches.length ? matches : flags, lastPart];
3378
+ }
3089
3379
  const flag = parts[parts.length - 2]?.toLowerCase();
3090
3380
  if (flag === "--add" || flag === "--remove") {
3091
- const partial = parts[parts.length - 1] || "";
3092
- return completeChainName(partial);
3093
- }
3094
- if (parts[parts.length - 1]?.toLowerCase() === "--add" || parts[parts.length - 1]?.toLowerCase() === "--remove") {
3095
- return completeChainName("");
3381
+ return completeChainName(lastPart);
3096
3382
  }
3097
3383
  }
3098
3384
  if (["balance", "bal", "tokens", "send", "swap", "swap-quote"].includes(command) && parts.length === 2) {
3099
3385
  const partial = parts[1] || "";
3100
3386
  return completeChainName(partial);
3101
3387
  }
3102
- if ((command === "create" || command === "import-seedphrase") && parts.length === 2) {
3388
+ if ((command === "create" || command === "create-from-seedphrase") && parts.length === 2) {
3103
3389
  const types = ["fast", "secure"];
3104
3390
  const partial = parts[1] || "";
3105
3391
  const partialLower = partial.toLowerCase();
3106
3392
  const matches = types.filter((t) => t.startsWith(partialLower));
3107
3393
  return [matches.length ? matches : types, partial];
3108
3394
  }
3395
+ if (command === "join" && parts.length === 2) {
3396
+ const types = ["secure"];
3397
+ const partial = parts[1] || "";
3398
+ const partialLower = partial.toLowerCase();
3399
+ const matches = types.filter((t) => t.startsWith(partialLower));
3400
+ return [matches.length ? matches : types, partial];
3401
+ }
3109
3402
  const hits = COMMANDS.filter((c) => c.startsWith(line));
3110
3403
  const show = hits.length ? hits : COMMANDS;
3111
3404
  return [show, line];
@@ -3178,7 +3471,7 @@ function findChainByName(name) {
3178
3471
  }
3179
3472
 
3180
3473
  // src/interactive/event-buffer.ts
3181
- import chalk7 from "chalk";
3474
+ import chalk8 from "chalk";
3182
3475
  var EventBuffer = class {
3183
3476
  eventBuffer = [];
3184
3477
  isCommandRunning = false;
@@ -3218,17 +3511,17 @@ var EventBuffer = class {
3218
3511
  displayEvent(message, type) {
3219
3512
  switch (type) {
3220
3513
  case "success":
3221
- console.log(chalk7.green(message));
3514
+ console.log(chalk8.green(message));
3222
3515
  break;
3223
3516
  case "warning":
3224
- console.log(chalk7.yellow(message));
3517
+ console.log(chalk8.yellow(message));
3225
3518
  break;
3226
3519
  case "error":
3227
- console.error(chalk7.red(message));
3520
+ console.error(chalk8.red(message));
3228
3521
  break;
3229
3522
  case "info":
3230
3523
  default:
3231
- console.log(chalk7.blue(message));
3524
+ console.log(chalk8.blue(message));
3232
3525
  break;
3233
3526
  }
3234
3527
  }
@@ -3239,13 +3532,13 @@ var EventBuffer = class {
3239
3532
  if (this.eventBuffer.length === 0) {
3240
3533
  return;
3241
3534
  }
3242
- console.log(chalk7.gray("\n--- Background Events ---"));
3535
+ console.log(chalk8.gray("\n--- Background Events ---"));
3243
3536
  this.eventBuffer.forEach((event) => {
3244
3537
  const timeStr = event.timestamp.toLocaleTimeString();
3245
3538
  const message = `[${timeStr}] ${event.message}`;
3246
3539
  this.displayEvent(message, event.type);
3247
3540
  });
3248
- console.log(chalk7.gray("--- End Events ---\n"));
3541
+ console.log(chalk8.gray("--- End Events ---\n"));
3249
3542
  }
3250
3543
  /**
3251
3544
  * Setup all vault event listeners
@@ -3347,12 +3640,12 @@ var EventBuffer = class {
3347
3640
 
3348
3641
  // src/interactive/session.ts
3349
3642
  import { fiatCurrencies as fiatCurrencies3 } from "@vultisig/sdk";
3350
- import chalk9 from "chalk";
3643
+ import chalk10 from "chalk";
3351
3644
  import ora3 from "ora";
3352
3645
  import * as readline from "readline";
3353
3646
 
3354
3647
  // src/interactive/shell-commands.ts
3355
- import chalk8 from "chalk";
3648
+ import chalk9 from "chalk";
3356
3649
  import Table from "cli-table3";
3357
3650
  import inquirer6 from "inquirer";
3358
3651
  import ora2 from "ora";
@@ -3369,25 +3662,25 @@ function formatTimeRemaining(ms) {
3369
3662
  async function executeLock(ctx2) {
3370
3663
  const vault = ctx2.getActiveVault();
3371
3664
  if (!vault) {
3372
- console.log(chalk8.red("No active vault."));
3373
- console.log(chalk8.yellow('Use "vault <name>" to switch to a vault first.'));
3665
+ console.log(chalk9.red("No active vault."));
3666
+ console.log(chalk9.yellow('Use "vault <name>" to switch to a vault first.'));
3374
3667
  return;
3375
3668
  }
3376
3669
  ctx2.lockVault(vault.id);
3377
- console.log(chalk8.green("\n+ Vault locked"));
3378
- console.log(chalk8.gray("Password cache cleared. You will need to enter the password again."));
3670
+ console.log(chalk9.green("\n+ Vault locked"));
3671
+ console.log(chalk9.gray("Password cache cleared. You will need to enter the password again."));
3379
3672
  }
3380
3673
  async function executeUnlock(ctx2) {
3381
3674
  const vault = ctx2.getActiveVault();
3382
3675
  if (!vault) {
3383
- console.log(chalk8.red("No active vault."));
3384
- console.log(chalk8.yellow('Use "vault <name>" to switch to a vault first.'));
3676
+ console.log(chalk9.red("No active vault."));
3677
+ console.log(chalk9.yellow('Use "vault <name>" to switch to a vault first.'));
3385
3678
  return;
3386
3679
  }
3387
3680
  if (ctx2.isVaultUnlocked(vault.id)) {
3388
3681
  const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
3389
- console.log(chalk8.yellow("\nVault is already unlocked."));
3390
- console.log(chalk8.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
3682
+ console.log(chalk9.yellow("\nVault is already unlocked."));
3683
+ console.log(chalk9.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
3391
3684
  return;
3392
3685
  }
3393
3686
  const { password } = await inquirer6.prompt([
@@ -3404,19 +3697,19 @@ async function executeUnlock(ctx2) {
3404
3697
  ctx2.cachePassword(vault.id, password);
3405
3698
  const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
3406
3699
  spinner.succeed("Vault unlocked");
3407
- console.log(chalk8.green(`
3700
+ console.log(chalk9.green(`
3408
3701
  + Vault unlocked for ${formatTimeRemaining(timeRemaining)}`));
3409
3702
  } catch (err) {
3410
3703
  spinner.fail("Failed to unlock vault");
3411
- console.error(chalk8.red(`
3704
+ console.error(chalk9.red(`
3412
3705
  x ${err.message}`));
3413
3706
  }
3414
3707
  }
3415
3708
  async function executeStatus(ctx2) {
3416
3709
  const vault = ctx2.getActiveVault();
3417
3710
  if (!vault) {
3418
- console.log(chalk8.red("No active vault."));
3419
- console.log(chalk8.yellow('Use "vault <name>" to switch to a vault first.'));
3711
+ console.log(chalk9.red("No active vault."));
3712
+ console.log(chalk9.yellow('Use "vault <name>" to switch to a vault first.'));
3420
3713
  return;
3421
3714
  }
3422
3715
  const isUnlocked = ctx2.isVaultUnlocked(vault.id);
@@ -3447,30 +3740,30 @@ async function executeStatus(ctx2) {
3447
3740
  displayStatus(status);
3448
3741
  }
3449
3742
  function displayStatus(status) {
3450
- console.log(chalk8.cyan("\n+----------------------------------------+"));
3451
- console.log(chalk8.cyan("| Vault Status |"));
3452
- console.log(chalk8.cyan("+----------------------------------------+\n"));
3453
- console.log(chalk8.bold("Vault:"));
3454
- console.log(` Name: ${chalk8.green(status.name)}`);
3743
+ console.log(chalk9.cyan("\n+----------------------------------------+"));
3744
+ console.log(chalk9.cyan("| Vault Status |"));
3745
+ console.log(chalk9.cyan("+----------------------------------------+\n"));
3746
+ console.log(chalk9.bold("Vault:"));
3747
+ console.log(` Name: ${chalk9.green(status.name)}`);
3455
3748
  console.log(` ID: ${status.id}`);
3456
- console.log(` Type: ${chalk8.yellow(status.type)}`);
3457
- console.log(chalk8.bold("\nSecurity:"));
3749
+ console.log(` Type: ${chalk9.yellow(status.type)}`);
3750
+ console.log(chalk9.bold("\nSecurity:"));
3458
3751
  if (status.isUnlocked) {
3459
- console.log(` Status: ${chalk8.green("Unlocked")} ${chalk8.green("\u{1F513}")}`);
3752
+ console.log(` Status: ${chalk9.green("Unlocked")} ${chalk9.green("\u{1F513}")}`);
3460
3753
  console.log(` Expires: ${status.timeRemainingFormatted}`);
3461
3754
  } else {
3462
- console.log(` Status: ${chalk8.yellow("Locked")} ${chalk8.yellow("\u{1F512}")}`);
3755
+ console.log(` Status: ${chalk9.yellow("Locked")} ${chalk9.yellow("\u{1F512}")}`);
3463
3756
  }
3464
- console.log(` Encrypted: ${status.isEncrypted ? chalk8.green("Yes") : chalk8.gray("No")}`);
3465
- console.log(` Backed Up: ${status.isBackedUp ? chalk8.green("Yes") : chalk8.yellow("No")}`);
3466
- console.log(chalk8.bold("\nMPC Configuration:"));
3757
+ console.log(` Encrypted: ${status.isEncrypted ? chalk9.green("Yes") : chalk9.gray("No")}`);
3758
+ console.log(` Backed Up: ${status.isBackedUp ? chalk9.green("Yes") : chalk9.yellow("No")}`);
3759
+ console.log(chalk9.bold("\nMPC Configuration:"));
3467
3760
  console.log(` Library: ${status.libType}`);
3468
- console.log(` Threshold: ${chalk8.cyan(status.threshold)} of ${chalk8.cyan(status.totalSigners)}`);
3469
- console.log(chalk8.bold("\nSigning Modes:"));
3761
+ console.log(` Threshold: ${chalk9.cyan(status.threshold)} of ${chalk9.cyan(status.totalSigners)}`);
3762
+ console.log(chalk9.bold("\nSigning Modes:"));
3470
3763
  status.availableSigningModes.forEach((mode) => {
3471
3764
  console.log(` - ${mode}`);
3472
3765
  });
3473
- console.log(chalk8.bold("\nDetails:"));
3766
+ console.log(chalk9.bold("\nDetails:"));
3474
3767
  console.log(` Chains: ${status.chains}`);
3475
3768
  console.log(` Currency: ${status.currency.toUpperCase()}`);
3476
3769
  console.log(` Created: ${new Date(status.createdAt).toLocaleString()}`);
@@ -3479,7 +3772,7 @@ function displayStatus(status) {
3479
3772
  }
3480
3773
  function showHelp() {
3481
3774
  const table = new Table({
3482
- head: [chalk8.bold("Available Commands")],
3775
+ head: [chalk9.bold("Available Commands")],
3483
3776
  colWidths: [50],
3484
3777
  chars: {
3485
3778
  mid: "",
@@ -3493,38 +3786,39 @@ function showHelp() {
3493
3786
  }
3494
3787
  });
3495
3788
  table.push(
3496
- [chalk8.bold("Vault Management:")],
3789
+ [chalk9.bold("Vault Management:")],
3497
3790
  [" vaults - List all vaults"],
3498
3791
  [" vault <name> - Switch to vault"],
3499
3792
  [" import <file> - Import vault from file"],
3793
+ [" delete [name] - Delete vault"],
3500
3794
  [" create - Create new vault"],
3501
3795
  [" info - Show vault details"],
3502
3796
  [" export [path] - Export vault to file"],
3503
3797
  [""],
3504
- [chalk8.bold("Wallet Operations:")],
3798
+ [chalk9.bold("Wallet Operations:")],
3505
3799
  [" balance [chain] - Show balances"],
3506
3800
  [" send <chain> <to> <amount> - Send transaction"],
3507
3801
  [" portfolio [-c usd] - Show portfolio value"],
3508
3802
  [" addresses - Show all addresses"],
3509
- [" chains [--add/--remove] - Manage chains"],
3803
+ [" chains [--add/--remove/--add-all] - Manage chains"],
3510
3804
  [" tokens <chain> - Manage tokens"],
3511
3805
  [""],
3512
- [chalk8.bold("Swap Operations:")],
3806
+ [chalk9.bold("Swap Operations:")],
3513
3807
  [" swap-chains - List swap-enabled chains"],
3514
3808
  [" swap-quote <from> <to> <amount> - Get quote"],
3515
3809
  [" swap <from> <to> <amount> - Execute swap"],
3516
3810
  [""],
3517
- [chalk8.bold("Session Commands (shell only):")],
3811
+ [chalk9.bold("Session Commands (shell only):")],
3518
3812
  [" lock - Lock vault"],
3519
3813
  [" unlock - Unlock vault"],
3520
3814
  [" status - Show vault status"],
3521
3815
  [""],
3522
- [chalk8.bold("Settings:")],
3816
+ [chalk9.bold("Settings:")],
3523
3817
  [" currency [code] - View/set currency"],
3524
3818
  [" server - Check server status"],
3525
3819
  [" address-book - Manage saved addresses"],
3526
3820
  [""],
3527
- [chalk8.bold("Help & Navigation:")],
3821
+ [chalk9.bold("Help & Navigation:")],
3528
3822
  [" help, ? - Show this help"],
3529
3823
  [" .clear - Clear screen"],
3530
3824
  [" .exit - Exit shell"]
@@ -3662,12 +3956,12 @@ var ShellSession = class {
3662
3956
  */
3663
3957
  async start() {
3664
3958
  console.clear();
3665
- console.log(chalk9.cyan.bold("\n=============================================="));
3666
- console.log(chalk9.cyan.bold(" Vultisig Interactive Shell"));
3667
- console.log(chalk9.cyan.bold("==============================================\n"));
3959
+ console.log(chalk10.cyan.bold("\n=============================================="));
3960
+ console.log(chalk10.cyan.bold(" Vultisig Interactive Shell"));
3961
+ console.log(chalk10.cyan.bold("==============================================\n"));
3668
3962
  await this.loadAllVaults();
3669
3963
  this.displayVaultList();
3670
- console.log(chalk9.gray('Type "help" for available commands, "exit" to quit\n'));
3964
+ console.log(chalk10.gray('Type "help" for available commands, "exit" to quit\n'));
3671
3965
  this.promptLoop().catch(() => {
3672
3966
  });
3673
3967
  }
@@ -3701,12 +3995,12 @@ var ShellSession = class {
3701
3995
  const now = Date.now();
3702
3996
  if (now - this.lastSigintTime < this.DOUBLE_CTRL_C_TIMEOUT) {
3703
3997
  rl.close();
3704
- console.log(chalk9.yellow("\nGoodbye!"));
3998
+ console.log(chalk10.yellow("\nGoodbye!"));
3705
3999
  this.ctx.dispose();
3706
4000
  process.exit(0);
3707
4001
  }
3708
4002
  this.lastSigintTime = now;
3709
- console.log(chalk9.yellow("\n(Press Ctrl+C again to exit)"));
4003
+ console.log(chalk10.yellow("\n(Press Ctrl+C again to exit)"));
3710
4004
  rl.close();
3711
4005
  resolve("");
3712
4006
  });
@@ -3801,7 +4095,7 @@ var ShellSession = class {
3801
4095
  stopAllSpinners();
3802
4096
  process.stdout.write("\x1B[?25h");
3803
4097
  process.stdout.write("\r\x1B[K");
3804
- console.log(chalk9.yellow("\nCancelling operation..."));
4098
+ console.log(chalk10.yellow("\nCancelling operation..."));
3805
4099
  };
3806
4100
  const cleanup = () => {
3807
4101
  process.removeListener("SIGINT", onSigint);
@@ -3838,10 +4132,10 @@ var ShellSession = class {
3838
4132
  stopAllSpinners();
3839
4133
  process.stdout.write("\x1B[?25h");
3840
4134
  process.stdout.write("\r\x1B[K");
3841
- console.log(chalk9.yellow("Operation cancelled"));
4135
+ console.log(chalk10.yellow("Operation cancelled"));
3842
4136
  return;
3843
4137
  }
3844
- console.error(chalk9.red(`
4138
+ console.error(chalk10.red(`
3845
4139
  Error: ${error2.message}`));
3846
4140
  }
3847
4141
  }
@@ -3863,7 +4157,7 @@ Error: ${error2.message}`));
3863
4157
  case "create":
3864
4158
  await this.createVault(args);
3865
4159
  break;
3866
- case "import-seedphrase":
4160
+ case "create-from-seedphrase":
3867
4161
  await this.importSeedphrase(args);
3868
4162
  break;
3869
4163
  case "info":
@@ -3874,11 +4168,14 @@ Error: ${error2.message}`));
3874
4168
  break;
3875
4169
  case "rename":
3876
4170
  if (args.length === 0) {
3877
- console.log(chalk9.yellow("Usage: rename <newName>"));
4171
+ console.log(chalk10.yellow("Usage: rename <newName>"));
3878
4172
  return;
3879
4173
  }
3880
4174
  await executeRename(this.ctx, args.join(" "));
3881
4175
  break;
4176
+ case "delete":
4177
+ await this.deleteVault(args);
4178
+ break;
3882
4179
  // Balance commands
3883
4180
  case "balance":
3884
4181
  case "bal":
@@ -3944,41 +4241,41 @@ Error: ${error2.message}`));
3944
4241
  // Exit
3945
4242
  case "exit":
3946
4243
  case "quit":
3947
- console.log(chalk9.yellow("\nGoodbye!"));
4244
+ console.log(chalk10.yellow("\nGoodbye!"));
3948
4245
  this.ctx.dispose();
3949
4246
  process.exit(0);
3950
4247
  break;
3951
4248
  // eslint requires break even after process.exit
3952
4249
  default:
3953
- console.log(chalk9.yellow(`Unknown command: ${command}`));
3954
- console.log(chalk9.gray('Type "help" for available commands'));
4250
+ console.log(chalk10.yellow(`Unknown command: ${command}`));
4251
+ console.log(chalk10.gray('Type "help" for available commands'));
3955
4252
  break;
3956
4253
  }
3957
4254
  }
3958
4255
  // ===== Command Helpers =====
3959
4256
  async switchVault(args) {
3960
4257
  if (args.length === 0) {
3961
- console.log(chalk9.yellow("Usage: vault <name>"));
3962
- console.log(chalk9.gray('Run "vaults" to see available vaults'));
4258
+ console.log(chalk10.yellow("Usage: vault <name>"));
4259
+ console.log(chalk10.gray('Run "vaults" to see available vaults'));
3963
4260
  return;
3964
4261
  }
3965
4262
  const vaultName = args.join(" ");
3966
4263
  const vault = this.ctx.findVaultByName(vaultName);
3967
4264
  if (!vault) {
3968
- console.log(chalk9.red(`Vault not found: ${vaultName}`));
3969
- console.log(chalk9.gray('Run "vaults" to see available vaults'));
4265
+ console.log(chalk10.red(`Vault not found: ${vaultName}`));
4266
+ console.log(chalk10.gray('Run "vaults" to see available vaults'));
3970
4267
  return;
3971
4268
  }
3972
4269
  await this.ctx.setActiveVault(vault);
3973
- console.log(chalk9.green(`
4270
+ console.log(chalk10.green(`
3974
4271
  + Switched to: ${vault.name}`));
3975
4272
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
3976
- const status = isUnlocked ? chalk9.green("Unlocked") : chalk9.yellow("Locked");
4273
+ const status = isUnlocked ? chalk10.green("Unlocked") : chalk10.yellow("Locked");
3977
4274
  console.log(`Status: ${status}`);
3978
4275
  }
3979
4276
  async importVault(args) {
3980
4277
  if (args.length === 0) {
3981
- console.log(chalk9.yellow("Usage: import <file>"));
4278
+ console.log(chalk10.yellow("Usage: import <file>"));
3982
4279
  return;
3983
4280
  }
3984
4281
  const filePath = args.join(" ");
@@ -3986,48 +4283,52 @@ Error: ${error2.message}`));
3986
4283
  this.ctx.addVault(vault);
3987
4284
  this.eventBuffer.setupVaultListeners(vault);
3988
4285
  }
4286
+ async deleteVault(args) {
4287
+ const vaultIdOrName = args.join(" ") || void 0;
4288
+ await executeDelete(this.ctx, { vaultId: vaultIdOrName });
4289
+ }
3989
4290
  async createVault(args) {
3990
4291
  const type = args[0]?.toLowerCase();
3991
4292
  if (!type || type !== "fast" && type !== "secure") {
3992
- console.log(chalk9.yellow("Usage: create <fast|secure>"));
3993
- console.log(chalk9.gray(" create fast - Create a fast vault (server-assisted 2-of-2)"));
3994
- console.log(chalk9.gray(" create secure - Create a secure vault (multi-device MPC)"));
4293
+ console.log(chalk10.yellow("Usage: create <fast|secure>"));
4294
+ console.log(chalk10.gray(" create fast - Create a fast vault (server-assisted 2-of-2)"));
4295
+ console.log(chalk10.gray(" create secure - Create a secure vault (multi-device MPC)"));
3995
4296
  return;
3996
4297
  }
3997
4298
  let vault;
3998
4299
  if (type === "fast") {
3999
4300
  const name = await this.prompt("Vault name");
4000
4301
  if (!name) {
4001
- console.log(chalk9.red("Name is required"));
4302
+ console.log(chalk10.red("Name is required"));
4002
4303
  return;
4003
4304
  }
4004
4305
  const password = await this.promptPassword("Vault password");
4005
4306
  if (!password) {
4006
- console.log(chalk9.red("Password is required"));
4307
+ console.log(chalk10.red("Password is required"));
4007
4308
  return;
4008
4309
  }
4009
4310
  const email = await this.prompt("Email for verification");
4010
4311
  if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
4011
- console.log(chalk9.red("Valid email is required"));
4312
+ console.log(chalk10.red("Valid email is required"));
4012
4313
  return;
4013
4314
  }
4014
4315
  vault = await this.withCancellation((signal) => executeCreateFast(this.ctx, { name, password, email, signal }));
4015
4316
  } else {
4016
4317
  const name = await this.prompt("Vault name");
4017
4318
  if (!name) {
4018
- console.log(chalk9.red("Name is required"));
4319
+ console.log(chalk10.red("Name is required"));
4019
4320
  return;
4020
4321
  }
4021
4322
  const sharesStr = await this.prompt("Total shares (devices)", "3");
4022
4323
  const shares = parseInt(sharesStr, 10);
4023
4324
  if (isNaN(shares) || shares < 2) {
4024
- console.log(chalk9.red("Must have at least 2 shares"));
4325
+ console.log(chalk10.red("Must have at least 2 shares"));
4025
4326
  return;
4026
4327
  }
4027
4328
  const thresholdStr = await this.prompt("Signing threshold", "2");
4028
4329
  const threshold = parseInt(thresholdStr, 10);
4029
4330
  if (isNaN(threshold) || threshold < 1 || threshold > shares) {
4030
- console.log(chalk9.red(`Threshold must be between 1 and ${shares}`));
4331
+ console.log(chalk10.red(`Threshold must be between 1 and ${shares}`));
4031
4332
  return;
4032
4333
  }
4033
4334
  const password = await this.promptPassword("Vault password (optional, press Enter to skip)");
@@ -4049,43 +4350,43 @@ Error: ${error2.message}`));
4049
4350
  async importSeedphrase(args) {
4050
4351
  const type = args[0]?.toLowerCase();
4051
4352
  if (!type || type !== "fast" && type !== "secure") {
4052
- console.log(chalk9.cyan("Usage: import-seedphrase <fast|secure>"));
4053
- console.log(chalk9.gray(" fast - Import with VultiServer (2-of-2)"));
4054
- console.log(chalk9.gray(" secure - Import with device coordination (N-of-M)"));
4353
+ console.log(chalk10.cyan("Usage: create-from-seedphrase <fast|secure>"));
4354
+ console.log(chalk10.gray(" fast - Import with VultiServer (2-of-2)"));
4355
+ console.log(chalk10.gray(" secure - Import with device coordination (N-of-M)"));
4055
4356
  return;
4056
4357
  }
4057
- console.log(chalk9.cyan("\nEnter your recovery phrase (words separated by spaces):"));
4358
+ console.log(chalk10.cyan("\nEnter your recovery phrase (words separated by spaces):"));
4058
4359
  const mnemonic = await this.promptPassword("Seedphrase");
4059
4360
  const validation = await this.ctx.sdk.validateSeedphrase(mnemonic);
4060
4361
  if (!validation.valid) {
4061
- console.log(chalk9.red(`Invalid seedphrase: ${validation.error}`));
4362
+ console.log(chalk10.red(`Invalid seedphrase: ${validation.error}`));
4062
4363
  if (validation.invalidWords?.length) {
4063
- console.log(chalk9.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
4364
+ console.log(chalk10.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
4064
4365
  }
4065
4366
  return;
4066
4367
  }
4067
- console.log(chalk9.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
4368
+ console.log(chalk10.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
4068
4369
  let vault;
4069
4370
  if (type === "fast") {
4070
4371
  const name = await this.prompt("Vault name");
4071
4372
  if (!name) {
4072
- console.log(chalk9.red("Name is required"));
4373
+ console.log(chalk10.red("Name is required"));
4073
4374
  return;
4074
4375
  }
4075
4376
  const password = await this.promptPassword("Vault password");
4076
4377
  if (!password) {
4077
- console.log(chalk9.red("Password is required"));
4378
+ console.log(chalk10.red("Password is required"));
4078
4379
  return;
4079
4380
  }
4080
4381
  const email = await this.prompt("Email for verification");
4081
4382
  if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
4082
- console.log(chalk9.red("Valid email is required"));
4383
+ console.log(chalk10.red("Valid email is required"));
4083
4384
  return;
4084
4385
  }
4085
4386
  const discoverStr = await this.prompt("Discover chains with balances? (y/n)", "y");
4086
4387
  const discoverChains = discoverStr.toLowerCase() === "y";
4087
4388
  vault = await this.withCancellation(
4088
- (signal) => executeImportSeedphraseFast(this.ctx, {
4389
+ (signal) => executeCreateFromSeedphraseFast(this.ctx, {
4089
4390
  mnemonic,
4090
4391
  name,
4091
4392
  password,
@@ -4097,26 +4398,26 @@ Error: ${error2.message}`));
4097
4398
  } else {
4098
4399
  const name = await this.prompt("Vault name");
4099
4400
  if (!name) {
4100
- console.log(chalk9.red("Name is required"));
4401
+ console.log(chalk10.red("Name is required"));
4101
4402
  return;
4102
4403
  }
4103
4404
  const sharesStr = await this.prompt("Total shares (devices)", "3");
4104
4405
  const shares = parseInt(sharesStr, 10);
4105
4406
  if (isNaN(shares) || shares < 2) {
4106
- console.log(chalk9.red("Must have at least 2 shares"));
4407
+ console.log(chalk10.red("Must have at least 2 shares"));
4107
4408
  return;
4108
4409
  }
4109
4410
  const thresholdStr = await this.prompt("Signing threshold", "2");
4110
4411
  const threshold = parseInt(thresholdStr, 10);
4111
4412
  if (isNaN(threshold) || threshold < 1 || threshold > shares) {
4112
- console.log(chalk9.red(`Threshold must be between 1 and ${shares}`));
4413
+ console.log(chalk10.red(`Threshold must be between 1 and ${shares}`));
4113
4414
  return;
4114
4415
  }
4115
4416
  const password = await this.promptPassword("Vault password (optional, Enter to skip)");
4116
4417
  const discoverStr = await this.prompt("Discover chains with balances? (y/n)", "y");
4117
4418
  const discoverChains = discoverStr.toLowerCase() === "y";
4118
4419
  vault = await this.withCancellation(
4119
- (signal) => executeImportSeedphraseSecure(this.ctx, {
4420
+ (signal) => executeCreateFromSeedphraseSecure(this.ctx, {
4120
4421
  mnemonic,
4121
4422
  name,
4122
4423
  password: password || void 0,
@@ -4133,12 +4434,14 @@ Error: ${error2.message}`));
4133
4434
  }
4134
4435
  }
4135
4436
  async runBalance(args) {
4136
- const chainStr = args[0];
4437
+ const chainStr = args.find((arg) => !arg.startsWith("-"));
4137
4438
  const includeTokens = args.includes("-t") || args.includes("--tokens");
4439
+ const raw = args.includes("--raw");
4138
4440
  await this.withCancellation(
4139
4441
  () => executeBalance(this.ctx, {
4140
4442
  chain: chainStr ? findChainByName(chainStr) || chainStr : void 0,
4141
- includeTokens
4443
+ includeTokens,
4444
+ raw
4142
4445
  })
4143
4446
  );
4144
4447
  }
@@ -4151,15 +4454,16 @@ Error: ${error2.message}`));
4151
4454
  }
4152
4455
  }
4153
4456
  if (!fiatCurrencies3.includes(currency)) {
4154
- console.log(chalk9.red(`Invalid currency: ${currency}`));
4155
- console.log(chalk9.yellow(`Supported currencies: ${fiatCurrencies3.join(", ")}`));
4457
+ console.log(chalk10.red(`Invalid currency: ${currency}`));
4458
+ console.log(chalk10.yellow(`Supported currencies: ${fiatCurrencies3.join(", ")}`));
4156
4459
  return;
4157
4460
  }
4158
- await this.withCancellation(() => executePortfolio(this.ctx, { currency }));
4461
+ const raw = args.includes("--raw");
4462
+ await this.withCancellation(() => executePortfolio(this.ctx, { currency, raw }));
4159
4463
  }
4160
4464
  async runSend(args) {
4161
4465
  if (args.length < 3) {
4162
- console.log(chalk9.yellow("Usage: send <chain> <to> <amount> [--token <tokenId>] [--memo <memo>]"));
4466
+ console.log(chalk10.yellow("Usage: send <chain> <to> <amount> [--token <tokenId>] [--memo <memo>]"));
4163
4467
  return;
4164
4468
  }
4165
4469
  const [chainStr, to, amount, ...rest] = args;
@@ -4179,7 +4483,7 @@ Error: ${error2.message}`));
4179
4483
  await this.withAbortHandler((signal) => executeSend(this.ctx, { chain, to, amount, tokenId, memo, signal }));
4180
4484
  } catch (err) {
4181
4485
  if (err.message === "Transaction cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
4182
- console.log(chalk9.yellow("\nTransaction cancelled"));
4486
+ console.log(chalk10.yellow("\nTransaction cancelled"));
4183
4487
  return;
4184
4488
  }
4185
4489
  throw err;
@@ -4188,12 +4492,15 @@ Error: ${error2.message}`));
4188
4492
  async runChains(args) {
4189
4493
  let addChain;
4190
4494
  let removeChain;
4495
+ let addAll = false;
4191
4496
  for (let i = 0; i < args.length; i++) {
4192
- if (args[i] === "--add" && i + 1 < args.length) {
4497
+ if (args[i] === "--add-all") {
4498
+ addAll = true;
4499
+ } else if (args[i] === "--add" && i + 1 < args.length) {
4193
4500
  const chain = findChainByName(args[i + 1]);
4194
4501
  if (!chain) {
4195
- console.log(chalk9.red(`Unknown chain: ${args[i + 1]}`));
4196
- console.log(chalk9.gray("Use tab completion to see available chains"));
4502
+ console.log(chalk10.red(`Unknown chain: ${args[i + 1]}`));
4503
+ console.log(chalk10.gray("Use tab completion to see available chains"));
4197
4504
  return;
4198
4505
  }
4199
4506
  addChain = chain;
@@ -4201,19 +4508,19 @@ Error: ${error2.message}`));
4201
4508
  } else if (args[i] === "--remove" && i + 1 < args.length) {
4202
4509
  const chain = findChainByName(args[i + 1]);
4203
4510
  if (!chain) {
4204
- console.log(chalk9.red(`Unknown chain: ${args[i + 1]}`));
4205
- console.log(chalk9.gray("Use tab completion to see available chains"));
4511
+ console.log(chalk10.red(`Unknown chain: ${args[i + 1]}`));
4512
+ console.log(chalk10.gray("Use tab completion to see available chains"));
4206
4513
  return;
4207
4514
  }
4208
4515
  removeChain = chain;
4209
4516
  i++;
4210
4517
  }
4211
4518
  }
4212
- await executeChains(this.ctx, { add: addChain, remove: removeChain });
4519
+ await executeChains(this.ctx, { add: addChain, remove: removeChain, addAll });
4213
4520
  }
4214
4521
  async runTokens(args) {
4215
4522
  if (args.length === 0) {
4216
- console.log(chalk9.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
4523
+ console.log(chalk10.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
4217
4524
  return;
4218
4525
  }
4219
4526
  const chainStr = args[0];
@@ -4234,7 +4541,7 @@ Error: ${error2.message}`));
4234
4541
  async runSwapQuote(args) {
4235
4542
  if (args.length < 3) {
4236
4543
  console.log(
4237
- chalk9.yellow("Usage: swap-quote <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>]")
4544
+ chalk10.yellow("Usage: swap-quote <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>]")
4238
4545
  );
4239
4546
  return;
4240
4547
  }
@@ -4258,7 +4565,7 @@ Error: ${error2.message}`));
4258
4565
  async runSwap(args) {
4259
4566
  if (args.length < 3) {
4260
4567
  console.log(
4261
- chalk9.yellow(
4568
+ chalk10.yellow(
4262
4569
  "Usage: swap <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>] [--slippage <pct>]"
4263
4570
  )
4264
4571
  );
@@ -4289,7 +4596,7 @@ Error: ${error2.message}`));
4289
4596
  );
4290
4597
  } catch (err) {
4291
4598
  if (err.message === "Swap cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
4292
- console.log(chalk9.yellow("\nSwap cancelled"));
4599
+ console.log(chalk10.yellow("\nSwap cancelled"));
4293
4600
  return;
4294
4601
  }
4295
4602
  throw err;
@@ -4351,24 +4658,24 @@ Error: ${error2.message}`));
4351
4658
  }
4352
4659
  getPrompt() {
4353
4660
  const vault = this.ctx.getActiveVault();
4354
- if (!vault) return chalk9.cyan("wallet> ");
4661
+ if (!vault) return chalk10.cyan("wallet> ");
4355
4662
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
4356
- const status = isUnlocked ? chalk9.green("\u{1F513}") : chalk9.yellow("\u{1F512}");
4357
- return chalk9.cyan(`wallet[${vault.name}]${status}> `);
4663
+ const status = isUnlocked ? chalk10.green("\u{1F513}") : chalk10.yellow("\u{1F512}");
4664
+ return chalk10.cyan(`wallet[${vault.name}]${status}> `);
4358
4665
  }
4359
4666
  displayVaultList() {
4360
4667
  const vaults = Array.from(this.ctx.getVaults().values());
4361
4668
  const activeVault = this.ctx.getActiveVault();
4362
4669
  if (vaults.length === 0) {
4363
- console.log(chalk9.yellow('No vaults found. Use "create" or "import <file>" to add a vault.\n'));
4670
+ console.log(chalk10.yellow('No vaults found. Use "create" or "import <file>" to add a vault.\n'));
4364
4671
  return;
4365
4672
  }
4366
- console.log(chalk9.cyan("Loaded Vaults:\n"));
4673
+ console.log(chalk10.cyan("Loaded Vaults:\n"));
4367
4674
  vaults.forEach((vault) => {
4368
4675
  const isActive = vault.id === activeVault?.id;
4369
4676
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
4370
- const activeMarker = isActive ? chalk9.green(" (active)") : "";
4371
- const lockIcon = isUnlocked ? chalk9.green("\u{1F513}") : chalk9.yellow("\u{1F512}");
4677
+ const activeMarker = isActive ? chalk10.green(" (active)") : "";
4678
+ const lockIcon = isUnlocked ? chalk10.green("\u{1F513}") : chalk10.yellow("\u{1F512}");
4372
4679
  console.log(` ${lockIcon} ${vault.name}${activeMarker} - ${vault.type}`);
4373
4680
  });
4374
4681
  console.log();
@@ -4376,10 +4683,10 @@ Error: ${error2.message}`));
4376
4683
  };
4377
4684
 
4378
4685
  // src/lib/errors.ts
4379
- import chalk10 from "chalk";
4686
+ import chalk11 from "chalk";
4380
4687
 
4381
4688
  // src/lib/version.ts
4382
- import chalk11 from "chalk";
4689
+ import chalk12 from "chalk";
4383
4690
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
4384
4691
  import { homedir } from "os";
4385
4692
  import { join } from "path";
@@ -4387,7 +4694,7 @@ var cachedVersion = null;
4387
4694
  function getVersion() {
4388
4695
  if (cachedVersion) return cachedVersion;
4389
4696
  if (true) {
4390
- cachedVersion = "0.2.0";
4697
+ cachedVersion = "0.4.0";
4391
4698
  return cachedVersion;
4392
4699
  }
4393
4700
  try {
@@ -4484,7 +4791,7 @@ function formatVersionShort() {
4484
4791
  }
4485
4792
  function formatVersionDetailed() {
4486
4793
  const lines = [];
4487
- lines.push(chalk11.bold(`Vultisig CLI v${getVersion()}`));
4794
+ lines.push(chalk12.bold(`Vultisig CLI v${getVersion()}`));
4488
4795
  lines.push("");
4489
4796
  lines.push(` Node.js: ${process.version}`);
4490
4797
  lines.push(` Platform: ${process.platform}-${process.arch}`);
@@ -4906,7 +5213,7 @@ program.command("import <file>").description("Import vault from .vult file").act
4906
5213
  await executeImport(context, file);
4907
5214
  })
4908
5215
  );
4909
- var importSeedphraseCmd = program.command("import-seedphrase").description("Import wallet from BIP39 seedphrase");
5216
+ var createFromSeedphraseCmd = program.command("create-from-seedphrase").description("Create vault from BIP39 seedphrase");
4910
5217
  async function promptSeedphrase() {
4911
5218
  info("\nEnter your 12 or 24-word recovery phrase.");
4912
5219
  info("Words will be hidden as you type.\n");
@@ -4927,7 +5234,26 @@ async function promptSeedphrase() {
4927
5234
  ]);
4928
5235
  return answer.mnemonic.trim().toLowerCase();
4929
5236
  }
4930
- importSeedphraseCmd.command("fast").description("Import as FastVault (server-assisted 2-of-2)").requiredOption("--name <name>", "Vault name").requiredOption("--password <password>", "Vault password").requiredOption("--email <email>", "Email for verification").option("--mnemonic <words>", "Seedphrase (12 or 24 words, space-separated)").option("--discover-chains", "Scan chains for existing balances").option("--chains <chains>", "Specific chains to enable (comma-separated)").action(
5237
+ async function promptQrPayload() {
5238
+ info("\nEnter the QR code payload from the initiator device.");
5239
+ info('The payload starts with "vultisig://".\n');
5240
+ const answer = await inquirer8.prompt([
5241
+ {
5242
+ type: "input",
5243
+ name: "qrPayload",
5244
+ message: "QR Payload:",
5245
+ validate: (input) => {
5246
+ const trimmed = input.trim();
5247
+ if (!trimmed.startsWith("vultisig://")) {
5248
+ return 'QR payload must start with "vultisig://"';
5249
+ }
5250
+ return true;
5251
+ }
5252
+ }
5253
+ ]);
5254
+ return answer.qrPayload.trim();
5255
+ }
5256
+ createFromSeedphraseCmd.command("fast").description("Create FastVault from seedphrase (server-assisted 2-of-2)").requiredOption("--name <name>", "Vault name").requiredOption("--password <password>", "Vault password").requiredOption("--email <email>", "Email for verification").option("--mnemonic <words>", "Seedphrase (12 or 24 words, space-separated)").option("--discover-chains", "Scan chains for existing balances").option("--chains <chains>", "Specific chains to enable (comma-separated)").option("--use-phantom-solana-path", "Use Phantom wallet derivation path for Solana").action(
4931
5257
  withExit(
4932
5258
  async (options) => {
4933
5259
  const context = await init(program.opts().vault);
@@ -4948,18 +5274,19 @@ importSeedphraseCmd.command("fast").description("Import as FastVault (server-ass
4948
5274
  }
4949
5275
  }
4950
5276
  }
4951
- await executeImportSeedphraseFast(context, {
5277
+ await executeCreateFromSeedphraseFast(context, {
4952
5278
  mnemonic,
4953
5279
  name: options.name,
4954
5280
  password: options.password,
4955
5281
  email: options.email,
4956
5282
  discoverChains: options.discoverChains,
4957
- chains
5283
+ chains,
5284
+ usePhantomSolanaPath: options.usePhantomSolanaPath
4958
5285
  });
4959
5286
  }
4960
5287
  )
4961
5288
  );
4962
- importSeedphraseCmd.command("secure").description("Import as SecureVault (multi-device MPC)").requiredOption("--name <name>", "Vault name").option("--password <password>", "Vault password (optional)").option("--threshold <m>", "Signing threshold", "2").option("--shares <n>", "Total shares", "3").option("--mnemonic <words>", "Seedphrase (12 or 24 words)").option("--discover-chains", "Scan chains for existing balances").option("--chains <chains>", "Specific chains to enable (comma-separated)").action(
5289
+ createFromSeedphraseCmd.command("secure").description("Create SecureVault from seedphrase (multi-device MPC)").requiredOption("--name <name>", "Vault name").option("--password <password>", "Vault password (optional)").option("--threshold <m>", "Signing threshold", "2").option("--shares <n>", "Total shares", "3").option("--mnemonic <words>", "Seedphrase (12 or 24 words)").option("--discover-chains", "Scan chains for existing balances").option("--chains <chains>", "Specific chains to enable (comma-separated)").option("--use-phantom-solana-path", "Use Phantom wallet derivation path for Solana").action(
4963
5290
  withExit(
4964
5291
  async (options) => {
4965
5292
  const context = await init(program.opts().vault);
@@ -4980,14 +5307,42 @@ importSeedphraseCmd.command("secure").description("Import as SecureVault (multi-
4980
5307
  }
4981
5308
  }
4982
5309
  }
4983
- await executeImportSeedphraseSecure(context, {
5310
+ await executeCreateFromSeedphraseSecure(context, {
4984
5311
  mnemonic,
4985
5312
  name: options.name,
4986
5313
  password: options.password,
4987
5314
  threshold: parseInt(options.threshold, 10),
4988
5315
  shares: parseInt(options.shares, 10),
4989
5316
  discoverChains: options.discoverChains,
4990
- chains
5317
+ chains,
5318
+ usePhantomSolanaPath: options.usePhantomSolanaPath
5319
+ });
5320
+ }
5321
+ )
5322
+ );
5323
+ var joinCmd = program.command("join").description("Join an existing vault creation session");
5324
+ joinCmd.command("secure").description("Join a SecureVault creation session").option("--qr <payload>", "QR code payload from initiator (vultisig://...)").option("--qr-file <path>", "Read QR payload from file").option("--mnemonic <words>", "Seedphrase (required for seedphrase-based sessions)").option("--password <password>", "Vault password (optional)").option("--devices <n>", "Total devices in session", "2").action(
5325
+ withExit(
5326
+ async (options) => {
5327
+ const context = await init(program.opts().vault);
5328
+ let qrPayload = options.qr;
5329
+ if (!qrPayload && options.qrFile) {
5330
+ qrPayload = (await fs3.readFile(options.qrFile, "utf-8")).trim();
5331
+ }
5332
+ if (!qrPayload) {
5333
+ qrPayload = await promptQrPayload();
5334
+ }
5335
+ const qrParams = await parseKeygenQR(qrPayload);
5336
+ let mnemonic = options.mnemonic;
5337
+ if (qrParams.libType === "KEYIMPORT" && !mnemonic) {
5338
+ info("\nThis session requires a seedphrase to join.");
5339
+ mnemonic = await promptSeedphrase();
5340
+ }
5341
+ await executeJoinSecure(context, {
5342
+ qrPayload,
5343
+ mnemonic,
5344
+ password: options.password,
5345
+ devices: parseInt(options.devices, 10)
4991
5346
  });
4992
5347
  }
4993
5348
  )
@@ -5005,12 +5360,13 @@ program.command("verify <vaultId>").description("Verify vault with email verific
5005
5360
  }
5006
5361
  )
5007
5362
  );
5008
- program.command("balance [chain]").description("Show balance for a chain or all chains").option("-t, --tokens", "Include token balances").action(
5363
+ program.command("balance [chain]").description("Show balance for a chain or all chains").option("-t, --tokens", "Include token balances").option("--raw", "Show raw values (wei/satoshis) for programmatic use").action(
5009
5364
  withExit(async (chainStr, options) => {
5010
5365
  const context = await init(program.opts().vault);
5011
5366
  await executeBalance(context, {
5012
5367
  chain: chainStr ? findChainByName(chainStr) || chainStr : void 0,
5013
- includeTokens: options.tokens
5368
+ includeTokens: options.tokens,
5369
+ raw: options.raw
5014
5370
  });
5015
5371
  })
5016
5372
  );
@@ -5057,10 +5413,13 @@ program.command("broadcast").description("Broadcast a pre-signed raw transaction
5057
5413
  });
5058
5414
  })
5059
5415
  );
5060
- program.command("portfolio").description("Show total portfolio value").option("-c, --currency <currency>", "Fiat currency (usd, eur, gbp, etc.)", "usd").action(
5416
+ program.command("portfolio").description("Show total portfolio value").option("-c, --currency <currency>", "Fiat currency (usd, eur, gbp, etc.)", "usd").option("--raw", "Show raw values (wei/satoshis) for programmatic use").action(
5061
5417
  withExit(async (options) => {
5062
5418
  const context = await init(program.opts().vault);
5063
- await executePortfolio(context, { currency: options.currency.toLowerCase() });
5419
+ await executePortfolio(context, {
5420
+ currency: options.currency.toLowerCase(),
5421
+ raw: options.raw
5422
+ });
5064
5423
  })
5065
5424
  );
5066
5425
  program.command("currency [newCurrency]").description("View or set the vault currency preference").action(
@@ -5075,6 +5434,12 @@ program.command("server").description("Check server connectivity and status").ac
5075
5434
  await executeServer(context);
5076
5435
  })
5077
5436
  );
5437
+ program.command("discount").description("Show your VULT discount tier for swap fees").option("--refresh", "Force refresh tier from blockchain").action(
5438
+ withExit(async (options) => {
5439
+ const context = await init(program.opts().vault);
5440
+ await executeDiscount(context, { refresh: options.refresh });
5441
+ })
5442
+ );
5078
5443
  program.command("export [path]").description("Export vault to file").option("--password <password>", "Password to unlock the vault (for encrypted vaults)").option("--exportPassword <password>", "Password to encrypt the exported file (defaults to --password)").action(
5079
5444
  withExit(async (path3, options) => {
5080
5445
  const context = await init(program.opts().vault, options.password);
@@ -5103,11 +5468,12 @@ program.command("address-book").description("Manage address book entries").optio
5103
5468
  });
5104
5469
  })
5105
5470
  );
5106
- program.command("chains").description("List and manage chains").option("--add <chain>", "Add a chain").option("--remove <chain>", "Remove a chain").action(
5471
+ program.command("chains").description("List and manage chains").option("--add <chain>", "Add a chain").option("--add-all", "Add all supported chains").option("--remove <chain>", "Remove a chain").action(
5107
5472
  withExit(async (options) => {
5108
5473
  const context = await init(program.opts().vault);
5109
5474
  await executeChains(context, {
5110
5475
  add: options.add ? findChainByName(options.add) || options.add : void 0,
5476
+ addAll: options.addAll,
5111
5477
  remove: options.remove ? findChainByName(options.remove) || options.remove : void 0
5112
5478
  });
5113
5479
  })
@@ -5136,6 +5502,15 @@ program.command("info").description("Show detailed vault information").action(
5136
5502
  await executeInfo(context);
5137
5503
  })
5138
5504
  );
5505
+ program.command("delete [vault]").description("Delete a vault from local storage").option("-y, --yes", "Skip confirmation prompt").action(
5506
+ withExit(async (vaultIdOrName, options) => {
5507
+ const context = await init(program.opts().vault);
5508
+ await executeDelete(context, {
5509
+ vaultId: vaultIdOrName,
5510
+ skipConfirmation: options.yes
5511
+ });
5512
+ })
5513
+ );
5139
5514
  program.command("tokens <chain>").description("List and manage tokens for a chain").option("--add <contractAddress>", "Add a token by contract address").option("--remove <tokenId>", "Remove a token by ID").option("--symbol <symbol>", "Token symbol (for --add)").option("--name <name>", "Token name (for --add)").option("--decimals <decimals>", "Token decimals (for --add)", "18").action(
5140
5515
  withExit(
5141
5516
  async (chainStr, options) => {
@@ -5202,8 +5577,8 @@ program.command("version").description("Show detailed version information").acti
5202
5577
  const result = await checkForUpdates();
5203
5578
  if (result?.updateAvailable && result.latestVersion) {
5204
5579
  info("");
5205
- info(chalk12.yellow(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
5206
- info(chalk12.gray(`Run "${getUpdateCommand()}" to update`));
5580
+ info(chalk13.yellow(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
5581
+ info(chalk13.gray(`Run "${getUpdateCommand()}" to update`));
5207
5582
  }
5208
5583
  })
5209
5584
  );
@@ -5212,22 +5587,22 @@ program.command("update").description("Check for updates and show update command
5212
5587
  info("Checking for updates...");
5213
5588
  const result = await checkForUpdates();
5214
5589
  if (!result) {
5215
- printResult(chalk12.gray("Update checking is disabled"));
5590
+ printResult(chalk13.gray("Update checking is disabled"));
5216
5591
  return;
5217
5592
  }
5218
5593
  if (result.updateAvailable && result.latestVersion) {
5219
5594
  printResult("");
5220
- printResult(chalk12.green(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
5595
+ printResult(chalk13.green(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
5221
5596
  printResult("");
5222
5597
  if (options.check) {
5223
5598
  printResult(`Run "${getUpdateCommand()}" to update`);
5224
5599
  } else {
5225
5600
  const updateCmd = getUpdateCommand();
5226
5601
  printResult(`To update, run:`);
5227
- printResult(chalk12.cyan(` ${updateCmd}`));
5602
+ printResult(chalk13.cyan(` ${updateCmd}`));
5228
5603
  }
5229
5604
  } else {
5230
- printResult(chalk12.green(`You're on the latest version (${result.currentVersion})`));
5605
+ printResult(chalk13.green(`You're on the latest version (${result.currentVersion})`));
5231
5606
  }
5232
5607
  })
5233
5608
  );