@vultisig/cli 0.3.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.
package/dist/index.js CHANGED
@@ -1104,7 +1104,7 @@ var require_main = __commonJS({
1104
1104
  // src/index.ts
1105
1105
  import "dotenv/config";
1106
1106
  import { parseKeygenQR, Vultisig as Vultisig4 } from "@vultisig/sdk";
1107
- import chalk12 from "chalk";
1107
+ import chalk13 from "chalk";
1108
1108
  import { program } from "commander";
1109
1109
  import { promises as fs3 } from "fs";
1110
1110
  import inquirer8 from "inquirer";
@@ -1446,25 +1446,26 @@ import { fiatCurrencies, fiatCurrencyNameRecord as fiatCurrencyNameRecord2 } fro
1446
1446
  import { fiatCurrencyNameRecord, Vultisig } from "@vultisig/sdk";
1447
1447
  import chalk2 from "chalk";
1448
1448
  import inquirer2 from "inquirer";
1449
- function displayBalance(chain, balance) {
1449
+ function displayBalance(chain, balance, raw = false) {
1450
1450
  printResult(chalk2.cyan(`
1451
1451
  ${chain} Balance:`));
1452
- printResult(` Amount: ${balance.amount} ${balance.symbol}`);
1452
+ const displayAmount = raw ? balance.amount : formatBalanceAmount(balance.amount, balance.decimals);
1453
+ printResult(` Amount: ${displayAmount} ${balance.symbol}`);
1453
1454
  if (balance.fiatValue && balance.fiatCurrency) {
1454
1455
  printResult(` Value: ${balance.fiatValue.toFixed(2)} ${balance.fiatCurrency}`);
1455
1456
  }
1456
1457
  }
1457
- function displayBalancesTable(balances) {
1458
+ function displayBalancesTable(balances, raw = false) {
1458
1459
  printResult(chalk2.cyan("\nPortfolio Balances:\n"));
1459
1460
  const tableData = Object.entries(balances).map(([chain, balance]) => ({
1460
1461
  Chain: chain,
1461
- Amount: balance.amount,
1462
+ Amount: raw ? balance.amount : formatBalanceAmount(balance.amount, balance.decimals),
1462
1463
  Symbol: balance.symbol,
1463
1464
  Value: balance.fiatValue && balance.fiatCurrency ? `${balance.fiatValue.toFixed(2)} ${balance.fiatCurrency}` : "N/A"
1464
1465
  }));
1465
1466
  printTable(tableData);
1466
1467
  }
1467
- function displayPortfolio(portfolio, currency) {
1468
+ function displayPortfolio(portfolio, currency, raw = false) {
1468
1469
  const currencyName = fiatCurrencyNameRecord[currency];
1469
1470
  printResult(chalk2.cyan("\n+----------------------------------------+"));
1470
1471
  printResult(chalk2.cyan(`| Portfolio Total Value (${currencyName}) |`));
@@ -1475,7 +1476,7 @@ function displayPortfolio(portfolio, currency) {
1475
1476
  printResult(chalk2.bold("Chain Breakdown:\n"));
1476
1477
  const table = portfolio.chainBalances.map(({ chain, balance, value }) => ({
1477
1478
  Chain: chain,
1478
- Amount: balance.amount,
1479
+ Amount: raw ? balance.amount : formatBalanceAmount(balance.amount, balance.decimals),
1479
1480
  Symbol: balance.symbol,
1480
1481
  Value: value ? `${value.amount} ${value.currency.toUpperCase()}` : "N/A"
1481
1482
  }));
@@ -1611,6 +1612,14 @@ function formatBigintAmount(amount, decimals) {
1611
1612
  const trimmed = fractionStr.replace(/0+$/, "");
1612
1613
  return `${whole}.${trimmed}`;
1613
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
+ }
1614
1623
  function displaySwapPreview(quote, fromAmount, fromSymbol, toSymbol, options) {
1615
1624
  const estimatedOutputFormatted = formatBigintAmount(quote.estimatedOutput, options.toDecimals);
1616
1625
  printResult(chalk2.cyan("\nSwap Preview:"));
@@ -1634,6 +1643,10 @@ function displaySwapPreview(quote, fromAmount, fromSymbol, toSymbol, options) {
1634
1643
  if (quote.feesFiat?.affiliate) {
1635
1644
  printResult(` (~$${quote.feesFiat.affiliate.toFixed(2)})`);
1636
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
+ }
1637
1650
  }
1638
1651
  printResult(` Total: ${totalFeeFormatted} ${options.feeSymbol}`);
1639
1652
  if (quote.feesFiat) {
@@ -1688,6 +1701,7 @@ async function confirmSwap() {
1688
1701
  async function executeBalance(ctx2, options = {}) {
1689
1702
  const vault = await ctx2.ensureActiveVault();
1690
1703
  const spinner = createSpinner("Loading balances...");
1704
+ const raw = options.raw ?? false;
1691
1705
  if (options.chain) {
1692
1706
  const balance = await vault.balance(options.chain);
1693
1707
  spinner.succeed("Balance loaded");
@@ -1695,7 +1709,7 @@ async function executeBalance(ctx2, options = {}) {
1695
1709
  outputJson({ chain: options.chain, balance });
1696
1710
  return;
1697
1711
  }
1698
- displayBalance(options.chain, balance);
1712
+ displayBalance(options.chain, balance, raw);
1699
1713
  } else {
1700
1714
  const balances = await vault.balances(void 0, options.includeTokens);
1701
1715
  spinner.succeed("Balances loaded");
@@ -1703,7 +1717,7 @@ async function executeBalance(ctx2, options = {}) {
1703
1717
  outputJson({ balances });
1704
1718
  return;
1705
1719
  }
1706
- displayBalancesTable(balances);
1720
+ displayBalancesTable(balances, raw);
1707
1721
  }
1708
1722
  }
1709
1723
  async function executePortfolio(ctx2, options = {}) {
@@ -1738,13 +1752,27 @@ async function executePortfolio(ctx2, options = {}) {
1738
1752
  outputJson({ portfolio, currency });
1739
1753
  return;
1740
1754
  }
1741
- displayPortfolio(portfolio, currency);
1755
+ displayPortfolio(portfolio, currency, options.raw ?? false);
1742
1756
  }
1743
1757
 
1744
1758
  // src/commands/chains.ts
1759
+ import { SUPPORTED_CHAINS } from "@vultisig/sdk";
1745
1760
  import chalk3 from "chalk";
1746
1761
  async function executeChains(ctx2, options = {}) {
1747
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
+ }
1748
1776
  if (options.add) {
1749
1777
  await vault.addChain(options.add);
1750
1778
  success(`
@@ -1765,7 +1793,9 @@ async function executeChains(ctx2, options = {}) {
1765
1793
  chains.forEach((chain) => {
1766
1794
  printResult(` - ${chain}`);
1767
1795
  });
1768
- 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>"));
1769
1799
  }
1770
1800
  }
1771
1801
  async function executeAddresses(ctx2) {
@@ -2508,7 +2538,7 @@ async function executeInfo(ctx2) {
2508
2538
  displayVaultInfo(vault);
2509
2539
  }
2510
2540
  async function executeCreateFromSeedphraseFast(ctx2, options) {
2511
- const { mnemonic, name, password, email, discoverChains, chains, signal } = options;
2541
+ const { mnemonic, name, password, email, discoverChains, chains, signal, usePhantomSolanaPath } = options;
2512
2542
  const validateSpinner = createSpinner("Validating seedphrase...");
2513
2543
  const validation = await ctx2.sdk.validateSeedphrase(mnemonic);
2514
2544
  if (!validation.valid) {
@@ -2519,19 +2549,26 @@ async function executeCreateFromSeedphraseFast(ctx2, options) {
2519
2549
  throw new Error(validation.error || "Invalid mnemonic phrase");
2520
2550
  }
2521
2551
  validateSpinner.succeed(`Valid ${validation.wordCount}-word seedphrase`);
2552
+ let detectedUsePhantomSolanaPath = usePhantomSolanaPath;
2522
2553
  if (discoverChains) {
2523
2554
  const discoverSpinner = createSpinner("Discovering chains with balances...");
2524
2555
  try {
2525
- const discovered = await ctx2.sdk.discoverChainsFromSeedphrase(mnemonic, chains, (p) => {
2556
+ const { results: discovered, usePhantomSolanaPath: detectedPhantomPath } = await ctx2.sdk.discoverChainsFromSeedphrase(mnemonic, chains, (p) => {
2526
2557
  discoverSpinner.text = `Discovering: ${p.chain || "scanning"} (${p.chainsProcessed}/${p.chainsTotal})`;
2527
2558
  });
2528
2559
  const chainsWithBalance = discovered.filter((c) => c.hasBalance);
2529
2560
  discoverSpinner.succeed(`Found ${chainsWithBalance.length} chains with balances`);
2561
+ if (usePhantomSolanaPath === void 0) {
2562
+ detectedUsePhantomSolanaPath = detectedPhantomPath;
2563
+ }
2530
2564
  if (chainsWithBalance.length > 0 && !isSilent()) {
2531
2565
  info("\nChains with balances:");
2532
2566
  for (const result of chainsWithBalance) {
2533
2567
  info(` ${result.chain}: ${result.balance} ${result.symbol}`);
2534
2568
  }
2569
+ if (detectedUsePhantomSolanaPath) {
2570
+ info(" (Using Phantom wallet derivation path for Solana)");
2571
+ }
2535
2572
  info("");
2536
2573
  }
2537
2574
  } catch {
@@ -2547,6 +2584,7 @@ async function executeCreateFromSeedphraseFast(ctx2, options) {
2547
2584
  email,
2548
2585
  // Don't pass discoverChains - CLI handles discovery above
2549
2586
  chains,
2587
+ usePhantomSolanaPath: detectedUsePhantomSolanaPath,
2550
2588
  onProgress: (step) => {
2551
2589
  importSpinner.text = `${step.message} (${step.progress}%)`;
2552
2590
  }
@@ -2625,7 +2663,17 @@ async function executeCreateFromSeedphraseFast(ctx2, options) {
2625
2663
  throw new Error("Verification loop exited unexpectedly");
2626
2664
  }
2627
2665
  async function executeCreateFromSeedphraseSecure(ctx2, options) {
2628
- const { mnemonic, name, password, threshold, shares: totalShares, discoverChains, chains, signal } = options;
2666
+ const {
2667
+ mnemonic,
2668
+ name,
2669
+ password,
2670
+ threshold,
2671
+ shares: totalShares,
2672
+ discoverChains,
2673
+ chains,
2674
+ signal,
2675
+ usePhantomSolanaPath
2676
+ } = options;
2629
2677
  const validateSpinner = createSpinner("Validating seedphrase...");
2630
2678
  const validation = await ctx2.sdk.validateSeedphrase(mnemonic);
2631
2679
  if (!validation.valid) {
@@ -2636,19 +2684,26 @@ async function executeCreateFromSeedphraseSecure(ctx2, options) {
2636
2684
  throw new Error(validation.error || "Invalid mnemonic phrase");
2637
2685
  }
2638
2686
  validateSpinner.succeed(`Valid ${validation.wordCount}-word seedphrase`);
2687
+ let detectedUsePhantomSolanaPath = usePhantomSolanaPath;
2639
2688
  if (discoverChains) {
2640
2689
  const discoverSpinner = createSpinner("Discovering chains with balances...");
2641
2690
  try {
2642
- const discovered = await ctx2.sdk.discoverChainsFromSeedphrase(mnemonic, chains, (p) => {
2691
+ const { results: discovered, usePhantomSolanaPath: detectedPhantomPath } = await ctx2.sdk.discoverChainsFromSeedphrase(mnemonic, chains, (p) => {
2643
2692
  discoverSpinner.text = `Discovering: ${p.chain || "scanning"} (${p.chainsProcessed}/${p.chainsTotal})`;
2644
2693
  });
2645
2694
  const chainsWithBalance = discovered.filter((c) => c.hasBalance);
2646
2695
  discoverSpinner.succeed(`Found ${chainsWithBalance.length} chains with balances`);
2696
+ if (usePhantomSolanaPath === void 0) {
2697
+ detectedUsePhantomSolanaPath = detectedPhantomPath;
2698
+ }
2647
2699
  if (chainsWithBalance.length > 0 && !isSilent()) {
2648
2700
  info("\nChains with balances:");
2649
2701
  for (const result of chainsWithBalance) {
2650
2702
  info(` ${result.chain}: ${result.balance} ${result.symbol}`);
2651
2703
  }
2704
+ if (detectedUsePhantomSolanaPath) {
2705
+ info(" (Using Phantom wallet derivation path for Solana)");
2706
+ }
2652
2707
  info("");
2653
2708
  }
2654
2709
  } catch {
@@ -2666,6 +2721,7 @@ async function executeCreateFromSeedphraseSecure(ctx2, options) {
2666
2721
  threshold,
2667
2722
  // Don't pass discoverChains - CLI handles discovery above
2668
2723
  chains,
2724
+ usePhantomSolanaPath: detectedUsePhantomSolanaPath,
2669
2725
  onProgress: (step) => {
2670
2726
  importSpinner.text = `${step.message} (${step.progress}%)`;
2671
2727
  },
@@ -2770,6 +2826,82 @@ async function executeJoinSecure(ctx2, options) {
2770
2826
  throw err;
2771
2827
  }
2772
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
+ }
2773
2905
 
2774
2906
  // src/commands/swap.ts
2775
2907
  async function executeSwapChains(ctx2) {
@@ -2812,11 +2944,13 @@ async function executeSwapQuote(ctx2, options) {
2812
2944
  return quote;
2813
2945
  }
2814
2946
  const feeBalance = await vault.balance(options.fromChain);
2947
+ const discountTier = await vault.getDiscountTier();
2815
2948
  displaySwapPreview(quote, String(options.amount), quote.fromCoin.ticker, quote.toCoin.ticker, {
2816
2949
  fromDecimals: quote.fromCoin.decimals,
2817
2950
  toDecimals: quote.toCoin.decimals,
2818
2951
  feeDecimals: feeBalance.decimals,
2819
- feeSymbol: feeBalance.symbol
2952
+ feeSymbol: feeBalance.symbol,
2953
+ discountTier
2820
2954
  });
2821
2955
  info('\nTo execute this swap, use the "swap" command');
2822
2956
  return quote;
@@ -2840,12 +2974,14 @@ async function executeSwap(ctx2, options) {
2840
2974
  });
2841
2975
  quoteSpinner.succeed("Quote received");
2842
2976
  const feeBalance = await vault.balance(options.fromChain);
2977
+ const discountTier = await vault.getDiscountTier();
2843
2978
  if (!isJsonOutput()) {
2844
2979
  displaySwapPreview(quote, String(options.amount), quote.fromCoin.ticker, quote.toCoin.ticker, {
2845
2980
  fromDecimals: quote.fromCoin.decimals,
2846
2981
  toDecimals: quote.toCoin.decimals,
2847
2982
  feeDecimals: feeBalance.decimals,
2848
- feeSymbol: feeBalance.symbol
2983
+ feeSymbol: feeBalance.symbol,
2984
+ discountTier
2849
2985
  });
2850
2986
  }
2851
2987
  if (!options.yes && !isJsonOutput()) {
@@ -3078,6 +3214,104 @@ Address Book${options.chain ? ` (${options.chain})` : ""}:
3078
3214
  return allEntries;
3079
3215
  }
3080
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
+
3081
3315
  // src/interactive/completer.ts
3082
3316
  import { Chain as Chain5 } from "@vultisig/sdk";
3083
3317
  import fs2 from "fs";
@@ -3087,6 +3321,7 @@ var COMMANDS = [
3087
3321
  "vaults",
3088
3322
  "vault",
3089
3323
  "import",
3324
+ "delete",
3090
3325
  "create-from-seedphrase",
3091
3326
  "create",
3092
3327
  "join",
@@ -3134,13 +3369,16 @@ function createCompleter(ctx2) {
3134
3369
  return completeVaultName(ctx2, partial);
3135
3370
  }
3136
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
+ }
3137
3379
  const flag = parts[parts.length - 2]?.toLowerCase();
3138
3380
  if (flag === "--add" || flag === "--remove") {
3139
- const partial = parts[parts.length - 1] || "";
3140
- return completeChainName(partial);
3141
- }
3142
- if (parts[parts.length - 1]?.toLowerCase() === "--add" || parts[parts.length - 1]?.toLowerCase() === "--remove") {
3143
- return completeChainName("");
3381
+ return completeChainName(lastPart);
3144
3382
  }
3145
3383
  }
3146
3384
  if (["balance", "bal", "tokens", "send", "swap", "swap-quote"].includes(command) && parts.length === 2) {
@@ -3233,7 +3471,7 @@ function findChainByName(name) {
3233
3471
  }
3234
3472
 
3235
3473
  // src/interactive/event-buffer.ts
3236
- import chalk7 from "chalk";
3474
+ import chalk8 from "chalk";
3237
3475
  var EventBuffer = class {
3238
3476
  eventBuffer = [];
3239
3477
  isCommandRunning = false;
@@ -3273,17 +3511,17 @@ var EventBuffer = class {
3273
3511
  displayEvent(message, type) {
3274
3512
  switch (type) {
3275
3513
  case "success":
3276
- console.log(chalk7.green(message));
3514
+ console.log(chalk8.green(message));
3277
3515
  break;
3278
3516
  case "warning":
3279
- console.log(chalk7.yellow(message));
3517
+ console.log(chalk8.yellow(message));
3280
3518
  break;
3281
3519
  case "error":
3282
- console.error(chalk7.red(message));
3520
+ console.error(chalk8.red(message));
3283
3521
  break;
3284
3522
  case "info":
3285
3523
  default:
3286
- console.log(chalk7.blue(message));
3524
+ console.log(chalk8.blue(message));
3287
3525
  break;
3288
3526
  }
3289
3527
  }
@@ -3294,13 +3532,13 @@ var EventBuffer = class {
3294
3532
  if (this.eventBuffer.length === 0) {
3295
3533
  return;
3296
3534
  }
3297
- console.log(chalk7.gray("\n--- Background Events ---"));
3535
+ console.log(chalk8.gray("\n--- Background Events ---"));
3298
3536
  this.eventBuffer.forEach((event) => {
3299
3537
  const timeStr = event.timestamp.toLocaleTimeString();
3300
3538
  const message = `[${timeStr}] ${event.message}`;
3301
3539
  this.displayEvent(message, event.type);
3302
3540
  });
3303
- console.log(chalk7.gray("--- End Events ---\n"));
3541
+ console.log(chalk8.gray("--- End Events ---\n"));
3304
3542
  }
3305
3543
  /**
3306
3544
  * Setup all vault event listeners
@@ -3402,12 +3640,12 @@ var EventBuffer = class {
3402
3640
 
3403
3641
  // src/interactive/session.ts
3404
3642
  import { fiatCurrencies as fiatCurrencies3 } from "@vultisig/sdk";
3405
- import chalk9 from "chalk";
3643
+ import chalk10 from "chalk";
3406
3644
  import ora3 from "ora";
3407
3645
  import * as readline from "readline";
3408
3646
 
3409
3647
  // src/interactive/shell-commands.ts
3410
- import chalk8 from "chalk";
3648
+ import chalk9 from "chalk";
3411
3649
  import Table from "cli-table3";
3412
3650
  import inquirer6 from "inquirer";
3413
3651
  import ora2 from "ora";
@@ -3424,25 +3662,25 @@ function formatTimeRemaining(ms) {
3424
3662
  async function executeLock(ctx2) {
3425
3663
  const vault = ctx2.getActiveVault();
3426
3664
  if (!vault) {
3427
- console.log(chalk8.red("No active vault."));
3428
- 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.'));
3429
3667
  return;
3430
3668
  }
3431
3669
  ctx2.lockVault(vault.id);
3432
- console.log(chalk8.green("\n+ Vault locked"));
3433
- 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."));
3434
3672
  }
3435
3673
  async function executeUnlock(ctx2) {
3436
3674
  const vault = ctx2.getActiveVault();
3437
3675
  if (!vault) {
3438
- console.log(chalk8.red("No active vault."));
3439
- 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.'));
3440
3678
  return;
3441
3679
  }
3442
3680
  if (ctx2.isVaultUnlocked(vault.id)) {
3443
3681
  const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
3444
- console.log(chalk8.yellow("\nVault is already unlocked."));
3445
- 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)}`));
3446
3684
  return;
3447
3685
  }
3448
3686
  const { password } = await inquirer6.prompt([
@@ -3459,19 +3697,19 @@ async function executeUnlock(ctx2) {
3459
3697
  ctx2.cachePassword(vault.id, password);
3460
3698
  const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
3461
3699
  spinner.succeed("Vault unlocked");
3462
- console.log(chalk8.green(`
3700
+ console.log(chalk9.green(`
3463
3701
  + Vault unlocked for ${formatTimeRemaining(timeRemaining)}`));
3464
3702
  } catch (err) {
3465
3703
  spinner.fail("Failed to unlock vault");
3466
- console.error(chalk8.red(`
3704
+ console.error(chalk9.red(`
3467
3705
  x ${err.message}`));
3468
3706
  }
3469
3707
  }
3470
3708
  async function executeStatus(ctx2) {
3471
3709
  const vault = ctx2.getActiveVault();
3472
3710
  if (!vault) {
3473
- console.log(chalk8.red("No active vault."));
3474
- 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.'));
3475
3713
  return;
3476
3714
  }
3477
3715
  const isUnlocked = ctx2.isVaultUnlocked(vault.id);
@@ -3502,30 +3740,30 @@ async function executeStatus(ctx2) {
3502
3740
  displayStatus(status);
3503
3741
  }
3504
3742
  function displayStatus(status) {
3505
- console.log(chalk8.cyan("\n+----------------------------------------+"));
3506
- console.log(chalk8.cyan("| Vault Status |"));
3507
- console.log(chalk8.cyan("+----------------------------------------+\n"));
3508
- console.log(chalk8.bold("Vault:"));
3509
- 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)}`);
3510
3748
  console.log(` ID: ${status.id}`);
3511
- console.log(` Type: ${chalk8.yellow(status.type)}`);
3512
- console.log(chalk8.bold("\nSecurity:"));
3749
+ console.log(` Type: ${chalk9.yellow(status.type)}`);
3750
+ console.log(chalk9.bold("\nSecurity:"));
3513
3751
  if (status.isUnlocked) {
3514
- console.log(` Status: ${chalk8.green("Unlocked")} ${chalk8.green("\u{1F513}")}`);
3752
+ console.log(` Status: ${chalk9.green("Unlocked")} ${chalk9.green("\u{1F513}")}`);
3515
3753
  console.log(` Expires: ${status.timeRemainingFormatted}`);
3516
3754
  } else {
3517
- console.log(` Status: ${chalk8.yellow("Locked")} ${chalk8.yellow("\u{1F512}")}`);
3755
+ console.log(` Status: ${chalk9.yellow("Locked")} ${chalk9.yellow("\u{1F512}")}`);
3518
3756
  }
3519
- console.log(` Encrypted: ${status.isEncrypted ? chalk8.green("Yes") : chalk8.gray("No")}`);
3520
- console.log(` Backed Up: ${status.isBackedUp ? chalk8.green("Yes") : chalk8.yellow("No")}`);
3521
- 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:"));
3522
3760
  console.log(` Library: ${status.libType}`);
3523
- console.log(` Threshold: ${chalk8.cyan(status.threshold)} of ${chalk8.cyan(status.totalSigners)}`);
3524
- 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:"));
3525
3763
  status.availableSigningModes.forEach((mode) => {
3526
3764
  console.log(` - ${mode}`);
3527
3765
  });
3528
- console.log(chalk8.bold("\nDetails:"));
3766
+ console.log(chalk9.bold("\nDetails:"));
3529
3767
  console.log(` Chains: ${status.chains}`);
3530
3768
  console.log(` Currency: ${status.currency.toUpperCase()}`);
3531
3769
  console.log(` Created: ${new Date(status.createdAt).toLocaleString()}`);
@@ -3534,7 +3772,7 @@ function displayStatus(status) {
3534
3772
  }
3535
3773
  function showHelp() {
3536
3774
  const table = new Table({
3537
- head: [chalk8.bold("Available Commands")],
3775
+ head: [chalk9.bold("Available Commands")],
3538
3776
  colWidths: [50],
3539
3777
  chars: {
3540
3778
  mid: "",
@@ -3548,38 +3786,39 @@ function showHelp() {
3548
3786
  }
3549
3787
  });
3550
3788
  table.push(
3551
- [chalk8.bold("Vault Management:")],
3789
+ [chalk9.bold("Vault Management:")],
3552
3790
  [" vaults - List all vaults"],
3553
3791
  [" vault <name> - Switch to vault"],
3554
3792
  [" import <file> - Import vault from file"],
3793
+ [" delete [name] - Delete vault"],
3555
3794
  [" create - Create new vault"],
3556
3795
  [" info - Show vault details"],
3557
3796
  [" export [path] - Export vault to file"],
3558
3797
  [""],
3559
- [chalk8.bold("Wallet Operations:")],
3798
+ [chalk9.bold("Wallet Operations:")],
3560
3799
  [" balance [chain] - Show balances"],
3561
3800
  [" send <chain> <to> <amount> - Send transaction"],
3562
3801
  [" portfolio [-c usd] - Show portfolio value"],
3563
3802
  [" addresses - Show all addresses"],
3564
- [" chains [--add/--remove] - Manage chains"],
3803
+ [" chains [--add/--remove/--add-all] - Manage chains"],
3565
3804
  [" tokens <chain> - Manage tokens"],
3566
3805
  [""],
3567
- [chalk8.bold("Swap Operations:")],
3806
+ [chalk9.bold("Swap Operations:")],
3568
3807
  [" swap-chains - List swap-enabled chains"],
3569
3808
  [" swap-quote <from> <to> <amount> - Get quote"],
3570
3809
  [" swap <from> <to> <amount> - Execute swap"],
3571
3810
  [""],
3572
- [chalk8.bold("Session Commands (shell only):")],
3811
+ [chalk9.bold("Session Commands (shell only):")],
3573
3812
  [" lock - Lock vault"],
3574
3813
  [" unlock - Unlock vault"],
3575
3814
  [" status - Show vault status"],
3576
3815
  [""],
3577
- [chalk8.bold("Settings:")],
3816
+ [chalk9.bold("Settings:")],
3578
3817
  [" currency [code] - View/set currency"],
3579
3818
  [" server - Check server status"],
3580
3819
  [" address-book - Manage saved addresses"],
3581
3820
  [""],
3582
- [chalk8.bold("Help & Navigation:")],
3821
+ [chalk9.bold("Help & Navigation:")],
3583
3822
  [" help, ? - Show this help"],
3584
3823
  [" .clear - Clear screen"],
3585
3824
  [" .exit - Exit shell"]
@@ -3717,12 +3956,12 @@ var ShellSession = class {
3717
3956
  */
3718
3957
  async start() {
3719
3958
  console.clear();
3720
- console.log(chalk9.cyan.bold("\n=============================================="));
3721
- console.log(chalk9.cyan.bold(" Vultisig Interactive Shell"));
3722
- 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"));
3723
3962
  await this.loadAllVaults();
3724
3963
  this.displayVaultList();
3725
- 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'));
3726
3965
  this.promptLoop().catch(() => {
3727
3966
  });
3728
3967
  }
@@ -3756,12 +3995,12 @@ var ShellSession = class {
3756
3995
  const now = Date.now();
3757
3996
  if (now - this.lastSigintTime < this.DOUBLE_CTRL_C_TIMEOUT) {
3758
3997
  rl.close();
3759
- console.log(chalk9.yellow("\nGoodbye!"));
3998
+ console.log(chalk10.yellow("\nGoodbye!"));
3760
3999
  this.ctx.dispose();
3761
4000
  process.exit(0);
3762
4001
  }
3763
4002
  this.lastSigintTime = now;
3764
- console.log(chalk9.yellow("\n(Press Ctrl+C again to exit)"));
4003
+ console.log(chalk10.yellow("\n(Press Ctrl+C again to exit)"));
3765
4004
  rl.close();
3766
4005
  resolve("");
3767
4006
  });
@@ -3856,7 +4095,7 @@ var ShellSession = class {
3856
4095
  stopAllSpinners();
3857
4096
  process.stdout.write("\x1B[?25h");
3858
4097
  process.stdout.write("\r\x1B[K");
3859
- console.log(chalk9.yellow("\nCancelling operation..."));
4098
+ console.log(chalk10.yellow("\nCancelling operation..."));
3860
4099
  };
3861
4100
  const cleanup = () => {
3862
4101
  process.removeListener("SIGINT", onSigint);
@@ -3893,10 +4132,10 @@ var ShellSession = class {
3893
4132
  stopAllSpinners();
3894
4133
  process.stdout.write("\x1B[?25h");
3895
4134
  process.stdout.write("\r\x1B[K");
3896
- console.log(chalk9.yellow("Operation cancelled"));
4135
+ console.log(chalk10.yellow("Operation cancelled"));
3897
4136
  return;
3898
4137
  }
3899
- console.error(chalk9.red(`
4138
+ console.error(chalk10.red(`
3900
4139
  Error: ${error2.message}`));
3901
4140
  }
3902
4141
  }
@@ -3929,11 +4168,14 @@ Error: ${error2.message}`));
3929
4168
  break;
3930
4169
  case "rename":
3931
4170
  if (args.length === 0) {
3932
- console.log(chalk9.yellow("Usage: rename <newName>"));
4171
+ console.log(chalk10.yellow("Usage: rename <newName>"));
3933
4172
  return;
3934
4173
  }
3935
4174
  await executeRename(this.ctx, args.join(" "));
3936
4175
  break;
4176
+ case "delete":
4177
+ await this.deleteVault(args);
4178
+ break;
3937
4179
  // Balance commands
3938
4180
  case "balance":
3939
4181
  case "bal":
@@ -3999,41 +4241,41 @@ Error: ${error2.message}`));
3999
4241
  // Exit
4000
4242
  case "exit":
4001
4243
  case "quit":
4002
- console.log(chalk9.yellow("\nGoodbye!"));
4244
+ console.log(chalk10.yellow("\nGoodbye!"));
4003
4245
  this.ctx.dispose();
4004
4246
  process.exit(0);
4005
4247
  break;
4006
4248
  // eslint requires break even after process.exit
4007
4249
  default:
4008
- console.log(chalk9.yellow(`Unknown command: ${command}`));
4009
- 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'));
4010
4252
  break;
4011
4253
  }
4012
4254
  }
4013
4255
  // ===== Command Helpers =====
4014
4256
  async switchVault(args) {
4015
4257
  if (args.length === 0) {
4016
- console.log(chalk9.yellow("Usage: vault <name>"));
4017
- 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'));
4018
4260
  return;
4019
4261
  }
4020
4262
  const vaultName = args.join(" ");
4021
4263
  const vault = this.ctx.findVaultByName(vaultName);
4022
4264
  if (!vault) {
4023
- console.log(chalk9.red(`Vault not found: ${vaultName}`));
4024
- 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'));
4025
4267
  return;
4026
4268
  }
4027
4269
  await this.ctx.setActiveVault(vault);
4028
- console.log(chalk9.green(`
4270
+ console.log(chalk10.green(`
4029
4271
  + Switched to: ${vault.name}`));
4030
4272
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
4031
- const status = isUnlocked ? chalk9.green("Unlocked") : chalk9.yellow("Locked");
4273
+ const status = isUnlocked ? chalk10.green("Unlocked") : chalk10.yellow("Locked");
4032
4274
  console.log(`Status: ${status}`);
4033
4275
  }
4034
4276
  async importVault(args) {
4035
4277
  if (args.length === 0) {
4036
- console.log(chalk9.yellow("Usage: import <file>"));
4278
+ console.log(chalk10.yellow("Usage: import <file>"));
4037
4279
  return;
4038
4280
  }
4039
4281
  const filePath = args.join(" ");
@@ -4041,48 +4283,52 @@ Error: ${error2.message}`));
4041
4283
  this.ctx.addVault(vault);
4042
4284
  this.eventBuffer.setupVaultListeners(vault);
4043
4285
  }
4286
+ async deleteVault(args) {
4287
+ const vaultIdOrName = args.join(" ") || void 0;
4288
+ await executeDelete(this.ctx, { vaultId: vaultIdOrName });
4289
+ }
4044
4290
  async createVault(args) {
4045
4291
  const type = args[0]?.toLowerCase();
4046
4292
  if (!type || type !== "fast" && type !== "secure") {
4047
- console.log(chalk9.yellow("Usage: create <fast|secure>"));
4048
- console.log(chalk9.gray(" create fast - Create a fast vault (server-assisted 2-of-2)"));
4049
- 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)"));
4050
4296
  return;
4051
4297
  }
4052
4298
  let vault;
4053
4299
  if (type === "fast") {
4054
4300
  const name = await this.prompt("Vault name");
4055
4301
  if (!name) {
4056
- console.log(chalk9.red("Name is required"));
4302
+ console.log(chalk10.red("Name is required"));
4057
4303
  return;
4058
4304
  }
4059
4305
  const password = await this.promptPassword("Vault password");
4060
4306
  if (!password) {
4061
- console.log(chalk9.red("Password is required"));
4307
+ console.log(chalk10.red("Password is required"));
4062
4308
  return;
4063
4309
  }
4064
4310
  const email = await this.prompt("Email for verification");
4065
4311
  if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
4066
- console.log(chalk9.red("Valid email is required"));
4312
+ console.log(chalk10.red("Valid email is required"));
4067
4313
  return;
4068
4314
  }
4069
4315
  vault = await this.withCancellation((signal) => executeCreateFast(this.ctx, { name, password, email, signal }));
4070
4316
  } else {
4071
4317
  const name = await this.prompt("Vault name");
4072
4318
  if (!name) {
4073
- console.log(chalk9.red("Name is required"));
4319
+ console.log(chalk10.red("Name is required"));
4074
4320
  return;
4075
4321
  }
4076
4322
  const sharesStr = await this.prompt("Total shares (devices)", "3");
4077
4323
  const shares = parseInt(sharesStr, 10);
4078
4324
  if (isNaN(shares) || shares < 2) {
4079
- console.log(chalk9.red("Must have at least 2 shares"));
4325
+ console.log(chalk10.red("Must have at least 2 shares"));
4080
4326
  return;
4081
4327
  }
4082
4328
  const thresholdStr = await this.prompt("Signing threshold", "2");
4083
4329
  const threshold = parseInt(thresholdStr, 10);
4084
4330
  if (isNaN(threshold) || threshold < 1 || threshold > shares) {
4085
- console.log(chalk9.red(`Threshold must be between 1 and ${shares}`));
4331
+ console.log(chalk10.red(`Threshold must be between 1 and ${shares}`));
4086
4332
  return;
4087
4333
  }
4088
4334
  const password = await this.promptPassword("Vault password (optional, press Enter to skip)");
@@ -4104,37 +4350,37 @@ Error: ${error2.message}`));
4104
4350
  async importSeedphrase(args) {
4105
4351
  const type = args[0]?.toLowerCase();
4106
4352
  if (!type || type !== "fast" && type !== "secure") {
4107
- console.log(chalk9.cyan("Usage: create-from-seedphrase <fast|secure>"));
4108
- console.log(chalk9.gray(" fast - Import with VultiServer (2-of-2)"));
4109
- 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)"));
4110
4356
  return;
4111
4357
  }
4112
- 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):"));
4113
4359
  const mnemonic = await this.promptPassword("Seedphrase");
4114
4360
  const validation = await this.ctx.sdk.validateSeedphrase(mnemonic);
4115
4361
  if (!validation.valid) {
4116
- console.log(chalk9.red(`Invalid seedphrase: ${validation.error}`));
4362
+ console.log(chalk10.red(`Invalid seedphrase: ${validation.error}`));
4117
4363
  if (validation.invalidWords?.length) {
4118
- console.log(chalk9.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
4364
+ console.log(chalk10.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
4119
4365
  }
4120
4366
  return;
4121
4367
  }
4122
- console.log(chalk9.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
4368
+ console.log(chalk10.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
4123
4369
  let vault;
4124
4370
  if (type === "fast") {
4125
4371
  const name = await this.prompt("Vault name");
4126
4372
  if (!name) {
4127
- console.log(chalk9.red("Name is required"));
4373
+ console.log(chalk10.red("Name is required"));
4128
4374
  return;
4129
4375
  }
4130
4376
  const password = await this.promptPassword("Vault password");
4131
4377
  if (!password) {
4132
- console.log(chalk9.red("Password is required"));
4378
+ console.log(chalk10.red("Password is required"));
4133
4379
  return;
4134
4380
  }
4135
4381
  const email = await this.prompt("Email for verification");
4136
4382
  if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
4137
- console.log(chalk9.red("Valid email is required"));
4383
+ console.log(chalk10.red("Valid email is required"));
4138
4384
  return;
4139
4385
  }
4140
4386
  const discoverStr = await this.prompt("Discover chains with balances? (y/n)", "y");
@@ -4152,19 +4398,19 @@ Error: ${error2.message}`));
4152
4398
  } else {
4153
4399
  const name = await this.prompt("Vault name");
4154
4400
  if (!name) {
4155
- console.log(chalk9.red("Name is required"));
4401
+ console.log(chalk10.red("Name is required"));
4156
4402
  return;
4157
4403
  }
4158
4404
  const sharesStr = await this.prompt("Total shares (devices)", "3");
4159
4405
  const shares = parseInt(sharesStr, 10);
4160
4406
  if (isNaN(shares) || shares < 2) {
4161
- console.log(chalk9.red("Must have at least 2 shares"));
4407
+ console.log(chalk10.red("Must have at least 2 shares"));
4162
4408
  return;
4163
4409
  }
4164
4410
  const thresholdStr = await this.prompt("Signing threshold", "2");
4165
4411
  const threshold = parseInt(thresholdStr, 10);
4166
4412
  if (isNaN(threshold) || threshold < 1 || threshold > shares) {
4167
- console.log(chalk9.red(`Threshold must be between 1 and ${shares}`));
4413
+ console.log(chalk10.red(`Threshold must be between 1 and ${shares}`));
4168
4414
  return;
4169
4415
  }
4170
4416
  const password = await this.promptPassword("Vault password (optional, Enter to skip)");
@@ -4188,12 +4434,14 @@ Error: ${error2.message}`));
4188
4434
  }
4189
4435
  }
4190
4436
  async runBalance(args) {
4191
- const chainStr = args[0];
4437
+ const chainStr = args.find((arg) => !arg.startsWith("-"));
4192
4438
  const includeTokens = args.includes("-t") || args.includes("--tokens");
4439
+ const raw = args.includes("--raw");
4193
4440
  await this.withCancellation(
4194
4441
  () => executeBalance(this.ctx, {
4195
4442
  chain: chainStr ? findChainByName(chainStr) || chainStr : void 0,
4196
- includeTokens
4443
+ includeTokens,
4444
+ raw
4197
4445
  })
4198
4446
  );
4199
4447
  }
@@ -4206,15 +4454,16 @@ Error: ${error2.message}`));
4206
4454
  }
4207
4455
  }
