@strkfarm/sdk 2.0.0-dev.33 → 2.0.0-dev.34
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.
|
@@ -2,7 +2,7 @@ import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
|
2
2
|
import { IConfig, TokenInfo } from "@/interfaces";
|
|
3
3
|
import { PricerBase } from "@/modules/pricerBase";
|
|
4
4
|
import { ERC20 } from "@/modules";
|
|
5
|
-
import { assert, logger } from "@/utils";
|
|
5
|
+
import { assert, HealthFactorMath, logger } from "@/utils";
|
|
6
6
|
import { ExtendedAdapter } from "../../universal-adapters/extended-adapter";
|
|
7
7
|
import { VesuMultiplyAdapter } from "../../universal-adapters/vesu-multiply-adapter";
|
|
8
8
|
import { AssetOperationType, AssetOperationStatus } from "@/modules/ExtendedWrapperSDk";
|
|
@@ -1684,6 +1684,160 @@ export function createSolveBudgetFromRawState(params: {
|
|
|
1684
1684
|
return budget;
|
|
1685
1685
|
}
|
|
1686
1686
|
|
|
1687
|
+
function assertRouteSanityAndAdjust(route: ExecutionRoute, availableAmount: number): ExecutionRoute {
|
|
1688
|
+
if (!(route as any).amount) {
|
|
1689
|
+
throw new Error(`Invalid route given to assertRouteSanityAndAdjust: ${JSON.stringify(route)}`);
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
const routeAmount = (route as any).amount.toNumber();
|
|
1693
|
+
if (routeAmount <= availableAmount) {
|
|
1694
|
+
return route; // all cool
|
|
1695
|
+
}
|
|
1696
|
+
if ((routeAmount - availableAmount) <= CASE_THRESHOLD_USD) {
|
|
1697
|
+
return { ...route, amount: new Web3Number(availableAmount, (route as any).amount.decimals) } as any;
|
|
1698
|
+
}
|
|
1699
|
+
throw new Error(`Route amount ${routeAmount} exceeds available amount ${availableAmount}, Route name: ${route.type}`);
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
function assertVesuMultiplyRouteSanityAndAdjust(route: VesuMultiplyRoute, availableAmount: number, state: SolveBudget, isIncrease: boolean) {
|
|
1703
|
+
const btcPrice = state.vesuPoolState.collateralPrice;
|
|
1704
|
+
// post state should be healthy
|
|
1705
|
+
const totalCollateralAmount = isIncrease ?
|
|
1706
|
+
route.marginAmount.plus(route.swappedCollateralAmount).plus(state.vesuPoolState.collateralAmount) :
|
|
1707
|
+
state.vesuPoolState.collateralAmount.minus(route.marginAmount.plus(route.swappedCollateralAmount));
|
|
1708
|
+
const totalDebtAmount = isIncrease ?
|
|
1709
|
+
route.debtAmount.plus(state.vesuPoolState.debtAmount) :
|
|
1710
|
+
state.vesuPoolState.debtAmount.minus(route.debtAmount);
|
|
1711
|
+
const newHF = HealthFactorMath.getHealthFactor(
|
|
1712
|
+
totalCollateralAmount,
|
|
1713
|
+
btcPrice,
|
|
1714
|
+
VesuConfig.maxLtv,
|
|
1715
|
+
totalDebtAmount,
|
|
1716
|
+
state.vesuPoolState.debtPrice,
|
|
1717
|
+
);
|
|
1718
|
+
const idealHF = VesuConfig.maxLtv / VesuConfig.targetLtv - 0.05;
|
|
1719
|
+
assert(newHF >= idealHF, `SolveBudget::applyRoutesAndVerifyLtvState newHF=${newHF} < idealHF=${idealHF}`);
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
function applyRoutesAndVerifyLtvState(
|
|
1723
|
+
state: SolveBudget,
|
|
1724
|
+
routes: ExecutionRoute[],
|
|
1725
|
+
): void {
|
|
1726
|
+
const btcPrice = state.vesuPoolState.collateralPrice;
|
|
1727
|
+
const adjustedRouters: ExecutionRoute[] = [...routes];
|
|
1728
|
+
let index = 0;
|
|
1729
|
+
for (const r of routes) {
|
|
1730
|
+
switch (r.type) {
|
|
1731
|
+
case RouteType.WALLET_TO_VA: {
|
|
1732
|
+
const adjustedRoute = assertRouteSanityAndAdjust(r, state.walletUsd);
|
|
1733
|
+
const amt = (adjustedRoute as TransferRoute).amount.toNumber();
|
|
1734
|
+
state.spendWallet(amt);
|
|
1735
|
+
state.addToVA(amt);
|
|
1736
|
+
break;
|
|
1737
|
+
}
|
|
1738
|
+
case RouteType.VESU_REPAY: {
|
|
1739
|
+
const adjustedRoute = assertRouteSanityAndAdjust(r, state.vaRawUsd);
|
|
1740
|
+
const amt = Math.abs((adjustedRoute as VesuDebtRoute).amount.toNumber());
|
|
1741
|
+
state.repayVesuBorrowCapacity(amt);
|
|
1742
|
+
state.spendVA(amt);
|
|
1743
|
+
break;
|
|
1744
|
+
}
|
|
1745
|
+
case RouteType.EXTENDED_TO_WALLET: {
|
|
1746
|
+
const adjustedRoute = assertRouteSanityAndAdjust(r, state.extAvailWithdraw);
|
|
1747
|
+
const amt = (adjustedRoute as TransferRoute).amount.toNumber();
|
|
1748
|
+
state.addToWallet(amt);
|
|
1749
|
+
state.spendExtAvailTrade(amt);
|
|
1750
|
+
break;
|
|
1751
|
+
}
|
|
1752
|
+
case RouteType.REALISE_PNL: {
|
|
1753
|
+
const amt = (r as RealisePnlRoute).amount.toNumber();
|
|
1754
|
+
state.spendExtAvailUpnl(amt);
|
|
1755
|
+
state.addToExtAvailTrade(amt);
|
|
1756
|
+
break;
|
|
1757
|
+
}
|
|
1758
|
+
case RouteType.VA_TO_EXTENDED: {
|
|
1759
|
+
const adjustedRoute = assertRouteSanityAndAdjust(r, state.vaRawUsd);
|
|
1760
|
+
const amt = (adjustedRoute as TransferRoute).amount.toNumber();
|
|
1761
|
+
state.spendVA(amt);
|
|
1762
|
+
state.addToExtAvailTrade(amt);
|
|
1763
|
+
break;
|
|
1764
|
+
}
|
|
1765
|
+
case RouteType.WALLET_TO_EXTENDED: {
|
|
1766
|
+
const adjustedRoute = assertRouteSanityAndAdjust(r, state.walletUsd);
|
|
1767
|
+
const amt = (adjustedRoute as TransferRoute).amount.toNumber();
|
|
1768
|
+
state.spendWallet(amt);
|
|
1769
|
+
state.addToExtAvailTrade(amt);
|
|
1770
|
+
break;
|
|
1771
|
+
}
|
|
1772
|
+
case RouteType.VESU_BORROW: {
|
|
1773
|
+
// assert amount being borrowed is acceptable
|
|
1774
|
+
const borrowedAmount = (r as VesuDebtRoute).amount.toNumber();
|
|
1775
|
+
const currentCollateralAmount = state.vesuPoolState.collateralAmount.toNumber();
|
|
1776
|
+
const currentDebtAmount = state.vesuPoolState.debtAmount.toNumber();
|
|
1777
|
+
const maxDebtAmount = HealthFactorMath.getMaxDebtAmount(
|
|
1778
|
+
new Web3Number(currentCollateralAmount, state.vesuPoolState.collateralToken.decimals),
|
|
1779
|
+
state.vesuPoolState.collateralPrice,
|
|
1780
|
+
VesuConfig.maxLtv,
|
|
1781
|
+
(VesuConfig.maxLtv / VesuConfig.targetLtv - 0.05), // e.g. if ideal HF is 1.4, upto 1.35 is ok.
|
|
1782
|
+
state.vesuPoolState.debtPrice,
|
|
1783
|
+
state.vesuPoolState.debtToken,
|
|
1784
|
+
);
|
|
1785
|
+
const availableDebtAmount = maxDebtAmount.minus(new Web3Number(currentDebtAmount, state.vesuPoolState.debtToken.decimals));
|
|
1786
|
+
if (!(availableDebtAmount.plus(CASE_THRESHOLD_USD).greaterThan(borrowedAmount))) {
|
|
1787
|
+
throw new Error(`SolveBudget::applyRoutesAndVerifyLtvState availableDebtAmount=${availableDebtAmount.toNumber()} < borrowedAmount=${borrowedAmount}`);
|
|
1788
|
+
}
|
|
1789
|
+
state.spendVesuBorrowCapacity(borrowedAmount);
|
|
1790
|
+
state.addToVA(borrowedAmount);
|
|
1791
|
+
break;
|
|
1792
|
+
}
|
|
1793
|
+
case RouteType.VESU_MULTIPLY_INCREASE_LEVER: {
|
|
1794
|
+
const mr = r as VesuMultiplyRoute;
|
|
1795
|
+
|
|
1796
|
+
// assert va has enough balance
|
|
1797
|
+
assertVesuMultiplyRouteSanityAndAdjust(mr, state.vaRawUsd, state, true);
|
|
1798
|
+
|
|
1799
|
+
state.applyVesuDelta(
|
|
1800
|
+
mr.poolId,
|
|
1801
|
+
mr.collateralToken,
|
|
1802
|
+
mr.debtToken,
|
|
1803
|
+
mr.marginAmount.plus(mr.swappedCollateralAmount),
|
|
1804
|
+
mr.debtAmount,
|
|
1805
|
+
);
|
|
1806
|
+
state.spendVA(mr.marginAmount.toNumber() * btcPrice);
|
|
1807
|
+
break;
|
|
1808
|
+
}
|
|
1809
|
+
case RouteType.VESU_MULTIPLY_DECREASE_LEVER: {
|
|
1810
|
+
const mr = r as VesuMultiplyRoute;
|
|
1811
|
+
// assert va has enough balance
|
|
1812
|
+
assertVesuMultiplyRouteSanityAndAdjust(mr, state.vaRawUsd, state, false);
|
|
1813
|
+
state.applyVesuDelta(
|
|
1814
|
+
mr.poolId,
|
|
1815
|
+
mr.collateralToken,
|
|
1816
|
+
mr.debtToken,
|
|
1817
|
+
new Web3Number(mr.marginAmount.negated().minus(mr.swappedCollateralAmount).toFixed(8), mr.marginAmount.decimals),
|
|
1818
|
+
mr.debtAmount,
|
|
1819
|
+
);
|
|
1820
|
+
// USDC from the margin leg hits VA before VA_TO_EXTENDED (matches _buildLtvRoutesFromRebalanceDeltas).
|
|
1821
|
+
const marginBtc = mr.marginAmount.toNumber();
|
|
1822
|
+
if (marginBtc > 10 ** -COLLATERAL_PRECISION) {
|
|
1823
|
+
state.addToVA(marginBtc * btcPrice);
|
|
1824
|
+
}
|
|
1825
|
+
break;
|
|
1826
|
+
}
|
|
1827
|
+
case RouteType.EXTENDED_DECREASE_LEVER:
|
|
1828
|
+
case RouteType.EXTENDED_INCREASE_LEVER: {
|
|
1829
|
+
const er = r as ExtendedLeverRoute;
|
|
1830
|
+
// mostly not required, as any lever changes in extended are already strictly governed by margin conditions
|
|
1831
|
+
state.applyExtendedExposureDelta(er.instrument, er.amount, btcPrice);
|
|
1832
|
+
break;
|
|
1833
|
+
}
|
|
1834
|
+
case RouteType.RETURN_TO_WAIT:
|
|
1835
|
+
break;
|
|
1836
|
+
}
|
|
1837
|
+
index++;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1687
1841
|
// ─── State Manager ─────────────────────────────────────────────────────────────
|
|
1688
1842
|
|
|
1689
1843
|
/**
|
|
@@ -1721,7 +1875,7 @@ export class ExtendedSVKVesuStateManager {
|
|
|
1721
1875
|
async solve(
|
|
1722
1876
|
withdrawAmount: Web3Number = new Web3Number(0, USDC_TOKEN_DECIMALS),
|
|
1723
1877
|
): Promise<SolveResult | null> {
|
|
1724
|
-
await this._refresh();
|
|
1878
|
+
const gaurdrailBudget = await this._refresh();
|
|
1725
1879
|
if (Math.abs(this._budget.extPendingDeposit) > 0) {
|
|
1726
1880
|
logger.warn(`${this._tag}::solve extPendingDeposit=${this._budget.extPendingDeposit}`);
|
|
1727
1881
|
return null;
|
|
@@ -1771,6 +1925,14 @@ export class ExtendedSVKVesuStateManager {
|
|
|
1771
1925
|
// its own routes with amounts and state info.
|
|
1772
1926
|
const cases = this._classifyCases(withdrawAmount);
|
|
1773
1927
|
|
|
1928
|
+
// validate routes
|
|
1929
|
+
for(const c of cases) {
|
|
1930
|
+
applyRoutesAndVerifyLtvState(gaurdrailBudget, c.routes);
|
|
1931
|
+
}
|
|
1932
|
+
const extendedPositionSize = gaurdrailBudget.extendedPositionsView[0].size.toNumber();
|
|
1933
|
+
const vesuPositionSize = gaurdrailBudget.vesuPoolState.collateralAmount.toNumber();
|
|
1934
|
+
assert(Math.abs(extendedPositionSize - vesuPositionSize) <= 0.00001, "extended positions size mismatch");
|
|
1935
|
+
|
|
1774
1936
|
const result: SolveResult = {
|
|
1775
1937
|
cases,
|
|
1776
1938
|
// ignore these fields for now. only cases are relevant.
|
|
@@ -1796,7 +1958,7 @@ export class ExtendedSVKVesuStateManager {
|
|
|
1796
1958
|
* Reads all on-chain and off-chain state in parallel and stores
|
|
1797
1959
|
* results in private instance variables.
|
|
1798
1960
|
*/
|
|
1799
|
-
private async _refresh(): Promise<
|
|
1961
|
+
private async _refresh(): Promise<SolveBudget> {
|
|
1800
1962
|
logger.info(`${this._tag}::_refresh starting`);
|
|
1801
1963
|
|
|
1802
1964
|
const [
|
|
@@ -1827,7 +1989,7 @@ export class ExtendedSVKVesuStateManager {
|
|
|
1827
1989
|
walletBalance,
|
|
1828
1990
|
);
|
|
1829
1991
|
|
|
1830
|
-
|
|
1992
|
+
const data = {
|
|
1831
1993
|
assetToken: this._config.assetToken,
|
|
1832
1994
|
usdcToken: this._config.usdcToken,
|
|
1833
1995
|
unusedBalance,
|
|
@@ -1854,7 +2016,10 @@ export class ExtendedSVKVesuStateManager {
|
|
|
1854
2016
|
new Web3Number(0, USDC_TOKEN_DECIMALS),
|
|
1855
2017
|
},
|
|
1856
2018
|
vesuPoolStates,
|
|
1857
|
-
}
|
|
2019
|
+
}
|
|
2020
|
+
this._budget = createSolveBudgetFromRawState(data);
|
|
2021
|
+
|
|
2022
|
+
const gaurdrailBudget = createSolveBudgetFromRawState({...data});
|
|
1858
2023
|
this._budget.logStateSummary();
|
|
1859
2024
|
|
|
1860
2025
|
const totalUnusedUsd = unusedBalance.reduce(
|
|
@@ -1874,6 +2039,8 @@ export class ExtendedSVKVesuStateManager {
|
|
|
1874
2039
|
`extendedBalance::pendingDeposit: ${extendedBalance?.pendingDeposit.toNumber()} - ` +
|
|
1875
2040
|
`extendedBalance::equity: ${extendedBalance?.equity.toNumber()}`,
|
|
1876
2041
|
);
|
|
2042
|
+
|
|
2043
|
+
return gaurdrailBudget;
|
|
1877
2044
|
}
|
|
1878
2045
|
|
|
1879
2046
|
// todo add communication check with python server of extended. if not working, throw error in solve function.
|