@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<void> {
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
- this._budget = createSolveBudgetFromRawState({
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.