4208
4456
  if (!fiatCurrencies3.includes(currency)) {
4209
- console.log(chalk9.red(`Invalid currency: ${currency}`));
4210
- 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(", ")}`));
4211
4459
  return;
4212
4460
  }
4213
- await this.withCancellation(() => executePortfolio(this.ctx, { currency }));
4461
+ const raw = args.includes("--raw");
4462
+ await this.withCancellation(() => executePortfolio(this.ctx, { currency, raw }));
4214
4463
  }
4215
4464
  async runSend(args) {
4216
4465
  if (args.length < 3) {
4217
- 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>]"));
4218
4467
  return;
4219
4468
  }
4220
4469
  const [chainStr, to, amount, ...rest] = args;
@@ -4234,7 +4483,7 @@ Error: ${error2.message}`));
4234
4483
  await this.withAbortHandler((signal) => executeSend(this.ctx, { chain, to, amount, tokenId, memo, signal }));
4235
4484
  } catch (err) {
4236
4485
  if (err.message === "Transaction cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
4237
- console.log(chalk9.yellow("\nTransaction cancelled"));
4486
+ console.log(chalk10.yellow("\nTransaction cancelled"));
4238
4487
  return;
4239
4488
  }
4240
4489
  throw err;
@@ -4243,12 +4492,15 @@ Error: ${error2.message}`));
4243
4492
  async runChains(args) {
4244
4493
  let addChain;
4245
4494
  let removeChain;
4495
+ let addAll = false;
4246
4496
  for (let i = 0; i < args.length; i++) {
4247
- 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) {
4248
4500
  const chain = findChainByName(args[i + 1]);
4249
4501
  if (!chain) {
4250
- console.log(chalk9.red(`Unknown chain: ${args[i + 1]}`));
4251
- 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"));
4252
4504
  return;
4253
4505
  }
4254
4506
  addChain = chain;
@@ -4256,19 +4508,19 @@ Error: ${error2.message}`));
4256
4508
  } else if (args[i] === "--remove" && i + 1 < args.length) {
4257
4509
  const chain = findChainByName(args[i + 1]);
4258
4510
  if (!chain) {
4259
- console.log(chalk9.red(`Unknown chain: ${args[i + 1]}`));
4260
- 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"));
4261
4513
  return;
4262
4514
  }
4263
4515
  removeChain = chain;
4264
4516
  i++;
4265
4517
  }
