@vultisig/cli 0.3.0 → 0.5.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
@@ -1042,14 +1042,14 @@ var require_main = __commonJS({
1042
1042
  cb = opts;
1043
1043
  opts = {};
1044
1044
  }
1045
- var qrcode4 = new QRCode(-1, this.error);
1046
- qrcode4.addData(input);
1047
- qrcode4.make();
1045
+ var qrcode5 = new QRCode(-1, this.error);
1046
+ qrcode5.addData(input);
1047
+ qrcode5.make();
1048
1048
  var output = "";
1049
1049
  if (opts && opts.small) {
1050
1050
  var BLACK = true, WHITE = false;
1051
- var moduleCount = qrcode4.getModuleCount();
1052
- var moduleData = qrcode4.modules.slice();
1051
+ var moduleCount = qrcode5.getModuleCount();
1052
+ var moduleData = qrcode5.modules.slice();
1053
1053
  var oddRow = moduleCount % 2 === 1;
1054
1054
  if (oddRow) {
1055
1055
  moduleData.push(fill(moduleCount, WHITE));
@@ -1082,9 +1082,9 @@ var require_main = __commonJS({
1082
1082
  output += borderBottom;
1083
1083
  }
1084
1084
  } else {
1085
- var border = repeat(white).times(qrcode4.getModuleCount() + 3);
1085
+ var border = repeat(white).times(qrcode5.getModuleCount() + 3);
1086
1086
  output += border + "\n";
1087
- qrcode4.modules.forEach(function(row2) {
1087
+ qrcode5.modules.forEach(function(row2) {
1088
1088
  output += white;
1089
1089
  output += row2.map(toCell).join("");
1090
1090
  output += white + "\n";
@@ -1103,10 +1103,10 @@ var require_main = __commonJS({
1103
1103
 
1104
1104
  // src/index.ts
1105
1105
  import "dotenv/config";
1106
- import { parseKeygenQR, Vultisig as Vultisig4 } from "@vultisig/sdk";
1107
- import chalk12 from "chalk";
1106
+ import { promises as fs3 } from "node:fs";
1107
+ import { parseKeygenQR, Vultisig as Vultisig5 } from "@vultisig/sdk";
1108
+ import chalk13 from "chalk";
1108
1109
  import { program } from "commander";
1109
- import { promises as fs3 } from "fs";
1110
1110
  import inquirer8 from "inquirer";
1111
1111
 
1112
1112
  // src/core/command-context.ts
@@ -1446,25 +1446,25 @@ 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
+ printResult(` Amount: ${balance.formattedAmount} ${balance.symbol}`);
1453
1453
  if (balance.fiatValue && balance.fiatCurrency) {
1454
1454
  printResult(` Value: ${balance.fiatValue.toFixed(2)} ${balance.fiatCurrency}`);
1455
1455
  }
1456
1456
  }
1457
- function displayBalancesTable(balances) {
1457
+ function displayBalancesTable(balances, _raw = false) {
1458
1458
  printResult(chalk2.cyan("\nPortfolio Balances:\n"));
1459
1459
  const tableData = Object.entries(balances).map(([chain, balance]) => ({
1460
1460
  Chain: chain,
1461
- Amount: balance.amount,
1461
+ Amount: balance.formattedAmount,
1462
1462
  Symbol: balance.symbol,
1463
1463
  Value: balance.fiatValue && balance.fiatCurrency ? `${balance.fiatValue.toFixed(2)} ${balance.fiatCurrency}` : "N/A"
1464
1464
  }));
1465
1465
  printTable(tableData);
1466
1466
  }
1467
- function displayPortfolio(portfolio, currency) {
1467
+ function displayPortfolio(portfolio, currency, _raw = false) {
1468
1468
  const currencyName = fiatCurrencyNameRecord[currency];
1469
1469
  printResult(chalk2.cyan("\n+----------------------------------------+"));
1470
1470
  printResult(chalk2.cyan(`| Portfolio Total Value (${currencyName}) |`));
@@ -1475,7 +1475,7 @@ function displayPortfolio(portfolio, currency) {
1475
1475
  printResult(chalk2.bold("Chain Breakdown:\n"));
1476
1476
  const table = portfolio.chainBalances.map(({ chain, balance, value }) => ({
1477
1477
  Chain: chain,
1478
- Amount: balance.amount,
1478
+ Amount: balance.formattedAmount,
1479
1479
  Symbol: balance.symbol,
1480
1480
  Value: value ? `${value.amount} ${value.currency.toUpperCase()}` : "N/A"
1481
1481
  }));
@@ -1571,7 +1571,7 @@ async function confirmTransaction() {
1571
1571
  function setupVaultEvents(vault) {
1572
1572
  vault.on("balanceUpdated", ({ chain, balance, tokenId }) => {
1573
1573
  const asset = tokenId ? `${balance.symbol} token` : balance.symbol;
1574
- info(chalk2.blue(`i Balance updated for ${chain} (${asset}): ${balance.amount}`));
1574
+ info(chalk2.blue(`i Balance updated for ${chain} (${asset}): ${balance.formattedAmount}`));
1575
1575
  });
1576
1576
  vault.on("transactionBroadcast", ({ chain, txHash }) => {
1577
1577
  info(chalk2.green(`+ Transaction broadcast on ${chain}`));
@@ -1634,6 +1634,10 @@ function displaySwapPreview(quote, fromAmount, fromSymbol, toSymbol, options) {
1634
1634
  if (quote.feesFiat?.affiliate) {
1635
1635
  printResult(` (~$${quote.feesFiat.affiliate.toFixed(2)})`);
1636
1636
  }
1637
+ if (options.discountTier) {
1638
+ const tierDisplay = options.discountTier.charAt(0).toUpperCase() + options.discountTier.slice(1);
1639
+ printResult(chalk2.green(` (${tierDisplay} tier discount applied)`));
1640
+ }
1637
1641
  }
1638
1642
  printResult(` Total: ${totalFeeFormatted} ${options.feeSymbol}`);
1639
1643
  if (quote.feesFiat) {
@@ -1688,6 +1692,7 @@ async function confirmSwap() {
1688
1692
  async function executeBalance(ctx2, options = {}) {
1689
1693
  const vault = await ctx2.ensureActiveVault();
1690
1694
  const spinner = createSpinner("Loading balances...");
1695
+ const raw = options.raw ?? false;
1691
1696
  if (options.chain) {
1692
1697
  const balance = await vault.balance(options.chain);
1693
1698
  spinner.succeed("Balance loaded");
@@ -1695,7 +1700,7 @@ async function executeBalance(ctx2, options = {}) {
1695
1700
  outputJson({ chain: options.chain, balance });
1696
1701
  return;
1697
1702
  }
1698
- displayBalance(options.chain, balance);
1703
+ displayBalance(options.chain, balance, raw);
1699
1704
  } else {
1700
1705
  const balances = await vault.balances(void 0, options.includeTokens);
1701
1706
  spinner.succeed("Balances loaded");
@@ -1703,7 +1708,7 @@ async function executeBalance(ctx2, options = {}) {
1703
1708
  outputJson({ balances });
1704
1709
  return;
1705
1710
  }
1706
- displayBalancesTable(balances);
1711
+ displayBalancesTable(balances, raw);
1707
1712
  }
1708
1713
  }
1709
1714
  async function executePortfolio(ctx2, options = {}) {
@@ -1738,13 +1743,27 @@ async function executePortfolio(ctx2, options = {}) {
1738
1743
  outputJson({ portfolio, currency });
1739
1744
  return;
1740
1745
  }
1741
- displayPortfolio(portfolio, currency);
1746
+ displayPortfolio(portfolio, currency, options.raw ?? false);
1742
1747
  }
1743
1748
 
1744
1749
  // src/commands/chains.ts
1750
+ import { SUPPORTED_CHAINS } from "@vultisig/sdk";
1745
1751
  import chalk3 from "chalk";
1746
1752
  async function executeChains(ctx2, options = {}) {
1747
1753
  const vault = await ctx2.ensureActiveVault();
1754
+ if (options.addAll) {
1755
+ const currentCount = vault.chains.length;
1756
+ const spinner = createSpinner(`Adding all ${SUPPORTED_CHAINS.length} supported chains...`);
1757
+ await vault.setChains([...SUPPORTED_CHAINS]);
1758
+ const addedCount = SUPPORTED_CHAINS.length - currentCount;
1759
+ spinner.succeed(`Added ${addedCount} chains (${SUPPORTED_CHAINS.length} total)`);
1760
+ if (isJsonOutput()) {
1761
+ outputJson({ chains: [...vault.chains], added: addedCount, total: SUPPORTED_CHAINS.length });
1762
+ return;
1763
+ }
1764
+ info(chalk3.gray("\nAll supported chains are now enabled."));
1765
+ return;
1766
+ }
1748
1767
  if (options.add) {
1749
1768
  await vault.addChain(options.add);
1750
1769
  success(`
@@ -1765,7 +1784,9 @@ async function executeChains(ctx2, options = {}) {
1765
1784
  chains.forEach((chain) => {
1766
1785
  printResult(` - ${chain}`);
1767
1786
  });
1768
- info(chalk3.gray("\nUse --add <chain> to add a chain or --remove <chain> to remove one"));
1787
+ info(chalk3.gray(`
1788
+ ${chains.length} of ${SUPPORTED_CHAINS.length} chains enabled`));
1789
+ info(chalk3.gray("Use --add <chain>, --add-all, or --remove <chain>"));
1769
1790
  }
1770
1791
  }
1771
1792
  async function executeAddresses(ctx2) {
@@ -1893,7 +1914,8 @@ async function executeSend(ctx2, params) {
1893
1914
  if (!Object.values(Chain).includes(params.chain)) {
1894
1915
  throw new Error(`Invalid chain: ${params.chain}`);
1895
1916
  }
1896
- if (isNaN(parseFloat(params.amount)) || parseFloat(params.amount) <= 0) {
1917
+ const isMax = params.amount === "max";
1918
+ if (!isMax && (isNaN(parseFloat(params.amount)) || parseFloat(params.amount) <= 0)) {
1897
1919
  throw new Error("Invalid amount");
1898
1920
  }
1899
1921
  return sendTransaction(vault, params);
@@ -1909,7 +1931,25 @@ async function sendTransaction(vault, params) {
1909
1931
  ticker: balance.symbol,
1910
1932
  id: params.tokenId
1911
1933
  };
1912
- const amount = BigInt(Math.floor(parseFloat(params.amount) * Math.pow(10, balance.decimals)));
1934
+ const isMax = params.amount === "max";
1935
+ let amount;
1936
+ let displayAmount;
1937
+ if (isMax) {
1938
+ const maxInfo = await vault.getMaxSendAmount({ coin, receiver: params.to, memo: params.memo });
1939
+ amount = maxInfo.maxSendable;
1940
+ if (amount === 0n) {
1941
+ throw new Error("Insufficient balance to cover network fees");
1942
+ }
1943
+ displayAmount = formatBigintAmount(amount, balance.decimals);
1944
+ } else {
1945
+ const [whole, frac = ""] = params.amount.split(".");
1946
+ if (frac.length > balance.decimals) {
1947
+ throw new Error(`Amount has more than ${balance.decimals} decimal places`);
1948
+ }
1949
+ const paddedFrac = frac.padEnd(balance.decimals, "0");
1950
+ amount = BigInt(whole || "0") * 10n ** BigInt(balance.decimals) + BigInt(paddedFrac || "0");
1951
+ displayAmount = params.amount;
1952
+ }
1913
1953
  const payload = await vault.prepareSendTx({
1914
1954
  coin,
1915
1955
  receiver: params.to,
@@ -1927,7 +1967,7 @@ async function sendTransaction(vault, params) {
1927
1967
  displayTransactionPreview(
1928
1968
  payload.coin.address,
1929
1969
  params.to,
1930
- params.amount,
1970
+ displayAmount,
1931
1971
  payload.coin.ticker,
1932
1972
  params.chain,
1933
1973
  params.memo,
@@ -2012,12 +2052,175 @@ Or use this URL: ${qrPayload}
2012
2052
  }
2013
2053
  }
2014
2054
 
2015
- // src/commands/sign.ts
2055
+ // src/commands/execute.ts
2016
2056
  var import_qrcode_terminal2 = __toESM(require_main(), 1);
2017
- import { Chain as Chain2 } from "@vultisig/sdk";
2057
+ import { Vultisig as Vultisig3 } from "@vultisig/sdk";
2058
+ var COSMOS_CHAIN_CONFIG = {
2059
+ THORChain: {
2060
+ chainId: "thorchain-1",
2061
+ prefix: "thor",
2062
+ denom: "rune",
2063
+ gasLimit: "500000"
2064
+ },
2065
+ MayaChain: {
2066
+ chainId: "mayachain-mainnet-v1",
2067
+ prefix: "maya",
2068
+ denom: "cacao",
2069
+ gasLimit: "500000"
2070
+ }
2071
+ };
2072
+ function parseFunds(fundsStr) {
2073
+ if (!fundsStr) return [];
2074
+ return fundsStr.split(",").map((fund) => {
2075
+ const [denom, amount] = fund.trim().split(":");
2076
+ if (!denom || !amount) {
2077
+ throw new Error(`Invalid funds format: "${fund}". Expected "denom:amount"`);
2078
+ }
2079
+ return { denom: denom.toLowerCase(), amount };
2080
+ });
2081
+ }
2082
+ async function executeExecute(ctx2, params) {
2083
+ const vault = await ctx2.ensureActiveVault();
2084
+ const chainConfig = COSMOS_CHAIN_CONFIG[params.chain];
2085
+ if (!chainConfig) {
2086
+ throw new Error(`Chain ${params.chain} does not support CosmWasm execute. Supported chains: ${Object.keys(COSMOS_CHAIN_CONFIG).join(", ")}`);
2087
+ }
2088
+ let msg;
2089
+ try {
2090
+ msg = JSON.parse(params.msg);
2091
+ } catch {
2092
+ throw new Error(`Invalid JSON message: ${params.msg}`);
2093
+ }
2094
+ const funds = parseFunds(params.funds);
2095
+ return executeContractTransaction(vault, params, chainConfig, msg, funds);
2096
+ }
2097
+ async function executeContractTransaction(vault, params, chainConfig, msg, funds) {
2098
+ const prepareSpinner = createSpinner("Preparing contract execution...");
2099
+ const address = await vault.address(params.chain);
2100
+ prepareSpinner.succeed("Transaction prepared");
2101
+ if (!isJsonOutput()) {
2102
+ info("\n\u{1F4DD} Contract Execution Preview");
2103
+ info("\u2501".repeat(50));
2104
+ info(`Chain: ${params.chain}`);
2105
+ info(`From: ${address}`);
2106
+ info(`Contract: ${params.contract}`);
2107
+ info(`Message: ${JSON.stringify(msg, null, 2).substring(0, 200)}${JSON.stringify(msg).length > 200 ? "..." : ""}`);
2108
+ if (funds.length > 0) {
2109
+ info(`Funds: ${funds.map((f) => `${f.amount} ${f.denom}`).join(", ")}`);
2110
+ }
2111
+ if (params.memo) {
2112
+ info(`Memo: ${params.memo}`);
2113
+ }
2114
+ info("\u2501".repeat(50));
2115
+ }
2116
+ if (!params.yes && !isJsonOutput()) {
2117
+ const confirmed = await confirmTransaction();
2118
+ if (!confirmed) {
2119
+ warn("Transaction cancelled");
2120
+ throw new Error("Transaction cancelled by user");
2121
+ }
2122
+ }
2123
+ await ensureVaultUnlocked(vault, params.password);
2124
+ const isSecureVault = vault.type === "secure";
2125
+ const signSpinner = createSpinner(isSecureVault ? "Preparing secure signing session..." : "Signing transaction...");
2126
+ vault.on("signingProgress", ({ step }) => {
2127
+ signSpinner.text = `${step.message} (${step.progress}%)`;
2128
+ });
2129
+ if (isSecureVault) {
2130
+ vault.on("qrCodeReady", ({ qrPayload }) => {
2131
+ if (isJsonOutput()) {
2132
+ printResult(qrPayload);
2133
+ } else if (isSilent()) {
2134
+ printResult(`QR Payload: ${qrPayload}`);
2135
+ } else {
2136
+ signSpinner.stop();
2137
+ info("\nScan this QR code with your Vultisig mobile app to sign:");
2138
+ import_qrcode_terminal2.default.generate(qrPayload, { small: true });
2139
+ info(`
2140
+ Or use this URL: ${qrPayload}
2141
+ `);
2142
+ signSpinner.start("Waiting for devices to join signing session...");
2143
+ }
2144
+ });
2145
+ vault.on("deviceJoined", ({ deviceId, totalJoined, required }) => {
2146
+ if (!isSilent()) {
2147
+ signSpinner.text = `Device joined: ${totalJoined}/${required} (${deviceId})`;
2148
+ } else if (!isJsonOutput()) {
2149
+ printResult(`Device joined: ${totalJoined}/${required}`);
2150
+ }
2151
+ });
2152
+ }
2153
+ try {
2154
+ const coin = {
2155
+ chain: params.chain,
2156
+ address,
2157
+ decimals: 8,
2158
+ // THORChain uses 8 decimals
2159
+ ticker: chainConfig.denom.toUpperCase()
2160
+ };
2161
+ const executeContractMsg = {
2162
+ type: "wasm/MsgExecuteContract",
2163
+ value: JSON.stringify({
2164
+ sender: address,
2165
+ contract: params.contract,
2166
+ msg,
2167
+ funds: funds.map((f) => ({ denom: f.denom, amount: f.amount }))
2168
+ })
2169
+ };
2170
+ const fee = {
2171
+ amount: [{ denom: chainConfig.denom, amount: "0" }],
2172
+ gas: chainConfig.gasLimit
2173
+ };
2174
+ const keysignPayload = await vault.prepareSignAminoTx({
2175
+ chain: params.chain,
2176
+ coin,
2177
+ msgs: [executeContractMsg],
2178
+ fee,
2179
+ memo: params.memo
2180
+ });
2181
+ const messageHashes = await vault.extractMessageHashes(keysignPayload);
2182
+ const signature = await vault.sign(
2183
+ {
2184
+ transaction: keysignPayload,
2185
+ chain: params.chain,
2186
+ messageHashes
2187
+ },
2188
+ { signal: params.signal }
2189
+ );
2190
+ signSpinner.succeed("Transaction signed");
2191
+ const broadcastSpinner = createSpinner("Broadcasting transaction...");
2192
+ const txHash = await vault.broadcastTx({
2193
+ chain: params.chain,
2194
+ keysignPayload,
2195
+ signature
2196
+ });
2197
+ broadcastSpinner.succeed(`Transaction broadcast: ${txHash}`);
2198
+ const result = {
2199
+ txHash,
2200
+ chain: params.chain,
2201
+ explorerUrl: Vultisig3.getTxExplorerUrl(params.chain, txHash)
2202
+ };
2203
+ if (isJsonOutput()) {
2204
+ outputJson(result);
2205
+ } else {
2206
+ displayTransactionResult(params.chain, txHash);
2207
+ }
2208
+ return result;
2209
+ } finally {
2210
+ vault.removeAllListeners("signingProgress");
2211
+ if (isSecureVault) {
2212
+ vault.removeAllListeners("qrCodeReady");
2213
+ vault.removeAllListeners("deviceJoined");
2214
+ }
2215
+ }
2216
+ }
2217
+
2218
+ // src/commands/sign.ts
2219
+ var import_qrcode_terminal3 = __toESM(require_main(), 1);
2220
+ import { Chain as Chain3 } from "@vultisig/sdk";
2018
2221
  async function executeSignBytes(ctx2, params) {
2019
2222
  const vault = await ctx2.ensureActiveVault();
2020
- if (!Object.values(Chain2).includes(params.chain)) {
2223
+ if (!Object.values(Chain3).includes(params.chain)) {
2021
2224
  throw new Error(`Invalid chain: ${params.chain}`);
2022
2225
  }
2023
2226
  return signBytes(vault, params);
@@ -2039,7 +2242,7 @@ async function signBytes(vault, params) {
2039
2242
  } else {
2040
2243
  signSpinner.stop();
2041
2244
  info("\nScan this QR code with your Vultisig mobile app to sign:");
2042
- import_qrcode_terminal2.default.generate(qrPayload, { small: true });
2245
+ import_qrcode_terminal3.default.generate(qrPayload, { small: true });
2043
2246
  info(`
2044
2247
  Or use this URL: ${qrPayload}
2045
2248
  `);
@@ -2093,10 +2296,10 @@ Or use this URL: ${qrPayload}
2093
2296
  }
2094
2297
 
2095
2298
  // src/commands/broadcast.ts
2096
- import { Chain as Chain3, Vultisig as Vultisig3 } from "@vultisig/sdk";
2299
+ import { Chain as Chain4, Vultisig as Vultisig4 } from "@vultisig/sdk";
2097
2300
  async function executeBroadcast(ctx2, params) {
2098
2301
  const vault = await ctx2.ensureActiveVault();
2099
- if (!Object.values(Chain3).includes(params.chain)) {
2302
+ if (!Object.values(Chain4).includes(params.chain)) {
2100
2303
  throw new Error(`Invalid chain: ${params.chain}`);
2101
2304
  }
2102
2305
  const broadcastSpinner = createSpinner("Broadcasting transaction...");
@@ -2109,7 +2312,7 @@ async function executeBroadcast(ctx2, params) {
2109
2312
  const result = {
2110
2313
  txHash,
2111
2314
  chain: params.chain,
2112
- explorerUrl: Vultisig3.getTxExplorerUrl(params.chain, txHash)
2315
+ explorerUrl: Vultisig4.getTxExplorerUrl(params.chain, txHash)
2113
2316
  };
2114
2317
  if (isJsonOutput()) {
2115
2318
  outputJson(result);
@@ -2125,7 +2328,7 @@ async function executeBroadcast(ctx2, params) {
2125
2328
  }
2126
2329
 
2127
2330
  // src/commands/vault-management.ts
2128
- var import_qrcode_terminal3 = __toESM(require_main(), 1);
2331
+ var import_qrcode_terminal4 = __toESM(require_main(), 1);
2129
2332
  import chalk5 from "chalk";
2130
2333
  import { promises as fs } from "fs";
2131
2334
  import inquirer4 from "inquirer";
@@ -2246,7 +2449,7 @@ async function executeCreateSecure(ctx2, options) {
2246
2449
  } else {
2247
2450
  spinner.stop();
2248
2451
  info("\nScan this QR code with your Vultisig mobile app:");
2249
- import_qrcode_terminal3.default.generate(qrPayload, { small: true });
2452
+ import_qrcode_terminal4.default.generate(qrPayload, { small: true });
2250
2453
  info(`
2251
2454
  Or use this URL: ${qrPayload}
2252
2455
  `);
@@ -2508,7 +2711,7 @@ async function executeInfo(ctx2) {
2508
2711
  displayVaultInfo(vault);
2509
2712
  }
2510
2713
  async function executeCreateFromSeedphraseFast(ctx2, options) {
2511
- const { mnemonic, name, password, email, discoverChains, chains, signal } = options;
2714
+ const { mnemonic, name, password, email, discoverChains, chains, signal, usePhantomSolanaPath } = options;
2512
2715
  const validateSpinner = createSpinner("Validating seedphrase...");
2513
2716
  const validation = await ctx2.sdk.validateSeedphrase(mnemonic);
2514
2717
  if (!validation.valid) {
@@ -2519,19 +2722,26 @@ async function executeCreateFromSeedphraseFast(ctx2, options) {
2519
2722
  throw new Error(validation.error || "Invalid mnemonic phrase");
2520
2723
  }
2521
2724
  validateSpinner.succeed(`Valid ${validation.wordCount}-word seedphrase`);
2725
+ let detectedUsePhantomSolanaPath = usePhantomSolanaPath;
2522
2726
  if (discoverChains) {
2523
2727
  const discoverSpinner = createSpinner("Discovering chains with balances...");
2524
2728
  try {
2525
- const discovered = await ctx2.sdk.discoverChainsFromSeedphrase(mnemonic, chains, (p) => {
2729
+ const { results: discovered, usePhantomSolanaPath: detectedPhantomPath } = await ctx2.sdk.discoverChainsFromSeedphrase(mnemonic, chains, (p) => {
2526
2730
  discoverSpinner.text = `Discovering: ${p.chain || "scanning"} (${p.chainsProcessed}/${p.chainsTotal})`;
2527
2731
  });
2528
2732
  const chainsWithBalance = discovered.filter((c) => c.hasBalance);
2529
2733
  discoverSpinner.succeed(`Found ${chainsWithBalance.length} chains with balances`);
2734
+ if (usePhantomSolanaPath === void 0) {
2735
+ detectedUsePhantomSolanaPath = detectedPhantomPath;
2736
+ }
2530
2737
  if (chainsWithBalance.length > 0 && !isSilent()) {
2531
2738
  info("\nChains with balances:");
2532
2739
  for (const result of chainsWithBalance) {
2533
2740
  info(` ${result.chain}: ${result.balance} ${result.symbol}`);
2534
2741
  }
2742
+ if (detectedUsePhantomSolanaPath) {
2743
+ info(" (Using Phantom wallet derivation path for Solana)");
2744
+ }
2535
2745
  info("");
2536
2746
  }
2537
2747
  } catch {
@@ -2547,6 +2757,7 @@ async function executeCreateFromSeedphraseFast(ctx2, options) {
2547
2757
  email,
2548
2758
  // Don't pass discoverChains - CLI handles discovery above
2549
2759
  chains,
2760
+ usePhantomSolanaPath: detectedUsePhantomSolanaPath,
2550
2761
  onProgress: (step) => {
2551
2762
  importSpinner.text = `${step.message} (${step.progress}%)`;
2552
2763
  }
@@ -2625,7 +2836,17 @@ async function executeCreateFromSeedphraseFast(ctx2, options) {
2625
2836
  throw new Error("Verification loop exited unexpectedly");
2626
2837
  }
2627
2838
  async function executeCreateFromSeedphraseSecure(ctx2, options) {
2628
- const { mnemonic, name, password, threshold, shares: totalShares, discoverChains, chains, signal } = options;
2839
+ const {
2840
+ mnemonic,
2841
+ name,
2842
+ password,
2843
+ threshold,
2844
+ shares: totalShares,
2845
+ discoverChains,
2846
+ chains,
2847
+ signal,
2848
+ usePhantomSolanaPath
2849
+ } = options;
2629
2850
  const validateSpinner = createSpinner("Validating seedphrase...");
2630
2851
  const validation = await ctx2.sdk.validateSeedphrase(mnemonic);
2631
2852
  if (!validation.valid) {
@@ -2636,19 +2857,26 @@ async function executeCreateFromSeedphraseSecure(ctx2, options) {
2636
2857
  throw new Error(validation.error || "Invalid mnemonic phrase");
2637
2858
  }
2638
2859
  validateSpinner.succeed(`Valid ${validation.wordCount}-word seedphrase`);
2860
+ let detectedUsePhantomSolanaPath = usePhantomSolanaPath;
2639
2861
  if (discoverChains) {
2640
2862
  const discoverSpinner = createSpinner("Discovering chains with balances...");
2641
2863
  try {
2642
- const discovered = await ctx2.sdk.discoverChainsFromSeedphrase(mnemonic, chains, (p) => {
2864
+ const { results: discovered, usePhantomSolanaPath: detectedPhantomPath } = await ctx2.sdk.discoverChainsFromSeedphrase(mnemonic, chains, (p) => {
2643
2865
  discoverSpinner.text = `Discovering: ${p.chain || "scanning"} (${p.chainsProcessed}/${p.chainsTotal})`;
2644
2866
  });
2645
2867
  const chainsWithBalance = discovered.filter((c) => c.hasBalance);
2646
2868
  discoverSpinner.succeed(`Found ${chainsWithBalance.length} chains with balances`);
2869
+ if (usePhantomSolanaPath === void 0) {
2870
+ detectedUsePhantomSolanaPath = detectedPhantomPath;
2871
+ }
2647
2872
  if (chainsWithBalance.length > 0 && !isSilent()) {
2648
2873
  info("\nChains with balances:");
2649
2874
  for (const result of chainsWithBalance) {
2650
2875
  info(` ${result.chain}: ${result.balance} ${result.symbol}`);
2651
2876
  }
2877
+ if (detectedUsePhantomSolanaPath) {
2878
+ info(" (Using Phantom wallet derivation path for Solana)");
2879
+ }
2652
2880
  info("");
2653
2881
  }
2654
2882
  } catch {
@@ -2666,6 +2894,7 @@ async function executeCreateFromSeedphraseSecure(ctx2, options) {
2666
2894
  threshold,
2667
2895
  // Don't pass discoverChains - CLI handles discovery above
2668
2896
  chains,
2897
+ usePhantomSolanaPath: detectedUsePhantomSolanaPath,
2669
2898
  onProgress: (step) => {
2670
2899
  importSpinner.text = `${step.message} (${step.progress}%)`;
2671
2900
  },
@@ -2677,7 +2906,7 @@ async function executeCreateFromSeedphraseSecure(ctx2, options) {
2677
2906
  } else {
2678
2907
  importSpinner.stop();
2679
2908
  info("\nScan this QR code with your Vultisig mobile app:");
2680
- import_qrcode_terminal3.default.generate(qrPayload, { small: true });
2909
+ import_qrcode_terminal4.default.generate(qrPayload, { small: true });
2681
2910
  info(`
2682
2911
  Or use this URL: ${qrPayload}
2683
2912
  `);
@@ -2770,6 +2999,82 @@ async function executeJoinSecure(ctx2, options) {
2770
2999
  throw err;
2771
3000
  }
2772
3001
  }
3002
+ async function executeDelete(ctx2, options = {}) {
3003
+ let vault;
3004
+ if (options.vaultId) {
3005
+ const vaults = await ctx2.sdk.listVaults();
3006
+ vault = findVaultByIdOrName({ vaults, idOrName: options.vaultId });
3007
+ } else {
3008
+ vault = await ctx2.ensureActiveVault();
3009
+ }
3010
+ if (isJsonOutput()) {
3011
+ const spinner2 = createSpinner("Deleting vault...");
3012
+ await ctx2.sdk.deleteVault(vault);
3013
+ spinner2.succeed("Vault deleted");
3014
+ outputJson({
3015
+ deleted: true,
3016
+ vault: {
3017
+ id: vault.id,
3018
+ name: vault.name,
3019
+ type: vault.type
3020
+ }
3021
+ });
3022
+ return;
3023
+ }
3024
+ info("\n" + chalk5.bold("Vault to delete:"));
3025
+ info(` Name: ${chalk5.cyan(vault.name)}`);
3026
+ info(` Type: ${chalk5.yellow(vault.type)}`);
3027
+ info(` ID: ${vault.id.slice(0, 16)}...`);
3028
+ info(` Chains: ${vault.chains.length}`);
3029
+ info("");
3030
+ if (!options.skipConfirmation) {
3031
+ warn(chalk5.red.bold("WARNING: This action cannot be undone!"));
3032
+ warn("Make sure you have a backup of your vault (.vult file) before proceeding.");
3033
+ info("");
3034
+ const { confirmed } = await inquirer4.prompt([
3035
+ {
3036
+ type: "confirm",
3037
+ name: "confirmed",
3038
+ message: `Are you sure you want to delete vault "${vault.name}"?`,
3039
+ default: false
3040
+ }
3041
+ ]);
3042
+ if (!confirmed) {
3043
+ info("Deletion cancelled.");
3044
+ return;
3045
+ }
3046
+ }
3047
+ const spinner = createSpinner("Deleting vault...");
3048
+ await ctx2.sdk.deleteVault(vault);
3049
+ spinner.succeed(`Vault deleted: ${vault.name}`);
3050
+ success("\n+ Vault deleted successfully");
3051
+ info(chalk5.gray('\nTip: Run "vultisig vaults" to see remaining vaults'));
3052
+ }
3053
+ function findVaultByIdOrName({ vaults, idOrName }) {
3054
+ const byId = vaults.find((v) => v.id === idOrName);
3055
+ if (byId) return byId;
3056
+ const byName = vaults.filter((v) => v.name.toLowerCase() === idOrName.toLowerCase());
3057
+ if (byName.length === 1) return byName[0];
3058
+ if (byName.length > 1) {
3059
+ const matches = byName.map((v) => ` - ${v.name} (${v.id.slice(0, 8)}...)`).join("\n");
3060
+ throw new Error(
3061
+ `Multiple vaults match "${idOrName}":
3062
+ ${matches}
3063
+ Please specify the full vault ID to be more specific.`
3064
+ );
3065
+ }
3066
+ const byPartialId = vaults.filter((v) => v.id.startsWith(idOrName));
3067
+ if (byPartialId.length === 1) return byPartialId[0];
3068
+ if (byPartialId.length > 1) {
3069
+ const matches = byPartialId.map((v) => ` - ${v.name} (${v.id.slice(0, 8)}...)`).join("\n");
3070
+ throw new Error(
3071
+ `Multiple vaults match prefix "${idOrName}":
3072
+ ${matches}
3073
+ Please specify more characters of the vault ID.`
3074
+ );
3075
+ }
3076
+ throw new Error(`Vault not found: "${idOrName}"`);
3077
+ }
2773
3078
 
2774
3079
  // src/commands/swap.ts
2775
3080
  async function executeSwapChains(ctx2) {
@@ -2786,66 +3091,95 @@ async function executeSwapChains(ctx2) {
2786
3091
  }
2787
3092
  async function executeSwapQuote(ctx2, options) {
2788
3093
  const vault = await ctx2.ensureActiveVault();
2789
- if (isNaN(options.amount) || options.amount <= 0) {
3094
+ const isMax = options.amount === "max";
3095
+ if (!isMax && (isNaN(options.amount) || options.amount <= 0)) {
2790
3096
  throw new Error("Invalid amount");
2791
3097
  }
2792
3098
  const isSupported = await vault.isSwapSupported(options.fromChain, options.toChain);
2793
3099
  if (!isSupported) {
2794
3100
  throw new Error(`Swaps from ${options.fromChain} to ${options.toChain} are not supported`);
2795
3101
  }
3102
+ let resolvedAmount;
3103
+ if (isMax) {
3104
+ const bal = await vault.balance(options.fromChain, options.fromToken);
3105
+ resolvedAmount = parseFloat(bal.formattedAmount);
3106
+ if (resolvedAmount <= 0) {
3107
+ throw new Error("Zero balance \u2014 nothing to swap");
3108
+ }
3109
+ } else {
3110
+ resolvedAmount = options.amount;
3111
+ }
2796
3112
  const spinner = createSpinner("Getting swap quote...");
2797
3113
  const quote = await vault.getSwapQuote({
2798
3114
  fromCoin: { chain: options.fromChain, token: options.fromToken },
2799
3115
  toCoin: { chain: options.toChain, token: options.toToken },
2800
- amount: options.amount,
3116
+ amount: resolvedAmount,
2801
3117
  fiatCurrency: "usd"
2802
3118
  // Request fiat conversion
2803
3119
  });
2804
3120
  spinner.succeed("Quote received");
3121
+ const fromAmountDisplay = isMax ? `${formatBigintAmount(quote.maxSwapable, quote.fromCoin.decimals)} (max)` : String(resolvedAmount);
2805
3122
  if (isJsonOutput()) {
2806
3123
  outputJson({
2807
3124
  fromChain: options.fromChain,
2808
3125
  toChain: options.toChain,
2809
- amount: options.amount,
3126
+ amount: resolvedAmount,
3127
+ isMax,
2810
3128
  quote
2811
3129
  });
2812
3130
  return quote;
2813
3131
  }
2814
3132
  const feeBalance = await vault.balance(options.fromChain);
2815
- displaySwapPreview(quote, String(options.amount), quote.fromCoin.ticker, quote.toCoin.ticker, {
3133
+ const discountTier = await vault.getDiscountTier();
3134
+ displaySwapPreview(quote, fromAmountDisplay, quote.fromCoin.ticker, quote.toCoin.ticker, {
2816
3135
  fromDecimals: quote.fromCoin.decimals,
2817
3136
  toDecimals: quote.toCoin.decimals,
2818
3137
  feeDecimals: feeBalance.decimals,
2819
- feeSymbol: feeBalance.symbol
3138
+ feeSymbol: feeBalance.symbol,
3139
+ discountTier
2820
3140
  });
2821
3141
  info('\nTo execute this swap, use the "swap" command');
2822
3142
  return quote;
2823
3143
  }
2824
3144
  async function executeSwap(ctx2, options) {
2825
3145
  const vault = await ctx2.ensureActiveVault();
2826
- if (isNaN(options.amount) || options.amount <= 0) {
3146
+ const isMax = options.amount === "max";
3147
+ if (!isMax && (isNaN(options.amount) || options.amount <= 0)) {
2827
3148
  throw new Error("Invalid amount");
2828
3149
  }
2829
3150
  const isSupported = await vault.isSwapSupported(options.fromChain, options.toChain);
2830
3151
  if (!isSupported) {
2831
3152
  throw new Error(`Swaps from ${options.fromChain} to ${options.toChain} are not supported`);
2832
3153
  }
3154
+ let resolvedAmount;
3155
+ if (isMax) {
3156
+ const bal = await vault.balance(options.fromChain, options.fromToken);
3157
+ resolvedAmount = parseFloat(bal.formattedAmount);
3158
+ if (resolvedAmount <= 0) {
3159
+ throw new Error("Zero balance \u2014 nothing to swap");
3160
+ }
3161
+ } else {
3162
+ resolvedAmount = options.amount;
3163
+ }
2833
3164
  const quoteSpinner = createSpinner("Getting swap quote...");
2834
3165
  const quote = await vault.getSwapQuote({
2835
3166
  fromCoin: { chain: options.fromChain, token: options.fromToken },
2836
3167
  toCoin: { chain: options.toChain, token: options.toToken },
2837
- amount: options.amount,
3168
+ amount: resolvedAmount,
2838
3169
  fiatCurrency: "usd"
2839
3170
  // Request fiat conversion
2840
3171
  });
2841
3172
  quoteSpinner.succeed("Quote received");
3173
+ const fromAmountDisplay = isMax ? `${formatBigintAmount(quote.maxSwapable, quote.fromCoin.decimals)} (max)` : String(resolvedAmount);
2842
3174
  const feeBalance = await vault.balance(options.fromChain);
3175
+ const discountTier = await vault.getDiscountTier();
2843
3176
  if (!isJsonOutput()) {
2844
- displaySwapPreview(quote, String(options.amount), quote.fromCoin.ticker, quote.toCoin.ticker, {
3177
+ displaySwapPreview(quote, fromAmountDisplay, quote.fromCoin.ticker, quote.toCoin.ticker, {
2845
3178
  fromDecimals: quote.fromCoin.decimals,
2846
3179
  toDecimals: quote.toCoin.decimals,
2847
3180
  feeDecimals: feeBalance.decimals,
2848
- feeSymbol: feeBalance.symbol
3181
+ feeSymbol: feeBalance.symbol,
3182
+ discountTier
2849
3183
  });
2850
3184
  }
2851
3185
  if (!options.yes && !isJsonOutput()) {
@@ -2859,7 +3193,7 @@ async function executeSwap(ctx2, options) {
2859
3193
  const { keysignPayload, approvalPayload } = await vault.prepareSwapTx({
2860
3194
  fromCoin: { chain: options.fromChain, token: options.fromToken },
2861
3195
  toCoin: { chain: options.toChain, token: options.toToken },
2862
- amount: options.amount,
3196
+ amount: resolvedAmount,
2863
3197
  swapQuote: quote,
2864
3198
  autoApprove: false
2865
3199
  });
@@ -2934,7 +3268,7 @@ async function executeSwap(ctx2, options) {
2934
3268
  }
2935
3269
 
2936
3270
  // src/commands/settings.ts
2937
- import { Chain as Chain4, fiatCurrencies as fiatCurrencies2, fiatCurrencyNameRecord as fiatCurrencyNameRecord3 } from "@vultisig/sdk";
3271
+ import { Chain as Chain5, fiatCurrencies as fiatCurrencies2, fiatCurrencyNameRecord as fiatCurrencyNameRecord3 } from "@vultisig/sdk";
2938
3272
  import chalk6 from "chalk";
2939
3273
  import inquirer5 from "inquirer";
2940
3274
  async function executeCurrency(ctx2, newCurrency) {
@@ -3002,7 +3336,7 @@ async function executeAddressBook(ctx2, options = {}) {
3002
3336
  type: "list",
3003
3337
  name: "chain",
3004
3338
  message: "Select chain:",
3005
- choices: Object.values(Chain4)
3339
+ choices: Object.values(Chain5)
3006
3340
  });
3007
3341
  }
3008
3342
  if (!address) {
@@ -3078,8 +3412,292 @@ Address Book${options.chain ? ` (${options.chain})` : ""}:
3078
3412
  return allEntries;
3079
3413
  }
3080
3414
 
3415
+ // src/commands/rujira.ts
3416
+ import { getRoutesSummary, listEasyRoutes, RujiraClient, VultisigRujiraProvider } from "@vultisig/rujira";
3417
+ async function createRujiraClient(ctx2, options = {}) {
3418
+ const vault = await ctx2.ensureActiveVault();
3419
+ const provider = new VultisigRujiraProvider(vault);
3420
+ const client = new RujiraClient({
3421
+ signer: provider,
3422
+ rpcEndpoint: options.rpcEndpoint,
3423
+ config: {
3424
+ // Allow overriding rest endpoint via config (used for thornode calls)
3425
+ ...options.restEndpoint ? { restEndpoint: options.restEndpoint } : {}
3426
+ }
3427
+ });
3428
+ const spinner = createSpinner("Connecting to Rujira/THORChain...");
3429
+ await client.connect();
3430
+ spinner.succeed("Connected");
3431
+ return client;
3432
+ }
3433
+ async function executeRujiraBalance(ctx2, options = {}) {
3434
+ const vault = await ctx2.ensureActiveVault();
3435
+ const thorAddress = await vault.address("THORChain");
3436
+ const client = await createRujiraClient(ctx2, options);
3437
+ const spinner = createSpinner("Loading THORChain balances...");
3438
+ const balances = await client.deposit.getBalances(thorAddress);
3439
+ spinner.succeed("Balances loaded");
3440
+ const filtered = options.securedOnly ? balances.filter((b) => b.denom.includes("-") || b.denom.includes("/")) : balances;
3441
+ if (isJsonOutput()) {
3442
+ outputJson({ thorAddress, balances: filtered });
3443
+ return;
3444
+ }
3445
+ info(`THORChain address: ${thorAddress}`);
3446
+ if (!filtered.length) {
3447
+ printResult("No balances found");
3448
+ return;
3449
+ }
3450
+ printTable(
3451
+ filtered.map((b) => ({
3452
+ asset: b.asset,
3453
+ denom: b.denom,
3454
+ amount: b.formatted,
3455
+ raw: b.amount
3456
+ }))
3457
+ );
3458
+ }
3459
+ async function executeRujiraRoutes() {
3460
+ const routes = listEasyRoutes();
3461
+ const summary = getRoutesSummary();
3462
+ if (isJsonOutput()) {
3463
+ outputJson({ routes, summary });
3464
+ return;
3465
+ }
3466
+ printResult(summary);
3467
+ printResult("");
3468
+ printTable(
3469
+ routes.map((r) => ({
3470
+ name: r.name,
3471
+ from: r.from,
3472
+ to: r.to,
3473
+ liquidity: r.liquidity,
3474
+ description: r.description
3475
+ }))
3476
+ );
3477
+ }
3478
+ async function executeRujiraDeposit(ctx2, options = {}) {
3479
+ const vault = await ctx2.ensureActiveVault();
3480
+ const thorAddress = await vault.address("THORChain");
3481
+ const client = await createRujiraClient(ctx2, options);
3482
+ if (!options.asset) {
3483
+ const spinner2 = createSpinner("Loading THORChain inbound addresses...");
3484
+ const inbound = await client.deposit.getInboundAddresses();
3485
+ spinner2.succeed("Inbound addresses loaded");
3486
+ if (isJsonOutput()) {
3487
+ outputJson({ thorAddress, inboundAddresses: inbound });
3488
+ return;
3489
+ }
3490
+ info(`THORChain address: ${thorAddress}`);
3491
+ printResult("Provide an L1 asset to get a chain-specific inbound address + memo.");
3492
+ printResult("Example: vultisig rujira deposit --asset BTC.BTC --amount 100000");
3493
+ printResult("");
3494
+ printTable(
3495
+ inbound.map((a) => ({
3496
+ chain: a.chain,
3497
+ address: a.address,
3498
+ halted: a.halted,
3499
+ globalTradingPaused: a.global_trading_paused,
3500
+ chainTradingPaused: a.chain_trading_paused
3501
+ }))
3502
+ );
3503
+ return;
3504
+ }
3505
+ const amount = options.amount ?? "1";
3506
+ const spinner = createSpinner("Preparing deposit instructions...");
3507
+ const prepared = await client.deposit.prepare({
3508
+ fromAsset: options.asset,
3509
+ amount,
3510
+ thorAddress,
3511
+ affiliate: options.affiliate,
3512
+ affiliateBps: options.affiliateBps
3513
+ });
3514
+ spinner.succeed("Deposit prepared");
3515
+ if (isJsonOutput()) {
3516
+ outputJson({ thorAddress, deposit: prepared });
3517
+ return;
3518
+ }
3519
+ info(`THORChain address: ${thorAddress}`);
3520
+ printResult("Deposit instructions (send from L1):");
3521
+ printResult(` Chain: ${prepared.chain}`);
3522
+ printResult(` Asset: ${prepared.asset}`);
3523
+ printResult(` Inbound address:${prepared.inboundAddress}`);
3524
+ printResult(` Memo: ${prepared.memo}`);
3525
+ printResult(` Min amount: ${prepared.minimumAmount}`);
3526
+ if (prepared.warning) {
3527
+ warn(prepared.warning);
3528
+ }
3529
+ }
3530
+ async function executeRujiraSwap(ctx2, options) {
3531
+ const vault = await ctx2.ensureActiveVault();
3532
+ await ensureVaultUnlocked(vault, options.password);
3533
+ const client = await createRujiraClient(ctx2, options);
3534
+ const destination = options.destination ?? await vault.address("THORChain");
3535
+ const quoteSpinner = createSpinner("Getting FIN swap quote...");
3536
+ const quote = await client.swap.getQuote({
3537
+ fromAsset: options.fromAsset,
3538
+ toAsset: options.toAsset,
3539
+ amount: options.amount,
3540
+ destination,
3541
+ slippageBps: options.slippageBps
3542
+ });
3543
+ quoteSpinner.succeed("Quote received");
3544
+ if (isJsonOutput()) {
3545
+ const result2 = await client.swap.execute(quote, { slippageBps: options.slippageBps });
3546
+ outputJson({ quote, result: result2 });
3547
+ return;
3548
+ }
3549
+ printResult("FIN Swap Preview");
3550
+ printResult(` From: ${options.fromAsset}`);
3551
+ printResult(` To: ${options.toAsset}`);
3552
+ printResult(` Amount (in): ${options.amount}`);
3553
+ printResult(` Expected out:${quote.expectedOutput}`);
3554
+ printResult(` Min out: ${quote.minimumOutput}`);
3555
+ printResult(` Contract: ${quote.contractAddress}`);
3556
+ if (quote.warning) {
3557
+ warn(quote.warning);
3558
+ }
3559
+ if (!options.yes) {
3560
+ warn("This command will execute a swap. Re-run with -y/--yes to skip this warning.");
3561
+ throw new Error("Confirmation required (use --yes)");
3562
+ }
3563
+ const execSpinner = createSpinner("Executing FIN swap...");
3564
+ const result = await client.swap.execute(quote, { slippageBps: options.slippageBps });
3565
+ execSpinner.succeed("Swap submitted");
3566
+ printResult(`Tx Hash: ${result.txHash}`);
3567
+ }
3568
+ async function executeRujiraWithdraw(ctx2, options) {
3569
+ const vault = await ctx2.ensureActiveVault();
3570
+ await ensureVaultUnlocked(vault, options.password);
3571
+ const client = await createRujiraClient(ctx2, options);
3572
+ const prepSpinner = createSpinner("Preparing withdrawal (MsgDeposit)...");
3573
+ const prepared = await client.withdraw.prepare({
3574
+ asset: options.asset,
3575
+ amount: options.amount,
3576
+ l1Address: options.l1Address,
3577
+ maxFeeBps: options.maxFeeBps
3578
+ });
3579
+ prepSpinner.succeed("Withdrawal prepared");
3580
+ if (isJsonOutput()) {
3581
+ const result2 = await client.withdraw.execute(prepared);
3582
+ outputJson({ prepared, result: result2 });
3583
+ return;
3584
+ }
3585
+ printResult("Withdraw Preview");
3586
+ printResult(` Asset: ${prepared.asset}`);
3587
+ printResult(` Amount: ${prepared.amount}`);
3588
+ printResult(` Destination: ${prepared.destination}`);
3589
+ printResult(` Memo: ${prepared.memo}`);
3590
+ printResult(` Est. fee: ${prepared.estimatedFee}`);
3591
+ if (!options.yes) {
3592
+ warn("This command will broadcast a THORChain MsgDeposit withdrawal. Re-run with -y/--yes to proceed.");
3593
+ throw new Error("Confirmation required (use --yes)");
3594
+ }
3595
+ const execSpinner = createSpinner("Broadcasting withdrawal...");
3596
+ const result = await client.withdraw.execute(prepared);
3597
+ execSpinner.succeed("Withdrawal submitted");
3598
+ printResult(`Tx Hash: ${result.txHash}`);
3599
+ }
3600
+
3601
+ // src/commands/discount.ts
3602
+ import {
3603
+ baseAffiliateBps,
3604
+ vultDiscountTierBps,
3605
+ vultDiscountTierMinBalances
3606
+ } from "@vultisig/sdk";
3607
+ import chalk7 from "chalk";
3608
+ var TIER_CONFIG = {
3609
+ none: { bps: baseAffiliateBps, discount: 0 },
3610
+ ...Object.fromEntries(
3611
+ Object.entries(vultDiscountTierMinBalances).map(([tier, minVult]) => [
3612
+ tier,
3613
+ {
3614
+ bps: baseAffiliateBps - vultDiscountTierBps[tier],
3615
+ discount: vultDiscountTierBps[tier],
3616
+ minVult
3617
+ }
3618
+ ])
3619
+ )
3620
+ };
3621
+ function getTierColor(tier) {
3622
+ const colors = {
3623
+ none: chalk7.gray,
3624
+ bronze: chalk7.hex("#CD7F32"),
3625
+ silver: chalk7.hex("#C0C0C0"),
3626
+ gold: chalk7.hex("#FFD700"),
3627
+ platinum: chalk7.hex("#E5E4E2"),
3628
+ diamond: chalk7.hex("#B9F2FF"),
3629
+ ultimate: chalk7.hex("#FF00FF")
3630
+ };
3631
+ return colors[tier] || chalk7.white;
3632
+ }
3633
+ function getNextTier(currentTier) {
3634
+ const tierOrder = ["none", "bronze", "silver", "gold", "platinum", "diamond", "ultimate"];
3635
+ const currentIndex = tierOrder.indexOf(currentTier);
3636
+ if (currentIndex === -1 || currentIndex >= tierOrder.length - 1) {
3637
+ return null;
3638
+ }
3639
+ const nextTierName = tierOrder[currentIndex + 1];
3640
+ const config = TIER_CONFIG[nextTierName];
3641
+ if ("minVult" in config) {
3642
+ return { name: nextTierName, vultRequired: config.minVult };
3643
+ }
3644
+ return null;
3645
+ }
3646
+ async function executeDiscount(ctx2, options = {}) {
3647
+ const vault = await ctx2.ensureActiveVault();
3648
+ const spinner = createSpinner(options.refresh ? "Refreshing discount tier..." : "Loading discount tier...");
3649
+ const tierResult = options.refresh ? await vault.updateDiscountTier() : await vault.getDiscountTier();
3650
+ const tier = tierResult || "none";
3651
+ const config = TIER_CONFIG[tier];
3652
+ const nextTier = getNextTier(tier);
3653
+ const tierInfo = {
3654
+ tier,
3655
+ feeBps: config.bps,
3656
+ discountBps: config.discount,
3657
+ nextTier
3658
+ };
3659
+ spinner.succeed("Discount tier loaded");
3660
+ if (isJsonOutput()) {
3661
+ outputJson({
3662
+ tier: tierInfo.tier,
3663
+ feeBps: tierInfo.feeBps,
3664
+ discountBps: tierInfo.discountBps,
3665
+ nextTier: tierInfo.nextTier
3666
+ });
3667
+ return tierInfo;
3668
+ }
3669
+ displayDiscountTier(tierInfo);
3670
+ return tierInfo;
3671
+ }
3672
+ function displayDiscountTier(tierInfo) {
3673
+ const tierColor = getTierColor(tierInfo.tier);
3674
+ printResult(chalk7.cyan("\n+----------------------------------------+"));
3675
+ printResult(chalk7.cyan("| VULT Discount Tier |"));
3676
+ printResult(chalk7.cyan("+----------------------------------------+\n"));
3677
+ const tierDisplay = tierInfo.tier === "none" ? chalk7.gray("No Tier") : tierColor(tierInfo.tier.charAt(0).toUpperCase() + tierInfo.tier.slice(1));
3678
+ printResult(` Current Tier: ${tierDisplay}`);
3679
+ if (tierInfo.tier === "none") {
3680
+ printResult(` Swap Fee: ${chalk7.gray("50 bps (0.50%)")}`);
3681
+ printResult(` Discount: ${chalk7.gray("None")}`);
3682
+ } else {
3683
+ printResult(` Swap Fee: ${chalk7.green(`${tierInfo.feeBps} bps (${(tierInfo.feeBps / 100).toFixed(2)}%)`)}`);
3684
+ printResult(` Discount: ${chalk7.green(`${tierInfo.discountBps} bps saved`)}`);
3685
+ }
3686
+ if (tierInfo.nextTier) {
3687
+ const nextTierColor = getTierColor(tierInfo.nextTier.name);
3688
+ printResult(chalk7.bold("\n Next Tier:"));
3689
+ printResult(
3690
+ ` ${nextTierColor(tierInfo.nextTier.name.charAt(0).toUpperCase() + tierInfo.nextTier.name.slice(1))} - requires ${tierInfo.nextTier.vultRequired.toLocaleString()} VULT`
3691
+ );
3692
+ } else if (tierInfo.tier === "ultimate") {
3693
+ printResult(chalk7.bold("\n ") + chalk7.magenta("You have the highest tier! 0% swap fees."));
3694
+ }
3695
+ info(chalk7.gray("\n Tip: Thorguard NFT holders get +1 tier upgrade (up to gold)"));
3696
+ printResult("");
3697
+ }
3698
+
3081
3699
  // src/interactive/completer.ts
3082
- import { Chain as Chain5 } from "@vultisig/sdk";
3700
+ import { Chain as Chain6 } from "@vultisig/sdk";
3083
3701
  import fs2 from "fs";
3084
3702
  import path2 from "path";
3085
3703
  var COMMANDS = [
@@ -3087,6 +3705,7 @@ var COMMANDS = [
3087
3705
  "vaults",
3088
3706
  "vault",
3089
3707
  "import",
3708
+ "delete",
3090
3709
  "create-from-seedphrase",
3091
3710
  "create",
3092
3711
  "join",
@@ -3134,13 +3753,16 @@ function createCompleter(ctx2) {
3134
3753
  return completeVaultName(ctx2, partial);
3135
3754
  }
3136
3755
  if (command === "chains" && parts.length >= 2) {
3756
+ const lastPart = parts[parts.length - 1] || "";
3757
+ const lastPartLower = lastPart.toLowerCase();
3758
+ if (lastPartLower.startsWith("-")) {
3759
+ const flags = ["--add", "--add-all", "--remove"];
3760
+ const matches = flags.filter((f) => f.startsWith(lastPartLower));
3761
+ return [matches.length ? matches : flags, lastPart];
3762
+ }
3137
3763
  const flag = parts[parts.length - 2]?.toLowerCase();
3138
3764
  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("");
3765
+ return completeChainName(lastPart);
3144
3766
  }
3145
3767
  }
3146
3768
  if (["balance", "bal", "tokens", "send", "swap", "swap-quote"].includes(command) && parts.length === 2) {
@@ -3218,7 +3840,7 @@ function completeVaultName(ctx2, partial) {
3218
3840
  return [show, partial];
3219
3841
  }
3220
3842
  function completeChainName(partial) {
3221
- const allChains = Object.values(Chain5);
3843
+ const allChains = Object.values(Chain6);
3222
3844
  const partialLower = partial.toLowerCase();
3223
3845
  const matches = allChains.filter((chain) => chain.toLowerCase().startsWith(partialLower));
3224
3846
  matches.sort();
@@ -3226,14 +3848,14 @@ function completeChainName(partial) {
3226
3848
  return [show, partial];
3227
3849
  }
3228
3850
  function findChainByName(name) {
3229
- const allChains = Object.values(Chain5);
3851
+ const allChains = Object.values(Chain6);
3230
3852
  const nameLower = name.toLowerCase();
3231
3853
  const found = allChains.find((chain) => chain.toLowerCase() === nameLower);
3232
3854
  return found ? found : null;
3233
3855
  }
3234
3856
 
3235
3857
  // src/interactive/event-buffer.ts
3236
- import chalk7 from "chalk";
3858
+ import chalk8 from "chalk";
3237
3859
  var EventBuffer = class {
3238
3860
  eventBuffer = [];
3239
3861
  isCommandRunning = false;
@@ -3273,17 +3895,17 @@ var EventBuffer = class {
3273
3895
  displayEvent(message, type) {
3274
3896
  switch (type) {
3275
3897
  case "success":
3276
- console.log(chalk7.green(message));
3898
+ console.log(chalk8.green(message));
3277
3899
  break;
3278
3900
  case "warning":
3279
- console.log(chalk7.yellow(message));
3901
+ console.log(chalk8.yellow(message));
3280
3902
  break;
3281
3903
  case "error":
3282
- console.error(chalk7.red(message));
3904
+ console.error(chalk8.red(message));
3283
3905
  break;
3284
3906
  case "info":
3285
3907
  default:
3286
- console.log(chalk7.blue(message));
3908
+ console.log(chalk8.blue(message));
3287
3909
  break;
3288
3910
  }
3289
3911
  }
@@ -3294,13 +3916,13 @@ var EventBuffer = class {
3294
3916
  if (this.eventBuffer.length === 0) {
3295
3917
  return;
3296
3918
  }
3297
- console.log(chalk7.gray("\n--- Background Events ---"));
3919
+ console.log(chalk8.gray("\n--- Background Events ---"));
3298
3920
  this.eventBuffer.forEach((event) => {
3299
3921
  const timeStr = event.timestamp.toLocaleTimeString();
3300
3922
  const message = `[${timeStr}] ${event.message}`;
3301
3923
  this.displayEvent(message, event.type);
3302
3924
  });
3303
- console.log(chalk7.gray("--- End Events ---\n"));
3925
+ console.log(chalk8.gray("--- End Events ---\n"));
3304
3926
  }
3305
3927
  /**
3306
3928
  * Setup all vault event listeners
@@ -3402,12 +4024,12 @@ var EventBuffer = class {
3402
4024
 
3403
4025
  // src/interactive/session.ts
3404
4026
  import { fiatCurrencies as fiatCurrencies3 } from "@vultisig/sdk";
3405
- import chalk9 from "chalk";
4027
+ import chalk10 from "chalk";
3406
4028
  import ora3 from "ora";
3407
4029
  import * as readline from "readline";
3408
4030
 
3409
4031
  // src/interactive/shell-commands.ts
3410
- import chalk8 from "chalk";
4032
+ import chalk9 from "chalk";
3411
4033
  import Table from "cli-table3";
3412
4034
  import inquirer6 from "inquirer";
3413
4035
  import ora2 from "ora";
@@ -3424,25 +4046,25 @@ function formatTimeRemaining(ms) {
3424
4046
  async function executeLock(ctx2) {
3425
4047
  const vault = ctx2.getActiveVault();
3426
4048
  if (!vault) {
3427
- console.log(chalk8.red("No active vault."));
3428
- console.log(chalk8.yellow('Use "vault <name>" to switch to a vault first.'));
4049
+ console.log(chalk9.red("No active vault."));
4050
+ console.log(chalk9.yellow('Use "vault <name>" to switch to a vault first.'));
3429
4051
  return;
3430
4052
  }
3431
4053
  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."));
4054
+ console.log(chalk9.green("\n+ Vault locked"));
4055
+ console.log(chalk9.gray("Password cache cleared. You will need to enter the password again."));
3434
4056
  }
3435
4057
  async function executeUnlock(ctx2) {
3436
4058
  const vault = ctx2.getActiveVault();
3437
4059
  if (!vault) {
3438
- console.log(chalk8.red("No active vault."));
3439
- console.log(chalk8.yellow('Use "vault <name>" to switch to a vault first.'));
4060
+ console.log(chalk9.red("No active vault."));
4061
+ console.log(chalk9.yellow('Use "vault <name>" to switch to a vault first.'));
3440
4062
  return;
3441
4063
  }
3442
4064
  if (ctx2.isVaultUnlocked(vault.id)) {
3443
4065
  const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
3444
- console.log(chalk8.yellow("\nVault is already unlocked."));
3445
- console.log(chalk8.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
4066
+ console.log(chalk9.yellow("\nVault is already unlocked."));
4067
+ console.log(chalk9.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
3446
4068
  return;
3447
4069
  }
3448
4070
  const { password } = await inquirer6.prompt([
@@ -3459,19 +4081,19 @@ async function executeUnlock(ctx2) {
3459
4081
  ctx2.cachePassword(vault.id, password);
3460
4082
  const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
3461
4083
  spinner.succeed("Vault unlocked");
3462
- console.log(chalk8.green(`
4084
+ console.log(chalk9.green(`
3463
4085
  + Vault unlocked for ${formatTimeRemaining(timeRemaining)}`));
3464
4086
  } catch (err) {
3465
4087
  spinner.fail("Failed to unlock vault");
3466
- console.error(chalk8.red(`
4088
+ console.error(chalk9.red(`
3467
4089
  x ${err.message}`));
3468
4090
  }
3469
4091
  }
3470
4092
  async function executeStatus(ctx2) {
3471
4093
  const vault = ctx2.getActiveVault();
3472
4094
  if (!vault) {
3473
- console.log(chalk8.red("No active vault."));
3474
- console.log(chalk8.yellow('Use "vault <name>" to switch to a vault first.'));
4095
+ console.log(chalk9.red("No active vault."));
4096
+ console.log(chalk9.yellow('Use "vault <name>" to switch to a vault first.'));
3475
4097
  return;
3476
4098
  }
3477
4099
  const isUnlocked = ctx2.isVaultUnlocked(vault.id);
@@ -3502,30 +4124,30 @@ async function executeStatus(ctx2) {
3502
4124
  displayStatus(status);
3503
4125
  }
3504
4126
  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)}`);
4127
+ console.log(chalk9.cyan("\n+----------------------------------------+"));
4128
+ console.log(chalk9.cyan("| Vault Status |"));
4129
+ console.log(chalk9.cyan("+----------------------------------------+\n"));
4130
+ console.log(chalk9.bold("Vault:"));
4131
+ console.log(` Name: ${chalk9.green(status.name)}`);
3510
4132
  console.log(` ID: ${status.id}`);
3511
- console.log(` Type: ${chalk8.yellow(status.type)}`);
3512
- console.log(chalk8.bold("\nSecurity:"));
4133
+ console.log(` Type: ${chalk9.yellow(status.type)}`);
4134
+ console.log(chalk9.bold("\nSecurity:"));
3513
4135
  if (status.isUnlocked) {
3514
- console.log(` Status: ${chalk8.green("Unlocked")} ${chalk8.green("\u{1F513}")}`);
4136
+ console.log(` Status: ${chalk9.green("Unlocked")} ${chalk9.green("\u{1F513}")}`);
3515
4137
  console.log(` Expires: ${status.timeRemainingFormatted}`);
3516
4138
  } else {
3517
- console.log(` Status: ${chalk8.yellow("Locked")} ${chalk8.yellow("\u{1F512}")}`);
4139
+ console.log(` Status: ${chalk9.yellow("Locked")} ${chalk9.yellow("\u{1F512}")}`);
3518
4140
  }
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:"));
4141
+ console.log(` Encrypted: ${status.isEncrypted ? chalk9.green("Yes") : chalk9.gray("No")}`);
4142
+ console.log(` Backed Up: ${status.isBackedUp ? chalk9.green("Yes") : chalk9.yellow("No")}`);
4143
+ console.log(chalk9.bold("\nMPC Configuration:"));
3522
4144
  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:"));
4145
+ console.log(` Threshold: ${chalk9.cyan(status.threshold)} of ${chalk9.cyan(status.totalSigners)}`);
4146
+ console.log(chalk9.bold("\nSigning Modes:"));
3525
4147
  status.availableSigningModes.forEach((mode) => {
3526
4148
  console.log(` - ${mode}`);
3527
4149
  });
3528
- console.log(chalk8.bold("\nDetails:"));
4150
+ console.log(chalk9.bold("\nDetails:"));
3529
4151
  console.log(` Chains: ${status.chains}`);
3530
4152
  console.log(` Currency: ${status.currency.toUpperCase()}`);
3531
4153
  console.log(` Created: ${new Date(status.createdAt).toLocaleString()}`);
@@ -3534,7 +4156,7 @@ function displayStatus(status) {
3534
4156
  }
3535
4157
  function showHelp() {
3536
4158
  const table = new Table({
3537
- head: [chalk8.bold("Available Commands")],
4159
+ head: [chalk9.bold("Available Commands")],
3538
4160
  colWidths: [50],
3539
4161
  chars: {
3540
4162
  mid: "",
@@ -3548,38 +4170,39 @@ function showHelp() {
3548
4170
  }
3549
4171
  });
3550
4172
  table.push(
3551
- [chalk8.bold("Vault Management:")],
4173
+ [chalk9.bold("Vault Management:")],
3552
4174
  [" vaults - List all vaults"],
3553
4175
  [" vault <name> - Switch to vault"],
3554
4176
  [" import <file> - Import vault from file"],
4177
+ [" delete [name] - Delete vault"],
3555
4178
  [" create - Create new vault"],
3556
4179
  [" info - Show vault details"],
3557
4180
  [" export [path] - Export vault to file"],
3558
4181
  [""],
3559
- [chalk8.bold("Wallet Operations:")],
4182
+ [chalk9.bold("Wallet Operations:")],
3560
4183
  [" balance [chain] - Show balances"],
3561
4184
  [" send <chain> <to> <amount> - Send transaction"],
3562
4185
  [" portfolio [-c usd] - Show portfolio value"],
3563
4186
  [" addresses - Show all addresses"],
3564
- [" chains [--add/--remove] - Manage chains"],
4187
+ [" chains [--add/--remove/--add-all] - Manage chains"],
3565
4188
  [" tokens <chain> - Manage tokens"],
3566
4189
  [""],
3567
- [chalk8.bold("Swap Operations:")],
4190
+ [chalk9.bold("Swap Operations:")],
3568
4191
  [" swap-chains - List swap-enabled chains"],
3569
4192
  [" swap-quote <from> <to> <amount> - Get quote"],
3570
4193
  [" swap <from> <to> <amount> - Execute swap"],
3571
4194
  [""],
3572
- [chalk8.bold("Session Commands (shell only):")],
4195
+ [chalk9.bold("Session Commands (shell only):")],
3573
4196
  [" lock - Lock vault"],
3574
4197
  [" unlock - Unlock vault"],
3575
4198
  [" status - Show vault status"],
3576
4199
  [""],
3577
- [chalk8.bold("Settings:")],
4200
+ [chalk9.bold("Settings:")],
3578
4201
  [" currency [code] - View/set currency"],
3579
4202
  [" server - Check server status"],
3580
4203
  [" address-book - Manage saved addresses"],
3581
4204
  [""],
3582
- [chalk8.bold("Help & Navigation:")],
4205
+ [chalk9.bold("Help & Navigation:")],
3583
4206
  [" help, ? - Show this help"],
3584
4207
  [" .clear - Clear screen"],
3585
4208
  [" .exit - Exit shell"]
@@ -3717,12 +4340,12 @@ var ShellSession = class {
3717
4340
  */
3718
4341
  async start() {
3719
4342
  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"));
4343
+ console.log(chalk10.cyan.bold("\n=============================================="));
4344
+ console.log(chalk10.cyan.bold(" Vultisig Interactive Shell"));
4345
+ console.log(chalk10.cyan.bold("==============================================\n"));
3723
4346
  await this.loadAllVaults();
3724
4347
  this.displayVaultList();
3725
- console.log(chalk9.gray('Type "help" for available commands, "exit" to quit\n'));
4348
+ console.log(chalk10.gray('Type "help" for available commands, "exit" to quit\n'));
3726
4349
  this.promptLoop().catch(() => {
3727
4350
  });
3728
4351
  }
@@ -3756,12 +4379,12 @@ var ShellSession = class {
3756
4379
  const now = Date.now();
3757
4380
  if (now - this.lastSigintTime < this.DOUBLE_CTRL_C_TIMEOUT) {
3758
4381
  rl.close();
3759
- console.log(chalk9.yellow("\nGoodbye!"));
4382
+ console.log(chalk10.yellow("\nGoodbye!"));
3760
4383
  this.ctx.dispose();
3761
4384
  process.exit(0);
3762
4385
  }
3763
4386
  this.lastSigintTime = now;
3764
- console.log(chalk9.yellow("\n(Press Ctrl+C again to exit)"));
4387
+ console.log(chalk10.yellow("\n(Press Ctrl+C again to exit)"));
3765
4388
  rl.close();
3766
4389
  resolve("");
3767
4390
  });
@@ -3856,7 +4479,7 @@ var ShellSession = class {
3856
4479
  stopAllSpinners();
3857
4480
  process.stdout.write("\x1B[?25h");
3858
4481
  process.stdout.write("\r\x1B[K");
3859
- console.log(chalk9.yellow("\nCancelling operation..."));
4482
+ console.log(chalk10.yellow("\nCancelling operation..."));
3860
4483
  };
3861
4484
  const cleanup = () => {
3862
4485
  process.removeListener("SIGINT", onSigint);
@@ -3893,10 +4516,10 @@ var ShellSession = class {
3893
4516
  stopAllSpinners();
3894
4517
  process.stdout.write("\x1B[?25h");
3895
4518
  process.stdout.write("\r\x1B[K");
3896
- console.log(chalk9.yellow("Operation cancelled"));
4519
+ console.log(chalk10.yellow("Operation cancelled"));
3897
4520
  return;
3898
4521
  }
3899
- console.error(chalk9.red(`
4522
+ console.error(chalk10.red(`
3900
4523
  Error: ${error2.message}`));
3901
4524
  }
3902
4525
  }
@@ -3929,11 +4552,14 @@ Error: ${error2.message}`));
3929
4552
  break;
3930
4553
  case "rename":
3931
4554
  if (args.length === 0) {
3932
- console.log(chalk9.yellow("Usage: rename <newName>"));
4555
+ console.log(chalk10.yellow("Usage: rename <newName>"));
3933
4556
  return;
3934
4557
  }
3935
4558
  await executeRename(this.ctx, args.join(" "));
3936
4559
  break;
4560
+ case "delete":
4561
+ await this.deleteVault(args);
4562
+ break;
3937
4563
  // Balance commands
3938
4564
  case "balance":
3939
4565
  case "bal":
@@ -3999,41 +4625,41 @@ Error: ${error2.message}`));
3999
4625
  // Exit
4000
4626
  case "exit":
4001
4627
  case "quit":
4002
- console.log(chalk9.yellow("\nGoodbye!"));
4628
+ console.log(chalk10.yellow("\nGoodbye!"));
4003
4629
  this.ctx.dispose();
4004
4630
  process.exit(0);
4005
4631
  break;
4006
4632
  // eslint requires break even after process.exit
4007
4633
  default:
4008
- console.log(chalk9.yellow(`Unknown command: ${command}`));
4009
- console.log(chalk9.gray('Type "help" for available commands'));
4634
+ console.log(chalk10.yellow(`Unknown command: ${command}`));
4635
+ console.log(chalk10.gray('Type "help" for available commands'));
4010
4636
  break;
4011
4637
  }
4012
4638
  }
4013
4639
  // ===== Command Helpers =====
4014
4640
  async switchVault(args) {
4015
4641
  if (args.length === 0) {
4016
- console.log(chalk9.yellow("Usage: vault <name>"));
4017
- console.log(chalk9.gray('Run "vaults" to see available vaults'));
4642
+ console.log(chalk10.yellow("Usage: vault <name>"));
4643
+ console.log(chalk10.gray('Run "vaults" to see available vaults'));
4018
4644
  return;
4019
4645
  }
4020
4646
  const vaultName = args.join(" ");
4021
4647
  const vault = this.ctx.findVaultByName(vaultName);
4022
4648
  if (!vault) {
4023
- console.log(chalk9.red(`Vault not found: ${vaultName}`));
4024
- console.log(chalk9.gray('Run "vaults" to see available vaults'));
4649
+ console.log(chalk10.red(`Vault not found: ${vaultName}`));
4650
+ console.log(chalk10.gray('Run "vaults" to see available vaults'));
4025
4651
  return;
4026
4652
  }
4027
4653
  await this.ctx.setActiveVault(vault);
4028
- console.log(chalk9.green(`
4654
+ console.log(chalk10.green(`
4029
4655
  + Switched to: ${vault.name}`));
4030
4656
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
4031
- const status = isUnlocked ? chalk9.green("Unlocked") : chalk9.yellow("Locked");
4657
+ const status = isUnlocked ? chalk10.green("Unlocked") : chalk10.yellow("Locked");
4032
4658
  console.log(`Status: ${status}`);
4033
4659
  }
4034
4660
  async importVault(args) {
4035
4661
  if (args.length === 0) {
4036
- console.log(chalk9.yellow("Usage: import <file>"));
4662
+ console.log(chalk10.yellow("Usage: import <file>"));
4037
4663
  return;
4038
4664
  }
4039
4665
  const filePath = args.join(" ");
@@ -4041,48 +4667,52 @@ Error: ${error2.message}`));
4041
4667
  this.ctx.addVault(vault);
4042
4668
  this.eventBuffer.setupVaultListeners(vault);
4043
4669
  }
4670
+ async deleteVault(args) {
4671
+ const vaultIdOrName = args.join(" ") || void 0;
4672
+ await executeDelete(this.ctx, { vaultId: vaultIdOrName });
4673
+ }
4044
4674
  async createVault(args) {
4045
4675
  const type = args[0]?.toLowerCase();
4046
4676
  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)"));
4677
+ console.log(chalk10.yellow("Usage: create <fast|secure>"));
4678
+ console.log(chalk10.gray(" create fast - Create a fast vault (server-assisted 2-of-2)"));
4679
+ console.log(chalk10.gray(" create secure - Create a secure vault (multi-device MPC)"));
4050
4680
  return;
4051
4681
  }
4052
4682
  let vault;
4053
4683
  if (type === "fast") {
4054
4684
  const name = await this.prompt("Vault name");
4055
4685
  if (!name) {
4056
- console.log(chalk9.red("Name is required"));
4686
+ console.log(chalk10.red("Name is required"));
4057
4687
  return;
4058
4688
  }
4059
4689
  const password = await this.promptPassword("Vault password");
4060
4690
  if (!password) {
4061
- console.log(chalk9.red("Password is required"));
4691
+ console.log(chalk10.red("Password is required"));
4062
4692
  return;
4063
4693
  }
4064
4694
  const email = await this.prompt("Email for verification");
4065
4695
  if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
4066
- console.log(chalk9.red("Valid email is required"));
4696
+ console.log(chalk10.red("Valid email is required"));
4067
4697
  return;
4068
4698
  }
4069
4699
  vault = await this.withCancellation((signal) => executeCreateFast(this.ctx, { name, password, email, signal }));
4070
4700
  } else {
4071
4701
  const name = await this.prompt("Vault name");
4072
4702
  if (!name) {
4073
- console.log(chalk9.red("Name is required"));
4703
+ console.log(chalk10.red("Name is required"));
4074
4704
  return;
4075
4705
  }
4076
4706
  const sharesStr = await this.prompt("Total shares (devices)", "3");
4077
4707
  const shares = parseInt(sharesStr, 10);
4078
4708
  if (isNaN(shares) || shares < 2) {
4079
- console.log(chalk9.red("Must have at least 2 shares"));
4709
+ console.log(chalk10.red("Must have at least 2 shares"));
4080
4710
  return;
4081
4711
  }
4082
4712
  const thresholdStr = await this.prompt("Signing threshold", "2");
4083
4713
  const threshold = parseInt(thresholdStr, 10);
4084
4714
  if (isNaN(threshold) || threshold < 1 || threshold > shares) {
4085
- console.log(chalk9.red(`Threshold must be between 1 and ${shares}`));
4715
+ console.log(chalk10.red(`Threshold must be between 1 and ${shares}`));
4086
4716
  return;
4087
4717
  }
4088
4718
  const password = await this.promptPassword("Vault password (optional, press Enter to skip)");
@@ -4104,37 +4734,37 @@ Error: ${error2.message}`));
4104
4734
  async importSeedphrase(args) {
4105
4735
  const type = args[0]?.toLowerCase();
4106
4736
  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)"));
4737
+ console.log(chalk10.cyan("Usage: create-from-seedphrase <fast|secure>"));
4738
+ console.log(chalk10.gray(" fast - Import with VultiServer (2-of-2)"));
4739
+ console.log(chalk10.gray(" secure - Import with device coordination (N-of-M)"));
4110
4740
  return;
4111
4741
  }
4112
- console.log(chalk9.cyan("\nEnter your recovery phrase (words separated by spaces):"));
4742
+ console.log(chalk10.cyan("\nEnter your recovery phrase (words separated by spaces):"));
4113
4743
  const mnemonic = await this.promptPassword("Seedphrase");
4114
4744
  const validation = await this.ctx.sdk.validateSeedphrase(mnemonic);
4115
4745
  if (!validation.valid) {
4116
- console.log(chalk9.red(`Invalid seedphrase: ${validation.error}`));
4746
+ console.log(chalk10.red(`Invalid seedphrase: ${validation.error}`));
4117
4747
  if (validation.invalidWords?.length) {
4118
- console.log(chalk9.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
4748
+ console.log(chalk10.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
4119
4749
  }
4120
4750
  return;
4121
4751
  }
4122
- console.log(chalk9.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
4752
+ console.log(chalk10.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
4123
4753
  let vault;
4124
4754
  if (type === "fast") {
4125
4755
  const name = await this.prompt("Vault name");
4126
4756
  if (!name) {
4127
- console.log(chalk9.red("Name is required"));
4757
+ console.log(chalk10.red("Name is required"));
4128
4758
  return;
4129
4759
  }
4130
4760
  const password = await this.promptPassword("Vault password");
4131
4761
  if (!password) {
4132
- console.log(chalk9.red("Password is required"));
4762
+ console.log(chalk10.red("Password is required"));
4133
4763
  return;
4134
4764
  }
4135
4765
  const email = await this.prompt("Email for verification");
4136
4766
  if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
4137
- console.log(chalk9.red("Valid email is required"));
4767
+ console.log(chalk10.red("Valid email is required"));
4138
4768
  return;
4139
4769
  }
4140
4770
  const discoverStr = await this.prompt("Discover chains with balances? (y/n)", "y");
@@ -4152,19 +4782,19 @@ Error: ${error2.message}`));
4152
4782
  } else {
4153
4783
  const name = await this.prompt("Vault name");
4154
4784
  if (!name) {
4155
- console.log(chalk9.red("Name is required"));
4785
+ console.log(chalk10.red("Name is required"));
4156
4786
  return;
4157
4787
  }
4158
4788
  const sharesStr = await this.prompt("Total shares (devices)", "3");
4159
4789
  const shares = parseInt(sharesStr, 10);
4160
4790
  if (isNaN(shares) || shares < 2) {
4161
- console.log(chalk9.red("Must have at least 2 shares"));
4791
+ console.log(chalk10.red("Must have at least 2 shares"));
4162
4792
  return;
4163
4793
  }
4164
4794
  const thresholdStr = await this.prompt("Signing threshold", "2");
4165
4795
  const threshold = parseInt(thresholdStr, 10);
4166
4796
  if (isNaN(threshold) || threshold < 1 || threshold > shares) {
4167
- console.log(chalk9.red(`Threshold must be between 1 and ${shares}`));
4797
+ console.log(chalk10.red(`Threshold must be between 1 and ${shares}`));
4168
4798
  return;
4169
4799
  }
4170
4800
  const password = await this.promptPassword("Vault password (optional, Enter to skip)");
@@ -4188,12 +4818,14 @@ Error: ${error2.message}`));
4188
4818
  }
4189
4819
  }
4190
4820
  async runBalance(args) {
4191
- const chainStr = args[0];
4821
+ const chainStr = args.find((arg) => !arg.startsWith("-"));
4192
4822
  const includeTokens = args.includes("-t") || args.includes("--tokens");
4823
+ const raw = args.includes("--raw");
4193
4824
  await this.withCancellation(
4194
4825
  () => executeBalance(this.ctx, {
4195
4826
  chain: chainStr ? findChainByName(chainStr) || chainStr : void 0,
4196
- includeTokens
4827
+ includeTokens,
4828
+ raw
4197
4829
  })
4198
4830
  );
4199
4831
  }
@@ -4206,15 +4838,16 @@ Error: ${error2.message}`));
4206
4838
  }
4207
4839
  }
4208
4840
  if (!fiatCurrencies3.includes(currency)) {
4209
- console.log(chalk9.red(`Invalid currency: ${currency}`));
4210
- console.log(chalk9.yellow(`Supported currencies: ${fiatCurrencies3.join(", ")}`));
4841
+ console.log(chalk10.red(`Invalid currency: ${currency}`));
4842
+ console.log(chalk10.yellow(`Supported currencies: ${fiatCurrencies3.join(", ")}`));
4211
4843
  return;
4212
4844
  }
4213
- await this.withCancellation(() => executePortfolio(this.ctx, { currency }));
4845
+ const raw = args.includes("--raw");
4846
+ await this.withCancellation(() => executePortfolio(this.ctx, { currency, raw }));
4214
4847
  }
4215
4848
  async runSend(args) {
4216
4849
  if (args.length < 3) {
4217
- console.log(chalk9.yellow("Usage: send <chain> <to> <amount> [--token <tokenId>] [--memo <memo>]"));
4850
+ console.log(chalk10.yellow("Usage: send <chain> <to> <amount> [--token <tokenId>] [--memo <memo>]"));
4218
4851
  return;
4219
4852
  }
4220
4853
  const [chainStr, to, amount, ...rest] = args;
@@ -4234,7 +4867,7 @@ Error: ${error2.message}`));
4234
4867
  await this.withAbortHandler((signal) => executeSend(this.ctx, { chain, to, amount, tokenId, memo, signal }));
4235
4868
  } catch (err) {
4236
4869
  if (err.message === "Transaction cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
4237
- console.log(chalk9.yellow("\nTransaction cancelled"));
4870
+ console.log(chalk10.yellow("\nTransaction cancelled"));
4238
4871
  return;
4239
4872
  }
4240
4873
  throw err;
@@ -4243,12 +4876,15 @@ Error: ${error2.message}`));
4243
4876
  async runChains(args) {
4244
4877
  let addChain;
4245
4878
  let removeChain;
4879
+ let addAll = false;
4246
4880
  for (let i = 0; i < args.length; i++) {
4247
- if (args[i] === "--add" && i + 1 < args.length) {
4881
+ if (args[i] === "--add-all") {
4882
+ addAll = true;
4883
+ } else if (args[i] === "--add" && i + 1 < args.length) {
4248
4884
  const chain = findChainByName(args[i + 1]);
4249
4885
  if (!chain) {
4250
- console.log(chalk9.red(`Unknown chain: ${args[i + 1]}`));
4251
- console.log(chalk9.gray("Use tab completion to see available chains"));
4886
+ console.log(chalk10.red(`Unknown chain: ${args[i + 1]}`));
4887
+ console.log(chalk10.gray("Use tab completion to see available chains"));
4252
4888
  return;
4253
4889
  }
4254
4890
  addChain = chain;
@@ -4256,19 +4892,19 @@ Error: ${error2.message}`));
4256
4892
  } else if (args[i] === "--remove" && i + 1 < args.length) {
4257
4893
  const chain = findChainByName(args[i + 1]);
4258
4894
  if (!chain) {
4259
- console.log(chalk9.red(`Unknown chain: ${args[i + 1]}`));
4260
- console.log(chalk9.gray("Use tab completion to see available chains"));
4895
+ console.log(chalk10.red(`Unknown chain: ${args[i + 1]}`));
4896
+ console.log(chalk10.gray("Use tab completion to see available chains"));
4261
4897
  return;
4262
4898
  }
4263
4899
  removeChain = chain;
4264
4900
  i++;
4265
4901
  }
4266
4902
  }
4267
- await executeChains(this.ctx, { add: addChain, remove: removeChain });
4903
+ await executeChains(this.ctx, { add: addChain, remove: removeChain, addAll });
4268
4904
  }
4269
4905
  async runTokens(args) {
4270
4906
  if (args.length === 0) {
4271
- console.log(chalk9.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
4907
+ console.log(chalk10.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
4272
4908
  return;
4273
4909
  }
4274
4910
  const chainStr = args[0];
@@ -4289,7 +4925,7 @@ Error: ${error2.message}`));
4289
4925
  async runSwapQuote(args) {
4290
4926
  if (args.length < 3) {
4291
4927
  console.log(
4292
- chalk9.yellow("Usage: swap-quote <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>]")
4928
+ chalk10.yellow("Usage: swap-quote <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>]")
4293
4929
  );
4294
4930
  return;
4295
4931
  }
@@ -4313,7 +4949,7 @@ Error: ${error2.message}`));
4313
4949
  async runSwap(args) {
4314
4950
  if (args.length < 3) {
4315
4951
  console.log(
4316
- chalk9.yellow(
4952
+ chalk10.yellow(
4317
4953
  "Usage: swap <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>] [--slippage <pct>]"
4318
4954
  )
4319
4955
  );
@@ -4344,7 +4980,7 @@ Error: ${error2.message}`));
4344
4980
  );
4345
4981
  } catch (err) {
4346
4982
  if (err.message === "Swap cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
4347
- console.log(chalk9.yellow("\nSwap cancelled"));
4983
+ console.log(chalk10.yellow("\nSwap cancelled"));
4348
4984
  return;
4349
4985
  }
4350
4986
  throw err;
@@ -4406,24 +5042,24 @@ Error: ${error2.message}`));
4406
5042
  }
4407
5043
  getPrompt() {
4408
5044
  const vault = this.ctx.getActiveVault();
4409
- if (!vault) return chalk9.cyan("wallet> ");
5045
+ if (!vault) return chalk10.cyan("wallet> ");
4410
5046
  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}> `);
5047
+ const status = isUnlocked ? chalk10.green("\u{1F513}") : chalk10.yellow("\u{1F512}");
5048
+ return chalk10.cyan(`wallet[${vault.name}]${status}> `);
4413
5049
  }
4414
5050
  displayVaultList() {
4415
5051
  const vaults = Array.from(this.ctx.getVaults().values());
4416
5052
  const activeVault = this.ctx.getActiveVault();
4417
5053
  if (vaults.length === 0) {
4418
- console.log(chalk9.yellow('No vaults found. Use "create" or "import <file>" to add a vault.\n'));
5054
+ console.log(chalk10.yellow('No vaults found. Use "create" or "import <file>" to add a vault.\n'));
4419
5055
  return;
4420
5056
  }
4421
- console.log(chalk9.cyan("Loaded Vaults:\n"));
5057
+ console.log(chalk10.cyan("Loaded Vaults:\n"));
4422
5058
  vaults.forEach((vault) => {
4423
5059
  const isActive = vault.id === activeVault?.id;
4424
5060
  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}");
5061
+ const activeMarker = isActive ? chalk10.green(" (active)") : "";
5062
+ const lockIcon = isUnlocked ? chalk10.green("\u{1F513}") : chalk10.yellow("\u{1F512}");
4427
5063
  console.log(` ${lockIcon} ${vault.name}${activeMarker} - ${vault.type}`);
4428
5064
  });
4429
5065
  console.log();
@@ -4431,10 +5067,10 @@ Error: ${error2.message}`));
4431
5067
  };
4432
5068
 
4433
5069
  // src/lib/errors.ts
4434
- import chalk10 from "chalk";
5070
+ import chalk11 from "chalk";
4435
5071
 
4436
5072
  // src/lib/version.ts
4437
- import chalk11 from "chalk";
5073
+ import chalk12 from "chalk";
4438
5074
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
4439
5075
  import { homedir } from "os";
4440
5076
  import { join } from "path";
@@ -4442,7 +5078,7 @@ var cachedVersion = null;
4442
5078
  function getVersion() {
4443
5079
  if (cachedVersion) return cachedVersion;
4444
5080
  if (true) {
4445
- cachedVersion = "0.3.0";
5081
+ cachedVersion = "0.5.0";
4446
5082
  return cachedVersion;
4447
5083
  }
4448
5084
  try {
@@ -4539,7 +5175,7 @@ function formatVersionShort() {
4539
5175
  }
4540
5176
  function formatVersionDetailed() {
4541
5177
  const lines = [];
4542
- lines.push(chalk11.bold(`Vultisig CLI v${getVersion()}`));
5178
+ lines.push(chalk12.bold(`Vultisig CLI v${getVersion()}`));
4543
5179
  lines.push("");
4544
5180
  lines.push(` Node.js: ${process.version}`);
4545
5181
  lines.push(` Platform: ${process.platform}-${process.arch}`);
@@ -4916,7 +5552,7 @@ async function init(vaultOverride, unlockPassword) {
4916
5552
  if (unlockPassword && vaultSelector) {
4917
5553
  cachePassword(vaultSelector, unlockPassword);
4918
5554
  }
4919
- const sdk = new Vultisig4({
5555
+ const sdk = new Vultisig5({
4920
5556
  onPasswordRequired: createPasswordCallback()
4921
5557
  });
4922
5558
  await sdk.initialize();
@@ -5001,7 +5637,7 @@ async function promptQrPayload() {
5001
5637
  ]);
5002
5638
  return answer.qrPayload.trim();
5003
5639
  }
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(
5640
+ 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
5641
  withExit(
5006
5642
  async (options) => {
5007
5643
  const context = await init(program.opts().vault);
@@ -5028,12 +5664,13 @@ createFromSeedphraseCmd.command("fast").description("Create FastVault from seedp
5028
5664
  password: options.password,
5029
5665
  email: options.email,
5030
5666
  discoverChains: options.discoverChains,
5031
- chains
5667
+ chains,
5668
+ usePhantomSolanaPath: options.usePhantomSolanaPath
5032
5669
  });
5033
5670
  }
5034
5671
  )
5035
5672
  );
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(
5673
+ 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
5674
  withExit(
5038
5675
  async (options) => {
5039
5676
  const context = await init(program.opts().vault);
@@ -5061,7 +5698,8 @@ createFromSeedphraseCmd.command("secure").description("Create SecureVault from s
5061
5698
  threshold: parseInt(options.threshold, 10),
5062
5699
  shares: parseInt(options.shares, 10),
5063
5700
  discoverChains: options.discoverChains,
5064
- chains
5701
+ chains,
5702
+ usePhantomSolanaPath: options.usePhantomSolanaPath
5065
5703
  });
5066
5704
  }
5067
5705
  )
@@ -5106,24 +5744,27 @@ program.command("verify <vaultId>").description("Verify vault with email verific
5106
5744
  }
5107
5745
  )
5108
5746
  );
5109
- program.command("balance [chain]").description("Show balance for a chain or all chains").option("-t, --tokens", "Include token balances").action(
5747
+ 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
5748
  withExit(async (chainStr, options) => {
5111
5749
  const context = await init(program.opts().vault);
5112
5750
  await executeBalance(context, {
5113
5751
  chain: chainStr ? findChainByName(chainStr) || chainStr : void 0,
5114
- includeTokens: options.tokens
5752
+ includeTokens: options.tokens,
5753
+ raw: options.raw
5115
5754
  });
5116
5755
  })
5117
5756
  );
5118
- program.command("send <chain> <to> <amount>").description("Send tokens to an address").option("--token <tokenId>", "Token to send (default: native)").option("--memo <memo>", "Transaction memo").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").action(
5757
+ program.command("send <chain> <to> [amount]").description("Send tokens to an address").option("--max", "Send maximum amount (balance minus fees)").option("--token <tokenId>", "Token to send (default: native)").option("--memo <memo>", "Transaction memo").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").action(
5119
5758
  withExit(
5120
5759
  async (chainStr, to, amount, options) => {
5760
+ if (!amount && !options.max) throw new Error("Provide an amount or use --max");
5761
+ if (amount && options.max) throw new Error("Cannot specify both amount and --max");
5121
5762
  const context = await init(program.opts().vault);
5122
5763
  try {
5123
5764
  await executeSend(context, {
5124
5765
  chain: findChainByName(chainStr) || chainStr,
5125
5766
  to,
5126
- amount,
5767
+ amount: amount ?? "max",
5127
5768
  tokenId: options.token,
5128
5769
  memo: options.memo,
5129
5770
  yes: options.yes,
@@ -5139,6 +5780,30 @@ program.command("send <chain> <to> <amount>").description("Send tokens to an add
5139
5780
  }
5140
5781
  )
5141
5782
  );
5783
+ program.command("execute <chain> <contract> <msg>").description("Execute a CosmWasm smart contract (THORChain, MayaChain)").option("--funds <funds>", 'Funds to send with execution (format: "denom:amount" or "denom:amount,denom2:amount2")').option("--memo <memo>", "Transaction memo").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").action(
5784
+ withExit(
5785
+ async (chainStr, contract, msg, options) => {
5786
+ const context = await init(program.opts().vault, options.password);
5787
+ try {
5788
+ await executeExecute(context, {
5789
+ chain: findChainByName(chainStr) || chainStr,
5790
+ contract,
5791
+ msg,
5792
+ funds: options.funds,
5793
+ memo: options.memo,
5794
+ yes: options.yes,
5795
+ password: options.password
5796
+ });
5797
+ } catch (err) {
5798
+ if (err.message === "Transaction cancelled by user") {
5799
+ warn("\nx Transaction cancelled");
5800
+ return;
5801
+ }
5802
+ throw err;
5803
+ }
5804
+ }
5805
+ )
5806
+ );
5142
5807
  program.command("sign").description("Sign pre-hashed bytes (for externally constructed transactions)").requiredOption("--chain <chain>", "Target blockchain").requiredOption("--bytes <base64>", "Base64-encoded pre-hashed data to sign").option("--password <password>", "Vault password for signing").action(
5143
5808
  withExit(async (options) => {
5144
5809
  const context = await init(program.opts().vault, options.password);
@@ -5158,10 +5823,13 @@ program.command("broadcast").description("Broadcast a pre-signed raw transaction
5158
5823
  });
5159
5824
  })
5160
5825
  );
5161
- program.command("portfolio").description("Show total portfolio value").option("-c, --currency <currency>", "Fiat currency (usd, eur, gbp, etc.)", "usd").action(
5826
+ 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
5827
  withExit(async (options) => {
5163
5828
  const context = await init(program.opts().vault);
5164
- await executePortfolio(context, { currency: options.currency.toLowerCase() });
5829
+ await executePortfolio(context, {
5830
+ currency: options.currency.toLowerCase(),
5831
+ raw: options.raw
5832
+ });
5165
5833
  })
5166
5834
  );
5167
5835
  program.command("currency [newCurrency]").description("View or set the vault currency preference").action(
@@ -5176,6 +5844,12 @@ program.command("server").description("Check server connectivity and status").ac
5176
5844
  await executeServer(context);
5177
5845
  })
5178
5846
  );
5847
+ program.command("discount").description("Show your VULT discount tier for swap fees").option("--refresh", "Force refresh tier from blockchain").action(
5848
+ withExit(async (options) => {
5849
+ const context = await init(program.opts().vault);
5850
+ await executeDiscount(context, { refresh: options.refresh });
5851
+ })
5852
+ );
5179
5853
  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
5854
  withExit(async (path3, options) => {
5181
5855
  const context = await init(program.opts().vault, options.password);
@@ -5204,11 +5878,12 @@ program.command("address-book").description("Manage address book entries").optio
5204
5878
  });
5205
5879
  })
5206
5880
  );
5207
- program.command("chains").description("List and manage chains").option("--add <chain>", "Add a chain").option("--remove <chain>", "Remove a chain").action(
5881
+ 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
5882
  withExit(async (options) => {
5209
5883
  const context = await init(program.opts().vault);
5210
5884
  await executeChains(context, {
5211
5885
  add: options.add ? findChainByName(options.add) || options.add : void 0,
5886
+ addAll: options.addAll,
5212
5887
  remove: options.remove ? findChainByName(options.remove) || options.remove : void 0
5213
5888
  });
5214
5889
  })
@@ -5237,6 +5912,15 @@ program.command("info").description("Show detailed vault information").action(
5237
5912
  await executeInfo(context);
5238
5913
  })
5239
5914
  );
5915
+ program.command("delete [vault]").description("Delete a vault from local storage").option("-y, --yes", "Skip confirmation prompt").action(
5916
+ withExit(async (vaultIdOrName, options) => {
5917
+ const context = await init(program.opts().vault);
5918
+ await executeDelete(context, {
5919
+ vaultId: vaultIdOrName,
5920
+ skipConfirmation: options.yes
5921
+ });
5922
+ })
5923
+ );
5240
5924
  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
5925
  withExit(
5242
5926
  async (chainStr, options) => {
@@ -5258,29 +5942,33 @@ program.command("swap-chains").description("List chains that support swaps").act
5258
5942
  await executeSwapChains(context);
5259
5943
  })
5260
5944
  );
5261
- program.command("swap-quote <fromChain> <toChain> <amount>").description("Get a swap quote without executing").option("--from-token <address>", "Token address to swap from (default: native)").option("--to-token <address>", "Token address to swap to (default: native)").action(
5945
+ program.command("swap-quote <fromChain> <toChain> [amount]").description("Get a swap quote without executing").option("--max", "Swap maximum amount (full balance minus fees for native)").option("--from-token <address>", "Token address to swap from (default: native)").option("--to-token <address>", "Token address to swap to (default: native)").action(
5262
5946
  withExit(
5263
5947
  async (fromChainStr, toChainStr, amountStr, options) => {
5948
+ if (!amountStr && !options.max) throw new Error("Provide an amount or use --max");
5949
+ if (amountStr && options.max) throw new Error("Cannot specify both amount and --max");
5264
5950
  const context = await init(program.opts().vault);
5265
5951
  await executeSwapQuote(context, {
5266
5952
  fromChain: findChainByName(fromChainStr) || fromChainStr,
5267
5953
  toChain: findChainByName(toChainStr) || toChainStr,
5268
- amount: parseFloat(amountStr),
5954
+ amount: options.max ? "max" : parseFloat(amountStr),
5269
5955
  fromToken: options.fromToken,
5270
5956
  toToken: options.toToken
5271
5957
  });
5272
5958
  }
5273
5959
  )
5274
5960
  );
5275
- program.command("swap <fromChain> <toChain> <amount>").description("Swap tokens between chains").option("--from-token <address>", "Token address to swap from (default: native)").option("--to-token <address>", "Token address to swap to (default: native)").option("--slippage <percent>", "Slippage tolerance in percent", "1").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").action(
5961
+ program.command("swap <fromChain> <toChain> [amount]").description("Swap tokens between chains").option("--max", "Swap maximum amount (full balance minus fees for native)").option("--from-token <address>", "Token address to swap from (default: native)").option("--to-token <address>", "Token address to swap to (default: native)").option("--slippage <percent>", "Slippage tolerance in percent", "1").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").action(
5276
5962
  withExit(
5277
5963
  async (fromChainStr, toChainStr, amountStr, options) => {
5964
+ if (!amountStr && !options.max) throw new Error("Provide an amount or use --max");
5965
+ if (amountStr && options.max) throw new Error("Cannot specify both amount and --max");
5278
5966
  const context = await init(program.opts().vault);
5279
5967
  try {
5280
5968
  await executeSwap(context, {
5281
5969
  fromChain: findChainByName(fromChainStr) || fromChainStr,
5282
5970
  toChain: findChainByName(toChainStr) || toChainStr,
5283
- amount: parseFloat(amountStr),
5971
+ amount: options.max ? "max" : parseFloat(amountStr),
5284
5972
  fromToken: options.fromToken,
5285
5973
  toToken: options.toToken,
5286
5974
  slippage: options.slippage ? parseFloat(options.slippage) : void 0,
@@ -5297,14 +5985,80 @@ program.command("swap <fromChain> <toChain> <amount>").description("Swap tokens
5297
5985
  }
5298
5986
  )
5299
5987
  );
5988
+ var rujiraCmd = program.command("rujira").description("Rujira FIN swaps + secured asset tools on THORChain");
5989
+ rujiraCmd.command("balance").description("Show secured asset balances on THORChain").option("--secured-only", "Filter to secured/FIN-like denoms only").option("--rpc <url>", "Override THORChain RPC endpoint").option("--rest <url>", "Override THORNode REST endpoint").action(
5990
+ withExit(async (options) => {
5991
+ const context = await init(program.opts().vault);
5992
+ await executeRujiraBalance(context, {
5993
+ securedOnly: options.securedOnly,
5994
+ rpcEndpoint: options.rpc,
5995
+ restEndpoint: options.rest
5996
+ });
5997
+ })
5998
+ );
5999
+ rujiraCmd.command("routes").description("List available FIN swap routes").action(
6000
+ withExit(async () => {
6001
+ await executeRujiraRoutes();
6002
+ })
6003
+ );
6004
+ rujiraCmd.command("deposit").description("Show deposit instructions (inbound address + memo)").option("--asset <asset>", "L1 asset to deposit (e.g., BTC.BTC, ETH.ETH)").option("--amount <amount>", "Amount in base units (optional; used for validation)", "1").option("--affiliate <thorAddress>", "Affiliate THOR address (optional)").option("--affiliate-bps <bps>", "Affiliate fee in basis points (optional)", "0").option("--rpc <url>", "Override THORChain RPC endpoint").option("--rest <url>", "Override THORNode REST endpoint").action(
6005
+ withExit(
6006
+ async (options) => {
6007
+ const context = await init(program.opts().vault);
6008
+ await executeRujiraDeposit(context, {
6009
+ asset: options.asset,
6010
+ amount: options.amount,
6011
+ affiliate: options.affiliate,
6012
+ affiliateBps: options.affiliateBps ? parseInt(options.affiliateBps, 10) : void 0,
6013
+ rpcEndpoint: options.rpc,
6014
+ restEndpoint: options.rest
6015
+ });
6016
+ }
6017
+ )
6018
+ );
6019
+ rujiraCmd.command("swap <fromAsset> <toAsset> <amount>").description("Execute a FIN swap (amount in base units)").option("--slippage-bps <bps>", "Slippage tolerance in basis points (default: 100 = 1%)", "100").option("--destination <thorAddress>", "Destination THOR address (default: vault THORChain address)").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").option("--rpc <url>", "Override THORChain RPC endpoint").option("--rest <url>", "Override THORNode REST endpoint").action(
6020
+ withExit(
6021
+ async (fromAsset, toAsset, amount, options) => {
6022
+ const context = await init(program.opts().vault, options.password);
6023
+ await executeRujiraSwap(context, {
6024
+ fromAsset,
6025
+ toAsset,
6026
+ amount,
6027
+ slippageBps: options.slippageBps ? parseInt(options.slippageBps, 10) : void 0,
6028
+ destination: options.destination,
6029
+ yes: options.yes,
6030
+ password: options.password,
6031
+ rpcEndpoint: options.rpc,
6032
+ restEndpoint: options.rest
6033
+ });
6034
+ }
6035
+ )
6036
+ );
6037
+ rujiraCmd.command("withdraw <asset> <amount> <l1Address>").description("Withdraw secured assets to L1 (amount in base units)").option("--max-fee-bps <bps>", "Max outbound fee as bps of amount (optional)").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").option("--rpc <url>", "Override THORChain RPC endpoint").option("--rest <url>", "Override THORNode REST endpoint").action(
6038
+ withExit(
6039
+ async (asset, amount, l1Address, options) => {
6040
+ const context = await init(program.opts().vault, options.password);
6041
+ await executeRujiraWithdraw(context, {
6042
+ asset,
6043
+ amount,
6044
+ l1Address,
6045
+ maxFeeBps: options.maxFeeBps ? parseInt(options.maxFeeBps, 10) : void 0,
6046
+ yes: options.yes,
6047
+ password: options.password,
6048
+ rpcEndpoint: options.rpc,
6049
+ restEndpoint: options.rest
6050
+ });
6051
+ }
6052
+ )
6053
+ );
5300
6054
  program.command("version").description("Show detailed version information").action(
5301
6055
  withExit(async () => {
5302
6056
  printResult(formatVersionDetailed());
5303
6057
  const result = await checkForUpdates();
5304
6058
  if (result?.updateAvailable && result.latestVersion) {
5305
6059
  info("");
5306
- info(chalk12.yellow(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
5307
- info(chalk12.gray(`Run "${getUpdateCommand()}" to update`));
6060
+ info(chalk13.yellow(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
6061
+ info(chalk13.gray(`Run "${getUpdateCommand()}" to update`));
5308
6062
  }
5309
6063
  })
5310
6064
  );
@@ -5313,28 +6067,28 @@ program.command("update").description("Check for updates and show update command
5313
6067
  info("Checking for updates...");
5314
6068
  const result = await checkForUpdates();
5315
6069
  if (!result) {
5316
- printResult(chalk12.gray("Update checking is disabled"));
6070
+ printResult(chalk13.gray("Update checking is disabled"));
5317
6071
  return;
5318
6072
  }
5319
6073
  if (result.updateAvailable && result.latestVersion) {
5320
6074
  printResult("");
5321
- printResult(chalk12.green(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
6075
+ printResult(chalk13.green(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
5322
6076
  printResult("");
5323
6077
  if (options.check) {
5324
6078
  printResult(`Run "${getUpdateCommand()}" to update`);
5325
6079
  } else {
5326
6080
  const updateCmd = getUpdateCommand();
5327
6081
  printResult(`To update, run:`);
5328
- printResult(chalk12.cyan(` ${updateCmd}`));
6082
+ printResult(chalk13.cyan(` ${updateCmd}`));
5329
6083
  }
5330
6084
  } else {
5331
- printResult(chalk12.green(`You're on the latest version (${result.currentVersion})`));
6085
+ printResult(chalk13.green(`You're on the latest version (${result.currentVersion})`));
5332
6086
  }
5333
6087
  })
5334
6088
  );
5335
6089
  setupCompletionCommand(program);
5336
6090
  async function startInteractiveMode() {
5337
- const sdk = new Vultisig4({
6091
+ const sdk = new Vultisig5({
5338
6092
  onPasswordRequired: createPasswordCallback()
5339
6093
  });
5340
6094
  await sdk.initialize();