@vultisig/cli 0.15.4 → 0.17.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 +55 -0
- package/dist/index.js +2678 -683
- package/package.json +9 -4
package/dist/index.js
CHANGED
|
@@ -1049,14 +1049,14 @@ var require_main = __commonJS({
|
|
|
1049
1049
|
cb = opts;
|
|
1050
1050
|
opts = {};
|
|
1051
1051
|
}
|
|
1052
|
-
var
|
|
1053
|
-
|
|
1054
|
-
|
|
1052
|
+
var qrcode4 = new QRCode(-1, this.error);
|
|
1053
|
+
qrcode4.addData(input);
|
|
1054
|
+
qrcode4.make();
|
|
1055
1055
|
var output = "";
|
|
1056
1056
|
if (opts && opts.small) {
|
|
1057
1057
|
var BLACK = true, WHITE = false;
|
|
1058
|
-
var moduleCount =
|
|
1059
|
-
var moduleData =
|
|
1058
|
+
var moduleCount = qrcode4.getModuleCount();
|
|
1059
|
+
var moduleData = qrcode4.modules.slice();
|
|
1060
1060
|
var oddRow = moduleCount % 2 === 1;
|
|
1061
1061
|
if (oddRow) {
|
|
1062
1062
|
moduleData.push(fill(moduleCount, WHITE));
|
|
@@ -1089,9 +1089,9 @@ var require_main = __commonJS({
|
|
|
1089
1089
|
output += borderBottom;
|
|
1090
1090
|
}
|
|
1091
1091
|
} else {
|
|
1092
|
-
var border = repeat(white).times(
|
|
1092
|
+
var border = repeat(white).times(qrcode4.getModuleCount() + 3);
|
|
1093
1093
|
output += border + "\n";
|
|
1094
|
-
|
|
1094
|
+
qrcode4.modules.forEach(function(row2) {
|
|
1095
1095
|
output += white;
|
|
1096
1096
|
output += row2.map(toCell).join("");
|
|
1097
1097
|
output += white + "\n";
|
|
@@ -1430,7 +1430,7 @@ var init_sha3 = __esm({
|
|
|
1430
1430
|
}
|
|
1431
1431
|
});
|
|
1432
1432
|
|
|
1433
|
-
// node_modules/viem/_esm/utils/unit/formatUnits.js
|
|
1433
|
+
// ../../node_modules/viem/_esm/utils/unit/formatUnits.js
|
|
1434
1434
|
function formatUnits(value, decimals) {
|
|
1435
1435
|
let display = value.toString();
|
|
1436
1436
|
const negative = display.startsWith("-");
|
|
@@ -1445,16 +1445,991 @@ function formatUnits(value, decimals) {
|
|
|
1445
1445
|
return `${negative ? "-" : ""}${integer || "0"}${fraction ? `.${fraction}` : ""}`;
|
|
1446
1446
|
}
|
|
1447
1447
|
var init_formatUnits = __esm({
|
|
1448
|
-
"node_modules/viem/_esm/utils/unit/formatUnits.js"() {
|
|
1448
|
+
"../../node_modules/viem/_esm/utils/unit/formatUnits.js"() {
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
// ../../packages/lib/utils/dist/promise/isPromise.js
|
|
1453
|
+
function isPromise(value) {
|
|
1454
|
+
return !!value && typeof value.then === "function";
|
|
1455
|
+
}
|
|
1456
|
+
var init_isPromise = __esm({
|
|
1457
|
+
"../../packages/lib/utils/dist/promise/isPromise.js"() {
|
|
1458
|
+
}
|
|
1459
|
+
});
|
|
1460
|
+
|
|
1461
|
+
// ../../packages/lib/utils/dist/attempt.js
|
|
1462
|
+
function attempt(input) {
|
|
1463
|
+
if (typeof input === "function") {
|
|
1464
|
+
try {
|
|
1465
|
+
const result = input();
|
|
1466
|
+
if (isPromise(result)) {
|
|
1467
|
+
return attempt(result);
|
|
1468
|
+
}
|
|
1469
|
+
return { data: result };
|
|
1470
|
+
} catch (error2) {
|
|
1471
|
+
return { error: error2 };
|
|
1472
|
+
}
|
|
1473
|
+
} else {
|
|
1474
|
+
return input.then((data) => ({ data }), (error2) => ({ error: error2 }));
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
function withFallback(result, fallback) {
|
|
1478
|
+
if (isPromise(result)) {
|
|
1479
|
+
return result.then((res) => {
|
|
1480
|
+
if ("error" in res) {
|
|
1481
|
+
return fallback;
|
|
1482
|
+
}
|
|
1483
|
+
return res.data;
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
if ("error" in result) {
|
|
1487
|
+
return fallback;
|
|
1488
|
+
}
|
|
1489
|
+
return result.data;
|
|
1490
|
+
}
|
|
1491
|
+
var init_attempt = __esm({
|
|
1492
|
+
"../../packages/lib/utils/dist/attempt.js"() {
|
|
1493
|
+
init_isPromise();
|
|
1494
|
+
}
|
|
1495
|
+
});
|
|
1496
|
+
|
|
1497
|
+
// ../../packages/lib/utils/dist/error/extractErrorMsg/index.js
|
|
1498
|
+
var extractErrorMsg;
|
|
1499
|
+
var init_extractErrorMsg = __esm({
|
|
1500
|
+
"../../packages/lib/utils/dist/error/extractErrorMsg/index.js"() {
|
|
1501
|
+
init_attempt();
|
|
1502
|
+
extractErrorMsg = (err) => {
|
|
1503
|
+
if (typeof err === "string") {
|
|
1504
|
+
return err;
|
|
1505
|
+
}
|
|
1506
|
+
if (typeof err === "number" || typeof err === "boolean") {
|
|
1507
|
+
return String(err);
|
|
1508
|
+
}
|
|
1509
|
+
if (typeof err === "object" && err && "message" in err) {
|
|
1510
|
+
return extractErrorMsg(err.message);
|
|
1511
|
+
}
|
|
1512
|
+
return withFallback(attempt(() => JSON.stringify(err)), "Unknown Error");
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
});
|
|
1516
|
+
|
|
1517
|
+
// ../../packages/lib/utils/dist/array/isEmpty/index.js
|
|
1518
|
+
var isEmpty;
|
|
1519
|
+
var init_isEmpty = __esm({
|
|
1520
|
+
"../../packages/lib/utils/dist/array/isEmpty/index.js"() {
|
|
1521
|
+
isEmpty = (items) => items.length === 0;
|
|
1522
|
+
}
|
|
1523
|
+
});
|
|
1524
|
+
|
|
1525
|
+
// ../../packages/lib/utils/dist/promise/asyncFallbackChain/index.js
|
|
1526
|
+
var asyncFallbackChain;
|
|
1527
|
+
var init_asyncFallbackChain = __esm({
|
|
1528
|
+
"../../packages/lib/utils/dist/promise/asyncFallbackChain/index.js"() {
|
|
1529
|
+
init_isEmpty();
|
|
1530
|
+
asyncFallbackChain = async (...functions) => {
|
|
1531
|
+
if (isEmpty(functions)) {
|
|
1532
|
+
throw new Error("No functions provided");
|
|
1533
|
+
}
|
|
1534
|
+
try {
|
|
1535
|
+
const result = await functions[0]();
|
|
1536
|
+
return result;
|
|
1537
|
+
} catch (error2) {
|
|
1538
|
+
if (functions.length <= 1) {
|
|
1539
|
+
throw error2;
|
|
1540
|
+
}
|
|
1541
|
+
return asyncFallbackChain(...functions.slice(1));
|
|
1542
|
+
}
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
});
|
|
1546
|
+
|
|
1547
|
+
// ../../packages/lib/utils/dist/fetch/HttpResponseError.js
|
|
1548
|
+
var HttpResponseError;
|
|
1549
|
+
var init_HttpResponseError = __esm({
|
|
1550
|
+
"../../packages/lib/utils/dist/fetch/HttpResponseError.js"() {
|
|
1551
|
+
HttpResponseError = class extends Error {
|
|
1552
|
+
status;
|
|
1553
|
+
statusText;
|
|
1554
|
+
url;
|
|
1555
|
+
body;
|
|
1556
|
+
constructor(opts) {
|
|
1557
|
+
super(opts.message);
|
|
1558
|
+
this.name = "HttpResponseError";
|
|
1559
|
+
this.status = opts.status;
|
|
1560
|
+
this.statusText = opts.statusText;
|
|
1561
|
+
this.url = opts.url;
|
|
1562
|
+
this.body = opts.body;
|
|
1563
|
+
}
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
});
|
|
1567
|
+
|
|
1568
|
+
// ../../packages/lib/utils/dist/fetch/assertFetchResponse.js
|
|
1569
|
+
var assertFetchResponse;
|
|
1570
|
+
var init_assertFetchResponse = __esm({
|
|
1571
|
+
"../../packages/lib/utils/dist/fetch/assertFetchResponse.js"() {
|
|
1572
|
+
init_extractErrorMsg();
|
|
1573
|
+
init_asyncFallbackChain();
|
|
1574
|
+
init_HttpResponseError();
|
|
1575
|
+
assertFetchResponse = async (response) => {
|
|
1576
|
+
if (!response.ok) {
|
|
1577
|
+
const body = await asyncFallbackChain(async () => response.json(), async () => response.text(), async () => `HTTP ${response.status} ${response.statusText || "Error"}: Request failed for ${response.url}`);
|
|
1578
|
+
const msg = extractErrorMsg(body);
|
|
1579
|
+
throw new HttpResponseError({
|
|
1580
|
+
message: msg,
|
|
1581
|
+
status: response.status,
|
|
1582
|
+
statusText: response.statusText,
|
|
1583
|
+
url: response.url,
|
|
1584
|
+
body
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
});
|
|
1590
|
+
|
|
1591
|
+
// ../../packages/lib/utils/dist/record/getRecordKeys/index.js
|
|
1592
|
+
var getRecordKeys;
|
|
1593
|
+
var init_getRecordKeys = __esm({
|
|
1594
|
+
"../../packages/lib/utils/dist/record/getRecordKeys/index.js"() {
|
|
1595
|
+
getRecordKeys = (record) => {
|
|
1596
|
+
return Object.keys(record);
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
});
|
|
1600
|
+
|
|
1601
|
+
// ../../packages/lib/utils/dist/record/withoutUndefinedFields.js
|
|
1602
|
+
function withoutUndefinedFields(record) {
|
|
1603
|
+
const result = {};
|
|
1604
|
+
getRecordKeys(record).forEach((key) => {
|
|
1605
|
+
const typedKey = key;
|
|
1606
|
+
const value = record[typedKey];
|
|
1607
|
+
if (value !== void 0) {
|
|
1608
|
+
result[typedKey] = value;
|
|
1609
|
+
}
|
|
1610
|
+
});
|
|
1611
|
+
return result;
|
|
1612
|
+
}
|
|
1613
|
+
var init_withoutUndefinedFields = __esm({
|
|
1614
|
+
"../../packages/lib/utils/dist/record/withoutUndefinedFields.js"() {
|
|
1615
|
+
init_getRecordKeys();
|
|
1616
|
+
}
|
|
1617
|
+
});
|
|
1618
|
+
|
|
1619
|
+
// ../../packages/lib/utils/dist/query/queryUrl.js
|
|
1620
|
+
async function queryUrl(url, options = {}) {
|
|
1621
|
+
const { responseType = "json", body, headers, method } = options;
|
|
1622
|
+
const response = await fetch(url, withoutUndefinedFields({
|
|
1623
|
+
method: method ?? (body ? "POST" : "GET"),
|
|
1624
|
+
headers: withoutUndefinedFields({
|
|
1625
|
+
...headers,
|
|
1626
|
+
"Content-Type": body ? "application/json" : void 0
|
|
1627
|
+
}),
|
|
1628
|
+
body: processBody(body)
|
|
1629
|
+
}));
|
|
1630
|
+
await assertFetchResponse(response);
|
|
1631
|
+
if (responseType !== "none") {
|
|
1632
|
+
return response[responseType]();
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
var processBody;
|
|
1636
|
+
var init_queryUrl = __esm({
|
|
1637
|
+
"../../packages/lib/utils/dist/query/queryUrl.js"() {
|
|
1638
|
+
init_assertFetchResponse();
|
|
1639
|
+
init_withoutUndefinedFields();
|
|
1640
|
+
processBody = (body) => {
|
|
1641
|
+
if (body === void 0) {
|
|
1642
|
+
return void 0;
|
|
1643
|
+
}
|
|
1644
|
+
if (typeof body === "string") {
|
|
1645
|
+
return body;
|
|
1646
|
+
}
|
|
1647
|
+
return JSON.stringify(body);
|
|
1648
|
+
};
|
|
1649
|
+
}
|
|
1650
|
+
});
|
|
1651
|
+
|
|
1652
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/lp/pools.js
|
|
1653
|
+
var thorchainMidgardBaseUrl, POOL_ID_RE, assertValidPoolId, isValidPoolId, normalizePool, getThorchainPools;
|
|
1654
|
+
var init_pools = __esm({
|
|
1655
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/lp/pools.js"() {
|
|
1656
|
+
init_queryUrl();
|
|
1657
|
+
thorchainMidgardBaseUrl = "https://midgard.ninerealms.com";
|
|
1658
|
+
POOL_ID_RE = /^[A-Z0-9]+\.[A-Z0-9]+(-[A-Z0-9]+)?$/;
|
|
1659
|
+
assertValidPoolId = (pool) => {
|
|
1660
|
+
if (typeof pool !== "string" || pool.length === 0) {
|
|
1661
|
+
throw new Error(`assertValidPoolId: pool id must be a non-empty string, got ${typeof pool} ${pool === "" ? '""' : ""}`);
|
|
1662
|
+
}
|
|
1663
|
+
if (!POOL_ID_RE.test(pool)) {
|
|
1664
|
+
throw new Error(`assertValidPoolId: ${JSON.stringify(pool)} is not a valid THORChain pool id. Expected uppercase CHAIN.ASSET (e.g. "BTC.BTC") or CHAIN.ASSET-CONTRACT (e.g. "ETH.USDC-0XA0B86991C6218B36C1D19D4A2E9EB0CE3606EB48").`);
|
|
1665
|
+
}
|
|
1666
|
+
};
|
|
1667
|
+
isValidPoolId = (pool) => {
|
|
1668
|
+
if (typeof pool !== "string" || pool.length === 0)
|
|
1669
|
+
return false;
|
|
1670
|
+
return POOL_ID_RE.test(pool);
|
|
1671
|
+
};
|
|
1672
|
+
normalizePool = (raw) => ({
|
|
1673
|
+
asset: raw.asset ?? "",
|
|
1674
|
+
status: raw.status ?? "",
|
|
1675
|
+
assetDepth: raw.assetDepth ?? "0",
|
|
1676
|
+
runeDepth: raw.runeDepth ?? "0",
|
|
1677
|
+
liquidityUnits: raw.liquidityUnits ?? "0",
|
|
1678
|
+
volume24h: raw.volume24h ?? "0",
|
|
1679
|
+
annualPercentageRate: raw.annualPercentageRate ?? "0"
|
|
1680
|
+
});
|
|
1681
|
+
getThorchainPools = async (options = {}) => {
|
|
1682
|
+
const status = options.status === void 0 ? "available" : options.status;
|
|
1683
|
+
const url = status === null ? `${thorchainMidgardBaseUrl}/v2/pools` : `${thorchainMidgardBaseUrl}/v2/pools?status=${encodeURIComponent(status)}`;
|
|
1684
|
+
const raw = await queryUrl(url);
|
|
1685
|
+
if (!Array.isArray(raw)) {
|
|
1686
|
+
throw new Error(`getThorchainPools: expected an array from ${url}, got ${typeof raw}`);
|
|
1687
|
+
}
|
|
1688
|
+
return raw.map(normalizePool);
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
});
|
|
1692
|
+
|
|
1693
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/lp/memo.js
|
|
1694
|
+
var assertMemoSegmentSafe, addLpMemo, removeLpMemo;
|
|
1695
|
+
var init_memo = __esm({
|
|
1696
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/lp/memo.js"() {
|
|
1697
|
+
init_pools();
|
|
1698
|
+
assertMemoSegmentSafe = (value, fieldName) => {
|
|
1699
|
+
if (typeof value !== "string") {
|
|
1700
|
+
throw new Error(`${fieldName} must be a string, got ${typeof value}`);
|
|
1701
|
+
}
|
|
1702
|
+
if (value.includes(":")) {
|
|
1703
|
+
throw new Error(`${fieldName} must not contain \`:\` (would inject extra memo segments), got ${JSON.stringify(value)}`);
|
|
1704
|
+
}
|
|
1705
|
+
if (/\s/.test(value)) {
|
|
1706
|
+
throw new Error(`${fieldName} must not contain whitespace, got ${JSON.stringify(value)}`);
|
|
1707
|
+
}
|
|
1708
|
+
};
|
|
1709
|
+
addLpMemo = ({ pool, pairedAddress }) => {
|
|
1710
|
+
assertValidPoolId(pool);
|
|
1711
|
+
if (pairedAddress && pairedAddress.length > 0) {
|
|
1712
|
+
assertMemoSegmentSafe(pairedAddress, "pairedAddress");
|
|
1713
|
+
return `+:${pool}:${pairedAddress}`;
|
|
1714
|
+
}
|
|
1715
|
+
return `+:${pool}`;
|
|
1716
|
+
};
|
|
1717
|
+
removeLpMemo = ({ pool, basisPoints, withdrawToAsset }) => {
|
|
1718
|
+
assertValidPoolId(pool);
|
|
1719
|
+
if (!Number.isInteger(basisPoints) || basisPoints < 1 || basisPoints > 1e4) {
|
|
1720
|
+
throw new Error(`removeLpMemo: basisPoints must be an integer in [1, 10000], got ${basisPoints}`);
|
|
1721
|
+
}
|
|
1722
|
+
if (withdrawToAsset && withdrawToAsset.length > 0) {
|
|
1723
|
+
assertMemoSegmentSafe(withdrawToAsset, "withdrawToAsset");
|
|
1724
|
+
return `-:${pool}:${basisPoints}:${withdrawToAsset}`;
|
|
1725
|
+
}
|
|
1726
|
+
return `-:${pool}:${basisPoints}`;
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1731
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/lp/payload.js
|
|
1732
|
+
var LP_REMOVE_DUST_RUNE_BASE_UNITS, isPositiveBaseUnitString, buildThorchainLpAddPayload, buildThorchainLpRemovePayload;
|
|
1733
|
+
var init_payload = __esm({
|
|
1734
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/lp/payload.js"() {
|
|
1735
|
+
init_memo();
|
|
1736
|
+
LP_REMOVE_DUST_RUNE_BASE_UNITS = "2000000";
|
|
1737
|
+
isPositiveBaseUnitString = (value) => /^\d+$/.test(value) && BigInt(value) > 0n;
|
|
1738
|
+
buildThorchainLpAddPayload = ({ pool, amountRuneBaseUnits, pairedAddress }) => {
|
|
1739
|
+
if (!isPositiveBaseUnitString(amountRuneBaseUnits)) {
|
|
1740
|
+
throw new Error(`buildThorchainLpAddPayload: amountRuneBaseUnits must be a positive integer string, got ${amountRuneBaseUnits}`);
|
|
1741
|
+
}
|
|
1742
|
+
const memo = addLpMemo({ pool, pairedAddress });
|
|
1743
|
+
return {
|
|
1744
|
+
kind: "thorchain_lp_add",
|
|
1745
|
+
chain: "THORChain",
|
|
1746
|
+
denom: "rune",
|
|
1747
|
+
amount: amountRuneBaseUnits,
|
|
1748
|
+
memo,
|
|
1749
|
+
pool,
|
|
1750
|
+
...pairedAddress ? { pairedAddress } : {}
|
|
1751
|
+
};
|
|
1752
|
+
};
|
|
1753
|
+
buildThorchainLpRemovePayload = ({ pool, basisPoints, withdrawToAsset }) => {
|
|
1754
|
+
if (!Number.isInteger(basisPoints) || basisPoints < 1 || basisPoints > 1e4) {
|
|
1755
|
+
throw new Error(`buildThorchainLpRemovePayload: basisPoints must be an integer in [1, 10000], got ${basisPoints}`);
|
|
1756
|
+
}
|
|
1757
|
+
const memo = removeLpMemo({ pool, basisPoints, withdrawToAsset });
|
|
1758
|
+
return {
|
|
1759
|
+
kind: "thorchain_lp_remove",
|
|
1760
|
+
chain: "THORChain",
|
|
1761
|
+
denom: "rune",
|
|
1762
|
+
amount: LP_REMOVE_DUST_RUNE_BASE_UNITS,
|
|
1763
|
+
memo,
|
|
1764
|
+
pool,
|
|
1765
|
+
basisPoints,
|
|
1766
|
+
...withdrawToAsset ? { withdrawToAsset } : {}
|
|
1767
|
+
};
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1770
|
+
});
|
|
1771
|
+
|
|
1772
|
+
// ../../packages/core/chain/dist/Chain.js
|
|
1773
|
+
var EthereumL2Chain, EvmChain, UtxoChain, cosmosChainsByKind, IbcEnabledCosmosChain, VaultBasedCosmosChain, CosmosChain, OtherChain, Chain9, UtxoBasedChain, defaultChains;
|
|
1774
|
+
var init_Chain = __esm({
|
|
1775
|
+
"../../packages/core/chain/dist/Chain.js"() {
|
|
1776
|
+
EthereumL2Chain = {
|
|
1777
|
+
Arbitrum: "Arbitrum",
|
|
1778
|
+
Base: "Base",
|
|
1779
|
+
Blast: "Blast",
|
|
1780
|
+
Optimism: "Optimism",
|
|
1781
|
+
Zksync: "Zksync",
|
|
1782
|
+
Mantle: "Mantle"
|
|
1783
|
+
};
|
|
1784
|
+
EvmChain = {
|
|
1785
|
+
...EthereumL2Chain,
|
|
1786
|
+
Avalanche: "Avalanche",
|
|
1787
|
+
CronosChain: "CronosChain",
|
|
1788
|
+
BSC: "BSC",
|
|
1789
|
+
Ethereum: "Ethereum",
|
|
1790
|
+
Polygon: "Polygon",
|
|
1791
|
+
Hyperliquid: "Hyperliquid",
|
|
1792
|
+
Sei: "Sei"
|
|
1793
|
+
};
|
|
1794
|
+
(function(UtxoChain2) {
|
|
1795
|
+
UtxoChain2["Bitcoin"] = "Bitcoin";
|
|
1796
|
+
UtxoChain2["BitcoinCash"] = "Bitcoin-Cash";
|
|
1797
|
+
UtxoChain2["Litecoin"] = "Litecoin";
|
|
1798
|
+
UtxoChain2["Dogecoin"] = "Dogecoin";
|
|
1799
|
+
UtxoChain2["Dash"] = "Dash";
|
|
1800
|
+
UtxoChain2["Zcash"] = "Zcash";
|
|
1801
|
+
})(UtxoChain || (UtxoChain = {}));
|
|
1802
|
+
cosmosChainsByKind = {
|
|
1803
|
+
ibcEnabled: {
|
|
1804
|
+
Cosmos: "Cosmos",
|
|
1805
|
+
Osmosis: "Osmosis",
|
|
1806
|
+
Dydx: "Dydx",
|
|
1807
|
+
Kujira: "Kujira",
|
|
1808
|
+
Terra: "Terra",
|
|
1809
|
+
TerraClassic: "TerraClassic",
|
|
1810
|
+
Noble: "Noble",
|
|
1811
|
+
Akash: "Akash"
|
|
1812
|
+
},
|
|
1813
|
+
vaultBased: {
|
|
1814
|
+
THORChain: "THORChain",
|
|
1815
|
+
MayaChain: "MayaChain"
|
|
1816
|
+
}
|
|
1817
|
+
};
|
|
1818
|
+
IbcEnabledCosmosChain = cosmosChainsByKind.ibcEnabled;
|
|
1819
|
+
VaultBasedCosmosChain = cosmosChainsByKind.vaultBased;
|
|
1820
|
+
CosmosChain = {
|
|
1821
|
+
...IbcEnabledCosmosChain,
|
|
1822
|
+
...VaultBasedCosmosChain
|
|
1823
|
+
};
|
|
1824
|
+
(function(OtherChain2) {
|
|
1825
|
+
OtherChain2["Sui"] = "Sui";
|
|
1826
|
+
OtherChain2["Solana"] = "Solana";
|
|
1827
|
+
OtherChain2["Polkadot"] = "Polkadot";
|
|
1828
|
+
OtherChain2["Bittensor"] = "Bittensor";
|
|
1829
|
+
OtherChain2["Ton"] = "Ton";
|
|
1830
|
+
OtherChain2["Ripple"] = "Ripple";
|
|
1831
|
+
OtherChain2["Tron"] = "Tron";
|
|
1832
|
+
OtherChain2["Cardano"] = "Cardano";
|
|
1833
|
+
OtherChain2["QBTC"] = "QBTC";
|
|
1834
|
+
})(OtherChain || (OtherChain = {}));
|
|
1835
|
+
Chain9 = {
|
|
1836
|
+
...EvmChain,
|
|
1837
|
+
...UtxoChain,
|
|
1838
|
+
...CosmosChain,
|
|
1839
|
+
...OtherChain
|
|
1840
|
+
};
|
|
1841
|
+
UtxoBasedChain = [
|
|
1842
|
+
...Object.values(UtxoChain),
|
|
1843
|
+
OtherChain.Cardano
|
|
1844
|
+
];
|
|
1845
|
+
defaultChains = [
|
|
1846
|
+
Chain9.Bitcoin,
|
|
1847
|
+
Chain9.Ethereum,
|
|
1848
|
+
Chain9.THORChain,
|
|
1849
|
+
Chain9.Solana,
|
|
1850
|
+
Chain9.BSC
|
|
1851
|
+
];
|
|
1852
|
+
}
|
|
1853
|
+
});
|
|
1854
|
+
|
|
1855
|
+
// ../../packages/core/chain/dist/chains/cosmos/cosmosRpcUrl.js
|
|
1856
|
+
var cosmosRpcUrl;
|
|
1857
|
+
var init_cosmosRpcUrl = __esm({
|
|
1858
|
+
"../../packages/core/chain/dist/chains/cosmos/cosmosRpcUrl.js"() {
|
|
1859
|
+
cosmosRpcUrl = {
|
|
1860
|
+
Cosmos: "https://cosmos-rest.publicnode.com",
|
|
1861
|
+
Osmosis: "https://osmosis-rest.publicnode.com",
|
|
1862
|
+
Dydx: "https://dydx-rest.publicnode.com",
|
|
1863
|
+
Kujira: "https://kujira-rest.publicnode.com",
|
|
1864
|
+
Terra: "https://terra-lcd.publicnode.com",
|
|
1865
|
+
TerraClassic: "https://terra-classic-lcd.publicnode.com",
|
|
1866
|
+
Noble: "https://noble-api.polkachu.com",
|
|
1867
|
+
THORChain: "https://thornode.thorchain.network",
|
|
1868
|
+
MayaChain: "https://mayanode.mayachain.info",
|
|
1869
|
+
Akash: "https://akash-rest.publicnode.com"
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
});
|
|
1873
|
+
|
|
1874
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/lp/memberPool.js
|
|
1875
|
+
var isNonZeroBaseUnit, normalizeMemberPool;
|
|
1876
|
+
var init_memberPool = __esm({
|
|
1877
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/lp/memberPool.js"() {
|
|
1878
|
+
isNonZeroBaseUnit = (value) => {
|
|
1879
|
+
if (!value)
|
|
1880
|
+
return false;
|
|
1881
|
+
try {
|
|
1882
|
+
return BigInt(value) > 0n;
|
|
1883
|
+
} catch {
|
|
1884
|
+
return false;
|
|
1885
|
+
}
|
|
1886
|
+
};
|
|
1887
|
+
normalizeMemberPool = (raw) => ({
|
|
1888
|
+
pool: raw.pool ?? "",
|
|
1889
|
+
liquidityUnits: raw.liquidityUnits ?? "0",
|
|
1890
|
+
runeAdded: raw.runeAdded ?? "0",
|
|
1891
|
+
assetAdded: raw.assetAdded ?? "0",
|
|
1892
|
+
runePending: raw.runePending ?? "0",
|
|
1893
|
+
assetPending: raw.assetPending ?? "0",
|
|
1894
|
+
runeAddress: raw.runeAddress ?? "",
|
|
1895
|
+
assetAddress: raw.assetAddress ?? "",
|
|
1896
|
+
dateLastAdded: raw.dateLastAdded ?? "0",
|
|
1897
|
+
lastAddHeight: "",
|
|
1898
|
+
isPending: isNonZeroBaseUnit(raw.runePending) || isNonZeroBaseUnit(raw.assetPending)
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1901
|
+
});
|
|
1902
|
+
|
|
1903
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/lp/position.js
|
|
1904
|
+
var isMidgardNotFoundError, getThorchainLpPosition, getThorchainLpPositionFromThornode;
|
|
1905
|
+
var init_position = __esm({
|
|
1906
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/lp/position.js"() {
|
|
1907
|
+
init_Chain();
|
|
1908
|
+
init_cosmosRpcUrl();
|
|
1909
|
+
init_HttpResponseError();
|
|
1910
|
+
init_queryUrl();
|
|
1911
|
+
init_memberPool();
|
|
1912
|
+
init_pools();
|
|
1913
|
+
isMidgardNotFoundError = (err) => err instanceof HttpResponseError && err.status === 404;
|
|
1914
|
+
getThorchainLpPosition = async ({ thorAddress, pool }) => {
|
|
1915
|
+
assertValidPoolId(pool);
|
|
1916
|
+
const url = `${thorchainMidgardBaseUrl}/v2/member/${encodeURIComponent(thorAddress)}`;
|
|
1917
|
+
let midgardNotFound = false;
|
|
1918
|
+
let raw = {};
|
|
1919
|
+
try {
|
|
1920
|
+
raw = await queryUrl(url);
|
|
1921
|
+
} catch (err) {
|
|
1922
|
+
if (isMidgardNotFoundError(err)) {
|
|
1923
|
+
midgardNotFound = true;
|
|
1924
|
+
} else {
|
|
1925
|
+
throw err;
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
if (!midgardNotFound) {
|
|
1929
|
+
const pools = Array.isArray(raw.pools) ? raw.pools : [];
|
|
1930
|
+
const found = pools.find((p) => p.pool === pool);
|
|
1931
|
+
if (found)
|
|
1932
|
+
return normalizeMemberPool(found);
|
|
1933
|
+
}
|
|
1934
|
+
return getThorchainLpPositionFromThornode({ thorAddress, pool });
|
|
1935
|
+
};
|
|
1936
|
+
getThorchainLpPositionFromThornode = async ({ thorAddress, pool }) => {
|
|
1937
|
+
assertValidPoolId(pool);
|
|
1938
|
+
const url = `${cosmosRpcUrl[Chain9.THORChain]}/thorchain/pool/${encodeURIComponent(pool)}/liquidity_provider/${encodeURIComponent(thorAddress)}`;
|
|
1939
|
+
let raw;
|
|
1940
|
+
try {
|
|
1941
|
+
raw = await queryUrl(url);
|
|
1942
|
+
} catch (err) {
|
|
1943
|
+
if (err instanceof HttpResponseError && err.status === 404)
|
|
1944
|
+
return null;
|
|
1945
|
+
throw err;
|
|
1946
|
+
}
|
|
1947
|
+
const units = raw.units ?? "0";
|
|
1948
|
+
const pendingRune = raw.pending_rune ?? "0";
|
|
1949
|
+
const pendingAsset = raw.pending_asset ?? "0";
|
|
1950
|
+
if (!isNonZeroBaseUnit(units) && !isNonZeroBaseUnit(pendingRune) && !isNonZeroBaseUnit(pendingAsset)) {
|
|
1951
|
+
return null;
|
|
1952
|
+
}
|
|
1953
|
+
return {
|
|
1954
|
+
pool: raw.asset ?? pool,
|
|
1955
|
+
liquidityUnits: units,
|
|
1956
|
+
// Thornode doesn't track historical added amounts — those are a
|
|
1957
|
+
// Midgard-only enrichment. Surface them as 0; the caller can still
|
|
1958
|
+
// act on units/pending.
|
|
1959
|
+
runeAdded: "0",
|
|
1960
|
+
assetAdded: "0",
|
|
1961
|
+
runePending: pendingRune,
|
|
1962
|
+
assetPending: pendingAsset,
|
|
1963
|
+
runeAddress: raw.rune_address ?? thorAddress,
|
|
1964
|
+
assetAddress: raw.asset_address ?? "",
|
|
1965
|
+
// Thornode exposes the last-add block height, not a Unix timestamp.
|
|
1966
|
+
// Keep `dateLastAdded` semantically Unix-seconds-or-0 and expose the
|
|
1967
|
+
// block height via the dedicated `lastAddHeight` field so lockup
|
|
1968
|
+
// checks can use either source.
|
|
1969
|
+
dateLastAdded: "0",
|
|
1970
|
+
lastAddHeight: typeof raw.last_add_height === "number" ? String(raw.last_add_height) : "",
|
|
1971
|
+
isPending: isNonZeroBaseUnit(pendingRune) || isNonZeroBaseUnit(pendingAsset)
|
|
1972
|
+
};
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1975
|
+
});
|
|
1976
|
+
|
|
1977
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/lp/validation.js
|
|
1978
|
+
var extractPoolStatus, poolPauseMimirKey, getThorchainMimir, assertPoolDepositable;
|
|
1979
|
+
var init_validation = __esm({
|
|
1980
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/lp/validation.js"() {
|
|
1981
|
+
init_Chain();
|
|
1982
|
+
init_cosmosRpcUrl();
|
|
1983
|
+
init_queryUrl();
|
|
1984
|
+
init_pools();
|
|
1985
|
+
extractPoolStatus = (raw) => {
|
|
1986
|
+
if (raw && typeof raw === "object" && "status" in raw) {
|
|
1987
|
+
const status = raw.status;
|
|
1988
|
+
return typeof status === "string" ? status : void 0;
|
|
1989
|
+
}
|
|
1990
|
+
return void 0;
|
|
1991
|
+
};
|
|
1992
|
+
poolPauseMimirKey = (pool) => {
|
|
1993
|
+
const dotIdx = pool.indexOf(".");
|
|
1994
|
+
if (dotIdx <= 0 || dotIdx >= pool.length - 1) {
|
|
1995
|
+
throw new Error(`poolPauseMimirKey: invalid pool id ${pool}`);
|
|
1996
|
+
}
|
|
1997
|
+
const chain = pool.slice(0, dotIdx);
|
|
1998
|
+
const asset = pool.slice(dotIdx + 1);
|
|
1999
|
+
return `PAUSELPDEPOSIT-${chain}-${asset}`;
|
|
2000
|
+
};
|
|
2001
|
+
getThorchainMimir = async () => {
|
|
2002
|
+
const url = `${cosmosRpcUrl[Chain9.THORChain]}/thorchain/mimir`;
|
|
2003
|
+
const raw = await queryUrl(url);
|
|
2004
|
+
if (!raw || typeof raw !== "object") {
|
|
2005
|
+
throw new Error(`getThorchainMimir: unexpected response shape from ${url}`);
|
|
2006
|
+
}
|
|
2007
|
+
const out = {};
|
|
2008
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
2009
|
+
if (typeof v === "number" && Number.isFinite(v)) {
|
|
2010
|
+
out[k] = v;
|
|
2011
|
+
} else if (typeof v === "string" && /^-?\d+$/.test(v)) {
|
|
2012
|
+
out[k] = Number(v);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
return out;
|
|
2016
|
+
};
|
|
2017
|
+
assertPoolDepositable = async (pool) => {
|
|
2018
|
+
assertValidPoolId(pool);
|
|
2019
|
+
const [poolRaw, mimir] = await Promise.all([
|
|
2020
|
+
queryUrl(`${cosmosRpcUrl[Chain9.THORChain]}/thorchain/pool/${encodeURIComponent(pool)}`),
|
|
2021
|
+
getThorchainMimir()
|
|
2022
|
+
]);
|
|
2023
|
+
const status = extractPoolStatus(poolRaw);
|
|
2024
|
+
if (status === void 0) {
|
|
2025
|
+
throw new Error(`assertPoolDepositable: pool ${pool} response from thornode did not include a string \`status\` field`);
|
|
2026
|
+
}
|
|
2027
|
+
if (status !== "Available") {
|
|
2028
|
+
throw new Error(`assertPoolDepositable: pool ${pool} status is ${status}, must be Available for LP add`);
|
|
2029
|
+
}
|
|
2030
|
+
const pauseKey = poolPauseMimirKey(pool);
|
|
2031
|
+
const pauseValue = mimir[pauseKey];
|
|
2032
|
+
if (typeof pauseValue === "number" && pauseValue > 0) {
|
|
2033
|
+
throw new Error(`assertPoolDepositable: pool ${pool} has LP deposits paused on-chain via mimir ${pauseKey}=${pauseValue}. THORChain validators have disabled new adds for this pool; any tx would be rejected at handler execution time.`);
|
|
2034
|
+
}
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
});
|
|
2038
|
+
|
|
2039
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/thorchainLp.js
|
|
2040
|
+
var thorchainLpChainCode;
|
|
2041
|
+
var init_thorchainLp = __esm({
|
|
2042
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/thorchainLp.js"() {
|
|
2043
|
+
init_Chain();
|
|
2044
|
+
thorchainLpChainCode = {
|
|
2045
|
+
[Chain9.Avalanche]: "AVAX",
|
|
2046
|
+
[Chain9.Base]: "BASE",
|
|
2047
|
+
[Chain9.BitcoinCash]: "BCH",
|
|
2048
|
+
[Chain9.BSC]: "BSC",
|
|
2049
|
+
[Chain9.Bitcoin]: "BTC",
|
|
2050
|
+
[Chain9.Dash]: "DASH",
|
|
2051
|
+
[Chain9.Dogecoin]: "DOGE",
|
|
2052
|
+
[Chain9.Ethereum]: "ETH",
|
|
2053
|
+
[Chain9.Cosmos]: "GAIA",
|
|
2054
|
+
[Chain9.Kujira]: "KUJI",
|
|
2055
|
+
[Chain9.Litecoin]: "LTC",
|
|
2056
|
+
[Chain9.THORChain]: "THOR",
|
|
2057
|
+
[Chain9.Tron]: "TRON",
|
|
2058
|
+
[Chain9.Ripple]: "XRP",
|
|
2059
|
+
[Chain9.Arbitrum]: "ARB",
|
|
2060
|
+
[Chain9.Zcash]: "ZEC"
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
});
|
|
2064
|
+
|
|
2065
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/lp/lpChainMap.js
|
|
2066
|
+
var lpChainMap, chainPrefixToChain, chainToLpPrefix;
|
|
2067
|
+
var init_lpChainMap = __esm({
|
|
2068
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/lp/lpChainMap.js"() {
|
|
2069
|
+
init_thorchainLp();
|
|
2070
|
+
lpChainMap = Object.freeze(Object.entries(thorchainLpChainCode).reduce((acc, [chainKey, prefix]) => {
|
|
2071
|
+
if (prefix) {
|
|
2072
|
+
acc[prefix] = chainKey;
|
|
2073
|
+
}
|
|
2074
|
+
return acc;
|
|
2075
|
+
}, {}));
|
|
2076
|
+
chainPrefixToChain = (prefix) => lpChainMap[prefix.toUpperCase()];
|
|
2077
|
+
chainToLpPrefix = (chain) => thorchainLpChainCode[chain];
|
|
2078
|
+
}
|
|
2079
|
+
});
|
|
2080
|
+
|
|
2081
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/lp/pairing.js
|
|
2082
|
+
var resolvePairedAddressForLpAdd;
|
|
2083
|
+
var init_pairing = __esm({
|
|
2084
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/lp/pairing.js"() {
|
|
2085
|
+
init_Chain();
|
|
2086
|
+
init_lpChainMap();
|
|
2087
|
+
init_pools();
|
|
2088
|
+
resolvePairedAddressForLpAdd = ({ pool, side, vaultAddresses }) => {
|
|
2089
|
+
assertValidPoolId(pool);
|
|
2090
|
+
const [chainPrefix] = pool.split(".");
|
|
2091
|
+
if (!chainPrefix)
|
|
2092
|
+
return void 0;
|
|
2093
|
+
const assetChain = chainPrefixToChain(chainPrefix);
|
|
2094
|
+
if (!assetChain)
|
|
2095
|
+
return void 0;
|
|
2096
|
+
if (side === "asset") {
|
|
2097
|
+
return vaultAddresses[Chain9.THORChain];
|
|
2098
|
+
}
|
|
2099
|
+
return vaultAddresses[assetChain];
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
});
|
|
2103
|
+
|
|
2104
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/lp/math.js
|
|
2105
|
+
var assertBaseUnitString, getLiquidityUnits, getPoolShare, getLpAddSlippage, estimateLpAdd;
|
|
2106
|
+
var init_math = __esm({
|
|
2107
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/lp/math.js"() {
|
|
2108
|
+
init_Chain();
|
|
2109
|
+
init_cosmosRpcUrl();
|
|
2110
|
+
init_queryUrl();
|
|
2111
|
+
init_pools();
|
|
2112
|
+
assertBaseUnitString = (value, fieldName) => {
|
|
2113
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
2114
|
+
throw new Error(`${fieldName} must be a non-empty base-unit string, got ${typeof value === "string" ? JSON.stringify(value) : typeof value}`);
|
|
2115
|
+
}
|
|
2116
|
+
if (!/^\d+$/.test(value)) {
|
|
2117
|
+
throw new Error(`${fieldName} must be a non-negative integer base-unit string, got ${JSON.stringify(value)}`);
|
|
2118
|
+
}
|
|
2119
|
+
return BigInt(value);
|
|
2120
|
+
};
|
|
2121
|
+
getLiquidityUnits = ({ pool, assetAmountBaseUnit, runeAmountBaseUnit }) => {
|
|
2122
|
+
const P = assertBaseUnitString(pool.poolUnits, "pool.poolUnits");
|
|
2123
|
+
const R = assertBaseUnitString(pool.runeDepth, "pool.runeDepth");
|
|
2124
|
+
const A = assertBaseUnitString(pool.assetDepth, "pool.assetDepth");
|
|
2125
|
+
const r = assertBaseUnitString(runeAmountBaseUnit, "runeAmountBaseUnit");
|
|
2126
|
+
const a = assertBaseUnitString(assetAmountBaseUnit, "assetAmountBaseUnit");
|
|
2127
|
+
if (R === 0n || A === 0n || P === 0n) {
|
|
2128
|
+
return "0";
|
|
2129
|
+
}
|
|
2130
|
+
const numerator = P * (R * a + r * A);
|
|
2131
|
+
const denominator = 2n * R * A;
|
|
2132
|
+
return (numerator / denominator).toString();
|
|
2133
|
+
};
|
|
2134
|
+
getPoolShare = ({ pool, liquidityUnits }) => {
|
|
2135
|
+
const P = assertBaseUnitString(pool.poolUnits, "pool.poolUnits");
|
|
2136
|
+
const L = assertBaseUnitString(liquidityUnits, "liquidityUnits");
|
|
2137
|
+
if (P === 0n || L === 0n) {
|
|
2138
|
+
return { poolShareDecimal: "0" };
|
|
2139
|
+
}
|
|
2140
|
+
const totalAfter = P + L;
|
|
2141
|
+
const SCALE = 10n ** 18n;
|
|
2142
|
+
const scaled = L * SCALE / totalAfter;
|
|
2143
|
+
const decimal = scaled.toString().padStart(19, "0");
|
|
2144
|
+
const intPart = decimal.slice(0, -18) || "0";
|
|
2145
|
+
const fracPart = decimal.slice(-18).replace(/0+$/, "");
|
|
2146
|
+
return {
|
|
2147
|
+
poolShareDecimal: fracPart.length > 0 ? `${intPart}.${fracPart}` : intPart
|
|
2148
|
+
};
|
|
2149
|
+
};
|
|
2150
|
+
getLpAddSlippage = ({ pool, assetAmountBaseUnit, runeAmountBaseUnit }) => {
|
|
2151
|
+
const R = assertBaseUnitString(pool.runeDepth, "pool.runeDepth");
|
|
2152
|
+
const A = assertBaseUnitString(pool.assetDepth, "pool.assetDepth");
|
|
2153
|
+
const r = assertBaseUnitString(runeAmountBaseUnit, "runeAmountBaseUnit");
|
|
2154
|
+
const a = assertBaseUnitString(assetAmountBaseUnit, "assetAmountBaseUnit");
|
|
2155
|
+
if (R === 0n || A === 0n) {
|
|
2156
|
+
return { decimalPercent: "0", slippageInRuneBaseUnit: "0" };
|
|
2157
|
+
}
|
|
2158
|
+
const ra = R * a;
|
|
2159
|
+
const ar = A * r;
|
|
2160
|
+
const numerator = ra > ar ? ra - ar : ar - ra;
|
|
2161
|
+
const denominator = A * r + R * A;
|
|
2162
|
+
if (denominator === 0n) {
|
|
2163
|
+
return { decimalPercent: "0", slippageInRuneBaseUnit: "0" };
|
|
2164
|
+
}
|
|
2165
|
+
const SCALE = 10n ** 18n;
|
|
2166
|
+
const scaled = numerator * SCALE / denominator;
|
|
2167
|
+
const decimal = scaled.toString().padStart(19, "0");
|
|
2168
|
+
const intPart = decimal.slice(0, -18) || "0";
|
|
2169
|
+
const fracPart = decimal.slice(-18).replace(/0+$/, "");
|
|
2170
|
+
const decimalPercent = fracPart.length > 0 ? `${intPart}.${fracPart}` : intPart;
|
|
2171
|
+
const imbalanceNumerator = ra > ar ? ra - ar : ar - ra;
|
|
2172
|
+
const imbalanceInRune = A === 0n ? 0n : imbalanceNumerator / (2n * A);
|
|
2173
|
+
const slippageInRune = imbalanceInRune * scaled / SCALE;
|
|
2174
|
+
return {
|
|
2175
|
+
decimalPercent,
|
|
2176
|
+
slippageInRuneBaseUnit: slippageInRune.toString()
|
|
2177
|
+
};
|
|
2178
|
+
};
|
|
2179
|
+
estimateLpAdd = async ({ pool, assetAmountBaseUnit, runeAmountBaseUnit, thornodeBaseUrl }) => {
|
|
2180
|
+
assertValidPoolId(pool);
|
|
2181
|
+
const base = thornodeBaseUrl ?? cosmosRpcUrl[Chain9.THORChain];
|
|
2182
|
+
const url = `${base}/thorchain/pool/${encodeURIComponent(pool)}`;
|
|
2183
|
+
const raw = await queryUrl(url);
|
|
2184
|
+
if (!raw || typeof raw !== "object" || typeof raw.balance_asset !== "string" || typeof raw.balance_rune !== "string") {
|
|
2185
|
+
throw new Error(`estimateLpAdd: pool ${pool} response from ${url} missing balance fields`);
|
|
2186
|
+
}
|
|
2187
|
+
const poolUnitsRaw = raw.pool_units ?? raw.LP_units;
|
|
2188
|
+
if (typeof poolUnitsRaw !== "string" || poolUnitsRaw.length === 0) {
|
|
2189
|
+
throw new Error(`estimateLpAdd: pool ${pool} response from ${url} missing pool_units / LP_units`);
|
|
2190
|
+
}
|
|
2191
|
+
const poolState = {
|
|
2192
|
+
assetDepth: raw.balance_asset,
|
|
2193
|
+
runeDepth: raw.balance_rune,
|
|
2194
|
+
poolUnits: poolUnitsRaw
|
|
2195
|
+
};
|
|
2196
|
+
const liquidityUnits = getLiquidityUnits({
|
|
2197
|
+
pool: poolState,
|
|
2198
|
+
assetAmountBaseUnit,
|
|
2199
|
+
runeAmountBaseUnit
|
|
2200
|
+
});
|
|
2201
|
+
const share = getPoolShare({
|
|
2202
|
+
pool: poolState,
|
|
2203
|
+
liquidityUnits
|
|
2204
|
+
});
|
|
2205
|
+
const slip = getLpAddSlippage({
|
|
2206
|
+
pool: poolState,
|
|
2207
|
+
assetAmountBaseUnit,
|
|
2208
|
+
runeAmountBaseUnit
|
|
2209
|
+
});
|
|
2210
|
+
const R = BigInt(poolState.runeDepth);
|
|
2211
|
+
const A = BigInt(poolState.assetDepth);
|
|
2212
|
+
const P = BigInt(poolState.poolUnits);
|
|
2213
|
+
const r = BigInt(runeAmountBaseUnit);
|
|
2214
|
+
const a = BigInt(assetAmountBaseUnit);
|
|
2215
|
+
const L = BigInt(liquidityUnits);
|
|
2216
|
+
const totalAfter = P + L;
|
|
2217
|
+
const runeDepthAfter = R + r;
|
|
2218
|
+
const assetDepthAfter = A + a;
|
|
2219
|
+
const runeShareBaseUnit = totalAfter === 0n ? "0" : (runeDepthAfter * L / totalAfter).toString();
|
|
2220
|
+
const assetShareBaseUnit = totalAfter === 0n ? "0" : (assetDepthAfter * L / totalAfter).toString();
|
|
2221
|
+
return {
|
|
2222
|
+
liquidityUnits,
|
|
2223
|
+
poolShareDecimal: share.poolShareDecimal,
|
|
2224
|
+
runeShareBaseUnit,
|
|
2225
|
+
assetShareBaseUnit,
|
|
2226
|
+
slippageDecimal: slip.decimalPercent,
|
|
2227
|
+
slippageRuneBaseUnit: slip.slippageInRuneBaseUnit
|
|
2228
|
+
};
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
});
|
|
2232
|
+
|
|
2233
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/lp/positions.js
|
|
2234
|
+
var getThorchainLpPositions;
|
|
2235
|
+
var init_positions = __esm({
|
|
2236
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/lp/positions.js"() {
|
|
2237
|
+
init_HttpResponseError();
|
|
2238
|
+
init_queryUrl();
|
|
2239
|
+
init_memberPool();
|
|
2240
|
+
init_pools();
|
|
2241
|
+
getThorchainLpPositions = async ({ thorAddress }) => {
|
|
2242
|
+
const url = `${thorchainMidgardBaseUrl}/v2/member/${encodeURIComponent(thorAddress)}`;
|
|
2243
|
+
let raw;
|
|
2244
|
+
try {
|
|
2245
|
+
raw = await queryUrl(url);
|
|
2246
|
+
} catch (err) {
|
|
2247
|
+
if (err instanceof HttpResponseError && err.status === 404)
|
|
2248
|
+
return [];
|
|
2249
|
+
throw err;
|
|
2250
|
+
}
|
|
2251
|
+
if (!raw || typeof raw !== "object") {
|
|
2252
|
+
throw new Error(`getThorchainLpPositions: unexpected Midgard response shape from ${url}`);
|
|
2253
|
+
}
|
|
2254
|
+
if (raw.pools === void 0) {
|
|
2255
|
+
return [];
|
|
2256
|
+
}
|
|
2257
|
+
if (!Array.isArray(raw.pools)) {
|
|
2258
|
+
throw new Error(`getThorchainLpPositions: Midgard response ${url} has non-array \`pools\` field`);
|
|
2259
|
+
}
|
|
2260
|
+
return raw.pools.map(normalizeMemberPool);
|
|
2261
|
+
};
|
|
2262
|
+
}
|
|
2263
|
+
});
|
|
2264
|
+
|
|
2265
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/lp/lockup.js
|
|
2266
|
+
var THORCHAIN_BLOCK_TIME_SECONDS, getThorchainLpLockupSeconds, getLpWithdrawReadiness;
|
|
2267
|
+
var init_lockup = __esm({
|
|
2268
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/lp/lockup.js"() {
|
|
2269
|
+
init_validation();
|
|
2270
|
+
THORCHAIN_BLOCK_TIME_SECONDS = 6;
|
|
2271
|
+
getThorchainLpLockupSeconds = async () => {
|
|
2272
|
+
const mimir = await getThorchainMimir();
|
|
2273
|
+
const blocks = mimir["LIQUIDITYLOCKUPBLOCKS"];
|
|
2274
|
+
if (typeof blocks !== "number" || !Number.isFinite(blocks) || blocks < 0) {
|
|
2275
|
+
throw new Error(`getThorchainLpLockupSeconds: mimir did not include a valid LIQUIDITYLOCKUPBLOCKS value`);
|
|
2276
|
+
}
|
|
2277
|
+
return blocks * THORCHAIN_BLOCK_TIME_SECONDS;
|
|
2278
|
+
};
|
|
2279
|
+
getLpWithdrawReadiness = async ({ position, lockupSeconds: providedLockupSeconds, nowUnix = Math.floor(Date.now() / 1e3) }) => {
|
|
2280
|
+
const lockupSeconds = providedLockupSeconds ?? await getThorchainLpLockupSeconds();
|
|
2281
|
+
const lastAdded = Number(position.dateLastAdded);
|
|
2282
|
+
if (!Number.isFinite(lastAdded) || lastAdded <= 0) {
|
|
2283
|
+
return {
|
|
2284
|
+
isWithdrawable: true,
|
|
2285
|
+
unlockAtUnix: 0,
|
|
2286
|
+
remainingSeconds: 0
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
const unlockAtUnix = lastAdded + lockupSeconds;
|
|
2290
|
+
const remainingSeconds = Math.max(unlockAtUnix - nowUnix, 0);
|
|
2291
|
+
return {
|
|
2292
|
+
isWithdrawable: remainingSeconds === 0,
|
|
2293
|
+
unlockAtUnix,
|
|
2294
|
+
remainingSeconds
|
|
2295
|
+
};
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
});
|
|
2299
|
+
|
|
2300
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/getThorchainInboundAddress.js
|
|
2301
|
+
var getThorchainInboundAddress_exports = {};
|
|
2302
|
+
__export(getThorchainInboundAddress_exports, {
|
|
2303
|
+
getThorchainInboundAddress: () => getThorchainInboundAddress
|
|
2304
|
+
});
|
|
2305
|
+
var thorchainInboundAddressApi, getThorchainInboundAddress;
|
|
2306
|
+
var init_getThorchainInboundAddress = __esm({
|
|
2307
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/getThorchainInboundAddress.js"() {
|
|
2308
|
+
init_queryUrl();
|
|
2309
|
+
init_Chain();
|
|
2310
|
+
init_cosmosRpcUrl();
|
|
2311
|
+
thorchainInboundAddressApi = `${cosmosRpcUrl[Chain9.THORChain]}/thorchain/inbound_addresses`;
|
|
2312
|
+
getThorchainInboundAddress = () => queryUrl(thorchainInboundAddressApi);
|
|
2313
|
+
}
|
|
2314
|
+
});
|
|
2315
|
+
|
|
2316
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/lp/halts.js
|
|
2317
|
+
var buildStatus, getThorchainLpHaltStatusAll, getThorchainLpHaltStatus, getThorchainLpPoolPauseStatus;
|
|
2318
|
+
var init_halts = __esm({
|
|
2319
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/lp/halts.js"() {
|
|
2320
|
+
init_getThorchainInboundAddress();
|
|
2321
|
+
init_validation();
|
|
2322
|
+
buildStatus = (raw) => {
|
|
2323
|
+
const reasons = [];
|
|
2324
|
+
if (raw.halted)
|
|
2325
|
+
reasons.push(`${raw.chain} chain is halted`);
|
|
2326
|
+
if (raw.global_trading_paused)
|
|
2327
|
+
reasons.push("global trading paused");
|
|
2328
|
+
if (raw.chain_trading_paused)
|
|
2329
|
+
reasons.push(`${raw.chain} chain trading paused`);
|
|
2330
|
+
if (raw.chain_lp_actions_paused)
|
|
2331
|
+
reasons.push(`${raw.chain} LP actions paused`);
|
|
2332
|
+
const depositable = !raw.halted && !raw.chain_lp_actions_paused && !raw.chain_trading_paused && !raw.global_trading_paused;
|
|
2333
|
+
const withdrawable = !raw.halted && !raw.chain_lp_actions_paused;
|
|
2334
|
+
return {
|
|
2335
|
+
chain: raw.chain,
|
|
2336
|
+
depositable,
|
|
2337
|
+
withdrawable,
|
|
2338
|
+
reasons,
|
|
2339
|
+
raw: {
|
|
2340
|
+
halted: raw.halted,
|
|
2341
|
+
chain_trading_paused: raw.chain_trading_paused,
|
|
2342
|
+
chain_lp_actions_paused: raw.chain_lp_actions_paused,
|
|
2343
|
+
global_trading_paused: raw.global_trading_paused
|
|
2344
|
+
}
|
|
2345
|
+
};
|
|
2346
|
+
};
|
|
2347
|
+
getThorchainLpHaltStatusAll = async () => {
|
|
2348
|
+
const addresses = await getThorchainInboundAddress();
|
|
2349
|
+
return addresses.map((a) => buildStatus({
|
|
2350
|
+
chain: a.chain,
|
|
2351
|
+
halted: a.halted,
|
|
2352
|
+
chain_trading_paused: a.chain_trading_paused,
|
|
2353
|
+
chain_lp_actions_paused: a.chain_lp_actions_paused,
|
|
2354
|
+
global_trading_paused: a.global_trading_paused
|
|
2355
|
+
}));
|
|
2356
|
+
};
|
|
2357
|
+
getThorchainLpHaltStatus = async (chain) => {
|
|
2358
|
+
const all = await getThorchainLpHaltStatusAll();
|
|
2359
|
+
const upper = chain.toUpperCase();
|
|
2360
|
+
const match = all.find((s) => s.chain.toUpperCase() === upper);
|
|
2361
|
+
if (!match) {
|
|
2362
|
+
throw new Error(`getThorchainLpHaltStatus: chain ${chain} not found in inbound_addresses`);
|
|
2363
|
+
}
|
|
2364
|
+
return match;
|
|
2365
|
+
};
|
|
2366
|
+
getThorchainLpPoolPauseStatus = async (pool) => {
|
|
2367
|
+
const mimir = await getThorchainMimir();
|
|
2368
|
+
const mimirKey = poolPauseMimirKey(pool);
|
|
2369
|
+
const mimirValue = mimir[mimirKey];
|
|
2370
|
+
if (typeof mimirValue === "number" && mimirValue > 0) {
|
|
2371
|
+
return { paused: true, mimirKey, mimirValue };
|
|
2372
|
+
}
|
|
2373
|
+
return { paused: false };
|
|
2374
|
+
};
|
|
2375
|
+
}
|
|
2376
|
+
});
|
|
2377
|
+
|
|
2378
|
+
// ../../packages/core/chain/dist/chains/cosmos/thor/lp/index.js
|
|
2379
|
+
var lp_exports = {};
|
|
2380
|
+
__export(lp_exports, {
|
|
2381
|
+
THORCHAIN_BLOCK_TIME_SECONDS: () => THORCHAIN_BLOCK_TIME_SECONDS,
|
|
2382
|
+
addLpMemo: () => addLpMemo,
|
|
2383
|
+
assertPoolDepositable: () => assertPoolDepositable,
|
|
2384
|
+
assertValidPoolId: () => assertValidPoolId,
|
|
2385
|
+
buildThorchainLpAddPayload: () => buildThorchainLpAddPayload,
|
|
2386
|
+
buildThorchainLpRemovePayload: () => buildThorchainLpRemovePayload,
|
|
2387
|
+
chainPrefixToChain: () => chainPrefixToChain,
|
|
2388
|
+
chainToLpPrefix: () => chainToLpPrefix,
|
|
2389
|
+
estimateLpAdd: () => estimateLpAdd,
|
|
2390
|
+
getLiquidityUnits: () => getLiquidityUnits,
|
|
2391
|
+
getLpAddSlippage: () => getLpAddSlippage,
|
|
2392
|
+
getLpWithdrawReadiness: () => getLpWithdrawReadiness,
|
|
2393
|
+
getPoolShare: () => getPoolShare,
|
|
2394
|
+
getThorchainLpHaltStatus: () => getThorchainLpHaltStatus,
|
|
2395
|
+
getThorchainLpHaltStatusAll: () => getThorchainLpHaltStatusAll,
|
|
2396
|
+
getThorchainLpLockupSeconds: () => getThorchainLpLockupSeconds,
|
|
2397
|
+
getThorchainLpPoolPauseStatus: () => getThorchainLpPoolPauseStatus,
|
|
2398
|
+
getThorchainLpPosition: () => getThorchainLpPosition,
|
|
2399
|
+
getThorchainLpPositionFromThornode: () => getThorchainLpPositionFromThornode,
|
|
2400
|
+
getThorchainLpPositions: () => getThorchainLpPositions,
|
|
2401
|
+
getThorchainMimir: () => getThorchainMimir,
|
|
2402
|
+
getThorchainPools: () => getThorchainPools,
|
|
2403
|
+
isValidPoolId: () => isValidPoolId,
|
|
2404
|
+
lpChainMap: () => lpChainMap,
|
|
2405
|
+
poolPauseMimirKey: () => poolPauseMimirKey,
|
|
2406
|
+
removeLpMemo: () => removeLpMemo,
|
|
2407
|
+
resolvePairedAddressForLpAdd: () => resolvePairedAddressForLpAdd,
|
|
2408
|
+
thorchainMidgardBaseUrl: () => thorchainMidgardBaseUrl
|
|
2409
|
+
});
|
|
2410
|
+
var init_lp = __esm({
|
|
2411
|
+
"../../packages/core/chain/dist/chains/cosmos/thor/lp/index.js"() {
|
|
2412
|
+
init_memo();
|
|
2413
|
+
init_payload();
|
|
2414
|
+
init_pools();
|
|
2415
|
+
init_position();
|
|
2416
|
+
init_validation();
|
|
2417
|
+
init_pairing();
|
|
2418
|
+
init_lpChainMap();
|
|
2419
|
+
init_math();
|
|
2420
|
+
init_positions();
|
|
2421
|
+
init_lockup();
|
|
2422
|
+
init_halts();
|
|
1449
2423
|
}
|
|
1450
2424
|
});
|
|
1451
2425
|
|
|
1452
2426
|
// src/index.ts
|
|
1453
2427
|
import "dotenv/config";
|
|
1454
2428
|
import { promises as fs4 } from "node:fs";
|
|
2429
|
+
import { descriptions } from "@vultisig/client-shared";
|
|
1455
2430
|
import { parseKeygenQR, Vultisig as Vultisig6 } from "@vultisig/sdk";
|
|
1456
2431
|
import chalk15 from "chalk";
|
|
1457
|
-
import { program } from "commander";
|
|
2432
|
+
import { InvalidArgumentError, program } from "commander";
|
|
1458
2433
|
import inquirer8 from "inquirer";
|
|
1459
2434
|
|
|
1460
2435
|
// src/core/command-context.ts
|
|
@@ -1536,12 +2511,32 @@ import chalk from "chalk";
|
|
|
1536
2511
|
import ora from "ora";
|
|
1537
2512
|
var silentMode = false;
|
|
1538
2513
|
var outputFormat = "table";
|
|
2514
|
+
var nonInteractive = false;
|
|
2515
|
+
var quietMode = false;
|
|
2516
|
+
var fieldFilter;
|
|
1539
2517
|
function setSilentMode(silent) {
|
|
1540
2518
|
silentMode = silent;
|
|
1541
2519
|
}
|
|
1542
2520
|
function isSilent() {
|
|
1543
2521
|
return silentMode;
|
|
1544
2522
|
}
|
|
2523
|
+
function setNonInteractive(value) {
|
|
2524
|
+
nonInteractive = value;
|
|
2525
|
+
}
|
|
2526
|
+
function isNonInteractive() {
|
|
2527
|
+
return nonInteractive;
|
|
2528
|
+
}
|
|
2529
|
+
function requireInteractive(hint) {
|
|
2530
|
+
if (nonInteractive) {
|
|
2531
|
+
throw new Error(`Interactive prompt required but --non-interactive is set. ${hint}`);
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
function setQuiet(value) {
|
|
2535
|
+
quietMode = value;
|
|
2536
|
+
}
|
|
2537
|
+
function setFields(fields) {
|
|
2538
|
+
fieldFilter = fields;
|
|
2539
|
+
}
|
|
1545
2540
|
function initOutputMode(options) {
|
|
1546
2541
|
outputFormat = options.output ?? "table";
|
|
1547
2542
|
silentMode = options.silent ?? process.env.VULTISIG_SILENT === "1";
|
|
@@ -1552,14 +2547,72 @@ function initOutputMode(options) {
|
|
|
1552
2547
|
function isJsonOutput() {
|
|
1553
2548
|
return outputFormat === "json";
|
|
1554
2549
|
}
|
|
2550
|
+
function stripEmpty(data) {
|
|
2551
|
+
if (Array.isArray(data)) return data.map(stripEmpty);
|
|
2552
|
+
if (data !== null && typeof data === "object") {
|
|
2553
|
+
return Object.fromEntries(
|
|
2554
|
+
Object.entries(data).filter(([, v]) => v != null && v !== "").map(([k, v]) => [k, stripEmpty(v)])
|
|
2555
|
+
);
|
|
2556
|
+
}
|
|
2557
|
+
return data;
|
|
2558
|
+
}
|
|
2559
|
+
function filterFields(data, fields) {
|
|
2560
|
+
if (!fields.length) return data;
|
|
2561
|
+
if (Array.isArray(data)) return data.map((item) => filterFields(item, fields));
|
|
2562
|
+
if (data !== null && typeof data === "object") {
|
|
2563
|
+
const entries = Object.entries(data);
|
|
2564
|
+
const matched = entries.filter(([k]) => fields.includes(k));
|
|
2565
|
+
if (matched.length > 0) return Object.fromEntries(matched);
|
|
2566
|
+
const recursed = entries.map(([k, v]) => [k, filterFields(v, fields)]);
|
|
2567
|
+
return Object.fromEntries(recursed);
|
|
2568
|
+
}
|
|
2569
|
+
return data;
|
|
2570
|
+
}
|
|
2571
|
+
function collectKeys(data) {
|
|
2572
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2573
|
+
if (Array.isArray(data)) {
|
|
2574
|
+
for (const item of data) {
|
|
2575
|
+
if (item !== null && typeof item === "object") {
|
|
2576
|
+
for (const k of Object.keys(item)) keys.add(k);
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
} else if (data !== null && typeof data === "object") {
|
|
2580
|
+
for (const [k, v] of Object.entries(data)) {
|
|
2581
|
+
keys.add(k);
|
|
2582
|
+
if (v !== null && typeof v === "object") {
|
|
2583
|
+
for (const nested of collectKeys(v)) keys.add(nested);
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
return keys;
|
|
2588
|
+
}
|
|
2589
|
+
function warnInvalidFields(data, fields) {
|
|
2590
|
+
const available = collectKeys(data);
|
|
2591
|
+
if (available.size === 0) return;
|
|
2592
|
+
const invalid = fields.filter((f) => !available.has(f));
|
|
2593
|
+
if (invalid.length > 0) {
|
|
2594
|
+
process.stderr.write(`Warning: unknown field(s): ${invalid.join(", ")}. Available: ${[...available].join(", ")}
|
|
2595
|
+
`);
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
function applyOutputTransforms(data) {
|
|
2599
|
+
let out = quietMode ? stripEmpty(data) : data;
|
|
2600
|
+
if (fieldFilter?.length) {
|
|
2601
|
+
warnInvalidFields(out, fieldFilter);
|
|
2602
|
+
out = filterFields(out, fieldFilter);
|
|
2603
|
+
}
|
|
2604
|
+
return out;
|
|
2605
|
+
}
|
|
1555
2606
|
function bigIntReplacer(_key, value) {
|
|
1556
2607
|
return typeof value === "bigint" ? value.toString() : value;
|
|
1557
2608
|
}
|
|
1558
2609
|
function outputJson(data) {
|
|
1559
|
-
|
|
2610
|
+
const transformed = applyOutputTransforms(data);
|
|
2611
|
+
console.log(JSON.stringify({ success: true, v: 1, data: transformed }, bigIntReplacer, 2));
|
|
1560
2612
|
}
|
|
1561
|
-
function
|
|
1562
|
-
|
|
2613
|
+
function outputErrorJson(errJson) {
|
|
2614
|
+
const transformed = applyOutputTransforms(errJson);
|
|
2615
|
+
console.log(JSON.stringify(transformed, bigIntReplacer, 2));
|
|
1563
2616
|
}
|
|
1564
2617
|
function info(message) {
|
|
1565
2618
|
if (!silentMode) {
|
|
@@ -1661,11 +2714,24 @@ function createSpinner(text) {
|
|
|
1661
2714
|
activeSpinners.add(spinner2);
|
|
1662
2715
|
return spinner2;
|
|
1663
2716
|
}
|
|
1664
|
-
const spinner = wrapOraSpinner(ora(text).start());
|
|
2717
|
+
const spinner = wrapOraSpinner(ora({ text, stream: process.stderr }).start());
|
|
1665
2718
|
activeSpinners.add(spinner);
|
|
1666
2719
|
return spinner;
|
|
1667
2720
|
}
|
|
1668
2721
|
|
|
2722
|
+
// src/core/credential-store.ts
|
|
2723
|
+
import {
|
|
2724
|
+
_resetAll,
|
|
2725
|
+
clearCredentials,
|
|
2726
|
+
getDecryptionPassword,
|
|
2727
|
+
getServerPassword,
|
|
2728
|
+
getStoredServerPassword,
|
|
2729
|
+
isUsingFileFallback,
|
|
2730
|
+
setDecryptionPassword,
|
|
2731
|
+
setFilePassphrase,
|
|
2732
|
+
setServerPassword
|
|
2733
|
+
} from "@vultisig/client-shared";
|
|
2734
|
+
|
|
1669
2735
|
// src/core/password-manager.ts
|
|
1670
2736
|
var passwordCache = /* @__PURE__ */ new Map();
|
|
1671
2737
|
function cachePassword(vaultIdOrName, password) {
|
|
@@ -1700,12 +2766,15 @@ function getPasswordFromEnv(vaultId, vaultName) {
|
|
|
1700
2766
|
if (vaultPasswords.has(vaultId)) {
|
|
1701
2767
|
return vaultPasswords.get(vaultId);
|
|
1702
2768
|
}
|
|
1703
|
-
if (process.env.VAULT_PASSWORD) {
|
|
1704
|
-
return process.env.VAULT_PASSWORD;
|
|
2769
|
+
if (process.env.VAULT_PASSWORD || process.env.VULTISIG_PASSWORD) {
|
|
2770
|
+
return process.env.VAULT_PASSWORD || process.env.VULTISIG_PASSWORD;
|
|
1705
2771
|
}
|
|
1706
2772
|
return null;
|
|
1707
2773
|
}
|
|
1708
2774
|
async function promptForPassword(vaultName, vaultId) {
|
|
2775
|
+
requireInteractive(
|
|
2776
|
+
'Use --password flag, VAULT_PASSWORD env var, or "vsig auth setup" to store credentials in keyring.'
|
|
2777
|
+
);
|
|
1709
2778
|
const displayName = vaultName || vaultId || "vault";
|
|
1710
2779
|
const { password } = await inquirer.prompt([
|
|
1711
2780
|
{
|
|
@@ -1722,51 +2791,281 @@ async function getPassword(vaultId, vaultName) {
|
|
|
1722
2791
|
if (cachedPassword) {
|
|
1723
2792
|
return cachedPassword;
|
|
1724
2793
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
2794
|
+
try {
|
|
2795
|
+
const keyringPassword = await getServerPassword(vaultId);
|
|
2796
|
+
if (keyringPassword) {
|
|
2797
|
+
cachePassword(vaultId, keyringPassword);
|
|
2798
|
+
if (vaultName) cachePassword(vaultName, keyringPassword);
|
|
2799
|
+
return keyringPassword;
|
|
2800
|
+
}
|
|
2801
|
+
} catch {
|
|
2802
|
+
}
|
|
2803
|
+
const envPassword = getPasswordFromEnv(vaultId, vaultName);
|
|
2804
|
+
if (envPassword) {
|
|
2805
|
+
cachePassword(vaultId, envPassword);
|
|
2806
|
+
if (vaultName) cachePassword(vaultName, envPassword);
|
|
2807
|
+
return envPassword;
|
|
2808
|
+
}
|
|
2809
|
+
if (isSilent() || isJsonOutput() || isNonInteractive()) {
|
|
2810
|
+
throw new Error(
|
|
2811
|
+
"Password required but not provided. Set VAULT_PASSWORD or VAULT_PASSWORDS environment variable, or use --password flag."
|
|
2812
|
+
);
|
|
2813
|
+
}
|
|
2814
|
+
const password = await promptForPassword(vaultName, vaultId);
|
|
2815
|
+
cachePassword(vaultId, password);
|
|
2816
|
+
if (vaultName) cachePassword(vaultName, password);
|
|
2817
|
+
return password;
|
|
2818
|
+
}
|
|
2819
|
+
function createPasswordCallback() {
|
|
2820
|
+
return async (vaultId, vaultName) => {
|
|
2821
|
+
return getPassword(vaultId, vaultName);
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
async function ensureVaultUnlocked(vault, password) {
|
|
2825
|
+
if (!vault.isEncrypted || vault.isUnlocked()) {
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
const resolvedPassword = password || await getPassword(vault.id, vault.name);
|
|
2829
|
+
await vault.unlock(resolvedPassword);
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
// src/adapters/cli-context.ts
|
|
2833
|
+
var CLIContext = class extends BaseCommandContext {
|
|
2834
|
+
get isInteractive() {
|
|
2835
|
+
return false;
|
|
2836
|
+
}
|
|
2837
|
+
/**
|
|
2838
|
+
* Get password for a vault
|
|
2839
|
+
* In CLI mode, we check env vars first, then prompt
|
|
2840
|
+
* No caching since each command runs independently
|
|
2841
|
+
*/
|
|
2842
|
+
async getPassword(vaultId, vaultName) {
|
|
2843
|
+
return getPassword(vaultId, vaultName);
|
|
2844
|
+
}
|
|
2845
|
+
};
|
|
2846
|
+
|
|
2847
|
+
// src/core/errors.ts
|
|
2848
|
+
import { VaultError, VaultErrorCode, VaultImportError, VaultImportErrorCode } from "@vultisig/sdk";
|
|
2849
|
+
var EXIT_CODE_DESCRIPTIONS = {
|
|
2850
|
+
[0 /* SUCCESS */]: "Success",
|
|
2851
|
+
[1 /* USAGE */]: "Usage error (bad arguments, unknown command)",
|
|
2852
|
+
[2 /* AUTH_REQUIRED */]: "Authentication required",
|
|
2853
|
+
[3 /* NETWORK */]: "Network error (retryable)",
|
|
2854
|
+
[4 /* INVALID_INPUT */]: "Invalid input (bad chain, address, amount)",
|
|
2855
|
+
[5 /* RESOURCE_NOT_FOUND */]: "Resource not found (token, route)",
|
|
2856
|
+
[6 /* EXTERNAL_SERVICE */]: "External service error (retryable)",
|
|
2857
|
+
[7 /* UNKNOWN */]: "Unknown/unexpected error"
|
|
2858
|
+
};
|
|
2859
|
+
var VsigError = class extends Error {
|
|
2860
|
+
hint;
|
|
2861
|
+
suggestions;
|
|
2862
|
+
context;
|
|
2863
|
+
retryable = false;
|
|
2864
|
+
constructor(message, hint, suggestions, context) {
|
|
2865
|
+
super(message);
|
|
2866
|
+
this.name = this.constructor.name;
|
|
2867
|
+
this.hint = hint;
|
|
2868
|
+
this.suggestions = suggestions;
|
|
2869
|
+
this.context = context;
|
|
2870
|
+
}
|
|
2871
|
+
};
|
|
2872
|
+
var UsageError = class extends VsigError {
|
|
2873
|
+
exitCode = 1 /* USAGE */;
|
|
2874
|
+
code = "USAGE_ERROR";
|
|
2875
|
+
constructor(message, hint, suggestions) {
|
|
2876
|
+
super(message, hint, suggestions);
|
|
2877
|
+
}
|
|
2878
|
+
};
|
|
2879
|
+
var AuthRequiredError = class extends VsigError {
|
|
2880
|
+
exitCode = 2 /* AUTH_REQUIRED */;
|
|
2881
|
+
code = "AUTH_REQUIRED";
|
|
2882
|
+
constructor(message) {
|
|
2883
|
+
super(message ?? "Authentication required. Set up vault credentials.", "Ensure your vault is unlocked", [
|
|
2884
|
+
"vsig vaults",
|
|
2885
|
+
"vsig create"
|
|
2886
|
+
]);
|
|
2887
|
+
}
|
|
2888
|
+
};
|
|
2889
|
+
var NetworkError = class extends VsigError {
|
|
2890
|
+
exitCode = 3 /* NETWORK */;
|
|
2891
|
+
code = "NETWORK_ERROR";
|
|
2892
|
+
retryable = true;
|
|
2893
|
+
constructor(message, hint, suggestions) {
|
|
2894
|
+
super(message, hint, suggestions);
|
|
2895
|
+
}
|
|
2896
|
+
};
|
|
2897
|
+
var InvalidChainError = class extends VsigError {
|
|
2898
|
+
exitCode = 4 /* INVALID_INPUT */;
|
|
2899
|
+
code = "INVALID_CHAIN";
|
|
2900
|
+
constructor(message, hint, suggestions, context) {
|
|
2901
|
+
super(message, hint, suggestions, context);
|
|
2902
|
+
}
|
|
2903
|
+
};
|
|
2904
|
+
var InvalidAddressError = class extends VsigError {
|
|
2905
|
+
exitCode = 4 /* INVALID_INPUT */;
|
|
2906
|
+
code = "INVALID_ADDRESS";
|
|
2907
|
+
constructor(message, hint, suggestions, context) {
|
|
2908
|
+
super(message, hint, suggestions, context);
|
|
2909
|
+
}
|
|
2910
|
+
};
|
|
2911
|
+
var InvalidInputError = class extends VsigError {
|
|
2912
|
+
exitCode = 4 /* INVALID_INPUT */;
|
|
2913
|
+
code = "INVALID_INPUT";
|
|
2914
|
+
constructor(message, hint, suggestions, context) {
|
|
2915
|
+
super(message, hint, suggestions, context);
|
|
2916
|
+
}
|
|
2917
|
+
};
|
|
2918
|
+
var InsufficientBalanceError = class extends VsigError {
|
|
2919
|
+
exitCode = 4 /* INVALID_INPUT */;
|
|
2920
|
+
code = "INSUFFICIENT_BALANCE";
|
|
2921
|
+
constructor(message, hint, suggestions, context) {
|
|
2922
|
+
super(message, hint, suggestions, context);
|
|
2923
|
+
}
|
|
2924
|
+
};
|
|
2925
|
+
var NoRouteError = class extends VsigError {
|
|
2926
|
+
exitCode = 5 /* RESOURCE_NOT_FOUND */;
|
|
2927
|
+
code = "NO_ROUTE";
|
|
2928
|
+
constructor(message, hint, suggestions, context) {
|
|
2929
|
+
super(message, hint, suggestions, context);
|
|
2930
|
+
}
|
|
2931
|
+
};
|
|
2932
|
+
var TokenNotFoundError = class extends VsigError {
|
|
2933
|
+
exitCode = 5 /* RESOURCE_NOT_FOUND */;
|
|
2934
|
+
code = "TOKEN_NOT_FOUND";
|
|
2935
|
+
constructor(message, hint, suggestions, context) {
|
|
2936
|
+
super(message, hint, suggestions, context);
|
|
2937
|
+
}
|
|
2938
|
+
};
|
|
2939
|
+
var ExternalServiceError = class extends VsigError {
|
|
2940
|
+
exitCode = 6 /* EXTERNAL_SERVICE */;
|
|
2941
|
+
code = "EXTERNAL_SERVICE";
|
|
2942
|
+
retryable = true;
|
|
2943
|
+
constructor(message, hint, suggestions) {
|
|
2944
|
+
super(message, hint, suggestions);
|
|
2945
|
+
}
|
|
2946
|
+
};
|
|
2947
|
+
var PricingUnavailableError = class extends VsigError {
|
|
2948
|
+
exitCode = 6 /* EXTERNAL_SERVICE */;
|
|
2949
|
+
code = "PRICING_UNAVAILABLE";
|
|
2950
|
+
retryable = true;
|
|
2951
|
+
constructor(message, hint, suggestions) {
|
|
2952
|
+
super(message, hint, suggestions);
|
|
2953
|
+
}
|
|
2954
|
+
};
|
|
2955
|
+
var UnknownError = class extends VsigError {
|
|
2956
|
+
exitCode = 7 /* UNKNOWN */;
|
|
2957
|
+
code = "UNKNOWN_ERROR";
|
|
2958
|
+
constructor(message) {
|
|
2959
|
+
super(message);
|
|
2960
|
+
}
|
|
2961
|
+
};
|
|
2962
|
+
function classifyError(err) {
|
|
2963
|
+
if (err instanceof VsigError) return err;
|
|
2964
|
+
if (err instanceof VaultError) {
|
|
2965
|
+
if (err.code === VaultErrorCode.BalanceFetchFailed && err.originalError) {
|
|
2966
|
+
const inner = classifyError(err.originalError);
|
|
2967
|
+
if (!(inner instanceof UnknownError)) return inner;
|
|
2968
|
+
}
|
|
2969
|
+
switch (err.code) {
|
|
2970
|
+
case VaultErrorCode.UnsupportedChain:
|
|
2971
|
+
case VaultErrorCode.ChainNotSupported:
|
|
2972
|
+
return new InvalidChainError(err.message);
|
|
2973
|
+
case VaultErrorCode.NetworkError:
|
|
2974
|
+
case VaultErrorCode.BalanceFetchFailed:
|
|
2975
|
+
case VaultErrorCode.Timeout:
|
|
2976
|
+
return new NetworkError(err.message);
|
|
2977
|
+
case VaultErrorCode.InvalidAmount:
|
|
2978
|
+
return new InvalidInputError(err.message);
|
|
2979
|
+
case VaultErrorCode.InvalidConfig: {
|
|
2980
|
+
const lowerMsg = err.message.toLowerCase();
|
|
2981
|
+
if (lowerMsg.includes("unknown chain") || lowerMsg.includes("unsupported chain") || lowerMsg.includes("chain not supported")) {
|
|
2982
|
+
const chainMatch = err.message.match(/chain[:\s]*"([^"]+)"/i);
|
|
2983
|
+
return new InvalidChainError(
|
|
2984
|
+
err.message,
|
|
2985
|
+
void 0,
|
|
2986
|
+
void 0,
|
|
2987
|
+
chainMatch ? { chain: chainMatch[1] } : void 0
|
|
2988
|
+
);
|
|
2989
|
+
}
|
|
2990
|
+
return new UsageError(err.message);
|
|
2991
|
+
}
|
|
2992
|
+
case VaultErrorCode.UnsupportedToken:
|
|
2993
|
+
return new TokenNotFoundError(err.message);
|
|
2994
|
+
case VaultErrorCode.BroadcastFailed:
|
|
2995
|
+
return new ExternalServiceError(err.message, "Broadcast failed \u2014 the node may be temporarily unavailable", [
|
|
2996
|
+
"Retry the transaction"
|
|
2997
|
+
]);
|
|
2998
|
+
case VaultErrorCode.GasEstimationFailed:
|
|
2999
|
+
return new InvalidInputError(err.message, "Gas estimation failed \u2014 check balance and transaction params");
|
|
3000
|
+
case VaultErrorCode.SigningFailed:
|
|
3001
|
+
return new UnknownError(err.message);
|
|
3002
|
+
default:
|
|
3003
|
+
return new UnknownError(err.message);
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
if (err instanceof VaultImportError) {
|
|
3007
|
+
switch (err.code) {
|
|
3008
|
+
case VaultImportErrorCode.PASSWORD_REQUIRED:
|
|
3009
|
+
case VaultImportErrorCode.INVALID_PASSWORD:
|
|
3010
|
+
return new AuthRequiredError(err.message);
|
|
3011
|
+
default:
|
|
3012
|
+
return new UsageError(err.message);
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
const msg = err.message.toLowerCase();
|
|
3016
|
+
if (msg.includes("unsupported chain") || msg.includes("invalid chain") || msg.includes("unknown chain")) {
|
|
3017
|
+
const chainMatch = err.message.match(/chain[:\s]*"([^"]+)"/i) || err.message.match(/chain[:\s]+(\S+)/i);
|
|
3018
|
+
return new InvalidChainError(err.message, void 0, void 0, chainMatch ? { chain: chainMatch[1] } : void 0);
|
|
1730
3019
|
}
|
|
1731
|
-
if (
|
|
1732
|
-
|
|
3020
|
+
if (msg.includes("invalid address") || msg.includes("bad address") || msg.includes("malformed address")) {
|
|
3021
|
+
const addrMatch = err.message.match(/(0x[a-fA-F0-9]+|bc1[a-z0-9]+|[13][a-km-zA-HJ-NP-Z1-9]+)/i);
|
|
3022
|
+
return new InvalidAddressError(err.message, void 0, void 0, addrMatch ? { address: addrMatch[1] } : void 0);
|
|
1733
3023
|
}
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
if (vaultName) cachePassword(vaultName, password);
|
|
1737
|
-
return password;
|
|
1738
|
-
}
|
|
1739
|
-
function createPasswordCallback() {
|
|
1740
|
-
return async (vaultId, vaultName) => {
|
|
1741
|
-
return getPassword(vaultId, vaultName);
|
|
1742
|
-
};
|
|
1743
|
-
}
|
|
1744
|
-
async function ensureVaultUnlocked(vault, password) {
|
|
1745
|
-
if (!vault.isEncrypted || vault.isUnlocked()) {
|
|
1746
|
-
return;
|
|
3024
|
+
if (msg.includes("insufficient") && msg.includes("balance")) {
|
|
3025
|
+
return new InsufficientBalanceError(err.message);
|
|
1747
3026
|
}
|
|
1748
|
-
if (
|
|
1749
|
-
|
|
1750
|
-
return;
|
|
3027
|
+
if (msg.includes("no route") || msg.includes("no swap") || msg.includes("no provider")) {
|
|
3028
|
+
return new NoRouteError(err.message);
|
|
1751
3029
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
}
|
|
1755
|
-
|
|
1756
|
-
// src/adapters/cli-context.ts
|
|
1757
|
-
var CLIContext = class extends BaseCommandContext {
|
|
1758
|
-
get isInteractive() {
|
|
1759
|
-
return false;
|
|
3030
|
+
if (msg.includes("token not found") || msg.includes("unknown token")) {
|
|
3031
|
+
return new TokenNotFoundError(err.message);
|
|
1760
3032
|
}
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
* In CLI mode, we check env vars first, then prompt
|
|
1764
|
-
* No caching since each command runs independently
|
|
1765
|
-
*/
|
|
1766
|
-
async getPassword(vaultId, vaultName) {
|
|
1767
|
-
return getPassword(vaultId, vaultName);
|
|
3033
|
+
if (msg.includes("pricing") || msg.includes("price unavailable") || msg.includes("price service")) {
|
|
3034
|
+
return new PricingUnavailableError(err.message);
|
|
1768
3035
|
}
|
|
1769
|
-
|
|
3036
|
+
if (msg.includes("econnrefused") || msg.includes("etimedout") || msg.includes("enotfound") || msg.includes("econnreset") || msg.includes("fetch failed") || msg.includes("network") || msg.includes("socket hang up") || msg.includes("dns")) {
|
|
3037
|
+
return new NetworkError(err.message);
|
|
3038
|
+
}
|
|
3039
|
+
return new UnknownError(err.message);
|
|
3040
|
+
}
|
|
3041
|
+
function toErrorJson(err) {
|
|
3042
|
+
if (err instanceof VsigError) {
|
|
3043
|
+
const json = {
|
|
3044
|
+
success: false,
|
|
3045
|
+
v: 1,
|
|
3046
|
+
error: {
|
|
3047
|
+
code: err.code,
|
|
3048
|
+
exitCode: err.exitCode,
|
|
3049
|
+
message: err.message,
|
|
3050
|
+
hint: err.hint,
|
|
3051
|
+
retryable: err.retryable
|
|
3052
|
+
}
|
|
3053
|
+
};
|
|
3054
|
+
if (err.suggestions?.length) json.error.suggestions = err.suggestions;
|
|
3055
|
+
if (err.context) json.error.context = err.context;
|
|
3056
|
+
return json;
|
|
3057
|
+
}
|
|
3058
|
+
return {
|
|
3059
|
+
success: false,
|
|
3060
|
+
v: 1,
|
|
3061
|
+
error: {
|
|
3062
|
+
code: "UNKNOWN_ERROR",
|
|
3063
|
+
exitCode: 7 /* UNKNOWN */,
|
|
3064
|
+
message: err.message,
|
|
3065
|
+
retryable: false
|
|
3066
|
+
}
|
|
3067
|
+
};
|
|
3068
|
+
}
|
|
1770
3069
|
|
|
1771
3070
|
// src/adapters/cli-runner.ts
|
|
1772
3071
|
function withExit(handler) {
|
|
@@ -1775,17 +3074,18 @@ function withExit(handler) {
|
|
|
1775
3074
|
await handler(...args);
|
|
1776
3075
|
process.exit(0);
|
|
1777
3076
|
} catch (err) {
|
|
1778
|
-
const
|
|
3077
|
+
const classified = err instanceof VsigError ? err : err instanceof Error ? classifyError(err) : classifyError(new Error(String(err)));
|
|
1779
3078
|
if (isJsonOutput()) {
|
|
1780
|
-
|
|
1781
|
-
process.exit(exitCode);
|
|
1782
|
-
}
|
|
1783
|
-
if (err.exitCode !== void 0) {
|
|
1784
|
-
process.exit(err.exitCode);
|
|
3079
|
+
outputErrorJson(toErrorJson(classified));
|
|
3080
|
+
process.exit(classified.exitCode);
|
|
1785
3081
|
}
|
|
1786
3082
|
printError(`
|
|
1787
|
-
x ${
|
|
1788
|
-
|
|
3083
|
+
x ${classified.message}`);
|
|
3084
|
+
if (classified.hint) printError(` hint: ${classified.hint}`);
|
|
3085
|
+
if (classified.suggestions?.length) {
|
|
3086
|
+
for (const s of classified.suggestions) printError(` - ${s}`);
|
|
3087
|
+
}
|
|
3088
|
+
process.exit(classified.exitCode);
|
|
1789
3089
|
}
|
|
1790
3090
|
};
|
|
1791
3091
|
}
|
|
@@ -1914,6 +3214,7 @@ function displayVaultsList(vaults, activeVault) {
|
|
|
1914
3214
|
printTable(table);
|
|
1915
3215
|
}
|
|
1916
3216
|
async function confirmTransaction() {
|
|
3217
|
+
requireInteractive("Use --yes to skip confirmation, or --password to provide password non-interactively.");
|
|
1917
3218
|
const { confirmed } = await inquirer2.prompt([
|
|
1918
3219
|
{
|
|
1919
3220
|
type: "confirm",
|
|
@@ -2033,6 +3334,7 @@ function displaySwapChains(chains) {
|
|
|
2033
3334
|
Total: ${chains.length} chains`);
|
|
2034
3335
|
}
|
|
2035
3336
|
async function confirmSwap() {
|
|
3337
|
+
requireInteractive("Use --yes to skip confirmation, or --password to provide password non-interactively.");
|
|
2036
3338
|
const { confirmed } = await inquirer2.prompt([
|
|
2037
3339
|
{
|
|
2038
3340
|
type: "confirm",
|
|
@@ -2121,13 +3423,25 @@ async function executeChains(ctx2, options = {}) {
|
|
|
2121
3423
|
return;
|
|
2122
3424
|
}
|
|
2123
3425
|
if (options.add) {
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
3426
|
+
const alreadyActive = vault.chains.includes(options.add);
|
|
3427
|
+
if (!alreadyActive) {
|
|
3428
|
+
await vault.addChain(options.add);
|
|
3429
|
+
}
|
|
2127
3430
|
const address = await vault.address(options.add);
|
|
3431
|
+
if (isJsonOutput()) {
|
|
3432
|
+
outputJson({ chain: options.add, alreadyActive, address, chains: [...vault.chains] });
|
|
3433
|
+
return;
|
|
3434
|
+
}
|
|
3435
|
+
success(alreadyActive ? `
|
|
3436
|
+
Chain already active: ${options.add}` : `
|
|
3437
|
+
+ Added chain: ${options.add}`);
|
|
2128
3438
|
info(`Address: ${address}`);
|
|
2129
3439
|
} else if (options.remove) {
|
|
2130
3440
|
await vault.removeChain(options.remove);
|
|
3441
|
+
if (isJsonOutput()) {
|
|
3442
|
+
outputJson({ chain: options.remove, removed: true, chains: [...vault.chains] });
|
|
3443
|
+
return;
|
|
3444
|
+
}
|
|
2131
3445
|
success(`
|
|
2132
3446
|
+ Removed chain: ${options.remove}`);
|
|
2133
3447
|
} else {
|
|
@@ -2162,6 +3476,10 @@ import chalk4 from "chalk";
|
|
|
2162
3476
|
import inquirer3 from "inquirer";
|
|
2163
3477
|
async function executeTokens(ctx2, options) {
|
|
2164
3478
|
await ctx2.ensureActiveVault();
|
|
3479
|
+
if (options.discover) {
|
|
3480
|
+
await discoverTokens(ctx2, options.chain);
|
|
3481
|
+
return;
|
|
3482
|
+
}
|
|
2165
3483
|
if (options.add) {
|
|
2166
3484
|
let symbol = options.symbol;
|
|
2167
3485
|
let name = options.name;
|
|
@@ -2193,6 +3511,7 @@ async function executeTokens(ctx2, options) {
|
|
|
2193
3511
|
});
|
|
2194
3512
|
}
|
|
2195
3513
|
if (prompts.length > 0) {
|
|
3514
|
+
requireInteractive("Provide --symbol, --name, and --decimals flags for non-interactive token addition.");
|
|
2196
3515
|
const answers = await inquirer3.prompt(prompts);
|
|
2197
3516
|
symbol = symbol || answers.symbol?.trim();
|
|
2198
3517
|
name = name || answers.name?.trim();
|
|
@@ -2231,6 +3550,57 @@ async function removeToken(ctx2, chain, tokenId) {
|
|
|
2231
3550
|
success(`
|
|
2232
3551
|
+ Removed token ${tokenId} from ${chain}`);
|
|
2233
3552
|
}
|
|
3553
|
+
async function discoverTokens(ctx2, chain) {
|
|
3554
|
+
const vault = await ctx2.ensureActiveVault();
|
|
3555
|
+
const spinner = createSpinner(`Discovering tokens on ${chain}...`);
|
|
3556
|
+
let discovered;
|
|
3557
|
+
try {
|
|
3558
|
+
discovered = await vault.discoverTokens(chain);
|
|
3559
|
+
} catch (err) {
|
|
3560
|
+
spinner.fail(`Token discovery failed for ${chain}`);
|
|
3561
|
+
throw err;
|
|
3562
|
+
}
|
|
3563
|
+
if (discovered.length === 0) {
|
|
3564
|
+
spinner.succeed(`No new tokens found on ${chain}`);
|
|
3565
|
+
if (isJsonOutput()) {
|
|
3566
|
+
outputJson({ chain, discovered: [], count: 0 });
|
|
3567
|
+
}
|
|
3568
|
+
return;
|
|
3569
|
+
}
|
|
3570
|
+
const existingTokens = vault.getTokens(chain);
|
|
3571
|
+
const existingAddresses = new Set(existingTokens.map((t) => t.contractAddress ?? t.id));
|
|
3572
|
+
const newTokens = discovered.filter((d) => d.contractAddress && !existingAddresses.has(d.contractAddress));
|
|
3573
|
+
for (const d of newTokens) {
|
|
3574
|
+
await vault.addToken(chain, {
|
|
3575
|
+
id: d.contractAddress,
|
|
3576
|
+
symbol: d.ticker,
|
|
3577
|
+
name: d.ticker,
|
|
3578
|
+
decimals: d.decimals,
|
|
3579
|
+
contractAddress: d.contractAddress,
|
|
3580
|
+
chainId: chain,
|
|
3581
|
+
isNative: false
|
|
3582
|
+
});
|
|
3583
|
+
}
|
|
3584
|
+
const allTokens = vault.getTokens(chain);
|
|
3585
|
+
spinner.succeed(`Discovered ${newTokens.length} new token(s) on ${chain}`);
|
|
3586
|
+
if (isJsonOutput()) {
|
|
3587
|
+
outputJson({
|
|
3588
|
+
chain,
|
|
3589
|
+
discovered: newTokens.map((d) => ({
|
|
3590
|
+
symbol: d.ticker,
|
|
3591
|
+
contractAddress: d.contractAddress,
|
|
3592
|
+
decimals: d.decimals
|
|
3593
|
+
})),
|
|
3594
|
+
count: newTokens.length
|
|
3595
|
+
});
|
|
3596
|
+
return;
|
|
3597
|
+
}
|
|
3598
|
+
for (const d of newTokens) {
|
|
3599
|
+
printResult(` ${d.ticker} (${d.contractAddress})`);
|
|
3600
|
+
}
|
|
3601
|
+
info(chalk4.gray(`
|
|
3602
|
+
${allTokens.length} total token(s) tracked on ${chain}`));
|
|
3603
|
+
}
|
|
2234
3604
|
async function listTokens(ctx2, chain) {
|
|
2235
3605
|
const vault = await ctx2.ensureActiveVault();
|
|
2236
3606
|
const spinner = createSpinner(`Loading tokens for ${chain}...`);
|
|
@@ -2263,8 +3633,15 @@ Use --add <contractAddress> to add or --remove <tokenId> to remove`));
|
|
|
2263
3633
|
}
|
|
2264
3634
|
|
|
2265
3635
|
// src/commands/transaction.ts
|
|
2266
|
-
var import_qrcode_terminal = __toESM(require_main(), 1);
|
|
2267
3636
|
import { Chain, Vultisig as Vultisig2 } from "@vultisig/sdk";
|
|
3637
|
+
|
|
3638
|
+
// src/core/config-store.ts
|
|
3639
|
+
import { getConfigPath, loadConfig, saveConfig } from "@vultisig/client-shared";
|
|
3640
|
+
|
|
3641
|
+
// src/core/vault-discovery.ts
|
|
3642
|
+
import { discoverVaultFiles, SEARCH_DIRS } from "@vultisig/client-shared";
|
|
3643
|
+
|
|
3644
|
+
// src/commands/transaction.ts
|
|
2268
3645
|
async function executeSend(ctx2, params) {
|
|
2269
3646
|
const vault = await ctx2.ensureActiveVault();
|
|
2270
3647
|
if (!Object.values(Chain).includes(params.chain)) {
|
|
@@ -2278,59 +3655,59 @@ async function executeSend(ctx2, params) {
|
|
|
2278
3655
|
}
|
|
2279
3656
|
async function sendTransaction(vault, params) {
|
|
2280
3657
|
const prepareSpinner = createSpinner("Preparing transaction...");
|
|
2281
|
-
const
|
|
2282
|
-
const balance = await vault.balance(params.chain, params.tokenId);
|
|
2283
|
-
const coin = {
|
|
3658
|
+
const dryResult = await vault.send({
|
|
2284
3659
|
chain: params.chain,
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
const isMax = params.amount === "max";
|
|
2291
|
-
let amount;
|
|
2292
|
-
let displayAmount;
|
|
2293
|
-
if (isMax) {
|
|
2294
|
-
const maxInfo = await vault.getMaxSendAmount({ coin, receiver: params.to, memo: params.memo });
|
|
2295
|
-
amount = maxInfo.maxSendable;
|
|
2296
|
-
if (amount === 0n) {
|
|
2297
|
-
throw new Error("Insufficient balance to cover network fees");
|
|
2298
|
-
}
|
|
2299
|
-
displayAmount = formatBigintAmount(amount, balance.decimals);
|
|
2300
|
-
} else {
|
|
2301
|
-
const [whole, frac = ""] = params.amount.split(".");
|
|
2302
|
-
if (frac.length > balance.decimals) {
|
|
2303
|
-
throw new Error(`Amount has more than ${balance.decimals} decimal places`);
|
|
2304
|
-
}
|
|
2305
|
-
const paddedFrac = frac.padEnd(balance.decimals, "0");
|
|
2306
|
-
amount = BigInt(whole || "0") * 10n ** BigInt(balance.decimals) + BigInt(paddedFrac || "0");
|
|
2307
|
-
displayAmount = params.amount;
|
|
2308
|
-
}
|
|
2309
|
-
const payload = await vault.prepareSendTx({
|
|
2310
|
-
coin,
|
|
2311
|
-
receiver: params.to,
|
|
2312
|
-
amount,
|
|
2313
|
-
memo: params.memo
|
|
3660
|
+
to: params.to,
|
|
3661
|
+
amount: params.amount,
|
|
3662
|
+
symbol: params.tokenId,
|
|
3663
|
+
memo: params.memo,
|
|
3664
|
+
dryRun: true
|
|
2314
3665
|
});
|
|
2315
3666
|
prepareSpinner.succeed("Transaction prepared");
|
|
3667
|
+
if (!dryResult.dryRun) throw new Error("unreachable");
|
|
3668
|
+
if (params.dryRun) {
|
|
3669
|
+
const balance2 = await vault.balance(params.chain, params.tokenId);
|
|
3670
|
+
const hasInsufficientBalance = parseFloat(dryResult.total) > parseFloat(balance2.formattedAmount);
|
|
3671
|
+
const result = {
|
|
3672
|
+
dryRun: true,
|
|
3673
|
+
chain: params.chain,
|
|
3674
|
+
to: params.to,
|
|
3675
|
+
amount: params.amount,
|
|
3676
|
+
symbol: balance2.symbol,
|
|
3677
|
+
balance: balance2.formattedAmount
|
|
3678
|
+
};
|
|
3679
|
+
if (hasInsufficientBalance) {
|
|
3680
|
+
result.warning = `Insufficient balance: you have ${balance2.formattedAmount} ${balance2.symbol}`;
|
|
3681
|
+
}
|
|
3682
|
+
if (isJsonOutput()) {
|
|
3683
|
+
outputJson(result);
|
|
3684
|
+
} else {
|
|
3685
|
+
info(`
|
|
3686
|
+
Dry-run preview:`);
|
|
3687
|
+
info(` Chain: ${result.chain}`);
|
|
3688
|
+
info(` To: ${result.to}`);
|
|
3689
|
+
info(` Amount: ${result.amount} ${result.symbol}`);
|
|
3690
|
+
info(` Fee: ${dryResult.fee} ${result.symbol}`);
|
|
3691
|
+
info(` Balance: ${result.balance} ${result.symbol}`);
|
|
3692
|
+
if (result.warning) warn(` Warning: ${result.warning}`);
|
|
3693
|
+
}
|
|
3694
|
+
return result;
|
|
3695
|
+
}
|
|
2316
3696
|
let gas;
|
|
2317
3697
|
try {
|
|
2318
3698
|
gas = await vault.gas(params.chain);
|
|
2319
3699
|
} catch {
|
|
2320
3700
|
warn("\nGas estimation unavailable");
|
|
2321
3701
|
}
|
|
3702
|
+
const balance = await vault.balance(params.chain, params.tokenId);
|
|
2322
3703
|
if (!isJsonOutput()) {
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
params.to,
|
|
2326
|
-
displayAmount,
|
|
2327
|
-
payload.coin.ticker,
|
|
2328
|
-
params.chain,
|
|
2329
|
-
params.memo,
|
|
2330
|
-
gas
|
|
2331
|
-
);
|
|
3704
|
+
const address = await vault.address(params.chain);
|
|
3705
|
+
displayTransactionPreview(address, params.to, dryResult.total, balance.symbol, params.chain, params.memo, gas);
|
|
2332
3706
|
}
|
|
2333
|
-
if (!params.yes
|
|
3707
|
+
if (!params.yes) {
|
|
3708
|
+
if (isNonInteractive()) {
|
|
3709
|
+
throw new Error("Transaction requires confirmation. Use --yes to skip, or --dry-run to preview.");
|
|
3710
|
+
}
|
|
2334
3711
|
const confirmed = await confirmTransaction();
|
|
2335
3712
|
if (!confirmed) {
|
|
2336
3713
|
warn("Transaction cancelled");
|
|
@@ -2338,90 +3715,55 @@ async function sendTransaction(vault, params) {
|
|
|
2338
3715
|
}
|
|
2339
3716
|
}
|
|
2340
3717
|
await ensureVaultUnlocked(vault, params.password);
|
|
2341
|
-
const
|
|
2342
|
-
|
|
3718
|
+
const signSpinner = createSpinner(
|
|
3719
|
+
vault.type === "secure" ? "Preparing secure signing session..." : "Signing transaction..."
|
|
3720
|
+
);
|
|
2343
3721
|
vault.on("signingProgress", ({ step }) => {
|
|
2344
3722
|
signSpinner.text = `${step.message} (${step.progress}%)`;
|
|
2345
3723
|
});
|
|
2346
|
-
if (isSecureVault) {
|
|
2347
|
-
vault.on("qrCodeReady", ({ qrPayload }) => {
|
|
2348
|
-
if (isJsonOutput()) {
|
|
2349
|
-
printResult(qrPayload);
|
|
2350
|
-
} else if (isSilent()) {
|
|
2351
|
-
printResult(`QR Payload: ${qrPayload}`);
|
|
2352
|
-
} else {
|
|
2353
|
-
signSpinner.stop();
|
|
2354
|
-
info("\nScan this QR code with your Vultisig mobile app to sign:");
|
|
2355
|
-
import_qrcode_terminal.default.generate(qrPayload, { small: true });
|
|
2356
|
-
info(`
|
|
2357
|
-
Or use this URL: ${qrPayload}
|
|
2358
|
-
`);
|
|
2359
|
-
signSpinner.start("Waiting for devices to join signing session...");
|
|
2360
|
-
}
|
|
2361
|
-
});
|
|
2362
|
-
vault.on(
|
|
2363
|
-
"deviceJoined",
|
|
2364
|
-
({ deviceId, totalJoined, required }) => {
|
|
2365
|
-
if (!isSilent()) {
|
|
2366
|
-
signSpinner.text = `Device joined: ${totalJoined}/${required} (${deviceId})`;
|
|
2367
|
-
} else if (!isJsonOutput()) {
|
|
2368
|
-
printResult(`Device joined: ${totalJoined}/${required}`);
|
|
2369
|
-
}
|
|
2370
|
-
}
|
|
2371
|
-
);
|
|
2372
|
-
}
|
|
2373
3724
|
try {
|
|
2374
|
-
const
|
|
2375
|
-
const signature = await vault.sign(
|
|
2376
|
-
{
|
|
2377
|
-
transaction: payload,
|
|
2378
|
-
chain: payload.coin.chain,
|
|
2379
|
-
messageHashes
|
|
2380
|
-
},
|
|
2381
|
-
{ signal: params.signal }
|
|
2382
|
-
);
|
|
2383
|
-
signSpinner.succeed("Transaction signed");
|
|
2384
|
-
const broadcastSpinner = createSpinner("Broadcasting transaction...");
|
|
2385
|
-
const txHash = await vault.broadcastTx({
|
|
3725
|
+
const result = await vault.send({
|
|
2386
3726
|
chain: params.chain,
|
|
2387
|
-
|
|
2388
|
-
|
|
3727
|
+
to: params.to,
|
|
3728
|
+
amount: params.amount,
|
|
3729
|
+
symbol: params.tokenId,
|
|
3730
|
+
memo: params.memo
|
|
2389
3731
|
});
|
|
2390
|
-
|
|
2391
|
-
const
|
|
2392
|
-
|
|
3732
|
+
if (result.dryRun) throw new Error("unreachable");
|
|
3733
|
+
const broadcast = result;
|
|
3734
|
+
signSpinner.succeed(`Transaction broadcast: ${broadcast.txHash}`);
|
|
3735
|
+
const txResult = {
|
|
3736
|
+
txHash: broadcast.txHash,
|
|
2393
3737
|
chain: params.chain,
|
|
2394
|
-
explorerUrl: Vultisig2.getTxExplorerUrl(params.chain, txHash)
|
|
3738
|
+
explorerUrl: Vultisig2.getTxExplorerUrl(params.chain, broadcast.txHash)
|
|
2395
3739
|
};
|
|
2396
3740
|
if (isJsonOutput()) {
|
|
2397
|
-
outputJson(
|
|
3741
|
+
outputJson(txResult);
|
|
2398
3742
|
} else {
|
|
2399
|
-
displayTransactionResult(params.chain, txHash);
|
|
3743
|
+
displayTransactionResult(params.chain, broadcast.txHash);
|
|
2400
3744
|
}
|
|
2401
|
-
return
|
|
3745
|
+
return txResult;
|
|
2402
3746
|
} finally {
|
|
2403
3747
|
vault.removeAllListeners("signingProgress");
|
|
2404
|
-
if (isSecureVault) {
|
|
2405
|
-
vault.removeAllListeners("qrCodeReady");
|
|
2406
|
-
vault.removeAllListeners("deviceJoined");
|
|
2407
|
-
}
|
|
2408
3748
|
}
|
|
2409
3749
|
}
|
|
2410
3750
|
|
|
2411
3751
|
// src/commands/execute.ts
|
|
2412
|
-
var
|
|
3752
|
+
var import_qrcode_terminal = __toESM(require_main(), 1);
|
|
2413
3753
|
import { Vultisig as Vultisig3 } from "@vultisig/sdk";
|
|
2414
3754
|
var COSMOS_CHAIN_CONFIG = {
|
|
2415
3755
|
THORChain: {
|
|
2416
3756
|
chainId: "thorchain-1",
|
|
2417
3757
|
prefix: "thor",
|
|
2418
3758
|
denom: "rune",
|
|
3759
|
+
decimals: 8,
|
|
2419
3760
|
gasLimit: "500000"
|
|
2420
3761
|
},
|
|
2421
3762
|
MayaChain: {
|
|
2422
3763
|
chainId: "mayachain-mainnet-v1",
|
|
2423
3764
|
prefix: "maya",
|
|
2424
3765
|
denom: "cacao",
|
|
3766
|
+
decimals: 10,
|
|
2425
3767
|
gasLimit: "500000"
|
|
2426
3768
|
}
|
|
2427
3769
|
};
|
|
@@ -2456,8 +3798,42 @@ async function executeContractTransaction(vault, params, chainConfig, msg, funds
|
|
|
2456
3798
|
const prepareSpinner = createSpinner("Preparing contract execution...");
|
|
2457
3799
|
const address = await vault.address(params.chain);
|
|
2458
3800
|
prepareSpinner.succeed("Transaction prepared");
|
|
3801
|
+
if (params.dryRun) {
|
|
3802
|
+
if (isJsonOutput()) {
|
|
3803
|
+
outputJson({
|
|
3804
|
+
dryRun: true,
|
|
3805
|
+
chain: params.chain,
|
|
3806
|
+
contract: params.contract,
|
|
3807
|
+
msg,
|
|
3808
|
+
funds,
|
|
3809
|
+
address,
|
|
3810
|
+
memo: params.memo
|
|
3811
|
+
});
|
|
3812
|
+
} else {
|
|
3813
|
+
info("\nContract Execution Preview (dry-run)");
|
|
3814
|
+
info("\u2501".repeat(50));
|
|
3815
|
+
info(`Chain: ${params.chain}`);
|
|
3816
|
+
info(`From: ${address}`);
|
|
3817
|
+
info(`Contract: ${params.contract}`);
|
|
3818
|
+
info(
|
|
3819
|
+
`Message: ${JSON.stringify(msg, null, 2).substring(0, 200)}${JSON.stringify(msg).length > 200 ? "..." : ""}`
|
|
3820
|
+
);
|
|
3821
|
+
if (funds.length > 0) {
|
|
3822
|
+
info(`Funds: ${funds.map((f) => `${f.amount} ${f.denom}`).join(", ")}`);
|
|
3823
|
+
}
|
|
3824
|
+
if (params.memo) {
|
|
3825
|
+
info(`Memo: ${params.memo}`);
|
|
3826
|
+
}
|
|
3827
|
+
info("\u2501".repeat(50));
|
|
3828
|
+
}
|
|
3829
|
+
return {
|
|
3830
|
+
txHash: "",
|
|
3831
|
+
chain: params.chain,
|
|
3832
|
+
explorerUrl: ""
|
|
3833
|
+
};
|
|
3834
|
+
}
|
|
2459
3835
|
if (!isJsonOutput()) {
|
|
2460
|
-
info("\
|
|
3836
|
+
info("\nContract Execution Preview");
|
|
2461
3837
|
info("\u2501".repeat(50));
|
|
2462
3838
|
info(`Chain: ${params.chain}`);
|
|
2463
3839
|
info(`From: ${address}`);
|
|
@@ -2473,7 +3849,10 @@ async function executeContractTransaction(vault, params, chainConfig, msg, funds
|
|
|
2473
3849
|
}
|
|
2474
3850
|
info("\u2501".repeat(50));
|
|
2475
3851
|
}
|
|
2476
|
-
if (!params.yes
|
|
3852
|
+
if (!params.yes) {
|
|
3853
|
+
if (isNonInteractive()) {
|
|
3854
|
+
throw new Error("Transaction requires confirmation. Use --yes to skip, or --dry-run to preview.");
|
|
3855
|
+
}
|
|
2477
3856
|
const confirmed = await confirmTransaction();
|
|
2478
3857
|
if (!confirmed) {
|
|
2479
3858
|
warn("Transaction cancelled");
|
|
@@ -2495,7 +3874,7 @@ async function executeContractTransaction(vault, params, chainConfig, msg, funds
|
|
|
2495
3874
|
} else {
|
|
2496
3875
|
signSpinner.stop();
|
|
2497
3876
|
info("\nScan this QR code with your Vultisig mobile app to sign:");
|
|
2498
|
-
|
|
3877
|
+
import_qrcode_terminal.default.generate(qrPayload, { small: true });
|
|
2499
3878
|
info(`
|
|
2500
3879
|
Or use this URL: ${qrPayload}
|
|
2501
3880
|
`);
|
|
@@ -2518,8 +3897,7 @@ Or use this URL: ${qrPayload}
|
|
|
2518
3897
|
const coin = {
|
|
2519
3898
|
chain: cosmosChain,
|
|
2520
3899
|
address,
|
|
2521
|
-
decimals:
|
|
2522
|
-
// THORChain uses 8 decimals
|
|
3900
|
+
decimals: chainConfig.decimals,
|
|
2523
3901
|
ticker: chainConfig.denom.toUpperCase()
|
|
2524
3902
|
};
|
|
2525
3903
|
const executeContractMsg = {
|
|
@@ -2580,7 +3958,7 @@ Or use this URL: ${qrPayload}
|
|
|
2580
3958
|
}
|
|
2581
3959
|
|
|
2582
3960
|
// src/commands/sign.ts
|
|
2583
|
-
var
|
|
3961
|
+
var import_qrcode_terminal2 = __toESM(require_main(), 1);
|
|
2584
3962
|
import { Chain as Chain3 } from "@vultisig/sdk";
|
|
2585
3963
|
async function executeSignBytes(ctx2, params) {
|
|
2586
3964
|
const vault = await ctx2.ensureActiveVault();
|
|
@@ -2606,7 +3984,7 @@ async function signBytes(vault, params) {
|
|
|
2606
3984
|
} else {
|
|
2607
3985
|
signSpinner.stop();
|
|
2608
3986
|
info("\nScan this QR code with your Vultisig mobile app to sign:");
|
|
2609
|
-
|
|
3987
|
+
import_qrcode_terminal2.default.generate(qrPayload, { small: true });
|
|
2610
3988
|
info(`
|
|
2611
3989
|
Or use this URL: ${qrPayload}
|
|
2612
3990
|
`);
|
|
@@ -2756,7 +4134,7 @@ function sleep(ms) {
|
|
|
2756
4134
|
}
|
|
2757
4135
|
|
|
2758
4136
|
// src/commands/vault-management.ts
|
|
2759
|
-
var
|
|
4137
|
+
var import_qrcode_terminal3 = __toESM(require_main(), 1);
|
|
2760
4138
|
import { FastVault } from "@vultisig/sdk";
|
|
2761
4139
|
import chalk5 from "chalk";
|
|
2762
4140
|
import { promises as fs } from "fs";
|
|
@@ -2904,7 +4282,7 @@ async function executeCreateSecure(ctx2, options) {
|
|
|
2904
4282
|
} else {
|
|
2905
4283
|
spinner.stop();
|
|
2906
4284
|
info("\nScan this QR code with your Vultisig mobile app:");
|
|
2907
|
-
|
|
4285
|
+
import_qrcode_terminal3.default.generate(qrPayload, { small: true });
|
|
2908
4286
|
info(`
|
|
2909
4287
|
Or use this URL: ${qrPayload}
|
|
2910
4288
|
`);
|
|
@@ -3427,7 +4805,7 @@ async function executeCreateFromSeedphraseSecure(ctx2, options) {
|
|
|
3427
4805
|
} else {
|
|
3428
4806
|
importSpinner.stop();
|
|
3429
4807
|
info("\nScan this QR code with your Vultisig mobile app:");
|
|
3430
|
-
|
|
4808
|
+
import_qrcode_terminal3.default.generate(qrPayload, { small: true });
|
|
3431
4809
|
info(`
|
|
3432
4810
|
Or use this URL: ${qrPayload}
|
|
3433
4811
|
`);
|
|
@@ -3616,35 +4994,24 @@ async function executeSwapQuote(ctx2, options) {
|
|
|
3616
4994
|
if (!isMax && (isNaN(options.amount) || options.amount <= 0)) {
|
|
3617
4995
|
throw new Error("Invalid amount");
|
|
3618
4996
|
}
|
|
3619
|
-
const isSupported = await vault.isSwapSupported(options.fromChain, options.toChain);
|
|
3620
|
-
if (!isSupported) {
|
|
3621
|
-
throw new Error(`Swaps from ${options.fromChain} to ${options.toChain} are not supported`);
|
|
3622
|
-
}
|
|
3623
|
-
let resolvedAmount;
|
|
3624
|
-
if (isMax) {
|
|
3625
|
-
const bal = await vault.balance(options.fromChain, options.fromToken);
|
|
3626
|
-
resolvedAmount = parseFloat(bal.formattedAmount);
|
|
3627
|
-
if (resolvedAmount <= 0) {
|
|
3628
|
-
throw new Error("Zero balance \u2014 nothing to swap");
|
|
3629
|
-
}
|
|
3630
|
-
} else {
|
|
3631
|
-
resolvedAmount = options.amount;
|
|
3632
|
-
}
|
|
3633
4997
|
const spinner = createSpinner("Getting swap quote...");
|
|
3634
|
-
const
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
4998
|
+
const result = await vault.swap({
|
|
4999
|
+
fromChain: options.fromChain,
|
|
5000
|
+
fromSymbol: options.fromToken || "",
|
|
5001
|
+
toChain: options.toChain,
|
|
5002
|
+
toSymbol: options.toToken || "",
|
|
5003
|
+
amount: isMax ? "max" : String(options.amount),
|
|
5004
|
+
dryRun: true
|
|
3640
5005
|
});
|
|
5006
|
+
if (!result.dryRun) throw new Error("unreachable");
|
|
3641
5007
|
spinner.succeed("Quote received");
|
|
3642
|
-
const
|
|
5008
|
+
const quote = result.quote;
|
|
5009
|
+
const fromAmountDisplay = isMax ? `${formatBigintAmount(quote.maxSwapable, quote.fromCoin.decimals)} (max)` : String(options.amount);
|
|
3643
5010
|
if (isJsonOutput()) {
|
|
3644
5011
|
outputJson({
|
|
3645
5012
|
fromChain: options.fromChain,
|
|
3646
5013
|
toChain: options.toChain,
|
|
3647
|
-
amount:
|
|
5014
|
+
amount: isMax ? "max" : options.amount,
|
|
3648
5015
|
isMax,
|
|
3649
5016
|
quote
|
|
3650
5017
|
});
|
|
@@ -3668,30 +5035,57 @@ async function executeSwap(ctx2, options) {
|
|
|
3668
5035
|
if (!isMax && (isNaN(options.amount) || options.amount <= 0)) {
|
|
3669
5036
|
throw new Error("Invalid amount");
|
|
3670
5037
|
}
|
|
3671
|
-
const
|
|
3672
|
-
if (!isSupported) {
|
|
3673
|
-
throw new Error(`Swaps from ${options.fromChain} to ${options.toChain} are not supported`);
|
|
3674
|
-
}
|
|
3675
|
-
let resolvedAmount;
|
|
3676
|
-
if (isMax) {
|
|
3677
|
-
const bal = await vault.balance(options.fromChain, options.fromToken);
|
|
3678
|
-
resolvedAmount = parseFloat(bal.formattedAmount);
|
|
3679
|
-
if (resolvedAmount <= 0) {
|
|
3680
|
-
throw new Error("Zero balance \u2014 nothing to swap");
|
|
3681
|
-
}
|
|
3682
|
-
} else {
|
|
3683
|
-
resolvedAmount = options.amount;
|
|
3684
|
-
}
|
|
5038
|
+
const amountStr = isMax ? "max" : String(options.amount);
|
|
3685
5039
|
const quoteSpinner = createSpinner("Getting swap quote...");
|
|
3686
|
-
const
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
5040
|
+
const dryResult = await vault.swap({
|
|
5041
|
+
fromChain: options.fromChain,
|
|
5042
|
+
fromSymbol: options.fromToken || "",
|
|
5043
|
+
toChain: options.toChain,
|
|
5044
|
+
toSymbol: options.toToken || "",
|
|
5045
|
+
amount: amountStr,
|
|
5046
|
+
dryRun: true
|
|
3692
5047
|
});
|
|
5048
|
+
if (!dryResult.dryRun) throw new Error("unreachable");
|
|
3693
5049
|
quoteSpinner.succeed("Quote received");
|
|
3694
|
-
const
|
|
5050
|
+
const quote = dryResult.quote;
|
|
5051
|
+
const fromAmountRaw = isMax ? formatBigintAmount(quote.maxSwapable, quote.fromCoin.decimals) : String(options.amount);
|
|
5052
|
+
const fromAmountDisplay = isMax ? `${fromAmountRaw} (max)` : fromAmountRaw;
|
|
5053
|
+
if (options.dryRun) {
|
|
5054
|
+
const estimatedOutput = formatBigintAmount(quote.estimatedOutput, quote.toCoin.decimals);
|
|
5055
|
+
const result = {
|
|
5056
|
+
dryRun: true,
|
|
5057
|
+
fromChain: String(options.fromChain),
|
|
5058
|
+
fromToken: quote.fromCoin.ticker,
|
|
5059
|
+
toChain: String(options.toChain),
|
|
5060
|
+
toToken: quote.toCoin.ticker,
|
|
5061
|
+
inputAmount: fromAmountRaw,
|
|
5062
|
+
...isMax && { isMax: true },
|
|
5063
|
+
estimatedOutput,
|
|
5064
|
+
provider: quote.provider
|
|
5065
|
+
};
|
|
5066
|
+
if (quote.estimatedOutputFiat != null) {
|
|
5067
|
+
result.estimatedOutputFiat = parseFloat(quote.estimatedOutputFiat.toFixed(2));
|
|
5068
|
+
}
|
|
5069
|
+
if (quote.requiresApproval) {
|
|
5070
|
+
result.requiresApproval = true;
|
|
5071
|
+
}
|
|
5072
|
+
if (quote.warnings && quote.warnings.length > 0) {
|
|
5073
|
+
result.warnings = [...quote.warnings];
|
|
5074
|
+
}
|
|
5075
|
+
if (isJsonOutput()) {
|
|
5076
|
+
outputJson(result);
|
|
5077
|
+
} else {
|
|
5078
|
+
info(`
|
|
5079
|
+
Dry-run preview:`);
|
|
5080
|
+
info(` From: ${result.inputAmount} ${result.fromToken} (${result.fromChain})`);
|
|
5081
|
+
info(` To: ${result.estimatedOutput} ${result.toToken} (${result.toChain})`);
|
|
5082
|
+
info(` Provider: ${result.provider}`);
|
|
5083
|
+
if (result.estimatedOutputFiat != null) info(` Est. value (USD): $${result.estimatedOutputFiat}`);
|
|
5084
|
+
if (result.requiresApproval) info(` Requires approval: yes`);
|
|
5085
|
+
if (result.warnings?.length) result.warnings.forEach((w) => warn(` Warning: ${w}`));
|
|
5086
|
+
}
|
|
5087
|
+
return result;
|
|
5088
|
+
}
|
|
3695
5089
|
const feeBalance = await vault.balance(options.fromChain);
|
|
3696
5090
|
const discountTier = await vault.getDiscountTier();
|
|
3697
5091
|
if (!isJsonOutput()) {
|
|
@@ -3703,86 +5097,43 @@ async function executeSwap(ctx2, options) {
|
|
|
3703
5097
|
discountTier
|
|
3704
5098
|
});
|
|
3705
5099
|
}
|
|
3706
|
-
if (!options.yes
|
|
5100
|
+
if (!options.yes) {
|
|
5101
|
+
if (isNonInteractive()) {
|
|
5102
|
+
throw new Error("Swap requires confirmation. Use --yes to skip, or --dry-run to preview.");
|
|
5103
|
+
}
|
|
3707
5104
|
const confirmed = await confirmSwap();
|
|
3708
5105
|
if (!confirmed) {
|
|
3709
5106
|
warn("Swap cancelled");
|
|
3710
5107
|
throw new Error("Swap cancelled by user");
|
|
3711
5108
|
}
|
|
3712
5109
|
}
|
|
3713
|
-
const prepSpinner = createSpinner("Preparing swap transaction...");
|
|
3714
|
-
const { keysignPayload, approvalPayload } = await vault.prepareSwapTx({
|
|
3715
|
-
fromCoin: { chain: options.fromChain, token: options.fromToken },
|
|
3716
|
-
toCoin: { chain: options.toChain, token: options.toToken },
|
|
3717
|
-
amount: resolvedAmount,
|
|
3718
|
-
swapQuote: quote,
|
|
3719
|
-
autoApprove: false
|
|
3720
|
-
});
|
|
3721
|
-
prepSpinner.succeed("Swap prepared");
|
|
3722
5110
|
await ensureVaultUnlocked(vault, options.password);
|
|
3723
|
-
if (approvalPayload) {
|
|
3724
|
-
info("\nToken approval required before swap...");
|
|
3725
|
-
const approvalSpinner = createSpinner("Signing approval transaction...");
|
|
3726
|
-
vault.on("signingProgress", ({ step }) => {
|
|
3727
|
-
approvalSpinner.text = `Approval: ${step.message} (${step.progress}%)`;
|
|
3728
|
-
});
|
|
3729
|
-
try {
|
|
3730
|
-
const approvalHashes = await vault.extractMessageHashes(approvalPayload);
|
|
3731
|
-
const approvalSig = await vault.sign(
|
|
3732
|
-
{
|
|
3733
|
-
transaction: approvalPayload,
|
|
3734
|
-
chain: options.fromChain,
|
|
3735
|
-
messageHashes: approvalHashes
|
|
3736
|
-
},
|
|
3737
|
-
{ signal: options.signal }
|
|
3738
|
-
);
|
|
3739
|
-
approvalSpinner.succeed("Approval signed");
|
|
3740
|
-
const broadcastApprovalSpinner = createSpinner("Broadcasting approval...");
|
|
3741
|
-
const approvalTxHash = await vault.broadcastTx({
|
|
3742
|
-
chain: options.fromChain,
|
|
3743
|
-
keysignPayload: approvalPayload,
|
|
3744
|
-
signature: approvalSig
|
|
3745
|
-
});
|
|
3746
|
-
broadcastApprovalSpinner.succeed(`Approval broadcast: ${approvalTxHash}`);
|
|
3747
|
-
info("Waiting for approval to confirm...");
|
|
3748
|
-
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
3749
|
-
} finally {
|
|
3750
|
-
vault.removeAllListeners("signingProgress");
|
|
3751
|
-
}
|
|
3752
|
-
}
|
|
3753
5111
|
const signSpinner = createSpinner("Signing swap transaction...");
|
|
3754
5112
|
vault.on("signingProgress", ({ step }) => {
|
|
3755
5113
|
signSpinner.text = `${step.message} (${step.progress}%)`;
|
|
3756
5114
|
});
|
|
3757
5115
|
try {
|
|
3758
|
-
const
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
},
|
|
3765
|
-
{ signal: options.signal }
|
|
3766
|
-
);
|
|
3767
|
-
signSpinner.succeed("Swap transaction signed");
|
|
3768
|
-
const broadcastSpinner = createSpinner("Broadcasting swap transaction...");
|
|
3769
|
-
const txHash = await vault.broadcastTx({
|
|
3770
|
-
chain: options.fromChain,
|
|
3771
|
-
keysignPayload,
|
|
3772
|
-
signature
|
|
5116
|
+
const result = await vault.swap({
|
|
5117
|
+
fromChain: options.fromChain,
|
|
5118
|
+
fromSymbol: options.fromToken || "",
|
|
5119
|
+
toChain: options.toChain,
|
|
5120
|
+
toSymbol: options.toToken || "",
|
|
5121
|
+
amount: amountStr
|
|
3773
5122
|
});
|
|
3774
|
-
|
|
5123
|
+
if (result.dryRun) throw new Error("unreachable");
|
|
5124
|
+
const broadcast = result;
|
|
5125
|
+
signSpinner.succeed(`Swap broadcast: ${broadcast.txHash}`);
|
|
3775
5126
|
if (isJsonOutput()) {
|
|
3776
5127
|
outputJson({
|
|
3777
|
-
txHash,
|
|
5128
|
+
txHash: broadcast.txHash,
|
|
3778
5129
|
fromChain: options.fromChain,
|
|
3779
5130
|
toChain: options.toChain,
|
|
3780
5131
|
quote
|
|
3781
5132
|
});
|
|
3782
5133
|
} else {
|
|
3783
|
-
displaySwapResult(options.fromChain, options.toChain, txHash, quote, quote.toCoin.decimals);
|
|
5134
|
+
displaySwapResult(options.fromChain, options.toChain, broadcast.txHash, quote, quote.toCoin.decimals);
|
|
3784
5135
|
}
|
|
3785
|
-
return { txHash, quote };
|
|
5136
|
+
return { txHash: broadcast.txHash, quote };
|
|
3786
5137
|
} finally {
|
|
3787
5138
|
vault.removeAllListeners("signingProgress");
|
|
3788
5139
|
}
|
|
@@ -4067,20 +5418,31 @@ async function executeRujiraSwap(ctx2, options) {
|
|
|
4067
5418
|
slippageBps: options.slippageBps
|
|
4068
5419
|
});
|
|
4069
5420
|
quoteSpinner.succeed("Quote received");
|
|
4070
|
-
if (
|
|
4071
|
-
|
|
4072
|
-
|
|
5421
|
+
if (options.dryRun) {
|
|
5422
|
+
if (isJsonOutput()) {
|
|
5423
|
+
outputJson({ dryRun: true, quote });
|
|
5424
|
+
} else {
|
|
5425
|
+
printResult("FIN Swap Preview (dry-run)");
|
|
5426
|
+
printResult(` From: ${options.fromAsset}`);
|
|
5427
|
+
printResult(` To: ${options.toAsset}`);
|
|
5428
|
+
printResult(` Amount (in): ${options.amount}`);
|
|
5429
|
+
printResult(` Expected out:${quote.expectedOutput}`);
|
|
5430
|
+
printResult(` Min out: ${quote.minimumOutput}`);
|
|
5431
|
+
printResult(` Contract: ${quote.contractAddress}`);
|
|
5432
|
+
}
|
|
4073
5433
|
return;
|
|
4074
5434
|
}
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
5435
|
+
if (!isJsonOutput()) {
|
|
5436
|
+
printResult("FIN Swap Preview");
|
|
5437
|
+
printResult(` From: ${options.fromAsset}`);
|
|
5438
|
+
printResult(` To: ${options.toAsset}`);
|
|
5439
|
+
printResult(` Amount (in): ${options.amount}`);
|
|
5440
|
+
printResult(` Expected out:${quote.expectedOutput}`);
|
|
5441
|
+
printResult(` Min out: ${quote.minimumOutput}`);
|
|
5442
|
+
printResult(` Contract: ${quote.contractAddress}`);
|
|
5443
|
+
if (quote.warning) {
|
|
5444
|
+
warn(quote.warning);
|
|
5445
|
+
}
|
|
4084
5446
|
}
|
|
4085
5447
|
if (!options.yes) {
|
|
4086
5448
|
warn("This command will execute a swap. Re-run with -y/--yes to skip this warning.");
|
|
@@ -4089,7 +5451,11 @@ async function executeRujiraSwap(ctx2, options) {
|
|
|
4089
5451
|
const execSpinner = createSpinner("Executing FIN swap...");
|
|
4090
5452
|
const result = await client.swap.execute(quote, { slippageBps: options.slippageBps });
|
|
4091
5453
|
execSpinner.succeed("Swap submitted");
|
|
4092
|
-
|
|
5454
|
+
if (isJsonOutput()) {
|
|
5455
|
+
outputJson({ quote, result });
|
|
5456
|
+
} else {
|
|
5457
|
+
printResult(`Tx Hash: ${result.txHash}`);
|
|
5458
|
+
}
|
|
4093
5459
|
}
|
|
4094
5460
|
async function executeRujiraWithdraw(ctx2, options) {
|
|
4095
5461
|
const vault = await ctx2.ensureActiveVault();
|
|
@@ -4103,17 +5469,27 @@ async function executeRujiraWithdraw(ctx2, options) {
|
|
|
4103
5469
|
maxFeeBps: options.maxFeeBps
|
|
4104
5470
|
});
|
|
4105
5471
|
prepSpinner.succeed("Withdrawal prepared");
|
|
4106
|
-
if (
|
|
4107
|
-
|
|
4108
|
-
|
|
5472
|
+
if (options.dryRun) {
|
|
5473
|
+
if (isJsonOutput()) {
|
|
5474
|
+
outputJson({ dryRun: true, prepared });
|
|
5475
|
+
} else {
|
|
5476
|
+
printResult("Withdraw Preview (dry-run)");
|
|
5477
|
+
printResult(` Asset: ${prepared.asset}`);
|
|
5478
|
+
printResult(` Amount: ${prepared.amount}`);
|
|
5479
|
+
printResult(` Destination: ${prepared.destination}`);
|
|
5480
|
+
printResult(` Memo: ${prepared.memo}`);
|
|
5481
|
+
printResult(` Est. fee: ${prepared.estimatedFee}`);
|
|
5482
|
+
}
|
|
4109
5483
|
return;
|
|
4110
5484
|
}
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
5485
|
+
if (!isJsonOutput()) {
|
|
5486
|
+
printResult("Withdraw Preview");
|
|
5487
|
+
printResult(` Asset: ${prepared.asset}`);
|
|
5488
|
+
printResult(` Amount: ${prepared.amount}`);
|
|
5489
|
+
printResult(` Destination: ${prepared.destination}`);
|
|
5490
|
+
printResult(` Memo: ${prepared.memo}`);
|
|
5491
|
+
printResult(` Est. fee: ${prepared.estimatedFee}`);
|
|
5492
|
+
}
|
|
4117
5493
|
if (!options.yes) {
|
|
4118
5494
|
warn("This command will broadcast a THORChain MsgDeposit withdrawal. Re-run with -y/--yes to proceed.");
|
|
4119
5495
|
throw new Error("Confirmation required (use --yes)");
|
|
@@ -4121,7 +5497,11 @@ async function executeRujiraWithdraw(ctx2, options) {
|
|
|
4121
5497
|
const execSpinner = createSpinner("Broadcasting withdrawal...");
|
|
4122
5498
|
const result = await client.withdraw.execute(prepared);
|
|
4123
5499
|
execSpinner.succeed("Withdrawal submitted");
|
|
4124
|
-
|
|
5500
|
+
if (isJsonOutput()) {
|
|
5501
|
+
outputJson({ prepared, result });
|
|
5502
|
+
} else {
|
|
5503
|
+
printResult(`Tx Hash: ${result.txHash}`);
|
|
5504
|
+
}
|
|
4125
5505
|
}
|
|
4126
5506
|
|
|
4127
5507
|
// src/commands/discount.ts
|
|
@@ -4222,12 +5602,15 @@ function displayDiscountTier(tierInfo) {
|
|
|
4222
5602
|
printResult("");
|
|
4223
5603
|
}
|
|
4224
5604
|
|
|
5605
|
+
// src/commands/auth.ts
|
|
5606
|
+
import { executeAuthLogout, executeAuthSetup, executeAuthStatus } from "@vultisig/client-shared";
|
|
5607
|
+
|
|
4225
5608
|
// src/commands/agent.ts
|
|
4226
5609
|
import chalk9 from "chalk";
|
|
4227
5610
|
import Table from "cli-table3";
|
|
4228
5611
|
|
|
4229
5612
|
// src/agent/agentErrors.ts
|
|
4230
|
-
import { VaultError, VaultErrorCode, VaultImportError, VaultImportErrorCode } from "@vultisig/sdk";
|
|
5613
|
+
import { VaultError as VaultError2, VaultErrorCode as VaultErrorCode2, VaultImportError as VaultImportError2, VaultImportErrorCode as VaultImportErrorCode2 } from "@vultisig/sdk";
|
|
4231
5614
|
var AgentErrorCode = /* @__PURE__ */ ((AgentErrorCode3) => {
|
|
4232
5615
|
AgentErrorCode3["BACKEND_UNREACHABLE"] = "BACKEND_UNREACHABLE";
|
|
4233
5616
|
AgentErrorCode3["AUTH_FAILED"] = "AUTH_FAILED";
|
|
@@ -4249,32 +5632,32 @@ function isAgentErrorCode(value) {
|
|
|
4249
5632
|
return AGENT_ERROR_CODE_VALUES.has(value);
|
|
4250
5633
|
}
|
|
4251
5634
|
function mapVaultError(err) {
|
|
4252
|
-
if (err.code ===
|
|
5635
|
+
if (err.code === VaultErrorCode2.InvalidConfig && /failed to unlock vault/i.test(err.message)) {
|
|
4253
5636
|
return "AUTH_FAILED" /* AUTH_FAILED */;
|
|
4254
5637
|
}
|
|
4255
5638
|
switch (err.code) {
|
|
4256
|
-
case
|
|
5639
|
+
case VaultErrorCode2.Timeout:
|
|
4257
5640
|
return "TIMEOUT" /* TIMEOUT */;
|
|
4258
|
-
case
|
|
4259
|
-
case
|
|
5641
|
+
case VaultErrorCode2.NetworkError:
|
|
5642
|
+
case VaultErrorCode2.BalanceFetchFailed:
|
|
4260
5643
|
return "NETWORK_ERROR" /* NETWORK_ERROR */;
|
|
4261
|
-
case
|
|
5644
|
+
case VaultErrorCode2.SigningFailed:
|
|
4262
5645
|
return "SIGNING_FAILED" /* SIGNING_FAILED */;
|
|
4263
|
-
case
|
|
4264
|
-
case
|
|
5646
|
+
case VaultErrorCode2.BroadcastFailed:
|
|
5647
|
+
case VaultErrorCode2.GasEstimationFailed:
|
|
4265
5648
|
return "TRANSACTION_FAILED" /* TRANSACTION_FAILED */;
|
|
4266
|
-
case
|
|
5649
|
+
case VaultErrorCode2.NotImplemented:
|
|
4267
5650
|
return "ACTION_NOT_IMPLEMENTED" /* ACTION_NOT_IMPLEMENTED */;
|
|
4268
|
-
case
|
|
4269
|
-
case
|
|
4270
|
-
case
|
|
4271
|
-
case
|
|
4272
|
-
case
|
|
4273
|
-
case
|
|
4274
|
-
case
|
|
4275
|
-
case
|
|
5651
|
+
case VaultErrorCode2.InvalidAmount:
|
|
5652
|
+
case VaultErrorCode2.UnsupportedChain:
|
|
5653
|
+
case VaultErrorCode2.UnsupportedToken:
|
|
5654
|
+
case VaultErrorCode2.ChainNotSupported:
|
|
5655
|
+
case VaultErrorCode2.InvalidVault:
|
|
5656
|
+
case VaultErrorCode2.InvalidPublicKey:
|
|
5657
|
+
case VaultErrorCode2.InvalidChainCode:
|
|
5658
|
+
case VaultErrorCode2.AddressDerivationFailed:
|
|
4276
5659
|
return "INVALID_INPUT" /* INVALID_INPUT */;
|
|
4277
|
-
case
|
|
5660
|
+
case VaultErrorCode2.InvalidConfig:
|
|
4278
5661
|
return "INVALID_INPUT" /* INVALID_INPUT */;
|
|
4279
5662
|
default:
|
|
4280
5663
|
return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
|
|
@@ -4282,9 +5665,9 @@ function mapVaultError(err) {
|
|
|
4282
5665
|
}
|
|
4283
5666
|
function mapVaultImportError(err) {
|
|
4284
5667
|
switch (err.code) {
|
|
4285
|
-
case
|
|
5668
|
+
case VaultImportErrorCode2.PASSWORD_REQUIRED:
|
|
4286
5669
|
return "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */;
|
|
4287
|
-
case
|
|
5670
|
+
case VaultImportErrorCode2.INVALID_PASSWORD:
|
|
4288
5671
|
return "AUTH_FAILED" /* AUTH_FAILED */;
|
|
4289
5672
|
default:
|
|
4290
5673
|
return "INVALID_INPUT" /* INVALID_INPUT */;
|
|
@@ -4322,10 +5705,10 @@ function nodeErrCode(err) {
|
|
|
4322
5705
|
return void 0;
|
|
4323
5706
|
}
|
|
4324
5707
|
function normalizeAgentError(err) {
|
|
4325
|
-
if (err instanceof
|
|
5708
|
+
if (err instanceof VaultError2) {
|
|
4326
5709
|
return { code: mapVaultError(err), message: err.message };
|
|
4327
5710
|
}
|
|
4328
|
-
if (err instanceof
|
|
5711
|
+
if (err instanceof VaultImportError2) {
|
|
4329
5712
|
return { code: mapVaultImportError(err), message: err.message };
|
|
4330
5713
|
}
|
|
4331
5714
|
const name = err instanceof Error ? err.name : "";
|
|
@@ -4441,10 +5824,10 @@ async function authenticateVault(client, vault, password, maxAttempts = 3) {
|
|
|
4441
5824
|
});
|
|
4442
5825
|
const messageHash = computePersonalSignHash(authMessage);
|
|
4443
5826
|
let lastError = null;
|
|
4444
|
-
for (let
|
|
5827
|
+
for (let attempt2 = 1; attempt2 <= maxAttempts; attempt2++) {
|
|
4445
5828
|
try {
|
|
4446
|
-
if (
|
|
4447
|
-
process.stderr.write(` Retry ${
|
|
5829
|
+
if (attempt2 > 1) {
|
|
5830
|
+
process.stderr.write(` Retry ${attempt2}/${maxAttempts}...
|
|
4448
5831
|
`);
|
|
4449
5832
|
}
|
|
4450
5833
|
const signature = await vault.signBytes({ data: Buffer.from(messageHash), chain: Chain7.Ethereum }, {});
|
|
@@ -4461,7 +5844,7 @@ async function authenticateVault(client, vault, password, maxAttempts = 3) {
|
|
|
4461
5844
|
};
|
|
4462
5845
|
} catch (err) {
|
|
4463
5846
|
lastError = err;
|
|
4464
|
-
if (
|
|
5847
|
+
if (attempt2 < maxAttempts && err.message?.includes("timeout")) {
|
|
4465
5848
|
continue;
|
|
4466
5849
|
}
|
|
4467
5850
|
throw err;
|
|
@@ -4522,6 +5905,18 @@ function padTo32Bytes(buf) {
|
|
|
4522
5905
|
}
|
|
4523
5906
|
|
|
4524
5907
|
// src/agent/client.ts
|
|
5908
|
+
function v1StatusFromType(type) {
|
|
5909
|
+
switch (type) {
|
|
5910
|
+
case "tool-input-start":
|
|
5911
|
+
case "tool-input-available":
|
|
5912
|
+
case "tool-input-delta":
|
|
5913
|
+
return "running";
|
|
5914
|
+
case "tool-output-available":
|
|
5915
|
+
return "done";
|
|
5916
|
+
default:
|
|
5917
|
+
return void 0;
|
|
5918
|
+
}
|
|
5919
|
+
}
|
|
4525
5920
|
function sseErrorToMessage(value) {
|
|
4526
5921
|
if (value == null) return "";
|
|
4527
5922
|
if (typeof value === "string") return value;
|
|
@@ -4625,6 +6020,7 @@ var AgentClient = class {
|
|
|
4625
6020
|
transactions: [],
|
|
4626
6021
|
message: null
|
|
4627
6022
|
};
|
|
6023
|
+
const toolNameByCallId = /* @__PURE__ */ new Map();
|
|
4628
6024
|
const reader = res.body.getReader();
|
|
4629
6025
|
const decoder = new TextDecoder();
|
|
4630
6026
|
let buffer = "";
|
|
@@ -4639,7 +6035,7 @@ var AgentClient = class {
|
|
|
4639
6035
|
currentData += (currentData ? "\n" : "") + stripLeadingSpace(line.slice(5));
|
|
4640
6036
|
} else if (line === "") {
|
|
4641
6037
|
if (currentData) {
|
|
4642
|
-
this.handleSSEEvent(currentEvent || "message", currentData, result, callbacks);
|
|
6038
|
+
this.handleSSEEvent(currentEvent || "message", currentData, result, callbacks, toolNameByCallId);
|
|
4643
6039
|
}
|
|
4644
6040
|
currentEvent = "";
|
|
4645
6041
|
currentData = "";
|
|
@@ -4659,7 +6055,7 @@ var AgentClient = class {
|
|
|
4659
6055
|
if (done) {
|
|
4660
6056
|
if (trailing) processLine(trailing);
|
|
4661
6057
|
if (currentData) {
|
|
4662
|
-
this.handleSSEEvent(currentEvent || "message", currentData, result, callbacks);
|
|
6058
|
+
this.handleSSEEvent(currentEvent || "message", currentData, result, callbacks, toolNameByCallId);
|
|
4663
6059
|
}
|
|
4664
6060
|
break;
|
|
4665
6061
|
}
|
|
@@ -4669,49 +6065,75 @@ var AgentClient = class {
|
|
|
4669
6065
|
}
|
|
4670
6066
|
return result;
|
|
4671
6067
|
}
|
|
4672
|
-
handleSSEEvent(event, data, result, callbacks) {
|
|
6068
|
+
handleSSEEvent(event, data, result, callbacks, toolNameByCallId) {
|
|
4673
6069
|
try {
|
|
4674
6070
|
const parsed = JSON.parse(data);
|
|
4675
|
-
|
|
6071
|
+
const v1Type = typeof parsed?.type === "string" && parsed.type.length > 0 ? parsed.type : null;
|
|
6072
|
+
const routingEvent = v1Type ? this.mapV1EventType(v1Type) : event;
|
|
6073
|
+
const v1Data = typeof parsed?.type === "string" && parsed.type.startsWith("data-") ? parsed.data : null;
|
|
6074
|
+
switch (routingEvent) {
|
|
4676
6075
|
case "text_delta":
|
|
4677
6076
|
if (typeof parsed.delta === "string") {
|
|
4678
6077
|
result.fullText += parsed.delta;
|
|
4679
6078
|
callbacks.onTextDelta?.(parsed.delta);
|
|
4680
6079
|
}
|
|
4681
6080
|
break;
|
|
4682
|
-
case "tool_progress":
|
|
6081
|
+
case "tool_progress": {
|
|
4683
6082
|
if (this.verbose) process.stderr.write(`[SSE:tool_progress] raw: ${data.slice(0, 1e3)}
|
|
4684
6083
|
`);
|
|
4685
|
-
|
|
6084
|
+
const v1Status = v1StatusFromType(v1Type);
|
|
6085
|
+
const status = parsed.status ?? v1Status;
|
|
6086
|
+
const callId = typeof parsed.toolCallId === "string" ? parsed.toolCallId : null;
|
|
6087
|
+
const rawInlineName = parsed.tool ?? parsed.toolName;
|
|
6088
|
+
const inlineName = typeof rawInlineName === "string" && rawInlineName.length > 0 ? rawInlineName : void 0;
|
|
6089
|
+
if (callId && inlineName) {
|
|
6090
|
+
toolNameByCallId.set(callId, inlineName);
|
|
6091
|
+
}
|
|
6092
|
+
const toolName = inlineName ?? (callId ? toolNameByCallId.get(callId) : void 0);
|
|
6093
|
+
const label = typeof parsed.label === "string" ? parsed.label : void 0;
|
|
6094
|
+
if (status && toolName) {
|
|
6095
|
+
callbacks.onToolProgress?.(toolName, status, label);
|
|
6096
|
+
}
|
|
6097
|
+
if (status === "done" && callId) toolNameByCallId.delete(callId);
|
|
4686
6098
|
break;
|
|
4687
|
-
|
|
4688
|
-
|
|
6099
|
+
}
|
|
6100
|
+
case "title": {
|
|
6101
|
+
const title = v1Data?.title ?? parsed.title;
|
|
6102
|
+
if (typeof title === "string") callbacks.onTitle?.(title);
|
|
4689
6103
|
break;
|
|
4690
|
-
|
|
6104
|
+
}
|
|
6105
|
+
case "actions": {
|
|
4691
6106
|
if (this.verbose) process.stderr.write(`[SSE:actions] raw: ${data.slice(0, 1e3)}
|
|
4692
6107
|
`);
|
|
4693
|
-
|
|
4694
|
-
|
|
6108
|
+
const actions = v1Data?.actions ?? parsed.actions ?? [];
|
|
6109
|
+
result.actions.push(...actions);
|
|
6110
|
+
callbacks.onActions?.(actions);
|
|
4695
6111
|
break;
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
6112
|
+
}
|
|
6113
|
+
case "suggestions": {
|
|
6114
|
+
const suggestions = v1Data?.suggestions ?? parsed.suggestions ?? [];
|
|
6115
|
+
result.suggestions.push(...suggestions);
|
|
6116
|
+
callbacks.onSuggestions?.(suggestions);
|
|
4699
6117
|
break;
|
|
6118
|
+
}
|
|
4700
6119
|
case "tx_ready":
|
|
4701
6120
|
if (this.verbose) process.stderr.write(`[SSE:tx_ready] raw: ${data.slice(0, 2e3)}
|
|
4702
6121
|
`);
|
|
4703
6122
|
{
|
|
4704
|
-
const txReady = parsed;
|
|
6123
|
+
const txReady = v1Data ?? parsed;
|
|
4705
6124
|
result.transactions.push(txReady);
|
|
4706
6125
|
callbacks.onTxReady?.(txReady);
|
|
4707
6126
|
}
|
|
4708
6127
|
break;
|
|
4709
|
-
case "message":
|
|
4710
|
-
|
|
6128
|
+
case "message": {
|
|
6129
|
+
const msg = v1Data?.message ?? parsed.message ?? parsed;
|
|
6130
|
+
result.message = msg;
|
|
4711
6131
|
callbacks.onMessage?.(result.message);
|
|
4712
6132
|
break;
|
|
6133
|
+
}
|
|
4713
6134
|
case "error": {
|
|
4714
|
-
const
|
|
6135
|
+
const errorText = typeof parsed.errorText === "string" ? parsed.errorText : parsed.error;
|
|
6136
|
+
const msg = sseErrorToMessage(errorText);
|
|
4715
6137
|
const codeFromBackend = typeof parsed.code === "string" && isAgentErrorCode(parsed.code) ? parsed.code : inferAgentErrorCodeFromMessage(msg);
|
|
4716
6138
|
callbacks.onError?.(msg, codeFromBackend);
|
|
4717
6139
|
break;
|
|
@@ -4728,6 +6150,37 @@ var AgentClient = class {
|
|
|
4728
6150
|
}
|
|
4729
6151
|
}
|
|
4730
6152
|
}
|
|
6153
|
+
// Maps a V1 `type` field to the legacy event bucket used by handleSSEEvent's
|
|
6154
|
+
// switch. Frame-level types (start, text-start, text-end, finish-step) and
|
|
6155
|
+
// non-critical telemetry (data-tokens, data-usage, data-confirmation) route
|
|
6156
|
+
// to 'ignore' which is a no-op.
|
|
6157
|
+
mapV1EventType(type) {
|
|
6158
|
+
switch (type) {
|
|
6159
|
+
case "text-delta":
|
|
6160
|
+
return "text_delta";
|
|
6161
|
+
case "tool-input-start":
|
|
6162
|
+
case "tool-input-available":
|
|
6163
|
+
case "tool-input-delta":
|
|
6164
|
+
case "tool-output-available":
|
|
6165
|
+
return "tool_progress";
|
|
6166
|
+
case "data-title":
|
|
6167
|
+
return "title";
|
|
6168
|
+
case "data-actions":
|
|
6169
|
+
return "actions";
|
|
6170
|
+
case "data-suggestions":
|
|
6171
|
+
return "suggestions";
|
|
6172
|
+
case "data-tx_ready":
|
|
6173
|
+
return "tx_ready";
|
|
6174
|
+
case "data-message":
|
|
6175
|
+
return "message";
|
|
6176
|
+
case "error":
|
|
6177
|
+
return "error";
|
|
6178
|
+
case "finish":
|
|
6179
|
+
return "done";
|
|
6180
|
+
default:
|
|
6181
|
+
return "ignore";
|
|
6182
|
+
}
|
|
6183
|
+
}
|
|
4731
6184
|
// ============================================================================
|
|
4732
6185
|
// Calldata
|
|
4733
6186
|
// ============================================================================
|
|
@@ -4922,9 +6375,9 @@ function getNativeTokenDecimals(chain) {
|
|
|
4922
6375
|
}
|
|
4923
6376
|
|
|
4924
6377
|
// src/agent/executor.ts
|
|
4925
|
-
import { Chain as
|
|
6378
|
+
import { Chain as Chain10, evmCall, fiatCurrencies as fiatCurrencies3, Vultisig as VultisigSdk } from "@vultisig/sdk";
|
|
4926
6379
|
|
|
4927
|
-
// node_modules/viem/_esm/index.js
|
|
6380
|
+
// ../../node_modules/viem/_esm/index.js
|
|
4928
6381
|
init_formatUnits();
|
|
4929
6382
|
|
|
4930
6383
|
// src/core/VaultStateStore.ts
|
|
@@ -5102,7 +6555,10 @@ var AUTO_EXECUTE_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
5102
6555
|
"sign_tx",
|
|
5103
6556
|
"sign_typed_data",
|
|
5104
6557
|
"read_evm_contract",
|
|
5105
|
-
"scan_tx"
|
|
6558
|
+
"scan_tx",
|
|
6559
|
+
"thorchain_pool_info",
|
|
6560
|
+
"thorchain_add_liquidity",
|
|
6561
|
+
"thorchain_remove_liquidity"
|
|
5106
6562
|
]);
|
|
5107
6563
|
var PASSWORD_REQUIRED_ACTIONS = /* @__PURE__ */ new Set(["sign_tx", "sign_typed_data", "build_custom_tx"]);
|
|
5108
6564
|
|
|
@@ -5137,7 +6593,7 @@ var EVM_GAS_RPC = {
|
|
|
5137
6593
|
Hyperliquid: "https://rpc.hyperliquid.xyz/evm",
|
|
5138
6594
|
Sei: "https://evm-rpc.sei-apis.com"
|
|
5139
6595
|
};
|
|
5140
|
-
var AgentExecutor = class {
|
|
6596
|
+
var AgentExecutor = class _AgentExecutor {
|
|
5141
6597
|
vault;
|
|
5142
6598
|
/** Owning SDK (optional); used for address book backed by app storage */
|
|
5143
6599
|
vultisig;
|
|
@@ -5147,6 +6603,7 @@ var AgentExecutor = class {
|
|
|
5147
6603
|
stateStore = null;
|
|
5148
6604
|
/** Held chain lock release functions, keyed by chain name */
|
|
5149
6605
|
chainLockReleases = /* @__PURE__ */ new Map();
|
|
6606
|
+
evmLastBroadcast = /* @__PURE__ */ new Map();
|
|
5150
6607
|
/** Backend client for resolving calldata_id references. */
|
|
5151
6608
|
backendClient = null;
|
|
5152
6609
|
constructor(vault, verbose = false, vaultId, vultisig) {
|
|
@@ -5187,7 +6644,7 @@ var AgentExecutor = class {
|
|
|
5187
6644
|
`);
|
|
5188
6645
|
return false;
|
|
5189
6646
|
}
|
|
5190
|
-
const chain = resolveChainFromTxReady(txReadyData) ||
|
|
6647
|
+
const chain = resolveChainFromTxReady(txReadyData) || Chain10.Ethereum;
|
|
5191
6648
|
this.pendingPayloads.clear();
|
|
5192
6649
|
this.pendingPayloads.set("latest", {
|
|
5193
6650
|
payload: { __serverTx: true, ...txReadyData },
|
|
@@ -5276,6 +6733,12 @@ var AgentExecutor = class {
|
|
|
5276
6733
|
return this.scanTx(params);
|
|
5277
6734
|
case "read_evm_contract":
|
|
5278
6735
|
return this.readEvmContract(params);
|
|
6736
|
+
case "thorchain_pool_info":
|
|
6737
|
+
return this.thorchainPoolInfo(params);
|
|
6738
|
+
case "thorchain_add_liquidity":
|
|
6739
|
+
return this.thorchainAddLiquidity(params);
|
|
6740
|
+
case "thorchain_remove_liquidity":
|
|
6741
|
+
return this.thorchainRemoveLiquidity(params);
|
|
5279
6742
|
default:
|
|
5280
6743
|
throw new Error(
|
|
5281
6744
|
`Action type '${action.type}' is not implemented locally. The backend may handle this action server-side.`
|
|
@@ -5545,6 +7008,227 @@ var AgentExecutor = class {
|
|
|
5545
7008
|
throw err;
|
|
5546
7009
|
}
|
|
5547
7010
|
}
|
|
7011
|
+
// ============================================================================
|
|
7012
|
+
// THORChain LP (RUNE-side asym add / remove via prepareSendTx + memo)
|
|
7013
|
+
// ============================================================================
|
|
7014
|
+
/**
|
|
7015
|
+
* Pool stats from Midgard (no signing). Optional `pool` filters to one row.
|
|
7016
|
+
*/
|
|
7017
|
+
async thorchainPoolInfo(params) {
|
|
7018
|
+
const { assertValidPoolId: assertValidPoolId2, getThorchainPools: getThorchainPools2, thorchainMidgardBaseUrl: thorchainMidgardBaseUrl2 } = await Promise.resolve().then(() => (init_lp(), lp_exports));
|
|
7019
|
+
const statusRaw = params.status;
|
|
7020
|
+
let poolFetchOpts = {};
|
|
7021
|
+
if (statusRaw === null || String(statusRaw).toLowerCase() === "all") {
|
|
7022
|
+
poolFetchOpts = { status: null };
|
|
7023
|
+
} else if (statusRaw !== void 0) {
|
|
7024
|
+
poolFetchOpts = { status: String(statusRaw) };
|
|
7025
|
+
}
|
|
7026
|
+
const pools = await getThorchainPools2(poolFetchOpts);
|
|
7027
|
+
const poolArg = params.pool;
|
|
7028
|
+
if (poolArg) {
|
|
7029
|
+
const normalized = poolArg.trim().toUpperCase();
|
|
7030
|
+
assertValidPoolId2(normalized);
|
|
7031
|
+
const row = pools.find((p) => p.asset.toUpperCase() === normalized);
|
|
7032
|
+
return {
|
|
7033
|
+
found: !!row,
|
|
7034
|
+
pool_id: normalized,
|
|
7035
|
+
summary: row ?? null,
|
|
7036
|
+
midgard: thorchainMidgardBaseUrl2
|
|
7037
|
+
};
|
|
7038
|
+
}
|
|
7039
|
+
const limitRaw = params.limit;
|
|
7040
|
+
const limit = Math.min(500, Math.max(1, typeof limitRaw === "number" ? limitRaw : Number(limitRaw) || 80));
|
|
7041
|
+
return {
|
|
7042
|
+
pools: pools.slice(0, limit),
|
|
7043
|
+
total: pools.length,
|
|
7044
|
+
midgard: thorchainMidgardBaseUrl2
|
|
7045
|
+
};
|
|
7046
|
+
}
|
|
7047
|
+
async thorchainAddLiquidity(params) {
|
|
7048
|
+
const { assertValidPoolId: assertValidPoolId2, buildThorchainLpAddPayload: buildThorchainLpAddPayload2, resolvePairedAddressForLpAdd: resolvePairedAddressForLpAdd2 } = await Promise.resolve().then(() => (init_lp(), lp_exports));
|
|
7049
|
+
const { getThorchainInboundAddress: getThorchainInboundAddress2 } = await Promise.resolve().then(() => (init_getThorchainInboundAddress(), getThorchainInboundAddress_exports));
|
|
7050
|
+
const pool = this.normalizeThorchainPoolId(params.pool, assertValidPoolId2);
|
|
7051
|
+
const amountStr = String(params.amount ?? "").trim();
|
|
7052
|
+
if (!amountStr) {
|
|
7053
|
+
throw new Error('amount is required (RUNE amount as a decimal string, e.g. "0.25")');
|
|
7054
|
+
}
|
|
7055
|
+
if (!/^\d+(\.\d+)?$/.test(amountStr)) {
|
|
7056
|
+
throw new Error('amount must be a positive decimal string (no sign or scientific notation), e.g. "0.25"');
|
|
7057
|
+
}
|
|
7058
|
+
const [, frac = ""] = amountStr.split(".");
|
|
7059
|
+
if (frac.length > 8) {
|
|
7060
|
+
throw new Error("RUNE amount supports at most 8 decimal places");
|
|
7061
|
+
}
|
|
7062
|
+
const amountRuneBaseUnits = parseAmount(amountStr, 8);
|
|
7063
|
+
if (amountRuneBaseUnits <= 0n) {
|
|
7064
|
+
throw new Error("amount must be greater than zero");
|
|
7065
|
+
}
|
|
7066
|
+
let pairedAddress = params.paired_address?.trim() || void 0;
|
|
7067
|
+
const autoPairRaw = params.auto_pair ?? params.autoPair;
|
|
7068
|
+
const autoPairDisabled = autoPairRaw === false || autoPairRaw === 0 || typeof autoPairRaw === "string" && ["false", "0"].includes(autoPairRaw.toLowerCase());
|
|
7069
|
+
const autoPair = !pairedAddress && !autoPairDisabled;
|
|
7070
|
+
if (autoPair) {
|
|
7071
|
+
const vaultAddresses = await this.buildVaultAddressMap();
|
|
7072
|
+
pairedAddress = resolvePairedAddressForLpAdd2({
|
|
7073
|
+
pool,
|
|
7074
|
+
side: "rune",
|
|
7075
|
+
vaultAddresses
|
|
7076
|
+
}) || void 0;
|
|
7077
|
+
}
|
|
7078
|
+
const lpPayload = buildThorchainLpAddPayload2({
|
|
7079
|
+
pool,
|
|
7080
|
+
amountRuneBaseUnits: amountRuneBaseUnits.toString(),
|
|
7081
|
+
...pairedAddress ? { pairedAddress } : {}
|
|
7082
|
+
});
|
|
7083
|
+
const receiver = await this.getThorchainNativeInboundAddress(getThorchainInboundAddress2);
|
|
7084
|
+
const address = await this.vault.address(Chain10.THORChain);
|
|
7085
|
+
const coin = {
|
|
7086
|
+
chain: Chain10.THORChain,
|
|
7087
|
+
address,
|
|
7088
|
+
decimals: 8,
|
|
7089
|
+
ticker: "RUNE"
|
|
7090
|
+
};
|
|
7091
|
+
const payload = await this.vault.prepareSendTx({
|
|
7092
|
+
coin,
|
|
7093
|
+
receiver,
|
|
7094
|
+
amount: BigInt(lpPayload.amount),
|
|
7095
|
+
memo: lpPayload.memo
|
|
7096
|
+
});
|
|
7097
|
+
const messageHashes = await this.vault.extractMessageHashes(payload);
|
|
7098
|
+
this.pendingPayloads.clear();
|
|
7099
|
+
const payloadId = `tc_lp_add_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
7100
|
+
this.pendingPayloads.set(payloadId, {
|
|
7101
|
+
payload,
|
|
7102
|
+
coin,
|
|
7103
|
+
chain: Chain10.THORChain,
|
|
7104
|
+
timestamp: Date.now()
|
|
7105
|
+
});
|
|
7106
|
+
this.pendingPayloads.set("latest", {
|
|
7107
|
+
payload,
|
|
7108
|
+
coin,
|
|
7109
|
+
chain: Chain10.THORChain,
|
|
7110
|
+
timestamp: Date.now()
|
|
7111
|
+
});
|
|
7112
|
+
return {
|
|
7113
|
+
keysign_payload: payloadId,
|
|
7114
|
+
chain: "THORChain",
|
|
7115
|
+
pool: lpPayload.pool,
|
|
7116
|
+
memo: lpPayload.memo,
|
|
7117
|
+
amount_rune: amountStr,
|
|
7118
|
+
paired_address: pairedAddress,
|
|
7119
|
+
inbound_receiver: receiver,
|
|
7120
|
+
sender: address,
|
|
7121
|
+
message_hashes: messageHashes
|
|
7122
|
+
};
|
|
7123
|
+
}
|
|
7124
|
+
async thorchainRemoveLiquidity(params) {
|
|
7125
|
+
const { assertValidPoolId: assertValidPoolId2, buildThorchainLpRemovePayload: buildThorchainLpRemovePayload2 } = await Promise.resolve().then(() => (init_lp(), lp_exports));
|
|
7126
|
+
const { getThorchainInboundAddress: getThorchainInboundAddress2 } = await Promise.resolve().then(() => (init_getThorchainInboundAddress(), getThorchainInboundAddress_exports));
|
|
7127
|
+
const pool = this.normalizeThorchainPoolId(params.pool, assertValidPoolId2);
|
|
7128
|
+
let basisPoints;
|
|
7129
|
+
if (params.basis_points != null) {
|
|
7130
|
+
basisPoints = Number(params.basis_points);
|
|
7131
|
+
} else if (params.basisPoints != null) {
|
|
7132
|
+
basisPoints = Number(params.basisPoints);
|
|
7133
|
+
}
|
|
7134
|
+
if (basisPoints == null && params.withdraw_percent != null) {
|
|
7135
|
+
const pct = Number(params.withdraw_percent);
|
|
7136
|
+
if (Number.isFinite(pct)) {
|
|
7137
|
+
basisPoints = Math.round(pct * 100);
|
|
7138
|
+
}
|
|
7139
|
+
}
|
|
7140
|
+
if (basisPoints == null || !Number.isInteger(basisPoints)) {
|
|
7141
|
+
throw new Error("basis_points (integer 1\u201310000) or withdraw_percent (0.01\u2013100, maps to basis points) is required");
|
|
7142
|
+
}
|
|
7143
|
+
let withdrawToAsset = params.withdraw_to_asset?.trim();
|
|
7144
|
+
if (!withdrawToAsset && params.withdrawToAsset) {
|
|
7145
|
+
withdrawToAsset = String(params.withdrawToAsset).trim();
|
|
7146
|
+
}
|
|
7147
|
+
if (withdrawToAsset) {
|
|
7148
|
+
withdrawToAsset = withdrawToAsset.toUpperCase();
|
|
7149
|
+
}
|
|
7150
|
+
const lpPayload = buildThorchainLpRemovePayload2({
|
|
7151
|
+
pool,
|
|
7152
|
+
basisPoints,
|
|
7153
|
+
...withdrawToAsset ? { withdrawToAsset } : {}
|
|
7154
|
+
});
|
|
7155
|
+
const receiver = await this.getThorchainNativeInboundAddress(getThorchainInboundAddress2);
|
|
7156
|
+
const address = await this.vault.address(Chain10.THORChain);
|
|
7157
|
+
const coin = {
|
|
7158
|
+
chain: Chain10.THORChain,
|
|
7159
|
+
address,
|
|
7160
|
+
decimals: 8,
|
|
7161
|
+
ticker: "RUNE"
|
|
7162
|
+
};
|
|
7163
|
+
const payload = await this.vault.prepareSendTx({
|
|
7164
|
+
coin,
|
|
7165
|
+
receiver,
|
|
7166
|
+
amount: BigInt(lpPayload.amount),
|
|
7167
|
+
memo: lpPayload.memo
|
|
7168
|
+
});
|
|
7169
|
+
const messageHashes = await this.vault.extractMessageHashes(payload);
|
|
7170
|
+
this.pendingPayloads.clear();
|
|
7171
|
+
const payloadId = `tc_lp_rm_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
7172
|
+
this.pendingPayloads.set(payloadId, {
|
|
7173
|
+
payload,
|
|
7174
|
+
coin,
|
|
7175
|
+
chain: Chain10.THORChain,
|
|
7176
|
+
timestamp: Date.now()
|
|
7177
|
+
});
|
|
7178
|
+
this.pendingPayloads.set("latest", {
|
|
7179
|
+
payload,
|
|
7180
|
+
coin,
|
|
7181
|
+
chain: Chain10.THORChain,
|
|
7182
|
+
timestamp: Date.now()
|
|
7183
|
+
});
|
|
7184
|
+
return {
|
|
7185
|
+
keysign_payload: payloadId,
|
|
7186
|
+
chain: "THORChain",
|
|
7187
|
+
pool: lpPayload.pool,
|
|
7188
|
+
memo: lpPayload.memo,
|
|
7189
|
+
basis_points: basisPoints,
|
|
7190
|
+
withdraw_to_asset: withdrawToAsset || void 0,
|
|
7191
|
+
dust_rune_base_units: lpPayload.amount,
|
|
7192
|
+
inbound_receiver: receiver,
|
|
7193
|
+
sender: address,
|
|
7194
|
+
message_hashes: messageHashes
|
|
7195
|
+
};
|
|
7196
|
+
}
|
|
7197
|
+
normalizeThorchainPoolId(raw, assertValidPoolId2) {
|
|
7198
|
+
const pool = String(raw ?? "").trim();
|
|
7199
|
+
if (!pool) {
|
|
7200
|
+
throw new Error('pool is required (e.g. "BTC.BTC")');
|
|
7201
|
+
}
|
|
7202
|
+
const upper = pool.toUpperCase();
|
|
7203
|
+
assertValidPoolId2(upper);
|
|
7204
|
+
return upper;
|
|
7205
|
+
}
|
|
7206
|
+
async buildVaultAddressMap() {
|
|
7207
|
+
const map = {};
|
|
7208
|
+
for (const c of this.vault.chains) {
|
|
7209
|
+
try {
|
|
7210
|
+
map[c] = await this.vault.address(c);
|
|
7211
|
+
} catch {
|
|
7212
|
+
}
|
|
7213
|
+
}
|
|
7214
|
+
return map;
|
|
7215
|
+
}
|
|
7216
|
+
/** THORChain protocol vault for native RUNE deposits (SWAP/LP memos). Matches SDK e2e fixture. */
|
|
7217
|
+
static THORCHAIN_RUNE_DEPOSIT_ADDRESS = "thor1g98cy3n9mmjrpn0sxmn63lztelera37n8n67c0";
|
|
7218
|
+
async getThorchainNativeInboundAddress(getInbound) {
|
|
7219
|
+
const rows = await getInbound();
|
|
7220
|
+
const thor = rows.find((r) => r.chain.toUpperCase() === "THOR");
|
|
7221
|
+
if (thor?.address) {
|
|
7222
|
+
return thor.address;
|
|
7223
|
+
}
|
|
7224
|
+
if (this.verbose) {
|
|
7225
|
+
process.stderr.write(
|
|
7226
|
+
`[thorchain_lp] THOR inbound row missing; using static RUNE deposit vault ${_AgentExecutor.THORCHAIN_RUNE_DEPOSIT_ADDRESS}
|
|
7227
|
+
`
|
|
7228
|
+
);
|
|
7229
|
+
}
|
|
7230
|
+
return _AgentExecutor.THORCHAIN_RUNE_DEPOSIT_ADDRESS;
|
|
7231
|
+
}
|
|
5548
7232
|
async buildTx(params) {
|
|
5549
7233
|
if (params.calldata_id && !params.data && this.backendClient) {
|
|
5550
7234
|
const id = params.calldata_id;
|
|
@@ -5576,7 +7260,7 @@ var AgentExecutor = class {
|
|
|
5576
7260
|
if (!stored) {
|
|
5577
7261
|
throw new Error("Could not stage calldata transaction for signing (invalid or empty tx payload)");
|
|
5578
7262
|
}
|
|
5579
|
-
const chain = resolveChain(params.chain) ||
|
|
7263
|
+
const chain = resolveChain(params.chain) || Chain10.Ethereum;
|
|
5580
7264
|
const address = await this.vault.address(chain);
|
|
5581
7265
|
return {
|
|
5582
7266
|
status: "ready",
|
|
@@ -5693,6 +7377,7 @@ var AgentExecutor = class {
|
|
|
5693
7377
|
keysignPayload: payload,
|
|
5694
7378
|
signature
|
|
5695
7379
|
});
|
|
7380
|
+
this.evmLastBroadcast.set(chain.toString(), Date.now());
|
|
5696
7381
|
try {
|
|
5697
7382
|
this.recordEvmNonceFromPayload(chain, payload, messageHashes.length);
|
|
5698
7383
|
} catch (nonceErr) {
|
|
@@ -5790,6 +7475,7 @@ var AgentExecutor = class {
|
|
|
5790
7475
|
keysignPayload,
|
|
5791
7476
|
signature
|
|
5792
7477
|
});
|
|
7478
|
+
this.evmLastBroadcast.set(chain.toString(), Date.now());
|
|
5793
7479
|
try {
|
|
5794
7480
|
this.recordEvmNonceFromPayload(chain, keysignPayload, messageHashes.length);
|
|
5795
7481
|
} catch (nonceErr) {
|
|
@@ -5948,6 +7634,16 @@ var AgentExecutor = class {
|
|
|
5948
7634
|
const rpcNonce = bs.value.nonce;
|
|
5949
7635
|
const nextNonce = this.stateStore.getNextEvmNonce(chain, rpcNonce);
|
|
5950
7636
|
if (nextNonce !== rpcNonce) {
|
|
7637
|
+
const lastBroadcast = this.evmLastBroadcast.get(chain.toString()) ?? 0;
|
|
7638
|
+
if (Date.now() - lastBroadcast < 15e3) {
|
|
7639
|
+
if (this.verbose)
|
|
7640
|
+
process.stderr.write(
|
|
7641
|
+
`[nonce] Keeping local nonce ${nextNonce} for ${chain} (broadcast ${Date.now() - lastBroadcast}ms ago)
|
|
7642
|
+
`
|
|
7643
|
+
);
|
|
7644
|
+
bs.value.nonce = nextNonce;
|
|
7645
|
+
return;
|
|
7646
|
+
}
|
|
5951
7647
|
const pendingNonce = await this.fetchEvmPendingNonce(chain);
|
|
5952
7648
|
if (pendingNonce !== null && pendingNonce === rpcNonce) {
|
|
5953
7649
|
if (this.verbose)
|
|
@@ -6115,11 +7811,11 @@ var AgentExecutor = class {
|
|
|
6115
7811
|
`);
|
|
6116
7812
|
const chainName = params.chain;
|
|
6117
7813
|
const chainId = domain.chainId;
|
|
6118
|
-
let chain =
|
|
7814
|
+
let chain = Chain10.Ethereum;
|
|
6119
7815
|
if (chainName) {
|
|
6120
|
-
chain = resolveChain(chainName) ||
|
|
7816
|
+
chain = resolveChain(chainName) || Chain10.Ethereum;
|
|
6121
7817
|
} else if (chainId) {
|
|
6122
|
-
chain = resolveChainId(chainId) ||
|
|
7818
|
+
chain = resolveChainId(chainId) || Chain10.Ethereum;
|
|
6123
7819
|
}
|
|
6124
7820
|
const sigResult = await this.vault.signBytes({
|
|
6125
7821
|
data: eip712Hash,
|
|
@@ -6189,7 +7885,7 @@ var AgentExecutor = class {
|
|
|
6189
7885
|
return { tokens };
|
|
6190
7886
|
}
|
|
6191
7887
|
const out = [];
|
|
6192
|
-
for (const c of Object.values(
|
|
7888
|
+
for (const c of Object.values(Chain10)) {
|
|
6193
7889
|
for (const t of VultisigSdk.getKnownTokens(c)) {
|
|
6194
7890
|
if (!tokenMatchesQuery(t)) continue;
|
|
6195
7891
|
out.push(t);
|
|
@@ -6272,11 +7968,11 @@ function abiEncodeParam(type, value) {
|
|
|
6272
7968
|
}
|
|
6273
7969
|
function resolveChain(name) {
|
|
6274
7970
|
if (!name) return null;
|
|
6275
|
-
if (Object.values(
|
|
7971
|
+
if (Object.values(Chain10).includes(name)) {
|
|
6276
7972
|
return name;
|
|
6277
7973
|
}
|
|
6278
7974
|
const lower = name.toLowerCase();
|
|
6279
|
-
for (const [, value] of Object.entries(
|
|
7975
|
+
for (const [, value] of Object.entries(Chain10)) {
|
|
6280
7976
|
if (typeof value === "string" && value.toLowerCase() === lower) {
|
|
6281
7977
|
return value;
|
|
6282
7978
|
}
|
|
@@ -6302,7 +7998,7 @@ function resolveChain(name) {
|
|
|
6302
7998
|
xrp: "Ripple"
|
|
6303
7999
|
};
|
|
6304
8000
|
const aliased = aliases[lower];
|
|
6305
|
-
if (aliased && Object.values(
|
|
8001
|
+
if (aliased && Object.values(Chain10).includes(aliased)) {
|
|
6306
8002
|
return aliased;
|
|
6307
8003
|
}
|
|
6308
8004
|
return null;
|
|
@@ -6336,16 +8032,16 @@ function resolveChainId(chainId) {
|
|
|
6336
8032
|
const id = typeof chainId === "string" ? parseInt(chainId, 10) : chainId;
|
|
6337
8033
|
if (isNaN(id)) return null;
|
|
6338
8034
|
const chainIdMap = {
|
|
6339
|
-
1:
|
|
6340
|
-
56:
|
|
6341
|
-
137:
|
|
6342
|
-
43114:
|
|
6343
|
-
42161:
|
|
6344
|
-
10:
|
|
6345
|
-
8453:
|
|
6346
|
-
81457:
|
|
6347
|
-
324:
|
|
6348
|
-
25:
|
|
8035
|
+
1: Chain10.Ethereum,
|
|
8036
|
+
56: Chain10.BSC,
|
|
8037
|
+
137: Chain10.Polygon,
|
|
8038
|
+
43114: Chain10.Avalanche,
|
|
8039
|
+
42161: Chain10.Arbitrum,
|
|
8040
|
+
10: Chain10.Optimism,
|
|
8041
|
+
8453: Chain10.Base,
|
|
8042
|
+
81457: Chain10.Blast,
|
|
8043
|
+
324: Chain10.Zksync,
|
|
8044
|
+
25: Chain10.CronosChain
|
|
6349
8045
|
};
|
|
6350
8046
|
return chainIdMap[id] || null;
|
|
6351
8047
|
}
|
|
@@ -7524,15 +9220,13 @@ async function executeAgentAsk(ctx2, message, options) {
|
|
|
7524
9220
|
const callbacks = ask.getCallbacks();
|
|
7525
9221
|
await session.initialize(callbacks);
|
|
7526
9222
|
const result = await ask.ask(message);
|
|
7527
|
-
if (options.json) {
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
}) + "\n"
|
|
7535
|
-
);
|
|
9223
|
+
if (options.json || isJsonOutput()) {
|
|
9224
|
+
outputJson({
|
|
9225
|
+
session_id: result.sessionId,
|
|
9226
|
+
response: result.response,
|
|
9227
|
+
tool_calls: result.toolCalls,
|
|
9228
|
+
transactions: result.transactions
|
|
9229
|
+
});
|
|
7536
9230
|
} else {
|
|
7537
9231
|
process.stdout.write(`session:${result.sessionId}
|
|
7538
9232
|
`);
|
|
@@ -7598,46 +9292,294 @@ async function executeAgentSessionsList(ctx2, options) {
|
|
|
7598
9292
|
printResult("No sessions found.");
|
|
7599
9293
|
return;
|
|
7600
9294
|
}
|
|
7601
|
-
const table = new Table({
|
|
7602
|
-
head: [chalk9.cyan("ID"), chalk9.cyan("Title"), chalk9.cyan("Created"), chalk9.cyan("Updated")]
|
|
7603
|
-
});
|
|
7604
|
-
for (const conv of allConversations) {
|
|
7605
|
-
table.push([
|
|
7606
|
-
conv.id,
|
|
7607
|
-
conv.title || chalk9.gray("(untitled)"),
|
|
7608
|
-
formatDate(conv.created_at),
|
|
7609
|
-
formatDate(conv.updated_at)
|
|
7610
|
-
]);
|
|
9295
|
+
const table = new Table({
|
|
9296
|
+
head: [chalk9.cyan("ID"), chalk9.cyan("Title"), chalk9.cyan("Created"), chalk9.cyan("Updated")]
|
|
9297
|
+
});
|
|
9298
|
+
for (const conv of allConversations) {
|
|
9299
|
+
table.push([
|
|
9300
|
+
conv.id,
|
|
9301
|
+
conv.title || chalk9.gray("(untitled)"),
|
|
9302
|
+
formatDate(conv.created_at),
|
|
9303
|
+
formatDate(conv.updated_at)
|
|
9304
|
+
]);
|
|
9305
|
+
}
|
|
9306
|
+
printResult(table.toString());
|
|
9307
|
+
printResult(chalk9.gray(`
|
|
9308
|
+
${totalCount} session(s) total`));
|
|
9309
|
+
}
|
|
9310
|
+
async function executeAgentSessionsDelete(ctx2, sessionId, options) {
|
|
9311
|
+
const vault = await ctx2.ensureActiveVault();
|
|
9312
|
+
const backendUrl = options.backendUrl || process.env.VULTISIG_AGENT_URL || "https://abe.vultisig.com";
|
|
9313
|
+
const client = await createAuthenticatedClient(backendUrl, vault, options.password);
|
|
9314
|
+
const publicKey = vault.publicKeys.ecdsa;
|
|
9315
|
+
await client.deleteConversation(sessionId, publicKey);
|
|
9316
|
+
if (isJsonOutput()) {
|
|
9317
|
+
outputJson({ deleted: sessionId });
|
|
9318
|
+
return;
|
|
9319
|
+
}
|
|
9320
|
+
printResult(chalk9.green(`Session ${sessionId} deleted.`));
|
|
9321
|
+
}
|
|
9322
|
+
async function createAuthenticatedClient(backendUrl, vault, password) {
|
|
9323
|
+
const client = new AgentClient(backendUrl);
|
|
9324
|
+
const auth = await authenticateVault(client, vault, password);
|
|
9325
|
+
client.setAuthToken(auth.token);
|
|
9326
|
+
return client;
|
|
9327
|
+
}
|
|
9328
|
+
function formatDate(iso) {
|
|
9329
|
+
try {
|
|
9330
|
+
const d = new Date(iso);
|
|
9331
|
+
return d.toLocaleDateString() + " " + d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
9332
|
+
} catch {
|
|
9333
|
+
return iso;
|
|
9334
|
+
}
|
|
9335
|
+
}
|
|
9336
|
+
|
|
9337
|
+
// src/lib/version.ts
|
|
9338
|
+
import chalk10 from "chalk";
|
|
9339
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync2 } from "fs";
|
|
9340
|
+
import { homedir as homedir3 } from "os";
|
|
9341
|
+
import { join as join3 } from "path";
|
|
9342
|
+
var cachedVersion = null;
|
|
9343
|
+
function getVersion() {
|
|
9344
|
+
if (cachedVersion) return cachedVersion;
|
|
9345
|
+
if (true) {
|
|
9346
|
+
cachedVersion = "0.17.0";
|
|
9347
|
+
return cachedVersion;
|
|
9348
|
+
}
|
|
9349
|
+
try {
|
|
9350
|
+
const packagePath = new URL("../../package.json", import.meta.url);
|
|
9351
|
+
const pkg = JSON.parse(readFileSync3(packagePath, "utf-8"));
|
|
9352
|
+
cachedVersion = pkg.version;
|
|
9353
|
+
return cachedVersion;
|
|
9354
|
+
} catch {
|
|
9355
|
+
cachedVersion = "0.0.0-unknown";
|
|
9356
|
+
return cachedVersion;
|
|
9357
|
+
}
|
|
9358
|
+
}
|
|
9359
|
+
var CACHE_DIR = join3(homedir3(), ".vultisig", "cache");
|
|
9360
|
+
var VERSION_CACHE_FILE = join3(CACHE_DIR, "version-check.json");
|
|
9361
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
9362
|
+
function readVersionCache() {
|
|
9363
|
+
try {
|
|
9364
|
+
if (!existsSync2(VERSION_CACHE_FILE)) return null;
|
|
9365
|
+
const data = readFileSync3(VERSION_CACHE_FILE, "utf-8");
|
|
9366
|
+
return JSON.parse(data);
|
|
9367
|
+
} catch {
|
|
9368
|
+
return null;
|
|
9369
|
+
}
|
|
9370
|
+
}
|
|
9371
|
+
function writeVersionCache(cache) {
|
|
9372
|
+
try {
|
|
9373
|
+
if (!existsSync2(CACHE_DIR)) {
|
|
9374
|
+
mkdirSync3(CACHE_DIR, { recursive: true });
|
|
9375
|
+
}
|
|
9376
|
+
writeFileSync3(VERSION_CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
9377
|
+
} catch {
|
|
9378
|
+
}
|
|
9379
|
+
}
|
|
9380
|
+
async function fetchLatestVersion() {
|
|
9381
|
+
try {
|
|
9382
|
+
const controller = new AbortController();
|
|
9383
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
9384
|
+
const response = await fetch("https://registry.npmjs.org/@vultisig/cli/latest", {
|
|
9385
|
+
signal: controller.signal,
|
|
9386
|
+
headers: {
|
|
9387
|
+
Accept: "application/json"
|
|
9388
|
+
}
|
|
9389
|
+
});
|
|
9390
|
+
clearTimeout(timeout);
|
|
9391
|
+
if (!response.ok) return null;
|
|
9392
|
+
const data = await response.json();
|
|
9393
|
+
return data.version ?? null;
|
|
9394
|
+
} catch {
|
|
9395
|
+
return null;
|
|
9396
|
+
}
|
|
9397
|
+
}
|
|
9398
|
+
function isNewerVersion(v1, v2) {
|
|
9399
|
+
const parse = (v) => {
|
|
9400
|
+
const clean2 = v.replace(/^v/, "").replace(/-.*$/, "");
|
|
9401
|
+
return clean2.split(".").map((n) => parseInt(n, 10) || 0);
|
|
9402
|
+
};
|
|
9403
|
+
const p1 = parse(v1);
|
|
9404
|
+
const p2 = parse(v2);
|
|
9405
|
+
for (let i = 0; i < 3; i++) {
|
|
9406
|
+
const n1 = p1[i] ?? 0;
|
|
9407
|
+
const n2 = p2[i] ?? 0;
|
|
9408
|
+
if (n2 > n1) return true;
|
|
9409
|
+
if (n2 < n1) return false;
|
|
9410
|
+
}
|
|
9411
|
+
if (v1.includes("-") && !v2.includes("-")) return true;
|
|
9412
|
+
return false;
|
|
9413
|
+
}
|
|
9414
|
+
async function checkForUpdates() {
|
|
9415
|
+
if (process.env.VULTISIG_NO_UPDATE_CHECK === "1") {
|
|
9416
|
+
return null;
|
|
9417
|
+
}
|
|
9418
|
+
const currentVersion = getVersion();
|
|
9419
|
+
const cache = readVersionCache();
|
|
9420
|
+
if (cache && Date.now() - cache.lastCheck < CACHE_TTL_MS) {
|
|
9421
|
+
return {
|
|
9422
|
+
currentVersion,
|
|
9423
|
+
latestVersion: cache.latestVersion,
|
|
9424
|
+
updateAvailable: cache.latestVersion ? isNewerVersion(currentVersion, cache.latestVersion) : false
|
|
9425
|
+
};
|
|
9426
|
+
}
|
|
9427
|
+
const latestVersion = await fetchLatestVersion();
|
|
9428
|
+
writeVersionCache({
|
|
9429
|
+
lastCheck: Date.now(),
|
|
9430
|
+
latestVersion
|
|
9431
|
+
});
|
|
9432
|
+
return {
|
|
9433
|
+
currentVersion,
|
|
9434
|
+
latestVersion,
|
|
9435
|
+
updateAvailable: latestVersion ? isNewerVersion(currentVersion, latestVersion) : false
|
|
9436
|
+
};
|
|
9437
|
+
}
|
|
9438
|
+
function formatVersionShort() {
|
|
9439
|
+
return `vultisig/${getVersion()}`;
|
|
9440
|
+
}
|
|
9441
|
+
function formatVersionDetailed() {
|
|
9442
|
+
const lines = [];
|
|
9443
|
+
lines.push(chalk10.bold(`Vultisig CLI v${getVersion()}`));
|
|
9444
|
+
lines.push("");
|
|
9445
|
+
lines.push(` Node.js: ${process.version}`);
|
|
9446
|
+
lines.push(` Platform: ${process.platform}-${process.arch}`);
|
|
9447
|
+
lines.push(` Config: ~/.vultisig/`);
|
|
9448
|
+
return lines.join("\n");
|
|
9449
|
+
}
|
|
9450
|
+
function detectInstallMethod() {
|
|
9451
|
+
const execPath = process.execPath;
|
|
9452
|
+
if (execPath.includes("homebrew") || execPath.includes("Cellar")) {
|
|
9453
|
+
return "homebrew";
|
|
9454
|
+
}
|
|
9455
|
+
if (process.env.npm_execpath?.includes("yarn")) {
|
|
9456
|
+
return "yarn";
|
|
9457
|
+
}
|
|
9458
|
+
if (process.env.npm_config_user_agent?.includes("npm")) {
|
|
9459
|
+
return "npm";
|
|
9460
|
+
}
|
|
9461
|
+
if (execPath.includes("node_modules")) {
|
|
9462
|
+
return "npm";
|
|
9463
|
+
}
|
|
9464
|
+
return "unknown";
|
|
9465
|
+
}
|
|
9466
|
+
function getUpdateCommand() {
|
|
9467
|
+
const method = detectInstallMethod();
|
|
9468
|
+
switch (method) {
|
|
9469
|
+
case "npm":
|
|
9470
|
+
return "npm update -g @vultisig/cli";
|
|
9471
|
+
case "yarn":
|
|
9472
|
+
return "yarn global upgrade @vultisig/cli";
|
|
9473
|
+
case "homebrew":
|
|
9474
|
+
return "brew upgrade vultisig";
|
|
9475
|
+
default:
|
|
9476
|
+
return "npm update -g @vultisig/cli";
|
|
7611
9477
|
}
|
|
7612
|
-
printResult(table.toString());
|
|
7613
|
-
printResult(chalk9.gray(`
|
|
7614
|
-
${totalCount} session(s) total`));
|
|
7615
9478
|
}
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
9479
|
+
|
|
9480
|
+
// src/commands/schema.ts
|
|
9481
|
+
var COMMAND_EXAMPLES = {
|
|
9482
|
+
balance: {
|
|
9483
|
+
examples: [
|
|
9484
|
+
"vultisig balance",
|
|
9485
|
+
"vultisig balance Ethereum --tokens",
|
|
9486
|
+
"vultisig balance --output json --fields amount,symbol"
|
|
9487
|
+
]
|
|
9488
|
+
},
|
|
9489
|
+
addresses: {
|
|
9490
|
+
examples: ["vultisig addresses", "vultisig addresses --output json"]
|
|
9491
|
+
},
|
|
9492
|
+
send: {
|
|
9493
|
+
examples: [
|
|
9494
|
+
"vultisig send Ethereum 0x... 0.1",
|
|
9495
|
+
"vultisig send Bitcoin bc1q... --max --yes",
|
|
9496
|
+
"vultisig send Ethereum 0x... 0.5 --dry-run --output json"
|
|
9497
|
+
]
|
|
9498
|
+
},
|
|
9499
|
+
execute: {
|
|
9500
|
+
examples: [`vultisig execute THORChain <contract> '{"swap":{}}'`]
|
|
9501
|
+
},
|
|
9502
|
+
"swap-quote": {
|
|
9503
|
+
examples: ["vultisig swap-quote Ethereum Bitcoin 0.1 --output json"]
|
|
9504
|
+
},
|
|
9505
|
+
vaults: {
|
|
9506
|
+
examples: ["vultisig vaults", "vultisig vaults --output json"]
|
|
9507
|
+
},
|
|
9508
|
+
chains: {
|
|
9509
|
+
examples: ["vultisig chains", "vultisig chains --add Solana"]
|
|
9510
|
+
},
|
|
9511
|
+
import: {
|
|
9512
|
+
examples: ["vultisig import ~/vault.vult", "vultisig import ~/vault.vult --password secret"]
|
|
9513
|
+
},
|
|
9514
|
+
export: {
|
|
9515
|
+
examples: ["vultisig export ~/backup.vult"]
|
|
9516
|
+
},
|
|
9517
|
+
"create.fast": {
|
|
9518
|
+
examples: ["vultisig create fast --name mywallet --password secret --email me@example.com"]
|
|
9519
|
+
},
|
|
9520
|
+
"agent.ask": {
|
|
9521
|
+
examples: [
|
|
9522
|
+
'vultisig agent ask "What is my ETH balance?" --output json',
|
|
9523
|
+
'vultisig agent ask "Send 0.1 ETH to 0x..." --session abc123'
|
|
9524
|
+
]
|
|
7625
9525
|
}
|
|
7626
|
-
|
|
9526
|
+
};
|
|
9527
|
+
var GLOBAL_ENUM_VALUES = {
|
|
9528
|
+
"--output": ["json", "table"]
|
|
9529
|
+
};
|
|
9530
|
+
function mapOption(o, enumValues) {
|
|
9531
|
+
return {
|
|
9532
|
+
flags: o.flags,
|
|
9533
|
+
description: o.description,
|
|
9534
|
+
required: !!o.mandatory,
|
|
9535
|
+
defaultValue: o.defaultValue,
|
|
9536
|
+
...enumValues?.[o.long] ? { enumValues: enumValues[o.long] } : {}
|
|
9537
|
+
};
|
|
7627
9538
|
}
|
|
7628
|
-
|
|
7629
|
-
const
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
9539
|
+
function mapArguments(cmd) {
|
|
9540
|
+
const args = cmd.registeredArguments;
|
|
9541
|
+
if (!args?.length) return void 0;
|
|
9542
|
+
return args.map((a) => ({
|
|
9543
|
+
name: a._name ?? a.name?.(),
|
|
9544
|
+
required: a.required,
|
|
9545
|
+
description: a.description
|
|
9546
|
+
}));
|
|
7633
9547
|
}
|
|
7634
|
-
function
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
9548
|
+
function executeSchema(prog) {
|
|
9549
|
+
const schema = {
|
|
9550
|
+
name: prog.name(),
|
|
9551
|
+
version: getVersion(),
|
|
9552
|
+
exitCodes: Object.fromEntries(Object.entries(EXIT_CODE_DESCRIPTIONS).map(([k, v]) => [String(k), v])),
|
|
9553
|
+
globalOptions: prog.options.filter((o) => !o.hidden).map((o) => mapOption(o, GLOBAL_ENUM_VALUES)),
|
|
9554
|
+
commands: prog.commands.filter((c) => !c._hidden).map((c) => {
|
|
9555
|
+
const meta = COMMAND_EXAMPLES[c.name()];
|
|
9556
|
+
const args = mapArguments(c);
|
|
9557
|
+
return {
|
|
9558
|
+
name: c.name(),
|
|
9559
|
+
description: c.description(),
|
|
9560
|
+
...args ? { arguments: args } : {},
|
|
9561
|
+
options: c.options.filter((o) => !o.hidden && o.long !== "--help").map((o) => mapOption(o, meta?.enumValues)),
|
|
9562
|
+
...meta?.examples ? { examples: meta.examples } : {},
|
|
9563
|
+
subcommands: c.commands.length ? c.commands.map((sub) => {
|
|
9564
|
+
const subMeta = COMMAND_EXAMPLES[`${c.name()}.${sub.name()}`];
|
|
9565
|
+
const subArgs = mapArguments(sub);
|
|
9566
|
+
return {
|
|
9567
|
+
name: sub.name(),
|
|
9568
|
+
description: sub.description(),
|
|
9569
|
+
...subArgs ? { arguments: subArgs } : {},
|
|
9570
|
+
options: sub.options.filter((o) => !o.hidden && o.long !== "--help").map((o) => ({
|
|
9571
|
+
flags: o.flags,
|
|
9572
|
+
description: o.description,
|
|
9573
|
+
required: !!o.mandatory
|
|
9574
|
+
})),
|
|
9575
|
+
...subMeta?.examples ? { examples: subMeta.examples } : {}
|
|
9576
|
+
};
|
|
9577
|
+
}) : void 0
|
|
9578
|
+
};
|
|
9579
|
+
})
|
|
9580
|
+
};
|
|
9581
|
+
process.stdout.write(`${JSON.stringify(schema, null, 2)}
|
|
9582
|
+
`);
|
|
7641
9583
|
}
|
|
7642
9584
|
|
|
7643
9585
|
// src/core/server-endpoints.ts
|
|
@@ -7684,7 +9626,7 @@ function readArgValue(args, optionName) {
|
|
|
7684
9626
|
}
|
|
7685
9627
|
|
|
7686
9628
|
// src/interactive/completer.ts
|
|
7687
|
-
import { Chain as
|
|
9629
|
+
import { Chain as Chain11 } from "@vultisig/sdk";
|
|
7688
9630
|
import fs3 from "fs";
|
|
7689
9631
|
import path3 from "path";
|
|
7690
9632
|
var COMMANDS = [
|
|
@@ -7828,7 +9770,7 @@ function completeVaultName(ctx2, partial) {
|
|
|
7828
9770
|
return [show, partial];
|
|
7829
9771
|
}
|
|
7830
9772
|
function completeChainName(partial) {
|
|
7831
|
-
const allChains = Object.values(
|
|
9773
|
+
const allChains = Object.values(Chain11);
|
|
7832
9774
|
const partialLower = partial.toLowerCase();
|
|
7833
9775
|
const matches = allChains.filter((chain) => chain.toLowerCase().startsWith(partialLower));
|
|
7834
9776
|
matches.sort();
|
|
@@ -7836,14 +9778,14 @@ function completeChainName(partial) {
|
|
|
7836
9778
|
return [show, partial];
|
|
7837
9779
|
}
|
|
7838
9780
|
function findChainByName(name) {
|
|
7839
|
-
const allChains = Object.values(
|
|
9781
|
+
const allChains = Object.values(Chain11);
|
|
7840
9782
|
const nameLower = name.toLowerCase();
|
|
7841
9783
|
const found = allChains.find((chain) => chain.toLowerCase() === nameLower);
|
|
7842
9784
|
return found ? found : null;
|
|
7843
9785
|
}
|
|
7844
9786
|
|
|
7845
9787
|
// src/interactive/event-buffer.ts
|
|
7846
|
-
import
|
|
9788
|
+
import chalk11 from "chalk";
|
|
7847
9789
|
var EventBuffer = class {
|
|
7848
9790
|
eventBuffer = [];
|
|
7849
9791
|
isCommandRunning = false;
|
|
@@ -7883,17 +9825,17 @@ var EventBuffer = class {
|
|
|
7883
9825
|
displayEvent(message, type) {
|
|
7884
9826
|
switch (type) {
|
|
7885
9827
|
case "success":
|
|
7886
|
-
console.log(
|
|
9828
|
+
console.log(chalk11.green(message));
|
|
7887
9829
|
break;
|
|
7888
9830
|
case "warning":
|
|
7889
|
-
console.log(
|
|
9831
|
+
console.log(chalk11.yellow(message));
|
|
7890
9832
|
break;
|
|
7891
9833
|
case "error":
|
|
7892
|
-
console.error(
|
|
9834
|
+
console.error(chalk11.red(message));
|
|
7893
9835
|
break;
|
|
7894
9836
|
case "info":
|
|
7895
9837
|
default:
|
|
7896
|
-
console.log(
|
|
9838
|
+
console.log(chalk11.blue(message));
|
|
7897
9839
|
break;
|
|
7898
9840
|
}
|
|
7899
9841
|
}
|
|
@@ -7904,13 +9846,13 @@ var EventBuffer = class {
|
|
|
7904
9846
|
if (this.eventBuffer.length === 0) {
|
|
7905
9847
|
return;
|
|
7906
9848
|
}
|
|
7907
|
-
console.log(
|
|
9849
|
+
console.log(chalk11.gray("\n--- Background Events ---"));
|
|
7908
9850
|
this.eventBuffer.forEach((event) => {
|
|
7909
9851
|
const timeStr = event.timestamp.toLocaleTimeString();
|
|
7910
9852
|
const message = `[${timeStr}] ${event.message}`;
|
|
7911
9853
|
this.displayEvent(message, event.type);
|
|
7912
9854
|
});
|
|
7913
|
-
console.log(
|
|
9855
|
+
console.log(chalk11.gray("--- End Events ---\n"));
|
|
7914
9856
|
}
|
|
7915
9857
|
/**
|
|
7916
9858
|
* Setup all vault event listeners
|
|
@@ -8020,12 +9962,12 @@ var EventBuffer = class {
|
|
|
8020
9962
|
|
|
8021
9963
|
// src/interactive/session.ts
|
|
8022
9964
|
import { fiatCurrencies as fiatCurrencies4 } from "@vultisig/sdk";
|
|
8023
|
-
import
|
|
9965
|
+
import chalk13 from "chalk";
|
|
8024
9966
|
import ora3 from "ora";
|
|
8025
9967
|
import * as readline3 from "readline";
|
|
8026
9968
|
|
|
8027
9969
|
// src/interactive/shell-commands.ts
|
|
8028
|
-
import
|
|
9970
|
+
import chalk12 from "chalk";
|
|
8029
9971
|
import Table2 from "cli-table3";
|
|
8030
9972
|
import inquirer6 from "inquirer";
|
|
8031
9973
|
import ora2 from "ora";
|
|
@@ -8042,25 +9984,25 @@ function formatTimeRemaining(ms) {
|
|
|
8042
9984
|
async function executeLock(ctx2) {
|
|
8043
9985
|
const vault = ctx2.getActiveVault();
|
|
8044
9986
|
if (!vault) {
|
|
8045
|
-
console.log(
|
|
8046
|
-
console.log(
|
|
9987
|
+
console.log(chalk12.red("No active vault."));
|
|
9988
|
+
console.log(chalk12.yellow('Use "vault <name>" to switch to a vault first.'));
|
|
8047
9989
|
return;
|
|
8048
9990
|
}
|
|
8049
9991
|
ctx2.lockVault(vault.id);
|
|
8050
|
-
console.log(
|
|
8051
|
-
console.log(
|
|
9992
|
+
console.log(chalk12.green("\n+ Vault locked"));
|
|
9993
|
+
console.log(chalk12.gray("Password cache cleared. You will need to enter the password again."));
|
|
8052
9994
|
}
|
|
8053
9995
|
async function executeUnlock(ctx2) {
|
|
8054
9996
|
const vault = ctx2.getActiveVault();
|
|
8055
9997
|
if (!vault) {
|
|
8056
|
-
console.log(
|
|
8057
|
-
console.log(
|
|
9998
|
+
console.log(chalk12.red("No active vault."));
|
|
9999
|
+
console.log(chalk12.yellow('Use "vault <name>" to switch to a vault first.'));
|
|
8058
10000
|
return;
|
|
8059
10001
|
}
|
|
8060
10002
|
if (ctx2.isVaultUnlocked(vault.id)) {
|
|
8061
10003
|
const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
|
|
8062
|
-
console.log(
|
|
8063
|
-
console.log(
|
|
10004
|
+
console.log(chalk12.yellow("\nVault is already unlocked."));
|
|
10005
|
+
console.log(chalk12.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
|
|
8064
10006
|
return;
|
|
8065
10007
|
}
|
|
8066
10008
|
const { password } = await inquirer6.prompt([
|
|
@@ -8077,19 +10019,19 @@ async function executeUnlock(ctx2) {
|
|
|
8077
10019
|
ctx2.cachePassword(vault.id, password);
|
|
8078
10020
|
const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
|
|
8079
10021
|
spinner.succeed("Vault unlocked");
|
|
8080
|
-
console.log(
|
|
10022
|
+
console.log(chalk12.green(`
|
|
8081
10023
|
+ Vault unlocked for ${formatTimeRemaining(timeRemaining)}`));
|
|
8082
10024
|
} catch (err) {
|
|
8083
10025
|
spinner.fail("Failed to unlock vault");
|
|
8084
|
-
console.error(
|
|
10026
|
+
console.error(chalk12.red(`
|
|
8085
10027
|
x ${err.message}`));
|
|
8086
10028
|
}
|
|
8087
10029
|
}
|
|
8088
10030
|
async function executeStatus(ctx2) {
|
|
8089
10031
|
const vault = ctx2.getActiveVault();
|
|
8090
10032
|
if (!vault) {
|
|
8091
|
-
console.log(
|
|
8092
|
-
console.log(
|
|
10033
|
+
console.log(chalk12.red("No active vault."));
|
|
10034
|
+
console.log(chalk12.yellow('Use "vault <name>" to switch to a vault first.'));
|
|
8093
10035
|
return;
|
|
8094
10036
|
}
|
|
8095
10037
|
const isUnlocked = ctx2.isVaultUnlocked(vault.id);
|
|
@@ -8120,30 +10062,30 @@ async function executeStatus(ctx2) {
|
|
|
8120
10062
|
displayStatus(status);
|
|
8121
10063
|
}
|
|
8122
10064
|
function displayStatus(status) {
|
|
8123
|
-
console.log(
|
|
8124
|
-
console.log(
|
|
8125
|
-
console.log(
|
|
8126
|
-
console.log(
|
|
8127
|
-
console.log(` Name: ${
|
|
10065
|
+
console.log(chalk12.cyan("\n+----------------------------------------+"));
|
|
10066
|
+
console.log(chalk12.cyan("| Vault Status |"));
|
|
10067
|
+
console.log(chalk12.cyan("+----------------------------------------+\n"));
|
|
10068
|
+
console.log(chalk12.bold("Vault:"));
|
|
10069
|
+
console.log(` Name: ${chalk12.green(status.name)}`);
|
|
8128
10070
|
console.log(` ID: ${status.id}`);
|
|
8129
|
-
console.log(` Type: ${
|
|
8130
|
-
console.log(
|
|
10071
|
+
console.log(` Type: ${chalk12.yellow(status.type)}`);
|
|
10072
|
+
console.log(chalk12.bold("\nSecurity:"));
|
|
8131
10073
|
if (status.isUnlocked) {
|
|
8132
|
-
console.log(` Status: ${
|
|
10074
|
+
console.log(` Status: ${chalk12.green("Unlocked")} ${chalk12.green("\u{1F513}")}`);
|
|
8133
10075
|
console.log(` Expires: ${status.timeRemainingFormatted}`);
|
|
8134
10076
|
} else {
|
|
8135
|
-
console.log(` Status: ${
|
|
10077
|
+
console.log(` Status: ${chalk12.yellow("Locked")} ${chalk12.yellow("\u{1F512}")}`);
|
|
8136
10078
|
}
|
|
8137
|
-
console.log(` Encrypted: ${status.isEncrypted ?
|
|
8138
|
-
console.log(` Backed Up: ${status.isBackedUp ?
|
|
8139
|
-
console.log(
|
|
10079
|
+
console.log(` Encrypted: ${status.isEncrypted ? chalk12.green("Yes") : chalk12.gray("No")}`);
|
|
10080
|
+
console.log(` Backed Up: ${status.isBackedUp ? chalk12.green("Yes") : chalk12.yellow("No")}`);
|
|
10081
|
+
console.log(chalk12.bold("\nMPC Configuration:"));
|
|
8140
10082
|
console.log(` Library: ${status.libType}`);
|
|
8141
|
-
console.log(` Threshold: ${
|
|
8142
|
-
console.log(
|
|
10083
|
+
console.log(` Threshold: ${chalk12.cyan(status.threshold)} of ${chalk12.cyan(status.totalSigners)}`);
|
|
10084
|
+
console.log(chalk12.bold("\nSigning Modes:"));
|
|
8143
10085
|
status.availableSigningModes.forEach((mode) => {
|
|
8144
10086
|
console.log(` - ${mode}`);
|
|
8145
10087
|
});
|
|
8146
|
-
console.log(
|
|
10088
|
+
console.log(chalk12.bold("\nDetails:"));
|
|
8147
10089
|
console.log(` Chains: ${status.chains}`);
|
|
8148
10090
|
console.log(` Currency: ${status.currency.toUpperCase()}`);
|
|
8149
10091
|
console.log(` Created: ${new Date(status.createdAt).toLocaleString()}`);
|
|
@@ -8152,7 +10094,7 @@ function displayStatus(status) {
|
|
|
8152
10094
|
}
|
|
8153
10095
|
function showHelp() {
|
|
8154
10096
|
const table = new Table2({
|
|
8155
|
-
head: [
|
|
10097
|
+
head: [chalk12.bold("Available Commands")],
|
|
8156
10098
|
colWidths: [50],
|
|
8157
10099
|
chars: {
|
|
8158
10100
|
mid: "",
|
|
@@ -8166,7 +10108,7 @@ function showHelp() {
|
|
|
8166
10108
|
}
|
|
8167
10109
|
});
|
|
8168
10110
|
table.push(
|
|
8169
|
-
[
|
|
10111
|
+
[chalk12.bold("Vault Management:")],
|
|
8170
10112
|
[" vaults - List all vaults"],
|
|
8171
10113
|
[" vault <name> - Switch to vault"],
|
|
8172
10114
|
[" import <file> - Import vault from file"],
|
|
@@ -8175,7 +10117,7 @@ function showHelp() {
|
|
|
8175
10117
|
[" info - Show vault details"],
|
|
8176
10118
|
[" export [path] - Export vault to file"],
|
|
8177
10119
|
[""],
|
|
8178
|
-
[
|
|
10120
|
+
[chalk12.bold("Wallet Operations:")],
|
|
8179
10121
|
[" balance [chain] - Show balances"],
|
|
8180
10122
|
[" send <chain> <to> <amount> - Send transaction"],
|
|
8181
10123
|
[" tx-status <chain> <txHash> - Check transaction status"],
|
|
@@ -8184,22 +10126,22 @@ function showHelp() {
|
|
|
8184
10126
|
[" chains [--add/--remove/--add-all] - Manage chains"],
|
|
8185
10127
|
[" tokens <chain> - Manage tokens"],
|
|
8186
10128
|
[""],
|
|
8187
|
-
[
|
|
10129
|
+
[chalk12.bold("Swap Operations:")],
|
|
8188
10130
|
[" swap-chains - List swap-enabled chains"],
|
|
8189
10131
|
[" swap-quote <from> <to> <amount> - Get quote"],
|
|
8190
10132
|
[" swap <from> <to> <amount> - Execute swap"],
|
|
8191
10133
|
[""],
|
|
8192
|
-
[
|
|
10134
|
+
[chalk12.bold("Session Commands (shell only):")],
|
|
8193
10135
|
[" lock - Lock vault"],
|
|
8194
10136
|
[" unlock - Unlock vault"],
|
|
8195
10137
|
[" status - Show vault status"],
|
|
8196
10138
|
[""],
|
|
8197
|
-
[
|
|
10139
|
+
[chalk12.bold("Settings:")],
|
|
8198
10140
|
[" currency [code] - View/set currency"],
|
|
8199
10141
|
[" server - Check server status"],
|
|
8200
10142
|
[" address-book - Manage saved addresses"],
|
|
8201
10143
|
[""],
|
|
8202
|
-
[
|
|
10144
|
+
[chalk12.bold("Help & Navigation:")],
|
|
8203
10145
|
[" help, ? - Show this help"],
|
|
8204
10146
|
[" .clear - Clear screen"],
|
|
8205
10147
|
[" .exit - Exit shell"]
|
|
@@ -8337,12 +10279,12 @@ var ShellSession = class {
|
|
|
8337
10279
|
*/
|
|
8338
10280
|
async start() {
|
|
8339
10281
|
console.clear();
|
|
8340
|
-
console.log(
|
|
8341
|
-
console.log(
|
|
8342
|
-
console.log(
|
|
10282
|
+
console.log(chalk13.cyan.bold("\n=============================================="));
|
|
10283
|
+
console.log(chalk13.cyan.bold(" Vultisig Interactive Shell"));
|
|
10284
|
+
console.log(chalk13.cyan.bold("==============================================\n"));
|
|
8343
10285
|
await this.loadAllVaults();
|
|
8344
10286
|
this.displayVaultList();
|
|
8345
|
-
console.log(
|
|
10287
|
+
console.log(chalk13.gray('Type "help" for available commands, "exit" to quit\n'));
|
|
8346
10288
|
this.promptLoop().catch(() => {
|
|
8347
10289
|
});
|
|
8348
10290
|
}
|
|
@@ -8376,12 +10318,12 @@ var ShellSession = class {
|
|
|
8376
10318
|
const now = Date.now();
|
|
8377
10319
|
if (now - this.lastSigintTime < this.DOUBLE_CTRL_C_TIMEOUT) {
|
|
8378
10320
|
rl.close();
|
|
8379
|
-
console.log(
|
|
10321
|
+
console.log(chalk13.yellow("\nGoodbye!"));
|
|
8380
10322
|
this.ctx.dispose();
|
|
8381
10323
|
process.exit(0);
|
|
8382
10324
|
}
|
|
8383
10325
|
this.lastSigintTime = now;
|
|
8384
|
-
console.log(
|
|
10326
|
+
console.log(chalk13.yellow("\n(Press Ctrl+C again to exit)"));
|
|
8385
10327
|
rl.close();
|
|
8386
10328
|
resolve("");
|
|
8387
10329
|
});
|
|
@@ -8476,7 +10418,7 @@ var ShellSession = class {
|
|
|
8476
10418
|
stopAllSpinners();
|
|
8477
10419
|
process.stdout.write("\x1B[?25h");
|
|
8478
10420
|
process.stdout.write("\r\x1B[K");
|
|
8479
|
-
console.log(
|
|
10421
|
+
console.log(chalk13.yellow("\nCancelling operation..."));
|
|
8480
10422
|
};
|
|
8481
10423
|
const cleanup = () => {
|
|
8482
10424
|
process.removeListener("SIGINT", onSigint);
|
|
@@ -8513,10 +10455,10 @@ var ShellSession = class {
|
|
|
8513
10455
|
stopAllSpinners();
|
|
8514
10456
|
process.stdout.write("\x1B[?25h");
|
|
8515
10457
|
process.stdout.write("\r\x1B[K");
|
|
8516
|
-
console.log(
|
|
10458
|
+
console.log(chalk13.yellow("Operation cancelled"));
|
|
8517
10459
|
return;
|
|
8518
10460
|
}
|
|
8519
|
-
console.error(
|
|
10461
|
+
console.error(chalk13.red(`
|
|
8520
10462
|
Error: ${error2.message}`));
|
|
8521
10463
|
}
|
|
8522
10464
|
}
|
|
@@ -8549,7 +10491,7 @@ Error: ${error2.message}`));
|
|
|
8549
10491
|
break;
|
|
8550
10492
|
case "rename":
|
|
8551
10493
|
if (args.length === 0) {
|
|
8552
|
-
console.log(
|
|
10494
|
+
console.log(chalk13.yellow("Usage: rename <newName>"));
|
|
8553
10495
|
return;
|
|
8554
10496
|
}
|
|
8555
10497
|
await executeRename(this.ctx, args.join(" "));
|
|
@@ -8625,41 +10567,41 @@ Error: ${error2.message}`));
|
|
|
8625
10567
|
// Exit
|
|
8626
10568
|
case "exit":
|
|
8627
10569
|
case "quit":
|
|
8628
|
-
console.log(
|
|
10570
|
+
console.log(chalk13.yellow("\nGoodbye!"));
|
|
8629
10571
|
this.ctx.dispose();
|
|
8630
10572
|
process.exit(0);
|
|
8631
10573
|
break;
|
|
8632
10574
|
// eslint requires break even after process.exit
|
|
8633
10575
|
default:
|
|
8634
|
-
console.log(
|
|
8635
|
-
console.log(
|
|
10576
|
+
console.log(chalk13.yellow(`Unknown command: ${command}`));
|
|
10577
|
+
console.log(chalk13.gray('Type "help" for available commands'));
|
|
8636
10578
|
break;
|
|
8637
10579
|
}
|
|
8638
10580
|
}
|
|
8639
10581
|
// ===== Command Helpers =====
|
|
8640
10582
|
async switchVault(args) {
|
|
8641
10583
|
if (args.length === 0) {
|
|
8642
|
-
console.log(
|
|
8643
|
-
console.log(
|
|
10584
|
+
console.log(chalk13.yellow("Usage: vault <name>"));
|
|
10585
|
+
console.log(chalk13.gray('Run "vaults" to see available vaults'));
|
|
8644
10586
|
return;
|
|
8645
10587
|
}
|
|
8646
10588
|
const vaultName = args.join(" ");
|
|
8647
10589
|
const vault = this.ctx.findVaultByName(vaultName);
|
|
8648
10590
|
if (!vault) {
|
|
8649
|
-
console.log(
|
|
8650
|
-
console.log(
|
|
10591
|
+
console.log(chalk13.red(`Vault not found: ${vaultName}`));
|
|
10592
|
+
console.log(chalk13.gray('Run "vaults" to see available vaults'));
|
|
8651
10593
|
return;
|
|
8652
10594
|
}
|
|
8653
10595
|
await this.ctx.setActiveVault(vault);
|
|
8654
|
-
console.log(
|
|
10596
|
+
console.log(chalk13.green(`
|
|
8655
10597
|
+ Switched to: ${vault.name}`));
|
|
8656
10598
|
const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
|
|
8657
|
-
const status = isUnlocked ?
|
|
10599
|
+
const status = isUnlocked ? chalk13.green("Unlocked") : chalk13.yellow("Locked");
|
|
8658
10600
|
console.log(`Status: ${status}`);
|
|
8659
10601
|
}
|
|
8660
10602
|
async importVault(args) {
|
|
8661
10603
|
if (args.length === 0) {
|
|
8662
|
-
console.log(
|
|
10604
|
+
console.log(chalk13.yellow("Usage: import <file>"));
|
|
8663
10605
|
return;
|
|
8664
10606
|
}
|
|
8665
10607
|
const filePath = args.join(" ");
|
|
@@ -8674,45 +10616,45 @@ Error: ${error2.message}`));
|
|
|
8674
10616
|
async createVault(args) {
|
|
8675
10617
|
const type = args[0]?.toLowerCase();
|
|
8676
10618
|
if (!type || type !== "fast" && type !== "secure") {
|
|
8677
|
-
console.log(
|
|
8678
|
-
console.log(
|
|
8679
|
-
console.log(
|
|
10619
|
+
console.log(chalk13.yellow("Usage: create <fast|secure>"));
|
|
10620
|
+
console.log(chalk13.gray(" create fast - Create a fast vault (server-assisted 2-of-2)"));
|
|
10621
|
+
console.log(chalk13.gray(" create secure - Create a secure vault (multi-device MPC)"));
|
|
8680
10622
|
return;
|
|
8681
10623
|
}
|
|
8682
10624
|
let vault;
|
|
8683
10625
|
if (type === "fast") {
|
|
8684
10626
|
const name = await this.prompt("Vault name");
|
|
8685
10627
|
if (!name) {
|
|
8686
|
-
console.log(
|
|
10628
|
+
console.log(chalk13.red("Name is required"));
|
|
8687
10629
|
return;
|
|
8688
10630
|
}
|
|
8689
10631
|
const password = await this.promptPassword("Vault password");
|
|
8690
10632
|
if (!password) {
|
|
8691
|
-
console.log(
|
|
10633
|
+
console.log(chalk13.red("Password is required"));
|
|
8692
10634
|
return;
|
|
8693
10635
|
}
|
|
8694
10636
|
const email = await this.prompt("Email for verification");
|
|
8695
10637
|
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
8696
|
-
console.log(
|
|
10638
|
+
console.log(chalk13.red("Valid email is required"));
|
|
8697
10639
|
return;
|
|
8698
10640
|
}
|
|
8699
10641
|
vault = await this.withCancellation((signal) => executeCreateFast(this.ctx, { name, password, email, signal }));
|
|
8700
10642
|
} else {
|
|
8701
10643
|
const name = await this.prompt("Vault name");
|
|
8702
10644
|
if (!name) {
|
|
8703
|
-
console.log(
|
|
10645
|
+
console.log(chalk13.red("Name is required"));
|
|
8704
10646
|
return;
|
|
8705
10647
|
}
|
|
8706
10648
|
const sharesStr = await this.prompt("Total shares (devices)", "3");
|
|
8707
10649
|
const shares = parseInt(sharesStr, 10);
|
|
8708
10650
|
if (isNaN(shares) || shares < 2) {
|
|
8709
|
-
console.log(
|
|
10651
|
+
console.log(chalk13.red("Must have at least 2 shares"));
|
|
8710
10652
|
return;
|
|
8711
10653
|
}
|
|
8712
10654
|
const thresholdStr = await this.prompt("Signing threshold", "2");
|
|
8713
10655
|
const threshold = parseInt(thresholdStr, 10);
|
|
8714
10656
|
if (isNaN(threshold) || threshold < 1 || threshold > shares) {
|
|
8715
|
-
console.log(
|
|
10657
|
+
console.log(chalk13.red(`Threshold must be between 1 and ${shares}`));
|
|
8716
10658
|
return;
|
|
8717
10659
|
}
|
|
8718
10660
|
const password = await this.promptPassword("Vault password (optional, press Enter to skip)");
|
|
@@ -8734,37 +10676,37 @@ Error: ${error2.message}`));
|
|
|
8734
10676
|
async importSeedphrase(args) {
|
|
8735
10677
|
const type = args[0]?.toLowerCase();
|
|
8736
10678
|
if (!type || type !== "fast" && type !== "secure") {
|
|
8737
|
-
console.log(
|
|
8738
|
-
console.log(
|
|
8739
|
-
console.log(
|
|
10679
|
+
console.log(chalk13.cyan("Usage: create-from-seedphrase <fast|secure>"));
|
|
10680
|
+
console.log(chalk13.gray(" fast - Import with VultiServer (2-of-2)"));
|
|
10681
|
+
console.log(chalk13.gray(" secure - Import with device coordination (N-of-M)"));
|
|
8740
10682
|
return;
|
|
8741
10683
|
}
|
|
8742
|
-
console.log(
|
|
10684
|
+
console.log(chalk13.cyan("\nEnter your recovery phrase (words separated by spaces):"));
|
|
8743
10685
|
const mnemonic = await this.promptPassword("Seedphrase");
|
|
8744
10686
|
const validation = await this.ctx.sdk.validateSeedphrase(mnemonic);
|
|
8745
10687
|
if (!validation.valid) {
|
|
8746
|
-
console.log(
|
|
10688
|
+
console.log(chalk13.red(`Invalid seedphrase: ${validation.error}`));
|
|
8747
10689
|
if (validation.invalidWords?.length) {
|
|
8748
|
-
console.log(
|
|
10690
|
+
console.log(chalk13.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
|
|
8749
10691
|
}
|
|
8750
10692
|
return;
|
|
8751
10693
|
}
|
|
8752
|
-
console.log(
|
|
10694
|
+
console.log(chalk13.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
|
|
8753
10695
|
let vault;
|
|
8754
10696
|
if (type === "fast") {
|
|
8755
10697
|
const name = await this.prompt("Vault name");
|
|
8756
10698
|
if (!name) {
|
|
8757
|
-
console.log(
|
|
10699
|
+
console.log(chalk13.red("Name is required"));
|
|
8758
10700
|
return;
|
|
8759
10701
|
}
|
|
8760
10702
|
const password = await this.promptPassword("Vault password");
|
|
8761
10703
|
if (!password) {
|
|
8762
|
-
console.log(
|
|
10704
|
+
console.log(chalk13.red("Password is required"));
|
|
8763
10705
|
return;
|
|
8764
10706
|
}
|
|
8765
10707
|
const email = await this.prompt("Email for verification");
|
|
8766
10708
|
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
8767
|
-
console.log(
|
|
10709
|
+
console.log(chalk13.red("Valid email is required"));
|
|
8768
10710
|
return;
|
|
8769
10711
|
}
|
|
8770
10712
|
const discoverStr = await this.prompt("Discover chains with balances? (y/n)", "y");
|
|
@@ -8782,19 +10724,19 @@ Error: ${error2.message}`));
|
|
|
8782
10724
|
} else {
|
|
8783
10725
|
const name = await this.prompt("Vault name");
|
|
8784
10726
|
if (!name) {
|
|
8785
|
-
console.log(
|
|
10727
|
+
console.log(chalk13.red("Name is required"));
|
|
8786
10728
|
return;
|
|
8787
10729
|
}
|
|
8788
10730
|
const sharesStr = await this.prompt("Total shares (devices)", "3");
|
|
8789
10731
|
const shares = parseInt(sharesStr, 10);
|
|
8790
10732
|
if (isNaN(shares) || shares < 2) {
|
|
8791
|
-
console.log(
|
|
10733
|
+
console.log(chalk13.red("Must have at least 2 shares"));
|
|
8792
10734
|
return;
|
|
8793
10735
|
}
|
|
8794
10736
|
const thresholdStr = await this.prompt("Signing threshold", "2");
|
|
8795
10737
|
const threshold = parseInt(thresholdStr, 10);
|
|
8796
10738
|
if (isNaN(threshold) || threshold < 1 || threshold > shares) {
|
|
8797
|
-
console.log(
|
|
10739
|
+
console.log(chalk13.red(`Threshold must be between 1 and ${shares}`));
|
|
8798
10740
|
return;
|
|
8799
10741
|
}
|
|
8800
10742
|
const password = await this.promptPassword("Vault password (optional, Enter to skip)");
|
|
@@ -8838,8 +10780,8 @@ Error: ${error2.message}`));
|
|
|
8838
10780
|
}
|
|
8839
10781
|
}
|
|
8840
10782
|
if (!fiatCurrencies4.includes(currency)) {
|
|
8841
|
-
console.log(
|
|
8842
|
-
console.log(
|
|
10783
|
+
console.log(chalk13.red(`Invalid currency: ${currency}`));
|
|
10784
|
+
console.log(chalk13.yellow(`Supported currencies: ${fiatCurrencies4.join(", ")}`));
|
|
8843
10785
|
return;
|
|
8844
10786
|
}
|
|
8845
10787
|
const raw = args.includes("--raw");
|
|
@@ -8847,7 +10789,7 @@ Error: ${error2.message}`));
|
|
|
8847
10789
|
}
|
|
8848
10790
|
async runSend(args) {
|
|
8849
10791
|
if (args.length < 3) {
|
|
8850
|
-
console.log(
|
|
10792
|
+
console.log(chalk13.yellow("Usage: send <chain> <to> <amount> [--token <tokenId>] [--memo <memo>]"));
|
|
8851
10793
|
return;
|
|
8852
10794
|
}
|
|
8853
10795
|
const [chainStr, to, amount, ...rest] = args;
|
|
@@ -8867,7 +10809,7 @@ Error: ${error2.message}`));
|
|
|
8867
10809
|
await this.withAbortHandler((signal) => executeSend(this.ctx, { chain, to, amount, tokenId, memo, signal }));
|
|
8868
10810
|
} catch (err) {
|
|
8869
10811
|
if (err.message === "Transaction cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
|
|
8870
|
-
console.log(
|
|
10812
|
+
console.log(chalk13.yellow("\nTransaction cancelled"));
|
|
8871
10813
|
return;
|
|
8872
10814
|
}
|
|
8873
10815
|
throw err;
|
|
@@ -8875,7 +10817,7 @@ Error: ${error2.message}`));
|
|
|
8875
10817
|
}
|
|
8876
10818
|
async runTxStatus(args) {
|
|
8877
10819
|
if (args.length < 2) {
|
|
8878
|
-
console.log(
|
|
10820
|
+
console.log(chalk13.yellow("Usage: tx-status <chain> <txHash> [--no-wait]"));
|
|
8879
10821
|
return;
|
|
8880
10822
|
}
|
|
8881
10823
|
const [chainStr, txHash, ...rest] = args;
|
|
@@ -8893,8 +10835,8 @@ Error: ${error2.message}`));
|
|
|
8893
10835
|
} else if (args[i] === "--add" && i + 1 < args.length) {
|
|
8894
10836
|
const chain = findChainByName(args[i + 1]);
|
|
8895
10837
|
if (!chain) {
|
|
8896
|
-
console.log(
|
|
8897
|
-
console.log(
|
|
10838
|
+
console.log(chalk13.red(`Unknown chain: ${args[i + 1]}`));
|
|
10839
|
+
console.log(chalk13.gray("Use tab completion to see available chains"));
|
|
8898
10840
|
return;
|
|
8899
10841
|
}
|
|
8900
10842
|
addChain = chain;
|
|
@@ -8902,8 +10844,8 @@ Error: ${error2.message}`));
|
|
|
8902
10844
|
} else if (args[i] === "--remove" && i + 1 < args.length) {
|
|
8903
10845
|
const chain = findChainByName(args[i + 1]);
|
|
8904
10846
|
if (!chain) {
|
|
8905
|
-
console.log(
|
|
8906
|
-
console.log(
|
|
10847
|
+
console.log(chalk13.red(`Unknown chain: ${args[i + 1]}`));
|
|
10848
|
+
console.log(chalk13.gray("Use tab completion to see available chains"));
|
|
8907
10849
|
return;
|
|
8908
10850
|
}
|
|
8909
10851
|
removeChain = chain;
|
|
@@ -8914,7 +10856,7 @@ Error: ${error2.message}`));
|
|
|
8914
10856
|
}
|
|
8915
10857
|
async runTokens(args) {
|
|
8916
10858
|
if (args.length === 0) {
|
|
8917
|
-
console.log(
|
|
10859
|
+
console.log(chalk13.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
|
|
8918
10860
|
return;
|
|
8919
10861
|
}
|
|
8920
10862
|
const chainStr = args[0];
|
|
@@ -8935,7 +10877,7 @@ Error: ${error2.message}`));
|
|
|
8935
10877
|
async runSwapQuote(args) {
|
|
8936
10878
|
if (args.length < 3) {
|
|
8937
10879
|
console.log(
|
|
8938
|
-
|
|
10880
|
+
chalk13.yellow("Usage: swap-quote <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>]")
|
|
8939
10881
|
);
|
|
8940
10882
|
return;
|
|
8941
10883
|
}
|
|
@@ -8959,7 +10901,7 @@ Error: ${error2.message}`));
|
|
|
8959
10901
|
async runSwap(args) {
|
|
8960
10902
|
if (args.length < 3) {
|
|
8961
10903
|
console.log(
|
|
8962
|
-
|
|
10904
|
+
chalk13.yellow(
|
|
8963
10905
|
"Usage: swap <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>] [--slippage <pct>]"
|
|
8964
10906
|
)
|
|
8965
10907
|
);
|
|
@@ -8990,7 +10932,7 @@ Error: ${error2.message}`));
|
|
|
8990
10932
|
);
|
|
8991
10933
|
} catch (err) {
|
|
8992
10934
|
if (err.message === "Swap cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
|
|
8993
|
-
console.log(
|
|
10935
|
+
console.log(chalk13.yellow("\nSwap cancelled"));
|
|
8994
10936
|
return;
|
|
8995
10937
|
}
|
|
8996
10938
|
throw err;
|
|
@@ -9052,24 +10994,24 @@ Error: ${error2.message}`));
|
|
|
9052
10994
|
}
|
|
9053
10995
|
getPrompt() {
|
|
9054
10996
|
const vault = this.ctx.getActiveVault();
|
|
9055
|
-
if (!vault) return
|
|
10997
|
+
if (!vault) return chalk13.cyan("wallet> ");
|
|
9056
10998
|
const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
|
|
9057
|
-
const status = isUnlocked ?
|
|
9058
|
-
return
|
|
10999
|
+
const status = isUnlocked ? chalk13.green("\u{1F513}") : chalk13.yellow("\u{1F512}");
|
|
11000
|
+
return chalk13.cyan(`wallet[${vault.name}]${status}> `);
|
|
9059
11001
|
}
|
|
9060
11002
|
displayVaultList() {
|
|
9061
11003
|
const vaults = Array.from(this.ctx.getVaults().values());
|
|
9062
11004
|
const activeVault = this.ctx.getActiveVault();
|
|
9063
11005
|
if (vaults.length === 0) {
|
|
9064
|
-
console.log(
|
|
11006
|
+
console.log(chalk13.yellow('No vaults found. Use "create" or "import <file>" to add a vault.\n'));
|
|
9065
11007
|
return;
|
|
9066
11008
|
}
|
|
9067
|
-
console.log(
|
|
11009
|
+
console.log(chalk13.cyan("Loaded Vaults:\n"));
|
|
9068
11010
|
vaults.forEach((vault) => {
|
|
9069
11011
|
const isActive = vault.id === activeVault?.id;
|
|
9070
11012
|
const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
|
|
9071
|
-
const activeMarker = isActive ?
|
|
9072
|
-
const lockIcon = isUnlocked ?
|
|
11013
|
+
const activeMarker = isActive ? chalk13.green(" (active)") : "";
|
|
11014
|
+
const lockIcon = isUnlocked ? chalk13.green("\u{1F513}") : chalk13.yellow("\u{1F512}");
|
|
9073
11015
|
console.log(` ${lockIcon} ${vault.name}${activeMarker} - ${vault.type}`);
|
|
9074
11016
|
});
|
|
9075
11017
|
console.log();
|
|
@@ -9077,150 +11019,7 @@ Error: ${error2.message}`));
|
|
|
9077
11019
|
};
|
|
9078
11020
|
|
|
9079
11021
|
// src/lib/errors.ts
|
|
9080
|
-
import chalk13 from "chalk";
|
|
9081
|
-
|
|
9082
|
-
// src/lib/version.ts
|
|
9083
11022
|
import chalk14 from "chalk";
|
|
9084
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync2 } from "fs";
|
|
9085
|
-
import { homedir as homedir3 } from "os";
|
|
9086
|
-
import { join as join3 } from "path";
|
|
9087
|
-
var cachedVersion = null;
|
|
9088
|
-
function getVersion() {
|
|
9089
|
-
if (cachedVersion) return cachedVersion;
|
|
9090
|
-
if (true) {
|
|
9091
|
-
cachedVersion = "0.15.4";
|
|
9092
|
-
return cachedVersion;
|
|
9093
|
-
}
|
|
9094
|
-
try {
|
|
9095
|
-
const packagePath = new URL("../../package.json", import.meta.url);
|
|
9096
|
-
const pkg = JSON.parse(readFileSync3(packagePath, "utf-8"));
|
|
9097
|
-
cachedVersion = pkg.version;
|
|
9098
|
-
return cachedVersion;
|
|
9099
|
-
} catch {
|
|
9100
|
-
cachedVersion = "0.0.0-unknown";
|
|
9101
|
-
return cachedVersion;
|
|
9102
|
-
}
|
|
9103
|
-
}
|
|
9104
|
-
var CACHE_DIR = join3(homedir3(), ".vultisig", "cache");
|
|
9105
|
-
var VERSION_CACHE_FILE = join3(CACHE_DIR, "version-check.json");
|
|
9106
|
-
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
9107
|
-
function readVersionCache() {
|
|
9108
|
-
try {
|
|
9109
|
-
if (!existsSync2(VERSION_CACHE_FILE)) return null;
|
|
9110
|
-
const data = readFileSync3(VERSION_CACHE_FILE, "utf-8");
|
|
9111
|
-
return JSON.parse(data);
|
|
9112
|
-
} catch {
|
|
9113
|
-
return null;
|
|
9114
|
-
}
|
|
9115
|
-
}
|
|
9116
|
-
function writeVersionCache(cache) {
|
|
9117
|
-
try {
|
|
9118
|
-
if (!existsSync2(CACHE_DIR)) {
|
|
9119
|
-
mkdirSync3(CACHE_DIR, { recursive: true });
|
|
9120
|
-
}
|
|
9121
|
-
writeFileSync3(VERSION_CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
9122
|
-
} catch {
|
|
9123
|
-
}
|
|
9124
|
-
}
|
|
9125
|
-
async function fetchLatestVersion() {
|
|
9126
|
-
try {
|
|
9127
|
-
const controller = new AbortController();
|
|
9128
|
-
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
9129
|
-
const response = await fetch("https://registry.npmjs.org/@vultisig/cli/latest", {
|
|
9130
|
-
signal: controller.signal,
|
|
9131
|
-
headers: {
|
|
9132
|
-
Accept: "application/json"
|
|
9133
|
-
}
|
|
9134
|
-
});
|
|
9135
|
-
clearTimeout(timeout);
|
|
9136
|
-
if (!response.ok) return null;
|
|
9137
|
-
const data = await response.json();
|
|
9138
|
-
return data.version ?? null;
|
|
9139
|
-
} catch {
|
|
9140
|
-
return null;
|
|
9141
|
-
}
|
|
9142
|
-
}
|
|
9143
|
-
function isNewerVersion(v1, v2) {
|
|
9144
|
-
const parse = (v) => {
|
|
9145
|
-
const clean2 = v.replace(/^v/, "").replace(/-.*$/, "");
|
|
9146
|
-
return clean2.split(".").map((n) => parseInt(n, 10) || 0);
|
|
9147
|
-
};
|
|
9148
|
-
const p1 = parse(v1);
|
|
9149
|
-
const p2 = parse(v2);
|
|
9150
|
-
for (let i = 0; i < 3; i++) {
|
|
9151
|
-
const n1 = p1[i] ?? 0;
|
|
9152
|
-
const n2 = p2[i] ?? 0;
|
|
9153
|
-
if (n2 > n1) return true;
|
|
9154
|
-
if (n2 < n1) return false;
|
|
9155
|
-
}
|
|
9156
|
-
if (v1.includes("-") && !v2.includes("-")) return true;
|
|
9157
|
-
return false;
|
|
9158
|
-
}
|
|
9159
|
-
async function checkForUpdates() {
|
|
9160
|
-
if (process.env.VULTISIG_NO_UPDATE_CHECK === "1") {
|
|
9161
|
-
return null;
|
|
9162
|
-
}
|
|
9163
|
-
const currentVersion = getVersion();
|
|
9164
|
-
const cache = readVersionCache();
|
|
9165
|
-
if (cache && Date.now() - cache.lastCheck < CACHE_TTL_MS) {
|
|
9166
|
-
return {
|
|
9167
|
-
currentVersion,
|
|
9168
|
-
latestVersion: cache.latestVersion,
|
|
9169
|
-
updateAvailable: cache.latestVersion ? isNewerVersion(currentVersion, cache.latestVersion) : false
|
|
9170
|
-
};
|
|
9171
|
-
}
|
|
9172
|
-
const latestVersion = await fetchLatestVersion();
|
|
9173
|
-
writeVersionCache({
|
|
9174
|
-
lastCheck: Date.now(),
|
|
9175
|
-
latestVersion
|
|
9176
|
-
});
|
|
9177
|
-
return {
|
|
9178
|
-
currentVersion,
|
|
9179
|
-
latestVersion,
|
|
9180
|
-
updateAvailable: latestVersion ? isNewerVersion(currentVersion, latestVersion) : false
|
|
9181
|
-
};
|
|
9182
|
-
}
|
|
9183
|
-
function formatVersionShort() {
|
|
9184
|
-
return `vultisig/${getVersion()}`;
|
|
9185
|
-
}
|
|
9186
|
-
function formatVersionDetailed() {
|
|
9187
|
-
const lines = [];
|
|
9188
|
-
lines.push(chalk14.bold(`Vultisig CLI v${getVersion()}`));
|
|
9189
|
-
lines.push("");
|
|
9190
|
-
lines.push(` Node.js: ${process.version}`);
|
|
9191
|
-
lines.push(` Platform: ${process.platform}-${process.arch}`);
|
|
9192
|
-
lines.push(` Config: ~/.vultisig/`);
|
|
9193
|
-
return lines.join("\n");
|
|
9194
|
-
}
|
|
9195
|
-
function detectInstallMethod() {
|
|
9196
|
-
const execPath = process.execPath;
|
|
9197
|
-
if (execPath.includes("homebrew") || execPath.includes("Cellar")) {
|
|
9198
|
-
return "homebrew";
|
|
9199
|
-
}
|
|
9200
|
-
if (process.env.npm_execpath?.includes("yarn")) {
|
|
9201
|
-
return "yarn";
|
|
9202
|
-
}
|
|
9203
|
-
if (process.env.npm_config_user_agent?.includes("npm")) {
|
|
9204
|
-
return "npm";
|
|
9205
|
-
}
|
|
9206
|
-
if (execPath.includes("node_modules")) {
|
|
9207
|
-
return "npm";
|
|
9208
|
-
}
|
|
9209
|
-
return "unknown";
|
|
9210
|
-
}
|
|
9211
|
-
function getUpdateCommand() {
|
|
9212
|
-
const method = detectInstallMethod();
|
|
9213
|
-
switch (method) {
|
|
9214
|
-
case "npm":
|
|
9215
|
-
return "npm update -g @vultisig/cli";
|
|
9216
|
-
case "yarn":
|
|
9217
|
-
return "yarn global upgrade @vultisig/cli";
|
|
9218
|
-
case "homebrew":
|
|
9219
|
-
return "brew upgrade vultisig";
|
|
9220
|
-
default:
|
|
9221
|
-
return "npm update -g @vultisig/cli";
|
|
9222
|
-
}
|
|
9223
|
-
}
|
|
9224
11023
|
|
|
9225
11024
|
// src/lib/completion.ts
|
|
9226
11025
|
import { homedir as homedir4 } from "os";
|
|
@@ -9561,9 +11360,32 @@ setupUserAgent();
|
|
|
9561
11360
|
if (handled) process.exit(0);
|
|
9562
11361
|
})();
|
|
9563
11362
|
var ctx;
|
|
9564
|
-
program.name("vultisig").description("Vultisig CLI - Secure multi-party crypto wallet").version(formatVersionShort(), "-v, --version", "Show version").option("--debug", "Enable debug output").option("--silent", "Suppress informational output, show only results").option(
|
|
11363
|
+
program.name("vultisig").description("Vultisig CLI - Secure multi-party crypto wallet").version(formatVersionShort(), "-v, --version", "Show version").option("--debug", "Enable debug output").option("--silent", "Suppress informational output, show only results").option(
|
|
11364
|
+
"-o, --output <format>",
|
|
11365
|
+
"Output format: json, table (defaults to json when piped)",
|
|
11366
|
+
(val) => {
|
|
11367
|
+
if (!["json", "table"].includes(val)) throw new InvalidArgumentError('Must be "json" or "table"');
|
|
11368
|
+
return val;
|
|
11369
|
+
},
|
|
11370
|
+
process.stdout.isTTY ? "table" : "json"
|
|
11371
|
+
).option("-q, --quiet", "Strip empty/zero fields from output").option("--fields <fields>", "Comma-separated list of fields to include in output").option("--non-interactive", "Disable interactive prompts (fail instead of asking)").option("--ci", "CI/automation mode (equivalent to --output json --non-interactive --quiet)").option("-i, --interactive", "Start interactive shell mode").option("--vault <nameOrId>", "Specify vault by name or ID").option("--server-url <url>", "Base Vultisig API URL for FastVault and relay endpoints").addHelpText(
|
|
11372
|
+
"after",
|
|
11373
|
+
"\nExit codes:\n" + Object.entries(EXIT_CODE_DESCRIPTIONS).map(([k, v]) => ` ${k} ${v}`).join("\n") + "\n\nEnvironment variables:\n VAULT_PASSWORD Vault password for signing (bypasses prompt)\n VULTISIG_PASSWORD Alias for VAULT_PASSWORD\n VAULT_PASSWORDS Space-separated VaultName:password pairs\n VULTISIG_VAULT Default vault name or ID\n VULTISIG_CONFIG_DIR Override config directory (~/.vultisig)\n VULTISIG_SILENT Set to 1 for silent mode\n NO_COLOR Disable colored output"
|
|
11374
|
+
).hook("preAction", (thisCommand) => {
|
|
9565
11375
|
const opts = thisCommand.opts();
|
|
11376
|
+
if (opts.ci) {
|
|
11377
|
+
const outputExplicit = process.argv.some((a) => a === "--output" || a === "-o" || a.startsWith("--output="));
|
|
11378
|
+
opts.output = opts.output === "table" && !outputExplicit ? "json" : opts.output;
|
|
11379
|
+
opts.quiet = true;
|
|
11380
|
+
opts.nonInteractive = true;
|
|
11381
|
+
}
|
|
9566
11382
|
initOutputMode({ silent: opts.silent, output: opts.output });
|
|
11383
|
+
setQuiet(!!opts.quiet);
|
|
11384
|
+
setNonInteractive(!!opts.nonInteractive);
|
|
11385
|
+
const fields = opts.fields;
|
|
11386
|
+
setFields(
|
|
11387
|
+
fields ? fields.split(",").map((f) => f.trim()).filter(Boolean) : void 0
|
|
11388
|
+
);
|
|
9567
11389
|
});
|
|
9568
11390
|
async function findVaultByNameOrId(sdk, nameOrId) {
|
|
9569
11391
|
const vaults = await sdk.listVaults();
|
|
@@ -9607,7 +11429,15 @@ async function init(vaultOverride, unlockPassword, passwordTTL) {
|
|
|
9607
11429
|
return ctx;
|
|
9608
11430
|
}
|
|
9609
11431
|
var createCmd = program.command("create").description("Create a vault");
|
|
9610
|
-
createCmd.command("fast").description("Create a fast vault (server-assisted 2-of-2)").requiredOption("--name <name>", "Vault name").requiredOption("--password <password>", "Vault password").requiredOption("--email <email>", "Email for verification").option("--two-step", 'Create vault without verifying OTP (verify later with "vultisig verify")').
|
|
11432
|
+
createCmd.command("fast").description("Create a fast vault (server-assisted 2-of-2)").requiredOption("--name <name>", "Vault name").requiredOption("--password <password>", "Vault password").requiredOption("--email <email>", "Email for verification").option("--two-step", 'Create vault without verifying OTP (verify later with "vultisig verify")').addHelpText(
|
|
11433
|
+
"after",
|
|
11434
|
+
`
|
|
11435
|
+
Examples:
|
|
11436
|
+
vultisig create fast --name mywallet --password secret --email me@example.com
|
|
11437
|
+
vultisig create fast --name mywallet --password secret --email me@example.com --two-step
|
|
11438
|
+
|
|
11439
|
+
See also: verify, auth setup`
|
|
11440
|
+
).action(
|
|
9611
11441
|
withExit(async (options) => {
|
|
9612
11442
|
const context = await init(program.opts().vault);
|
|
9613
11443
|
await executeCreateFast(context, { ...options, twoStep: options.twoStep });
|
|
@@ -9633,7 +11463,13 @@ program.command("add-mldsa").description("Add ML-DSA (post-quantum) keys to the
|
|
|
9633
11463
|
});
|
|
9634
11464
|
})
|
|
9635
11465
|
);
|
|
9636
|
-
program.command("import <file>").description("Import vault from .vult file").option("--password <password>", "Password to decrypt the vault file").
|
|
11466
|
+
program.command("import <file>").description("Import vault from .vult file").option("--password <password>", "Password to decrypt the vault file").addHelpText(
|
|
11467
|
+
"after",
|
|
11468
|
+
`
|
|
11469
|
+
Examples:
|
|
11470
|
+
vultisig import ~/vault-backup.vult
|
|
11471
|
+
vultisig import ~/vault-backup.vult --password mypassword`
|
|
11472
|
+
).action(
|
|
9637
11473
|
withExit(async (file, options) => {
|
|
9638
11474
|
const context = await init(program.opts().vault);
|
|
9639
11475
|
await executeImport(context, file, options.password);
|
|
@@ -9641,6 +11477,7 @@ program.command("import <file>").description("Import vault from .vult file").opt
|
|
|
9641
11477
|
);
|
|
9642
11478
|
var createFromSeedphraseCmd = program.command("create-from-seedphrase").description("Create vault from BIP39 seedphrase");
|
|
9643
11479
|
async function promptSeedphrase() {
|
|
11480
|
+
requireInteractive("Use --mnemonic flag to provide seedphrase non-interactively.");
|
|
9644
11481
|
info("\nEnter your 12 or 24-word recovery phrase.");
|
|
9645
11482
|
info("Words will be hidden as you type.\n");
|
|
9646
11483
|
const answer = await inquirer8.prompt([
|
|
@@ -9661,6 +11498,7 @@ async function promptSeedphrase() {
|
|
|
9661
11498
|
return answer.mnemonic.trim().toLowerCase();
|
|
9662
11499
|
}
|
|
9663
11500
|
async function promptQrPayload() {
|
|
11501
|
+
requireInteractive("Use --qr or --qr-file flag to provide QR payload non-interactively.");
|
|
9664
11502
|
info("\nEnter the QR code payload from the initiator device.");
|
|
9665
11503
|
info('The payload starts with "vultisig://".\n');
|
|
9666
11504
|
const answer = await inquirer8.prompt([
|
|
@@ -9786,7 +11624,15 @@ program.command("verify <vaultId>").description("Verify vault with email verific
|
|
|
9786
11624
|
}
|
|
9787
11625
|
)
|
|
9788
11626
|
);
|
|
9789
|
-
program.command("balance [chain]").description(
|
|
11627
|
+
program.command("balance [chain]").description(descriptions.balance.description).option("-t, --tokens", descriptions.balance.params.includeTokens).option("--raw", "Show raw values (wei/satoshis) for programmatic use").addHelpText(
|
|
11628
|
+
"after",
|
|
11629
|
+
`
|
|
11630
|
+
Examples:
|
|
11631
|
+
vultisig balance
|
|
11632
|
+
vultisig balance Ethereum --tokens
|
|
11633
|
+
vultisig balance --output json --fields chain,amount
|
|
11634
|
+
vultisig balance --output json -q`
|
|
11635
|
+
).action(
|
|
9790
11636
|
withExit(async (chainStr, options) => {
|
|
9791
11637
|
const context = await init(program.opts().vault);
|
|
9792
11638
|
await executeBalance(context, {
|
|
@@ -9796,7 +11642,20 @@ program.command("balance [chain]").description("Show balance for a chain or all
|
|
|
9796
11642
|
});
|
|
9797
11643
|
})
|
|
9798
11644
|
);
|
|
9799
|
-
program.command("send <chain> <to> [amount]").description(
|
|
11645
|
+
program.command("send <chain> <to> [amount]").description(descriptions.send.description).option("--max", "Send maximum amount (balance minus fees)").option("--token <tokenId>", "Token to send (default: native)").option("--memo <memo>", "Transaction memo").option("--dry-run", "Preview transaction without signing or broadcasting").option("--confirm", "Confirm and broadcast (without this flag, runs as a preview)").option("-y, --yes", "Alias for --confirm").option("--password <password>", "Vault password for signing").addHelpText(
|
|
11646
|
+
"after",
|
|
11647
|
+
`
|
|
11648
|
+
Examples:
|
|
11649
|
+
vultisig send Ethereum 0x1234...abcd 0.1
|
|
11650
|
+
vultisig send Bitcoin bc1q... --max --confirm
|
|
11651
|
+
vultisig send Ethereum 0x... 0.5 --dry-run --output json
|
|
11652
|
+
|
|
11653
|
+
Environment variables:
|
|
11654
|
+
VAULT_PASSWORD Vault password (bypasses prompt)
|
|
11655
|
+
VAULT_PASSWORDS Space-separated VaultName:password pairs
|
|
11656
|
+
|
|
11657
|
+
See also: balance, tx-status`
|
|
11658
|
+
).action(
|
|
9800
11659
|
withExit(
|
|
9801
11660
|
async (chainStr, to, amount, options) => {
|
|
9802
11661
|
if (!amount && !options.max) throw new Error("Provide an amount or use --max");
|
|
@@ -9809,7 +11668,8 @@ program.command("send <chain> <to> [amount]").description("Send tokens to an add
|
|
|
9809
11668
|
amount: amount ?? "max",
|
|
9810
11669
|
tokenId: options.token,
|
|
9811
11670
|
memo: options.memo,
|
|
9812
|
-
|
|
11671
|
+
dryRun: options.dryRun,
|
|
11672
|
+
yes: options.yes || options.confirm,
|
|
9813
11673
|
password: options.password
|
|
9814
11674
|
});
|
|
9815
11675
|
} catch (err) {
|
|
@@ -9822,7 +11682,13 @@ program.command("send <chain> <to> [amount]").description("Send tokens to an add
|
|
|
9822
11682
|
}
|
|
9823
11683
|
)
|
|
9824
11684
|
);
|
|
9825
|
-
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").
|
|
11685
|
+
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("--dry-run", "Preview execution without signing or broadcasting").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").addHelpText(
|
|
11686
|
+
"after",
|
|
11687
|
+
`
|
|
11688
|
+
Examples:
|
|
11689
|
+
vultisig execute THORChain <contract> '{"swap":{}}' --yes
|
|
11690
|
+
vultisig execute THORChain <contract> '{"deposit":{}}' --funds rune:1000000 --output json`
|
|
11691
|
+
).action(
|
|
9826
11692
|
withExit(
|
|
9827
11693
|
async (chainStr, contract, msg, options) => {
|
|
9828
11694
|
const context = await init(program.opts().vault, options.password);
|
|
@@ -9833,6 +11699,7 @@ program.command("execute <chain> <contract> <msg>").description("Execute a CosmW
|
|
|
9833
11699
|
msg,
|
|
9834
11700
|
funds: options.funds,
|
|
9835
11701
|
memo: options.memo,
|
|
11702
|
+
dryRun: options.dryRun,
|
|
9836
11703
|
yes: options.yes,
|
|
9837
11704
|
password: options.password
|
|
9838
11705
|
});
|
|
@@ -9865,7 +11732,13 @@ program.command("broadcast").description("Broadcast a pre-signed raw transaction
|
|
|
9865
11732
|
});
|
|
9866
11733
|
})
|
|
9867
11734
|
);
|
|
9868
|
-
program.command("tx-status").description("Check the status of a transaction (polls until confirmed)").requiredOption("--chain <chain>", "Target blockchain").requiredOption("--tx-hash <hash>", "Transaction hash to check").option("--no-wait", "Return immediately without waiting for confirmation").
|
|
11735
|
+
program.command("tx-status").description("Check the status of a transaction (polls until confirmed)").requiredOption("--chain <chain>", "Target blockchain").requiredOption("--tx-hash <hash>", "Transaction hash to check").option("--no-wait", "Return immediately without waiting for confirmation").addHelpText(
|
|
11736
|
+
"after",
|
|
11737
|
+
`
|
|
11738
|
+
Examples:
|
|
11739
|
+
vultisig tx-status --chain Ethereum --tx-hash 0xabc...
|
|
11740
|
+
vultisig tx-status --chain Bitcoin --tx-hash abc... --no-wait --output json`
|
|
11741
|
+
).action(
|
|
9869
11742
|
withExit(async (options) => {
|
|
9870
11743
|
const context = await init(program.opts().vault);
|
|
9871
11744
|
await executeTxStatus(context, {
|
|
@@ -9875,7 +11748,13 @@ program.command("tx-status").description("Check the status of a transaction (pol
|
|
|
9875
11748
|
});
|
|
9876
11749
|
})
|
|
9877
11750
|
);
|
|
9878
|
-
program.command("portfolio").description(
|
|
11751
|
+
program.command("portfolio").description(descriptions.portfolio.description).option("-c, --currency <currency>", "Fiat currency (usd, eur, gbp, etc.)", "usd").option("--raw", "Show raw values (wei/satoshis) for programmatic use").addHelpText(
|
|
11752
|
+
"after",
|
|
11753
|
+
`
|
|
11754
|
+
Examples:
|
|
11755
|
+
vultisig portfolio
|
|
11756
|
+
vultisig portfolio --currency eur --output json`
|
|
11757
|
+
).action(
|
|
9879
11758
|
withExit(async (options) => {
|
|
9880
11759
|
const context = await init(program.opts().vault);
|
|
9881
11760
|
await executePortfolio(context, {
|
|
@@ -9902,7 +11781,13 @@ program.command("discount").description("Show your VULT discount tier for swap f
|
|
|
9902
11781
|
await executeDiscount(context, { refresh: options.refresh });
|
|
9903
11782
|
})
|
|
9904
11783
|
);
|
|
9905
|
-
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)").
|
|
11784
|
+
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)").addHelpText(
|
|
11785
|
+
"after",
|
|
11786
|
+
`
|
|
11787
|
+
Examples:
|
|
11788
|
+
vultisig export ~/backup.vult
|
|
11789
|
+
vultisig export ~/backup.vult --password mypass --output json`
|
|
11790
|
+
).action(
|
|
9906
11791
|
withExit(async (path4, options) => {
|
|
9907
11792
|
const context = await init(program.opts().vault, options.password);
|
|
9908
11793
|
await executeExport(context, {
|
|
@@ -9912,7 +11797,13 @@ program.command("export [path]").description("Export vault to file").option("--p
|
|
|
9912
11797
|
});
|
|
9913
11798
|
})
|
|
9914
11799
|
);
|
|
9915
|
-
program.command("addresses").description(
|
|
11800
|
+
program.command("addresses").description(descriptions.address.description).addHelpText(
|
|
11801
|
+
"after",
|
|
11802
|
+
`
|
|
11803
|
+
Examples:
|
|
11804
|
+
vultisig addresses
|
|
11805
|
+
vultisig addresses --output json --fields chain,address`
|
|
11806
|
+
).action(
|
|
9916
11807
|
withExit(async () => {
|
|
9917
11808
|
const context = await init(program.opts().vault);
|
|
9918
11809
|
await executeAddresses(context);
|
|
@@ -9930,7 +11821,14 @@ program.command("address-book").description("Manage address book entries").optio
|
|
|
9930
11821
|
});
|
|
9931
11822
|
})
|
|
9932
11823
|
);
|
|
9933
|
-
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").
|
|
11824
|
+
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").addHelpText(
|
|
11825
|
+
"after",
|
|
11826
|
+
`
|
|
11827
|
+
Examples:
|
|
11828
|
+
vultisig chains
|
|
11829
|
+
vultisig chains --add Solana
|
|
11830
|
+
vultisig chains --add-all --output json`
|
|
11831
|
+
).action(
|
|
9934
11832
|
withExit(async (options) => {
|
|
9935
11833
|
const context = await init(program.opts().vault);
|
|
9936
11834
|
await executeChains(context, {
|
|
@@ -9940,7 +11838,13 @@ program.command("chains").description("List and manage chains").option("--add <c
|
|
|
9940
11838
|
});
|
|
9941
11839
|
})
|
|
9942
11840
|
);
|
|
9943
|
-
program.command("vaults").description("List all stored vaults").
|
|
11841
|
+
program.command("vaults").description("List all stored vaults").addHelpText(
|
|
11842
|
+
"after",
|
|
11843
|
+
`
|
|
11844
|
+
Examples:
|
|
11845
|
+
vultisig vaults
|
|
11846
|
+
vultisig vaults --output json`
|
|
11847
|
+
).action(
|
|
9944
11848
|
withExit(async () => {
|
|
9945
11849
|
const context = await init(program.opts().vault);
|
|
9946
11850
|
await executeVaults(context);
|
|
@@ -9958,7 +11862,7 @@ program.command("rename <newName>").description("Rename the active vault").actio
|
|
|
9958
11862
|
await executeRename(context, newName);
|
|
9959
11863
|
})
|
|
9960
11864
|
);
|
|
9961
|
-
program.command("info").description(
|
|
11865
|
+
program.command("info").description(descriptions.vaultInfo.description).action(
|
|
9962
11866
|
withExit(async () => {
|
|
9963
11867
|
const context = await init(program.opts().vault);
|
|
9964
11868
|
await executeInfo(context);
|
|
@@ -9973,7 +11877,14 @@ program.command("delete [vault]").description("Delete a vault from local storage
|
|
|
9973
11877
|
});
|
|
9974
11878
|
})
|
|
9975
11879
|
);
|
|
9976
|
-
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("--
|
|
11880
|
+
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("--discover", "Auto-discover tokens with balances on the chain").addHelpText(
|
|
11881
|
+
"after",
|
|
11882
|
+
`
|
|
11883
|
+
Examples:
|
|
11884
|
+
vultisig tokens Ethereum
|
|
11885
|
+
vultisig tokens Ethereum --discover --output json
|
|
11886
|
+
vultisig tokens Ethereum --add 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 --symbol USDC --decimals 6`
|
|
11887
|
+
).option("--symbol <symbol>", "Token symbol (for --add)").option("--name <name>", "Token name (for --add)").option("--decimals <decimals>", "Token decimals (for --add)", "18").action(
|
|
9977
11888
|
withExit(
|
|
9978
11889
|
async (chainStr, options) => {
|
|
9979
11890
|
const context = await init(program.opts().vault);
|
|
@@ -9981,6 +11892,7 @@ program.command("tokens <chain>").description("List and manage tokens for a chai
|
|
|
9981
11892
|
chain: findChainByName(chainStr) || chainStr,
|
|
9982
11893
|
add: options.add,
|
|
9983
11894
|
remove: options.remove,
|
|
11895
|
+
discover: options.discover,
|
|
9984
11896
|
symbol: options.symbol,
|
|
9985
11897
|
name: options.name,
|
|
9986
11898
|
decimals: options.decimals ? parseInt(options.decimals, 10) : void 0
|
|
@@ -9988,13 +11900,19 @@ program.command("tokens <chain>").description("List and manage tokens for a chai
|
|
|
9988
11900
|
}
|
|
9989
11901
|
)
|
|
9990
11902
|
);
|
|
9991
|
-
program.command("swap-chains").description(
|
|
11903
|
+
program.command("swap-chains").description(descriptions.supportedChains.description).action(
|
|
9992
11904
|
withExit(async () => {
|
|
9993
11905
|
const context = await init(program.opts().vault);
|
|
9994
11906
|
await executeSwapChains(context);
|
|
9995
11907
|
})
|
|
9996
11908
|
);
|
|
9997
|
-
program.command("swap-quote <fromChain> <toChain> [amount]").description(
|
|
11909
|
+
program.command("swap-quote <fromChain> <toChain> [amount]").description(descriptions.swapQuote.description).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)").addHelpText(
|
|
11910
|
+
"after",
|
|
11911
|
+
`
|
|
11912
|
+
Examples:
|
|
11913
|
+
vultisig swap-quote Ethereum Bitcoin 0.1
|
|
11914
|
+
vultisig swap-quote Ethereum Bitcoin --max --output json`
|
|
11915
|
+
).action(
|
|
9998
11916
|
withExit(
|
|
9999
11917
|
async (fromChainStr, toChainStr, amountStr, options) => {
|
|
10000
11918
|
if (!amountStr && !options.max) throw new Error("Provide an amount or use --max");
|
|
@@ -10010,7 +11928,16 @@ program.command("swap-quote <fromChain> <toChain> [amount]").description("Get a
|
|
|
10010
11928
|
}
|
|
10011
11929
|
)
|
|
10012
11930
|
);
|
|
10013
|
-
program.command("swap <fromChain> <toChain> [amount]").description(
|
|
11931
|
+
program.command("swap <fromChain> <toChain> [amount]").description(descriptions.swap.description).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("--dry-run", "Preview swap without signing or broadcasting").option("--confirm", "Confirm and broadcast (without this flag, runs as a preview)").option("-y, --yes", "Alias for --confirm").option("--password <password>", "Vault password for signing").addHelpText(
|
|
11932
|
+
"after",
|
|
11933
|
+
`
|
|
11934
|
+
Examples:
|
|
11935
|
+
vultisig swap Ethereum Bitcoin 0.1
|
|
11936
|
+
vultisig swap Ethereum Bitcoin --max --confirm
|
|
11937
|
+
vultisig swap Ethereum Bitcoin 0.5 --dry-run --output json
|
|
11938
|
+
|
|
11939
|
+
See also: swap-quote, swap-chains, balance`
|
|
11940
|
+
).action(
|
|
10014
11941
|
withExit(
|
|
10015
11942
|
async (fromChainStr, toChainStr, amountStr, options) => {
|
|
10016
11943
|
if (!amountStr && !options.max) throw new Error("Provide an amount or use --max");
|
|
@@ -10024,7 +11951,8 @@ program.command("swap <fromChain> <toChain> [amount]").description("Swap tokens
|
|
|
10024
11951
|
fromToken: options.fromToken,
|
|
10025
11952
|
toToken: options.toToken,
|
|
10026
11953
|
slippage: options.slippage ? parseFloat(options.slippage) : void 0,
|
|
10027
|
-
|
|
11954
|
+
dryRun: options.dryRun,
|
|
11955
|
+
yes: options.yes || options.confirm,
|
|
10028
11956
|
password: options.password
|
|
10029
11957
|
});
|
|
10030
11958
|
} catch (err) {
|
|
@@ -10068,7 +11996,7 @@ rujiraCmd.command("deposit").description("Show deposit instructions (inbound add
|
|
|
10068
11996
|
}
|
|
10069
11997
|
)
|
|
10070
11998
|
);
|
|
10071
|
-
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(
|
|
11999
|
+
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("--dry-run", "Preview swap quote without executing").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(
|
|
10072
12000
|
withExit(
|
|
10073
12001
|
async (fromAsset, toAsset, amount, options) => {
|
|
10074
12002
|
const context = await init(program.opts().vault, options.password);
|
|
@@ -10078,6 +12006,7 @@ rujiraCmd.command("swap <fromAsset> <toAsset> <amount>").description("Execute a
|
|
|
10078
12006
|
amount,
|
|
10079
12007
|
slippageBps: options.slippageBps ? parseInt(options.slippageBps, 10) : void 0,
|
|
10080
12008
|
destination: options.destination,
|
|
12009
|
+
dryRun: options.dryRun,
|
|
10081
12010
|
yes: options.yes,
|
|
10082
12011
|
password: options.password,
|
|
10083
12012
|
rpcEndpoint: options.rpc,
|
|
@@ -10086,7 +12015,7 @@ rujiraCmd.command("swap <fromAsset> <toAsset> <amount>").description("Execute a
|
|
|
10086
12015
|
}
|
|
10087
12016
|
)
|
|
10088
12017
|
);
|
|
10089
|
-
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(
|
|
12018
|
+
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("--dry-run", "Preview withdrawal without executing").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(
|
|
10090
12019
|
withExit(
|
|
10091
12020
|
async (asset, amount, l1Address, options) => {
|
|
10092
12021
|
const context = await init(program.opts().vault, options.password);
|
|
@@ -10095,6 +12024,7 @@ rujiraCmd.command("withdraw <asset> <amount> <l1Address>").description("Withdraw
|
|
|
10095
12024
|
amount,
|
|
10096
12025
|
l1Address,
|
|
10097
12026
|
maxFeeBps: options.maxFeeBps ? parseInt(options.maxFeeBps, 10) : void 0,
|
|
12027
|
+
dryRun: options.dryRun,
|
|
10098
12028
|
yes: options.yes,
|
|
10099
12029
|
password: options.password,
|
|
10100
12030
|
rpcEndpoint: options.rpc,
|
|
@@ -10129,7 +12059,13 @@ var agentCmd = program.command("agent").description("AI-powered chat interface f
|
|
|
10129
12059
|
});
|
|
10130
12060
|
}
|
|
10131
12061
|
);
|
|
10132
|
-
agentCmd.command("ask <message>").description("Send a single message and get the response (for AI agent integration)").option("--session <id>", "Continue an existing conversation").option("--backend-url <url>", "Agent backend URL (default: https://abe.vultisig.com)").option("--password <password>", "Vault password for signing operations").option("--verbose", "Show tool calls and debug info on stderr").option("--json", "Output structured JSON
|
|
12062
|
+
agentCmd.command("ask <message>").description("Send a single message and get the response (for AI agent integration)").option("--session <id>", "Continue an existing conversation").option("--backend-url <url>", "Agent backend URL (default: https://abe.vultisig.com)").option("--password <password>", "Vault password for signing operations").option("--verbose", "Show tool calls and debug info on stderr").option("--json", "Output structured JSON (deprecated: use --output json)").addHelpText(
|
|
12063
|
+
"after",
|
|
12064
|
+
`
|
|
12065
|
+
Examples:
|
|
12066
|
+
vultisig agent ask "What is my ETH balance?" --output json
|
|
12067
|
+
vultisig agent ask "Send 0.1 ETH to 0x..." --session abc123 --yes`
|
|
12068
|
+
).action(
|
|
10133
12069
|
async (message, options) => {
|
|
10134
12070
|
const parentOpts = agentCmd.opts();
|
|
10135
12071
|
const context = await init(program.opts().vault, options.password || parentOpts.password);
|
|
@@ -10197,7 +12133,66 @@ program.command("update").description("Check for updates and show update command
|
|
|
10197
12133
|
}
|
|
10198
12134
|
})
|
|
10199
12135
|
);
|
|
12136
|
+
var authCmd = program.command("auth").description("Manage keyring-stored vault credentials");
|
|
12137
|
+
authCmd.command("setup").description("Discover .vult files, prompt for passwords, and store credentials in the OS keyring").option("--vault-file <path>", "Path to a specific .vult file").option("--non-interactive", "Fail instead of prompting (use env vars)").addHelpText(
|
|
12138
|
+
"after",
|
|
12139
|
+
`
|
|
12140
|
+
Examples:
|
|
12141
|
+
vultisig auth setup
|
|
12142
|
+
vultisig auth setup --vault-file ~/vault.vult
|
|
12143
|
+
VAULT_PASSWORD=secret VAULT_DECRYPT_PASSWORD=pass vultisig auth setup --non-interactive`
|
|
12144
|
+
).action(
|
|
12145
|
+
withExit(async (options) => {
|
|
12146
|
+
const result = await executeAuthSetup({
|
|
12147
|
+
vaultFile: options.vaultFile,
|
|
12148
|
+
nonInteractive: options.nonInteractive || isNonInteractive()
|
|
12149
|
+
});
|
|
12150
|
+
if (isJsonOutput()) {
|
|
12151
|
+
outputJson({
|
|
12152
|
+
stored: true,
|
|
12153
|
+
vaultId: result.vaultId,
|
|
12154
|
+
vaultName: result.vaultName,
|
|
12155
|
+
storageBackend: result.storageBackend
|
|
12156
|
+
});
|
|
12157
|
+
} else {
|
|
12158
|
+
printResult(
|
|
12159
|
+
chalk15.green(`Vault "${result.vaultName}" (${result.vaultId}) credentials stored in ${result.storageBackend}.`)
|
|
12160
|
+
);
|
|
12161
|
+
}
|
|
12162
|
+
})
|
|
12163
|
+
);
|
|
12164
|
+
authCmd.command("status").description("List configured vaults and their keyring credential status").action(
|
|
12165
|
+
withExit(async () => {
|
|
12166
|
+
const vaults = await executeAuthStatus();
|
|
12167
|
+
if (isJsonOutput()) {
|
|
12168
|
+
outputJson({
|
|
12169
|
+
vaults: vaults.map((v) => ({ id: v.id, name: v.name, filePath: v.filePath, hasCredentials: v.hasCredentials }))
|
|
12170
|
+
});
|
|
12171
|
+
return;
|
|
12172
|
+
}
|
|
12173
|
+
if (vaults.length === 0) {
|
|
12174
|
+
printResult("No vaults configured. Run: vsig auth setup");
|
|
12175
|
+
return;
|
|
12176
|
+
}
|
|
12177
|
+
for (const v of vaults) {
|
|
12178
|
+
const status = v.hasCredentials ? chalk15.green("authenticated") : chalk15.red("no credentials");
|
|
12179
|
+
printResult(` ${v.name} (${v.id}) - ${status}`);
|
|
12180
|
+
printResult(` File: ${v.filePath}`);
|
|
12181
|
+
}
|
|
12182
|
+
})
|
|
12183
|
+
);
|
|
12184
|
+
authCmd.command("logout").description("Clear keyring credentials for a vault").option("--vault-id <id>", "Specific vault ID to clear").option("--all", "Clear credentials for all configured vaults").action(
|
|
12185
|
+
withExit(async (options) => {
|
|
12186
|
+
await executeAuthLogout({ vaultId: options.vaultId, all: options.all });
|
|
12187
|
+
if (isJsonOutput()) {
|
|
12188
|
+
outputJson({ cleared: true, vaultId: options.vaultId ?? null, all: !!options.all });
|
|
12189
|
+
} else {
|
|
12190
|
+
printResult(chalk15.green("Credentials cleared."));
|
|
12191
|
+
}
|
|
12192
|
+
})
|
|
12193
|
+
);
|
|
10200
12194
|
setupCompletionCommand(program);
|
|
12195
|
+
program.command("schema", { hidden: true }).description("Output machine-readable command schema (JSON introspection for agents)").helpOption(false).action(withExit(async () => executeSchema(program)));
|
|
10201
12196
|
async function startInteractiveMode() {
|
|
10202
12197
|
const serverEndpoints = resolveServerEndpoints(parseServerEndpointOverridesFromArgv(process.argv.slice(2)));
|
|
10203
12198
|
const sdk = new Vultisig6({
|