4266
4518
  }
4267
- await executeChains(this.ctx, { add: addChain, remove: removeChain });
4519
+ await executeChains(this.ctx, { add: addChain, remove: removeChain, addAll });
4268
4520
  }
4269
4521
  async runTokens(args) {
4270
4522
  if (args.length === 0) {
4271
- console.log(chalk9.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
4523
+ console.log(chalk10.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
4272
4524
  return;
4273
4525
  }
4274
4526
  const chainStr = args[0];
@@ -4289,7 +4541,7 @@ Error: ${error2.message}`));
4289
4541
  async runSwapQuote(args) {
4290
4542
  if (args.length < 3) {
4291
4543
  console.log(
4292
- 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>]")
4293
4545
  );
4294
4546
  return;
4295
4547
  }
@@ -4313,7 +4565,7 @@ Error: ${error2.message}`));
4313
4565
  async runSwap(args) {
4314
4566
  if (args.length < 3) {
4315
4567
  console.log(
4316
- chalk9.yellow(
4568
+ chalk10.yellow(
4317
4569
  "Usage: swap <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>] [--slippage <pct>]"
4318
4570
  )
4319
4571
  );
@@ -4344,7 +4596,7 @@ Error: ${error2.message}`));
4344
4596
  );
4345
4597
  } catch (err) {
4346
4598
  if (err.message === "Swap cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
4347
- console.log(chalk9.yellow("\nSwap cancelled"));
4599
+ console.log(chalk10.yellow("\nSwap cancelled"));
4348
4600
  return;
4349
4601
  }
4350
4602
  throw err;
@@ -4406,24 +4658,24 @@ Error: ${error2.message}`));
4406
4658
  }
4407
4659
  getPrompt() {
4408
4660
  const vault = this.ctx.getActiveVault();
4409
- if (!vault) return chalk9.cyan("wallet> ");
4661
+ if (!vault) return chalk10.cyan("wallet> ");
4410
4662
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
4411
- const status = isUnlocked ? chalk9.green("\u{1F513}") : chalk9.yellow("\u{1F512}");
4412
- 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}> `);
4413
4665
  }
4414
4666
  displayVaultList() {
4415
4667
  const vaults = Array.from(this.ctx.getVaults().values());
4416
4668
  const activeVault = this.ctx.getActiveVault();
4417
4669
  if (vaults.length === 0) {
4418
- 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'));
4419
4671
  return;
4420
4672
  }
4421
- console.log(chalk9.cyan("Loaded Vaults:\n"));
4673
+ console.log(chalk10.cyan("Loaded Vaults:\n"));
4422
4674
  vaults.forEach((vault) => {
4423
4675
  const isActive = vault.id === activeVault?.id;
4424
4676
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
4425
- const activeMarker = isActive ? chalk9.green(" (active)") : "";
4426
- 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}");
4427
4679
  console.log(` ${lockIcon} ${vault.name}${activeMarker} - ${vault.type}`);
4428
4680
  });
4429
4681
  console.log();
@@ -4431,10 +4683,10 @@ Error: ${error2.message}`));
4431
4683
  };
