@strkfarm/sdk 2.0.0-dev.32 → 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.
@@ -43470,6 +43470,142 @@ function createSolveBudgetFromRawState(params) {
43470
43470
  }
43471
43471
  return budget;
43472
43472
  }
43473
+ function assertRouteSanityAndAdjust(route, availableAmount) {
43474
+ if (!route.amount) {
43475
+ throw new Error(`Invalid route given to assertRouteSanityAndAdjust: ${JSON.stringify(route)}`);
43476
+ }
43477
+ const routeAmount = route.amount.toNumber();
43478
+ if (routeAmount <= availableAmount) {
43479
+ return route;
43480
+ }
43481
+ if (routeAmount - availableAmount <= CASE_THRESHOLD_USD) {
43482
+ return { ...route, amount: new Web3Number(availableAmount, route.amount.decimals) };
43483
+ }
43484
+ throw new Error(`Route amount ${routeAmount} exceeds available amount ${availableAmount}, Route name: ${route.type}`);
43485
+ }
43486
+ function assertVesuMultiplyRouteSanityAndAdjust(route, availableAmount, state, isIncrease) {
43487
+ const btcPrice = state.vesuPoolState.collateralPrice;
43488
+ const totalCollateralAmount = isIncrease ? route.marginAmount.plus(route.swappedCollateralAmount).plus(state.vesuPoolState.collateralAmount) : state.vesuPoolState.collateralAmount.minus(route.marginAmount.plus(route.swappedCollateralAmount));
43489
+ const totalDebtAmount = isIncrease ? route.debtAmount.plus(state.vesuPoolState.debtAmount) : state.vesuPoolState.debtAmount.minus(route.debtAmount);
43490
+ const newHF = HealthFactorMath.getHealthFactor(
43491
+ totalCollateralAmount,
43492
+ btcPrice,
43493
+ VesuConfig.maxLtv,
43494
+ totalDebtAmount,
43495
+ state.vesuPoolState.debtPrice
43496
+ );
43497
+ const idealHF = VesuConfig.maxLtv / VesuConfig.targetLtv - 0.05;
43498
+ assert(newHF >= idealHF, `SolveBudget::applyRoutesAndVerifyLtvState newHF=${newHF} < idealHF=${idealHF}`);
43499
+ }
43500
+ function applyRoutesAndVerifyLtvState(state, routes) {
43501
+ const btcPrice = state.vesuPoolState.collateralPrice;
43502
+ const adjustedRouters = [...routes];
43503
+ let index = 0;
43504
+ for (const r of routes) {
43505
+ switch (r.type) {
43506
+ case "WALLET_TO_VA" /* WALLET_TO_VA */: {
43507
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.walletUsd);
43508
+ const amt = adjustedRoute.amount.toNumber();
43509
+ state.spendWallet(amt);
43510
+ state.addToVA(amt);
43511
+ break;
43512
+ }
43513
+ case "VESU_REPAY" /* VESU_REPAY */: {
43514
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.vaRawUsd);
43515
+ const amt = Math.abs(adjustedRoute.amount.toNumber());
43516
+ state.repayVesuBorrowCapacity(amt);
43517
+ state.spendVA(amt);
43518
+ break;
43519
+ }
43520
+ case "EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */: {
43521
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.extAvailWithdraw);
43522
+ const amt = adjustedRoute.amount.toNumber();
43523
+ state.addToWallet(amt);
43524
+ state.spendExtAvailTrade(amt);
43525
+ break;
43526
+ }
43527
+ case "REALISE_PNL" /* REALISE_PNL */: {
43528
+ const amt = r.amount.toNumber();
43529
+ state.spendExtAvailUpnl(amt);
43530
+ state.addToExtAvailTrade(amt);
43531
+ break;
43532
+ }
43533
+ case "VA_TO_EXTENDED" /* VA_TO_EXTENDED */: {
43534
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.vaRawUsd);
43535
+ const amt = adjustedRoute.amount.toNumber();
43536
+ state.spendVA(amt);
43537
+ state.addToExtAvailTrade(amt);
43538
+ break;
43539
+ }
43540
+ case "WALLET_TO_EXTENDED" /* WALLET_TO_EXTENDED */: {
43541
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.walletUsd);
43542
+ const amt = adjustedRoute.amount.toNumber();
43543
+ state.spendWallet(amt);
43544
+ state.addToExtAvailTrade(amt);
43545
+ break;
43546
+ }
43547
+ case "VESU_BORROW" /* VESU_BORROW */: {
43548
+ const borrowedAmount = r.amount.toNumber();
43549
+ const currentCollateralAmount = state.vesuPoolState.collateralAmount.toNumber();
43550
+ const currentDebtAmount = state.vesuPoolState.debtAmount.toNumber();
43551
+ const maxDebtAmount = HealthFactorMath.getMaxDebtAmount(
43552
+ new Web3Number(currentCollateralAmount, state.vesuPoolState.collateralToken.decimals),
43553
+ state.vesuPoolState.collateralPrice,
43554
+ VesuConfig.maxLtv,
43555
+ VesuConfig.maxLtv / VesuConfig.targetLtv - 0.05,
43556
+ // e.g. if ideal HF is 1.4, upto 1.35 is ok.
43557
+ state.vesuPoolState.debtPrice,
43558
+ state.vesuPoolState.debtToken
43559
+ );
43560
+ const availableDebtAmount = maxDebtAmount.minus(new Web3Number(currentDebtAmount, state.vesuPoolState.debtToken.decimals));
43561
+ if (!availableDebtAmount.plus(CASE_THRESHOLD_USD).greaterThan(borrowedAmount)) {
43562
+ throw new Error(`SolveBudget::applyRoutesAndVerifyLtvState availableDebtAmount=${availableDebtAmount.toNumber()} < borrowedAmount=${borrowedAmount}`);
43563
+ }
43564
+ state.spendVesuBorrowCapacity(borrowedAmount);
43565
+ state.addToVA(borrowedAmount);
43566
+ break;
43567
+ }
43568
+ case "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */: {
43569
+ const mr = r;
43570
+ assertVesuMultiplyRouteSanityAndAdjust(mr, state.vaRawUsd, state, true);
43571
+ state.applyVesuDelta(
43572
+ mr.poolId,
43573
+ mr.collateralToken,
43574
+ mr.debtToken,
43575
+ mr.marginAmount.plus(mr.swappedCollateralAmount),
43576
+ mr.debtAmount
43577
+ );
43578
+ state.spendVA(mr.marginAmount.toNumber() * btcPrice);
43579
+ break;
43580
+ }
43581
+ case "VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */: {
43582
+ const mr = r;
43583
+ assertVesuMultiplyRouteSanityAndAdjust(mr, state.vaRawUsd, state, false);
43584
+ state.applyVesuDelta(
43585
+ mr.poolId,
43586
+ mr.collateralToken,
43587
+ mr.debtToken,
43588
+ new Web3Number(mr.marginAmount.negated().minus(mr.swappedCollateralAmount).toFixed(8), mr.marginAmount.decimals),
43589
+ mr.debtAmount
43590
+ );
43591
+ const marginBtc = mr.marginAmount.toNumber();
43592
+ if (marginBtc > 10 ** -COLLATERAL_PRECISION) {
43593
+ state.addToVA(marginBtc * btcPrice);
43594
+ }
43595
+ break;
43596
+ }
43597
+ case "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */:
43598
+ case "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */: {
43599
+ const er = r;
43600
+ state.applyExtendedExposureDelta(er.instrument, er.amount, btcPrice);
43601
+ break;
43602
+ }
43603
+ case "RETURN_TO_WAIT" /* RETURN_TO_WAIT */:
43604
+ break;
43605
+ }
43606
+ index++;
43607
+ }
43608
+ }
43473
43609
  var ExtendedSVKVesuStateManager = class {
43474
43610
  constructor(config) {
43475
43611
  this._tag = "ExtendedSVKVesuStateManager";
@@ -43488,13 +43624,19 @@ var ExtendedSVKVesuStateManager = class {
43488
43624
  * Pass 0 (default) for normal investment / rebalancing cycles.
43489
43625
  */
43490
43626
  async solve(withdrawAmount = new Web3Number(0, USDC_TOKEN_DECIMALS)) {
43491
- await this._refresh();
43627
+ const gaurdrailBudget = await this._refresh();
43492
43628
  if (Math.abs(this._budget.extPendingDeposit) > 0) {
43493
43629
  logger.warn(`${this._tag}::solve extPendingDeposit=${this._budget.extPendingDeposit}`);
43494
43630
  return null;
43495
43631
  }
43496
43632
  this._validateRefreshedState();
43497
43633
  const cases = this._classifyCases(withdrawAmount);
43634
+ for (const c of cases) {
43635
+ applyRoutesAndVerifyLtvState(gaurdrailBudget, c.routes);
43636
+ }
43637
+ const extendedPositionSize = gaurdrailBudget.extendedPositionsView[0].size.toNumber();
43638
+ const vesuPositionSize = gaurdrailBudget.vesuPoolState.collateralAmount.toNumber();
43639
+ assert(Math.abs(extendedPositionSize - vesuPositionSize) <= 1e-5, "extended positions size mismatch");
43498
43640
  const result = {
43499
43641
  cases,
43500
43642
  // ignore these fields for now. only cases are relevant.
@@ -43542,7 +43684,7 @@ var ExtendedSVKVesuStateManager = class {
43542
43684
  vaultUsdcBalance,
43543
43685
  walletBalance
43544
43686
  );
43545
- this._budget = createSolveBudgetFromRawState({
43687
+ const data = {
43546
43688
  assetToken: this._config.assetToken,
43547
43689
  usdcToken: this._config.usdcToken,
43548
43690
  unusedBalance,
@@ -43559,7 +43701,9 @@ var ExtendedSVKVesuStateManager = class {
43559
43701
  pendingDeposit: extendedBalance?.pendingDeposit || new Web3Number(0, USDC_TOKEN_DECIMALS)
43560
43702
  },
43561
43703
  vesuPoolStates
43562
- });
43704
+ };
43705
+ this._budget = createSolveBudgetFromRawState(data);
43706
+ const gaurdrailBudget = createSolveBudgetFromRawState({ ...data });
43563
43707
  this._budget.logStateSummary();
43564
43708
  const totalUnusedUsd = unusedBalance.reduce(
43565
43709
  (acc, b) => acc + b.usdValue,
@@ -43568,6 +43712,7 @@ var ExtendedSVKVesuStateManager = class {
43568
43712
  logger.info(
43569
43713
  `${this._tag}::_refresh completed \u2014 unusedBalances: ${unusedBalance.length} tokens [${unusedBalance.map((b) => `${b.token.symbol}=$${b.usdValue.toFixed(2)}`).join(", ")}], totalUnusedUsd: ${totalUnusedUsd.toFixed(2)}, extendedPositions: ${extendedPositions.length} [${extendedPositions.map((p) => `${p.instrument}=${p.size.toFixed(6)} ${p.side}, ${p.valueUsd.toFixed(6)} ${p.instrument}`).join(", ")}], vesuPools: ${vesuPoolStates.length} [${vesuPoolStates.map((p) => `${p.poolId.shortString()}=${p.debtAmount.toFixed(6)} ${p.debtToken.symbol}, ${p.collateralAmount.toFixed(6)} ${p.collateralToken.symbol}`).join(", ")}], availableForTrade: ${extendedBalance?.availableForTrade.toNumber()} - availableForWithdrawal: ${extendedBalance?.availableForWithdrawal.toNumber()} - unrealisedPnl: ${extendedBalance?.unrealisedPnl.toNumber()} - extendedBalance::balance: ${extendedBalance?.balance.toNumber()} - extendedBalance::pendingDeposit: ${extendedBalance?.pendingDeposit.toNumber()} - extendedBalance::equity: ${extendedBalance?.equity.toNumber()}`
43570
43714
  );
43715
+ return gaurdrailBudget;
43571
43716
  }
43572
43717
  // todo add communication check with python server of extended. if not working, throw error in solve function.
43573
43718
  /** True when strategy asset and USDC share one token — VA idle balance is tracked as USDC, not as asset. */
@@ -47328,7 +47473,7 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
47328
47473
  );
47329
47474
  allPositions.push({
47330
47475
  tokenInfo: this.usdcToken,
47331
- amount: new Web3Number(extendedPosition.size, 0),
47476
+ amount: new Web3Number(extendedPosition?.size || 0, 0),
47332
47477
  usdValue: Number(extendedEquity),
47333
47478
  apy: { apy: extendedApy, type: "base" /* BASE */ },
47334
47479
  remarks: "finalised" /* FINALISED */,
package/dist/index.js CHANGED
@@ -43833,6 +43833,142 @@ function createSolveBudgetFromRawState(params) {
43833
43833
  }
43834
43834
  return budget;
43835
43835
  }
43836
+ function assertRouteSanityAndAdjust(route, availableAmount) {
43837
+ if (!route.amount) {
43838
+ throw new Error(`Invalid route given to assertRouteSanityAndAdjust: ${JSON.stringify(route)}`);
43839
+ }
43840
+ const routeAmount = route.amount.toNumber();
43841
+ if (routeAmount <= availableAmount) {
43842
+ return route;
43843
+ }
43844
+ if (routeAmount - availableAmount <= CASE_THRESHOLD_USD) {
43845
+ return { ...route, amount: new Web3Number(availableAmount, route.amount.decimals) };
43846
+ }
43847
+ throw new Error(`Route amount ${routeAmount} exceeds available amount ${availableAmount}, Route name: ${route.type}`);
43848
+ }
43849
+ function assertVesuMultiplyRouteSanityAndAdjust(route, availableAmount, state, isIncrease) {
43850
+ const btcPrice = state.vesuPoolState.collateralPrice;
43851
+ const totalCollateralAmount = isIncrease ? route.marginAmount.plus(route.swappedCollateralAmount).plus(state.vesuPoolState.collateralAmount) : state.vesuPoolState.collateralAmount.minus(route.marginAmount.plus(route.swappedCollateralAmount));
43852
+ const totalDebtAmount = isIncrease ? route.debtAmount.plus(state.vesuPoolState.debtAmount) : state.vesuPoolState.debtAmount.minus(route.debtAmount);
43853
+ const newHF = HealthFactorMath.getHealthFactor(
43854
+ totalCollateralAmount,
43855
+ btcPrice,
43856
+ VesuConfig.maxLtv,
43857
+ totalDebtAmount,
43858
+ state.vesuPoolState.debtPrice
43859
+ );
43860
+ const idealHF = VesuConfig.maxLtv / VesuConfig.targetLtv - 0.05;
43861
+ assert(newHF >= idealHF, `SolveBudget::applyRoutesAndVerifyLtvState newHF=${newHF} < idealHF=${idealHF}`);
43862
+ }
43863
+ function applyRoutesAndVerifyLtvState(state, routes) {
43864
+ const btcPrice = state.vesuPoolState.collateralPrice;
43865
+ const adjustedRouters = [...routes];
43866
+ let index = 0;
43867
+ for (const r of routes) {
43868
+ switch (r.type) {
43869
+ case "WALLET_TO_VA" /* WALLET_TO_VA */: {
43870
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.walletUsd);
43871
+ const amt = adjustedRoute.amount.toNumber();
43872
+ state.spendWallet(amt);
43873
+ state.addToVA(amt);
43874
+ break;
43875
+ }
43876
+ case "VESU_REPAY" /* VESU_REPAY */: {
43877
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.vaRawUsd);
43878
+ const amt = Math.abs(adjustedRoute.amount.toNumber());
43879
+ state.repayVesuBorrowCapacity(amt);
43880
+ state.spendVA(amt);
43881
+ break;
43882
+ }
43883
+ case "EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */: {
43884
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.extAvailWithdraw);
43885
+ const amt = adjustedRoute.amount.toNumber();
43886
+ state.addToWallet(amt);
43887
+ state.spendExtAvailTrade(amt);
43888
+ break;
43889
+ }
43890
+ case "REALISE_PNL" /* REALISE_PNL */: {
43891
+ const amt = r.amount.toNumber();
43892
+ state.spendExtAvailUpnl(amt);
43893
+ state.addToExtAvailTrade(amt);
43894
+ break;
43895
+ }
43896
+ case "VA_TO_EXTENDED" /* VA_TO_EXTENDED */: {
43897
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.vaRawUsd);
43898
+ const amt = adjustedRoute.amount.toNumber();
43899
+ state.spendVA(amt);
43900
+ state.addToExtAvailTrade(amt);
43901
+ break;
43902
+ }
43903
+ case "WALLET_TO_EXTENDED" /* WALLET_TO_EXTENDED */: {
43904
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.walletUsd);
43905
+ const amt = adjustedRoute.amount.toNumber();
43906
+ state.spendWallet(amt);
43907
+ state.addToExtAvailTrade(amt);
43908
+ break;
43909
+ }
43910
+ case "VESU_BORROW" /* VESU_BORROW */: {
43911
+ const borrowedAmount = r.amount.toNumber();
43912
+ const currentCollateralAmount = state.vesuPoolState.collateralAmount.toNumber();
43913
+ const currentDebtAmount = state.vesuPoolState.debtAmount.toNumber();
43914
+ const maxDebtAmount = HealthFactorMath.getMaxDebtAmount(
43915
+ new Web3Number(currentCollateralAmount, state.vesuPoolState.collateralToken.decimals),
43916
+ state.vesuPoolState.collateralPrice,
43917
+ VesuConfig.maxLtv,
43918
+ VesuConfig.maxLtv / VesuConfig.targetLtv - 0.05,
43919
+ // e.g. if ideal HF is 1.4, upto 1.35 is ok.
43920
+ state.vesuPoolState.debtPrice,
43921
+ state.vesuPoolState.debtToken
43922
+ );
43923
+ const availableDebtAmount = maxDebtAmount.minus(new Web3Number(currentDebtAmount, state.vesuPoolState.debtToken.decimals));
43924
+ if (!availableDebtAmount.plus(CASE_THRESHOLD_USD).greaterThan(borrowedAmount)) {
43925
+ throw new Error(`SolveBudget::applyRoutesAndVerifyLtvState availableDebtAmount=${availableDebtAmount.toNumber()} < borrowedAmount=${borrowedAmount}`);
43926
+ }
43927
+ state.spendVesuBorrowCapacity(borrowedAmount);
43928
+ state.addToVA(borrowedAmount);
43929
+ break;
43930
+ }
43931
+ case "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */: {
43932
+ const mr = r;
43933
+ assertVesuMultiplyRouteSanityAndAdjust(mr, state.vaRawUsd, state, true);
43934
+ state.applyVesuDelta(
43935
+ mr.poolId,
43936
+ mr.collateralToken,
43937
+ mr.debtToken,
43938
+ mr.marginAmount.plus(mr.swappedCollateralAmount),
43939
+ mr.debtAmount
43940
+ );
43941
+ state.spendVA(mr.marginAmount.toNumber() * btcPrice);
43942
+ break;
43943
+ }
43944
+ case "VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */: {
43945
+ const mr = r;
43946
+ assertVesuMultiplyRouteSanityAndAdjust(mr, state.vaRawUsd, state, false);
43947
+ state.applyVesuDelta(
43948
+ mr.poolId,
43949
+ mr.collateralToken,
43950
+ mr.debtToken,
43951
+ new Web3Number(mr.marginAmount.negated().minus(mr.swappedCollateralAmount).toFixed(8), mr.marginAmount.decimals),
43952
+ mr.debtAmount
43953
+ );
43954
+ const marginBtc = mr.marginAmount.toNumber();
43955
+ if (marginBtc > 10 ** -COLLATERAL_PRECISION) {
43956
+ state.addToVA(marginBtc * btcPrice);
43957
+ }
43958
+ break;
43959
+ }
43960
+ case "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */:
43961
+ case "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */: {
43962
+ const er = r;
43963
+ state.applyExtendedExposureDelta(er.instrument, er.amount, btcPrice);
43964
+ break;
43965
+ }
43966
+ case "RETURN_TO_WAIT" /* RETURN_TO_WAIT */:
43967
+ break;
43968
+ }
43969
+ index++;
43970
+ }
43971
+ }
43836
43972
  var ExtendedSVKVesuStateManager = class {
43837
43973
  constructor(config) {
43838
43974
  this._tag = "ExtendedSVKVesuStateManager";
@@ -43851,13 +43987,19 @@ var ExtendedSVKVesuStateManager = class {
43851
43987
  * Pass 0 (default) for normal investment / rebalancing cycles.
43852
43988
  */
43853
43989
  async solve(withdrawAmount = new Web3Number(0, USDC_TOKEN_DECIMALS)) {
43854
- await this._refresh();
43990
+ const gaurdrailBudget = await this._refresh();
43855
43991
  if (Math.abs(this._budget.extPendingDeposit) > 0) {
43856
43992
  logger.warn(`${this._tag}::solve extPendingDeposit=${this._budget.extPendingDeposit}`);
43857
43993
  return null;
43858
43994
  }
43859
43995
  this._validateRefreshedState();
43860
43996
  const cases = this._classifyCases(withdrawAmount);
43997
+ for (const c of cases) {
43998
+ applyRoutesAndVerifyLtvState(gaurdrailBudget, c.routes);
43999
+ }
44000
+ const extendedPositionSize = gaurdrailBudget.extendedPositionsView[0].size.toNumber();
44001
+ const vesuPositionSize = gaurdrailBudget.vesuPoolState.collateralAmount.toNumber();
44002
+ assert(Math.abs(extendedPositionSize - vesuPositionSize) <= 1e-5, "extended positions size mismatch");
43861
44003
  const result = {
43862
44004
  cases,
43863
44005
  // ignore these fields for now. only cases are relevant.
@@ -43905,7 +44047,7 @@ var ExtendedSVKVesuStateManager = class {
43905
44047
  vaultUsdcBalance,
43906
44048
  walletBalance
43907
44049
  );
43908
- this._budget = createSolveBudgetFromRawState({
44050
+ const data = {
43909
44051
  assetToken: this._config.assetToken,
43910
44052
  usdcToken: this._config.usdcToken,
43911
44053
  unusedBalance,
@@ -43922,7 +44064,9 @@ var ExtendedSVKVesuStateManager = class {
43922
44064
  pendingDeposit: extendedBalance?.pendingDeposit || new Web3Number(0, USDC_TOKEN_DECIMALS)
43923
44065
  },
43924
44066
  vesuPoolStates
43925
- });
44067
+ };
44068
+ this._budget = createSolveBudgetFromRawState(data);
44069
+ const gaurdrailBudget = createSolveBudgetFromRawState({ ...data });
43926
44070
  this._budget.logStateSummary();
43927
44071
  const totalUnusedUsd = unusedBalance.reduce(
43928
44072
  (acc, b) => acc + b.usdValue,
@@ -43931,6 +44075,7 @@ var ExtendedSVKVesuStateManager = class {
43931
44075
  logger.info(
43932
44076
  `${this._tag}::_refresh completed \u2014 unusedBalances: ${unusedBalance.length} tokens [${unusedBalance.map((b) => `${b.token.symbol}=$${b.usdValue.toFixed(2)}`).join(", ")}], totalUnusedUsd: ${totalUnusedUsd.toFixed(2)}, extendedPositions: ${extendedPositions.length} [${extendedPositions.map((p) => `${p.instrument}=${p.size.toFixed(6)} ${p.side}, ${p.valueUsd.toFixed(6)} ${p.instrument}`).join(", ")}], vesuPools: ${vesuPoolStates.length} [${vesuPoolStates.map((p) => `${p.poolId.shortString()}=${p.debtAmount.toFixed(6)} ${p.debtToken.symbol}, ${p.collateralAmount.toFixed(6)} ${p.collateralToken.symbol}`).join(", ")}], availableForTrade: ${extendedBalance?.availableForTrade.toNumber()} - availableForWithdrawal: ${extendedBalance?.availableForWithdrawal.toNumber()} - unrealisedPnl: ${extendedBalance?.unrealisedPnl.toNumber()} - extendedBalance::balance: ${extendedBalance?.balance.toNumber()} - extendedBalance::pendingDeposit: ${extendedBalance?.pendingDeposit.toNumber()} - extendedBalance::equity: ${extendedBalance?.equity.toNumber()}`
43933
44077
  );
44078
+ return gaurdrailBudget;
43934
44079
  }
43935
44080
  // todo add communication check with python server of extended. if not working, throw error in solve function.
43936
44081
  /** True when strategy asset and USDC share one token — VA idle balance is tracked as USDC, not as asset. */
@@ -47691,7 +47836,7 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
47691
47836
  );
47692
47837
  allPositions.push({
47693
47838
  tokenInfo: this.usdcToken,
47694
- amount: new Web3Number(extendedPosition.size, 0),
47839
+ amount: new Web3Number(extendedPosition?.size || 0, 0),
47695
47840
  usdValue: Number(extendedEquity),
47696
47841
  apy: { apy: extendedApy, type: "base" /* BASE */ },
47697
47842
  remarks: "finalised" /* FINALISED */,
package/dist/index.mjs CHANGED
@@ -43635,6 +43635,142 @@ function createSolveBudgetFromRawState(params) {
43635
43635
  }
43636
43636
  return budget;
43637
43637
  }
43638
+ function assertRouteSanityAndAdjust(route, availableAmount) {
43639
+ if (!route.amount) {
43640
+ throw new Error(`Invalid route given to assertRouteSanityAndAdjust: ${JSON.stringify(route)}`);
43641
+ }
43642
+ const routeAmount = route.amount.toNumber();
43643
+ if (routeAmount <= availableAmount) {
43644
+ return route;
43645
+ }
43646
+ if (routeAmount - availableAmount <= CASE_THRESHOLD_USD) {
43647
+ return { ...route, amount: new Web3Number(availableAmount, route.amount.decimals) };
43648
+ }
43649
+ throw new Error(`Route amount ${routeAmount} exceeds available amount ${availableAmount}, Route name: ${route.type}`);
43650
+ }
43651
+ function assertVesuMultiplyRouteSanityAndAdjust(route, availableAmount, state, isIncrease) {
43652
+ const btcPrice = state.vesuPoolState.collateralPrice;
43653
+ const totalCollateralAmount = isIncrease ? route.marginAmount.plus(route.swappedCollateralAmount).plus(state.vesuPoolState.collateralAmount) : state.vesuPoolState.collateralAmount.minus(route.marginAmount.plus(route.swappedCollateralAmount));
43654
+ const totalDebtAmount = isIncrease ? route.debtAmount.plus(state.vesuPoolState.debtAmount) : state.vesuPoolState.debtAmount.minus(route.debtAmount);
43655
+ const newHF = HealthFactorMath.getHealthFactor(
43656
+ totalCollateralAmount,
43657
+ btcPrice,
43658
+ VesuConfig.maxLtv,
43659
+ totalDebtAmount,
43660
+ state.vesuPoolState.debtPrice
43661
+ );
43662
+ const idealHF = VesuConfig.maxLtv / VesuConfig.targetLtv - 0.05;
43663
+ assert(newHF >= idealHF, `SolveBudget::applyRoutesAndVerifyLtvState newHF=${newHF} < idealHF=${idealHF}`);
43664
+ }
43665
+ function applyRoutesAndVerifyLtvState(state, routes) {
43666
+ const btcPrice = state.vesuPoolState.collateralPrice;
43667
+ const adjustedRouters = [...routes];
43668
+ let index = 0;
43669
+ for (const r of routes) {
43670
+ switch (r.type) {
43671
+ case "WALLET_TO_VA" /* WALLET_TO_VA */: {
43672
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.walletUsd);
43673
+ const amt = adjustedRoute.amount.toNumber();
43674
+ state.spendWallet(amt);
43675
+ state.addToVA(amt);
43676
+ break;
43677
+ }
43678
+ case "VESU_REPAY" /* VESU_REPAY */: {
43679
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.vaRawUsd);
43680
+ const amt = Math.abs(adjustedRoute.amount.toNumber());
43681
+ state.repayVesuBorrowCapacity(amt);
43682
+ state.spendVA(amt);
43683
+ break;
43684
+ }
43685
+ case "EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */: {
43686
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.extAvailWithdraw);
43687
+ const amt = adjustedRoute.amount.toNumber();
43688
+ state.addToWallet(amt);
43689
+ state.spendExtAvailTrade(amt);
43690
+ break;
43691
+ }
43692
+ case "REALISE_PNL" /* REALISE_PNL */: {
43693
+ const amt = r.amount.toNumber();
43694
+ state.spendExtAvailUpnl(amt);
43695
+ state.addToExtAvailTrade(amt);
43696
+ break;
43697
+ }
43698
+ case "VA_TO_EXTENDED" /* VA_TO_EXTENDED */: {
43699
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.vaRawUsd);
43700
+ const amt = adjustedRoute.amount.toNumber();
43701
+ state.spendVA(amt);
43702
+ state.addToExtAvailTrade(amt);
43703
+ break;
43704
+ }
43705
+ case "WALLET_TO_EXTENDED" /* WALLET_TO_EXTENDED */: {
43706
+ const adjustedRoute = assertRouteSanityAndAdjust(r, state.walletUsd);
43707
+ const amt = adjustedRoute.amount.toNumber();
43708
+ state.spendWallet(amt);
43709
+ state.addToExtAvailTrade(amt);
43710
+ break;
43711
+ }
43712
+ case "VESU_BORROW" /* VESU_BORROW */: {
43713
+ const borrowedAmount = r.amount.toNumber();
43714
+ const currentCollateralAmount = state.vesuPoolState.collateralAmount.toNumber();
43715
+ const currentDebtAmount = state.vesuPoolState.debtAmount.toNumber();
43716
+ const maxDebtAmount = HealthFactorMath.getMaxDebtAmount(
43717
+ new Web3Number(currentCollateralAmount, state.vesuPoolState.collateralToken.decimals),
43718
+ state.vesuPoolState.collateralPrice,
43719
+ VesuConfig.maxLtv,
43720
+ VesuConfig.maxLtv / VesuConfig.targetLtv - 0.05,
43721
+ // e.g. if ideal HF is 1.4, upto 1.35 is ok.
43722
+ state.vesuPoolState.debtPrice,
43723
+ state.vesuPoolState.debtToken
43724
+ );
43725
+ const availableDebtAmount = maxDebtAmount.minus(new Web3Number(currentDebtAmount, state.vesuPoolState.debtToken.decimals));
43726
+ if (!availableDebtAmount.plus(CASE_THRESHOLD_USD).greaterThan(borrowedAmount)) {
43727
+ throw new Error(`SolveBudget::applyRoutesAndVerifyLtvState availableDebtAmount=${availableDebtAmount.toNumber()} < borrowedAmount=${borrowedAmount}`);
43728
+ }
43729
+ state.spendVesuBorrowCapacity(borrowedAmount);
43730
+ state.addToVA(borrowedAmount);
43731
+ break;
43732
+ }
43733
+ case "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */: {
43734
+ const mr = r;
43735
+ assertVesuMultiplyRouteSanityAndAdjust(mr, state.vaRawUsd, state, true);
43736
+ state.applyVesuDelta(
43737
+ mr.poolId,
43738
+ mr.collateralToken,
43739
+ mr.debtToken,
43740
+ mr.marginAmount.plus(mr.swappedCollateralAmount),
43741
+ mr.debtAmount
43742
+ );
43743
+ state.spendVA(mr.marginAmount.toNumber() * btcPrice);
43744
+ break;
43745
+ }
43746
+ case "VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */: {
43747
+ const mr = r;
43748
+ assertVesuMultiplyRouteSanityAndAdjust(mr, state.vaRawUsd, state, false);
43749
+ state.applyVesuDelta(
43750
+ mr.poolId,
43751
+ mr.collateralToken,
43752
+ mr.debtToken,
43753
+ new Web3Number(mr.marginAmount.negated().minus(mr.swappedCollateralAmount).toFixed(8), mr.marginAmount.decimals),
43754
+ mr.debtAmount
43755
+ );
43756
+ const marginBtc = mr.marginAmount.toNumber();
43757
+ if (marginBtc > 10 ** -COLLATERAL_PRECISION) {
43758
+ state.addToVA(marginBtc * btcPrice);
43759
+ }
43760
+ break;
43761
+ }
43762
+ case "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */:
43763
+ case "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */: {
43764
+ const er = r;
43765
+ state.applyExtendedExposureDelta(er.instrument, er.amount, btcPrice);
43766
+ break;
43767
+ }
43768
+ case "RETURN_TO_WAIT" /* RETURN_TO_WAIT */:
43769
+ break;
43770
+ }
43771
+ index++;
43772
+ }
43773
+ }
43638
43774
  var ExtendedSVKVesuStateManager = class {
43639
43775
  constructor(config) {
43640
43776
  this._tag = "ExtendedSVKVesuStateManager";
@@ -43653,13 +43789,19 @@ var ExtendedSVKVesuStateManager = class {
43653
43789
  * Pass 0 (default) for normal investment / rebalancing cycles.
43654
43790
  */
43655
43791
  async solve(withdrawAmount = new Web3Number(0, USDC_TOKEN_DECIMALS)) {
43656
- await this._refresh();
43792
+ const gaurdrailBudget = await this._refresh();
43657
43793
  if (Math.abs(this._budget.extPendingDeposit) > 0) {
43658
43794
  logger.warn(`${this._tag}::solve extPendingDeposit=${this._budget.extPendingDeposit}`);
43659
43795
  return null;
43660
43796
  }
43661
43797
  this._validateRefreshedState();
43662
43798
  const cases = this._classifyCases(withdrawAmount);
43799
+ for (const c of cases) {
43800
+ applyRoutesAndVerifyLtvState(gaurdrailBudget, c.routes);
43801
+ }
43802
+ const extendedPositionSize = gaurdrailBudget.extendedPositionsView[0].size.toNumber();
43803
+ const vesuPositionSize = gaurdrailBudget.vesuPoolState.collateralAmount.toNumber();
43804
+ assert(Math.abs(extendedPositionSize - vesuPositionSize) <= 1e-5, "extended positions size mismatch");
43663
43805
  const result = {
43664
43806
  cases,
43665
43807
  // ignore these fields for now. only cases are relevant.
@@ -43707,7 +43849,7 @@ var ExtendedSVKVesuStateManager = class {
43707
43849
  vaultUsdcBalance,
43708
43850
  walletBalance
43709
43851
  );
43710
- this._budget = createSolveBudgetFromRawState({
43852
+ const data = {
43711
43853
  assetToken: this._config.assetToken,
43712
43854
  usdcToken: this._config.usdcToken,
43713
43855
  unusedBalance,
@@ -43724,7 +43866,9 @@ var ExtendedSVKVesuStateManager = class {
43724
43866
  pendingDeposit: extendedBalance?.pendingDeposit || new Web3Number(0, USDC_TOKEN_DECIMALS)
43725
43867
  },
43726
43868
  vesuPoolStates
43727
- });
43869
+ };
43870
+ this._budget = createSolveBudgetFromRawState(data);
43871
+ const gaurdrailBudget = createSolveBudgetFromRawState({ ...data });
43728
43872
  this._budget.logStateSummary();
43729
43873
  const totalUnusedUsd = unusedBalance.reduce(
43730
43874
  (acc, b) => acc + b.usdValue,
@@ -43733,6 +43877,7 @@ var ExtendedSVKVesuStateManager = class {
43733
43877
  logger.info(
43734
43878
  `${this._tag}::_refresh completed \u2014 unusedBalances: ${unusedBalance.length} tokens [${unusedBalance.map((b) => `${b.token.symbol}=$${b.usdValue.toFixed(2)}`).join(", ")}], totalUnusedUsd: ${totalUnusedUsd.toFixed(2)}, extendedPositions: ${extendedPositions.length} [${extendedPositions.map((p) => `${p.instrument}=${p.size.toFixed(6)} ${p.side}, ${p.valueUsd.toFixed(6)} ${p.instrument}`).join(", ")}], vesuPools: ${vesuPoolStates.length} [${vesuPoolStates.map((p) => `${p.poolId.shortString()}=${p.debtAmount.toFixed(6)} ${p.debtToken.symbol}, ${p.collateralAmount.toFixed(6)} ${p.collateralToken.symbol}`).join(", ")}], availableForTrade: ${extendedBalance?.availableForTrade.toNumber()} - availableForWithdrawal: ${extendedBalance?.availableForWithdrawal.toNumber()} - unrealisedPnl: ${extendedBalance?.unrealisedPnl.toNumber()} - extendedBalance::balance: ${extendedBalance?.balance.toNumber()} - extendedBalance::pendingDeposit: ${extendedBalance?.pendingDeposit.toNumber()} - extendedBalance::equity: ${extendedBalance?.equity.toNumber()}`
43735
43879
  );
43880
+ return gaurdrailBudget;
43736
43881
  }
43737
43882
  // todo add communication check with python server of extended. if not working, throw error in solve function.
43738
43883
  /** True when strategy asset and USDC share one token — VA idle balance is tracked as USDC, not as asset. */
@@ -47493,7 +47638,7 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
47493
47638
  );
47494
47639
  allPositions.push({
47495
47640
  tokenInfo: this.usdcToken,
47496
- amount: new Web3Number(extendedPosition.size, 0),
47641
+ amount: new Web3Number(extendedPosition?.size || 0, 0),
47497
47642
  usdValue: Number(extendedEquity),
47498
47643
  apy: { apy: extendedApy, type: "base" /* BASE */ },
47499
47644
  remarks: "finalised" /* FINALISED */,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strkfarm/sdk",
3
- "version": "2.0.0-dev.32",
3
+ "version": "2.0.0-dev.34",
4
4
  "description": "STRKFarm TS SDK (Meant for our internal use, but feel free to use it)",
5
5
  "typings": "dist/index.d.ts",
6
6
  "types": "dist/index.d.ts",