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