4432
4684
 
4433
4685
  // src/lib/errors.ts
4434
- import chalk10 from "chalk";
4686
+ import chalk11 from "chalk";
4435
4687
 
4436
4688
  // src/lib/version.ts
4437
- import chalk11 from "chalk";
4689
+ import chalk12 from "chalk";
4438
4690
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
4439
4691
  import { homedir } from "os";
4440
4692
  import { join } from "path";
@@ -4442,7 +4694,7 @@ var cachedVersion = null;
4442
4694
  function getVersion() {
4443
4695
  if (cachedVersion) return cachedVersion;
4444
4696
  if (true) {
4445
- cachedVersion = "0.3.0";
4697
+ cachedVersion = "0.4.0";
4446
4698
  return cachedVersion;
4447
4699
  }
4448
4700
  try {
@@ -4539,7 +4791,7 @@ function formatVersionShort() {
4539
4791
  }
4540
4792
  function formatVersionDetailed() {
4541
4793
  const lines = [];
4542
- lines.push(chalk11.bold(`Vultisig CLI v${getVersion()}`));
4794
+ lines.push(chalk12.bold(`Vultisig CLI v${getVersion()}`));
4543
4795
  lines.push("");
4544
4796
  lines.push(` Node.js: ${process.version}`);
4545
4797
  lines.push(` Platform: ${process.platform}-${process.arch}`);
@@ -5001,7 +5253,7 @@ async function promptQrPayload() {
5001
5253
  ]);
5002
5254
  return answer.qrPayload.trim();
5003
5255
  }
5004
- 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)").action(
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(
5005
5257
  withExit(
5006
5258
  async (options) => {
5007
5259
  const context = await init(program.opts().vault);
@@ -5028,12 +5280,13 @@ createFromSeedphraseCmd.command("fast").description("Create FastVault from seedp
5028
5280
  password: options.password,
5029
5281
  email: options.email,
5030
5282
  discoverChains: options.discoverChains,
5031
- chains
5283
+ chains,
5284
+ usePhantomSolanaPath: options.usePhantomSolanaPath
5032
5285
  });
5033
5286
  }
5034
5287
  )
