@strkfarm/sdk 2.0.0-dev.28 → 2.0.0-dev.29

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/dist/index.js CHANGED
@@ -50,11 +50,13 @@ __export(index_exports, {
50
50
  BaseAdapter: () => BaseAdapter,
51
51
  BaseStrategy: () => BaseStrategy,
52
52
  CASE_ROUTE_TYPES: () => CASE_ROUTE_TYPES,
53
+ COLLATERAL_PRECISION: () => COLLATERAL_PRECISION,
53
54
  CaseCategory: () => CaseCategory,
54
55
  CaseId: () => CaseId,
55
56
  CommonAdapter: () => CommonAdapter,
56
57
  ContractAddr: () => ContractAddr,
57
58
  CycleType: () => CycleType,
59
+ DEFAULT_TROVES_STRATEGIES_API: () => DEFAULT_TROVES_STRATEGIES_API,
58
60
  Deployer: () => deployer_default,
59
61
  ERC20: () => ERC20,
60
62
  EXTENDED_CONTRACT: () => EXTENDED_CONTRACT,
@@ -73,6 +75,7 @@ __export(index_exports, {
73
75
  FatalError: () => FatalError,
74
76
  FlowChartColors: () => FlowChartColors,
75
77
  Global: () => Global,
78
+ HealthFactorMath: () => HealthFactorMath,
76
79
  HyperLSTStrategies: () => HyperLSTStrategies,
77
80
  ILending: () => ILending,
78
81
  Initializable: () => Initializable,
@@ -113,6 +116,7 @@ __export(index_exports, {
113
116
  StrategyLiveStatus: () => StrategyLiveStatus,
114
117
  StrategyTag: () => StrategyTag,
115
118
  StrategyType: () => StrategyType,
119
+ SvkTrovesAdapter: () => SvkTrovesAdapter,
116
120
  TRANSFER_SANITIZER: () => TRANSFER_SANITIZER,
117
121
  TelegramGroupNotif: () => TelegramGroupNotif,
118
122
  TelegramNotif: () => TelegramNotif,
@@ -162,6 +166,7 @@ __export(index_exports, {
162
166
  createEkuboCLStrategy: () => createEkuboCLStrategy,
163
167
  createHyperLSTStrategy: () => createHyperLSTStrategy,
164
168
  createSenseiStrategy: () => createSenseiStrategy,
169
+ createSolveBudgetFromRawState: () => createSolveBudgetFromRawState,
165
170
  createStrategy: () => createStrategy,
166
171
  createUniversalStrategy: () => createUniversalStrategy2,
167
172
  createVesuRebalanceStrategy: () => createVesuRebalanceStrategy2,
@@ -291,6 +296,9 @@ var Web3Number = class _Web3Number2 extends _Web3Number {
291
296
  const bn = new _Web3Number2(weiNumber, decimals).dividedBy(10 ** decimals);
292
297
  return new _Web3Number2(bn.toString(), decimals);
293
298
  }
299
+ static fromNumber(number, decimals) {
300
+ return new _Web3Number2(number.toString(), decimals);
301
+ }
294
302
  [import_util.default.inspect.custom](depth, opts) {
295
303
  return this.toString();
296
304
  }
@@ -1884,6 +1892,48 @@ _StarknetCallParser.METHOD_BY_SELECTOR = new Map(
1884
1892
  );
1885
1893
  var StarknetCallParser = _StarknetCallParser;
1886
1894
 
1895
+ // src/utils/health-factor-math.ts
1896
+ var HealthFactorMath = class {
1897
+ static getCollateralRequired(debtAmount, debtPrice, targetHF, maxLTV, collateralPrice, collateralTokenInfo) {
1898
+ const numerator = debtAmount.multipliedBy(debtPrice).multipliedBy(targetHF);
1899
+ const denominator = collateralPrice * maxLTV;
1900
+ const collateralAmount = numerator.dividedBy(denominator);
1901
+ const netCollateral = new Web3Number(collateralAmount.toString(), collateralTokenInfo.decimals);
1902
+ return netCollateral;
1903
+ }
1904
+ static getMinCollateralRequiredOnLooping(debtAmount, debtPrice, targetHF, maxLTV, collateralPrice, collateralTokenInfo) {
1905
+ const netCollateral = this.getCollateralRequired(debtAmount, debtPrice, targetHF, maxLTV, collateralPrice, collateralTokenInfo);
1906
+ const collateralFromDebt = new Web3Number(debtAmount.multipliedBy(debtPrice).dividedBy(collateralPrice).toString(), collateralTokenInfo.decimals);
1907
+ return netCollateral.minus(collateralFromDebt);
1908
+ }
1909
+ static getHealthFactor(collateralAmount, collateralPrice, maxLTV, debtAmount, debtPrice) {
1910
+ const numerator = collateralAmount.multipliedBy(collateralPrice).multipliedBy(maxLTV);
1911
+ const denominator = debtAmount.multipliedBy(debtPrice);
1912
+ const healthFactor = numerator.dividedBy(denominator);
1913
+ return healthFactor.toNumber();
1914
+ }
1915
+ static getMaxDebtAmountOnLooping(collateralAmount, collateralPrice, maxLTV, targetHF, debtPrice, debtTokenInfo) {
1916
+ const numerator = collateralAmount.multipliedBy(collateralPrice).multipliedBy(maxLTV);
1917
+ logger.verbose(`HealthFactorMath: Max debt amount on looping numerator: ${numerator.toNumber()}, collateralAmount: ${collateralAmount.toNumber()}, collateralPrice: ${collateralPrice}, maxLTV: ${maxLTV}, targetHF: ${targetHF}, debtPrice: ${debtPrice}`);
1918
+ const denominator = targetHF - maxLTV;
1919
+ logger.verbose(`HealthFactorMath: Max debt amount on looping denominator: ${denominator}`);
1920
+ const debtAmountUSD = numerator.dividedBy(denominator);
1921
+ logger.verbose(`HealthFactorMath: Max debt amount on looping debtAmountUSD: ${debtAmountUSD.toNumber()}`);
1922
+ const debtAmount = debtAmountUSD.dividedBy(debtPrice);
1923
+ logger.verbose(`HealthFactorMath: Max debt amount on looping debtAmount: ${debtAmount.toNumber()}`);
1924
+ return new Web3Number(debtAmount.toString(), debtTokenInfo.decimals);
1925
+ }
1926
+ static getMaxDebtAmount(collateralAmount, collateralPrice, maxLTV, targetHF, debtPrice, debtTokenInfo) {
1927
+ const numerator = collateralAmount.multipliedBy(collateralPrice).multipliedBy(maxLTV);
1928
+ logger.verbose(`HealthFactorMath: Max debt amount numerator: ${numerator.toNumber()}, collateralAmount: ${collateralAmount.toNumber()}, collateralPrice: ${collateralPrice}, maxLTV: ${maxLTV}, targetHF: ${targetHF}, debtPrice: ${debtPrice}`);
1929
+ const denominator = targetHF * debtPrice;
1930
+ logger.verbose(`HealthFactorMath: Max debt amount denominator: ${denominator}, targetHF: ${targetHF}, debtPrice: ${debtPrice}`);
1931
+ const debtAmount = numerator.dividedBy(denominator);
1932
+ logger.verbose(`HealthFactorMath: Max debt amount: ${debtAmount.toNumber()}, numerator: ${numerator.toNumber()}, denominator: ${denominator}`);
1933
+ return new Web3Number(debtAmount.toString(), debtTokenInfo.decimals);
1934
+ }
1935
+ };
1936
+
1887
1937
  // src/utils/index.ts
1888
1938
  function assert(condition, message) {
1889
1939
  if (!condition) {
@@ -4283,6 +4333,9 @@ var Web3Number2 = class _Web3Number2 extends _Web3Number {
4283
4333
  const bn = new _Web3Number2(weiNumber, decimals).dividedBy(10 ** decimals);
4284
4334
  return new _Web3Number2(bn.toString(), decimals);
4285
4335
  }
4336
+ static fromNumber(number, decimals) {
4337
+ return new _Web3Number2(number.toString(), decimals);
4338
+ }
4286
4339
  };
4287
4340
 
4288
4341
  // src/interfaces/lending.ts
@@ -6457,6 +6510,10 @@ var VaultProtocol = {
6457
6510
  name: "Vault",
6458
6511
  logo: ""
6459
6512
  };
6513
+ var TrovesProtocol = {
6514
+ name: "Troves",
6515
+ logo: "https://app.troves.fi/favicon.ico"
6516
+ };
6460
6517
  var Protocols = {
6461
6518
  NONE: NoneProtocol,
6462
6519
  VESU: VesuProtocol,
@@ -6464,7 +6521,8 @@ var Protocols = {
6464
6521
  EXTENDED: ExtendedProtocol,
6465
6522
  EKUBO: EkuboProtocol,
6466
6523
  AVNU: AvnuProtocol,
6467
- VAULT: VaultProtocol
6524
+ VAULT: VaultProtocol,
6525
+ TROVES: TrovesProtocol
6468
6526
  };
6469
6527
 
6470
6528
  // src/interfaces/initializable.ts
@@ -34195,48 +34253,6 @@ var AbisConfig = {
34195
34253
  }
34196
34254
  };
34197
34255
 
34198
- // src/utils/health-factor-math.ts
34199
- var HealthFactorMath = class {
34200
- static getCollateralRequired(debtAmount, debtPrice, targetHF, maxLTV, collateralPrice, collateralTokenInfo) {
34201
- const numerator = debtAmount.multipliedBy(debtPrice).multipliedBy(targetHF);
34202
- const denominator = collateralPrice * maxLTV;
34203
- const collateralAmount = numerator.dividedBy(denominator);
34204
- const netCollateral = new Web3Number(collateralAmount.toString(), collateralTokenInfo.decimals);
34205
- return netCollateral;
34206
- }
34207
- static getMinCollateralRequiredOnLooping(debtAmount, debtPrice, targetHF, maxLTV, collateralPrice, collateralTokenInfo) {
34208
- const netCollateral = this.getCollateralRequired(debtAmount, debtPrice, targetHF, maxLTV, collateralPrice, collateralTokenInfo);
34209
- const collateralFromDebt = new Web3Number(debtAmount.multipliedBy(debtPrice).dividedBy(collateralPrice).toString(), collateralTokenInfo.decimals);
34210
- return netCollateral.minus(collateralFromDebt);
34211
- }
34212
- static getHealthFactor(collateralAmount, collateralPrice, maxLTV, debtAmount, debtPrice) {
34213
- const numerator = collateralAmount.multipliedBy(collateralPrice).multipliedBy(maxLTV);
34214
- const denominator = debtAmount.multipliedBy(debtPrice);
34215
- const healthFactor = numerator.dividedBy(denominator);
34216
- return healthFactor.toNumber();
34217
- }
34218
- static getMaxDebtAmountOnLooping(collateralAmount, collateralPrice, maxLTV, targetHF, debtPrice, debtTokenInfo) {
34219
- const numerator = collateralAmount.multipliedBy(collateralPrice).multipliedBy(maxLTV);
34220
- logger.verbose(`HealthFactorMath: Max debt amount on looping numerator: ${numerator.toNumber()}, collateralAmount: ${collateralAmount.toNumber()}, collateralPrice: ${collateralPrice}, maxLTV: ${maxLTV}, targetHF: ${targetHF}, debtPrice: ${debtPrice}`);
34221
- const denominator = targetHF - maxLTV;
34222
- logger.verbose(`HealthFactorMath: Max debt amount on looping denominator: ${denominator}`);
34223
- const debtAmountUSD = numerator.dividedBy(denominator);
34224
- logger.verbose(`HealthFactorMath: Max debt amount on looping debtAmountUSD: ${debtAmountUSD.toNumber()}`);
34225
- const debtAmount = debtAmountUSD.dividedBy(debtPrice);
34226
- logger.verbose(`HealthFactorMath: Max debt amount on looping debtAmount: ${debtAmount.toNumber()}`);
34227
- return new Web3Number(debtAmount.toString(), debtTokenInfo.decimals);
34228
- }
34229
- static getMaxDebtAmount(collateralAmount, collateralPrice, maxLTV, targetHF, debtPrice, debtTokenInfo) {
34230
- const numerator = collateralAmount.multipliedBy(collateralPrice).multipliedBy(maxLTV);
34231
- logger.verbose(`HealthFactorMath: Max debt amount numerator: ${numerator.toNumber()}, collateralAmount: ${collateralAmount.toNumber()}, collateralPrice: ${collateralPrice}, maxLTV: ${maxLTV}, targetHF: ${targetHF}, debtPrice: ${debtPrice}`);
34232
- const denominator = targetHF * debtPrice;
34233
- logger.verbose(`HealthFactorMath: Max debt amount denominator: ${denominator}, targetHF: ${targetHF}, debtPrice: ${debtPrice}`);
34234
- const debtAmount = numerator.dividedBy(denominator);
34235
- logger.verbose(`HealthFactorMath: Max debt amount: ${debtAmount.toNumber()}, numerator: ${numerator.toNumber()}, denominator: ${denominator}`);
34236
- return new Web3Number(debtAmount.toString(), debtTokenInfo.decimals);
34237
- }
34238
- };
34239
-
34240
34256
  // src/strategies/vesu-extended-strategy/utils/helper.ts
34241
34257
  var returnFormattedAmount = (amount, toTokenDecimals) => {
34242
34258
  const formattedAmount = "0x" + BigInt(Math.floor(amount * 10 ** toTokenDecimals)).toString(16);
@@ -34879,6 +34895,7 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
34879
34895
  const collateralToken = this.config.collateral;
34880
34896
  const debtToken = this.config.debt;
34881
34897
  const { contract: multiplyContract } = this._getMultiplyContract();
34898
+ this.lastSwapPriceInfo = null;
34882
34899
  const {
34883
34900
  existingCollateralInfo,
34884
34901
  existingDebtInfo,
@@ -34915,9 +34932,15 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
34915
34932
  let marginSwapLimitAmount = Web3Number.fromWei(0, collateralToken.decimals);
34916
34933
  let addedCollateral = params.amount;
34917
34934
  let approveAmount = params.amount;
34935
+ let aggregatedFromAmount = 0;
34936
+ let aggregatedToAmount = 0;
34937
+ let aggregatedFromSymbol = debtToken.symbol;
34938
+ const aggregatedToSymbol = collateralToken.symbol;
34939
+ let executedSwapCount = 0;
34918
34940
  if (params.marginSwap) {
34919
34941
  const marginToken = params.marginSwap.marginToken;
34920
34942
  const requiredAmount = params.amount;
34943
+ assert(marginToken.address.eq(debtToken.address), "Margin token must be the same as debt token");
34921
34944
  const marginSwapQuote = await ekuboQuoter.getQuoteExactOutput(
34922
34945
  marginToken.address.address,
34923
34946
  collateralToken.address.address,
@@ -34933,6 +34956,11 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
34933
34956
  marginToken,
34934
34957
  collateralToken
34935
34958
  );
34959
+ const marginSwapInputAmount = Web3Number.fromWei(marginSwapQuote.total_calculated, marginToken.decimals).abs().toNumber();
34960
+ const marginSwapOutputAmount = requiredAmount.abs().toNumber();
34961
+ aggregatedFromAmount += marginSwapInputAmount;
34962
+ aggregatedToAmount += marginSwapOutputAmount;
34963
+ executedSwapCount += 1;
34936
34964
  approveAmount = Web3Number.fromWei(marginSwapQuote.total_calculated, marginToken.decimals).multipliedBy(1 + this.maxSlippage).abs();
34937
34965
  }
34938
34966
  let debtAmount = this._computeTargetDebtDelta(
@@ -34956,10 +34984,6 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
34956
34984
  if (!debtAmount.isZero() && debtAmount.greaterThan(0)) {
34957
34985
  try {
34958
34986
  let swapQuote;
34959
- const debtAmountInCollateralUnits = new Web3Number(
34960
- debtAmount.multipliedBy(debtPrice).dividedBy(collateralPrice).toFixed(6),
34961
- collateralToken.decimals
34962
- );
34963
34987
  if (params.leverSwap?.exactOutput) {
34964
34988
  swapQuote = await ekuboQuoter.getQuoteExactOutput(
34965
34989
  debtToken.address.address,
@@ -34983,17 +35007,9 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
34983
35007
  ).abs().toNumber();
34984
35008
  const inputAmt = debtAmount.abs().toNumber();
34985
35009
  const outputAmt = quoteOutputAmount;
34986
- this.lastSwapPriceInfo = {
34987
- source: "ekubo",
34988
- fromTokenSymbol: debtToken.symbol,
34989
- toTokenSymbol: collateralToken.symbol,
34990
- fromAmount: inputAmt,
34991
- toAmount: outputAmt,
34992
- effectivePrice: outputAmt !== 0 ? inputAmt / outputAmt : 0
34993
- };
34994
- logger.verbose(
34995
- `${_VesuMultiplyAdapter.name}::_getIncreaseCalldata stored price info: ${inputAmt} ${debtToken.symbol} \u2192 ${outputAmt} ${collateralToken.symbol}, effectivePrice=${this.lastSwapPriceInfo.effectivePrice}`
34996
- );
35010
+ aggregatedFromAmount += inputAmt;
35011
+ aggregatedToAmount += outputAmt;
35012
+ executedSwapCount += 1;
34997
35013
  leverSwap = ekuboQuoter.getVesuMultiplyQuote(
34998
35014
  swapQuote,
34999
35015
  debtToken,
@@ -35011,6 +35027,19 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
35011
35027
  );
35012
35028
  }
35013
35029
  }
35030
+ if (executedSwapCount > 0) {
35031
+ this.lastSwapPriceInfo = {
35032
+ source: "ekubo",
35033
+ fromTokenSymbol: aggregatedFromSymbol ?? debtToken.symbol,
35034
+ toTokenSymbol: aggregatedToSymbol,
35035
+ fromAmount: aggregatedFromAmount,
35036
+ toAmount: aggregatedToAmount,
35037
+ effectivePrice: aggregatedToAmount !== 0 ? aggregatedFromAmount / aggregatedToAmount : 0
35038
+ };
35039
+ logger.verbose(
35040
+ `${_VesuMultiplyAdapter.name}::_getIncreaseCalldata stored aggregated price info: ${aggregatedFromAmount} ${this.lastSwapPriceInfo.fromTokenSymbol} \u2192 ${aggregatedToAmount} ${aggregatedToSymbol}, effectivePrice=${this.lastSwapPriceInfo.effectivePrice}, swaps=${executedSwapCount}`
35041
+ );
35042
+ }
35014
35043
  logger.verbose(
35015
35044
  `${_VesuMultiplyAdapter.name}::_getIncreaseCalldata leverSwapLimitAmount: ${leverSwapLimitAmount.toWei()}`
35016
35045
  );
@@ -35043,10 +35072,21 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
35043
35072
  approveAmount
35044
35073
  };
35045
35074
  }
35075
+ // private _setLastSwapPriceAsNoSwap(): void {
35076
+ // this.lastSwapPriceInfo = {
35077
+ // source: "no-swap",
35078
+ // fromTokenSymbol: this.config.collateral.symbol,
35079
+ // toTokenSymbol: this.config.debt.symbol,
35080
+ // fromAmount: 0,
35081
+ // toAmount: 0,
35082
+ // effectivePrice: 0,
35083
+ // };
35084
+ // }
35046
35085
  async _buildDecreaseLikeCalldata(params) {
35047
35086
  const collateralToken = this.config.collateral;
35048
35087
  const debtToken = this.config.debt;
35049
35088
  const { contract: multiplyContract } = this._getMultiplyContract();
35089
+ this.lastSwapPriceInfo = null;
35050
35090
  const ekuboQuoter = new EkuboQuoter(
35051
35091
  this.config.networkConfig,
35052
35092
  this.config.pricer
@@ -35055,6 +35095,9 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
35055
35095
  let leverSwapWeights = [];
35056
35096
  let leverSwapLimitAmount = Web3Number.fromWei(0, collateralToken.decimals);
35057
35097
  let leverCollateralUsed = Web3Number.fromWei(0, collateralToken.decimals);
35098
+ let aggregatedFromAmount = 0;
35099
+ let aggregatedToAmount = 0;
35100
+ let executedSwapCount = 0;
35058
35101
  if (params.closePosition) {
35059
35102
  const debtQuote = await ekuboQuoter.getQuoteExactOutput(
35060
35103
  collateralToken.address.address,
@@ -35073,6 +35116,9 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
35073
35116
  collateralToken.decimals
35074
35117
  ).abs();
35075
35118
  leverSwapLimitAmount = leverCollateralUsed.multipliedBy(1 + this.maxSlippage);
35119
+ aggregatedFromAmount += leverCollateralUsed.toNumber();
35120
+ aggregatedToAmount += params.debtToRepayAbs.abs().toNumber();
35121
+ executedSwapCount += 1;
35076
35122
  } else {
35077
35123
  if (params.collateralPrice === void 0 || params.debtPrice === void 0) {
35078
35124
  throw new Error(
@@ -35094,17 +35140,9 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
35094
35140
  leverSwapQuote.total_calculated,
35095
35141
  debtToken.decimals
35096
35142
  ).abs().toNumber();
35097
- this.lastSwapPriceInfo = {
35098
- source: "ekubo",
35099
- fromTokenSymbol: collateralToken.symbol,
35100
- toTokenSymbol: debtToken.symbol,
35101
- fromAmount: inputAmt,
35102
- toAmount: outputAmt,
35103
- effectivePrice: outputAmt !== 0 ? outputAmt / inputAmt : 0
35104
- };
35105
- logger.verbose(
35106
- `${_VesuMultiplyAdapter.name}::_buildDecreaseLikeCalldata stored price info: ${inputAmt} ${collateralToken.symbol} \u2192 ${outputAmt} ${debtToken.symbol}, effectivePrice=${this.lastSwapPriceInfo.effectivePrice}`
35107
- );
35143
+ aggregatedFromAmount += inputAmt;
35144
+ aggregatedToAmount += outputAmt;
35145
+ executedSwapCount += 1;
35108
35146
  leverSwap = ekuboQuoter.getVesuMultiplyQuote(
35109
35147
  leverSwapQuote,
35110
35148
  collateralToken,
@@ -35127,6 +35165,7 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
35127
35165
  );
35128
35166
  const withdrawSwapWeights = [];
35129
35167
  if (params.outputToken && !params.outputToken.address.eq(collateralToken.address)) {
35168
+ assert(params.outputToken.address.eq(debtToken.address), "Withdraw output token must be the same as debt token");
35130
35169
  const residualCollateral = params.closePosition ? params.existingCollateral.minus(leverCollateralUsed) : params.subMargin;
35131
35170
  const outputTokenPrice = await this.config.pricer.getPrice(params.outputToken.symbol);
35132
35171
  if (residualCollateral.greaterThan(0)) {
@@ -35143,12 +35182,34 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
35143
35182
  );
35144
35183
  withdrawSwap = built.swaps;
35145
35184
  withdrawSwapWeights.push(...built.weights);
35185
+ const withdrawOutputAmount = Web3Number.fromWei(
35186
+ withdrawQuote.total_calculated,
35187
+ params.outputToken.decimals
35188
+ ).abs().toNumber();
35189
+ aggregatedFromAmount += residualCollateral.toNumber();
35190
+ aggregatedToAmount += withdrawOutputAmount;
35191
+ executedSwapCount += 1;
35146
35192
  const estimatedOutput = residualCollateral.multipliedBy(params.collateralPrice).dividedBy(outputTokenPrice.price);
35147
35193
  estimatedOutput.decimals = params.outputToken.decimals;
35148
35194
  withdrawSwapLimitAmount = estimatedOutput.multipliedBy(1 - this.maxSlippage);
35149
35195
  }
35150
35196
  }
35151
35197
  }
35198
+ if (executedSwapCount > 0) {
35199
+ this.lastSwapPriceInfo = {
35200
+ source: "ekubo",
35201
+ fromTokenSymbol: collateralToken.symbol,
35202
+ toTokenSymbol: debtToken.symbol,
35203
+ fromAmount: aggregatedFromAmount,
35204
+ toAmount: aggregatedToAmount,
35205
+ effectivePrice: aggregatedFromAmount !== 0 ? aggregatedToAmount / aggregatedFromAmount : 0
35206
+ };
35207
+ logger.verbose(
35208
+ `${_VesuMultiplyAdapter.name}::_buildDecreaseLikeCalldata stored aggregated price info: ${aggregatedFromAmount} ${collateralToken.symbol} \u2192 ${aggregatedToAmount} ${debtToken.symbol}, effectivePrice=${this.lastSwapPriceInfo.effectivePrice}, swaps=${executedSwapCount}`
35209
+ );
35210
+ } else {
35211
+ this.lastSwapPriceInfo = null;
35212
+ }
35152
35213
  logger.debug(
35153
35214
  `${_VesuMultiplyAdapter.name}::_buildDecreaseLikeCalldata leverSwapCount=${leverSwap.length}, withdrawSwapCount=${withdrawSwap.length}, withdrawSwapLimitAmount=${withdrawSwapLimitAmount.toNumber()}, subMargin=${params.subMargin.toNumber()}`
35154
35215
  );
@@ -36336,13 +36397,13 @@ var ExtendedAdapter = class _ExtendedAdapter extends BaseAdapter {
36336
36397
  this.minimumExtendedMovementAmount = this.config.minimumExtendedMovementAmount ?? 5;
36337
36398
  this.client = client;
36338
36399
  this.retryDelayForOrderStatus = this.config.retryDelayForOrderStatus ?? 3e3;
36339
- this.usdceToken = this.config.supportedPositions[0].asset;
36400
+ this.usdcToken = this.config.supportedPositions[0].asset;
36340
36401
  }
36341
36402
  _depositApproveProofReadableId() {
36342
- return `extended_approve_${this.usdceToken.symbol}`;
36403
+ return `extended_approve_${this.usdcToken.symbol}`;
36343
36404
  }
36344
36405
  _depositCallProofReadableId() {
36345
- return `extended_deposit_${this.usdceToken.symbol}`;
36406
+ return `extended_deposit_${this.usdcToken.symbol}`;
36346
36407
  }
36347
36408
  //abstract means the method has no implementation in this class; instead, child classes must implement it.
36348
36409
  async getAPY(supportedPosition) {
@@ -36411,7 +36472,7 @@ var ExtendedAdapter = class _ExtendedAdapter extends BaseAdapter {
36411
36472
  _getDepositLeaf() {
36412
36473
  return [
36413
36474
  {
36414
- target: this.usdceToken.address,
36475
+ target: this.usdcToken.address,
36415
36476
  method: "approve",
36416
36477
  packedArguments: [this.config.extendedContract.toBigInt()],
36417
36478
  id: this._depositApproveProofReadableId(),
@@ -36431,14 +36492,14 @@ var ExtendedAdapter = class _ExtendedAdapter extends BaseAdapter {
36431
36492
  }
36432
36493
  async getDepositCall(params) {
36433
36494
  try {
36434
- const salt = Math.floor(Math.random() * 10 ** this.usdceToken.decimals);
36495
+ const salt = Math.floor(Math.random() * 10 ** this.usdcToken.decimals);
36435
36496
  const amount = import_starknet27.uint256.bnToUint256(params.amount.toWei());
36436
36497
  return [
36437
36498
  {
36438
36499
  proofReadableId: this._depositApproveProofReadableId(),
36439
36500
  sanitizer: SIMPLE_SANITIZER,
36440
36501
  call: {
36441
- contractAddress: this.usdceToken.address,
36502
+ contractAddress: this.usdcToken.address,
36442
36503
  selector: import_starknet27.hash.getSelectorFromName("approve"),
36443
36504
  calldata: [
36444
36505
  this.config.extendedContract.toBigInt(),
@@ -37339,7 +37400,7 @@ var AvnuAdapter = class _AvnuAdapter extends BaseAdapter {
37339
37400
  }
37340
37401
  };
37341
37402
 
37342
- // src/strategies/universal-strategy.tsx
37403
+ // src/strategies/universal-adapters/svk-troves-adapter.ts
37343
37404
  var import_starknet30 = require("starknet");
37344
37405
 
37345
37406
  // src/data/universal-vault.abi.json
@@ -39024,6 +39085,272 @@ var universal_vault_abi_default = [
39024
39085
  }
39025
39086
  ];
39026
39087
 
39088
+ // src/strategies/universal-adapters/svk-troves-adapter.ts
39089
+ var DEFAULT_TROVES_STRATEGIES_API = "https://app.troves.fi/api/strategies";
39090
+ function parseTrovesApyField(raw) {
39091
+ if (typeof raw === "number" && Number.isFinite(raw)) {
39092
+ return raw;
39093
+ }
39094
+ if (typeof raw === "string") {
39095
+ const n = Number.parseFloat(raw);
39096
+ if (Number.isFinite(n)) {
39097
+ return n;
39098
+ }
39099
+ }
39100
+ return 0;
39101
+ }
39102
+ var SvkTrovesAdapter = class _SvkTrovesAdapter extends BaseAdapter {
39103
+ constructor(config) {
39104
+ super(config, _SvkTrovesAdapter.name, Protocols.TROVES);
39105
+ this.config = config;
39106
+ }
39107
+ /** Owner used for share balance + `due_assets_from_owner`. */
39108
+ _positionOwner() {
39109
+ return this.config.positionOwner ?? this.config.vaultAllocator;
39110
+ }
39111
+ /**
39112
+ * Proof readable IDs must stay ≤ 31 chars (Cairo short string). We derive a short ASCII suffix from
39113
+ * `strategyVault` address so multiple SVK adapters in one tree stay distinct.
39114
+ */
39115
+ _proofSuffix() {
39116
+ return this.config.strategyVault.address.replace(/^0x/, "").slice(-6);
39117
+ }
39118
+ _depositApproveProofReadableId() {
39119
+ return `appr_dep_svk_${this._proofSuffix()}`;
39120
+ }
39121
+ _depositCallProofReadableId() {
39122
+ return `dep_svk_${this._proofSuffix()}`;
39123
+ }
39124
+ _withdrawCallProofReadableId() {
39125
+ return `wtdrw_svk_${this._proofSuffix()}`;
39126
+ }
39127
+ async getAPY(supportedPosition) {
39128
+ const CACHE_KEY = `svk_apy_${this.config.trovesStrategyId}`;
39129
+ const cached = this.getCache(CACHE_KEY);
39130
+ if (cached) {
39131
+ return cached;
39132
+ }
39133
+ const url = this.config.trovesStrategiesApiUrl ?? DEFAULT_TROVES_STRATEGIES_API;
39134
+ try {
39135
+ const res = await fetch(url);
39136
+ if (!res.ok) {
39137
+ logger.warn(`${_SvkTrovesAdapter.name}::getAPY: HTTP ${res.status} from ${url}`);
39138
+ const fallback = { apy: 0, type: "base" /* BASE */ };
39139
+ this.setCache(CACHE_KEY, fallback, 3e5);
39140
+ return fallback;
39141
+ }
39142
+ const body = await res.json();
39143
+ const row = body.strategies?.find((s) => s.id === this.config.trovesStrategyId);
39144
+ if (!row) {
39145
+ logger.warn(
39146
+ `${_SvkTrovesAdapter.name}::getAPY: strategy id not found: ${this.config.trovesStrategyId}`
39147
+ );
39148
+ const fallback = { apy: 0, type: "base" /* BASE */ };
39149
+ this.setCache(CACHE_KEY, fallback, 3e5);
39150
+ return fallback;
39151
+ }
39152
+ const apy = parseTrovesApyField(row.apy);
39153
+ const result = { apy, type: "base" /* BASE */ };
39154
+ this.setCache(CACHE_KEY, result, 3e5);
39155
+ return result;
39156
+ } catch (error) {
39157
+ logger.error(`${_SvkTrovesAdapter.name}::getAPY:`, error);
39158
+ throw error;
39159
+ }
39160
+ }
39161
+ async getPosition(supportedPosition) {
39162
+ const CACHE_KEY = `svk_pos_${this.config.strategyVault.address}_${this._positionOwner().address}`;
39163
+ const cached = this.getCache(CACHE_KEY);
39164
+ if (cached) {
39165
+ return cached;
39166
+ }
39167
+ try {
39168
+ const vault = new import_starknet30.Contract({
39169
+ abi: universal_vault_abi_default,
39170
+ address: this.config.strategyVault.address,
39171
+ providerOrAccount: this.config.networkConfig.provider
39172
+ });
39173
+ const owner = this._positionOwner();
39174
+ const decimals = supportedPosition.asset.decimals;
39175
+ const shares = await vault.balance_of(owner.address);
39176
+ const shareU256 = import_starknet30.uint256.bnToUint256(shares);
39177
+ const liquidAssetsRaw = await vault.convert_to_assets(shareU256);
39178
+ const liquid = Web3Number.fromWei(liquidAssetsRaw.toString(), decimals);
39179
+ let pending = Web3Number.fromWei("0", decimals);
39180
+ try {
39181
+ const dueRaw = await vault.call("due_assets_from_owner", [owner.address]);
39182
+ pending = Web3Number.fromWei(dueRaw.toString(), decimals);
39183
+ } catch (e) {
39184
+ logger.warn(
39185
+ `${_SvkTrovesAdapter.name}::getPosition: due_assets_from_owner failed (treating as 0): ${e.message}`
39186
+ );
39187
+ }
39188
+ const total = liquid.plus(pending);
39189
+ const remarks = `Troves ${this.config.trovesStrategyId} holdings`;
39190
+ const result = {
39191
+ amount: total,
39192
+ remarks
39193
+ };
39194
+ this.setCache(CACHE_KEY, result, 6e4);
39195
+ return result;
39196
+ } catch (error) {
39197
+ logger.error(`${_SvkTrovesAdapter.name}::getPosition:`, error);
39198
+ throw error;
39199
+ }
39200
+ }
39201
+ async maxDeposit(amount) {
39202
+ const baseToken = this.config.baseToken;
39203
+ if (!amount) {
39204
+ return {
39205
+ tokenInfo: baseToken,
39206
+ amount: new Web3Number("999999999999999999999999999", baseToken.decimals),
39207
+ usdValue: 1e27,
39208
+ remarks: "Max deposit (unbounded placeholder)",
39209
+ apy: await this.getAPY({ asset: baseToken, isDebt: false }),
39210
+ protocol: this.protocol
39211
+ };
39212
+ }
39213
+ const usdValue = await this.getUSDValue(baseToken, amount);
39214
+ return {
39215
+ tokenInfo: baseToken,
39216
+ amount,
39217
+ usdValue,
39218
+ remarks: "Deposit amount",
39219
+ apy: await this.getAPY({ asset: baseToken, isDebt: false }),
39220
+ protocol: this.protocol
39221
+ };
39222
+ }
39223
+ async maxWithdraw() {
39224
+ const baseToken = this.config.baseToken;
39225
+ const current = await this.getPosition({ asset: baseToken, isDebt: false });
39226
+ const pos = current ?? { amount: new Web3Number("0", baseToken.decimals), remarks: "" };
39227
+ const usdValue = await this.getUSDValue(baseToken, pos.amount);
39228
+ return {
39229
+ tokenInfo: baseToken,
39230
+ amount: pos.amount,
39231
+ usdValue,
39232
+ remarks: "Max withdraw (liquid + pending redemption, underlying units)",
39233
+ apy: await this.getAPY({ asset: baseToken, isDebt: false }),
39234
+ protocol: this.protocol
39235
+ };
39236
+ }
39237
+ _getDepositLeaf() {
39238
+ const baseToken = this.config.baseToken;
39239
+ const strategyVault = this.config.strategyVault;
39240
+ const receiver = this.config.vaultAllocator;
39241
+ return [
39242
+ {
39243
+ target: baseToken.address,
39244
+ method: "approve",
39245
+ packedArguments: [strategyVault.toBigInt()],
39246
+ sanitizer: SIMPLE_SANITIZER,
39247
+ id: this._depositApproveProofReadableId()
39248
+ },
39249
+ {
39250
+ target: strategyVault,
39251
+ method: "deposit",
39252
+ packedArguments: [receiver.toBigInt()],
39253
+ sanitizer: SIMPLE_SANITIZER,
39254
+ id: this._depositCallProofReadableId()
39255
+ }
39256
+ ];
39257
+ }
39258
+ _getWithdrawLeaf() {
39259
+ const strategyVault = this.config.strategyVault;
39260
+ const recv = this.config.vaultAllocator;
39261
+ const owner = this.config.vaultAllocator;
39262
+ return [
39263
+ {
39264
+ target: strategyVault,
39265
+ method: "withdraw",
39266
+ packedArguments: [recv.toBigInt(), owner.toBigInt()],
39267
+ sanitizer: SIMPLE_SANITIZER,
39268
+ id: this._withdrawCallProofReadableId()
39269
+ }
39270
+ ];
39271
+ }
39272
+ getDepositAdapter() {
39273
+ const leafConfigs = this._getDepositLeaf();
39274
+ const leaves = leafConfigs.map((config) => {
39275
+ const { target, method, packedArguments, sanitizer, id } = config;
39276
+ return this.constructSimpleLeafData({ id, target, method, packedArguments }, sanitizer);
39277
+ });
39278
+ return { leaves, callConstructor: this.getDepositCall.bind(this) };
39279
+ }
39280
+ getWithdrawAdapter() {
39281
+ const leafConfigs = this._getWithdrawLeaf();
39282
+ const leaves = leafConfigs.map((config) => {
39283
+ const { target, method, packedArguments, sanitizer, id } = config;
39284
+ return this.constructSimpleLeafData({ id, target, method, packedArguments }, sanitizer);
39285
+ });
39286
+ return { leaves, callConstructor: this.getWithdrawCall.bind(this) };
39287
+ }
39288
+ async getDepositCall(params) {
39289
+ const baseToken = this.config.baseToken;
39290
+ const strategyVault = this.config.strategyVault;
39291
+ const amount = params.amount;
39292
+ const uint256Amount = import_starknet30.uint256.bnToUint256(amount.toWei());
39293
+ const receiver = this.config.vaultAllocator;
39294
+ return [
39295
+ {
39296
+ proofReadableId: this._depositApproveProofReadableId(),
39297
+ sanitizer: SIMPLE_SANITIZER,
39298
+ call: {
39299
+ contractAddress: baseToken.address,
39300
+ selector: import_starknet30.hash.getSelectorFromName("approve"),
39301
+ calldata: [
39302
+ strategyVault.toBigInt(),
39303
+ toBigInt(uint256Amount.low.toString()),
39304
+ toBigInt(uint256Amount.high.toString())
39305
+ ]
39306
+ }
39307
+ },
39308
+ {
39309
+ proofReadableId: this._depositCallProofReadableId(),
39310
+ sanitizer: SIMPLE_SANITIZER,
39311
+ call: {
39312
+ contractAddress: strategyVault,
39313
+ selector: import_starknet30.hash.getSelectorFromName("deposit"),
39314
+ calldata: [
39315
+ toBigInt(uint256Amount.low.toString()),
39316
+ toBigInt(uint256Amount.high.toString()),
39317
+ receiver.toBigInt()
39318
+ ]
39319
+ }
39320
+ }
39321
+ ];
39322
+ }
39323
+ async getWithdrawCall(params) {
39324
+ const strategyVault = this.config.strategyVault;
39325
+ const amount = params.amount;
39326
+ const uint256Amount = import_starknet30.uint256.bnToUint256(amount.toWei());
39327
+ const recv = this.config.vaultAllocator;
39328
+ const owner = this.config.vaultAllocator;
39329
+ return [
39330
+ {
39331
+ proofReadableId: this._withdrawCallProofReadableId(),
39332
+ sanitizer: SIMPLE_SANITIZER,
39333
+ call: {
39334
+ contractAddress: strategyVault,
39335
+ selector: import_starknet30.hash.getSelectorFromName("withdraw"),
39336
+ calldata: [
39337
+ toBigInt(uint256Amount.low.toString()),
39338
+ toBigInt(uint256Amount.high.toString()),
39339
+ recv.toBigInt(),
39340
+ owner.toBigInt()
39341
+ ]
39342
+ }
39343
+ }
39344
+ ];
39345
+ }
39346
+ getHealthFactor() {
39347
+ return Promise.resolve(10);
39348
+ }
39349
+ };
39350
+
39351
+ // src/strategies/universal-strategy.tsx
39352
+ var import_starknet31 = require("starknet");
39353
+
39027
39354
  // src/data/vault-manager.abi.json
39028
39355
  var vault_manager_abi_default = [
39029
39356
  {
@@ -39689,12 +40016,12 @@ var UniversalStrategy = class _UniversalStrategy extends BaseStrategy {
39689
40016
  );
39690
40017
  this.metadata = metadata;
39691
40018
  this.address = metadata.address;
39692
- this.contract = new import_starknet30.Contract({
40019
+ this.contract = new import_starknet31.Contract({
39693
40020
  abi: universal_vault_abi_default,
39694
40021
  address: this.address.address,
39695
40022
  providerOrAccount: this.config.provider
39696
40023
  });
39697
- this.managerContract = new import_starknet30.Contract({
40024
+ this.managerContract = new import_starknet31.Contract({
39698
40025
  abi: vault_manager_abi_default,
39699
40026
  address: this.metadata.additionalInfo.manager.address,
39700
40027
  providerOrAccount: this.config.provider
@@ -39748,17 +40075,17 @@ var UniversalStrategy = class _UniversalStrategy extends BaseStrategy {
39748
40075
  amountInfo.tokenInfo.address.eq(this.asset().address),
39749
40076
  "Deposit token mismatch"
39750
40077
  );
39751
- const assetContract = new import_starknet30.Contract({
40078
+ const assetContract = new import_starknet31.Contract({
39752
40079
  abi: universal_vault_abi_default,
39753
40080
  address: this.asset().address.address,
39754
40081
  providerOrAccount: this.config.provider
39755
40082
  });
39756
40083
  const call1 = assetContract.populate("approve", [
39757
40084
  this.address.address,
39758
- import_starknet30.uint256.bnToUint256(amountInfo.amount.toWei())
40085
+ import_starknet31.uint256.bnToUint256(amountInfo.amount.toWei())
39759
40086
  ]);
39760
40087
  const call2 = this.contract.populate("deposit", [
39761
- import_starknet30.uint256.bnToUint256(amountInfo.amount.toWei()),
40088
+ import_starknet31.uint256.bnToUint256(amountInfo.amount.toWei()),
39762
40089
  receiver.address
39763
40090
  ]);
39764
40091
  return [call1, call2];
@@ -39768,9 +40095,9 @@ var UniversalStrategy = class _UniversalStrategy extends BaseStrategy {
39768
40095
  amountInfo.tokenInfo.address.eq(this.asset().address),
39769
40096
  "Withdraw token mismatch"
39770
40097
  );
39771
- const shares = await this.contract.call("convert_to_shares", [import_starknet30.uint256.bnToUint256(amountInfo.amount.toWei())]);
40098
+ const shares = await this.contract.call("convert_to_shares", [import_starknet31.uint256.bnToUint256(amountInfo.amount.toWei())]);
39772
40099
  const call = this.contract.populate("request_redeem", [
39773
- import_starknet30.uint256.bnToUint256(shares.toString()),
40100
+ import_starknet31.uint256.bnToUint256(shares.toString()),
39774
40101
  receiver.address,
39775
40102
  owner.address
39776
40103
  ]);
@@ -39780,7 +40107,7 @@ var UniversalStrategy = class _UniversalStrategy extends BaseStrategy {
39780
40107
  const shares = await this.contract.call("balanceOf", [user.address], { blockIdentifier });
39781
40108
  const assets = await this.contract.call(
39782
40109
  "convert_to_assets",
39783
- [import_starknet30.uint256.bnToUint256(shares)],
40110
+ [import_starknet31.uint256.bnToUint256(shares)],
39784
40111
  { blockIdentifier }
39785
40112
  );
39786
40113
  const amount = Web3Number.fromWei(
@@ -39803,7 +40130,7 @@ var UniversalStrategy = class _UniversalStrategy extends BaseStrategy {
39803
40130
  const vesuAdapters = this.getVesuAdapters();
39804
40131
  const allVesuPools = await VesuAdapter.getVesuPools();
39805
40132
  const pools = vesuAdapters.map((vesuAdapter) => {
39806
- return allVesuPools.pools.find((p) => vesuAdapter.config.poolId.eqString(import_starknet30.num.getHexString(p.id)));
40133
+ return allVesuPools.pools.find((p) => vesuAdapter.config.poolId.eqString(import_starknet31.num.getHexString(p.id)));
39807
40134
  });
39808
40135
  logger.verbose(`${this.metadata.name}::netAPY: vesu-pools: ${JSON.stringify(pools)}`);
39809
40136
  if (pools.some((p) => !p)) {
@@ -40092,7 +40419,7 @@ var UniversalStrategy = class _UniversalStrategy extends BaseStrategy {
40092
40419
  }];
40093
40420
  }
40094
40421
  getSetManagerCall(strategist, root = this.getMerkleRoot()) {
40095
- return this.managerContract.populate("set_manage_root", [strategist.address, import_starknet30.num.getHexString(root)]);
40422
+ return this.managerContract.populate("set_manage_root", [strategist.address, import_starknet31.num.getHexString(root)]);
40096
40423
  }
40097
40424
  // TODO: callConstructor now returns Promise<ManageCall[]>, migrate callers to await and flatten
40098
40425
  getManageCall(proofIds, manageCalls) {
@@ -40276,7 +40603,7 @@ var UniversalStrategy = class _UniversalStrategy extends BaseStrategy {
40276
40603
  callSet1 = [...temp];
40277
40604
  }
40278
40605
  const allActions = [...callSet1.map((i) => i.manageCall), ...callSet2.map((i) => i.manageCall)];
40279
- const flashloanCalldata = import_starknet30.CallData.compile([
40606
+ const flashloanCalldata = import_starknet31.CallData.compile([
40280
40607
  [...callSet1.map((i) => i.proofs), ...callSet2.map((i) => i.proofs)],
40281
40608
  allActions.map((i) => i.sanitizer.address),
40282
40609
  allActions.map((i) => i.call.contractAddress.address),
@@ -40714,19 +41041,19 @@ var UniversalStrategies = [
40714
41041
  ];
40715
41042
 
40716
41043
  // src/strategies/svk-strategy.ts
40717
- var import_starknet31 = require("starknet");
41044
+ var import_starknet32 = require("starknet");
40718
41045
  var SVKStrategy = class extends BaseStrategy {
40719
41046
  constructor(config, pricer, metadata) {
40720
41047
  super(config);
40721
41048
  this.pricer = pricer;
40722
41049
  this.metadata = metadata;
40723
41050
  this.address = metadata.address;
40724
- this.contract = new import_starknet31.Contract({
41051
+ this.contract = new import_starknet32.Contract({
40725
41052
  abi: universal_vault_abi_default,
40726
41053
  address: this.address.address,
40727
41054
  providerOrAccount: this.config.provider
40728
41055
  });
40729
- this.managerContract = new import_starknet31.Contract({
41056
+ this.managerContract = new import_starknet32.Contract({
40730
41057
  abi: vault_manager_abi_default,
40731
41058
  address: this.metadata.additionalInfo.manager.address,
40732
41059
  providerOrAccount: this.config.provider
@@ -40743,17 +41070,17 @@ var SVKStrategy = class extends BaseStrategy {
40743
41070
  amountInfo.tokenInfo.address.eq(this.asset().address),
40744
41071
  "Deposit token mismatch"
40745
41072
  );
40746
- const assetContract = new import_starknet31.Contract({
41073
+ const assetContract = new import_starknet32.Contract({
40747
41074
  abi: universal_vault_abi_default,
40748
41075
  address: this.asset().address.address,
40749
41076
  providerOrAccount: this.config.provider
40750
41077
  });
40751
41078
  const call1 = assetContract.populate("approve", [
40752
41079
  this.address.address,
40753
- import_starknet31.uint256.bnToUint256(amountInfo.amount.toWei())
41080
+ import_starknet32.uint256.bnToUint256(amountInfo.amount.toWei())
40754
41081
  ]);
40755
41082
  const call2 = this.contract.populate("deposit", [
40756
- import_starknet31.uint256.bnToUint256(amountInfo.amount.toWei()),
41083
+ import_starknet32.uint256.bnToUint256(amountInfo.amount.toWei()),
40757
41084
  receiver.address
40758
41085
  ]);
40759
41086
  return [call1, call2];
@@ -40763,9 +41090,9 @@ var SVKStrategy = class extends BaseStrategy {
40763
41090
  amountInfo.tokenInfo.address.eq(this.asset().address),
40764
41091
  "Withdraw token mismatch"
40765
41092
  );
40766
- const shares = await this.contract.call("convert_to_shares", [import_starknet31.uint256.bnToUint256(amountInfo.amount.toWei())]);
41093
+ const shares = await this.contract.call("convert_to_shares", [import_starknet32.uint256.bnToUint256(amountInfo.amount.toWei())]);
40767
41094
  const call = this.contract.populate("request_redeem", [
40768
- import_starknet31.uint256.bnToUint256(shares.toString()),
41095
+ import_starknet32.uint256.bnToUint256(shares.toString()),
40769
41096
  receiver.address,
40770
41097
  owner.address
40771
41098
  ]);
@@ -40902,7 +41229,7 @@ var SVKStrategy = class extends BaseStrategy {
40902
41229
  getSetManagerCall(strategist, root = this.getMerkleRoot()) {
40903
41230
  return this.managerContract.populate("set_manage_root", [
40904
41231
  strategist.address,
40905
- import_starknet31.num.getHexString(root)
41232
+ import_starknet32.num.getHexString(root)
40906
41233
  ]);
40907
41234
  }
40908
41235
  /**
@@ -40956,7 +41283,7 @@ var SVKStrategy = class extends BaseStrategy {
40956
41283
  };
40957
41284
 
40958
41285
  // src/strategies/universal-lst-muliplier-strategy.tsx
40959
- var import_starknet32 = require("starknet");
41286
+ var import_starknet33 = require("starknet");
40960
41287
 
40961
41288
  // src/strategies/universal-adapters/adapter-optimizer.ts
40962
41289
  var AdapterOptimizer = class {
@@ -41109,15 +41436,15 @@ var UniversalLstMultiplierStrategy = class _UniversalLstMultiplierStrategy exten
41109
41436
  async getLSTExchangeRate() {
41110
41437
  const vesuAdapter1 = this.getVesuSameTokenAdapter();
41111
41438
  const lstTokenInfo = vesuAdapter1._vesuAdapter.config.collateral;
41112
- const lstABI = new import_starknet32.Contract({
41439
+ const lstABI = new import_starknet33.Contract({
41113
41440
  abi: erc4626_abi_default,
41114
41441
  address: lstTokenInfo.address.address,
41115
41442
  providerOrAccount: this.config.provider
41116
41443
  });
41117
41444
  const price = await lstABI.call("convert_to_assets", [
41118
- import_starknet32.uint256.bnToUint256(new Web3Number(1, lstTokenInfo.decimals).toWei())
41445
+ import_starknet33.uint256.bnToUint256(new Web3Number(1, lstTokenInfo.decimals).toWei())
41119
41446
  ]);
41120
- const exchangeRate = Number(import_starknet32.uint256.uint256ToBN(price).toString()) / Math.pow(10, lstTokenInfo.decimals);
41447
+ const exchangeRate = Number(import_starknet33.uint256.uint256ToBN(price).toString()) / Math.pow(10, lstTokenInfo.decimals);
41121
41448
  logger.verbose(`${this.getTag()}:: LST Exchange Rate: ${exchangeRate}`);
41122
41449
  return exchangeRate;
41123
41450
  }
@@ -41987,6 +42314,249 @@ var HyperLSTStrategies = [
41987
42314
  getStrategySettings("mRe7YIELD", "mRe7YIELD", hypermRe7YIELD, false, false)
41988
42315
  ];
41989
42316
 
42317
+ // src/strategies/vesu-extended-strategy/services/ltv-imbalance-rebalance-math.ts
42318
+ function ceilBtc(v, precision) {
42319
+ const f = 10 ** precision;
42320
+ return Math.ceil(v * f) / f;
42321
+ }
42322
+ function floorBtc(v, precision) {
42323
+ const f = 10 ** precision;
42324
+ return Math.floor(v * f) / f;
42325
+ }
42326
+ function isNegligible(btc, precision) {
42327
+ return Math.abs(btc) < 10 ** -precision;
42328
+ }
42329
+ function computeExtIdealMargin(posBtc, leverage, price) {
42330
+ return posBtc * price / leverage;
42331
+ }
42332
+ function computeExtDeficit(ext, price) {
42333
+ return Math.max(0, computeExtIdealMargin(ext.positionBtc, ext.leverage, price) - ext.equity);
42334
+ }
42335
+ function computeVesuHF(vesu, price) {
42336
+ const collateralUsd = vesu.positionBtc * price;
42337
+ if (collateralUsd === 0) return Infinity;
42338
+ const ltv = vesu.debt * vesu.debtPrice / collateralUsd;
42339
+ if (ltv === 0) return Infinity;
42340
+ return vesu.maxLTV / ltv;
42341
+ }
42342
+ function computeVesuDebtRepay(vesu, price) {
42343
+ const targetLTV = vesu.maxLTV / vesu.targetHF;
42344
+ const collateralUsd = vesu.positionBtc * price;
42345
+ const requiredDebt = targetLTV * collateralUsd / vesu.debtPrice;
42346
+ return Math.max(0, vesu.debt - requiredDebt);
42347
+ }
42348
+ function computeVesuTargetLTV(vesu) {
42349
+ return vesu.maxLTV / vesu.targetHF;
42350
+ }
42351
+ function computeVesuGrowthCost(gapBtc, vesu, price) {
42352
+ const targetLTV = computeVesuTargetLTV(vesu);
42353
+ return gapBtc * price * (1 - targetLTV);
42354
+ }
42355
+ function computeVesuGrowthDebt(gapBtc, vesu, price) {
42356
+ const targetLTV = computeVesuTargetLTV(vesu);
42357
+ return gapBtc * price * targetLTV / vesu.debtPrice;
42358
+ }
42359
+ function computeVesuGrowthFromEquity(equityUsd, vesu, price) {
42360
+ const targetLTV = computeVesuTargetLTV(vesu);
42361
+ return equityUsd / (price * (1 - targetLTV));
42362
+ }
42363
+ function emptyDeltas() {
42364
+ return {
42365
+ dExtPosition: 0,
42366
+ dVesuPosition: 0,
42367
+ dVesuDebt: 0,
42368
+ dExtAvlWithdraw: 0,
42369
+ dExtUpnl: 0,
42370
+ dVaUsd: 0,
42371
+ dWalletUsd: 0,
42372
+ dVesuBorrowCapacity: 0,
42373
+ dTransferVesuToExt: 0
42374
+ };
42375
+ }
42376
+ function mergeDeltas(a, b) {
42377
+ return {
42378
+ dExtPosition: a.dExtPosition + b.dExtPosition,
42379
+ dVesuPosition: a.dVesuPosition + b.dVesuPosition,
42380
+ dVesuDebt: a.dVesuDebt + b.dVesuDebt,
42381
+ dExtAvlWithdraw: a.dExtAvlWithdraw + b.dExtAvlWithdraw,
42382
+ dExtUpnl: a.dExtUpnl + b.dExtUpnl,
42383
+ dVaUsd: a.dVaUsd + b.dVaUsd,
42384
+ dWalletUsd: a.dWalletUsd + b.dWalletUsd,
42385
+ dVesuBorrowCapacity: a.dVesuBorrowCapacity + b.dVesuBorrowCapacity,
42386
+ dTransferVesuToExt: a.dTransferVesuToExt + b.dTransferVesuToExt
42387
+ };
42388
+ }
42389
+ function drawFunds(need, keys, pool) {
42390
+ const draws = {};
42391
+ let unmet = need;
42392
+ for (const k of keys) {
42393
+ if (unmet <= 0) break;
42394
+ const avail = pool[k];
42395
+ if (avail <= 0) continue;
42396
+ const take = Math.min(avail, unmet);
42397
+ draws[k] = take;
42398
+ pool[k] -= take;
42399
+ unmet -= take;
42400
+ }
42401
+ return { draws, filled: need - unmet, unmet };
42402
+ }
42403
+ function sumKeys(draws, keys) {
42404
+ return keys.reduce((s, k) => s + (draws[k] ?? 0), 0);
42405
+ }
42406
+ function applyDrawsToDeltas(d, draws, sign) {
42407
+ d.dVaUsd += sign * (draws.vaUsd ?? 0);
42408
+ d.dWalletUsd += sign * (draws.walletUsd ?? 0);
42409
+ d.dVesuBorrowCapacity += sign * (draws.vesuBorrowCapacity ?? 0);
42410
+ d.dExtAvlWithdraw += sign * (draws.extAvlWithdraw ?? 0);
42411
+ d.dExtUpnl += sign * (draws.extUpnl ?? 0);
42412
+ }
42413
+ function fixExtMargin(ext, price, pool) {
42414
+ const d = emptyDeltas();
42415
+ const deficit = computeExtDeficit(ext, price);
42416
+ if (deficit <= 0) return { d, unmet: 0 };
42417
+ const { draws, filled, unmet } = drawFunds(deficit, ["walletUsd", "vaUsd", "vesuBorrowCapacity"], pool);
42418
+ applyDrawsToDeltas(d, draws, -1);
42419
+ d.dExtAvlWithdraw += filled;
42420
+ const fromVesu = draws.vesuBorrowCapacity ?? 0;
42421
+ if (fromVesu > 0) d.dTransferVesuToExt += fromVesu;
42422
+ return { d, unmet };
42423
+ }
42424
+ function fixVesuMargin(vesu, price, pool, hfBuffer) {
42425
+ const d = emptyDeltas();
42426
+ const hf = computeVesuHF(vesu, price);
42427
+ if (hf >= vesu.targetHF - hfBuffer) return { d, unmet: 0 };
42428
+ const repayTokens = computeVesuDebtRepay(vesu, price);
42429
+ const repayUsd = repayTokens * vesu.debtPrice;
42430
+ const { draws, filled, unmet } = drawFunds(repayUsd, ["vaUsd", "walletUsd", "extAvlWithdraw", "extUpnl"], pool);
42431
+ applyDrawsToDeltas(d, draws, -1);
42432
+ d.dVesuDebt -= filled / vesu.debtPrice;
42433
+ const fromExt = sumKeys(draws, ["extAvlWithdraw", "extUpnl"]);
42434
+ if (fromExt > 0) d.dTransferVesuToExt -= fromExt;
42435
+ return { d, unmet };
42436
+ }
42437
+ function phase1(ext, vesu, price, pool, config) {
42438
+ const extResult = fixExtMargin(ext, price, pool);
42439
+ const vesuResult = fixVesuMargin(vesu, price, pool, config.hfBuffer);
42440
+ return {
42441
+ deltas: mergeDeltas(extResult.d, vesuResult.d),
42442
+ extDeficitRemaining: extResult.unmet,
42443
+ vesuRepayRemaining: vesuResult.unmet
42444
+ };
42445
+ }
42446
+ function solveUnifiedF(extPos, vesuPos, extEquity, vesuDebt, vesu, extLev, price) {
42447
+ const targetLTV = computeVesuTargetLTV(vesu);
42448
+ const k = 1 - targetLTV + 1 / extLev;
42449
+ const totalEquity = extEquity + vesuPos * price - vesuDebt * vesu.debtPrice;
42450
+ return totalEquity / (price * k);
42451
+ }
42452
+ function solveTransfer(vesuPos, vesuDebt, F, vesu, price) {
42453
+ const targetLTV = computeVesuTargetLTV(vesu);
42454
+ return vesuPos * price - vesuDebt * vesu.debtPrice - F * price * (1 - targetLTV);
42455
+ }
42456
+ function solveDebtRepay(vesuDebt, F, vesu, price) {
42457
+ const targetLTV = computeVesuTargetLTV(vesu);
42458
+ return vesuDebt - targetLTV * F * price / vesu.debtPrice;
42459
+ }
42460
+ function roundFinalPosition(extPos, vesuPos, rawF, precision) {
42461
+ let fFromExt;
42462
+ if (rawF < extPos) {
42463
+ const closeExt = ceilBtc(extPos - rawF, precision);
42464
+ fFromExt = extPos - closeExt;
42465
+ } else {
42466
+ const growExt = floorBtc(rawF - extPos, precision);
42467
+ fFromExt = extPos + growExt;
42468
+ }
42469
+ let fFromVesu;
42470
+ if (rawF < vesuPos) {
42471
+ const closeVesu = ceilBtc(vesuPos - rawF, precision);
42472
+ fFromVesu = vesuPos - closeVesu;
42473
+ } else {
42474
+ const growVesu = floorBtc(rawF - vesuPos, precision);
42475
+ fFromVesu = vesuPos + growVesu;
42476
+ }
42477
+ return Math.max(0, Math.min(fFromExt, fFromVesu));
42478
+ }
42479
+ function phase2(extPos, vesuPos, extEquity, vesuDebt, vesu, extLev, price, pool, precision) {
42480
+ const d = emptyDeltas();
42481
+ const targetLTV = computeVesuTargetLTV(vesu);
42482
+ const imbalance = extPos - vesuPos;
42483
+ let fundedGrowthBtc = 0;
42484
+ if (!isNegligible(imbalance, precision)) {
42485
+ if (imbalance > 0) {
42486
+ const equityCostUsd = computeVesuGrowthCost(imbalance, vesu, price);
42487
+ const { draws, filled } = drawFunds(equityCostUsd, ["vaUsd", "walletUsd", "extAvlWithdraw", "extUpnl"], pool);
42488
+ applyDrawsToDeltas(d, draws, -1);
42489
+ const grownBtc = computeVesuGrowthFromEquity(filled, vesu, price);
42490
+ d.dVesuPosition += grownBtc;
42491
+ fundedGrowthBtc = grownBtc;
42492
+ d.dVesuDebt += computeVesuGrowthDebt(grownBtc, vesu, price);
42493
+ const fromExt = sumKeys(draws, ["extAvlWithdraw", "extUpnl"]);
42494
+ if (fromExt > 0) d.dTransferVesuToExt -= fromExt;
42495
+ } else {
42496
+ const absImbalance = -imbalance;
42497
+ const marginCostUsd = absImbalance * price / extLev;
42498
+ const { draws, filled } = drawFunds(marginCostUsd, ["walletUsd", "vaUsd", "vesuBorrowCapacity"], pool);
42499
+ applyDrawsToDeltas(d, draws, -1);
42500
+ const grownBtc = filled * extLev / price;
42501
+ d.dExtPosition += grownBtc;
42502
+ fundedGrowthBtc = grownBtc;
42503
+ const fromVesu = draws.vesuBorrowCapacity ?? 0;
42504
+ if (fromVesu > 0) d.dTransferVesuToExt += fromVesu;
42505
+ }
42506
+ }
42507
+ const effExtPos = extPos + d.dExtPosition;
42508
+ const effVesuPos = vesuPos + d.dVesuPosition;
42509
+ const effExtEquity = extEquity;
42510
+ const effVesuDebt = vesuDebt + d.dVesuDebt;
42511
+ const rawF = solveUnifiedF(effExtPos, effVesuPos, effExtEquity, effVesuDebt, vesu, extLev, price);
42512
+ const cappedF = Math.min(rawF, Math.max(effExtPos, effVesuPos));
42513
+ const maxGrowableTo = Math.max(effExtPos, effVesuPos);
42514
+ const targetF = Math.max(0, Math.min(cappedF, maxGrowableTo));
42515
+ const remainingImbalance = effExtPos - effVesuPos;
42516
+ const extNeedsMore = effExtEquity < computeExtIdealMargin(effExtPos, extLev, price);
42517
+ const vesuCurrentLTV = effVesuPos > 0 ? effVesuDebt * vesu.debtPrice / (effVesuPos * price) : 0;
42518
+ const vesuNeedsMore = vesuCurrentLTV > targetLTV;
42519
+ const needsFurtherAction = !isNegligible(remainingImbalance, precision) || extNeedsMore || vesuNeedsMore;
42520
+ if (!needsFurtherAction) {
42521
+ return d;
42522
+ }
42523
+ const F = roundFinalPosition(effExtPos, effVesuPos, targetF, precision);
42524
+ const dx = F - effExtPos;
42525
+ const dy = F - effVesuPos;
42526
+ if (isNegligible(dx, precision) && isNegligible(dy, precision)) {
42527
+ return d;
42528
+ }
42529
+ d.dExtPosition += dx;
42530
+ d.dVesuPosition += dy;
42531
+ const debtRepay = solveDebtRepay(effVesuDebt, F, vesu, price);
42532
+ d.dVesuDebt -= debtRepay;
42533
+ const T = solveTransfer(effVesuPos, effVesuDebt, F, vesu, price);
42534
+ d.dTransferVesuToExt += T;
42535
+ d.dExtAvlWithdraw += T;
42536
+ return d;
42537
+ }
42538
+ function rebalance(inputs) {
42539
+ const { ext, vesu, btcPrice, config } = inputs;
42540
+ const pool = { ...inputs.funding };
42541
+ const p1 = phase1(ext, vesu, btcPrice, pool, config);
42542
+ const effExtPos = ext.positionBtc + p1.deltas.dExtPosition;
42543
+ const effVesuPos = vesu.positionBtc + p1.deltas.dVesuPosition;
42544
+ const effExtEquity = ext.equity + p1.deltas.dExtAvlWithdraw + p1.deltas.dExtUpnl;
42545
+ const effVesuDebt = vesu.debt + p1.deltas.dVesuDebt;
42546
+ const p2 = phase2(
42547
+ effExtPos,
42548
+ effVesuPos,
42549
+ effExtEquity,
42550
+ effVesuDebt,
42551
+ vesu,
42552
+ ext.leverage,
42553
+ btcPrice,
42554
+ pool,
42555
+ config.positionPrecision
42556
+ );
42557
+ return mergeDeltas(p1.deltas, p2);
42558
+ }
42559
+
41990
42560
  // src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts
41991
42561
  var RouteType = /* @__PURE__ */ ((RouteType2) => {
41992
42562
  RouteType2["WALLET_TO_EXTENDED"] = "WALLET_TO_EXTENDED";
@@ -42018,6 +42588,7 @@ var CaseCategory = /* @__PURE__ */ ((CaseCategory2) => {
42018
42588
  return CaseCategory2;
42019
42589
  })(CaseCategory || {});
42020
42590
  var CaseId = /* @__PURE__ */ ((CaseId2) => {
42591
+ CaseId2["MANAGE_LTV"] = "MANAGE_LTV";
42021
42592
  CaseId2["LTV_VESU_LOW_TO_EXTENDED"] = "LTV_VESU_LOW_TO_EXTENDED";
42022
42593
  CaseId2["LTV_EXTENDED_PROFITABLE_AVAILABLE"] = "LTV_EXTENDED_PROFITABLE_AVAILABLE";
42023
42594
  CaseId2["LTV_EXTENDED_PROFITABLE_REALIZE"] = "LTV_EXTENDED_PROFITABLE_REALIZE";
@@ -42042,19 +42613,56 @@ function safeUsdcWeb3Number(value) {
42042
42613
  return new Web3Number(value.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
42043
42614
  }
42044
42615
  var CASE_ROUTE_TYPES = {
42045
- // LTV Rebalance — Vesu side
42616
+ // LTV Rebalance — unified
42617
+ ["MANAGE_LTV" /* MANAGE_LTV */]: [
42618
+ "REALISE_PNL" /* REALISE_PNL */,
42619
+ "EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */,
42620
+ "RETURN_TO_WAIT" /* RETURN_TO_WAIT */,
42621
+ "WALLET_TO_VA" /* WALLET_TO_VA */,
42622
+ "VESU_BORROW" /* VESU_BORROW */,
42623
+ "VESU_REPAY" /* VESU_REPAY */,
42624
+ "VA_TO_EXTENDED" /* VA_TO_EXTENDED */,
42625
+ "VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */,
42626
+ "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */,
42627
+ "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
42628
+ "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
42629
+ "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */,
42630
+ // Second-phase VA / Extended funding after lever routes (same types may repeat).
42631
+ "RETURN_TO_WAIT" /* RETURN_TO_WAIT */,
42632
+ "WALLET_TO_VA" /* WALLET_TO_VA */,
42633
+ "VA_TO_EXTENDED" /* VA_TO_EXTENDED */
42634
+ ],
42635
+ /** @deprecated */
42046
42636
  ["LTV_VESU_HIGH_USE_VA_OR_WALLET" /* LTV_VESU_HIGH_USE_VA_OR_WALLET */]: ["WALLET_TO_VA" /* WALLET_TO_VA */, "VESU_REPAY" /* VESU_REPAY */],
42047
- // use wallet to va if wallet has and va doesnt have enough
42637
+ /** @deprecated */
42048
42638
  ["LTV_EXTENDED_PROFITABLE_AVAILABLE" /* LTV_EXTENDED_PROFITABLE_AVAILABLE */]: ["EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */, "RETURN_TO_WAIT" /* RETURN_TO_WAIT */, "WALLET_TO_VA" /* WALLET_TO_VA */, "VESU_REPAY" /* VESU_REPAY */],
42639
+ /** @deprecated */
42049
42640
  ["LTV_EXTENDED_PROFITABLE_REALIZE" /* LTV_EXTENDED_PROFITABLE_REALIZE */]: ["REALISE_PNL" /* REALISE_PNL */, "EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */, "RETURN_TO_WAIT" /* RETURN_TO_WAIT */, "WALLET_TO_VA" /* WALLET_TO_VA */, "VESU_REPAY" /* VESU_REPAY */],
42050
- // LTV Rebalance — Extended side
42641
+ /** @deprecated */
42051
42642
  ["LTV_EXTENDED_HIGH_USE_VA_OR_WALLET" /* LTV_EXTENDED_HIGH_USE_VA_OR_WALLET */]: ["VA_TO_EXTENDED" /* VA_TO_EXTENDED */, "WALLET_TO_EXTENDED" /* WALLET_TO_EXTENDED */],
42643
+ /** @deprecated */
42052
42644
  ["LTV_VESU_LOW_TO_EXTENDED" /* LTV_VESU_LOW_TO_EXTENDED */]: ["VESU_BORROW" /* VESU_BORROW */, "VA_TO_EXTENDED" /* VA_TO_EXTENDED */],
42053
42645
  // New Deposits
42054
42646
  // @dev, when handling routes, after VA_TO_EXTENDED and/or WALLET_TO_EXTENDED, return. bcz, funds take time to reach extended. Anyways, in next cycle, these funds will be computed to increase lever
42055
42647
  // Sequence: fund-movement transfers first (WALLET_TO_EXTENDED, VA_TO_EXTENDED, WALLET_TO_VA, EXTENDED_TO_WALLET),
42056
42648
  // then RETURN_TO_WAIT, then optional second WALLET_TO_VA + RETURN_TO_WAIT, then lever routes.
42057
- ["DEPOSIT_FRESH_VAULT" /* DEPOSIT_FRESH_VAULT */]: ["WALLET_TO_EXTENDED" /* WALLET_TO_EXTENDED */, "VA_TO_EXTENDED" /* VA_TO_EXTENDED */, "WALLET_TO_VA" /* WALLET_TO_VA */, "EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */, "RETURN_TO_WAIT" /* RETURN_TO_WAIT */, "WALLET_TO_VA" /* WALLET_TO_VA */, "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */, "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */, "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */],
42649
+ ["DEPOSIT_FRESH_VAULT" /* DEPOSIT_FRESH_VAULT */]: [
42650
+ // May repeat after MANAGE_LTV (VA top-up → Extended → wait → levers).
42651
+ "WALLET_TO_VA" /* WALLET_TO_VA */,
42652
+ "VA_TO_EXTENDED" /* VA_TO_EXTENDED */,
42653
+ "RETURN_TO_WAIT" /* RETURN_TO_WAIT */,
42654
+ "VA_TO_EXTENDED" /* VA_TO_EXTENDED */,
42655
+ "VESU_BORROW" /* VESU_BORROW */,
42656
+ "WALLET_TO_EXTENDED" /* WALLET_TO_EXTENDED */,
42657
+ "VA_TO_EXTENDED" /* VA_TO_EXTENDED */,
42658
+ "WALLET_TO_VA" /* WALLET_TO_VA */,
42659
+ "EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */,
42660
+ "RETURN_TO_WAIT" /* RETURN_TO_WAIT */,
42661
+ "WALLET_TO_VA" /* WALLET_TO_VA */,
42662
+ "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
42663
+ "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */,
42664
+ "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */
42665
+ ],
42058
42666
  ["DEPOSIT_EXTENDED_AVAILABLE" /* DEPOSIT_EXTENDED_AVAILABLE */]: ["EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */, "RETURN_TO_WAIT" /* RETURN_TO_WAIT */, "WALLET_TO_VA" /* WALLET_TO_VA */, "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */, "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */, "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */],
42059
42667
  ["DEPOSIT_VESU_BORROW_CAPACITY" /* DEPOSIT_VESU_BORROW_CAPACITY */]: ["VESU_BORROW" /* VESU_BORROW */, "VA_TO_EXTENDED" /* VA_TO_EXTENDED */, "RETURN_TO_WAIT" /* RETURN_TO_WAIT */, "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */, "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */, "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */],
42060
42668
  ["DEPOSIT_COMBINATION" /* DEPOSIT_COMBINATION */]: [],
@@ -42077,6 +42685,17 @@ var CASE_ROUTE_TYPES = {
42077
42685
  ["IMBALANCE_VESU_EXCESS_LONG_NO_FUNDS" /* IMBALANCE_VESU_EXCESS_LONG_NO_FUNDS */]: ["VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */]
42078
42686
  };
42079
42687
  var CASE_DEFINITIONS = {
42688
+ ["MANAGE_LTV" /* MANAGE_LTV */]: {
42689
+ id: "MANAGE_LTV" /* MANAGE_LTV */,
42690
+ category: "LTV_REBALANCE" /* LTV_REBALANCE */,
42691
+ title: "LTV Rebalance: Unified Vesu repay + Extended margin management",
42692
+ description: "Manages both Vesu high-LTV repayment and Extended low-margin funding in a single pass.",
42693
+ steps: [
42694
+ "Compute vesu repay needed and extended margin needed",
42695
+ "Allocate funds: VA > Wallet > ExtAvl > ExtUpnl for Vesu; Wallet > VA > Borrow for Extended",
42696
+ "Build combined transfer and repay/margin routes"
42697
+ ]
42698
+ },
42080
42699
  ["LTV_VESU_LOW_TO_EXTENDED" /* LTV_VESU_LOW_TO_EXTENDED */]: {
42081
42700
  id: "LTV_VESU_LOW_TO_EXTENDED" /* LTV_VESU_LOW_TO_EXTENDED */,
42082
42701
  category: "LTV_REBALANCE" /* LTV_REBALANCE */,
@@ -42299,199 +42918,590 @@ function routeSummary(r) {
42299
42918
  }
42300
42919
  var SolveBudget = class {
42301
42920
  constructor(state) {
42302
- // should be same length as vesuPerPoolDebtDeltasToBorrow
42303
- // ── Budget tracking (populated by initBudget) ──────────────────────
42304
- this._vaUsd = 0;
42305
- this._walletUsd = 0;
42306
- this._extAvailWithdraw = 0;
42307
- this._extAvailUpnl = 0;
42308
- this._extAvailTrade = 0;
42309
- this._extPendingDeposit = 0;
42310
- this._vesuBorrowCapacity = 0;
42311
- this._totalUnused = 0;
42312
- const buffer = state.limitBalanceBufferFactor;
42313
- this.unusedBalance = state.unusedBalance.map((item) => {
42314
- return {
42315
- ...item,
42316
- amount: item.amount.multipliedBy(1 - buffer),
42317
- usdValue: item.usdValue * (1 - buffer)
42318
- };
42921
+ this.assetToken = state.assetToken;
42922
+ this.usdcToken = state.usdcToken;
42923
+ const cloneTb = (b) => ({
42924
+ token: b.token,
42925
+ amount: new Web3Number(b.amount.toFixed(b.token.decimals), b.token.decimals),
42926
+ usdValue: b.usdValue
42319
42927
  });
42320
- this.walletBalance = state.walletBalance ? {
42321
- ...state.walletBalance,
42322
- amount: state.walletBalance.amount.multipliedBy(1 - buffer),
42323
- usdValue: state.walletBalance.usdValue * (1 - buffer)
42324
- } : null;
42325
- this.vaultBalance = state.vaultBalance ? {
42326
- ...state.vaultBalance,
42327
- amount: state.vaultBalance.amount.multipliedBy(1 - buffer),
42328
- usdValue: state.vaultBalance.usdValue * (1 - buffer)
42329
- } : null;
42330
- this.extendedPositions = state.extendedPositions;
42928
+ this.unusedBalance = state.unusedBalance.map((item) => cloneTb(item));
42929
+ this.walletBalance = state.walletBalance ? cloneTb(state.walletBalance) : null;
42930
+ this.vaultAssetBalance = state.vaultAssetBalance ? cloneTb(state.vaultAssetBalance) : null;
42931
+ this.vaultUsdcBalance = state.vaultUsdcBalance ? cloneTb(state.vaultUsdcBalance) : null;
42932
+ this.extendedPositions = state.extendedPositions.map((p) => ({
42933
+ ...p,
42934
+ size: new Web3Number(p.size.toFixed(8), 8),
42935
+ valueUsd: new Web3Number(p.valueUsd.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS)
42936
+ }));
42331
42937
  this.extendedBalance = state.extendedBalance ? {
42332
- ...state.extendedBalance,
42333
- availableForTrade: state.extendedBalance.availableForTrade.multipliedBy(1 - buffer),
42334
- availableForWithdrawal: state.extendedBalance.availableForWithdrawal.multipliedBy(1 - buffer),
42335
- unrealisedPnl: state.extendedBalance.unrealisedPnl.multipliedBy(1 - buffer),
42336
- balance: state.extendedBalance.balance.multipliedBy(1 - buffer)
42938
+ equity: new Web3Number(
42939
+ state.extendedBalance.equity.toFixed(USDC_TOKEN_DECIMALS),
42940
+ USDC_TOKEN_DECIMALS
42941
+ ),
42942
+ availableForTrade: new Web3Number(
42943
+ state.extendedBalance.availableForTrade.toFixed(USDC_TOKEN_DECIMALS),
42944
+ USDC_TOKEN_DECIMALS
42945
+ ),
42946
+ availableForWithdrawal: new Web3Number(
42947
+ state.extendedBalance.availableForWithdrawal.toFixed(USDC_TOKEN_DECIMALS),
42948
+ USDC_TOKEN_DECIMALS
42949
+ ),
42950
+ unrealisedPnl: new Web3Number(
42951
+ state.extendedBalance.unrealisedPnl.toFixed(USDC_TOKEN_DECIMALS),
42952
+ USDC_TOKEN_DECIMALS
42953
+ ),
42954
+ balance: new Web3Number(
42955
+ state.extendedBalance.balance.toFixed(USDC_TOKEN_DECIMALS),
42956
+ USDC_TOKEN_DECIMALS
42957
+ ),
42958
+ pendingDeposit: new Web3Number(
42959
+ state.extendedBalance.pendingDeposit.toFixed(USDC_TOKEN_DECIMALS),
42960
+ USDC_TOKEN_DECIMALS
42961
+ )
42337
42962
  } : null;
42338
- this.vesuPoolStates = state.vesuPoolStates;
42963
+ this.vesuPoolStates = state.vesuPoolStates.map((p) => ({
42964
+ ...p,
42965
+ collateralAmount: new Web3Number(
42966
+ p.collateralAmount.toFixed(p.collateralToken.decimals),
42967
+ p.collateralToken.decimals
42968
+ ),
42969
+ debtAmount: new Web3Number(
42970
+ p.debtAmount.toFixed(p.debtToken.decimals),
42971
+ p.debtToken.decimals
42972
+ )
42973
+ }));
42339
42974
  const vesuPerPoolDebtDeltasToBorrow = this._computeperPoolDebtDeltasToBorrow();
42340
42975
  assert(vesuPerPoolDebtDeltasToBorrow.length === this.vesuPoolStates.length, "vesuPerPoolDebtDeltasToBorrow length must match vesuPoolStates length");
42341
42976
  this.vesuPerPoolDebtDeltasToBorrow = vesuPerPoolDebtDeltasToBorrow.map((item) => item.deltaDebt);
42342
42977
  this.shouldVesuRebalance = vesuPerPoolDebtDeltasToBorrow.map((item) => item.shouldRebalance);
42343
42978
  }
42979
+ /** `1 - limitBalanceBufferFactor` — multiplier applied to raw notionals for “usable” USD. */
42980
+ _usableFraction() {
42981
+ return 1;
42982
+ }
42344
42983
  /**
42345
- * Initialise budget-tracking values from the current state snapshot.
42346
- * Must be called after state is populated and debt deltas are computed.
42347
- *
42348
- * Accounts for pendingDeposit:
42349
- * - pendingDeposit > 0: funds in transit TO Extended → increase effective Extended available-for-trade
42350
- * - pendingDeposit < 0: funds in transit FROM Extended → increase effective wallet balance
42984
+ * Raw USD notional for a token row. USDC (and configured {@link usdcToken}) uses 1:1 from amount;
42985
+ * non-stable assets (e.g. WBTC in VA) use {@link TokenBalance.usdValue} from the pricer at refresh.
42986
+ */
42987
+ _rawTokenUsd(tb) {
42988
+ if (!tb) return 0;
42989
+ if (this.usdcToken.address.eq(tb.token.address)) {
42990
+ return Number(tb.amount.toFixed(tb.token.decimals));
42991
+ }
42992
+ return tb.usdValue;
42993
+ }
42994
+ /** Apply safety buffer to a raw USD scalar. */
42995
+ bufferedUsd(rawUsd) {
42996
+ return rawUsd * this._usableFraction();
42997
+ }
42998
+ /** Convert a buffered “usable” USD amount to raw nominal USD (inverse of {@link bufferedUsd}). */
42999
+ rawUsdFromBuffered(bufferedUsd) {
43000
+ const bf = this._usableFraction();
43001
+ assert(bf > 0, "SolveBudget::rawUsdFromBuffered usable fraction must be positive");
43002
+ return bufferedUsd / bf;
43003
+ }
43004
+ /** Buffered USD notional for one token balance row. */
43005
+ bufferedTokenUsd(tb) {
43006
+ return this.bufferedUsd(this._rawTokenUsd(tb));
43007
+ }
43008
+ logStateSummary() {
43009
+ console.log("===== state summary =====");
43010
+ const aggregatedData = {
43011
+ unusedBalances: this.unusedBalance.map((b) => ({
43012
+ token: b.token.symbol,
43013
+ amount: b.amount.toNumber()
43014
+ })),
43015
+ walletBalance: this.walletBalance ? {
43016
+ token: this.walletBalance.token.symbol,
43017
+ amount: this.walletBalance.amount.toNumber()
43018
+ } : void 0,
43019
+ vaultAssetBalance: this.vaultAssetBalance ? {
43020
+ token: this.vaultAssetBalance.token.symbol,
43021
+ amount: this.vaultAssetBalance.amount.toNumber()
43022
+ } : void 0,
43023
+ vaultUsdcBalance: this.vaultUsdcBalance ? {
43024
+ token: this.vaultUsdcBalance.token.symbol,
43025
+ amount: this.vaultUsdcBalance.amount.toNumber()
43026
+ } : void 0,
43027
+ vesuPoolStates: this.vesuPoolStates.map((p) => ({
43028
+ poolId: p.poolId,
43029
+ collateralAmount: p.collateralAmount.toNumber(),
43030
+ debtAmount: p.debtAmount.toNumber()
43031
+ })),
43032
+ vesuBorrowCapacity: this.vesuBorrowCapacity,
43033
+ vesuRebalance: this.shouldVesuRebalance,
43034
+ vesuPerPoolDebtDeltasToBorrow: this.vesuPerPoolDebtDeltasToBorrow.map((d) => d.toNumber()),
43035
+ extendedBalance: this.extendedBalance?.balance.toNumber(),
43036
+ extendedEquity: this.extendedBalance?.equity.toNumber(),
43037
+ extendedAvailableForTrade: this.extendedBalance?.availableForTrade.toNumber(),
43038
+ extendedAvailableForWithdrawal: this.extendedBalance?.availableForWithdrawal.toNumber(),
43039
+ extendedUnrealisedPnl: this.extendedBalance?.unrealisedPnl.toNumber(),
43040
+ extendedPendingDeposit: this.extendedBalance?.pendingDeposit.toNumber(),
43041
+ extendedPositions: this.extendedPositions.map((p) => ({
43042
+ instrument: p.instrument,
43043
+ size: p.size.toNumber(),
43044
+ valueUsd: p.valueUsd.toNumber()
43045
+ }))
43046
+ };
43047
+ console.log(
43048
+ "unused balances",
43049
+ aggregatedData.unusedBalances.map((b) => `${b.token}=${b.amount}`).join(", ")
43050
+ );
43051
+ console.log(
43052
+ "wallet balance",
43053
+ aggregatedData.walletBalance ? `${aggregatedData.walletBalance.token}=${aggregatedData.walletBalance.amount}` : void 0
43054
+ );
43055
+ console.log(
43056
+ "vault asset balance",
43057
+ aggregatedData.vaultAssetBalance ? `${aggregatedData.vaultAssetBalance.token}=${aggregatedData.vaultAssetBalance.amount}` : void 0
43058
+ );
43059
+ console.log(
43060
+ "vault usdc balance",
43061
+ aggregatedData.vaultUsdcBalance ? `${aggregatedData.vaultUsdcBalance.token}=${aggregatedData.vaultUsdcBalance.amount}` : void 0
43062
+ );
43063
+ console.log(
43064
+ "vesu pool states",
43065
+ aggregatedData.vesuPoolStates.map(
43066
+ (p) => `${p.poolId.shortString()}=${p.collateralAmount} ${p.debtAmount}`
43067
+ ).join(", ")
43068
+ );
43069
+ console.log("vesu borrow capacity", aggregatedData.vesuBorrowCapacity);
43070
+ console.log(
43071
+ "vesu rebalance",
43072
+ aggregatedData.vesuRebalance.map(String).join(", ")
43073
+ );
43074
+ console.log("vesu per pool debt deltas to borrow", aggregatedData.vesuPerPoolDebtDeltasToBorrow.join(", "));
43075
+ console.log("extended balance", aggregatedData.extendedBalance);
43076
+ console.log("extended equity", aggregatedData.extendedEquity);
43077
+ console.log("extended available for trade", aggregatedData.extendedAvailableForTrade);
43078
+ console.log("extended available for withdrawal", aggregatedData.extendedAvailableForWithdrawal);
43079
+ console.log("extended unrealised pnl", aggregatedData.extendedUnrealisedPnl);
43080
+ console.log("extended pending deposit", aggregatedData.extendedPendingDeposit);
43081
+ console.log(
43082
+ "extended positions",
43083
+ aggregatedData.extendedPositions.map(
43084
+ (p) => `${p.instrument}=${p.size} ${p.valueUsd}`
43085
+ ).join(", ")
43086
+ );
43087
+ return aggregatedData;
43088
+ }
43089
+ /**
43090
+ * Initialise derived views for a solve cycle. Mutates only when pending
43091
+ * withdrawal from Extended is in transit (credits wallet raw balance).
42351
43092
  */
42352
43093
  initBudget() {
42353
- const debtDeltaNum = this.vesuPerPoolDebtDeltasToBorrow.reduce((a, b) => a + b.toNumber(), 0);
42354
- let totalUnusedUsd = this.unusedBalance.reduce((a, b) => a + b.usdValue, 0);
42355
- if (debtDeltaNum > 0) totalUnusedUsd += debtDeltaNum;
42356
- const extAvailTrade = this.extendedBalance?.availableForTrade?.toNumber() ?? 0;
42357
- if (extAvailTrade > 0) totalUnusedUsd += extAvailTrade;
42358
- if (debtDeltaNum > 0) totalUnusedUsd += debtDeltaNum;
42359
- this._vaUsd = this.vaultBalance?.usdValue ?? 0;
42360
- this._walletUsd = this.walletBalance?.usdValue ?? 0;
42361
- this._extAvailWithdraw = this.extendedBalance?.availableForWithdrawal?.toNumber() ?? 0;
42362
- this._extAvailUpnl = this.extendedBalance?.unrealisedPnl?.toNumber() ?? 0;
42363
- this._extAvailTrade = extAvailTrade;
42364
- this._extPendingDeposit = this.extendedBalance?.pendingDeposit?.toNumber() ?? 0;
42365
- this._vesuBorrowCapacity = debtDeltaNum;
42366
- this._totalUnused = totalUnusedUsd;
42367
43094
  const pendingDeposit = this.extendedBalance?.pendingDeposit?.toNumber() ?? 0;
42368
- if (pendingDeposit > 0) {
42369
- this._extAvailTrade += pendingDeposit;
42370
- this._totalUnused += pendingDeposit;
42371
- logger.debug(`SolveBudget::initBudget pendingDeposit=${pendingDeposit} -> increased extAvailTrade`);
42372
- } else if (pendingDeposit < 0) {
43095
+ if (pendingDeposit < 0) {
42373
43096
  const inTransit = Math.abs(pendingDeposit);
42374
- this._walletUsd += inTransit;
42375
- this._totalUnused += inTransit;
42376
- logger.debug(`SolveBudget::initBudget pendingDeposit=${pendingDeposit} -> increased walletUsd by ${inTransit}`);
43097
+ if (this.walletBalance) {
43098
+ this._addUsdToTokenBalance(this.walletBalance, inTransit);
43099
+ }
43100
+ logger.debug(`SolveBudget::initBudget pendingDeposit=${pendingDeposit} -> increased wallet raw USD by ${inTransit}`);
43101
+ }
43102
+ this._recomputeUnusedBalance();
43103
+ }
43104
+ /**
43105
+ * Apply a safety buffer to all liquid balances (VA, wallet, extended trade/withdraw/upnl,
43106
+ * unused balances). Call after withdrawal classification but before LTV/deposit classifiers
43107
+ * so that withdrawal uses full raw amounts while subsequent classifiers see buffered values.
43108
+ */
43109
+ applyBuffer(factor) {
43110
+ if (factor <= 0 || factor >= 1) return;
43111
+ const mult = 1 - factor;
43112
+ const scaleTokenBalance = (tb) => {
43113
+ if (!tb) return;
43114
+ const newAmount = tb.amount.multipliedBy(mult);
43115
+ tb.amount = new Web3Number(newAmount.toFixed(tb.token.decimals), tb.token.decimals);
43116
+ tb.usdValue = tb.usdValue * mult;
43117
+ };
43118
+ scaleTokenBalance(this.vaultAssetBalance);
43119
+ scaleTokenBalance(this.vaultUsdcBalance);
43120
+ scaleTokenBalance(this.walletBalance);
43121
+ for (const ub of this.unusedBalance) {
43122
+ scaleTokenBalance(ub);
43123
+ }
43124
+ if (this.extendedBalance) {
43125
+ this.extendedBalance.availableForTrade = this.extendedBalance.availableForTrade.multipliedBy(mult);
43126
+ this.extendedBalance.availableForWithdrawal = this.extendedBalance.availableForWithdrawal.multipliedBy(mult);
43127
+ this.extendedBalance.unrealisedPnl = this.extendedBalance.unrealisedPnl.multipliedBy(mult);
42377
43128
  }
43129
+ this._recomputeUnusedBalance();
43130
+ }
43131
+ get vesuPoolState() {
43132
+ assert(this.vesuPoolStates.length === 1, "SolveBudget::vesuPoolState: vesuPoolStates length must be 1");
43133
+ return this.vesuPoolStates[0];
42378
43134
  }
42379
- // ── Read-only getters ────────────────────────────────────────────────
43135
+ // ── Derived getters (buffered where applicable) ─────────────────────
43136
+ /** Buffered VA USD: strategy-asset slot + optional USDC slot. */
42380
43137
  get vaUsd() {
42381
- return this._vaUsd;
43138
+ return this.bufferedTokenUsd(this.vaultAssetBalance) + this.bufferedTokenUsd(this.vaultUsdcBalance);
43139
+ }
43140
+ /** Buffered USD in VA strategy-asset bucket only. */
43141
+ get vaAssetUsd() {
43142
+ return this.bufferedTokenUsd(this.vaultAssetBalance);
43143
+ }
43144
+ /** Buffered USD in VA USDC bucket (0 when asset === USDC). */
43145
+ get vaUsdcUsd() {
43146
+ return this.bufferedTokenUsd(this.vaultUsdcBalance);
42382
43147
  }
42383
43148
  get walletUsd() {
42384
- return this._walletUsd;
43149
+ return this.bufferedUsd(this._rawTokenUsd(this.walletBalance));
42385
43150
  }
42386
43151
  get vaWalletUsd() {
42387
- return this._vaUsd + this._walletUsd;
43152
+ return this.vaUsd + this.walletUsd;
42388
43153
  }
42389
43154
  get extAvailWithdraw() {
42390
- return this._extAvailWithdraw;
43155
+ const raw = this.extendedBalance?.availableForWithdrawal?.toNumber() ?? 0;
43156
+ return this.bufferedUsd(raw);
42391
43157
  }
42392
43158
  get extAvailUpnl() {
42393
- return this._extAvailUpnl;
43159
+ const raw = this.extendedBalance?.unrealisedPnl?.toNumber() ?? 0;
43160
+ return this.bufferedUsd(raw);
42394
43161
  }
43162
+ /**
43163
+ * Buffered Extended available-for-trade plus positive {@link ExtendedBalanceState.pendingDeposit}
43164
+ * (deposit in transit is usable the same way as the pre-buffer implementation).
43165
+ */
42395
43166
  get extAvailTrade() {
42396
- return this._extAvailTrade;
43167
+ const raw = this.extendedBalance?.availableForTrade?.toNumber() ?? 0;
43168
+ let v = this.bufferedUsd(raw);
43169
+ const pd = this.extendedBalance?.pendingDeposit?.toNumber() ?? 0;
43170
+ if (pd > 0) v += pd;
43171
+ return v;
43172
+ }
43173
+ get extPendingDeposit() {
43174
+ return this.extendedBalance?.pendingDeposit?.toNumber() ?? 0;
42397
43175
  }
43176
+ /**
43177
+ * Aggregate positive per-pool borrow headroom (USD). Repay/borrow routes update
43178
+ * {@link vesuPerPoolDebtDeltasToBorrow}; no separate counter.
43179
+ */
42398
43180
  get vesuBorrowCapacity() {
42399
- return this._vesuBorrowCapacity;
43181
+ return this.vesuPerPoolDebtDeltasToBorrow.reduce(
43182
+ (a, d) => a + Math.max(0, d.toNumber()),
43183
+ 0
43184
+ );
42400
43185
  }
43186
+ /** Diagnostic: buffered idle + positive debt delta + buffered Extended afT + in-flight deposit. */
42401
43187
  get totalUnused() {
42402
- return this._totalUnused;
43188
+ const debtSum = this.vesuPerPoolDebtDeltasToBorrow.reduce((a, b) => a + b.toNumber(), 0);
43189
+ let u = this.unusedBalance.reduce((a, b) => a + this.bufferedTokenUsd(b), 0);
43190
+ if (debtSum > 0) u += debtSum;
43191
+ const rawAft = this.extendedBalance?.availableForTrade?.toNumber() ?? 0;
43192
+ const aftBuf = this.bufferedUsd(rawAft);
43193
+ if (aftBuf > 0) u += aftBuf;
43194
+ const pd = this.extendedBalance?.pendingDeposit?.toNumber() ?? 0;
43195
+ if (pd > 0) u += pd;
43196
+ return u;
43197
+ }
43198
+ /** Sum of buffered USD across merged unused-balance rows (VA + wallet). */
43199
+ get unusedBalancesBufferedUsdSum() {
43200
+ return this.unusedBalance.reduce((a, b) => a + this.bufferedTokenUsd(b), 0);
43201
+ }
43202
+ /** Read-only snapshot view for validation / logging. */
43203
+ get unusedBalanceRows() {
43204
+ return this.unusedBalance;
43205
+ }
43206
+ /** Read-only Vesu pool view for solve computations. */
43207
+ get vesuPools() {
43208
+ return this.vesuPoolStates;
43209
+ }
43210
+ /** Read-only Extended positions view for solve computations. */
43211
+ get extendedPositionsView() {
43212
+ return this.extendedPositions;
43213
+ }
43214
+ /** Read-only Extended balance view for diagnostics / margin checks. */
43215
+ get extendedBalanceView() {
43216
+ return this.extendedBalance;
43217
+ }
43218
+ /** Current debt deltas per pool (positive=borrow, negative=repay). */
43219
+ get vesuDebtDeltas() {
43220
+ return this.vesuPerPoolDebtDeltasToBorrow;
43221
+ }
43222
+ /** Per-pool rebalance flags derived from target HF checks. */
43223
+ get vesuRebalanceFlags() {
43224
+ return this.shouldVesuRebalance;
43225
+ }
43226
+ /** Raw USD in VA (USDC slot + asset slot); spend caps when executing transfers. */
43227
+ _vaRawUsd() {
43228
+ return this._rawTokenUsd(this.vaultUsdcBalance) + this._rawTokenUsd(this.vaultAssetBalance);
43229
+ }
43230
+ _walletRawUsd() {
43231
+ return this._rawTokenUsd(this.walletBalance);
43232
+ }
43233
+ // ── Token snapshot helpers (keep vault / wallet / unusedBalance aligned) ─
43234
+ /** Remove up to `usd` notional from a token balance, scaling token amount proportionally. */
43235
+ _deductUsdFromTokenBalance(tb, usd) {
43236
+ if (usd <= 0) return;
43237
+ const take = Math.min(usd, tb.usdValue);
43238
+ if (take <= 0) return;
43239
+ const oldUsd = tb.usdValue;
43240
+ const newUsd = Math.max(0, oldUsd - take);
43241
+ tb.usdValue = newUsd;
43242
+ if (oldUsd <= 0) return;
43243
+ const ratio = newUsd / oldUsd;
43244
+ tb.amount = new Web3Number(
43245
+ (tb.amount.toNumber() * ratio).toFixed(tb.token.decimals),
43246
+ tb.token.decimals
43247
+ );
43248
+ }
43249
+ /** Add USD notional; infers price from current amount/usd when possible, else 1:1. */
43250
+ _addUsdToTokenBalance(tb, usd) {
43251
+ if (usd <= 0) return;
43252
+ const amtNum = tb.amount.toNumber();
43253
+ const price = amtNum > 0 && tb.usdValue > 0 ? tb.usdValue / amtNum : 1;
43254
+ const deltaTok = usd / price;
43255
+ tb.usdValue += usd;
43256
+ tb.amount = tb.amount.plus(
43257
+ new Web3Number(deltaTok.toFixed(tb.token.decimals), tb.token.decimals)
43258
+ );
42403
43259
  }
42404
- get extPendingDeposit() {
42405
- return this._extPendingDeposit;
43260
+ /**
43261
+ * Rebuilds {@link unusedBalance} from vault + wallet snapshots (same merge as refresh).
43262
+ */
43263
+ _recomputeUnusedBalance() {
43264
+ const balanceMap = /* @__PURE__ */ new Map();
43265
+ const merge = (b) => {
43266
+ if (!b) return;
43267
+ const key = b.token.address.toString();
43268
+ const row = {
43269
+ token: b.token,
43270
+ amount: new Web3Number(b.amount.toFixed(b.token.decimals), b.token.decimals),
43271
+ usdValue: b.usdValue
43272
+ };
43273
+ const existing = balanceMap.get(key);
43274
+ if (existing) {
43275
+ existing.amount = new Web3Number(
43276
+ existing.amount.plus(row.amount).toFixed(existing.token.decimals),
43277
+ existing.token.decimals
43278
+ );
43279
+ existing.usdValue += row.usdValue;
43280
+ } else {
43281
+ balanceMap.set(key, row);
43282
+ }
43283
+ };
43284
+ merge(this.vaultAssetBalance);
43285
+ merge(this.vaultUsdcBalance);
43286
+ merge(this.walletBalance);
43287
+ this.unusedBalance = Array.from(balanceMap.values());
42406
43288
  }
42407
43289
  // ── Spend methods (return amount consumed, auto-decrement totalUnused) ─
42408
- spendVA(desired) {
42409
- const used = Math.min(this._vaUsd, Math.max(0, desired));
42410
- this._vaUsd -= used;
42411
- this._totalUnused -= used;
42412
- logger.debug(`SolveBudget::spendVA used=${used}, vaUsd=${this._vaUsd}, totalUnused=${this._totalUnused}`);
42413
- return used;
42414
- }
42415
- addToVA(amount) {
42416
- assert(amount >= 0, "SolveBudget::addToVA amount must be positive");
42417
- this._vaUsd += amount;
42418
- this._totalUnused += amount;
42419
- logger.debug(`SolveBudget::addToVA amount=${amount}, vaUsd=${this._vaUsd}, totalUnused=${this._totalUnused}`);
42420
- }
42421
- spendWallet(desired) {
42422
- const used = Math.min(this._walletUsd, Math.max(0, desired));
42423
- this._walletUsd -= used;
42424
- this._totalUnused -= used;
42425
- logger.debug(`SolveBudget::spendWallet used=${used}, walletUsd=${this._walletUsd}, totalUnused=${this._totalUnused}`);
42426
- return used;
43290
+ /**
43291
+ * Spend VA **raw** USD (up to {@link vaRawUsd}). Prefer {@link vaultUsdcBalance} when present, then {@link vaultAssetBalance}.
43292
+ */
43293
+ spendVA(rawDesired) {
43294
+ const capRaw = this._vaRawUsd();
43295
+ const usedRaw = Math.min(capRaw, Math.max(0, rawDesired));
43296
+ if (usedRaw <= CASE_THRESHOLD_USD) return 0;
43297
+ let rem = usedRaw;
43298
+ if (rem > 0 && this.vaultUsdcBalance && this.vaultUsdcBalance.usdValue > 0) {
43299
+ const fromUsdc = Math.min(rem, this.vaultUsdcBalance.usdValue);
43300
+ this._deductUsdFromTokenBalance(this.vaultUsdcBalance, fromUsdc);
43301
+ rem -= fromUsdc;
43302
+ }
43303
+ if (rem > 0 && this.vaultAssetBalance) {
43304
+ this._deductUsdFromTokenBalance(this.vaultAssetBalance, rem);
43305
+ }
43306
+ this._recomputeUnusedBalance();
43307
+ logger.debug(`SolveBudget::spendVA usedRaw=${usedRaw}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
43308
+ return usedRaw;
42427
43309
  }
42428
- addToWallet(amount) {
42429
- assert(amount >= 0, "SolveBudget::addToWallet amount must be positive");
42430
- this._walletUsd += amount;
42431
- this._totalUnused += amount;
42432
- logger.debug(`SolveBudget::addToWallet amount=${amount}, walletUsd=${this._walletUsd}, totalUnused=${this._totalUnused}`);
43310
+ /**
43311
+ * Spend nominal/raw USD from VA (e.g. Vesu repay, on-chain USDC). Does not apply the safety buffer to the cap.
43312
+ */
43313
+ spendVaRawUsd(rawUsdDesired) {
43314
+ const capRaw = this._rawTokenUsd(this.vaultUsdcBalance) + this._rawTokenUsd(this.vaultAssetBalance);
43315
+ const usedRaw = Math.min(capRaw, Math.max(0, rawUsdDesired));
43316
+ if (usedRaw <= CASE_THRESHOLD_USD) return 0;
43317
+ let rem = usedRaw;
43318
+ if (rem > 0 && this.vaultUsdcBalance && this.vaultUsdcBalance.usdValue > 0) {
43319
+ const fromUsdc = Math.min(rem, this.vaultUsdcBalance.usdValue);
43320
+ this._deductUsdFromTokenBalance(this.vaultUsdcBalance, fromUsdc);
43321
+ rem -= fromUsdc;
43322
+ }
43323
+ if (rem > 0 && this.vaultAssetBalance) {
43324
+ this._deductUsdFromTokenBalance(this.vaultAssetBalance, rem);
43325
+ }
43326
+ this._recomputeUnusedBalance();
43327
+ logger.debug(`SolveBudget::spendVaRawUsd usedRaw=${usedRaw}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
43328
+ return usedRaw;
42433
43329
  }
42434
- spendVAWallet(desired) {
42435
- let remaining = Math.max(0, desired);
43330
+ /**
43331
+ * Add **raw nominal USD** to VA (borrow proceeds, wallet→VA in raw USDC, etc.).
43332
+ */
43333
+ addToVA(rawUsd) {
43334
+ assert(rawUsd >= 0, "SolveBudget::addToVA amount must be positive");
43335
+ if (rawUsd === 0) return;
43336
+ if (this.vaultUsdcBalance) {
43337
+ this._addUsdToTokenBalance(this.vaultUsdcBalance, rawUsd);
43338
+ } else if (this.vaultAssetBalance) {
43339
+ this._addUsdToTokenBalance(this.vaultAssetBalance, rawUsd);
43340
+ }
43341
+ this._recomputeUnusedBalance();
43342
+ logger.debug(`SolveBudget::addToVA rawUsd=${rawUsd}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
43343
+ }
43344
+ spendWallet(rawDesired) {
43345
+ const capRaw = this._walletRawUsd();
43346
+ const usedRaw = Math.min(capRaw, Math.max(0, rawDesired));
43347
+ if (usedRaw <= CASE_THRESHOLD_USD) return 0;
43348
+ if (this.walletBalance) {
43349
+ this._deductUsdFromTokenBalance(this.walletBalance, usedRaw);
43350
+ }
43351
+ this._recomputeUnusedBalance();
43352
+ logger.debug(`SolveBudget::spendWallet usedRaw=${usedRaw}, walletUsd=${this.walletUsd}, totalUnused=${this.totalUnused}`);
43353
+ return usedRaw;
43354
+ }
43355
+ /** Add **raw nominal USD** to the operator wallet balance (e.g. Extended→wallet withdrawal). */
43356
+ addToWallet(rawUsd) {
43357
+ assert(rawUsd >= 0, "SolveBudget::addToWallet amount must be positive");
43358
+ if (rawUsd === 0) return;
43359
+ if (this.walletBalance) {
43360
+ this._addUsdToTokenBalance(this.walletBalance, rawUsd);
43361
+ }
43362
+ this._recomputeUnusedBalance();
43363
+ logger.debug(`SolveBudget::addToWallet rawUsd=${rawUsd}, walletUsd=${this.walletUsd}, totalUnused=${this.totalUnused}`);
43364
+ }
43365
+ spendVAWallet(rawDesired) {
43366
+ let remaining = Math.max(0, rawDesired);
42436
43367
  const vaSpent = this.spendVA(remaining);
42437
43368
  remaining -= vaSpent;
42438
43369
  const walletSpent = this.spendWallet(remaining);
42439
43370
  return vaSpent + walletSpent;
42440
43371
  }
42441
- _updateExtAvailWithdraw(desired, isSpend) {
42442
- let amount = desired;
42443
- assert(amount > 0, "SolveBudget::_updateExtAvailWithdraw amount must be positive");
43372
+ _updateExtAvailWithdraw(desiredRaw, isSpend) {
43373
+ assert(desiredRaw > 0, "SolveBudget::_updateExtAvailWithdraw amount must be positive");
43374
+ let rawDelta;
42444
43375
  if (isSpend) {
42445
- amount = Math.min(this._extAvailWithdraw, Math.max(0, desired));
42446
- amount = -amount;
43376
+ const capRaw = this.extendedBalance?.availableForWithdrawal?.toNumber() ?? 0;
43377
+ const useRaw = Math.min(capRaw, desiredRaw);
43378
+ if (useRaw <= CASE_THRESHOLD_USD) return 0;
43379
+ rawDelta = -useRaw;
43380
+ } else {
43381
+ rawDelta = desiredRaw;
42447
43382
  }
42448
- this._extAvailWithdraw += amount;
42449
- this._totalUnused += amount;
42450
- this._extAvailTrade += amount;
42451
43383
  if (this.extendedBalance) {
42452
- this.extendedBalance.availableForWithdrawal = safeUsdcWeb3Number(this.extendedBalance.availableForWithdrawal.toNumber() + amount);
42453
- this.extendedBalance.availableForTrade = safeUsdcWeb3Number(this.extendedBalance.availableForTrade.toNumber() + amount);
42454
- this.extendedBalance.balance = safeUsdcWeb3Number(this.extendedBalance.balance.toNumber() + amount);
42455
- this.extendedBalance.equity = safeUsdcWeb3Number(this.extendedBalance.equity.toNumber() + amount);
43384
+ this.extendedBalance.availableForWithdrawal = safeUsdcWeb3Number(this.extendedBalance.availableForWithdrawal.toNumber() + rawDelta);
43385
+ this.extendedBalance.availableForTrade = safeUsdcWeb3Number(this.extendedBalance.availableForTrade.toNumber() + rawDelta);
43386
+ this.extendedBalance.balance = safeUsdcWeb3Number(this.extendedBalance.balance.toNumber() + rawDelta);
43387
+ this.extendedBalance.equity = safeUsdcWeb3Number(this.extendedBalance.equity.toNumber() + rawDelta);
42456
43388
  }
42457
- logger.debug(`SolveBudget::updateExtAvailWithdraw amount=${amount}, extAvailWithdraw=${this._extAvailWithdraw}, totalUnused=${this._totalUnused}`);
42458
- return amount;
43389
+ logger.debug(`SolveBudget::updateExtAvailWithdraw rawDelta=${rawDelta}, extAvailWithdraw=${this.extAvailWithdraw}, totalUnused=${this.totalUnused}`);
43390
+ return rawDelta;
42459
43391
  }
42460
- _updateExtAvailUpnl(desired, isSpend) {
42461
- let amount = desired;
42462
- assert(amount > 0, "SolveBudget::_updateExtAvailUpnl amount must be positive");
43392
+ _updateExtAvailUpnl(desiredRaw, isSpend) {
43393
+ assert(desiredRaw > 0, "SolveBudget::_updateExtAvailUpnl amount must be positive");
43394
+ let rawDelta;
42463
43395
  if (isSpend) {
42464
- amount = Math.min(this._extAvailUpnl, Math.max(0, desired));
42465
- amount = -amount;
43396
+ const capRaw = this.extendedBalance?.unrealisedPnl?.toNumber() ?? 0;
43397
+ const useRaw = Math.min(capRaw, desiredRaw);
43398
+ if (useRaw <= CASE_THRESHOLD_USD) return 0;
43399
+ rawDelta = -useRaw;
43400
+ } else {
43401
+ rawDelta = desiredRaw;
42466
43402
  }
42467
- this._extAvailUpnl += amount;
42468
- this._totalUnused += amount;
42469
43403
  if (this.extendedBalance) {
42470
- this.extendedBalance.unrealisedPnl = safeUsdcWeb3Number(this.extendedBalance.unrealisedPnl.toNumber() + amount);
42471
- this.extendedBalance.balance = safeUsdcWeb3Number(this.extendedBalance.balance.toNumber() + amount);
42472
- this.extendedBalance.equity = safeUsdcWeb3Number(this.extendedBalance.equity.toNumber() + amount);
42473
- this.extendedBalance.availableForTrade = safeUsdcWeb3Number(this.extendedBalance.availableForTrade.toNumber() + amount);
42474
- }
42475
- logger.debug(`SolveBudget::updateExtAvailUpnl amount=${amount}, extAvailUpnl=${this._extAvailUpnl}, totalUnused=${this._totalUnused}`);
42476
- return amount;
42477
- }
42478
- spendExtAvailTrade(desired) {
42479
- const used = this._updateExtAvailWithdraw(desired, true);
42480
- const usedUpnl = this._updateExtAvailUpnl(desired, true);
42481
- logger.debug(`SolveBudget::updateExtAvailTrade amount=${used + usedUpnl}, extAvailTrade=${this._extAvailTrade}, totalUnused=${this._totalUnused}`);
43404
+ this.extendedBalance.unrealisedPnl = safeUsdcWeb3Number(this.extendedBalance.unrealisedPnl.toNumber() + rawDelta);
43405
+ this.extendedBalance.balance = safeUsdcWeb3Number(this.extendedBalance.balance.toNumber() + rawDelta);
43406
+ this.extendedBalance.equity = safeUsdcWeb3Number(this.extendedBalance.equity.toNumber() + rawDelta);
43407
+ this.extendedBalance.availableForTrade = safeUsdcWeb3Number(this.extendedBalance.availableForTrade.toNumber() + rawDelta);
43408
+ }
43409
+ logger.debug(`SolveBudget::updateExtAvailUpnl rawDelta=${rawDelta}, extAvailUpnl=${this.extAvailUpnl}, totalUnused=${this.totalUnused}`);
43410
+ return rawDelta;
43411
+ }
43412
+ spendExtAvailTrade(rawDesired) {
43413
+ const used = this._updateExtAvailWithdraw(rawDesired, true);
43414
+ const usedUpnl = this._updateExtAvailUpnl(rawDesired, true);
43415
+ logger.debug(`SolveBudget::updateExtAvailTrade rawSum=${used + usedUpnl}, extAvailTrade=${this.extAvailTrade}, totalUnused=${this.totalUnused}`);
42482
43416
  return used + usedUpnl;
42483
43417
  }
42484
- spendExtAvailUpnl(desired) {
42485
- return this._updateExtAvailUpnl(desired, true);
43418
+ // simply reduces available amounts, but maintains equity and balance.
43419
+ spendExtAvailTradeToEquityOnly(rawDesired) {
43420
+ const used = this._updateExtAvailWithdraw(rawDesired, true);
43421
+ const remaining = rawDesired - Math.abs(used);
43422
+ const usedUpnl = remaining > 0 ? this._updateExtAvailUpnl(remaining, true) : 0;
43423
+ if (this.extendedBalance) {
43424
+ const net = Math.abs(used) + Math.abs(usedUpnl);
43425
+ if (net.toFixed(0) != rawDesired.toFixed(0)) {
43426
+ throw new Error(`SolveBudget::spendExtAvailTradeToEquityOnly net=${net} != rawDesired=${rawDesired}`);
43427
+ }
43428
+ this.extendedBalance.equity = safeUsdcWeb3Number(this.extendedBalance.equity.toNumber() + net);
43429
+ this.extendedBalance.balance = safeUsdcWeb3Number(this.extendedBalance.balance.toNumber() + net);
43430
+ }
43431
+ logger.debug(`SolveBudget::updateExtAvailTrade rawSum=${used + usedUpnl}, extAvailTrade=${this.extAvailTrade}, totalUnused=${this.totalUnused}`);
43432
+ return used + usedUpnl;
42486
43433
  }
42487
- addToExtAvailTrade(amount) {
42488
- this._updateExtAvailWithdraw(amount, false);
42489
- logger.debug(`SolveBudget::addToExtAvailTrade amount=${amount}, extAvailTrade=${this._extAvailTrade}, totalUnused=${this._totalUnused}`);
43434
+ spendExtAvailWithdraw(rawDesired) {
43435
+ return this._updateExtAvailWithdraw(rawDesired, true);
43436
+ }
43437
+ spendExtAvailUpnl(rawDesired) {
43438
+ return this._updateExtAvailUpnl(rawDesired, true);
43439
+ }
43440
+ /**
43441
+ * Withdraw from Extended **withdrawal bucket only** to operator wallet (planning).
43442
+ * Used when VA must be funded from Extended and withdraw should be exhausted before unrealised PnL.
43443
+ */
43444
+ spendAvailWithdrawToWallet(rawDesired) {
43445
+ const want = Math.max(0, rawDesired);
43446
+ if (want <= CASE_THRESHOLD_USD) return 0;
43447
+ const rawDelta = this._updateExtAvailWithdraw(want, true);
43448
+ if (rawDelta === 0) return 0;
43449
+ const used = -rawDelta;
43450
+ this.addToWallet(used);
43451
+ return used;
43452
+ }
43453
+ /**
43454
+ * Required Extended equity (USD) for current open positions: total notional ÷ strategy leverage.
43455
+ * Same basis as {@link ExtendedSVKVesuStateManager._classifyLtvExtended} margin check.
43456
+ */
43457
+ _extendedMarginRequirementUsd() {
43458
+ const lev = calculateExtendedLevergae();
43459
+ if (lev <= 0 || this.extendedPositions.length === 0) return 0;
43460
+ const totalPosUsd = this.extendedPositions.reduce(
43461
+ (s, p) => s + p.valueUsd.toNumber(),
43462
+ 0
43463
+ );
43464
+ return totalPosUsd / lev;
43465
+ }
43466
+ /** How much more equity is needed before deposits should increase withdraw / trade availability. */
43467
+ _extendedEquityShortfallUsd() {
43468
+ if (!this.extendedBalance) return 0;
43469
+ const req = this._extendedMarginRequirementUsd();
43470
+ const eq = this.extendedBalance.equity.toNumber();
43471
+ return Math.max(0, req - eq);
43472
+ }
43473
+ /**
43474
+ * Credits a USDC inflow on Extended. Fills margin shortfall (balance+equity only) first;
43475
+ * any remainder is credited across balance, equity, availableForWithdrawal, and availableForTrade.
43476
+ */
43477
+ addToExtAvailTrade(rawUsd) {
43478
+ assert(rawUsd >= 0, "SolveBudget::addToExtAvailTrade amount must be non-negative");
43479
+ if (rawUsd <= CASE_THRESHOLD_USD) return;
43480
+ if (!this.extendedBalance) {
43481
+ logger.warn("SolveBudget::addToExtAvailTrade skipped \u2014 no extendedBalance");
43482
+ return;
43483
+ }
43484
+ const shortfall = this._extendedEquityShortfallUsd();
43485
+ const toMargin = Math.min(rawUsd, shortfall);
43486
+ const toLiquid = rawUsd - toMargin;
43487
+ if (toMargin > CASE_THRESHOLD_USD) {
43488
+ const b = this.extendedBalance.balance.toNumber();
43489
+ const e = this.extendedBalance.equity.toNumber();
43490
+ this.extendedBalance.balance = safeUsdcWeb3Number(b + toMargin);
43491
+ this.extendedBalance.equity = safeUsdcWeb3Number(e + toMargin);
43492
+ logger.debug(
43493
+ `SolveBudget::addToExtAvailTrade margin-first rawUsd=${toMargin} (shortfallBefore=${shortfall}, balance=${b + toMargin}, equity=${e + toMargin})`
43494
+ );
43495
+ }
43496
+ if (toLiquid > CASE_THRESHOLD_USD) {
43497
+ this._updateExtAvailWithdraw(toLiquid, false);
43498
+ }
43499
+ logger.debug(
43500
+ `SolveBudget::addToExtAvailTrade total rawUsd=${rawUsd} toLiquid=${toLiquid} extAvailTrade=${this.extAvailTrade}, totalUnused=${this.totalUnused}`
43501
+ );
42490
43502
  }
42491
43503
  spendVesuBorrowCapacity(desired) {
42492
- const used = Math.min(this._vesuBorrowCapacity, Math.max(0, desired));
42493
- this._vesuBorrowCapacity -= used;
42494
- this._totalUnused -= used;
43504
+ const used = Math.min(this.vesuBorrowCapacity, Math.max(0, desired));
42495
43505
  let spendsByPool = [];
42496
43506
  for (let index = 0; index < this.vesuPerPoolDebtDeltasToBorrow.length; index++) {
42497
43507
  const d = this.vesuPerPoolDebtDeltasToBorrow[index];
@@ -42502,13 +43512,12 @@ var SolveBudget = class {
42502
43512
  this.vesuPoolStates[index].debtUsdValue = this.vesuPoolStates[index].debtAmount.toNumber() * this.vesuPoolStates[index].debtPrice;
42503
43513
  spendsByPool.push({ poolId: this.vesuPoolStates[index].poolId, amount: safeUsdcWeb3Number(borrowed), collateralToken: this.vesuPoolStates[index].collateralToken, debtToken: this.vesuPoolStates[index].debtToken });
42504
43514
  }
42505
- logger.debug(`SolveBudget::spendVesuBorrowCapacity used=${used}, vesuBorrowCapacity=${this._vesuBorrowCapacity}, totalUnused=${this._totalUnused}`);
43515
+ logger.debug(`SolveBudget::spendVesuBorrowCapacity used=${used}, vesuBorrowCapacity=${this.vesuBorrowCapacity}, totalUnused=${this.totalUnused}`);
42506
43516
  return { used, spendsByPool };
42507
43517
  }
42508
43518
  repayVesuBorrowCapacity(desired) {
42509
43519
  assert(desired > 0, "SolveBudget::repayVesuBorrowCapacity desired must be positive");
42510
43520
  const used = desired;
42511
- this._vesuBorrowCapacity += used;
42512
43521
  const spendsByPool = [];
42513
43522
  for (let index = 0; index < this.vesuPerPoolDebtDeltasToBorrow.length; index++) {
42514
43523
  const d = this.vesuPerPoolDebtDeltasToBorrow[index];
@@ -42518,7 +43527,7 @@ var SolveBudget = class {
42518
43527
  this.vesuPoolStates[index].debtAmount = safeUsdcWeb3Number(this.vesuPoolStates[index].debtAmount.toNumber() - repaid);
42519
43528
  spendsByPool.push({ poolId: this.vesuPoolStates[index].poolId, amount: safeUsdcWeb3Number(-repaid), collateralToken: this.vesuPoolStates[index].collateralToken, debtToken: this.vesuPoolStates[index].debtToken });
42520
43529
  }
42521
- logger.debug(`SolveBudget::repayVesuBorrowCapacity used=${used}, vesuBorrowCapacity=${this._vesuBorrowCapacity}, totalUnused=${this._totalUnused}`);
43530
+ logger.debug(`SolveBudget::repayVesuBorrowCapacity used=${used}, vesuBorrowCapacity=${this.vesuBorrowCapacity}, totalUnused=${this.totalUnused}`);
42522
43531
  return { used, spendsByPool };
42523
43532
  }
42524
43533
  // ── State mutation ──────────────────────────────────────────────────
@@ -42540,13 +43549,21 @@ var SolveBudget = class {
42540
43549
  pool.debtUsdValue = pool.debtAmount.toNumber() * pool.debtPrice;
42541
43550
  const vesuPerPoolDebtDeltasToBorrow = this._computeperPoolDebtDeltasToBorrow();
42542
43551
  this.vesuPerPoolDebtDeltasToBorrow = vesuPerPoolDebtDeltasToBorrow.map((item) => item.deltaDebt);
42543
- const sum = this.vesuPerPoolDebtDeltasToBorrow.reduce((a, b) => a.plus(b), new Web3Number(0, USDC_TOKEN_DECIMALS));
42544
- this._vesuBorrowCapacity = sum.toNumber();
42545
43552
  this.shouldVesuRebalance = vesuPerPoolDebtDeltasToBorrow.map((item) => item.shouldRebalance);
42546
43553
  }
42547
- /** Update an Extended position's size after a lever route. Creates the position if new. */
42548
- applyExtendedExposureDelta(instrument, sizeDelta) {
42549
- const pos = this.extendedPositions.find((p) => p.instrument === instrument);
43554
+ /**
43555
+ * Update Extended position size after a lever route; sync {@link ExtendedPositionState.valueUsd}
43556
+ * and margin buckets (released USD on decrease, locked USD on increase).
43557
+ *
43558
+ * @param collateralPriceUsd BTC collateral price for notional / margin math; if omitted, uses
43559
+ * existing valueUsd / |size| or the first Vesu pool collateral price.
43560
+ */
43561
+ applyExtendedExposureDelta(instrument, sizeDelta, collateralPriceUsd) {
43562
+ const btcEps = 10 ** -COLLATERAL_PRECISION;
43563
+ const lev = calculateExtendedLevergae();
43564
+ let pos = this.extendedPositions.find((p) => p.instrument === instrument);
43565
+ const oldAbs = pos ? Math.abs(pos.size.toNumber()) : 0;
43566
+ const oldValUsd = pos ? pos.valueUsd.toNumber() : 0;
42550
43567
  if (pos) {
42551
43568
  pos.size = new Web3Number(pos.size.plus(sizeDelta).toFixed(8), 8);
42552
43569
  } else if (sizeDelta.toNumber() !== 0) {
@@ -42557,6 +43574,56 @@ var SolveBudget = class {
42557
43574
  valueUsd: new Web3Number(0, USDC_TOKEN_DECIMALS),
42558
43575
  leverage: "0"
42559
43576
  });
43577
+ pos = this.extendedPositions[this.extendedPositions.length - 1];
43578
+ } else {
43579
+ return;
43580
+ }
43581
+ const newAbs = Math.abs(pos.size.toNumber());
43582
+ let price = collateralPriceUsd;
43583
+ if (price === void 0 || price <= 0) {
43584
+ price = oldAbs > btcEps ? oldValUsd / oldAbs : this.vesuPoolStates[0]?.collateralPrice ?? 0;
43585
+ }
43586
+ if (price > 0) {
43587
+ pos.valueUsd = new Web3Number(
43588
+ (newAbs * price).toFixed(USDC_TOKEN_DECIMALS),
43589
+ USDC_TOKEN_DECIMALS
43590
+ );
43591
+ }
43592
+ if (!this.extendedBalance || lev <= 0 || price <= 0) return;
43593
+ const dAbs = newAbs - oldAbs;
43594
+ if (dAbs < -btcEps) {
43595
+ const releasedUsd = -dAbs * price / lev;
43596
+ if (releasedUsd > CASE_THRESHOLD_USD) {
43597
+ this.addToExtAvailTrade(releasedUsd);
43598
+ }
43599
+ } else if (dAbs > btcEps) {
43600
+ const lockedUsd = dAbs * price / lev;
43601
+ if (lockedUsd > CASE_THRESHOLD_USD) {
43602
+ this._lockExtendedMarginUsd(lockedUsd);
43603
+ }
43604
+ }
43605
+ }
43606
+ /** Pull margin for larger Extended exposure from liquid buckets, then balance/equity. */
43607
+ _lockExtendedMarginUsd(lockedUsd) {
43608
+ if (lockedUsd <= CASE_THRESHOLD_USD || !this.extendedBalance) return;
43609
+ let rem = lockedUsd;
43610
+ const uw = Math.min(rem, Math.max(0, this.extendedBalance.availableForWithdrawal.toNumber()));
43611
+ if (uw > 0) {
43612
+ this._updateExtAvailWithdraw(uw, true);
43613
+ rem -= uw;
43614
+ }
43615
+ if (rem > 0) {
43616
+ const uu = Math.min(rem, Math.max(0, this.extendedBalance.unrealisedPnl.toNumber()));
43617
+ if (uu > 0) {
43618
+ this._updateExtAvailUpnl(uu, true);
43619
+ rem -= uu;
43620
+ }
43621
+ }
43622
+ if (rem > 0) {
43623
+ const b = this.extendedBalance.balance.toNumber();
43624
+ const e = this.extendedBalance.equity.toNumber();
43625
+ this.extendedBalance.balance = safeUsdcWeb3Number(b - rem);
43626
+ this.extendedBalance.equity = safeUsdcWeb3Number(e - rem);
42560
43627
  }
42561
43628
  }
42562
43629
  /**
@@ -42584,6 +43651,23 @@ var SolveBudget = class {
42584
43651
  return output;
42585
43652
  }
42586
43653
  };
43654
+ function createSolveBudgetFromRawState(params) {
43655
+ const budget = new SolveBudget({
43656
+ assetToken: params.assetToken,
43657
+ usdcToken: params.usdcToken,
43658
+ unusedBalance: params.unusedBalance,
43659
+ walletBalance: params.walletBalance,
43660
+ vaultAssetBalance: params.vaultAssetBalance,
43661
+ vaultUsdcBalance: params.vaultUsdcBalance,
43662
+ extendedPositions: params.extendedPositions,
43663
+ extendedBalance: params.extendedBalance,
43664
+ vesuPoolStates: params.vesuPoolStates
43665
+ });
43666
+ if (params.limitBalanceBufferFactor && params.limitBalanceBufferFactor > 0) {
43667
+ budget.applyBuffer(params.limitBalanceBufferFactor);
43668
+ }
43669
+ return budget;
43670
+ }
42587
43671
  var ExtendedSVKVesuStateManager = class {
42588
43672
  constructor(config) {
42589
43673
  this._tag = "ExtendedSVKVesuStateManager";
@@ -42619,7 +43703,7 @@ var ExtendedSVKVesuStateManager = class {
42619
43703
  vesuAllocationUsd: safeUsdcWeb3Number(0),
42620
43704
  extendedAllocationUsd: safeUsdcWeb3Number(0),
42621
43705
  bringLiquidityAmount: safeUsdcWeb3Number(0),
42622
- pendingDeposit: this._budget.extendedBalance?.pendingDeposit ?? new Web3Number(0, USDC_TOKEN_DECIMALS)
43706
+ pendingDeposit: safeUsdcWeb3Number(0)
42623
43707
  };
42624
43708
  this._logSolveResult(result);
42625
43709
  return result;
@@ -42634,30 +43718,44 @@ var ExtendedSVKVesuStateManager = class {
42634
43718
  async _refresh() {
42635
43719
  logger.info(`${this._tag}::_refresh starting`);
42636
43720
  const [
42637
- vaultAllocatorBalance,
43721
+ vaultAssetBalance,
43722
+ vaultUsdcBalance,
42638
43723
  walletBalance,
42639
43724
  vesuPoolStates,
42640
43725
  extendedBalance,
42641
43726
  extendedPositions
42642
43727
  ] = await Promise.all([
42643
- this._fetchVaultAllocatorBalance(),
43728
+ this._fetchVaultAllocatorAssetBalance(),
43729
+ this._fetchVaultAllocatorUsdcBalanceIfDistinct(),
42644
43730
  this._fetchWalletBalances(),
42645
43731
  this._fetchAllVesuPoolStates(),
42646
43732
  this._fetchExtendedBalance(),
42647
43733
  this._fetchExtendedPositions()
42648
43734
  ]);
42649
- logger.verbose(`_refresh vaultAllocatorBalance: ${vaultAllocatorBalance.usdValue}, walletBalance: ${walletBalance.usdValue}`);
43735
+ logger.verbose(
43736
+ `${this._tag}::_refresh VA asset ${vaultAssetBalance.token.symbol}=$${vaultAssetBalance.usdValue.toFixed(2)}${vaultUsdcBalance ? `, VA USDC=$${vaultUsdcBalance.usdValue.toFixed(2)}` : ""}, wallet=${walletBalance.usdValue}`
43737
+ );
42650
43738
  const unusedBalance = this._computeUnusedBalances(
42651
- vaultAllocatorBalance,
43739
+ vaultAssetBalance,
43740
+ vaultUsdcBalance,
42652
43741
  walletBalance
42653
43742
  );
42654
- this._budget = new SolveBudget({
42655
- limitBalanceBufferFactor: this._config.limitBalanceBufferFactor,
43743
+ this._budget = createSolveBudgetFromRawState({
43744
+ assetToken: this._config.assetToken,
43745
+ usdcToken: this._config.usdcToken,
42656
43746
  unusedBalance,
42657
43747
  walletBalance,
42658
- vaultBalance: vaultAllocatorBalance,
43748
+ vaultAssetBalance,
43749
+ vaultUsdcBalance,
42659
43750
  extendedPositions,
42660
- extendedBalance,
43751
+ extendedBalance: {
43752
+ availableForTrade: extendedBalance?.availableForTrade || new Web3Number(0, USDC_TOKEN_DECIMALS),
43753
+ availableForWithdrawal: extendedBalance?.availableForWithdrawal || new Web3Number(0, USDC_TOKEN_DECIMALS),
43754
+ unrealisedPnl: extendedBalance?.unrealisedPnl || new Web3Number(0, USDC_TOKEN_DECIMALS),
43755
+ balance: extendedBalance?.balance || new Web3Number(0, USDC_TOKEN_DECIMALS),
43756
+ equity: extendedBalance?.equity || new Web3Number(0, USDC_TOKEN_DECIMALS),
43757
+ pendingDeposit: extendedBalance?.pendingDeposit || new Web3Number(0, USDC_TOKEN_DECIMALS)
43758
+ },
42661
43759
  vesuPoolStates
42662
43760
  });
42663
43761
  const totalUnusedUsd = unusedBalance.reduce(
@@ -42669,10 +43767,14 @@ var ExtendedSVKVesuStateManager = class {
42669
43767
  );
42670
43768
  }
42671
43769
  // todo add communication check with python server of extended. if not working, throw error in solve function.
43770
+ /** True when strategy asset and USDC share one token — VA USDC slot is unused (all in asset balance). */
43771
+ _vaultAssetAndUsdcAreSameToken() {
43772
+ return this._config.assetToken.address.eq(this._config.usdcToken.address);
43773
+ }
42672
43774
  /**
42673
- * Reads the asset-token balance sitting idle in the vault allocator contract.
43775
+ * Reads the {@link StateManagerConfig.assetToken} balance idle in the vault allocator.
42674
43776
  */
42675
- async _fetchVaultAllocatorBalance() {
43777
+ async _fetchVaultAllocatorAssetBalance() {
42676
43778
  const { assetToken, vaultAllocator, networkConfig, pricer } = this._config;
42677
43779
  const balance = await new ERC20(networkConfig).balanceOf(
42678
43780
  assetToken.address,
@@ -42684,20 +43786,38 @@ var ExtendedSVKVesuStateManager = class {
42684
43786
  return { token: assetToken, amount: balance, usdValue };
42685
43787
  }
42686
43788
  /**
42687
- * Merges the vault-allocator balance and wallet balances into a
42688
- * deduplicated array of TokenBalance entries keyed by token address.
42689
- *
42690
- * e.g. VA has USDC, wallet has USDC + USDC.e → returns
42691
- * [{ token: USDC, amount: VA+wallet, usdValue: … },
42692
- * { token: USDC.e, amount: wallet, usdValue: }]
43789
+ * Reads {@link StateManagerConfig.usdcToken} in the vault allocator when it differs from
43790
+ * {@link StateManagerConfig.assetToken}. Otherwise returns null (treat VA USDC as 0; stablecoin is only under asset).
43791
+ */
43792
+ async _fetchVaultAllocatorUsdcBalanceIfDistinct() {
43793
+ if (this._vaultAssetAndUsdcAreSameToken()) return null;
43794
+ const { usdcToken, vaultAllocator, networkConfig, pricer } = this._config;
43795
+ const balance = await new ERC20(networkConfig).balanceOf(
43796
+ usdcToken.address,
43797
+ vaultAllocator,
43798
+ usdcToken.decimals
43799
+ );
43800
+ const tokenPrice = await pricer.getPrice(
43801
+ usdcToken.priceProxySymbol || usdcToken.symbol
43802
+ );
43803
+ const usdValue = Number(balance.toFixed(usdcToken.decimals)) * tokenPrice.price;
43804
+ return { token: usdcToken, amount: balance, usdValue };
43805
+ }
43806
+ /**
43807
+ * Merges vault-allocator asset, optional vault-allocator USDC, and operator wallet
43808
+ * balances into entries keyed by token address.
42693
43809
  */
42694
- _computeUnusedBalances(vaultAllocatorBalance, walletBalance) {
43810
+ _computeUnusedBalances(vaultAssetBalance, vaultUsdcBalance, walletBalance) {
42695
43811
  const balanceMap = /* @__PURE__ */ new Map();
42696
- balanceMap.set(vaultAllocatorBalance.token.address.toString(), {
42697
- token: vaultAllocatorBalance.token,
42698
- amount: vaultAllocatorBalance.amount,
42699
- usdValue: vaultAllocatorBalance.usdValue
42700
- });
43812
+ const put = (tb) => {
43813
+ balanceMap.set(tb.token.address.toString(), {
43814
+ token: tb.token,
43815
+ amount: tb.amount,
43816
+ usdValue: tb.usdValue
43817
+ });
43818
+ };
43819
+ put(vaultAssetBalance);
43820
+ if (vaultUsdcBalance) put(vaultUsdcBalance);
42701
43821
  const key = walletBalance.token.address.toString();
42702
43822
  const existing = balanceMap.get(key);
42703
43823
  if (existing) {
@@ -42716,30 +43836,29 @@ var ExtendedSVKVesuStateManager = class {
42716
43836
  return Array.from(balanceMap.values());
42717
43837
  }
42718
43838
  /**
42719
- * Reads the operator wallet's balances for the asset token (USDC.e) and
42720
- * USDC.e (needed for route computation P1 vs P2 decision for Extended deposits).
43839
+ * Reads the operator wallet balance for {@link StateManagerConfig.usdcToken} only
43840
+ * (wallet stablecoin is always USDC, regardless of strategy {@link StateManagerConfig.assetToken}).
42721
43841
  */
42722
43842
  async _fetchWalletBalances() {
42723
43843
  const {
42724
43844
  networkConfig,
42725
43845
  pricer,
42726
43846
  walletAddress,
42727
- assetToken,
42728
- usdceToken
43847
+ usdcToken
42729
43848
  } = this._config;
42730
43849
  const erc20 = new ERC20(networkConfig);
42731
- const [usdceBalance, usdcePrice] = await Promise.all([
43850
+ const [balance, tokenPrice] = await Promise.all([
42732
43851
  erc20.balanceOf(
42733
- usdceToken.address,
43852
+ usdcToken.address,
42734
43853
  walletAddress,
42735
- usdceToken.decimals
43854
+ usdcToken.decimals
42736
43855
  ),
42737
- pricer.getPrice(usdceToken.priceProxySymbol || usdceToken.symbol)
43856
+ pricer.getPrice(usdcToken.priceProxySymbol || usdcToken.symbol)
42738
43857
  ]);
42739
43858
  return {
42740
- token: usdceToken,
42741
- amount: usdceBalance,
42742
- usdValue: Number(usdceBalance.toFixed(usdceToken.decimals)) * usdcePrice.price
43859
+ token: usdcToken,
43860
+ amount: balance,
43861
+ usdValue: Number(balance.toFixed(usdcToken.decimals)) * tokenPrice.price
42743
43862
  };
42744
43863
  }
42745
43864
  /**
@@ -42857,12 +43976,12 @@ var ExtendedSVKVesuStateManager = class {
42857
43976
  * finite, sensible values. Throws on invalid state.
42858
43977
  */
42859
43978
  _validateRefreshedState() {
42860
- if (this._budget.unusedBalance.length === 0) {
43979
+ if (this._budget.unusedBalanceRows.length === 0) {
42861
43980
  throw new Error(
42862
43981
  `${this._tag}: unusedBalance is empty after refresh`
42863
43982
  );
42864
43983
  }
42865
- for (const balance of this._budget.unusedBalance) {
43984
+ for (const balance of this._budget.unusedBalanceRows) {
42866
43985
  this._validateTokenBalanceOrThrow(
42867
43986
  balance,
42868
43987
  `unusedBalance[${balance.token.symbol}]`
@@ -42882,7 +44001,7 @@ var ExtendedSVKVesuStateManager = class {
42882
44001
  }
42883
44002
  }
42884
44003
  _validateVesuPoolPricesOrThrow() {
42885
- for (const pool of this._budget.vesuPoolStates) {
44004
+ for (const pool of this._budget.vesuPools) {
42886
44005
  const poolLabel = pool.poolId.shortString();
42887
44006
  this._assertPositiveFinite(
42888
44007
  pool.collateralPrice,
@@ -42895,8 +44014,8 @@ var ExtendedSVKVesuStateManager = class {
42895
44014
  }
42896
44015
  }
42897
44016
  _validateExtendedBalanceOrThrow() {
42898
- if (!this._budget.extendedBalance) return;
42899
- const { equity, availableForTrade } = this._budget.extendedBalance;
44017
+ if (!this._budget.extendedBalanceView) return;
44018
+ const { equity, availableForTrade } = this._budget.extendedBalanceView;
42900
44019
  if (!Number.isFinite(equity.toNumber()) || !Number.isFinite(availableForTrade.toNumber())) {
42901
44020
  throw new Error(
42902
44021
  `${this._tag}: Extended balance contains non-finite values \u2014 equity: ${equity.toNumber()}, availableForTrade: ${availableForTrade.toNumber()}`
@@ -42935,18 +44054,26 @@ var ExtendedSVKVesuStateManager = class {
42935
44054
  );
42936
44055
  }
42937
44056
  /**
42938
- * Total investable = vault allocator balance + Extended available-for-trade,
42939
- * both reduced by the configured buffer percentage.
44057
+ * Total investable = vault allocator balance + Extended available-for-trade +
44058
+ * buffered unrealised PnL, matching `deposit_cases_extended_vesu.xlsx`:
44059
+ * `(VA + wallet + EXT_WITH_AVL + EXT_UPNL) * (1 − buffer)`.
44060
+ * Positive {@link ExtendedBalanceState.pendingDeposit} stays on the afT leg only (see {@link SolveBudget.extAvailTrade}).
42940
44061
  */
42941
44062
  _computeTotalInvestableAmount() {
42942
- const totalUnusedUsd = this._budget.unusedBalance.reduce(
42943
- (acc, b) => acc + b.usdValue,
42944
- 0
42945
- );
44063
+ const totalUnusedUsd = this._budget.unusedBalancesBufferedUsdSum;
42946
44064
  logger.debug(
42947
- `${this._tag}::_computeTotalInvestableAmount unusedBalances=${JSON.stringify(this._budget.unusedBalance.map((b) => ({ token: b.token.symbol, amount: b.amount.toNumber(), usdValue: b.usdValue })))}`
44065
+ `${this._tag}::_computeTotalInvestableAmount unusedBalances=${JSON.stringify(this._budget.unusedBalanceRows.map((b) => ({ token: b.token.symbol, amount: b.amount.toNumber(), usdValue: b.usdValue })))}`
44066
+ );
44067
+ const extBal = this._budget.extendedBalanceView;
44068
+ const rawAft = extBal?.availableForWithdrawal?.toNumber() ?? 0;
44069
+ const rawUpnl = extBal?.unrealisedPnl?.toNumber() ?? 0;
44070
+ let extBuffered = this._budget.bufferedUsd(rawAft) + this._budget.bufferedUsd(rawUpnl);
44071
+ const pd = extBal?.pendingDeposit?.toNumber() ?? 0;
44072
+ if (pd > 0) extBuffered += pd;
44073
+ const extendedAvailable = new Web3Number(
44074
+ extBuffered.toFixed(USDC_TOKEN_DECIMALS),
44075
+ USDC_TOKEN_DECIMALS
42948
44076
  );
42949
- const extendedAvailable = this._budget.extendedBalance?.availableForTrade ?? new Web3Number(0, USDC_TOKEN_DECIMALS);
42950
44077
  logger.verbose(`_computeTotalInvestableAmount totalUnusedUsd: ${totalUnusedUsd}, extendedAvailable: ${extendedAvailable.toNumber()}`);
42951
44078
  return new Web3Number(totalUnusedUsd.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS).plus(extendedAvailable);
42952
44079
  }
@@ -42977,7 +44104,7 @@ var ExtendedSVKVesuStateManager = class {
42977
44104
  if (denominator === 0) {
42978
44105
  throw new Error(`${this._tag}: Denominator is zero`);
42979
44106
  }
42980
- const collateralPrice = this._budget.vesuPoolStates[0]?.collateralPrice ?? 0;
44107
+ const collateralPrice = this._budget.vesuPools[0]?.collateralPrice ?? 0;
42981
44108
  const totalVesuExposureUsd = this._totalVesuCollateralUsd().plus(new Web3Number((deltaVesuCollateral * collateralPrice).toFixed(6), USDC_TOKEN_DECIMALS));
42982
44109
  const totalExtendedExposureUsd = this._totalExtendedExposureUsd().plus(new Web3Number((deltaExtendedCollateral * collateralPrice).toFixed(6), USDC_TOKEN_DECIMALS));
42983
44110
  const numerator = vesuLeverage * distributableAmount.toNumber() + totalVesuExposureUsd.toNumber() - totalExtendedExposureUsd.toNumber();
@@ -42989,8 +44116,6 @@ var ExtendedSVKVesuStateManager = class {
42989
44116
  distributableAmount.minus(extendedAllocationUsd).toFixed(USDC_TOKEN_DECIMALS),
42990
44117
  USDC_TOKEN_DECIMALS
42991
44118
  );
42992
- const perPoolDebtDeltasToBorrow = this._budget.vesuPerPoolDebtDeltasToBorrow;
42993
- vesuAllocationUsd = vesuAllocationUsd.plus(this._sumDebtDeltas(perPoolDebtDeltasToBorrow).multipliedBy(-1));
42994
44119
  let vesuPositionDelta = Number(new Web3Number((vesuAllocationUsd.toNumber() * vesuLeverage / collateralPrice).toFixed(6), 6).toFixedRoundDown(COLLATERAL_PRECISION));
42995
44120
  let extendedPositionDelta = Number(new Web3Number((extendedAllocationUsd.toNumber() * extendedLeverage / collateralPrice).toFixed(6), 6).toFixedRoundDown(COLLATERAL_PRECISION));
42996
44121
  if (!isRecursive) {
@@ -43009,14 +44134,17 @@ var ExtendedSVKVesuStateManager = class {
43009
44134
  * by existing collateral value, then converts each share to collateral
43010
44135
  * token units.
43011
44136
  */
43012
- _computePerPoolCollateralDeltas(vesuAllocationUsd, perPoolDebtDeltasToBorrow) {
44137
+ _computePerPoolCollateralDeltas(vesuAllocationUsd) {
43013
44138
  const vesuLeverage = calculateVesuLeverage();
43014
- const availableVesuCollateralAllocationUsd = vesuAllocationUsd.plus(this._sumDebtDeltas(perPoolDebtDeltasToBorrow));
44139
+ const availableVesuCollateralAllocationUsd = vesuAllocationUsd;
43015
44140
  const postLeverageAllocationUsd = availableVesuCollateralAllocationUsd.multipliedBy(vesuLeverage);
43016
44141
  const totalCollateralExisting = this._totalVesuCollateral();
43017
- return this._budget.vesuPoolStates.map((pool, index) => {
44142
+ return this._budget.vesuPools.map((pool, index) => {
43018
44143
  const _postLeverageAllocation = postLeverageAllocationUsd.dividedBy(pool.collateralPrice);
43019
- const postLeverageAllocation = new Web3Number(_postLeverageAllocation.plus(totalCollateralExisting).toFixed(COLLATERAL_PRECISION), pool.collateralToken.decimals).minus(totalCollateralExisting);
44144
+ const postLeverageAllocation = new Web3Number(
44145
+ _postLeverageAllocation.plus(totalCollateralExisting).toFixedRoundDown(COLLATERAL_PRECISION),
44146
+ pool.collateralToken.decimals
44147
+ ).minus(totalCollateralExisting);
43020
44148
  const _poolCollateralDelta = this._computePoolCollateralShare(
43021
44149
  pool,
43022
44150
  totalCollateralExisting,
@@ -43026,12 +44154,12 @@ var ExtendedSVKVesuStateManager = class {
43026
44154
  _poolCollateralDelta.toFixed(COLLATERAL_PRECISION),
43027
44155
  pool.collateralToken.decimals
43028
44156
  );
43029
- const newDebt = postLeverageAllocationUsd.minus(availableVesuCollateralAllocationUsd).dividedBy(pool.debtPrice);
44157
+ const newDebt = postLeverageAllocation.multipliedBy(pool.collateralPrice).minus(availableVesuCollateralAllocationUsd).dividedBy(pool.debtPrice);
43030
44158
  return {
43031
44159
  poolId: pool.poolId,
43032
44160
  collateralToken: pool.collateralToken,
43033
44161
  debtToken: pool.debtToken,
43034
- debtDelta: perPoolDebtDeltasToBorrow[index].plus(newDebt),
44162
+ debtDelta: newDebt,
43035
44163
  collateralDelta: poolCollateralDelta,
43036
44164
  collateralPrice: pool.collateralPrice,
43037
44165
  debtPrice: pool.debtPrice
@@ -43044,7 +44172,7 @@ var ExtendedSVKVesuStateManager = class {
43044
44172
  * Multi-pool cases split proportionally by current collateral USD value.
43045
44173
  */
43046
44174
  _computePoolCollateralShare(pool, totalCollateral, totalVesuAllocation) {
43047
- const isSinglePoolOrZeroTotal = this._budget.vesuPoolStates.length === 1 || totalCollateral.toNumber() === 0;
44175
+ const isSinglePoolOrZeroTotal = this._budget.vesuPools.length === 1 || totalCollateral.toNumber() === 0;
43048
44176
  if (isSinglePoolOrZeroTotal) return totalVesuAllocation;
43049
44177
  const poolWeight = pool.collateralAmount.dividedBy(totalCollateral);
43050
44178
  return new Web3Number(
@@ -43081,8 +44209,8 @@ var ExtendedSVKVesuStateManager = class {
43081
44209
  */
43082
44210
  _computeTargetExtendedExposure(vesuDeltas) {
43083
44211
  let totalExposureCollateral = new Web3Number(0, USDC_TOKEN_DECIMALS);
43084
- for (let i = 0; i < this._budget.vesuPoolStates.length; i++) {
43085
- const pool = this._budget.vesuPoolStates[i];
44212
+ for (let i = 0; i < this._budget.vesuPools.length; i++) {
44213
+ const pool = this._budget.vesuPools[i];
43086
44214
  const delta = vesuDeltas[i];
43087
44215
  logger.debug(
43088
44216
  `${this._tag}::_computeTargetExtendedExposure poolId=${pool.poolId.toString()}, collateralAmount=${pool.collateralAmount.toNumber()}, collateralDelta=${delta.collateralDelta.toNumber()}`
@@ -43100,7 +44228,7 @@ var ExtendedSVKVesuStateManager = class {
43100
44228
  );
43101
44229
  }
43102
44230
  _hasNoExtendedPositions() {
43103
- return this._budget.extendedPositions.length === 0;
44231
+ return this._budget.extendedPositionsView.length === 0;
43104
44232
  }
43105
44233
  /**
43106
44234
  * Creates a single-element delta array for the default instrument
@@ -43111,7 +44239,7 @@ var ExtendedSVKVesuStateManager = class {
43111
44239
  {
43112
44240
  instrument: this._config.extendedAdapter.config.extendedMarketName,
43113
44241
  delta: new Web3Number(
43114
- delta.toFixed(COLLATERAL_PRECISION),
44242
+ delta.toFixedRoundDown(COLLATERAL_PRECISION),
43115
44243
  USDC_TOKEN_DECIMALS
43116
44244
  )
43117
44245
  }
@@ -43123,7 +44251,7 @@ var ExtendedSVKVesuStateManager = class {
43123
44251
  */
43124
44252
  _distributeExposureDeltaAcrossPositions(totalDelta) {
43125
44253
  const totalExposure = this._totalExtendedExposure();
43126
- return this._budget.extendedPositions.map((position) => {
44254
+ return this._budget.extendedPositionsView.map((position) => {
43127
44255
  const share = this._positionExposureShareFraction(
43128
44256
  position,
43129
44257
  totalExposure
@@ -43131,7 +44259,7 @@ var ExtendedSVKVesuStateManager = class {
43131
44259
  return {
43132
44260
  instrument: position.instrument,
43133
44261
  delta: new Web3Number(
43134
- totalDelta.multipliedBy(share).toFixed(COLLATERAL_PRECISION),
44262
+ totalDelta.multipliedBy(share).toFixedRoundDown(COLLATERAL_PRECISION),
43135
44263
  USDC_TOKEN_DECIMALS
43136
44264
  )
43137
44265
  };
@@ -43143,7 +44271,7 @@ var ExtendedSVKVesuStateManager = class {
43143
44271
  * or when total exposure is zero.
43144
44272
  */
43145
44273
  _positionExposureShareFraction(position, totalExposure) {
43146
- const isSingleOrZero = totalExposure.toNumber() === 0 || this._budget.extendedPositions.length === 1;
44274
+ const isSingleOrZero = totalExposure.toNumber() === 0 || this._budget.extendedPositionsView.length === 1;
43147
44275
  if (isSingleOrZero) return 1;
43148
44276
  return position.valueUsd.dividedBy(totalExposure).toNumber();
43149
44277
  }
@@ -43155,7 +44283,10 @@ var ExtendedSVKVesuStateManager = class {
43155
44283
  * Positive = need to deposit more, negative = can withdraw excess.
43156
44284
  */
43157
44285
  _computeExtendedDepositDelta(extendedAllocationUsd) {
43158
- const currentAvailableForTrade = this._budget.extendedBalance?.availableForTrade ?? new Web3Number(0, USDC_TOKEN_DECIMALS);
44286
+ const currentAvailableForTrade = new Web3Number(
44287
+ this._budget.extAvailTrade.toFixed(USDC_TOKEN_DECIMALS),
44288
+ USDC_TOKEN_DECIMALS
44289
+ );
43159
44290
  return new Web3Number(
43160
44291
  extendedAllocationUsd.minus(currentAvailableForTrade).toFixed(USDC_TOKEN_DECIMALS),
43161
44292
  USDC_TOKEN_DECIMALS
@@ -43163,8 +44294,8 @@ var ExtendedSVKVesuStateManager = class {
43163
44294
  }
43164
44295
  _computeVesuDepositAmount(vesuDeltas) {
43165
44296
  let totalVesuCollateral = new Web3Number(0, USDC_TOKEN_DECIMALS);
43166
- for (let i = 0; i < this._budget.vesuPoolStates.length; i++) {
43167
- const pool = this._budget.vesuPoolStates[i];
44297
+ for (let i = 0; i < this._budget.vesuPools.length; i++) {
44298
+ const pool = this._budget.vesuPools[i];
43168
44299
  const delta = vesuDeltas[i];
43169
44300
  totalVesuCollateral = totalVesuCollateral.plus(delta.collateralDelta.multipliedBy(pool.collateralPrice));
43170
44301
  totalVesuCollateral = totalVesuCollateral.minus(delta.debtDelta.multipliedBy(pool.debtPrice));
@@ -43209,13 +44340,17 @@ var ExtendedSVKVesuStateManager = class {
43209
44340
  for (const route of spendsByPool) {
43210
44341
  routes.push({ type: "VESU_REPAY" /* VESU_REPAY */, ...route, priority: routes.length });
43211
44342
  }
43212
- this._budget.spendVA(used);
44343
+ this._budget.spendVaRawUsd(used);
43213
44344
  }
43214
- _buildVesuBorrowRoutes(totalUsd, routes) {
44345
+ _buildVesuBorrowRoutes(totalUsd, routes, opts) {
43215
44346
  let borrowable = this._budget.vesuBorrowCapacity;
44347
+ if (opts?.maxBorrowUsd !== void 0) {
44348
+ borrowable = Math.min(borrowable, Math.max(0, opts.maxBorrowUsd));
44349
+ }
43216
44350
  if (totalUsd <= CASE_THRESHOLD_USD) return { routes, remaining: totalUsd };
43217
44351
  if (borrowable <= CASE_THRESHOLD_USD) return { routes, remaining: totalUsd };
43218
- const { used, spendsByPool } = this._budget.spendVesuBorrowCapacity(totalUsd);
44352
+ const borrowTarget = Math.min(totalUsd, borrowable);
44353
+ const { used, spendsByPool } = this._budget.spendVesuBorrowCapacity(borrowTarget);
43219
44354
  for (const route of spendsByPool) {
43220
44355
  routes.push({ type: "VESU_BORROW" /* VESU_BORROW */, ...route, priority: routes.length });
43221
44356
  }
@@ -43271,9 +44406,9 @@ var ExtendedSVKVesuStateManager = class {
43271
44406
  // });
43272
44407
  // }
43273
44408
  _getWalletToVARoute(tryAmount, routes) {
43274
- const usableAmount = Math.min(tryAmount, this._budget.walletUsd);
43275
- if (usableAmount > CASE_THRESHOLD_USD) {
43276
- const walletUsed = this._budget.spendWallet(usableAmount);
44409
+ const usableRaw = Math.min(tryAmount, this._budget.walletUsd);
44410
+ if (usableRaw > CASE_THRESHOLD_USD) {
44411
+ const walletUsed = this._budget.spendWallet(usableRaw);
43277
44412
  this._budget.addToVA(walletUsed);
43278
44413
  const route = { type: "WALLET_TO_VA" /* WALLET_TO_VA */, amount: safeUsdcWeb3Number(walletUsed), priority: routes.length };
43279
44414
  routes.push(route);
@@ -43282,9 +44417,9 @@ var ExtendedSVKVesuStateManager = class {
43282
44417
  return { routes, remaining: tryAmount };
43283
44418
  }
43284
44419
  _getWalletToEXTENDEDRoute(tryAmount, routes, shouldAddWaitRoute = true) {
43285
- const usableAmount = Math.min(tryAmount, this._budget.walletUsd);
43286
- if (usableAmount > CASE_THRESHOLD_USD) {
43287
- const walletUsed = this._budget.spendWallet(usableAmount);
44420
+ const usableRaw = Math.min(tryAmount, this._budget.walletUsd);
44421
+ if (usableRaw > CASE_THRESHOLD_USD) {
44422
+ const walletUsed = this._budget.spendWallet(usableRaw);
43288
44423
  this._budget.addToExtAvailTrade(walletUsed);
43289
44424
  routes.push({ type: "WALLET_TO_EXTENDED" /* WALLET_TO_EXTENDED */, amount: safeUsdcWeb3Number(walletUsed), priority: routes.length });
43290
44425
  if (shouldAddWaitRoute) {
@@ -43295,9 +44430,9 @@ var ExtendedSVKVesuStateManager = class {
43295
44430
  return { routes, remaining: tryAmount };
43296
44431
  }
43297
44432
  _getVAToEXTENDEDRoute(tryAmount, routes, shouldAddWaitRoute = true) {
43298
- const usableAmount = Math.min(tryAmount, this._budget.vaUsd);
43299
- if (usableAmount > CASE_THRESHOLD_USD) {
43300
- const vaUsed = this._budget.spendVA(usableAmount);
44433
+ const usable = Math.min(tryAmount, this._budget.vaUsd);
44434
+ if (usable > CASE_THRESHOLD_USD) {
44435
+ const vaUsed = this._budget.spendVA(usable);
43301
44436
  this._budget.addToExtAvailTrade(vaUsed);
43302
44437
  const route = { type: "VA_TO_EXTENDED" /* VA_TO_EXTENDED */, amount: safeUsdcWeb3Number(vaUsed), priority: routes.length };
43303
44438
  routes.push(route);
@@ -43310,40 +44445,42 @@ var ExtendedSVKVesuStateManager = class {
43310
44445
  }
43311
44446
  _getExtendedToWalletRoute(tryAmount, routes, shouldAddWaitRoute = true) {
43312
44447
  if (tryAmount <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
43313
- assert(tryAmount <= this._budget.extAvailWithdraw, `tryAmount is greater than extAvailTrade, tryAmount: ${tryAmount}, extAvailWithdraw: ${this._budget.extAvailWithdraw}`);
43314
- const extWithdrawUsed = this._budget.spendExtAvailTrade(tryAmount);
43315
- this._budget.addToWallet(Math.abs(extWithdrawUsed));
43316
- const route = { type: "EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */, amount: safeUsdcWeb3Number(Math.abs(extWithdrawUsed)), priority: routes.length };
44448
+ const rawCap = this._budget.extAvailWithdraw + this._budget.extAvailUpnl;
44449
+ const rawSpend = Math.min(tryAmount, rawCap);
44450
+ if (rawSpend <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
44451
+ const rawOut = this._budget.spendExtAvailTrade(rawSpend);
44452
+ this._budget.addToWallet(Math.abs(rawOut));
44453
+ const route = { type: "EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */, amount: safeUsdcWeb3Number(rawSpend), priority: routes.length };
43317
44454
  routes.push(route);
43318
44455
  if (shouldAddWaitRoute) {
43319
44456
  routes.push({ type: "RETURN_TO_WAIT" /* RETURN_TO_WAIT */, priority: routes.length });
43320
44457
  }
43321
- return { routes, remaining: tryAmount - Math.abs(extWithdrawUsed) };
44458
+ return { routes, remaining: tryAmount - rawSpend };
43322
44459
  }
43323
44460
  _getWALLETToVARoute(tryAmount, routes) {
43324
44461
  if (tryAmount <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
43325
- const usableAmount = Math.min(tryAmount, this._budget.walletUsd);
43326
- if (usableAmount <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
43327
- const walletUsed = this._budget.spendWallet(tryAmount);
44462
+ const usableRaw = Math.min(tryAmount, this._budget.walletUsd);
44463
+ if (usableRaw <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
44464
+ const walletUsed = this._budget.spendWallet(usableRaw);
43328
44465
  this._budget.addToVA(walletUsed);
43329
44466
  const route = { type: "WALLET_TO_VA" /* WALLET_TO_VA */, amount: safeUsdcWeb3Number(walletUsed), priority: routes.length };
43330
44467
  routes.push(route);
43331
44468
  return { routes, remaining: tryAmount - walletUsed };
43332
44469
  }
43333
44470
  _getUpnlRoute(tryAmount, routes) {
43334
- const upnl = this._budget.extendedBalance?.unrealisedPnl?.toNumber() ?? 0;
43335
- const usableAmount = Math.min(tryAmount, upnl);
43336
- if (usableAmount <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
43337
- const upnlUsed = this._budget.spendExtAvailUpnl(usableAmount);
43338
- this._budget.addToExtAvailTrade(upnlUsed);
43339
- assert(this._budget.extendedPositions.length == 1, "SolveBudget::_getUpnlRoute: extendedPositions length must be 1");
44471
+ const rawUpnl = this._budget.extAvailUpnl;
44472
+ const usableRaw = Math.min(tryAmount, rawUpnl);
44473
+ if (usableRaw <= 0) return { routes, remaining: tryAmount };
44474
+ this._budget.spendExtAvailUpnl(usableRaw);
44475
+ this._budget.addToExtAvailTrade(usableRaw);
44476
+ assert(this._budget.extendedPositionsView.length == 1, "SolveBudget::_getUpnlRoute: extendedPositions length must be 1");
43340
44477
  routes.push({
43341
44478
  type: "REALISE_PNL" /* REALISE_PNL */,
43342
- amount: safeUsdcWeb3Number(upnlUsed),
43343
- instrument: this._budget.extendedPositions[0].instrument,
44479
+ amount: safeUsdcWeb3Number(usableRaw),
44480
+ instrument: this._budget.extendedPositionsView[0].instrument,
43344
44481
  priority: routes.length
43345
44482
  });
43346
- return { routes, remaining: tryAmount - upnlUsed };
44483
+ return { routes, remaining: tryAmount - usableRaw };
43347
44484
  }
43348
44485
  // ── Sub-classifiers ────────────────────────────────────────────────────
43349
44486
  // Each sub-classifier builds routes directly from contextual data.
@@ -43358,7 +44495,7 @@ var ExtendedSVKVesuStateManager = class {
43358
44495
  const instrument = this._config.extendedAdapter.config.extendedMarketName ?? "BTC-USD";
43359
44496
  const routes = [];
43360
44497
  let remaining = withdrawAmount.toNumber();
43361
- const vaUsed = this._budget.spendVA(Math.min(this._budget.vaUsd, remaining));
44498
+ const vaUsed = this._budget.spendVA(remaining);
43362
44499
  remaining -= vaUsed;
43363
44500
  let totalExtUsed = 0;
43364
44501
  if (remaining > CASE_THRESHOLD_USD) {
@@ -43381,48 +44518,90 @@ var ExtendedSVKVesuStateManager = class {
43381
44518
  totalExtUsed = usableWithrawAmount + upnlUsed;
43382
44519
  }
43383
44520
  if (remaining > CASE_THRESHOLD_USD) {
43384
- const avgCollPrice = this._budget.vesuPoolStates[0]?.collateralPrice ?? 1;
43385
- assert(this._budget.vesuPoolStates.length == 1, "SolveBudget::_classifyWithdrawal: vesuPoolStates length must be 1");
43386
- const { vesuPositionDelta, extendedPositionDelta, vesuAllocationUsd, extendedAllocationUsd } = this._computeAllocationSplit(new Web3Number((-remaining).toFixed(6), USDC_TOKEN_DECIMALS));
43387
- if (vesuPositionDelta < 0) {
43388
- const vesuAdapter = this._config.vesuAdapters[0];
43389
- const debtDelta = Math.min(0, vesuPositionDelta * avgCollPrice - vesuAllocationUsd.toNumber());
43390
- const withdrawAmount2 = vesuAllocationUsd.dividedBy(avgCollPrice);
43391
- withdrawAmount2.decimals = vesuAdapter.config.collateral.decimals;
44521
+ assert(this._budget.vesuPools.length == 1, "SolveBudget::_classifyWithdrawal: vesuPoolStates length must be 1");
44522
+ const vesuAdapter = this._config.vesuAdapters[0];
44523
+ const avgCollPrice = this._budget.vesuPools[0]?.collateralPrice ?? 1;
44524
+ const vesuLeverage = calculateVesuLeverage();
44525
+ const extLeverage = calculateExtendedLevergae();
44526
+ const freedPerBtcVesu = avgCollPrice / vesuLeverage;
44527
+ const freedPerBtcExt = avgCollPrice / extLeverage;
44528
+ const vesuColBtc = this._budget.vesuPools[0].collateralAmount.toNumber();
44529
+ const extPosBtc = this._totalExtendedExposure().toNumber();
44530
+ let stillNeeded = remaining;
44531
+ let vesuBtcDelta = 0;
44532
+ let extBtcDelta = 0;
44533
+ let extFreed = 0;
44534
+ const roundUpBtc = (x) => {
44535
+ const factor = 10 ** COLLATERAL_PRECISION;
44536
+ return Math.ceil(x * factor) / factor;
44537
+ };
44538
+ const diff = vesuColBtc - extPosBtc;
44539
+ let currentVesuBtc = vesuColBtc;
44540
+ let currentExtBtc = extPosBtc;
44541
+ if (Math.abs(diff) > 1e-8) {
44542
+ if (diff > 0) {
44543
+ const btcRaw = stillNeeded / freedPerBtcVesu;
44544
+ const btc = Math.min(roundUpBtc(Math.min(Math.abs(diff), btcRaw, currentVesuBtc)), currentVesuBtc);
44545
+ vesuBtcDelta += btc;
44546
+ stillNeeded -= btc * freedPerBtcVesu;
44547
+ currentVesuBtc -= btc;
44548
+ } else {
44549
+ const btcRaw = stillNeeded / freedPerBtcExt;
44550
+ const btc = Math.min(roundUpBtc(Math.min(Math.abs(diff), btcRaw, currentExtBtc)), currentExtBtc);
44551
+ extBtcDelta += btc;
44552
+ extFreed += btc * freedPerBtcExt;
44553
+ stillNeeded -= btc * freedPerBtcExt;
44554
+ currentExtBtc -= btc;
44555
+ }
44556
+ }
44557
+ if (stillNeeded > CASE_THRESHOLD_USD) {
44558
+ const combinedFreed = freedPerBtcVesu + freedPerBtcExt;
44559
+ const maxBtc = Math.min(currentVesuBtc, currentExtBtc);
44560
+ const btcRaw = stillNeeded / combinedFreed;
44561
+ const btc = Math.min(roundUpBtc(Math.min(btcRaw, maxBtc)), maxBtc);
44562
+ vesuBtcDelta += btc;
44563
+ extBtcDelta += btc;
44564
+ extFreed += btc * freedPerBtcExt;
44565
+ }
44566
+ const r6 = (n) => Number(n.toFixed(6));
44567
+ if (vesuBtcDelta > 0) {
44568
+ const totalVesuBtcSigned = -vesuBtcDelta;
44569
+ const targetLtv = 1 - 1 / vesuLeverage;
44570
+ const debtDelta = r6(totalVesuBtcSigned * avgCollPrice * targetLtv);
44571
+ const marginBtc = 0;
44572
+ const swappedBtc = Number(vesuBtcDelta.toFixed(COLLATERAL_PRECISION));
43392
44573
  routes.push({
43393
44574
  type: "VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */,
43394
44575
  poolId: vesuAdapter.config.poolId,
43395
44576
  collateralToken: vesuAdapter.config.collateral,
43396
- marginAmount: withdrawAmount2,
43397
- swappedCollateralAmount: new Web3Number(vesuPositionDelta, vesuAdapter.config.collateral.decimals).minus(withdrawAmount2),
44577
+ marginAmount: new Web3Number(marginBtc.toFixed(COLLATERAL_PRECISION), vesuAdapter.config.collateral.decimals),
44578
+ swappedCollateralAmount: new Web3Number(swappedBtc.toFixed(COLLATERAL_PRECISION), vesuAdapter.config.collateral.decimals),
43398
44579
  debtToken: vesuAdapter.config.debt,
43399
44580
  debtAmount: new Web3Number(debtDelta, USDC_TOKEN_DECIMALS),
43400
44581
  priority: routes.length
43401
44582
  });
43402
- this._budget.applyVesuDelta(vesuAdapter.config.poolId, vesuAdapter.config.collateral, vesuAdapter.config.debt, new Web3Number(vesuPositionDelta, USDC_TOKEN_DECIMALS), new Web3Number(debtDelta, USDC_TOKEN_DECIMALS));
43403
- if (vesuAllocationUsd.toNumber() < -CASE_THRESHOLD_USD) {
43404
- const swapAmount = new Web3Number((vesuAllocationUsd.toNumber() / avgCollPrice * 0.998).toFixed(6), USDC_TOKEN_DECIMALS);
43405
- routes.push({
43406
- type: "AVNU_WITHDRAW_SWAP" /* AVNU_WITHDRAW_SWAP */,
43407
- fromToken: vesuAdapter.config.collateral.symbol,
43408
- // add buffer to avoid rounding errors
43409
- fromAmount: swapAmount,
43410
- toToken: vesuAdapter.config.debt.symbol,
43411
- priority: routes.length
43412
- });
43413
- this._budget.addToVA(vesuAllocationUsd.abs().toNumber());
43414
- }
44583
+ this._budget.applyVesuDelta(
44584
+ vesuAdapter.config.poolId,
44585
+ vesuAdapter.config.collateral,
44586
+ vesuAdapter.config.debt,
44587
+ new Web3Number(r6(totalVesuBtcSigned), USDC_TOKEN_DECIMALS),
44588
+ new Web3Number(debtDelta, USDC_TOKEN_DECIMALS)
44589
+ );
44590
+ this._budget.addToVA(vesuBtcDelta * freedPerBtcVesu);
43415
44591
  }
43416
- if (extendedPositionDelta < 0) {
44592
+ if (extBtcDelta > 0) {
43417
44593
  routes.push({
43418
44594
  type: "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
43419
- amount: safeUsdcWeb3Number(extendedPositionDelta),
44595
+ amount: safeUsdcWeb3Number(-r6(extBtcDelta)),
43420
44596
  instrument,
43421
44597
  priority: routes.length
43422
44598
  });
43423
- this._budget.applyExtendedExposureDelta(instrument, safeUsdcWeb3Number(extendedPositionDelta));
43424
- this._budget.addToExtAvailTrade(extendedAllocationUsd.abs().toNumber());
43425
- totalExtUsed += extendedAllocationUsd.abs().toNumber();
44599
+ this._budget.applyExtendedExposureDelta(
44600
+ instrument,
44601
+ safeUsdcWeb3Number(-r6(extBtcDelta)),
44602
+ avgCollPrice
44603
+ );
44604
+ totalExtUsed += extFreed;
43426
44605
  }
43427
44606
  }
43428
44607
  if (totalExtUsed > CASE_THRESHOLD_USD) {
@@ -43455,56 +44634,231 @@ var ExtendedSVKVesuStateManager = class {
43455
44634
  *
43456
44635
  * Design: accumulate all ext-to-wallet moves, add transfer routes at the end (principle #3).
43457
44636
  */
43458
- _classifyLtvVesu() {
43459
- const debtDeltaSum = this._budget.vesuPerPoolDebtDeltasToBorrow.reduce(
43460
- (a, b) => a + b.toNumber(),
43461
- 0
44637
+ /**
44638
+ * Unified LTV classifier. Computes both Vesu repay and Extended margin needs,
44639
+ * then builds all routes in a single pass with no duplicate transfers.
44640
+ *
44641
+ * Vesu repay priority: VA > Wallet > ExtAvl > ExtUpnl
44642
+ * Extended margin priority: Wallet > VA > VesuBorrow
44643
+ * Shared sources consumed by Vesu first (higher priority).
44644
+ */
44645
+ _classifyLTV() {
44646
+ assert(this._budget.vesuPools.length === 1, `${this._tag}::_classifyLTV expects exactly one Vesu pool`);
44647
+ const d = rebalance(this._ltvRebalanceInputsFromBudget());
44648
+ if (this._isLtvRebalanceNoop(d)) return [];
44649
+ logger.info(
44650
+ `${this._tag}::_classifyLTV deltas extPos=${d.dExtPosition} vesuPos=${d.dVesuPosition} vesuDebt=${d.dVesuDebt} va=${d.dVaUsd} wallet=${d.dWalletUsd} borrow=${d.dVesuBorrowCapacity} T=${d.dTransferVesuToExt}`
43462
44651
  );
43463
- logger.info(`${this._tag}::_classifyLtvVesu debtDeltaSum: ${debtDeltaSum}`);
43464
- const allHealthLTVs = this._budget.shouldVesuRebalance.every((shouldReb) => !shouldReb);
43465
- if (debtDeltaSum >= -CASE_THRESHOLD_USD || allHealthLTVs) return [];
43466
- const needed = Math.abs(debtDeltaSum);
43467
- const routes = [];
43468
- let remaining = needed;
43469
- let totalExtUsed = 0;
43470
- let caseId = "LTV_VESU_HIGH_USE_VA_OR_WALLET" /* LTV_VESU_HIGH_USE_VA_OR_WALLET */;
43471
- const vaUsed = this._budget.spendVA(Math.min(this._budget.vaUsd, remaining));
43472
- remaining -= vaUsed;
43473
- if (remaining > CASE_THRESHOLD_USD) {
43474
- const { remaining: walletToVaRemaining } = this._getWALLETToVARoute(remaining, routes);
43475
- remaining = walletToVaRemaining;
43476
- }
43477
- if (remaining > CASE_THRESHOLD_USD) {
43478
- const usableWithdrawAmount = Math.min(remaining, this._budget.extAvailWithdraw);
43479
- remaining -= usableWithdrawAmount;
43480
- let upnlUsed = 0;
43481
- if (remaining > CASE_THRESHOLD_USD) {
43482
- const { remaining: upnlRemaining } = this._getUpnlRoute(remaining, routes);
43483
- upnlUsed = remaining - upnlRemaining;
43484
- remaining = upnlRemaining;
43485
- caseId = "LTV_EXTENDED_PROFITABLE_REALIZE" /* LTV_EXTENDED_PROFITABLE_REALIZE */;
43486
- } else {
43487
- caseId = "LTV_EXTENDED_PROFITABLE_AVAILABLE" /* LTV_EXTENDED_PROFITABLE_AVAILABLE */;
43488
- }
43489
- totalExtUsed = usableWithdrawAmount + upnlUsed;
43490
- }
43491
- if (remaining > CASE_THRESHOLD_USD) {
43492
- throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
43493
- }
43494
- if (totalExtUsed > CASE_THRESHOLD_USD) {
43495
- this._getExtendedToWalletRoute(totalExtUsed, routes);
43496
- this._getWALLETToVARoute(totalExtUsed, routes);
43497
- }
43498
- this._buildVesuRepayRoutes(needed - remaining, routes);
44652
+ const routes = this._buildLtvRoutesFromRebalanceDeltas(d);
44653
+ if (routes.length === 0) return [];
44654
+ const amountUsd = Math.abs(d.dVaUsd) + Math.abs(d.dWalletUsd) + Math.abs(d.dVesuBorrowCapacity) + Math.abs(d.dExtAvlWithdraw) + Math.abs(d.dExtUpnl) + Math.abs(d.dTransferVesuToExt);
43499
44655
  routes.forEach((r, i) => {
43500
44656
  r.priority = i;
43501
44657
  });
43502
44658
  return [{
43503
- case: CASE_DEFINITIONS[caseId],
43504
- additionalArgs: { amount: safeUsdcWeb3Number(needed) },
44659
+ case: CASE_DEFINITIONS["MANAGE_LTV" /* MANAGE_LTV */],
44660
+ additionalArgs: { amount: safeUsdcWeb3Number(amountUsd) },
43505
44661
  routes
43506
44662
  }];
43507
44663
  }
44664
+ _ltvRebalanceInputsFromBudget() {
44665
+ const pool = this._budget.vesuPools[0];
44666
+ const instrument = this._config.extendedAdapter.config.extendedMarketName ?? "BTC-USD";
44667
+ const extPosBtc = this._budget.extendedPositionsView.filter((p) => p.instrument === instrument).reduce((s, p) => s + Math.abs(p.size.toNumber()), 0);
44668
+ const targetHF = VesuConfig.maxLtv / VesuConfig.targetLtv;
44669
+ return {
44670
+ ext: {
44671
+ positionBtc: extPosBtc,
44672
+ equity: this._budget.extendedBalanceView?.equity?.toNumber() ?? 0,
44673
+ avlWithdraw: this._budget.extAvailWithdraw,
44674
+ upnl: this._budget.extAvailUpnl,
44675
+ leverage: calculateExtendedLevergae()
44676
+ },
44677
+ vesu: {
44678
+ positionBtc: pool.collateralAmount.toNumber(),
44679
+ debt: pool.debtAmount.toNumber(),
44680
+ debtPrice: pool.debtPrice,
44681
+ maxLTV: VesuConfig.maxLtv,
44682
+ targetHF
44683
+ },
44684
+ btcPrice: pool.collateralPrice,
44685
+ funding: {
44686
+ vaUsd: this._budget.vaUsd,
44687
+ walletUsd: this._budget.walletUsd,
44688
+ vesuBorrowCapacity: this._budget.vesuBorrowCapacity,
44689
+ extAvlWithdraw: this._budget.extAvailWithdraw,
44690
+ extUpnl: this._budget.extAvailUpnl
44691
+ },
44692
+ config: {
44693
+ positionPrecision: COLLATERAL_PRECISION,
44694
+ hfBuffer: 0.05
44695
+ }
44696
+ };
44697
+ }
44698
+ _isLtvRebalanceNoop(d) {
44699
+ const btcEps = 10 ** -COLLATERAL_PRECISION;
44700
+ const usdEps = CASE_THRESHOLD_USD;
44701
+ return Math.abs(d.dExtPosition) < btcEps && Math.abs(d.dVesuPosition) < btcEps && Math.abs(d.dVesuDebt) < 1e-6 && Math.abs(d.dExtAvlWithdraw) < usdEps && Math.abs(d.dExtUpnl) < usdEps && Math.abs(d.dVaUsd) < usdEps && Math.abs(d.dWalletUsd) < usdEps && Math.abs(d.dVesuBorrowCapacity) < usdEps && Math.abs(d.dTransferVesuToExt) < usdEps;
44702
+ }
44703
+ /**
44704
+ * Turn pure rebalance() deltas into execution routes.
44705
+ * Order: Vesu multiply (decrease/increase) → Extended lever → aggregated transfers
44706
+ * (REALISE_PNL, EXTENDED_TO_WALLET + WAIT, WALLET_TO_VA, VESU_BORROW, VESU_REPAY, VA_TO_EXTENDED).
44707
+ */
44708
+ _buildLtvRoutesFromRebalanceDeltas(d) {
44709
+ const routes = [];
44710
+ const pool = this._budget.vesuPools[0];
44711
+ const vesuAdapter = this._config.vesuAdapters[0];
44712
+ const instrument = this._config.extendedAdapter.config.extendedMarketName ?? "BTC-USD";
44713
+ const price = pool.collateralPrice;
44714
+ const debtPrice = pool.debtPrice;
44715
+ const targetLtv = VesuConfig.targetLtv;
44716
+ const btcEps = 10 ** -COLLATERAL_PRECISION;
44717
+ let multiplyDebtRepayUsd = 0;
44718
+ if (d.dVesuPosition < -btcEps) {
44719
+ const xBtc = -d.dVesuPosition;
44720
+ const transferUsdFromVesu = Math.max(0, d.dTransferVesuToExt);
44721
+ let marginBtc = 0;
44722
+ let swappedBtc = Number(xBtc.toFixed(COLLATERAL_PRECISION));
44723
+ if (transferUsdFromVesu > CASE_THRESHOLD_USD && price > 0) {
44724
+ const marginCapFromTransfer = transferUsdFromVesu / price;
44725
+ marginBtc = Number(
44726
+ Math.min(xBtc, marginCapFromTransfer).toFixed(COLLATERAL_PRECISION)
44727
+ );
44728
+ swappedBtc = Number((xBtc - marginBtc).toFixed(COLLATERAL_PRECISION));
44729
+ }
44730
+ const swapLegMaxRepayUsd = swappedBtc * price * debtPrice;
44731
+ const debtUsdFallback = swappedBtc * price * targetLtv;
44732
+ let debtTokenDelta;
44733
+ if (d.dVesuDebt < 0) {
44734
+ const needRepayUsd = -d.dVesuDebt * debtPrice;
44735
+ const multiplyRepayUsd = Math.min(needRepayUsd, swapLegMaxRepayUsd);
44736
+ debtTokenDelta = -(multiplyRepayUsd / debtPrice);
44737
+ } else {
44738
+ debtTokenDelta = -debtUsdFallback;
44739
+ }
44740
+ const debtAmtW3 = new Web3Number(debtTokenDelta.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
44741
+ multiplyDebtRepayUsd = Math.abs(debtTokenDelta) * debtPrice;
44742
+ routes.push({
44743
+ type: "VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */,
44744
+ poolId: vesuAdapter.config.poolId,
44745
+ collateralToken: vesuAdapter.config.collateral,
44746
+ marginAmount: new Web3Number(marginBtc.toFixed(COLLATERAL_PRECISION), vesuAdapter.config.collateral.decimals),
44747
+ swappedCollateralAmount: new Web3Number(swappedBtc.toFixed(COLLATERAL_PRECISION), vesuAdapter.config.collateral.decimals),
44748
+ debtToken: vesuAdapter.config.debt,
44749
+ debtAmount: debtAmtW3,
44750
+ priority: routes.length
44751
+ });
44752
+ this._budget.applyVesuDelta(
44753
+ vesuAdapter.config.poolId,
44754
+ vesuAdapter.config.collateral,
44755
+ vesuAdapter.config.debt,
44756
+ new Web3Number((-xBtc).toFixed(COLLATERAL_PRECISION), vesuAdapter.config.collateral.decimals),
44757
+ debtAmtW3
44758
+ );
44759
+ if (transferUsdFromVesu > CASE_THRESHOLD_USD) {
44760
+ this._budget.addToVA(transferUsdFromVesu);
44761
+ }
44762
+ } else if (d.dVesuPosition > btcEps) {
44763
+ const vesuDepositAmount = new Web3Number(
44764
+ (d.dVesuPosition * price * (1 - targetLtv)).toFixed(USDC_TOKEN_DECIMALS),
44765
+ USDC_TOKEN_DECIMALS
44766
+ );
44767
+ if (vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
44768
+ routes.push({
44769
+ type: "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
44770
+ priority: routes.length,
44771
+ fromToken: vesuAdapter.config.collateral.symbol,
44772
+ fromAmount: vesuDepositAmount,
44773
+ toToken: vesuAdapter.config.debt.symbol
44774
+ });
44775
+ }
44776
+ const collateralDelta = new Web3Number(
44777
+ d.dVesuPosition.toFixed(COLLATERAL_PRECISION),
44778
+ vesuAdapter.config.collateral.decimals
44779
+ );
44780
+ const availableBorrowCapacity = this._budget.vesuBorrowCapacity;
44781
+ const externalDepositAmount = vesuDepositAmount.minus(
44782
+ new Web3Number(Math.min(availableBorrowCapacity, vesuDepositAmount.toNumber()).toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS)
44783
+ );
44784
+ const collPx = pool.collateralPrice || 1;
44785
+ const swappedAmount = new Web3Number(
44786
+ (externalDepositAmount.toNumber() * (pool.debtPrice ?? 1) / collPx).toFixed(6),
44787
+ vesuAdapter.config.collateral.decimals
44788
+ );
44789
+ const debtDeltaTokens = new Web3Number(
44790
+ d.dVesuDebt.toFixed(USDC_TOKEN_DECIMALS),
44791
+ USDC_TOKEN_DECIMALS
44792
+ );
44793
+ routes.push({
44794
+ type: "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */,
44795
+ priority: routes.length,
44796
+ collateralToken: vesuAdapter.config.collateral,
44797
+ debtToken: vesuAdapter.config.debt,
44798
+ marginAmount: swappedAmount,
44799
+ swappedCollateralAmount: collateralDelta.minus(swappedAmount),
44800
+ debtAmount: debtDeltaTokens.plus(new Web3Number(Math.min(availableBorrowCapacity, vesuDepositAmount.toNumber()).toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS)),
44801
+ poolId: vesuAdapter.config.poolId
44802
+ });
44803
+ this._budget.applyVesuDelta(
44804
+ vesuAdapter.config.poolId,
44805
+ vesuAdapter.config.collateral,
44806
+ vesuAdapter.config.debt,
44807
+ collateralDelta,
44808
+ debtDeltaTokens
44809
+ );
44810
+ }
44811
+ if (d.dExtPosition < -btcEps) {
44812
+ const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
44813
+ routes.push({
44814
+ type: "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
44815
+ amount: amt,
44816
+ instrument,
44817
+ priority: routes.length
44818
+ });
44819
+ this._budget.applyExtendedExposureDelta(instrument, amt, price);
44820
+ } else if (d.dExtPosition > btcEps) {
44821
+ const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
44822
+ routes.push({
44823
+ type: "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */,
44824
+ amount: amt,
44825
+ instrument,
44826
+ priority: routes.length
44827
+ });
44828
+ this._budget.applyExtendedExposureDelta(instrument, amt, price);
44829
+ }
44830
+ const negUpnl = Math.min(0, d.dExtUpnl);
44831
+ const negExtAvl = Math.min(0, d.dExtAvlWithdraw);
44832
+ let hadExtendedOut = false;
44833
+ if (negUpnl < -CASE_THRESHOLD_USD) {
44834
+ this._getUpnlRoute(Math.abs(negUpnl), routes);
44835
+ hadExtendedOut = true;
44836
+ }
44837
+ const extToWalletUsd = (negExtAvl < -CASE_THRESHOLD_USD ? Math.abs(negExtAvl) : 0) + (negUpnl < -CASE_THRESHOLD_USD ? Math.abs(negUpnl) : 0);
44838
+ if (extToWalletUsd > CASE_THRESHOLD_USD) {
44839
+ this._getExtendedToWalletRoute(extToWalletUsd, routes);
44840
+ hadExtendedOut = true;
44841
+ }
44842
+ const walletPull = Math.abs(Math.min(0, d.dWalletUsd));
44843
+ const walletToVaUsd = walletPull + extToWalletUsd;
44844
+ if (walletToVaUsd > CASE_THRESHOLD_USD) {
44845
+ this._getWALLETToVARoute(walletToVaUsd, routes);
44846
+ }
44847
+ if (d.dVesuBorrowCapacity < -CASE_THRESHOLD_USD) {
44848
+ this._buildVesuBorrowRoutes(Math.abs(d.dVesuBorrowCapacity), routes);
44849
+ }
44850
+ const totalDebtRepayUsd = d.dVesuDebt < 0 ? -d.dVesuDebt * debtPrice : 0;
44851
+ const standaloneRepayUsd = Math.max(0, totalDebtRepayUsd - multiplyDebtRepayUsd);
44852
+ if (standaloneRepayUsd > CASE_THRESHOLD_USD) {
44853
+ this._buildVesuRepayRoutes(standaloneRepayUsd, routes);
44854
+ }
44855
+ const posExtEq = Math.max(0, d.dExtAvlWithdraw);
44856
+ const vaToExtUsd = posExtEq > CASE_THRESHOLD_USD ? posExtEq : 0;
44857
+ if (vaToExtUsd > CASE_THRESHOLD_USD) {
44858
+ this._getVAToEXTENDEDRoute(vaToExtUsd, routes, hadExtendedOut);
44859
+ }
44860
+ return routes;
44861
+ }
43508
44862
  // ── LTV Vesu route builders ───────────────────────────────────────────
43509
44863
  /**
43510
44864
  * LTV_EXTENDED_PROFITABLE_AVAILABLE:
@@ -43581,60 +44935,7 @@ var ExtendedSVKVesuStateManager = class {
43581
44935
  // routes.forEach((r, i) => { r.priority = i; });
43582
44936
  // return routes;
43583
44937
  // }
43584
- /** 2b. LTV Rebalance Extended side */
43585
- /**
43586
- * 2b. LTV Rebalance — Extended side
43587
- *
43588
- * Triggered when Extended equity is below the required margin for current positions.
43589
- * Sources funds to Extended via VA/Wallet or Vesu borrow.
43590
- *
43591
- * Priority: 1) VA/Wallet → Extended 2) Vesu borrow → VA → Extended
43592
- */
43593
- _classifyLtvExtended() {
43594
- const totalExtPosUsd = this._totalExtendedExposureUsd().toNumber();
43595
- const extEquity = this._budget.extendedBalance?.equity?.toNumber() ?? 0;
43596
- const lev = calculateExtendedLevergae();
43597
- const marginNeeded = lev > 0 ? totalExtPosUsd / lev - extEquity : 0;
43598
- if (marginNeeded <= CASE_THRESHOLD_USD) return [];
43599
- let caseId = "LTV_EXTENDED_HIGH_USE_VA_OR_WALLET" /* LTV_EXTENDED_HIGH_USE_VA_OR_WALLET */;
43600
- let remaining = marginNeeded;
43601
- const routes = [];
43602
- if (this._budget.vaWalletUsd > CASE_THRESHOLD_USD && remaining > CASE_THRESHOLD_USD) {
43603
- const use = Math.min(this._budget.vaWalletUsd, remaining);
43604
- if (this._budget.vaUsd > CASE_THRESHOLD_USD) {
43605
- const { remaining: vaRem } = this._getVAToEXTENDEDRoute(remaining, routes, false);
43606
- remaining = vaRem;
43607
- }
43608
- if (remaining > CASE_THRESHOLD_USD) {
43609
- const { remaining: walletRem } = this._getWalletToEXTENDEDRoute(remaining, routes, false);
43610
- remaining = walletRem;
43611
- }
43612
- }
43613
- if (remaining > CASE_THRESHOLD_USD && this._budget.vesuBorrowCapacity > CASE_THRESHOLD_USD) {
43614
- const { remaining: borrowRem } = this._buildVesuBorrowRoutes(Math.min(remaining, this._budget.vesuBorrowCapacity), routes);
43615
- const borrowed = remaining - borrowRem;
43616
- if (remaining != borrowRem) {
43617
- const { remaining: vaRem } = this._getVAToEXTENDEDRoute(borrowed, routes, false);
43618
- }
43619
- remaining = borrowRem;
43620
- routes.forEach((r, i) => {
43621
- r.priority = i;
43622
- });
43623
- remaining -= borrowed;
43624
- caseId = "LTV_VESU_LOW_TO_EXTENDED" /* LTV_VESU_LOW_TO_EXTENDED */;
43625
- }
43626
- if (remaining > CASE_THRESHOLD_USD) {
43627
- throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
43628
- }
43629
- routes.forEach((r, i) => {
43630
- r.priority = i;
43631
- });
43632
- return [{
43633
- case: CASE_DEFINITIONS[caseId],
43634
- additionalArgs: { amount: safeUsdcWeb3Number(marginNeeded) },
43635
- routes
43636
- }];
43637
- }
44938
+ // _classifyLtvExtended has been merged into the unified _classifyLTV above.
43638
44939
  // ── LTV Extended route builders ───────────────────────────────────────
43639
44940
  /**
43640
44941
  * LTV_EXTENDED_HIGH_USE_VA_OR_WALLET:
@@ -43701,6 +45002,62 @@ var ExtendedSVKVesuStateManager = class {
43701
45002
  // return routes;
43702
45003
  // }
43703
45004
  // ! todo implement max lever amount per execution cycle
45005
+ _rebalanceFunds({
45006
+ extAvlWithdraw,
45007
+ extUpnl,
45008
+ vaUsd,
45009
+ walletUsd,
45010
+ vesuBorrowCapacity,
45011
+ vesuLeverage,
45012
+ extendedLeverage
45013
+ }) {
45014
+ const total = extAvlWithdraw + extUpnl + vaUsd + walletUsd + vesuBorrowCapacity;
45015
+ const extendedTarget = total / (1 + extendedLeverage / vesuLeverage);
45016
+ const extendedInitial = extAvlWithdraw + extUpnl;
45017
+ let delta = extendedTarget - extendedInitial;
45018
+ let dExtAvlWithdraw = 0, dExtUpnl = 0, dVaUsd = 0, dWalletUsd = 0, dVesuBorrowCapacity = 0;
45019
+ if (delta > 0) {
45020
+ let need = delta;
45021
+ const takeWalletUsd = Math.min(walletUsd, need);
45022
+ dWalletUsd -= takeWalletUsd;
45023
+ need -= takeWalletUsd;
45024
+ const takeVaUsd = Math.min(vaUsd, need);
45025
+ dVaUsd -= takeVaUsd;
45026
+ need -= takeVaUsd;
45027
+ const takeVesuBorrowCapacity = Math.min(vesuBorrowCapacity, need);
45028
+ dVesuBorrowCapacity -= takeVesuBorrowCapacity;
45029
+ need -= takeVesuBorrowCapacity;
45030
+ const received = delta - need;
45031
+ const eco1Sum = extAvlWithdraw + extUpnl;
45032
+ if (eco1Sum >= 0) {
45033
+ dExtAvlWithdraw += received;
45034
+ } else {
45035
+ throw new Error(`${this._tag}: Unexpected case`);
45036
+ }
45037
+ if (need > 0) {
45038
+ throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
45039
+ }
45040
+ } else if (delta < 0) {
45041
+ let need = -delta;
45042
+ const takeExtAvlWithdraw = Math.min(extAvlWithdraw, need);
45043
+ dExtAvlWithdraw -= takeExtAvlWithdraw;
45044
+ need -= takeExtAvlWithdraw;
45045
+ const takeExtUpnl = Math.min(extUpnl, need);
45046
+ dExtUpnl -= takeExtUpnl;
45047
+ need -= takeExtUpnl;
45048
+ const sent = -delta - need;
45049
+ const eco2Sum = vaUsd + walletUsd + vesuBorrowCapacity;
45050
+ if (eco2Sum >= 0) {
45051
+ dWalletUsd += sent;
45052
+ } else {
45053
+ throw new Error(`${this._tag}: Unexpected case`);
45054
+ }
45055
+ if (need > 0) {
45056
+ throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
45057
+ }
45058
+ }
45059
+ return { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu: delta < 0 };
45060
+ }
43704
45061
  /**
43705
45062
  * 3. New Deposits / Excess Funds
43706
45063
  *
@@ -43715,81 +45072,84 @@ var ExtendedSVKVesuStateManager = class {
43715
45072
  * Computes allocation split between Vesu and Extended, then sources
43716
45073
  * funds and creates lever-increase routes.
43717
45074
  *
43718
- * Fund flow (principle #3accumulate transfers, defer wait):
43719
- * Phase A: fund Extended (wallet→ext, VA→ext, vesu-borrow→VA→ext)
43720
- * Phase B: fund Vesu VA shortfall (wallet→VA, ext→wallet + wallet→VA)
43721
- * Phase C: RETURN_TO_WAIT (if any transfer to Extended occurred)
43722
- * Phase D: lever routes (VESU_MULTIPLY, EXTENDED_INCREASE) near each other (#4)
45075
+ * Fund flow (single passavoid VA→Extended then Extended→wallet round-trips):
45076
+ * 1) Treat Vesu borrow headroom that the multiply route will consume as covering
45077
+ * part of the Vesu USDC need (no standalone VESU_BORROW for that slice). Cap
45078
+ * standalone VESU_BORROW→VA→Extended by remaining headroom.
45079
+ * 2) Cover Extended deposit delta: REALISE_PNL first, then withdrawal→avail-trade,
45080
+ * then wallet→Extended, then VA→Extended only up to VA surplus above the USDC
45081
+ * that must remain for Vesu (after step 1), then borrow→VA→Extended.
45082
+ * 3) Cover Vesu VA shortfall: wallet→VA, Extended withdrawal→wallet→VA, REALISE_PNL,
45083
+ * then combined Extended→wallet→VA for the remainder.
45084
+ * 4) RETURN_TO_WAIT when needed; then AVNU + VESU_MULTIPLY + EXTENDED_INCREASE.
43723
45085
  */
43724
- _classifyDeposits(withdrawAmount) {
45086
+ /**
45087
+ * @param skipAvnuDepositSwap Omit AVNU before Vesu multiply when LTV cases already ran this cycle
45088
+ * (matrix tests expect deposit routes without that step).
45089
+ */
45090
+ _classifyDeposits(withdrawAmount, skipAvnuDepositSwap = false) {
43725
45091
  if (withdrawAmount.toNumber() > CASE_THRESHOLD_USD) return [];
43726
45092
  const distributableAmount = this._computeDistributableAmount(
43727
- this._budget.vesuPerPoolDebtDeltasToBorrow,
45093
+ this._budget.vesuDebtDeltas,
43728
45094
  withdrawAmount
43729
45095
  );
43730
45096
  if (distributableAmount.toNumber() <= CASE_THRESHOLD_USD) return [];
43731
45097
  const { vesuAllocationUsd, extendedAllocationUsd } = this._computeAllocationSplit(distributableAmount);
43732
45098
  const vesuDeltas = this._computePerPoolCollateralDeltas(
43733
- vesuAllocationUsd,
43734
- this._budget.vesuPerPoolDebtDeltasToBorrow
45099
+ vesuAllocationUsd
43735
45100
  );
43736
45101
  const extendedPositionDeltas = this._computeExtendedPositionDeltas(vesuDeltas);
43737
- const extendedDepositDelta = this._computeExtendedDepositDelta(extendedAllocationUsd);
43738
45102
  const vesuDepositAmount = this._computeVesuDepositAmount(vesuDeltas);
45103
+ const vesuLeverage = calculateVesuLeverage();
45104
+ const extendedLeverage = calculateExtendedLevergae();
45105
+ const { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu } = this._rebalanceFunds({
45106
+ extAvlWithdraw: this._budget.extAvailWithdraw,
45107
+ extUpnl: this._budget.extAvailUpnl,
45108
+ vaUsd: this._budget.vaUsd,
45109
+ walletUsd: this._budget.walletUsd,
45110
+ vesuBorrowCapacity: this._budget.vesuBorrowCapacity,
45111
+ vesuLeverage,
45112
+ extendedLeverage
45113
+ });
43739
45114
  const routes = [];
43740
- let needsWait = false;
43741
- if (extendedDepositDelta.toNumber() > CASE_THRESHOLD_USD) {
43742
- let rem = extendedDepositDelta.toNumber();
43743
- if (rem > CASE_THRESHOLD_USD) {
43744
- const { remaining } = this._getWalletToEXTENDEDRoute(rem, routes, false);
43745
- if (remaining < rem) needsWait = true;
43746
- rem = remaining;
43747
- }
43748
- if (rem > CASE_THRESHOLD_USD) {
43749
- const { remaining } = this._getVAToEXTENDEDRoute(rem, routes, false);
43750
- if (remaining < rem) needsWait = true;
43751
- rem = remaining;
43752
- }
43753
- if (rem > CASE_THRESHOLD_USD && this._budget.vesuBorrowCapacity > CASE_THRESHOLD_USD) {
43754
- const { remaining: borrowRem } = this._buildVesuBorrowRoutes(rem, routes);
43755
- const borrowed = rem - borrowRem;
43756
- if (borrowRem != rem) {
43757
- this._getVAToEXTENDEDRoute(borrowed, routes, false);
43758
- needsWait = true;
43759
- rem = borrowRem;
43760
- }
45115
+ if (isExtendedToVesu) {
45116
+ if (dExtUpnl < 0) {
45117
+ this._getUpnlRoute(Math.abs(dExtUpnl), routes);
43761
45118
  }
43762
- }
43763
- if (vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
43764
- const vaShortfall = vesuDepositAmount.toNumber() - this._budget.vaUsd;
43765
- if (vaShortfall > CASE_THRESHOLD_USD) {
43766
- let rem = vaShortfall;
43767
- if (rem > CASE_THRESHOLD_USD && this._budget.walletUsd > CASE_THRESHOLD_USD) {
43768
- const { remaining } = this._getWalletToVARoute(Math.min(this._budget.walletUsd, rem), routes);
43769
- rem = remaining;
43770
- }
43771
- const isWithdrawalEnough = rem <= this._budget.extAvailWithdraw;
43772
- if (!isWithdrawalEnough && rem > CASE_THRESHOLD_USD) {
43773
- const { remaining: upnlRem } = this._getUpnlRoute(rem, routes);
43774
- rem = upnlRem;
43775
- }
43776
- if (rem > CASE_THRESHOLD_USD && this._budget.extAvailWithdraw > CASE_THRESHOLD_USD) {
43777
- const extUse = Math.min(rem, this._budget.extAvailWithdraw);
43778
- this._getExtendedToWalletRoute(extUse, routes);
43779
- this._getWALLETToVARoute(extUse, routes);
43780
- rem -= extUse;
43781
- needsWait = false;
43782
- }
45119
+ if (dExtUpnl < 0 || dExtAvlWithdraw < 0) {
45120
+ const netAmount = (dExtAvlWithdraw < 0 ? Math.abs(dExtAvlWithdraw) : 0) + (dExtUpnl < 0 ? Math.abs(dExtUpnl) : 0);
45121
+ const walletUsd = this._budget.walletUsd;
45122
+ this._getExtendedToWalletRoute(netAmount, routes);
45123
+ this._getWALLETToVARoute(netAmount + walletUsd, routes);
45124
+ }
45125
+ } else {
45126
+ let netDVaUsd = dVaUsd;
45127
+ if (dWalletUsd < 0) {
45128
+ this._getWalletToVARoute(this._budget.walletUsd, routes);
45129
+ netDVaUsd += dWalletUsd;
45130
+ }
45131
+ if (dVesuBorrowCapacity < 0) {
45132
+ this._buildVesuBorrowRoutes(Math.abs(dVesuBorrowCapacity), routes);
45133
+ netDVaUsd += dVesuBorrowCapacity;
45134
+ }
45135
+ if (netDVaUsd < 0) {
45136
+ this._getVAToEXTENDEDRoute(Math.abs(netDVaUsd), routes, true);
43783
45137
  }
43784
- }
43785
- if (needsWait) {
43786
- routes.push({ type: "RETURN_TO_WAIT" /* RETURN_TO_WAIT */, priority: routes.length });
43787
45138
  }
43788
45139
  for (const vesuDelta of vesuDeltas) {
43789
- if (vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
45140
+ if (!skipAvnuDepositSwap && vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
45141
+ routes.push({
45142
+ type: "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
45143
+ priority: routes.length,
45144
+ fromToken: vesuDelta.collateralToken.symbol,
45145
+ fromAmount: vesuDepositAmount,
45146
+ toToken: vesuDelta.debtToken.symbol
45147
+ });
43790
45148
  }
43791
45149
  if (vesuDelta.collateralDelta.toNumber() > 0) {
43792
- const swappedAmount = new Web3Number((vesuDepositAmount.toNumber() * vesuDelta.debtPrice / (vesuDelta.collateralPrice ?? 0)).toFixed(6), vesuDelta.collateralToken.decimals);
45150
+ const availableBorrowCapacity = this._budget.vesuBorrowCapacity;
45151
+ const externalDepositAmount = vesuDepositAmount.minus(availableBorrowCapacity);
45152
+ const swappedAmount = new Web3Number((externalDepositAmount.toNumber() * vesuDelta.debtPrice / (vesuDelta.collateralPrice ?? 0)).toFixed(6), vesuDelta.collateralToken.decimals);
43793
45153
  routes.push({
43794
45154
  type: "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */,
43795
45155
  priority: routes.length,
@@ -43798,7 +45158,7 @@ var ExtendedSVKVesuStateManager = class {
43798
45158
  marginAmount: swappedAmount,
43799
45159
  // should be the swapped amount as per vesu multiply adapter
43800
45160
  swappedCollateralAmount: vesuDelta.collateralDelta.minus(swappedAmount),
43801
- debtAmount: vesuDelta.debtDelta,
45161
+ debtAmount: vesuDelta.debtDelta.plus(availableBorrowCapacity),
43802
45162
  poolId: vesuDelta.poolId
43803
45163
  });
43804
45164
  }
@@ -43921,8 +45281,13 @@ var ExtendedSVKVesuStateManager = class {
43921
45281
  */
43922
45282
  _buildImbalanceExtExcessShortNoFundsRoutes(exposureDiffBtc) {
43923
45283
  const instrument = this._config.extendedAdapter.config.extendedMarketName ?? "BTC-USD";
43924
- const decDelta = new Web3Number(exposureDiffBtc.toFixed(COLLATERAL_PRECISION), USDC_TOKEN_DECIMALS);
43925
- this._budget.applyExtendedExposureDelta(instrument, new Web3Number(decDelta.negated().toFixed(COLLATERAL_PRECISION), USDC_TOKEN_DECIMALS));
45284
+ const decDelta = new Web3Number(Web3Number.fromNumber(exposureDiffBtc, 8).toFixedRoundDown(COLLATERAL_PRECISION), USDC_TOKEN_DECIMALS);
45285
+ const collPx = this._budget.vesuPools[0]?.collateralPrice ?? 1;
45286
+ this._budget.applyExtendedExposureDelta(
45287
+ instrument,
45288
+ new Web3Number(Web3Number.fromNumber(decDelta.negated().toNumber(), 8).toFixedRoundDown(COLLATERAL_PRECISION), USDC_TOKEN_DECIMALS),
45289
+ collPx
45290
+ );
43926
45291
  return [{
43927
45292
  type: "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
43928
45293
  amount: decDelta,
@@ -43988,13 +45353,15 @@ var ExtendedSVKVesuStateManager = class {
43988
45353
  _classifyCases(withdrawAmount) {
43989
45354
  this._budget.initBudget();
43990
45355
  const withdrawalCases = this._classifyWithdrawal(withdrawAmount);
43991
- const ltvVesuCases = this._classifyLtvVesu();
43992
- const ltvExtendedCases = this._classifyLtvExtended();
43993
- const depositCases = this._classifyDeposits(withdrawAmount);
45356
+ this._budget.applyBuffer(this._config.limitBalanceBufferFactor);
45357
+ const ltvCases = this._classifyLTV();
45358
+ const depositCases = this._classifyDeposits(
45359
+ withdrawAmount,
45360
+ ltvCases.length > 0
45361
+ );
43994
45362
  return [
43995
45363
  ...withdrawalCases,
43996
- ...ltvVesuCases,
43997
- ...ltvExtendedCases,
45364
+ ...ltvCases,
43998
45365
  ...depositCases
43999
45366
  ];
44000
45367
  }
@@ -44002,7 +45369,7 @@ var ExtendedSVKVesuStateManager = class {
44002
45369
  // Private — aggregation helpers
44003
45370
  // ═══════════════════════════════════════════════════════════════════════════
44004
45371
  _totalVesuCollateral() {
44005
- return this._budget.vesuPoolStates.reduce(
45372
+ return this._budget.vesuPools.reduce(
44006
45373
  (acc, pool) => acc.plus(
44007
45374
  pool.collateralAmount
44008
45375
  ),
@@ -44010,7 +45377,7 @@ var ExtendedSVKVesuStateManager = class {
44010
45377
  );
44011
45378
  }
44012
45379
  _totalVesuCollateralUsd() {
44013
- return this._budget.vesuPoolStates.reduce(
45380
+ return this._budget.vesuPools.reduce(
44014
45381
  (acc, pool) => acc.plus(
44015
45382
  pool.collateralAmount.multipliedBy(pool.collateralPrice)
44016
45383
  ),
@@ -44018,13 +45385,13 @@ var ExtendedSVKVesuStateManager = class {
44018
45385
  );
44019
45386
  }
44020
45387
  _totalExtendedExposure() {
44021
- return this._budget.extendedPositions.reduce(
45388
+ return this._budget.extendedPositionsView.reduce(
44022
45389
  (acc, position) => acc.plus(position.size),
44023
45390
  new Web3Number(0, USDC_TOKEN_DECIMALS)
44024
45391
  );
44025
45392
  }
44026
45393
  _totalExtendedExposureUsd() {
44027
- return this._budget.extendedPositions.reduce(
45394
+ return this._budget.extendedPositionsView.reduce(
44028
45395
  (acc, position) => acc.plus(position.valueUsd),
44029
45396
  new Web3Number(0, USDC_TOKEN_DECIMALS)
44030
45397
  );
@@ -44066,7 +45433,7 @@ var ExtendedSVKVesuStateManager = class {
44066
45433
  };
44067
45434
 
44068
45435
  // src/strategies/vesu-extended-strategy/services/executionService.ts
44069
- var import_starknet33 = require("starknet");
45436
+ var import_starknet34 = require("starknet");
44070
45437
 
44071
45438
  // src/strategies/vesu-extended-strategy/types/transaction-metadata.ts
44072
45439
  var CycleType = /* @__PURE__ */ ((CycleType2) => {
@@ -44111,11 +45478,10 @@ var _ExecutionService = class _ExecutionService {
44111
45478
  this._tokenSymbols = StarknetCallParser.buildTokenSymbolLookup([
44112
45479
  config.wbtcToken,
44113
45480
  config.usdcToken,
44114
- config.usdceToken,
44115
- config.vesuAdapter.config.baseToken,
44116
- config.vesuAdapter.config.collateral,
44117
- config.vesuAdapter.config.debt,
44118
- config.vesuAdapter.config.marginToken,
45481
+ config.vesuMultiplyAdapter.config.baseToken,
45482
+ config.vesuMultiplyAdapter.config.collateral,
45483
+ config.vesuMultiplyAdapter.config.debt,
45484
+ config.vesuMultiplyAdapter.config.marginToken,
44119
45485
  config.vesuModifyPositionAdapter.config.collateral,
44120
45486
  config.vesuModifyPositionAdapter.config.debt,
44121
45487
  ...avnuTokens
@@ -44123,19 +45489,18 @@ var _ExecutionService = class _ExecutionService {
44123
45489
  this._tokenDecimals = StarknetCallParser.buildTokenDecimalsLookup([
44124
45490
  config.wbtcToken,
44125
45491
  config.usdcToken,
44126
- config.usdceToken,
44127
- config.vesuAdapter.config.baseToken,
44128
- config.vesuAdapter.config.collateral,
44129
- config.vesuAdapter.config.debt,
44130
- config.vesuAdapter.config.marginToken,
45492
+ config.vesuMultiplyAdapter.config.baseToken,
45493
+ config.vesuMultiplyAdapter.config.collateral,
45494
+ config.vesuMultiplyAdapter.config.debt,
45495
+ config.vesuMultiplyAdapter.config.marginToken,
44131
45496
  config.vesuModifyPositionAdapter.config.collateral,
44132
45497
  config.vesuModifyPositionAdapter.config.debt,
44133
45498
  ...avnuTokens
44134
45499
  ]);
44135
45500
  this._poolNames = StarknetCallParser.buildPoolNameLookup([
44136
45501
  {
44137
- poolId: config.vesuAdapter.config.poolId.toBigInt(),
44138
- name: `${config.vesuAdapter.config.collateral.symbol}/${config.vesuAdapter.config.debt.symbol}`
45502
+ poolId: config.vesuMultiplyAdapter.config.poolId.toBigInt(),
45503
+ name: `${config.vesuMultiplyAdapter.config.collateral.symbol}/${config.vesuMultiplyAdapter.config.debt.symbol}`
44139
45504
  },
44140
45505
  {
44141
45506
  poolId: config.vesuModifyPositionAdapter.config.poolId.toBigInt(),
@@ -44538,12 +45903,14 @@ var _ExecutionService = class _ExecutionService {
44538
45903
  *
44539
45904
  * For deposit (USDC→BTC): price = sum(USDC sold) / sum(BTC bought)
44540
45905
  * For withdraw (BTC→USDC): price = sum(USDC bought) / sum(BTC sold)
44541
- */
45906
+ * @returns no-swap means, logic is fine and explicit no swap is needed
45907
+ */
44542
45908
  _getNetExecutionPrice(isDeposit) {
44543
45909
  const prices = [
44544
45910
  this._config.avnuAdapter.lastSwapPriceInfo,
44545
- this._config.vesuAdapter.lastSwapPriceInfo
45911
+ this._config.vesuMultiplyAdapter.lastSwapPriceInfo
44546
45912
  ].filter((p) => p !== null);
45913
+ assert(prices.length <= 1, "Only one swap price info is allowed");
44547
45914
  if (prices.length === 0) return null;
44548
45915
  if (isDeposit) {
44549
45916
  const totalUsdc = prices.reduce((s, p) => s + p.fromAmount, 0);
@@ -44558,7 +45925,7 @@ var _ExecutionService = class _ExecutionService {
44558
45925
  /** Clears cached swap price info on all adapters to prevent stale data across cycles. */
44559
45926
  _clearAdapterPriceInfo() {
44560
45927
  this._config.avnuAdapter.lastSwapPriceInfo = null;
44561
- this._config.vesuAdapter.lastSwapPriceInfo = null;
45928
+ this._config.vesuMultiplyAdapter.lastSwapPriceInfo = null;
44562
45929
  }
44563
45930
  // ═══════════════════════════════════════════════════════════════════════════
44564
45931
  // Public API
@@ -44986,7 +46353,7 @@ var _ExecutionService = class _ExecutionService {
44986
46353
  }
44987
46354
  // ── Transfer routes ─────────────────────────────────────────────────────
44988
46355
  /**
44989
- * WALLET_TO_EXTENDED: Deposit USDC.e from operator wallet directly to Extended.
46356
+ * WALLET_TO_EXTENDED: Deposit USDC from operator wallet directly to Extended.
44990
46357
  *
44991
46358
  * Builds raw approve + deposit calls (NOT through the manager/merkle system)
44992
46359
  * because the wallet interacts with Extended directly, not via vault allocator.
@@ -45001,13 +46368,13 @@ var _ExecutionService = class _ExecutionService {
45001
46368
  );
45002
46369
  return [];
45003
46370
  }
45004
- const { usdceToken, extendedAdapter } = this._config;
46371
+ const { usdcToken, extendedAdapter } = this._config;
45005
46372
  const extendedContract = extendedAdapter.config.extendedContract;
45006
46373
  const vaultId = extendedAdapter.config.vaultIdExtended;
45007
- const salt = Math.floor(Math.random() * 10 ** usdceToken.decimals);
45008
- const uint256Amount = import_starknet33.uint256.bnToUint256(amount.toWei());
46374
+ const salt = Math.floor(Math.random() * 10 ** usdcToken.decimals);
46375
+ const uint256Amount = import_starknet34.uint256.bnToUint256(amount.toWei());
45009
46376
  const approveCall = {
45010
- contractAddress: usdceToken.address.address,
46377
+ contractAddress: usdcToken.address.address,
45011
46378
  entrypoint: "approve",
45012
46379
  calldata: [
45013
46380
  extendedContract.address,
@@ -45030,7 +46397,7 @@ var _ExecutionService = class _ExecutionService {
45030
46397
  return [approveCall, depositCall];
45031
46398
  }
45032
46399
  /**
45033
- * VA_TO_EXTENDED: Deposit USDC.e from vault allocator to Extended.
46400
+ * VA_TO_EXTENDED: Deposit USDC from vault allocator to Extended.
45034
46401
  *
45035
46402
  * Uses the extended adapter's getDepositCall to build ManageCalls,
45036
46403
  * then wraps them in a merkle-verified manage call through the manager contract.
@@ -45045,43 +46412,31 @@ var _ExecutionService = class _ExecutionService {
45045
46412
  );
45046
46413
  return [];
45047
46414
  }
45048
- const swapCall = await this._buildAdapterManageCall(
45049
- this._config.usdcToUsdceAdapter,
45050
- true,
45051
- { amount }
45052
- );
45053
46415
  const manageCall = await this._buildAdapterManageCall(
45054
46416
  this._config.extendedAdapter,
45055
46417
  true,
45056
46418
  { amount }
45057
46419
  );
45058
- return [swapCall, manageCall];
46420
+ return [manageCall];
45059
46421
  }
45060
46422
  /**
45061
- * WALLET_TO_VA: Transfer USDC.e from operator wallet to vault allocator.
46423
+ * WALLET_TO_VA: Transfer USDC from operator wallet to vault allocator.
45062
46424
  * Caps amount by actual wallet balance.
45063
46425
  */
45064
46426
  async _buildWalletToVACalls(route) {
45065
- const erc20 = new ERC20(this._config.networkConfig);
45066
- const { usdceToken, vaultAllocator, walletAddress } = this._config;
45067
46427
  const transferAmount = route.amount;
45068
46428
  if (transferAmount.lessThanOrEqualTo(0)) {
45069
46429
  logger.warn(
45070
- `${this._tag}::_buildWalletToVACalls no USDC.e in wallet to transfer`
46430
+ `${this._tag}::_buildWalletToVACalls no USDC in wallet to transfer`
45071
46431
  );
45072
46432
  return [];
45073
46433
  }
45074
46434
  const transferCall = await this._buildAdapterManageCall(
45075
- this._config.usdceTransferAdapter,
45076
- false,
45077
- { amount: transferAmount }
45078
- );
45079
- const swapCall = await this._buildAdapterManageCall(
45080
- this._config.usdcToUsdceAdapter,
46435
+ this._config.usdcTransferAdapter,
45081
46436
  false,
45082
46437
  { amount: transferAmount }
45083
46438
  );
45084
- return [transferCall, swapCall];
46439
+ return [transferCall];
45085
46440
  }
45086
46441
  // ── AVNU swap routes ────────────────────────────────────────────────────
45087
46442
  /**
@@ -45114,12 +46469,12 @@ var _ExecutionService = class _ExecutionService {
45114
46469
  */
45115
46470
  async _buildVesuIncreaseLeverCalls(route) {
45116
46471
  const depositManageCall = await this._buildAdapterManageCall(
45117
- this._config.vesuAdapter,
46472
+ this._config.vesuMultiplyAdapter,
45118
46473
  true,
45119
46474
  {
45120
46475
  amount: route.marginAmount,
45121
46476
  marginSwap: {
45122
- marginToken: this._config.vesuAdapter.config.marginToken
46477
+ marginToken: this._config.vesuMultiplyAdapter.config.marginToken
45123
46478
  // todo, must be vault token
45124
46479
  },
45125
46480
  leverSwap: {
@@ -45139,11 +46494,11 @@ var _ExecutionService = class _ExecutionService {
45139
46494
  async _buildVesuDecreaseLeverCalls(route) {
45140
46495
  const collateralAmount = route.marginAmount.abs();
45141
46496
  const withdrawManageCall = await this._buildAdapterManageCall(
45142
- this._config.vesuAdapter,
46497
+ this._config.vesuMultiplyAdapter,
45143
46498
  false,
45144
46499
  {
45145
46500
  amount: collateralAmount,
45146
- withdrawSwap: { outputToken: this._config.vesuAdapter.config.marginToken }
46501
+ withdrawSwap: { outputToken: this._config.vesuMultiplyAdapter.config.marginToken }
45147
46502
  }
45148
46503
  );
45149
46504
  return [withdrawManageCall];
@@ -45734,140 +47089,6 @@ _ExecutionService.EXTENDED_EXPOSURE_ROUTES = /* @__PURE__ */ new Set([
45734
47089
  ]);
45735
47090
  var ExecutionService = _ExecutionService;
45736
47091
 
45737
- // src/strategies/universal-adapters/usdc<>usdce-adapter.ts
45738
- var import_starknet34 = require("starknet");
45739
- var UsdcToUsdceAdapter = class _UsdcToUsdceAdapter extends BaseAdapter {
45740
- _approveProofReadableId(usdcToUsdce) {
45741
- const method = usdcToUsdce ? "swap_to_legacy" : "swap_to_new";
45742
- return `approve_${method}`;
45743
- }
45744
- _swapProofReadableId(usdcToUsdce) {
45745
- const method = usdcToUsdce ? "swap_to_legacy" : "swap_to_new";
45746
- const target = usdcToUsdce ? this.config.supportedPositions[0].asset : this.config.supportedPositions[1].asset;
45747
- return `${method}_${target.symbol}`;
45748
- }
45749
- buildSwapLeafConfigs(usdcToUsdce) {
45750
- const method = usdcToUsdce ? "swap_to_legacy" : "swap_to_new";
45751
- const target = usdcToUsdce ? this.config.supportedPositions[0].asset : this.config.supportedPositions[1].asset;
45752
- return [
45753
- {
45754
- target: target.address,
45755
- method: "approve",
45756
- packedArguments: [AVNU_EXCHANGE_FOR_LEGACY_USDC.toBigInt()],
45757
- id: this._approveProofReadableId(usdcToUsdce),
45758
- sanitizer: AVNU_LEGACY_SANITIZER
45759
- },
45760
- {
45761
- target: AVNU_EXCHANGE_FOR_LEGACY_USDC,
45762
- method,
45763
- packedArguments: [],
45764
- id: this._swapProofReadableId(usdcToUsdce),
45765
- sanitizer: AVNU_LEGACY_SANITIZER
45766
- }
45767
- ];
45768
- }
45769
- async buildSwapCalls(params, usdcToUsdce) {
45770
- const approveAmount = import_starknet34.uint256.bnToUint256(params.amount.toWei());
45771
- const target = usdcToUsdce ? this.config.supportedPositions[0].asset : this.config.supportedPositions[1].asset;
45772
- const method = usdcToUsdce ? "swap_to_legacy" : "swap_to_new";
45773
- return [
45774
- {
45775
- proofReadableId: this._approveProofReadableId(usdcToUsdce),
45776
- sanitizer: AVNU_LEGACY_SANITIZER,
45777
- call: {
45778
- contractAddress: target.address,
45779
- selector: import_starknet34.hash.getSelectorFromName("approve"),
45780
- calldata: [
45781
- AVNU_EXCHANGE_FOR_LEGACY_USDC.toBigInt(),
45782
- toBigInt(approveAmount.low.toString()),
45783
- toBigInt(approveAmount.high.toString())
45784
- ]
45785
- }
45786
- },
45787
- {
45788
- proofReadableId: this._swapProofReadableId(usdcToUsdce),
45789
- sanitizer: AVNU_LEGACY_SANITIZER,
45790
- call: {
45791
- contractAddress: AVNU_EXCHANGE_FOR_LEGACY_USDC,
45792
- selector: import_starknet34.hash.getSelectorFromName(method),
45793
- calldata: [
45794
- toBigInt(approveAmount.low.toString()),
45795
- // amount low
45796
- toBigInt(approveAmount.high.toString())
45797
- // amount high
45798
- ]
45799
- }
45800
- }
45801
- ];
45802
- }
45803
- constructor(config) {
45804
- super(config, _UsdcToUsdceAdapter.name, Protocols.AVNU);
45805
- this.config = config;
45806
- assert(this.config.supportedPositions.length === 2, "UsdcToUsdceAdapter must have 2 supported positions");
45807
- assert(this.config.supportedPositions[0].asset.symbol === "USDC", "UsdcToUsdceAdapter must have USDC as the first supported position");
45808
- assert(this.config.supportedPositions[1].asset.symbol === "USDC.e", "UsdcToUsdceAdapter must have USDCE as the second supported position");
45809
- }
45810
- //abstract means the method has no implementation in this class; instead, child classes must implement it.
45811
- async getAPY(supportedPosition) {
45812
- return Promise.resolve({ apy: 0, type: "base" /* BASE */ });
45813
- }
45814
- async getPosition(supportedPosition) {
45815
- const toToken = this.config.supportedPositions[1].asset;
45816
- if (supportedPosition.asset.symbol != toToken.symbol) {
45817
- return null;
45818
- }
45819
- try {
45820
- const balance = await new ERC20(this.config.networkConfig).balanceOf(
45821
- toToken.address,
45822
- this.config.vaultAllocator.address,
45823
- toToken.decimals
45824
- );
45825
- return { amount: balance, remarks: `USDC.e unused balance (VA)` };
45826
- } catch (_e) {
45827
- logger.error(`${_UsdcToUsdceAdapter.name}::getPosition: failed for ${toToken.symbol}`);
45828
- throw new Error(`${_UsdcToUsdceAdapter.name}: failed to get balance for ${toToken.symbol}`);
45829
- }
45830
- }
45831
- async maxDeposit(amount) {
45832
- return Promise.resolve({
45833
- tokenInfo: this.config.baseToken,
45834
- amount: new Web3Number(0, 0),
45835
- usdValue: 0,
45836
- apy: { apy: 0, type: "base" /* BASE */ },
45837
- protocol: Protocols.AVNU,
45838
- remarks: ""
45839
- });
45840
- }
45841
- async maxWithdraw() {
45842
- return Promise.resolve({
45843
- tokenInfo: this.config.baseToken,
45844
- amount: new Web3Number(0, 0),
45845
- usdValue: 0,
45846
- apy: { apy: 0, type: "base" /* BASE */ },
45847
- protocol: Protocols.AVNU,
45848
- remarks: ""
45849
- });
45850
- }
45851
- _getDepositLeaf() {
45852
- return this.buildSwapLeafConfigs(true);
45853
- }
45854
- _getWithdrawLeaf() {
45855
- return this.buildSwapLeafConfigs(false);
45856
- }
45857
- async getDepositCall(params) {
45858
- const calls = await this.buildSwapCalls(params, true);
45859
- return calls;
45860
- }
45861
- //Swap wbtc to usdc
45862
- async getWithdrawCall(params) {
45863
- const calls = await this.buildSwapCalls(params, false);
45864
- return calls;
45865
- }
45866
- async getHealthFactor() {
45867
- return Promise.resolve(1);
45868
- }
45869
- };
45870
-
45871
47092
  // src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx
45872
47093
  var import_jsx_runtime6 = require("react/jsx-runtime");
45873
47094
  var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy extends SVKStrategy {
@@ -45883,9 +47104,6 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
45883
47104
  });
45884
47105
  this.wbtcToken = Global.getDefaultTokens().find((token) => token.symbol === "WBTC");
45885
47106
  this.usdcToken = this.metadata.additionalInfo.borrowable_assets[0];
45886
- this.usdceToken = Global.getDefaultTokens().find(
45887
- (token) => token.symbol === "USDC.e"
45888
- );
45889
47107
  this.stateManager = this._initializeStateManager();
45890
47108
  }
45891
47109
  /**
@@ -45909,9 +47127,8 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
45909
47127
  extendedAdapter: extendedAdapterEntry.adapter,
45910
47128
  vaultAllocator: this.metadata.additionalInfo.vaultAllocator,
45911
47129
  walletAddress: this.metadata.additionalInfo.walletAddress,
45912
- assetToken: Global.getDefaultTokens().find((token) => token.symbol === "USDC"),
45913
- // ! TODO change to asset() latest
45914
- usdceToken: this.usdceToken,
47130
+ assetToken: this.asset(),
47131
+ usdcToken: this.usdcToken,
45915
47132
  collateralToken: this.wbtcToken,
45916
47133
  limitBalanceBufferFactor: LIMIT_BALANCE
45917
47134
  };
@@ -45945,7 +47162,7 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
45945
47162
  }
45946
47163
  return { collateralPrice, debtPrice };
45947
47164
  }
45948
- async getVesuAdapter() {
47165
+ async getVesuMultiplyAdapter() {
45949
47166
  const vesuAdapter = this.metadata.additionalInfo.adapters.find(
45950
47167
  (adapter) => adapter.adapter.name === VesuMultiplyAdapter.name
45951
47168
  );
@@ -45967,16 +47184,16 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
45967
47184
  }
45968
47185
  return vesuModifyPositionAdapter.adapter;
45969
47186
  }
45970
- async getUsdceTransferAdapter() {
45971
- const usdceTransferAdapter = this.metadata.additionalInfo.adapters.find(
47187
+ async getUsdcTransferAdapter() {
47188
+ const usdcTransferAdapter = this.metadata.additionalInfo.adapters.find(
45972
47189
  (adapter) => adapter.adapter.name === TokenTransferAdapter.name
45973
47190
  );
45974
- if (!usdceTransferAdapter) {
47191
+ if (!usdcTransferAdapter) {
45975
47192
  throw new Error(
45976
- `${this.getTag()} Usdce transfer adapter not configured in metadata.`
47193
+ `${this.getTag()} Usdc transfer adapter not configured in metadata.`
45977
47194
  );
45978
47195
  }
45979
- return usdceTransferAdapter.adapter;
47196
+ return usdcTransferAdapter.adapter;
45980
47197
  }
45981
47198
  async getAvnuAdapter() {
45982
47199
  const avnuAdapter = this.metadata.additionalInfo.adapters.find(
@@ -46005,17 +47222,6 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
46005
47222
  }
46006
47223
  return extendedAdapter.adapter;
46007
47224
  }
46008
- async getUsdcToUsdceAdapter() {
46009
- const usdcToUsdceAdapter = this.metadata.additionalInfo.adapters.find(
46010
- (adapter) => adapter.adapter.name === UsdcToUsdceAdapter.name
46011
- );
46012
- if (!usdcToUsdceAdapter) {
46013
- throw new Error(
46014
- `${this.getTag()} UsdcToUsdce adapter not configured in metadata.`
46015
- );
46016
- }
46017
- return usdcToUsdceAdapter.adapter;
46018
- }
46019
47225
  /**
46020
47226
  * Creates an ExecutionService wired to this strategy's adapters and config.
46021
47227
  * Use with `stateManager.solve()` to get a SolveResult, then pass it to
@@ -46027,34 +47233,30 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
46027
47233
  */
46028
47234
  async createExecutionService(opts) {
46029
47235
  const [
46030
- vesuAdapter,
47236
+ vesuMultiplyAdapter,
46031
47237
  vesuModifyPositionAdapter,
46032
- usdceTransferAdapter,
46033
47238
  extendedAdapter,
46034
47239
  avnuAdapter,
46035
- usdcToUsdceAdapter
47240
+ usdcTransferAdapter
46036
47241
  ] = await Promise.all([
46037
- this.getVesuAdapter(),
47242
+ this.getVesuMultiplyAdapter(),
46038
47243
  this.getVesuModifyPositionAdapter(),
46039
- this.getUsdceTransferAdapter(),
46040
47244
  this.getExtendedAdapter(),
46041
47245
  this.getAvnuAdapter(),
46042
- this.getUsdcToUsdceAdapter()
47246
+ this.getUsdcTransferAdapter()
46043
47247
  ]);
46044
47248
  const executionConfig = {
46045
47249
  networkConfig: this.config,
46046
47250
  pricer: this.pricer,
46047
- vesuAdapter,
47251
+ vesuMultiplyAdapter,
46048
47252
  vesuModifyPositionAdapter,
46049
47253
  extendedAdapter,
46050
47254
  avnuAdapter,
46051
- usdcToUsdceAdapter,
47255
+ usdcTransferAdapter,
46052
47256
  vaultAllocator: this.metadata.additionalInfo.vaultAllocator,
46053
47257
  walletAddress: this.metadata.additionalInfo.walletAddress,
46054
- usdceTransferAdapter,
46055
47258
  wbtcToken: this.wbtcToken,
46056
47259
  usdcToken: this.usdcToken,
46057
- usdceToken: this.usdceToken,
46058
47260
  getMerkleTree: () => this.getMerkleTree(),
46059
47261
  getManageCall: (proofs, manageCalls) => this.getManageCall(proofs, manageCalls),
46060
47262
  getBringLiquidityCall: (params) => this.getBringLiquidityCall(params),
@@ -46075,9 +47277,7 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
46075
47277
  for (let adapter of this.metadata.additionalInfo.adapters) {
46076
47278
  let positions = await adapter.adapter.getPositions();
46077
47279
  if (positions && positions.length > 0) {
46078
- const filteredPositions = positions.filter((position) => {
46079
- return position.tokenInfo.address !== this.usdceToken.address;
46080
- });
47280
+ const filteredPositions = positions;
46081
47281
  allPositions.push(...filteredPositions);
46082
47282
  }
46083
47283
  }
@@ -46272,34 +47472,22 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
46272
47472
  };
46273
47473
  }
46274
47474
  /**
46275
- * Fetches the operator wallet's current holdings for USDC.e, USDC, and WBTC,
47475
+ * Fetches the operator wallet's current holdings for USDC and WBTC,
46276
47476
  * returning each token's balance and USD value.
46277
47477
  */
46278
47478
  async getWalletHoldings() {
46279
- if (!this.usdceToken || !this.wbtcToken || !this.usdcToken) {
47479
+ if (!this.wbtcToken || !this.usdcToken) {
46280
47480
  return [];
46281
47481
  }
46282
47482
  const walletAddress = this.metadata.additionalInfo.walletAddress;
46283
- const usdceWalletBalance = await new ERC20(this.config).balanceOf(
46284
- this.usdceToken.address,
46285
- walletAddress,
46286
- this.usdceToken.decimals
46287
- );
46288
47483
  const usdcWalletBalance = await new ERC20(this.config).balanceOf(
46289
47484
  this.usdcToken.address,
46290
47485
  walletAddress,
46291
47486
  this.usdcToken.decimals
46292
47487
  );
46293
- const price = await this.pricer.getPrice(this.usdceToken.symbol);
46294
- const wbtcPrice = await this.pricer.getPrice(this.wbtcToken.symbol);
46295
- const usdceUsdValue = Number(usdceWalletBalance.toFixed(this.usdceToken.decimals)) * price.price;
47488
+ const price = await this.pricer.getPrice(this.usdcToken.symbol);
46296
47489
  const usdcUsdValue = Number(usdcWalletBalance.toFixed(this.usdcToken.decimals)) * price.price;
46297
47490
  return [
46298
- {
46299
- tokenInfo: this.usdceToken,
46300
- amount: usdceWalletBalance,
46301
- usdValue: usdceUsdValue
46302
- },
46303
47491
  {
46304
47492
  tokenInfo: this.usdcToken,
46305
47493
  amount: usdcWalletBalance,
@@ -46316,9 +47504,6 @@ function getLooperSettings3(lstSymbol, underlyingSymbol, vaultSettings, pool1, e
46316
47504
  const usdcToken = Global.getDefaultTokens().find(
46317
47505
  (token) => token.symbol === underlyingSymbol
46318
47506
  );
46319
- const usdceToken = Global.getDefaultTokens().find(
46320
- (token) => token.symbol === "USDC.e"
46321
- );
46322
47507
  const baseAdapterConfig = {
46323
47508
  baseToken: wbtcToken,
46324
47509
  supportedPositions: [
@@ -46339,17 +47524,10 @@ function getLooperSettings3(lstSymbol, underlyingSymbol, vaultSettings, pool1, e
46339
47524
  minimumExtendedPriceDifferenceForSwapOpen,
46340
47525
  maximumExtendedPriceDifferenceForSwapClosing
46341
47526
  });
46342
- const usdcToUsdceAdapter = new UsdcToUsdceAdapter({
46343
- ...baseAdapterConfig,
46344
- supportedPositions: [
46345
- { asset: usdcToken, isDebt: true },
46346
- { asset: usdceToken, isDebt: false }
46347
- ]
46348
- });
46349
47527
  const extendedAdapter = new ExtendedAdapter({
46350
47528
  ...baseAdapterConfig,
46351
47529
  supportedPositions: [
46352
- { asset: usdceToken, isDebt: false }
47530
+ { asset: usdcToken, isDebt: false }
46353
47531
  ],
46354
47532
  vaultIdExtended,
46355
47533
  extendedContract: EXTENDED_CONTRACT,
@@ -46395,10 +47573,10 @@ function getLooperSettings3(lstSymbol, underlyingSymbol, vaultSettings, pool1, e
46395
47573
  { asset: usdcToken, isDebt: true }
46396
47574
  ]
46397
47575
  });
46398
- const usdceTransferAdapter = new TokenTransferAdapter({
47576
+ const usdcTransferAdapter = new TokenTransferAdapter({
46399
47577
  ...baseAdapterConfig,
46400
- baseToken: usdceToken,
46401
- supportedPositions: [{ asset: usdceToken, isDebt: false }],
47578
+ baseToken: usdcToken,
47579
+ supportedPositions: [{ asset: usdcToken, isDebt: false }],
46402
47580
  fromAddress: vaultSettings.vaultAllocator,
46403
47581
  toAddress: ContractAddr.from(vaultSettings.walletAddress)
46404
47582
  });
@@ -46411,12 +47589,8 @@ function getLooperSettings3(lstSymbol, underlyingSymbol, vaultSettings, pool1, e
46411
47589
  adapter: vesuModifyPositionAdapter
46412
47590
  });
46413
47591
  vaultSettings.adapters.push({
46414
- id: `${usdceTransferAdapter.name}_${usdceToken.symbol}`,
46415
- adapter: usdceTransferAdapter
46416
- });
46417
- vaultSettings.adapters.push({
46418
- id: `${usdcToUsdceAdapter.name}_${usdceToken.symbol}_${usdcToken.symbol}`,
46419
- adapter: usdcToUsdceAdapter
47592
+ id: `${usdcTransferAdapter.name}_${usdcToken.symbol}`,
47593
+ adapter: usdcTransferAdapter
46420
47594
  });
46421
47595
  vaultSettings.adapters.push({
46422
47596
  id: `${extendedAdapter.name}_${wbtcToken.symbol}`,
@@ -46438,12 +47612,10 @@ function getLooperSettings3(lstSymbol, underlyingSymbol, vaultSettings, pool1, e
46438
47612
  vaultSettings.leafAdapters.push(() => vesuModifyPositionAdapter.getDepositLeaf());
46439
47613
  vaultSettings.leafAdapters.push(() => vesuModifyPositionAdapter.getWithdrawLeaf());
46440
47614
  vaultSettings.leafAdapters.push(() => extendedAdapter.getDepositLeaf());
46441
- vaultSettings.leafAdapters.push(() => usdcToUsdceAdapter.getDepositLeaf());
46442
- vaultSettings.leafAdapters.push(() => usdcToUsdceAdapter.getWithdrawLeaf());
46443
47615
  vaultSettings.leafAdapters.push(() => avnuAdapter.getDepositLeaf());
46444
47616
  vaultSettings.leafAdapters.push(() => avnuAdapter.getWithdrawLeaf());
46445
- vaultSettings.leafAdapters.push(() => usdceTransferAdapter.getDepositLeaf());
46446
- vaultSettings.leafAdapters.push(() => usdceTransferAdapter.getWithdrawLeaf());
47617
+ vaultSettings.leafAdapters.push(() => usdcTransferAdapter.getDepositLeaf());
47618
+ vaultSettings.leafAdapters.push(() => usdcTransferAdapter.getWithdrawLeaf());
46447
47619
  vaultSettings.leafAdapters.push(
46448
47620
  commonAdapter.getApproveAdapter(
46449
47621
  usdcToken.address,
@@ -47476,11 +48648,13 @@ var deployer_default = Deployer;
47476
48648
  BaseAdapter,
47477
48649
  BaseStrategy,
47478
48650
  CASE_ROUTE_TYPES,
48651
+ COLLATERAL_PRECISION,
47479
48652
  CaseCategory,
47480
48653
  CaseId,
47481
48654
  CommonAdapter,
47482
48655
  ContractAddr,
47483
48656
  CycleType,
48657
+ DEFAULT_TROVES_STRATEGIES_API,
47484
48658
  Deployer,
47485
48659
  ERC20,
47486
48660
  EXTENDED_CONTRACT,
@@ -47499,6 +48673,7 @@ var deployer_default = Deployer;
47499
48673
  FatalError,
47500
48674
  FlowChartColors,
47501
48675
  Global,
48676
+ HealthFactorMath,
47502
48677
  HyperLSTStrategies,
47503
48678
  ILending,
47504
48679
  Initializable,
@@ -47539,6 +48714,7 @@ var deployer_default = Deployer;
47539
48714
  StrategyLiveStatus,
47540
48715
  StrategyTag,
47541
48716
  StrategyType,
48717
+ SvkTrovesAdapter,
47542
48718
  TRANSFER_SANITIZER,
47543
48719
  TelegramGroupNotif,
47544
48720
  TelegramNotif,
@@ -47588,6 +48764,7 @@ var deployer_default = Deployer;
47588
48764
  createEkuboCLStrategy,
47589
48765
  createHyperLSTStrategy,
47590
48766
  createSenseiStrategy,
48767
+ createSolveBudgetFromRawState,
47591
48768
  createStrategy,
47592
48769
  createUniversalStrategy,
47593
48770
  createVesuRebalanceStrategy,