5035
5288
  );
5036
- 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)").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(
5037
5290
  withExit(
5038
5291
  async (options) => {
5039
5292
  const context = await init(program.opts().vault);
@@ -5061,7 +5314,8 @@ createFromSeedphraseCmd.command("secure").description("Create SecureVault from s
5061
5314
  threshold: parseInt(options.threshold, 10),
5062
5315
  shares: parseInt(options.shares, 10),
5063
5316
  discoverChains: options.discoverChains,
5064
- chains
5317
+ chains,
5318
+ usePhantomSolanaPath: options.usePhantomSolanaPath
5065
5319
  });
5066
5320
  }
5067
5321
  )
@@ -5106,12 +5360,13 @@ program.command("verify <vaultId>").description("Verify vault with email verific
5106
5360
  }
5107
5361
  )
5108
5362
  );
5109
- 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(
5110
5364
  withExit(async (chainStr, options) => {
5111
5365
  const context = await init(program.opts().vault);
5112
5366
  await executeBalance(context, {
5113
5367
  chain: chainStr ? findChainByName(chainStr) || chainStr : void 0,
5114
- includeTokens: options.tokens
5368
+ includeTokens: options.tokens,
5369
+ raw: options.raw
5115
5370
  });
5116
5371
  })
5117
5372
  );
@@ -5158,10 +5413,13 @@ program.command("broadcast").description("Broadcast a pre-signed raw transaction
5158
5413
  });
5159
5414
  })
5160
5415
  );
5161
- 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(
5162
5417
  withExit(async (options) => {
5163
5418
  const context = await init(program.opts().vault);
5164
- await executePortfolio(context, { currency: options.currency.toLowerCase() });
5419
+ await executePortfolio(context, {
5420
+ currency: options.currency.toLowerCase(),
5421
+ raw: options.raw
5422
+ });
5165
5423
  })
5166
5424
  );
5167
5425
  program.command("currency [newCurrency]").description("View or set the vault currency preference").action(
@@ -5176,6 +5434,12 @@ program.command("server").description("Check server connectivity and status").ac
5176
5434
  await executeServer(context);
5177
5435
  })
5178
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
+ );
5179
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(
5180
5444
  withExit(async (path3, options) => {
5181
5445
  const context = await init(program.opts().vault, options.password);
@@ -5204,11 +5468,12 @@ program.command("address-book").description("Manage address book entries").optio
5204
5468
  });
5205
5469
  })
5206
5470
  );
5207
- 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(
5208
5472
  withExit(async (options) => {
5209
5473
  const context = await init(program.opts().vault);
5210
5474
  await executeChains(context, {
5211
5475
  add: options.add ? findChainByName(options.add) || options.add : void 0,
5476
+ addAll: options.addAll,
5212
5477
  remove: options.remove ? findChainByName(options.remove) || options.remove : void 0
5213
5478
  });
5214
5479
  })
@@ -5237,6 +5502,15 @@ program.command("info").description("Show detailed vault information").action(
5237
5502
  await executeInfo(context);
5238
5503
  })
5239
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
+ );
5240
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(
5241
5515
  withExit(
5242
5516
  async (chainStr, options) => {
@@ -5303,8 +5577,8 @@ program.command("version").description("Show detailed version information").acti
5303
5577
  const result = await checkForUpdates();
5304
5578
  if (result?.updateAvailable && result.latestVersion) {
5305
5579
  info("");
5306
- info(chalk12.yellow(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
5307
- 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`));
5308
5582
  }
5309
5583
  })
5310
5584
  );
@@ -5313,22 +5587,22 @@ program.command("update").description("Check for updates and show update command
5313
5587
  info("Checking for updates...");
5314
5588
  const result = await checkForUpdates();
5315
5589
  if (!result) {
5316
- printResult(chalk12.gray("Update checking is disabled"));
5590
+ printResult(chalk13.gray("Update checking is disabled"));
5317
5591
  return;
5318
5592
  }
5319
5593
  if (result.updateAvailable && result.latestVersion) {
5320
5594
  printResult("");
5321
- printResult(chalk12.green(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
5595
+ printResult(chalk13.green(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
5322
5596
  printResult("");
5323
5597
  if (options.check) {
5324
5598
  printResult(`Run "${getUpdateCommand()}" to update`);
5325
5599
  } else {
5326
5600
  const updateCmd = getUpdateCommand();
5327
5601
  printResult(`To update, run:`);
5328
- printResult(chalk12.cyan(` ${updateCmd}`));
5602
+ printResult(chalk13.cyan(` ${updateCmd}`));
5329
5603
  }
5330
5604
  } else {
5331
- printResult(chalk12.green(`You're on the latest version (${result.currentVersion})`));
5605
+ printResult(chalk13.green(`You're on the latest version (${result.currentVersion})`));
5332
5606
  }
5333
5607
  })
5334
5608
  );