@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.
@@ -28676,11 +28676,13 @@ ${r2}}` : "}", l2;
28676
28676
  BaseAdapter: () => BaseAdapter,
28677
28677
  BaseStrategy: () => BaseStrategy,
28678
28678
  CASE_ROUTE_TYPES: () => CASE_ROUTE_TYPES,
28679
+ COLLATERAL_PRECISION: () => COLLATERAL_PRECISION,
28679
28680
  CaseCategory: () => CaseCategory,
28680
28681
  CaseId: () => CaseId,
28681
28682
  CommonAdapter: () => CommonAdapter,
28682
28683
  ContractAddr: () => ContractAddr,
28683
28684
  CycleType: () => CycleType,
28685
+ DEFAULT_TROVES_STRATEGIES_API: () => DEFAULT_TROVES_STRATEGIES_API,
28684
28686
  ERC20: () => ERC20,
28685
28687
  EXTENDED_CONTRACT: () => EXTENDED_CONTRACT,
28686
28688
  EXTENDED_SANITIZER: () => EXTENDED_SANITIZER,
@@ -28698,6 +28700,7 @@ ${r2}}` : "}", l2;
28698
28700
  FatalError: () => FatalError,
28699
28701
  FlowChartColors: () => FlowChartColors,
28700
28702
  Global: () => Global,
28703
+ HealthFactorMath: () => HealthFactorMath,
28701
28704
  HyperLSTStrategies: () => HyperLSTStrategies,
28702
28705
  ILending: () => ILending,
28703
28706
  Initializable: () => Initializable,
@@ -28735,6 +28738,7 @@ ${r2}}` : "}", l2;
28735
28738
  StrategyLiveStatus: () => StrategyLiveStatus,
28736
28739
  StrategyTag: () => StrategyTag,
28737
28740
  StrategyType: () => StrategyType,
28741
+ SvkTrovesAdapter: () => SvkTrovesAdapter,
28738
28742
  TRANSFER_SANITIZER: () => TRANSFER_SANITIZER,
28739
28743
  TimeInForce: () => TimeInForce,
28740
28744
  TokenMarketData: () => TokenMarketData,
@@ -28782,6 +28786,7 @@ ${r2}}` : "}", l2;
28782
28786
  createEkuboCLStrategy: () => createEkuboCLStrategy,
28783
28787
  createHyperLSTStrategy: () => createHyperLSTStrategy,
28784
28788
  createSenseiStrategy: () => createSenseiStrategy,
28789
+ createSolveBudgetFromRawState: () => createSolveBudgetFromRawState,
28785
28790
  createStrategy: () => createStrategy,
28786
28791
  createUniversalStrategy: () => createUniversalStrategy2,
28787
28792
  createVesuRebalanceStrategy: () => createVesuRebalanceStrategy2,
@@ -46609,6 +46614,9 @@ ${JSON.stringify(data, null, 2)}`;
46609
46614
  const bn = new _Web3Number2(weiNumber, decimals).dividedBy(10 ** decimals);
46610
46615
  return new _Web3Number2(bn.toString(), decimals);
46611
46616
  }
46617
+ static fromNumber(number2, decimals) {
46618
+ return new _Web3Number2(number2.toString(), decimals);
46619
+ }
46612
46620
  };
46613
46621
 
46614
46622
  // src/dataTypes/address.ts
@@ -76385,6 +76393,48 @@ ${JSON.stringify(data, null, 2)}`;
76385
76393
  );
76386
76394
  var StarknetCallParser = _StarknetCallParser;
76387
76395
 
76396
+ // src/utils/health-factor-math.ts
76397
+ var HealthFactorMath = class {
76398
+ static getCollateralRequired(debtAmount, debtPrice, targetHF, maxLTV, collateralPrice, collateralTokenInfo) {
76399
+ const numerator = debtAmount.multipliedBy(debtPrice).multipliedBy(targetHF);
76400
+ const denominator = collateralPrice * maxLTV;
76401
+ const collateralAmount = numerator.dividedBy(denominator);
76402
+ const netCollateral = new Web3Number(collateralAmount.toString(), collateralTokenInfo.decimals);
76403
+ return netCollateral;
76404
+ }
76405
+ static getMinCollateralRequiredOnLooping(debtAmount, debtPrice, targetHF, maxLTV, collateralPrice, collateralTokenInfo) {
76406
+ const netCollateral = this.getCollateralRequired(debtAmount, debtPrice, targetHF, maxLTV, collateralPrice, collateralTokenInfo);
76407
+ const collateralFromDebt = new Web3Number(debtAmount.multipliedBy(debtPrice).dividedBy(collateralPrice).toString(), collateralTokenInfo.decimals);
76408
+ return netCollateral.minus(collateralFromDebt);
76409
+ }
76410
+ static getHealthFactor(collateralAmount, collateralPrice, maxLTV, debtAmount, debtPrice) {
76411
+ const numerator = collateralAmount.multipliedBy(collateralPrice).multipliedBy(maxLTV);
76412
+ const denominator = debtAmount.multipliedBy(debtPrice);
76413
+ const healthFactor = numerator.dividedBy(denominator);
76414
+ return healthFactor.toNumber();
76415
+ }
76416
+ static getMaxDebtAmountOnLooping(collateralAmount, collateralPrice, maxLTV, targetHF, debtPrice, debtTokenInfo) {
76417
+ const numerator = collateralAmount.multipliedBy(collateralPrice).multipliedBy(maxLTV);
76418
+ logger2.verbose(`HealthFactorMath: Max debt amount on looping numerator: ${numerator.toNumber()}, collateralAmount: ${collateralAmount.toNumber()}, collateralPrice: ${collateralPrice}, maxLTV: ${maxLTV}, targetHF: ${targetHF}, debtPrice: ${debtPrice}`);
76419
+ const denominator = targetHF - maxLTV;
76420
+ logger2.verbose(`HealthFactorMath: Max debt amount on looping denominator: ${denominator}`);
76421
+ const debtAmountUSD = numerator.dividedBy(denominator);
76422
+ logger2.verbose(`HealthFactorMath: Max debt amount on looping debtAmountUSD: ${debtAmountUSD.toNumber()}`);
76423
+ const debtAmount = debtAmountUSD.dividedBy(debtPrice);
76424
+ logger2.verbose(`HealthFactorMath: Max debt amount on looping debtAmount: ${debtAmount.toNumber()}`);
76425
+ return new Web3Number(debtAmount.toString(), debtTokenInfo.decimals);
76426
+ }
76427
+ static getMaxDebtAmount(collateralAmount, collateralPrice, maxLTV, targetHF, debtPrice, debtTokenInfo) {
76428
+ const numerator = collateralAmount.multipliedBy(collateralPrice).multipliedBy(maxLTV);
76429
+ logger2.verbose(`HealthFactorMath: Max debt amount numerator: ${numerator.toNumber()}, collateralAmount: ${collateralAmount.toNumber()}, collateralPrice: ${collateralPrice}, maxLTV: ${maxLTV}, targetHF: ${targetHF}, debtPrice: ${debtPrice}`);
76430
+ const denominator = targetHF * debtPrice;
76431
+ logger2.verbose(`HealthFactorMath: Max debt amount denominator: ${denominator}, targetHF: ${targetHF}, debtPrice: ${debtPrice}`);
76432
+ const debtAmount = numerator.dividedBy(denominator);
76433
+ logger2.verbose(`HealthFactorMath: Max debt amount: ${debtAmount.toNumber()}, numerator: ${numerator.toNumber()}, denominator: ${denominator}`);
76434
+ return new Web3Number(debtAmount.toString(), debtTokenInfo.decimals);
76435
+ }
76436
+ };
76437
+
76388
76438
  // src/utils/index.ts
76389
76439
  function assert3(condition, message) {
76390
76440
  if (!condition) {
@@ -93771,6 +93821,10 @@ spurious results.`);
93771
93821
  name: "Vault",
93772
93822
  logo: ""
93773
93823
  };
93824
+ var TrovesProtocol = {
93825
+ name: "Troves",
93826
+ logo: "https://app.troves.fi/favicon.ico"
93827
+ };
93774
93828
  var Protocols = {
93775
93829
  NONE: NoneProtocol,
93776
93830
  VESU: VesuProtocol,
@@ -93778,7 +93832,8 @@ spurious results.`);
93778
93832
  EXTENDED: ExtendedProtocol,
93779
93833
  EKUBO: EkuboProtocol,
93780
93834
  AVNU: AvnuProtocol,
93781
- VAULT: VaultProtocol
93835
+ VAULT: VaultProtocol,
93836
+ TROVES: TrovesProtocol
93782
93837
  };
93783
93838
 
93784
93839
  // src/interfaces/initializable.ts
@@ -121484,48 +121539,6 @@ spurious results.`);
121484
121539
  }
121485
121540
  };
121486
121541
 
121487
- // src/utils/health-factor-math.ts
121488
- var HealthFactorMath = class {
121489
- static getCollateralRequired(debtAmount, debtPrice, targetHF, maxLTV, collateralPrice, collateralTokenInfo) {
121490
- const numerator = debtAmount.multipliedBy(debtPrice).multipliedBy(targetHF);
121491
- const denominator = collateralPrice * maxLTV;
121492
- const collateralAmount = numerator.dividedBy(denominator);
121493
- const netCollateral = new Web3Number(collateralAmount.toString(), collateralTokenInfo.decimals);
121494
- return netCollateral;
121495
- }
121496
- static getMinCollateralRequiredOnLooping(debtAmount, debtPrice, targetHF, maxLTV, collateralPrice, collateralTokenInfo) {
121497
- const netCollateral = this.getCollateralRequired(debtAmount, debtPrice, targetHF, maxLTV, collateralPrice, collateralTokenInfo);
121498
- const collateralFromDebt = new Web3Number(debtAmount.multipliedBy(debtPrice).dividedBy(collateralPrice).toString(), collateralTokenInfo.decimals);
121499
- return netCollateral.minus(collateralFromDebt);
121500
- }
121501
- static getHealthFactor(collateralAmount, collateralPrice, maxLTV, debtAmount, debtPrice) {
121502
- const numerator = collateralAmount.multipliedBy(collateralPrice).multipliedBy(maxLTV);
121503
- const denominator = debtAmount.multipliedBy(debtPrice);
121504
- const healthFactor = numerator.dividedBy(denominator);
121505
- return healthFactor.toNumber();
121506
- }
121507
- static getMaxDebtAmountOnLooping(collateralAmount, collateralPrice, maxLTV, targetHF, debtPrice, debtTokenInfo) {
121508
- const numerator = collateralAmount.multipliedBy(collateralPrice).multipliedBy(maxLTV);
121509
- logger2.verbose(`HealthFactorMath: Max debt amount on looping numerator: ${numerator.toNumber()}, collateralAmount: ${collateralAmount.toNumber()}, collateralPrice: ${collateralPrice}, maxLTV: ${maxLTV}, targetHF: ${targetHF}, debtPrice: ${debtPrice}`);
121510
- const denominator = targetHF - maxLTV;
121511
- logger2.verbose(`HealthFactorMath: Max debt amount on looping denominator: ${denominator}`);
121512
- const debtAmountUSD = numerator.dividedBy(denominator);
121513
- logger2.verbose(`HealthFactorMath: Max debt amount on looping debtAmountUSD: ${debtAmountUSD.toNumber()}`);
121514
- const debtAmount = debtAmountUSD.dividedBy(debtPrice);
121515
- logger2.verbose(`HealthFactorMath: Max debt amount on looping debtAmount: ${debtAmount.toNumber()}`);
121516
- return new Web3Number(debtAmount.toString(), debtTokenInfo.decimals);
121517
- }
121518
- static getMaxDebtAmount(collateralAmount, collateralPrice, maxLTV, targetHF, debtPrice, debtTokenInfo) {
121519
- const numerator = collateralAmount.multipliedBy(collateralPrice).multipliedBy(maxLTV);
121520
- logger2.verbose(`HealthFactorMath: Max debt amount numerator: ${numerator.toNumber()}, collateralAmount: ${collateralAmount.toNumber()}, collateralPrice: ${collateralPrice}, maxLTV: ${maxLTV}, targetHF: ${targetHF}, debtPrice: ${debtPrice}`);
121521
- const denominator = targetHF * debtPrice;
121522
- logger2.verbose(`HealthFactorMath: Max debt amount denominator: ${denominator}, targetHF: ${targetHF}, debtPrice: ${debtPrice}`);
121523
- const debtAmount = numerator.dividedBy(denominator);
121524
- logger2.verbose(`HealthFactorMath: Max debt amount: ${debtAmount.toNumber()}, numerator: ${numerator.toNumber()}, denominator: ${denominator}`);
121525
- return new Web3Number(debtAmount.toString(), debtTokenInfo.decimals);
121526
- }
121527
- };
121528
-
121529
121542
  // src/strategies/vesu-extended-strategy/utils/helper.ts
121530
121543
  var returnFormattedAmount = (amount, toTokenDecimals) => {
121531
121544
  const formattedAmount = "0x" + BigInt(Math.floor(amount * 10 ** toTokenDecimals)).toString(16);
@@ -122168,6 +122181,7 @@ spurious results.`);
122168
122181
  const collateralToken = this.config.collateral;
122169
122182
  const debtToken = this.config.debt;
122170
122183
  const { contract: multiplyContract } = this._getMultiplyContract();
122184
+ this.lastSwapPriceInfo = null;
122171
122185
  const {
122172
122186
  existingCollateralInfo,
122173
122187
  existingDebtInfo,
@@ -122204,9 +122218,15 @@ spurious results.`);
122204
122218
  let marginSwapLimitAmount = Web3Number.fromWei(0, collateralToken.decimals);
122205
122219
  let addedCollateral = params.amount;
122206
122220
  let approveAmount = params.amount;
122221
+ let aggregatedFromAmount = 0;
122222
+ let aggregatedToAmount = 0;
122223
+ let aggregatedFromSymbol = debtToken.symbol;
122224
+ const aggregatedToSymbol = collateralToken.symbol;
122225
+ let executedSwapCount = 0;
122207
122226
  if (params.marginSwap) {
122208
122227
  const marginToken = params.marginSwap.marginToken;
122209
122228
  const requiredAmount = params.amount;
122229
+ assert3(marginToken.address.eq(debtToken.address), "Margin token must be the same as debt token");
122210
122230
  const marginSwapQuote = await ekuboQuoter.getQuoteExactOutput(
122211
122231
  marginToken.address.address,
122212
122232
  collateralToken.address.address,
@@ -122222,6 +122242,11 @@ spurious results.`);
122222
122242
  marginToken,
122223
122243
  collateralToken
122224
122244
  );
122245
+ const marginSwapInputAmount = Web3Number.fromWei(marginSwapQuote.total_calculated, marginToken.decimals).abs().toNumber();
122246
+ const marginSwapOutputAmount = requiredAmount.abs().toNumber();
122247
+ aggregatedFromAmount += marginSwapInputAmount;
122248
+ aggregatedToAmount += marginSwapOutputAmount;
122249
+ executedSwapCount += 1;
122225
122250
  approveAmount = Web3Number.fromWei(marginSwapQuote.total_calculated, marginToken.decimals).multipliedBy(1 + this.maxSlippage).abs();
122226
122251
  }
122227
122252
  let debtAmount = this._computeTargetDebtDelta(
@@ -122245,10 +122270,6 @@ spurious results.`);
122245
122270
  if (!debtAmount.isZero() && debtAmount.greaterThan(0)) {
122246
122271
  try {
122247
122272
  let swapQuote;
122248
- const debtAmountInCollateralUnits = new Web3Number(
122249
- debtAmount.multipliedBy(debtPrice).dividedBy(collateralPrice).toFixed(6),
122250
- collateralToken.decimals
122251
- );
122252
122273
  if (params.leverSwap?.exactOutput) {
122253
122274
  swapQuote = await ekuboQuoter.getQuoteExactOutput(
122254
122275
  debtToken.address.address,
@@ -122272,17 +122293,9 @@ spurious results.`);
122272
122293
  ).abs().toNumber();
122273
122294
  const inputAmt = debtAmount.abs().toNumber();
122274
122295
  const outputAmt = quoteOutputAmount;
122275
- this.lastSwapPriceInfo = {
122276
- source: "ekubo",
122277
- fromTokenSymbol: debtToken.symbol,
122278
- toTokenSymbol: collateralToken.symbol,
122279
- fromAmount: inputAmt,
122280
- toAmount: outputAmt,
122281
- effectivePrice: outputAmt !== 0 ? inputAmt / outputAmt : 0
122282
- };
122283
- logger2.verbose(
122284
- `${_VesuMultiplyAdapter.name}::_getIncreaseCalldata stored price info: ${inputAmt} ${debtToken.symbol} \u2192 ${outputAmt} ${collateralToken.symbol}, effectivePrice=${this.lastSwapPriceInfo.effectivePrice}`
122285
- );
122296
+ aggregatedFromAmount += inputAmt;
122297
+ aggregatedToAmount += outputAmt;
122298
+ executedSwapCount += 1;
122286
122299
  leverSwap = ekuboQuoter.getVesuMultiplyQuote(
122287
122300
  swapQuote,
122288
122301
  debtToken,
@@ -122300,6 +122313,19 @@ spurious results.`);
122300
122313
  );
122301
122314
  }
122302
122315
  }
122316
+ if (executedSwapCount > 0) {
122317
+ this.lastSwapPriceInfo = {
122318
+ source: "ekubo",
122319
+ fromTokenSymbol: aggregatedFromSymbol ?? debtToken.symbol,
122320
+ toTokenSymbol: aggregatedToSymbol,
122321
+ fromAmount: aggregatedFromAmount,
122322
+ toAmount: aggregatedToAmount,
122323
+ effectivePrice: aggregatedToAmount !== 0 ? aggregatedFromAmount / aggregatedToAmount : 0
122324
+ };
122325
+ logger2.verbose(
122326
+ `${_VesuMultiplyAdapter.name}::_getIncreaseCalldata stored aggregated price info: ${aggregatedFromAmount} ${this.lastSwapPriceInfo.fromTokenSymbol} \u2192 ${aggregatedToAmount} ${aggregatedToSymbol}, effectivePrice=${this.lastSwapPriceInfo.effectivePrice}, swaps=${executedSwapCount}`
122327
+ );
122328
+ }
122303
122329
  logger2.verbose(
122304
122330
  `${_VesuMultiplyAdapter.name}::_getIncreaseCalldata leverSwapLimitAmount: ${leverSwapLimitAmount.toWei()}`
122305
122331
  );
@@ -122332,10 +122358,21 @@ spurious results.`);
122332
122358
  approveAmount
122333
122359
  };
122334
122360
  }
122361
+ // private _setLastSwapPriceAsNoSwap(): void {
122362
+ // this.lastSwapPriceInfo = {
122363
+ // source: "no-swap",
122364
+ // fromTokenSymbol: this.config.collateral.symbol,
122365
+ // toTokenSymbol: this.config.debt.symbol,
122366
+ // fromAmount: 0,
122367
+ // toAmount: 0,
122368
+ // effectivePrice: 0,
122369
+ // };
122370
+ // }
122335
122371
  async _buildDecreaseLikeCalldata(params) {
122336
122372
  const collateralToken = this.config.collateral;
122337
122373
  const debtToken = this.config.debt;
122338
122374
  const { contract: multiplyContract } = this._getMultiplyContract();
122375
+ this.lastSwapPriceInfo = null;
122339
122376
  const ekuboQuoter = new EkuboQuoter(
122340
122377
  this.config.networkConfig,
122341
122378
  this.config.pricer
@@ -122344,6 +122381,9 @@ spurious results.`);
122344
122381
  let leverSwapWeights = [];
122345
122382
  let leverSwapLimitAmount = Web3Number.fromWei(0, collateralToken.decimals);
122346
122383
  let leverCollateralUsed = Web3Number.fromWei(0, collateralToken.decimals);
122384
+ let aggregatedFromAmount = 0;
122385
+ let aggregatedToAmount = 0;
122386
+ let executedSwapCount = 0;
122347
122387
  if (params.closePosition) {
122348
122388
  const debtQuote = await ekuboQuoter.getQuoteExactOutput(
122349
122389
  collateralToken.address.address,
@@ -122362,6 +122402,9 @@ spurious results.`);
122362
122402
  collateralToken.decimals
122363
122403
  ).abs();
122364
122404
  leverSwapLimitAmount = leverCollateralUsed.multipliedBy(1 + this.maxSlippage);
122405
+ aggregatedFromAmount += leverCollateralUsed.toNumber();
122406
+ aggregatedToAmount += params.debtToRepayAbs.abs().toNumber();
122407
+ executedSwapCount += 1;
122365
122408
  } else {
122366
122409
  if (params.collateralPrice === void 0 || params.debtPrice === void 0) {
122367
122410
  throw new Error(
@@ -122383,17 +122426,9 @@ spurious results.`);
122383
122426
  leverSwapQuote.total_calculated,
122384
122427
  debtToken.decimals
122385
122428
  ).abs().toNumber();
122386
- this.lastSwapPriceInfo = {
122387
- source: "ekubo",
122388
- fromTokenSymbol: collateralToken.symbol,
122389
- toTokenSymbol: debtToken.symbol,
122390
- fromAmount: inputAmt,
122391
- toAmount: outputAmt,
122392
- effectivePrice: outputAmt !== 0 ? outputAmt / inputAmt : 0
122393
- };
122394
- logger2.verbose(
122395
- `${_VesuMultiplyAdapter.name}::_buildDecreaseLikeCalldata stored price info: ${inputAmt} ${collateralToken.symbol} \u2192 ${outputAmt} ${debtToken.symbol}, effectivePrice=${this.lastSwapPriceInfo.effectivePrice}`
122396
- );
122429
+ aggregatedFromAmount += inputAmt;
122430
+ aggregatedToAmount += outputAmt;
122431
+ executedSwapCount += 1;
122397
122432
  leverSwap = ekuboQuoter.getVesuMultiplyQuote(
122398
122433
  leverSwapQuote,
122399
122434
  collateralToken,
@@ -122416,6 +122451,7 @@ spurious results.`);
122416
122451
  );
122417
122452
  const withdrawSwapWeights = [];
122418
122453
  if (params.outputToken && !params.outputToken.address.eq(collateralToken.address)) {
122454
+ assert3(params.outputToken.address.eq(debtToken.address), "Withdraw output token must be the same as debt token");
122419
122455
  const residualCollateral = params.closePosition ? params.existingCollateral.minus(leverCollateralUsed) : params.subMargin;
122420
122456
  const outputTokenPrice = await this.config.pricer.getPrice(params.outputToken.symbol);
122421
122457
  if (residualCollateral.greaterThan(0)) {
@@ -122432,12 +122468,34 @@ spurious results.`);
122432
122468
  );
122433
122469
  withdrawSwap = built.swaps;
122434
122470
  withdrawSwapWeights.push(...built.weights);
122471
+ const withdrawOutputAmount = Web3Number.fromWei(
122472
+ withdrawQuote.total_calculated,
122473
+ params.outputToken.decimals
122474
+ ).abs().toNumber();
122475
+ aggregatedFromAmount += residualCollateral.toNumber();
122476
+ aggregatedToAmount += withdrawOutputAmount;
122477
+ executedSwapCount += 1;
122435
122478
  const estimatedOutput = residualCollateral.multipliedBy(params.collateralPrice).dividedBy(outputTokenPrice.price);
122436
122479
  estimatedOutput.decimals = params.outputToken.decimals;
122437
122480
  withdrawSwapLimitAmount = estimatedOutput.multipliedBy(1 - this.maxSlippage);
122438
122481
  }
122439
122482
  }
122440
122483
  }
122484
+ if (executedSwapCount > 0) {
122485
+ this.lastSwapPriceInfo = {
122486
+ source: "ekubo",
122487
+ fromTokenSymbol: collateralToken.symbol,
122488
+ toTokenSymbol: debtToken.symbol,
122489
+ fromAmount: aggregatedFromAmount,
122490
+ toAmount: aggregatedToAmount,
122491
+ effectivePrice: aggregatedFromAmount !== 0 ? aggregatedToAmount / aggregatedFromAmount : 0
122492
+ };
122493
+ logger2.verbose(
122494
+ `${_VesuMultiplyAdapter.name}::_buildDecreaseLikeCalldata stored aggregated price info: ${aggregatedFromAmount} ${collateralToken.symbol} \u2192 ${aggregatedToAmount} ${debtToken.symbol}, effectivePrice=${this.lastSwapPriceInfo.effectivePrice}, swaps=${executedSwapCount}`
122495
+ );
122496
+ } else {
122497
+ this.lastSwapPriceInfo = null;
122498
+ }
122441
122499
  logger2.debug(
122442
122500
  `${_VesuMultiplyAdapter.name}::_buildDecreaseLikeCalldata leverSwapCount=${leverSwap.length}, withdrawSwapCount=${withdrawSwap.length}, withdrawSwapLimitAmount=${withdrawSwapLimitAmount.toNumber()}, subMargin=${params.subMargin.toNumber()}`
122443
122501
  );
@@ -123240,13 +123298,13 @@ spurious results.`);
123240
123298
  this.minimumExtendedMovementAmount = this.config.minimumExtendedMovementAmount ?? 5;
123241
123299
  this.client = client;
123242
123300
  this.retryDelayForOrderStatus = this.config.retryDelayForOrderStatus ?? 3e3;
123243
- this.usdceToken = this.config.supportedPositions[0].asset;
123301
+ this.usdcToken = this.config.supportedPositions[0].asset;
123244
123302
  }
123245
123303
  _depositApproveProofReadableId() {
123246
- return `extended_approve_${this.usdceToken.symbol}`;
123304
+ return `extended_approve_${this.usdcToken.symbol}`;
123247
123305
  }
123248
123306
  _depositCallProofReadableId() {
123249
- return `extended_deposit_${this.usdceToken.symbol}`;
123307
+ return `extended_deposit_${this.usdcToken.symbol}`;
123250
123308
  }
123251
123309
  //abstract means the method has no implementation in this class; instead, child classes must implement it.
123252
123310
  async getAPY(supportedPosition) {
@@ -123315,7 +123373,7 @@ spurious results.`);
123315
123373
  _getDepositLeaf() {
123316
123374
  return [
123317
123375
  {
123318
- target: this.usdceToken.address,
123376
+ target: this.usdcToken.address,
123319
123377
  method: "approve",
123320
123378
  packedArguments: [this.config.extendedContract.toBigInt()],
123321
123379
  id: this._depositApproveProofReadableId(),
@@ -123335,14 +123393,14 @@ spurious results.`);
123335
123393
  }
123336
123394
  async getDepositCall(params) {
123337
123395
  try {
123338
- const salt = Math.floor(Math.random() * 10 ** this.usdceToken.decimals);
123396
+ const salt = Math.floor(Math.random() * 10 ** this.usdcToken.decimals);
123339
123397
  const amount = uint256_exports.bnToUint256(params.amount.toWei());
123340
123398
  return [
123341
123399
  {
123342
123400
  proofReadableId: this._depositApproveProofReadableId(),
123343
123401
  sanitizer: SIMPLE_SANITIZER,
123344
123402
  call: {
123345
- contractAddress: this.usdceToken.address,
123403
+ contractAddress: this.usdcToken.address,
123346
123404
  selector: hash_exports.getSelectorFromName("approve"),
123347
123405
  calldata: [
123348
123406
  this.config.extendedContract.toBigInt(),
@@ -125922,6 +125980,269 @@ spurious results.`);
125922
125980
  }
125923
125981
  ];
125924
125982
 
125983
+ // src/strategies/universal-adapters/svk-troves-adapter.ts
125984
+ var DEFAULT_TROVES_STRATEGIES_API = "https://app.troves.fi/api/strategies";
125985
+ function parseTrovesApyField(raw) {
125986
+ if (typeof raw === "number" && Number.isFinite(raw)) {
125987
+ return raw;
125988
+ }
125989
+ if (typeof raw === "string") {
125990
+ const n2 = Number.parseFloat(raw);
125991
+ if (Number.isFinite(n2)) {
125992
+ return n2;
125993
+ }
125994
+ }
125995
+ return 0;
125996
+ }
125997
+ var SvkTrovesAdapter = class _SvkTrovesAdapter extends BaseAdapter {
125998
+ constructor(config3) {
125999
+ super(config3, _SvkTrovesAdapter.name, Protocols.TROVES);
126000
+ this.config = config3;
126001
+ }
126002
+ /** Owner used for share balance + `due_assets_from_owner`. */
126003
+ _positionOwner() {
126004
+ return this.config.positionOwner ?? this.config.vaultAllocator;
126005
+ }
126006
+ /**
126007
+ * Proof readable IDs must stay ≤ 31 chars (Cairo short string). We derive a short ASCII suffix from
126008
+ * `strategyVault` address so multiple SVK adapters in one tree stay distinct.
126009
+ */
126010
+ _proofSuffix() {
126011
+ return this.config.strategyVault.address.replace(/^0x/, "").slice(-6);
126012
+ }
126013
+ _depositApproveProofReadableId() {
126014
+ return `appr_dep_svk_${this._proofSuffix()}`;
126015
+ }
126016
+ _depositCallProofReadableId() {
126017
+ return `dep_svk_${this._proofSuffix()}`;
126018
+ }
126019
+ _withdrawCallProofReadableId() {
126020
+ return `wtdrw_svk_${this._proofSuffix()}`;
126021
+ }
126022
+ async getAPY(supportedPosition) {
126023
+ const CACHE_KEY = `svk_apy_${this.config.trovesStrategyId}`;
126024
+ const cached = this.getCache(CACHE_KEY);
126025
+ if (cached) {
126026
+ return cached;
126027
+ }
126028
+ const url = this.config.trovesStrategiesApiUrl ?? DEFAULT_TROVES_STRATEGIES_API;
126029
+ try {
126030
+ const res = await fetch(url);
126031
+ if (!res.ok) {
126032
+ logger2.warn(`${_SvkTrovesAdapter.name}::getAPY: HTTP ${res.status} from ${url}`);
126033
+ const fallback = { apy: 0, type: "base" /* BASE */ };
126034
+ this.setCache(CACHE_KEY, fallback, 3e5);
126035
+ return fallback;
126036
+ }
126037
+ const body = await res.json();
126038
+ const row = body.strategies?.find((s) => s.id === this.config.trovesStrategyId);
126039
+ if (!row) {
126040
+ logger2.warn(
126041
+ `${_SvkTrovesAdapter.name}::getAPY: strategy id not found: ${this.config.trovesStrategyId}`
126042
+ );
126043
+ const fallback = { apy: 0, type: "base" /* BASE */ };
126044
+ this.setCache(CACHE_KEY, fallback, 3e5);
126045
+ return fallback;
126046
+ }
126047
+ const apy = parseTrovesApyField(row.apy);
126048
+ const result2 = { apy, type: "base" /* BASE */ };
126049
+ this.setCache(CACHE_KEY, result2, 3e5);
126050
+ return result2;
126051
+ } catch (error2) {
126052
+ logger2.error(`${_SvkTrovesAdapter.name}::getAPY:`, error2);
126053
+ throw error2;
126054
+ }
126055
+ }
126056
+ async getPosition(supportedPosition) {
126057
+ const CACHE_KEY = `svk_pos_${this.config.strategyVault.address}_${this._positionOwner().address}`;
126058
+ const cached = this.getCache(CACHE_KEY);
126059
+ if (cached) {
126060
+ return cached;
126061
+ }
126062
+ try {
126063
+ const vault = new Contract({
126064
+ abi: universal_vault_abi_default,
126065
+ address: this.config.strategyVault.address,
126066
+ providerOrAccount: this.config.networkConfig.provider
126067
+ });
126068
+ const owner = this._positionOwner();
126069
+ const decimals = supportedPosition.asset.decimals;
126070
+ const shares = await vault.balance_of(owner.address);
126071
+ const shareU256 = uint256_exports.bnToUint256(shares);
126072
+ const liquidAssetsRaw = await vault.convert_to_assets(shareU256);
126073
+ const liquid = Web3Number.fromWei(liquidAssetsRaw.toString(), decimals);
126074
+ let pending = Web3Number.fromWei("0", decimals);
126075
+ try {
126076
+ const dueRaw = await vault.call("due_assets_from_owner", [owner.address]);
126077
+ pending = Web3Number.fromWei(dueRaw.toString(), decimals);
126078
+ } catch (e) {
126079
+ logger2.warn(
126080
+ `${_SvkTrovesAdapter.name}::getPosition: due_assets_from_owner failed (treating as 0): ${e.message}`
126081
+ );
126082
+ }
126083
+ const total = liquid.plus(pending);
126084
+ const remarks = `Troves ${this.config.trovesStrategyId} holdings`;
126085
+ const result2 = {
126086
+ amount: total,
126087
+ remarks
126088
+ };
126089
+ this.setCache(CACHE_KEY, result2, 6e4);
126090
+ return result2;
126091
+ } catch (error2) {
126092
+ logger2.error(`${_SvkTrovesAdapter.name}::getPosition:`, error2);
126093
+ throw error2;
126094
+ }
126095
+ }
126096
+ async maxDeposit(amount) {
126097
+ const baseToken = this.config.baseToken;
126098
+ if (!amount) {
126099
+ return {
126100
+ tokenInfo: baseToken,
126101
+ amount: new Web3Number("999999999999999999999999999", baseToken.decimals),
126102
+ usdValue: 1e27,
126103
+ remarks: "Max deposit (unbounded placeholder)",
126104
+ apy: await this.getAPY({ asset: baseToken, isDebt: false }),
126105
+ protocol: this.protocol
126106
+ };
126107
+ }
126108
+ const usdValue = await this.getUSDValue(baseToken, amount);
126109
+ return {
126110
+ tokenInfo: baseToken,
126111
+ amount,
126112
+ usdValue,
126113
+ remarks: "Deposit amount",
126114
+ apy: await this.getAPY({ asset: baseToken, isDebt: false }),
126115
+ protocol: this.protocol
126116
+ };
126117
+ }
126118
+ async maxWithdraw() {
126119
+ const baseToken = this.config.baseToken;
126120
+ const current = await this.getPosition({ asset: baseToken, isDebt: false });
126121
+ const pos = current ?? { amount: new Web3Number("0", baseToken.decimals), remarks: "" };
126122
+ const usdValue = await this.getUSDValue(baseToken, pos.amount);
126123
+ return {
126124
+ tokenInfo: baseToken,
126125
+ amount: pos.amount,
126126
+ usdValue,
126127
+ remarks: "Max withdraw (liquid + pending redemption, underlying units)",
126128
+ apy: await this.getAPY({ asset: baseToken, isDebt: false }),
126129
+ protocol: this.protocol
126130
+ };
126131
+ }
126132
+ _getDepositLeaf() {
126133
+ const baseToken = this.config.baseToken;
126134
+ const strategyVault = this.config.strategyVault;
126135
+ const receiver = this.config.vaultAllocator;
126136
+ return [
126137
+ {
126138
+ target: baseToken.address,
126139
+ method: "approve",
126140
+ packedArguments: [strategyVault.toBigInt()],
126141
+ sanitizer: SIMPLE_SANITIZER,
126142
+ id: this._depositApproveProofReadableId()
126143
+ },
126144
+ {
126145
+ target: strategyVault,
126146
+ method: "deposit",
126147
+ packedArguments: [receiver.toBigInt()],
126148
+ sanitizer: SIMPLE_SANITIZER,
126149
+ id: this._depositCallProofReadableId()
126150
+ }
126151
+ ];
126152
+ }
126153
+ _getWithdrawLeaf() {
126154
+ const strategyVault = this.config.strategyVault;
126155
+ const recv = this.config.vaultAllocator;
126156
+ const owner = this.config.vaultAllocator;
126157
+ return [
126158
+ {
126159
+ target: strategyVault,
126160
+ method: "withdraw",
126161
+ packedArguments: [recv.toBigInt(), owner.toBigInt()],
126162
+ sanitizer: SIMPLE_SANITIZER,
126163
+ id: this._withdrawCallProofReadableId()
126164
+ }
126165
+ ];
126166
+ }
126167
+ getDepositAdapter() {
126168
+ const leafConfigs = this._getDepositLeaf();
126169
+ const leaves = leafConfigs.map((config3) => {
126170
+ const { target, method, packedArguments, sanitizer, id: id2 } = config3;
126171
+ return this.constructSimpleLeafData({ id: id2, target, method, packedArguments }, sanitizer);
126172
+ });
126173
+ return { leaves, callConstructor: this.getDepositCall.bind(this) };
126174
+ }
126175
+ getWithdrawAdapter() {
126176
+ const leafConfigs = this._getWithdrawLeaf();
126177
+ const leaves = leafConfigs.map((config3) => {
126178
+ const { target, method, packedArguments, sanitizer, id: id2 } = config3;
126179
+ return this.constructSimpleLeafData({ id: id2, target, method, packedArguments }, sanitizer);
126180
+ });
126181
+ return { leaves, callConstructor: this.getWithdrawCall.bind(this) };
126182
+ }
126183
+ async getDepositCall(params) {
126184
+ const baseToken = this.config.baseToken;
126185
+ const strategyVault = this.config.strategyVault;
126186
+ const amount = params.amount;
126187
+ const uint256Amount = uint256_exports.bnToUint256(amount.toWei());
126188
+ const receiver = this.config.vaultAllocator;
126189
+ return [
126190
+ {
126191
+ proofReadableId: this._depositApproveProofReadableId(),
126192
+ sanitizer: SIMPLE_SANITIZER,
126193
+ call: {
126194
+ contractAddress: baseToken.address,
126195
+ selector: hash_exports.getSelectorFromName("approve"),
126196
+ calldata: [
126197
+ strategyVault.toBigInt(),
126198
+ toBigInt3(uint256Amount.low.toString()),
126199
+ toBigInt3(uint256Amount.high.toString())
126200
+ ]
126201
+ }
126202
+ },
126203
+ {
126204
+ proofReadableId: this._depositCallProofReadableId(),
126205
+ sanitizer: SIMPLE_SANITIZER,
126206
+ call: {
126207
+ contractAddress: strategyVault,
126208
+ selector: hash_exports.getSelectorFromName("deposit"),
126209
+ calldata: [
126210
+ toBigInt3(uint256Amount.low.toString()),
126211
+ toBigInt3(uint256Amount.high.toString()),
126212
+ receiver.toBigInt()
126213
+ ]
126214
+ }
126215
+ }
126216
+ ];
126217
+ }
126218
+ async getWithdrawCall(params) {
126219
+ const strategyVault = this.config.strategyVault;
126220
+ const amount = params.amount;
126221
+ const uint256Amount = uint256_exports.bnToUint256(amount.toWei());
126222
+ const recv = this.config.vaultAllocator;
126223
+ const owner = this.config.vaultAllocator;
126224
+ return [
126225
+ {
126226
+ proofReadableId: this._withdrawCallProofReadableId(),
126227
+ sanitizer: SIMPLE_SANITIZER,
126228
+ call: {
126229
+ contractAddress: strategyVault,
126230
+ selector: hash_exports.getSelectorFromName("withdraw"),
126231
+ calldata: [
126232
+ toBigInt3(uint256Amount.low.toString()),
126233
+ toBigInt3(uint256Amount.high.toString()),
126234
+ recv.toBigInt(),
126235
+ owner.toBigInt()
126236
+ ]
126237
+ }
126238
+ }
126239
+ ];
126240
+ }
126241
+ getHealthFactor() {
126242
+ return Promise.resolve(10);
126243
+ }
126244
+ };
126245
+
125925
126246
  // src/data/vault-manager.abi.json
125926
126247
  var vault_manager_abi_default = [
125927
126248
  {
@@ -128881,6 +129202,249 @@ spurious results.`);
128881
129202
  getStrategySettings("mRe7YIELD", "mRe7YIELD", hypermRe7YIELD, false, false)
128882
129203
  ];
128883
129204
 
129205
+ // src/strategies/vesu-extended-strategy/services/ltv-imbalance-rebalance-math.ts
129206
+ function ceilBtc(v, precision) {
129207
+ const f2 = 10 ** precision;
129208
+ return Math.ceil(v * f2) / f2;
129209
+ }
129210
+ function floorBtc(v, precision) {
129211
+ const f2 = 10 ** precision;
129212
+ return Math.floor(v * f2) / f2;
129213
+ }
129214
+ function isNegligible(btc, precision) {
129215
+ return Math.abs(btc) < 10 ** -precision;
129216
+ }
129217
+ function computeExtIdealMargin(posBtc, leverage, price) {
129218
+ return posBtc * price / leverage;
129219
+ }
129220
+ function computeExtDeficit(ext, price) {
129221
+ return Math.max(0, computeExtIdealMargin(ext.positionBtc, ext.leverage, price) - ext.equity);
129222
+ }
129223
+ function computeVesuHF(vesu, price) {
129224
+ const collateralUsd = vesu.positionBtc * price;
129225
+ if (collateralUsd === 0) return Infinity;
129226
+ const ltv = vesu.debt * vesu.debtPrice / collateralUsd;
129227
+ if (ltv === 0) return Infinity;
129228
+ return vesu.maxLTV / ltv;
129229
+ }
129230
+ function computeVesuDebtRepay(vesu, price) {
129231
+ const targetLTV = vesu.maxLTV / vesu.targetHF;
129232
+ const collateralUsd = vesu.positionBtc * price;
129233
+ const requiredDebt = targetLTV * collateralUsd / vesu.debtPrice;
129234
+ return Math.max(0, vesu.debt - requiredDebt);
129235
+ }
129236
+ function computeVesuTargetLTV(vesu) {
129237
+ return vesu.maxLTV / vesu.targetHF;
129238
+ }
129239
+ function computeVesuGrowthCost(gapBtc, vesu, price) {
129240
+ const targetLTV = computeVesuTargetLTV(vesu);
129241
+ return gapBtc * price * (1 - targetLTV);
129242
+ }
129243
+ function computeVesuGrowthDebt(gapBtc, vesu, price) {
129244
+ const targetLTV = computeVesuTargetLTV(vesu);
129245
+ return gapBtc * price * targetLTV / vesu.debtPrice;
129246
+ }
129247
+ function computeVesuGrowthFromEquity(equityUsd, vesu, price) {
129248
+ const targetLTV = computeVesuTargetLTV(vesu);
129249
+ return equityUsd / (price * (1 - targetLTV));
129250
+ }
129251
+ function emptyDeltas() {
129252
+ return {
129253
+ dExtPosition: 0,
129254
+ dVesuPosition: 0,
129255
+ dVesuDebt: 0,
129256
+ dExtAvlWithdraw: 0,
129257
+ dExtUpnl: 0,
129258
+ dVaUsd: 0,
129259
+ dWalletUsd: 0,
129260
+ dVesuBorrowCapacity: 0,
129261
+ dTransferVesuToExt: 0
129262
+ };
129263
+ }
129264
+ function mergeDeltas(a, b2) {
129265
+ return {
129266
+ dExtPosition: a.dExtPosition + b2.dExtPosition,
129267
+ dVesuPosition: a.dVesuPosition + b2.dVesuPosition,
129268
+ dVesuDebt: a.dVesuDebt + b2.dVesuDebt,
129269
+ dExtAvlWithdraw: a.dExtAvlWithdraw + b2.dExtAvlWithdraw,
129270
+ dExtUpnl: a.dExtUpnl + b2.dExtUpnl,
129271
+ dVaUsd: a.dVaUsd + b2.dVaUsd,
129272
+ dWalletUsd: a.dWalletUsd + b2.dWalletUsd,
129273
+ dVesuBorrowCapacity: a.dVesuBorrowCapacity + b2.dVesuBorrowCapacity,
129274
+ dTransferVesuToExt: a.dTransferVesuToExt + b2.dTransferVesuToExt
129275
+ };
129276
+ }
129277
+ function drawFunds(need, keys, pool) {
129278
+ const draws = {};
129279
+ let unmet = need;
129280
+ for (const k of keys) {
129281
+ if (unmet <= 0) break;
129282
+ const avail = pool[k];
129283
+ if (avail <= 0) continue;
129284
+ const take = Math.min(avail, unmet);
129285
+ draws[k] = take;
129286
+ pool[k] -= take;
129287
+ unmet -= take;
129288
+ }
129289
+ return { draws, filled: need - unmet, unmet };
129290
+ }
129291
+ function sumKeys(draws, keys) {
129292
+ return keys.reduce((s, k) => s + (draws[k] ?? 0), 0);
129293
+ }
129294
+ function applyDrawsToDeltas(d, draws, sign2) {
129295
+ d.dVaUsd += sign2 * (draws.vaUsd ?? 0);
129296
+ d.dWalletUsd += sign2 * (draws.walletUsd ?? 0);
129297
+ d.dVesuBorrowCapacity += sign2 * (draws.vesuBorrowCapacity ?? 0);
129298
+ d.dExtAvlWithdraw += sign2 * (draws.extAvlWithdraw ?? 0);
129299
+ d.dExtUpnl += sign2 * (draws.extUpnl ?? 0);
129300
+ }
129301
+ function fixExtMargin(ext, price, pool) {
129302
+ const d = emptyDeltas();
129303
+ const deficit = computeExtDeficit(ext, price);
129304
+ if (deficit <= 0) return { d, unmet: 0 };
129305
+ const { draws, filled, unmet } = drawFunds(deficit, ["walletUsd", "vaUsd", "vesuBorrowCapacity"], pool);
129306
+ applyDrawsToDeltas(d, draws, -1);
129307
+ d.dExtAvlWithdraw += filled;
129308
+ const fromVesu = draws.vesuBorrowCapacity ?? 0;
129309
+ if (fromVesu > 0) d.dTransferVesuToExt += fromVesu;
129310
+ return { d, unmet };
129311
+ }
129312
+ function fixVesuMargin(vesu, price, pool, hfBuffer) {
129313
+ const d = emptyDeltas();
129314
+ const hf = computeVesuHF(vesu, price);
129315
+ if (hf >= vesu.targetHF - hfBuffer) return { d, unmet: 0 };
129316
+ const repayTokens = computeVesuDebtRepay(vesu, price);
129317
+ const repayUsd = repayTokens * vesu.debtPrice;
129318
+ const { draws, filled, unmet } = drawFunds(repayUsd, ["vaUsd", "walletUsd", "extAvlWithdraw", "extUpnl"], pool);
129319
+ applyDrawsToDeltas(d, draws, -1);
129320
+ d.dVesuDebt -= filled / vesu.debtPrice;
129321
+ const fromExt = sumKeys(draws, ["extAvlWithdraw", "extUpnl"]);
129322
+ if (fromExt > 0) d.dTransferVesuToExt -= fromExt;
129323
+ return { d, unmet };
129324
+ }
129325
+ function phase1(ext, vesu, price, pool, config3) {
129326
+ const extResult = fixExtMargin(ext, price, pool);
129327
+ const vesuResult = fixVesuMargin(vesu, price, pool, config3.hfBuffer);
129328
+ return {
129329
+ deltas: mergeDeltas(extResult.d, vesuResult.d),
129330
+ extDeficitRemaining: extResult.unmet,
129331
+ vesuRepayRemaining: vesuResult.unmet
129332
+ };
129333
+ }
129334
+ function solveUnifiedF(extPos, vesuPos, extEquity, vesuDebt, vesu, extLev, price) {
129335
+ const targetLTV = computeVesuTargetLTV(vesu);
129336
+ const k = 1 - targetLTV + 1 / extLev;
129337
+ const totalEquity = extEquity + vesuPos * price - vesuDebt * vesu.debtPrice;
129338
+ return totalEquity / (price * k);
129339
+ }
129340
+ function solveTransfer(vesuPos, vesuDebt, F, vesu, price) {
129341
+ const targetLTV = computeVesuTargetLTV(vesu);
129342
+ return vesuPos * price - vesuDebt * vesu.debtPrice - F * price * (1 - targetLTV);
129343
+ }
129344
+ function solveDebtRepay(vesuDebt, F, vesu, price) {
129345
+ const targetLTV = computeVesuTargetLTV(vesu);
129346
+ return vesuDebt - targetLTV * F * price / vesu.debtPrice;
129347
+ }
129348
+ function roundFinalPosition(extPos, vesuPos, rawF, precision) {
129349
+ let fFromExt;
129350
+ if (rawF < extPos) {
129351
+ const closeExt = ceilBtc(extPos - rawF, precision);
129352
+ fFromExt = extPos - closeExt;
129353
+ } else {
129354
+ const growExt = floorBtc(rawF - extPos, precision);
129355
+ fFromExt = extPos + growExt;
129356
+ }
129357
+ let fFromVesu;
129358
+ if (rawF < vesuPos) {
129359
+ const closeVesu = ceilBtc(vesuPos - rawF, precision);
129360
+ fFromVesu = vesuPos - closeVesu;
129361
+ } else {
129362
+ const growVesu = floorBtc(rawF - vesuPos, precision);
129363
+ fFromVesu = vesuPos + growVesu;
129364
+ }
129365
+ return Math.max(0, Math.min(fFromExt, fFromVesu));
129366
+ }
129367
+ function phase2(extPos, vesuPos, extEquity, vesuDebt, vesu, extLev, price, pool, precision) {
129368
+ const d = emptyDeltas();
129369
+ const targetLTV = computeVesuTargetLTV(vesu);
129370
+ const imbalance = extPos - vesuPos;
129371
+ let fundedGrowthBtc = 0;
129372
+ if (!isNegligible(imbalance, precision)) {
129373
+ if (imbalance > 0) {
129374
+ const equityCostUsd = computeVesuGrowthCost(imbalance, vesu, price);
129375
+ const { draws, filled } = drawFunds(equityCostUsd, ["vaUsd", "walletUsd", "extAvlWithdraw", "extUpnl"], pool);
129376
+ applyDrawsToDeltas(d, draws, -1);
129377
+ const grownBtc = computeVesuGrowthFromEquity(filled, vesu, price);
129378
+ d.dVesuPosition += grownBtc;
129379
+ fundedGrowthBtc = grownBtc;
129380
+ d.dVesuDebt += computeVesuGrowthDebt(grownBtc, vesu, price);
129381
+ const fromExt = sumKeys(draws, ["extAvlWithdraw", "extUpnl"]);
129382
+ if (fromExt > 0) d.dTransferVesuToExt -= fromExt;
129383
+ } else {
129384
+ const absImbalance = -imbalance;
129385
+ const marginCostUsd = absImbalance * price / extLev;
129386
+ const { draws, filled } = drawFunds(marginCostUsd, ["walletUsd", "vaUsd", "vesuBorrowCapacity"], pool);
129387
+ applyDrawsToDeltas(d, draws, -1);
129388
+ const grownBtc = filled * extLev / price;
129389
+ d.dExtPosition += grownBtc;
129390
+ fundedGrowthBtc = grownBtc;
129391
+ const fromVesu = draws.vesuBorrowCapacity ?? 0;
129392
+ if (fromVesu > 0) d.dTransferVesuToExt += fromVesu;
129393
+ }
129394
+ }
129395
+ const effExtPos = extPos + d.dExtPosition;
129396
+ const effVesuPos = vesuPos + d.dVesuPosition;
129397
+ const effExtEquity = extEquity;
129398
+ const effVesuDebt = vesuDebt + d.dVesuDebt;
129399
+ const rawF = solveUnifiedF(effExtPos, effVesuPos, effExtEquity, effVesuDebt, vesu, extLev, price);
129400
+ const cappedF = Math.min(rawF, Math.max(effExtPos, effVesuPos));
129401
+ const maxGrowableTo = Math.max(effExtPos, effVesuPos);
129402
+ const targetF = Math.max(0, Math.min(cappedF, maxGrowableTo));
129403
+ const remainingImbalance = effExtPos - effVesuPos;
129404
+ const extNeedsMore = effExtEquity < computeExtIdealMargin(effExtPos, extLev, price);
129405
+ const vesuCurrentLTV = effVesuPos > 0 ? effVesuDebt * vesu.debtPrice / (effVesuPos * price) : 0;
129406
+ const vesuNeedsMore = vesuCurrentLTV > targetLTV;
129407
+ const needsFurtherAction = !isNegligible(remainingImbalance, precision) || extNeedsMore || vesuNeedsMore;
129408
+ if (!needsFurtherAction) {
129409
+ return d;
129410
+ }
129411
+ const F = roundFinalPosition(effExtPos, effVesuPos, targetF, precision);
129412
+ const dx = F - effExtPos;
129413
+ const dy = F - effVesuPos;
129414
+ if (isNegligible(dx, precision) && isNegligible(dy, precision)) {
129415
+ return d;
129416
+ }
129417
+ d.dExtPosition += dx;
129418
+ d.dVesuPosition += dy;
129419
+ const debtRepay = solveDebtRepay(effVesuDebt, F, vesu, price);
129420
+ d.dVesuDebt -= debtRepay;
129421
+ const T = solveTransfer(effVesuPos, effVesuDebt, F, vesu, price);
129422
+ d.dTransferVesuToExt += T;
129423
+ d.dExtAvlWithdraw += T;
129424
+ return d;
129425
+ }
129426
+ function rebalance(inputs) {
129427
+ const { ext, vesu, btcPrice, config: config3 } = inputs;
129428
+ const pool = { ...inputs.funding };
129429
+ const p1 = phase1(ext, vesu, btcPrice, pool, config3);
129430
+ const effExtPos = ext.positionBtc + p1.deltas.dExtPosition;
129431
+ const effVesuPos = vesu.positionBtc + p1.deltas.dVesuPosition;
129432
+ const effExtEquity = ext.equity + p1.deltas.dExtAvlWithdraw + p1.deltas.dExtUpnl;
129433
+ const effVesuDebt = vesu.debt + p1.deltas.dVesuDebt;
129434
+ const p2 = phase2(
129435
+ effExtPos,
129436
+ effVesuPos,
129437
+ effExtEquity,
129438
+ effVesuDebt,
129439
+ vesu,
129440
+ ext.leverage,
129441
+ btcPrice,
129442
+ pool,
129443
+ config3.positionPrecision
129444
+ );
129445
+ return mergeDeltas(p1.deltas, p2);
129446
+ }
129447
+
128884
129448
  // src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts
128885
129449
  var RouteType = /* @__PURE__ */ ((RouteType2) => {
128886
129450
  RouteType2["WALLET_TO_EXTENDED"] = "WALLET_TO_EXTENDED";
@@ -128912,6 +129476,7 @@ spurious results.`);
128912
129476
  return CaseCategory2;
128913
129477
  })(CaseCategory || {});
128914
129478
  var CaseId = /* @__PURE__ */ ((CaseId2) => {
129479
+ CaseId2["MANAGE_LTV"] = "MANAGE_LTV";
128915
129480
  CaseId2["LTV_VESU_LOW_TO_EXTENDED"] = "LTV_VESU_LOW_TO_EXTENDED";
128916
129481
  CaseId2["LTV_EXTENDED_PROFITABLE_AVAILABLE"] = "LTV_EXTENDED_PROFITABLE_AVAILABLE";
128917
129482
  CaseId2["LTV_EXTENDED_PROFITABLE_REALIZE"] = "LTV_EXTENDED_PROFITABLE_REALIZE";
@@ -128936,19 +129501,56 @@ spurious results.`);
128936
129501
  return new Web3Number(value.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
128937
129502
  }
128938
129503
  var CASE_ROUTE_TYPES = {
128939
- // LTV Rebalance — Vesu side
129504
+ // LTV Rebalance — unified
129505
+ ["MANAGE_LTV" /* MANAGE_LTV */]: [
129506
+ "REALISE_PNL" /* REALISE_PNL */,
129507
+ "EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */,
129508
+ "RETURN_TO_WAIT" /* RETURN_TO_WAIT */,
129509
+ "WALLET_TO_VA" /* WALLET_TO_VA */,
129510
+ "VESU_BORROW" /* VESU_BORROW */,
129511
+ "VESU_REPAY" /* VESU_REPAY */,
129512
+ "VA_TO_EXTENDED" /* VA_TO_EXTENDED */,
129513
+ "VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */,
129514
+ "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */,
129515
+ "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
129516
+ "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
129517
+ "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */,
129518
+ // Second-phase VA / Extended funding after lever routes (same types may repeat).
129519
+ "RETURN_TO_WAIT" /* RETURN_TO_WAIT */,
129520
+ "WALLET_TO_VA" /* WALLET_TO_VA */,
129521
+ "VA_TO_EXTENDED" /* VA_TO_EXTENDED */
129522
+ ],
129523
+ /** @deprecated */
128940
129524
  ["LTV_VESU_HIGH_USE_VA_OR_WALLET" /* LTV_VESU_HIGH_USE_VA_OR_WALLET */]: ["WALLET_TO_VA" /* WALLET_TO_VA */, "VESU_REPAY" /* VESU_REPAY */],
128941
- // use wallet to va if wallet has and va doesnt have enough
129525
+ /** @deprecated */
128942
129526
  ["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 */],
129527
+ /** @deprecated */
128943
129528
  ["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 */],
128944
- // LTV Rebalance — Extended side
129529
+ /** @deprecated */
128945
129530
  ["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 */],
129531
+ /** @deprecated */
128946
129532
  ["LTV_VESU_LOW_TO_EXTENDED" /* LTV_VESU_LOW_TO_EXTENDED */]: ["VESU_BORROW" /* VESU_BORROW */, "VA_TO_EXTENDED" /* VA_TO_EXTENDED */],
128947
129533
  // New Deposits
128948
129534
  // @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
128949
129535
  // Sequence: fund-movement transfers first (WALLET_TO_EXTENDED, VA_TO_EXTENDED, WALLET_TO_VA, EXTENDED_TO_WALLET),
128950
129536
  // then RETURN_TO_WAIT, then optional second WALLET_TO_VA + RETURN_TO_WAIT, then lever routes.
128951
- ["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 */],
129537
+ ["DEPOSIT_FRESH_VAULT" /* DEPOSIT_FRESH_VAULT */]: [
129538
+ // May repeat after MANAGE_LTV (VA top-up → Extended → wait → levers).
129539
+ "WALLET_TO_VA" /* WALLET_TO_VA */,
129540
+ "VA_TO_EXTENDED" /* VA_TO_EXTENDED */,
129541
+ "RETURN_TO_WAIT" /* RETURN_TO_WAIT */,
129542
+ "VA_TO_EXTENDED" /* VA_TO_EXTENDED */,
129543
+ "VESU_BORROW" /* VESU_BORROW */,
129544
+ "WALLET_TO_EXTENDED" /* WALLET_TO_EXTENDED */,
129545
+ "VA_TO_EXTENDED" /* VA_TO_EXTENDED */,
129546
+ "WALLET_TO_VA" /* WALLET_TO_VA */,
129547
+ "EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */,
129548
+ "RETURN_TO_WAIT" /* RETURN_TO_WAIT */,
129549
+ "WALLET_TO_VA" /* WALLET_TO_VA */,
129550
+ "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
129551
+ "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */,
129552
+ "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */
129553
+ ],
128952
129554
  ["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 */],
128953
129555
  ["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 */],
128954
129556
  ["DEPOSIT_COMBINATION" /* DEPOSIT_COMBINATION */]: [],
@@ -128971,6 +129573,17 @@ spurious results.`);
128971
129573
  ["IMBALANCE_VESU_EXCESS_LONG_NO_FUNDS" /* IMBALANCE_VESU_EXCESS_LONG_NO_FUNDS */]: ["VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */]
128972
129574
  };
128973
129575
  var CASE_DEFINITIONS = {
129576
+ ["MANAGE_LTV" /* MANAGE_LTV */]: {
129577
+ id: "MANAGE_LTV" /* MANAGE_LTV */,
129578
+ category: "LTV_REBALANCE" /* LTV_REBALANCE */,
129579
+ title: "LTV Rebalance: Unified Vesu repay + Extended margin management",
129580
+ description: "Manages both Vesu high-LTV repayment and Extended low-margin funding in a single pass.",
129581
+ steps: [
129582
+ "Compute vesu repay needed and extended margin needed",
129583
+ "Allocate funds: VA > Wallet > ExtAvl > ExtUpnl for Vesu; Wallet > VA > Borrow for Extended",
129584
+ "Build combined transfer and repay/margin routes"
129585
+ ]
129586
+ },
128974
129587
  ["LTV_VESU_LOW_TO_EXTENDED" /* LTV_VESU_LOW_TO_EXTENDED */]: {
128975
129588
  id: "LTV_VESU_LOW_TO_EXTENDED" /* LTV_VESU_LOW_TO_EXTENDED */,
128976
129589
  category: "LTV_REBALANCE" /* LTV_REBALANCE */,
@@ -129193,199 +129806,590 @@ spurious results.`);
129193
129806
  }
129194
129807
  var SolveBudget = class {
129195
129808
  constructor(state) {
129196
- // should be same length as vesuPerPoolDebtDeltasToBorrow
129197
- // ── Budget tracking (populated by initBudget) ──────────────────────
129198
- this._vaUsd = 0;
129199
- this._walletUsd = 0;
129200
- this._extAvailWithdraw = 0;
129201
- this._extAvailUpnl = 0;
129202
- this._extAvailTrade = 0;
129203
- this._extPendingDeposit = 0;
129204
- this._vesuBorrowCapacity = 0;
129205
- this._totalUnused = 0;
129206
- const buffer = state.limitBalanceBufferFactor;
129207
- this.unusedBalance = state.unusedBalance.map((item) => {
129208
- return {
129209
- ...item,
129210
- amount: item.amount.multipliedBy(1 - buffer),
129211
- usdValue: item.usdValue * (1 - buffer)
129212
- };
129809
+ this.assetToken = state.assetToken;
129810
+ this.usdcToken = state.usdcToken;
129811
+ const cloneTb = (b2) => ({
129812
+ token: b2.token,
129813
+ amount: new Web3Number(b2.amount.toFixed(b2.token.decimals), b2.token.decimals),
129814
+ usdValue: b2.usdValue
129213
129815
  });
129214
- this.walletBalance = state.walletBalance ? {
129215
- ...state.walletBalance,
129216
- amount: state.walletBalance.amount.multipliedBy(1 - buffer),
129217
- usdValue: state.walletBalance.usdValue * (1 - buffer)
129218
- } : null;
129219
- this.vaultBalance = state.vaultBalance ? {
129220
- ...state.vaultBalance,
129221
- amount: state.vaultBalance.amount.multipliedBy(1 - buffer),
129222
- usdValue: state.vaultBalance.usdValue * (1 - buffer)
129223
- } : null;
129224
- this.extendedPositions = state.extendedPositions;
129816
+ this.unusedBalance = state.unusedBalance.map((item) => cloneTb(item));
129817
+ this.walletBalance = state.walletBalance ? cloneTb(state.walletBalance) : null;
129818
+ this.vaultAssetBalance = state.vaultAssetBalance ? cloneTb(state.vaultAssetBalance) : null;
129819
+ this.vaultUsdcBalance = state.vaultUsdcBalance ? cloneTb(state.vaultUsdcBalance) : null;
129820
+ this.extendedPositions = state.extendedPositions.map((p) => ({
129821
+ ...p,
129822
+ size: new Web3Number(p.size.toFixed(8), 8),
129823
+ valueUsd: new Web3Number(p.valueUsd.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS)
129824
+ }));
129225
129825
  this.extendedBalance = state.extendedBalance ? {
129226
- ...state.extendedBalance,
129227
- availableForTrade: state.extendedBalance.availableForTrade.multipliedBy(1 - buffer),
129228
- availableForWithdrawal: state.extendedBalance.availableForWithdrawal.multipliedBy(1 - buffer),
129229
- unrealisedPnl: state.extendedBalance.unrealisedPnl.multipliedBy(1 - buffer),
129230
- balance: state.extendedBalance.balance.multipliedBy(1 - buffer)
129826
+ equity: new Web3Number(
129827
+ state.extendedBalance.equity.toFixed(USDC_TOKEN_DECIMALS),
129828
+ USDC_TOKEN_DECIMALS
129829
+ ),
129830
+ availableForTrade: new Web3Number(
129831
+ state.extendedBalance.availableForTrade.toFixed(USDC_TOKEN_DECIMALS),
129832
+ USDC_TOKEN_DECIMALS
129833
+ ),
129834
+ availableForWithdrawal: new Web3Number(
129835
+ state.extendedBalance.availableForWithdrawal.toFixed(USDC_TOKEN_DECIMALS),
129836
+ USDC_TOKEN_DECIMALS
129837
+ ),
129838
+ unrealisedPnl: new Web3Number(
129839
+ state.extendedBalance.unrealisedPnl.toFixed(USDC_TOKEN_DECIMALS),
129840
+ USDC_TOKEN_DECIMALS
129841
+ ),
129842
+ balance: new Web3Number(
129843
+ state.extendedBalance.balance.toFixed(USDC_TOKEN_DECIMALS),
129844
+ USDC_TOKEN_DECIMALS
129845
+ ),
129846
+ pendingDeposit: new Web3Number(
129847
+ state.extendedBalance.pendingDeposit.toFixed(USDC_TOKEN_DECIMALS),
129848
+ USDC_TOKEN_DECIMALS
129849
+ )
129231
129850
  } : null;
129232
- this.vesuPoolStates = state.vesuPoolStates;
129851
+ this.vesuPoolStates = state.vesuPoolStates.map((p) => ({
129852
+ ...p,
129853
+ collateralAmount: new Web3Number(
129854
+ p.collateralAmount.toFixed(p.collateralToken.decimals),
129855
+ p.collateralToken.decimals
129856
+ ),
129857
+ debtAmount: new Web3Number(
129858
+ p.debtAmount.toFixed(p.debtToken.decimals),
129859
+ p.debtToken.decimals
129860
+ )
129861
+ }));
129233
129862
  const vesuPerPoolDebtDeltasToBorrow = this._computeperPoolDebtDeltasToBorrow();
129234
129863
  assert3(vesuPerPoolDebtDeltasToBorrow.length === this.vesuPoolStates.length, "vesuPerPoolDebtDeltasToBorrow length must match vesuPoolStates length");
129235
129864
  this.vesuPerPoolDebtDeltasToBorrow = vesuPerPoolDebtDeltasToBorrow.map((item) => item.deltaDebt);
129236
129865
  this.shouldVesuRebalance = vesuPerPoolDebtDeltasToBorrow.map((item) => item.shouldRebalance);
129237
129866
  }
129867
+ /** `1 - limitBalanceBufferFactor` — multiplier applied to raw notionals for “usable” USD. */
129868
+ _usableFraction() {
129869
+ return 1;
129870
+ }
129238
129871
  /**
129239
- * Initialise budget-tracking values from the current state snapshot.
129240
- * Must be called after state is populated and debt deltas are computed.
129241
- *
129242
- * Accounts for pendingDeposit:
129243
- * - pendingDeposit > 0: funds in transit TO Extended → increase effective Extended available-for-trade
129244
- * - pendingDeposit < 0: funds in transit FROM Extended → increase effective wallet balance
129872
+ * Raw USD notional for a token row. USDC (and configured {@link usdcToken}) uses 1:1 from amount;
129873
+ * non-stable assets (e.g. WBTC in VA) use {@link TokenBalance.usdValue} from the pricer at refresh.
129874
+ */
129875
+ _rawTokenUsd(tb) {
129876
+ if (!tb) return 0;
129877
+ if (this.usdcToken.address.eq(tb.token.address)) {
129878
+ return Number(tb.amount.toFixed(tb.token.decimals));
129879
+ }
129880
+ return tb.usdValue;
129881
+ }
129882
+ /** Apply safety buffer to a raw USD scalar. */
129883
+ bufferedUsd(rawUsd) {
129884
+ return rawUsd * this._usableFraction();
129885
+ }
129886
+ /** Convert a buffered “usable” USD amount to raw nominal USD (inverse of {@link bufferedUsd}). */
129887
+ rawUsdFromBuffered(bufferedUsd) {
129888
+ const bf = this._usableFraction();
129889
+ assert3(bf > 0, "SolveBudget::rawUsdFromBuffered usable fraction must be positive");
129890
+ return bufferedUsd / bf;
129891
+ }
129892
+ /** Buffered USD notional for one token balance row. */
129893
+ bufferedTokenUsd(tb) {
129894
+ return this.bufferedUsd(this._rawTokenUsd(tb));
129895
+ }
129896
+ logStateSummary() {
129897
+ console.log("===== state summary =====");
129898
+ const aggregatedData = {
129899
+ unusedBalances: this.unusedBalance.map((b2) => ({
129900
+ token: b2.token.symbol,
129901
+ amount: b2.amount.toNumber()
129902
+ })),
129903
+ walletBalance: this.walletBalance ? {
129904
+ token: this.walletBalance.token.symbol,
129905
+ amount: this.walletBalance.amount.toNumber()
129906
+ } : void 0,
129907
+ vaultAssetBalance: this.vaultAssetBalance ? {
129908
+ token: this.vaultAssetBalance.token.symbol,
129909
+ amount: this.vaultAssetBalance.amount.toNumber()
129910
+ } : void 0,
129911
+ vaultUsdcBalance: this.vaultUsdcBalance ? {
129912
+ token: this.vaultUsdcBalance.token.symbol,
129913
+ amount: this.vaultUsdcBalance.amount.toNumber()
129914
+ } : void 0,
129915
+ vesuPoolStates: this.vesuPoolStates.map((p) => ({
129916
+ poolId: p.poolId,
129917
+ collateralAmount: p.collateralAmount.toNumber(),
129918
+ debtAmount: p.debtAmount.toNumber()
129919
+ })),
129920
+ vesuBorrowCapacity: this.vesuBorrowCapacity,
129921
+ vesuRebalance: this.shouldVesuRebalance,
129922
+ vesuPerPoolDebtDeltasToBorrow: this.vesuPerPoolDebtDeltasToBorrow.map((d) => d.toNumber()),
129923
+ extendedBalance: this.extendedBalance?.balance.toNumber(),
129924
+ extendedEquity: this.extendedBalance?.equity.toNumber(),
129925
+ extendedAvailableForTrade: this.extendedBalance?.availableForTrade.toNumber(),
129926
+ extendedAvailableForWithdrawal: this.extendedBalance?.availableForWithdrawal.toNumber(),
129927
+ extendedUnrealisedPnl: this.extendedBalance?.unrealisedPnl.toNumber(),
129928
+ extendedPendingDeposit: this.extendedBalance?.pendingDeposit.toNumber(),
129929
+ extendedPositions: this.extendedPositions.map((p) => ({
129930
+ instrument: p.instrument,
129931
+ size: p.size.toNumber(),
129932
+ valueUsd: p.valueUsd.toNumber()
129933
+ }))
129934
+ };
129935
+ console.log(
129936
+ "unused balances",
129937
+ aggregatedData.unusedBalances.map((b2) => `${b2.token}=${b2.amount}`).join(", ")
129938
+ );
129939
+ console.log(
129940
+ "wallet balance",
129941
+ aggregatedData.walletBalance ? `${aggregatedData.walletBalance.token}=${aggregatedData.walletBalance.amount}` : void 0
129942
+ );
129943
+ console.log(
129944
+ "vault asset balance",
129945
+ aggregatedData.vaultAssetBalance ? `${aggregatedData.vaultAssetBalance.token}=${aggregatedData.vaultAssetBalance.amount}` : void 0
129946
+ );
129947
+ console.log(
129948
+ "vault usdc balance",
129949
+ aggregatedData.vaultUsdcBalance ? `${aggregatedData.vaultUsdcBalance.token}=${aggregatedData.vaultUsdcBalance.amount}` : void 0
129950
+ );
129951
+ console.log(
129952
+ "vesu pool states",
129953
+ aggregatedData.vesuPoolStates.map(
129954
+ (p) => `${p.poolId.shortString()}=${p.collateralAmount} ${p.debtAmount}`
129955
+ ).join(", ")
129956
+ );
129957
+ console.log("vesu borrow capacity", aggregatedData.vesuBorrowCapacity);
129958
+ console.log(
129959
+ "vesu rebalance",
129960
+ aggregatedData.vesuRebalance.map(String).join(", ")
129961
+ );
129962
+ console.log("vesu per pool debt deltas to borrow", aggregatedData.vesuPerPoolDebtDeltasToBorrow.join(", "));
129963
+ console.log("extended balance", aggregatedData.extendedBalance);
129964
+ console.log("extended equity", aggregatedData.extendedEquity);
129965
+ console.log("extended available for trade", aggregatedData.extendedAvailableForTrade);
129966
+ console.log("extended available for withdrawal", aggregatedData.extendedAvailableForWithdrawal);
129967
+ console.log("extended unrealised pnl", aggregatedData.extendedUnrealisedPnl);
129968
+ console.log("extended pending deposit", aggregatedData.extendedPendingDeposit);
129969
+ console.log(
129970
+ "extended positions",
129971
+ aggregatedData.extendedPositions.map(
129972
+ (p) => `${p.instrument}=${p.size} ${p.valueUsd}`
129973
+ ).join(", ")
129974
+ );
129975
+ return aggregatedData;
129976
+ }
129977
+ /**
129978
+ * Initialise derived views for a solve cycle. Mutates only when pending
129979
+ * withdrawal from Extended is in transit (credits wallet raw balance).
129245
129980
  */
129246
129981
  initBudget() {
129247
- const debtDeltaNum = this.vesuPerPoolDebtDeltasToBorrow.reduce((a, b2) => a + b2.toNumber(), 0);
129248
- let totalUnusedUsd = this.unusedBalance.reduce((a, b2) => a + b2.usdValue, 0);
129249
- if (debtDeltaNum > 0) totalUnusedUsd += debtDeltaNum;
129250
- const extAvailTrade = this.extendedBalance?.availableForTrade?.toNumber() ?? 0;
129251
- if (extAvailTrade > 0) totalUnusedUsd += extAvailTrade;
129252
- if (debtDeltaNum > 0) totalUnusedUsd += debtDeltaNum;
129253
- this._vaUsd = this.vaultBalance?.usdValue ?? 0;
129254
- this._walletUsd = this.walletBalance?.usdValue ?? 0;
129255
- this._extAvailWithdraw = this.extendedBalance?.availableForWithdrawal?.toNumber() ?? 0;
129256
- this._extAvailUpnl = this.extendedBalance?.unrealisedPnl?.toNumber() ?? 0;
129257
- this._extAvailTrade = extAvailTrade;
129258
- this._extPendingDeposit = this.extendedBalance?.pendingDeposit?.toNumber() ?? 0;
129259
- this._vesuBorrowCapacity = debtDeltaNum;
129260
- this._totalUnused = totalUnusedUsd;
129261
129982
  const pendingDeposit = this.extendedBalance?.pendingDeposit?.toNumber() ?? 0;
129262
- if (pendingDeposit > 0) {
129263
- this._extAvailTrade += pendingDeposit;
129264
- this._totalUnused += pendingDeposit;
129265
- logger2.debug(`SolveBudget::initBudget pendingDeposit=${pendingDeposit} -> increased extAvailTrade`);
129266
- } else if (pendingDeposit < 0) {
129983
+ if (pendingDeposit < 0) {
129267
129984
  const inTransit = Math.abs(pendingDeposit);
129268
- this._walletUsd += inTransit;
129269
- this._totalUnused += inTransit;
129270
- logger2.debug(`SolveBudget::initBudget pendingDeposit=${pendingDeposit} -> increased walletUsd by ${inTransit}`);
129985
+ if (this.walletBalance) {
129986
+ this._addUsdToTokenBalance(this.walletBalance, inTransit);
129987
+ }
129988
+ logger2.debug(`SolveBudget::initBudget pendingDeposit=${pendingDeposit} -> increased wallet raw USD by ${inTransit}`);
129989
+ }
129990
+ this._recomputeUnusedBalance();
129991
+ }
129992
+ /**
129993
+ * Apply a safety buffer to all liquid balances (VA, wallet, extended trade/withdraw/upnl,
129994
+ * unused balances). Call after withdrawal classification but before LTV/deposit classifiers
129995
+ * so that withdrawal uses full raw amounts while subsequent classifiers see buffered values.
129996
+ */
129997
+ applyBuffer(factor) {
129998
+ if (factor <= 0 || factor >= 1) return;
129999
+ const mult = 1 - factor;
130000
+ const scaleTokenBalance = (tb) => {
130001
+ if (!tb) return;
130002
+ const newAmount = tb.amount.multipliedBy(mult);
130003
+ tb.amount = new Web3Number(newAmount.toFixed(tb.token.decimals), tb.token.decimals);
130004
+ tb.usdValue = tb.usdValue * mult;
130005
+ };
130006
+ scaleTokenBalance(this.vaultAssetBalance);
130007
+ scaleTokenBalance(this.vaultUsdcBalance);
130008
+ scaleTokenBalance(this.walletBalance);
130009
+ for (const ub of this.unusedBalance) {
130010
+ scaleTokenBalance(ub);
130011
+ }
130012
+ if (this.extendedBalance) {
130013
+ this.extendedBalance.availableForTrade = this.extendedBalance.availableForTrade.multipliedBy(mult);
130014
+ this.extendedBalance.availableForWithdrawal = this.extendedBalance.availableForWithdrawal.multipliedBy(mult);
130015
+ this.extendedBalance.unrealisedPnl = this.extendedBalance.unrealisedPnl.multipliedBy(mult);
129271
130016
  }
130017
+ this._recomputeUnusedBalance();
129272
130018
  }
129273
- // ── Read-only getters ────────────────────────────────────────────────
130019
+ get vesuPoolState() {
130020
+ assert3(this.vesuPoolStates.length === 1, "SolveBudget::vesuPoolState: vesuPoolStates length must be 1");
130021
+ return this.vesuPoolStates[0];
130022
+ }
130023
+ // ── Derived getters (buffered where applicable) ─────────────────────
130024
+ /** Buffered VA USD: strategy-asset slot + optional USDC slot. */
129274
130025
  get vaUsd() {
129275
- return this._vaUsd;
130026
+ return this.bufferedTokenUsd(this.vaultAssetBalance) + this.bufferedTokenUsd(this.vaultUsdcBalance);
130027
+ }
130028
+ /** Buffered USD in VA strategy-asset bucket only. */
130029
+ get vaAssetUsd() {
130030
+ return this.bufferedTokenUsd(this.vaultAssetBalance);
130031
+ }
130032
+ /** Buffered USD in VA USDC bucket (0 when asset === USDC). */
130033
+ get vaUsdcUsd() {
130034
+ return this.bufferedTokenUsd(this.vaultUsdcBalance);
129276
130035
  }
129277
130036
  get walletUsd() {
129278
- return this._walletUsd;
130037
+ return this.bufferedUsd(this._rawTokenUsd(this.walletBalance));
129279
130038
  }
129280
130039
  get vaWalletUsd() {
129281
- return this._vaUsd + this._walletUsd;
130040
+ return this.vaUsd + this.walletUsd;
129282
130041
  }
129283
130042
  get extAvailWithdraw() {
129284
- return this._extAvailWithdraw;
130043
+ const raw = this.extendedBalance?.availableForWithdrawal?.toNumber() ?? 0;
130044
+ return this.bufferedUsd(raw);
129285
130045
  }
129286
130046
  get extAvailUpnl() {
129287
- return this._extAvailUpnl;
130047
+ const raw = this.extendedBalance?.unrealisedPnl?.toNumber() ?? 0;
130048
+ return this.bufferedUsd(raw);
129288
130049
  }
130050
+ /**
130051
+ * Buffered Extended available-for-trade plus positive {@link ExtendedBalanceState.pendingDeposit}
130052
+ * (deposit in transit is usable the same way as the pre-buffer implementation).
130053
+ */
129289
130054
  get extAvailTrade() {
129290
- return this._extAvailTrade;
130055
+ const raw = this.extendedBalance?.availableForTrade?.toNumber() ?? 0;
130056
+ let v = this.bufferedUsd(raw);
130057
+ const pd = this.extendedBalance?.pendingDeposit?.toNumber() ?? 0;
130058
+ if (pd > 0) v += pd;
130059
+ return v;
130060
+ }
130061
+ get extPendingDeposit() {
130062
+ return this.extendedBalance?.pendingDeposit?.toNumber() ?? 0;
129291
130063
  }
130064
+ /**
130065
+ * Aggregate positive per-pool borrow headroom (USD). Repay/borrow routes update
130066
+ * {@link vesuPerPoolDebtDeltasToBorrow}; no separate counter.
130067
+ */
129292
130068
  get vesuBorrowCapacity() {
129293
- return this._vesuBorrowCapacity;
130069
+ return this.vesuPerPoolDebtDeltasToBorrow.reduce(
130070
+ (a, d) => a + Math.max(0, d.toNumber()),
130071
+ 0
130072
+ );
129294
130073
  }
130074
+ /** Diagnostic: buffered idle + positive debt delta + buffered Extended afT + in-flight deposit. */
129295
130075
  get totalUnused() {
129296
- return this._totalUnused;
129297
- }
129298
- get extPendingDeposit() {
129299
- return this._extPendingDeposit;
129300
- }
129301
- // ── Spend methods (return amount consumed, auto-decrement totalUnused)
129302
- spendVA(desired) {
129303
- const used = Math.min(this._vaUsd, Math.max(0, desired));
129304
- this._vaUsd -= used;
129305
- this._totalUnused -= used;
129306
- logger2.debug(`SolveBudget::spendVA used=${used}, vaUsd=${this._vaUsd}, totalUnused=${this._totalUnused}`);
129307
- return used;
130076
+ const debtSum = this.vesuPerPoolDebtDeltasToBorrow.reduce((a, b2) => a + b2.toNumber(), 0);
130077
+ let u = this.unusedBalance.reduce((a, b2) => a + this.bufferedTokenUsd(b2), 0);
130078
+ if (debtSum > 0) u += debtSum;
130079
+ const rawAft = this.extendedBalance?.availableForTrade?.toNumber() ?? 0;
130080
+ const aftBuf = this.bufferedUsd(rawAft);
130081
+ if (aftBuf > 0) u += aftBuf;
130082
+ const pd = this.extendedBalance?.pendingDeposit?.toNumber() ?? 0;
130083
+ if (pd > 0) u += pd;
130084
+ return u;
130085
+ }
130086
+ /** Sum of buffered USD across merged unused-balance rows (VA + wallet). */
130087
+ get unusedBalancesBufferedUsdSum() {
130088
+ return this.unusedBalance.reduce((a, b2) => a + this.bufferedTokenUsd(b2), 0);
130089
+ }
130090
+ /** Read-only snapshot view for validation / logging. */
130091
+ get unusedBalanceRows() {
130092
+ return this.unusedBalance;
130093
+ }
130094
+ /** Read-only Vesu pool view for solve computations. */
130095
+ get vesuPools() {
130096
+ return this.vesuPoolStates;
130097
+ }
130098
+ /** Read-only Extended positions view for solve computations. */
130099
+ get extendedPositionsView() {
130100
+ return this.extendedPositions;
130101
+ }
130102
+ /** Read-only Extended balance view for diagnostics / margin checks. */
130103
+ get extendedBalanceView() {
130104
+ return this.extendedBalance;
130105
+ }
130106
+ /** Current debt deltas per pool (positive=borrow, negative=repay). */
130107
+ get vesuDebtDeltas() {
130108
+ return this.vesuPerPoolDebtDeltasToBorrow;
130109
+ }
130110
+ /** Per-pool rebalance flags derived from target HF checks. */
130111
+ get vesuRebalanceFlags() {
130112
+ return this.shouldVesuRebalance;
130113
+ }
130114
+ /** Raw USD in VA (USDC slot + asset slot); spend caps when executing transfers. */
130115
+ _vaRawUsd() {
130116
+ return this._rawTokenUsd(this.vaultUsdcBalance) + this._rawTokenUsd(this.vaultAssetBalance);
130117
+ }
130118
+ _walletRawUsd() {
130119
+ return this._rawTokenUsd(this.walletBalance);
130120
+ }
130121
+ // ── Token snapshot helpers (keep vault / wallet / unusedBalance aligned) ─
130122
+ /** Remove up to `usd` notional from a token balance, scaling token amount proportionally. */
130123
+ _deductUsdFromTokenBalance(tb, usd) {
130124
+ if (usd <= 0) return;
130125
+ const take = Math.min(usd, tb.usdValue);
130126
+ if (take <= 0) return;
130127
+ const oldUsd = tb.usdValue;
130128
+ const newUsd = Math.max(0, oldUsd - take);
130129
+ tb.usdValue = newUsd;
130130
+ if (oldUsd <= 0) return;
130131
+ const ratio = newUsd / oldUsd;
130132
+ tb.amount = new Web3Number(
130133
+ (tb.amount.toNumber() * ratio).toFixed(tb.token.decimals),
130134
+ tb.token.decimals
130135
+ );
129308
130136
  }
129309
- addToVA(amount) {
129310
- assert3(amount >= 0, "SolveBudget::addToVA amount must be positive");
129311
- this._vaUsd += amount;
129312
- this._totalUnused += amount;
129313
- logger2.debug(`SolveBudget::addToVA amount=${amount}, vaUsd=${this._vaUsd}, totalUnused=${this._totalUnused}`);
129314
- }
129315
- spendWallet(desired) {
129316
- const used = Math.min(this._walletUsd, Math.max(0, desired));
129317
- this._walletUsd -= used;
129318
- this._totalUnused -= used;
129319
- logger2.debug(`SolveBudget::spendWallet used=${used}, walletUsd=${this._walletUsd}, totalUnused=${this._totalUnused}`);
129320
- return used;
130137
+ /** Add USD notional; infers price from current amount/usd when possible, else 1:1. */
130138
+ _addUsdToTokenBalance(tb, usd) {
130139
+ if (usd <= 0) return;
130140
+ const amtNum = tb.amount.toNumber();
130141
+ const price = amtNum > 0 && tb.usdValue > 0 ? tb.usdValue / amtNum : 1;
130142
+ const deltaTok = usd / price;
130143
+ tb.usdValue += usd;
130144
+ tb.amount = tb.amount.plus(
130145
+ new Web3Number(deltaTok.toFixed(tb.token.decimals), tb.token.decimals)
130146
+ );
129321
130147
  }
129322
- addToWallet(amount) {
129323
- assert3(amount >= 0, "SolveBudget::addToWallet amount must be positive");
129324
- this._walletUsd += amount;
129325
- this._totalUnused += amount;
129326
- logger2.debug(`SolveBudget::addToWallet amount=${amount}, walletUsd=${this._walletUsd}, totalUnused=${this._totalUnused}`);
130148
+ /**
130149
+ * Rebuilds {@link unusedBalance} from vault + wallet snapshots (same merge as refresh).
130150
+ */
130151
+ _recomputeUnusedBalance() {
130152
+ const balanceMap = /* @__PURE__ */ new Map();
130153
+ const merge2 = (b2) => {
130154
+ if (!b2) return;
130155
+ const key = b2.token.address.toString();
130156
+ const row = {
130157
+ token: b2.token,
130158
+ amount: new Web3Number(b2.amount.toFixed(b2.token.decimals), b2.token.decimals),
130159
+ usdValue: b2.usdValue
130160
+ };
130161
+ const existing = balanceMap.get(key);
130162
+ if (existing) {
130163
+ existing.amount = new Web3Number(
130164
+ existing.amount.plus(row.amount).toFixed(existing.token.decimals),
130165
+ existing.token.decimals
130166
+ );
130167
+ existing.usdValue += row.usdValue;
130168
+ } else {
130169
+ balanceMap.set(key, row);
130170
+ }
130171
+ };
130172
+ merge2(this.vaultAssetBalance);
130173
+ merge2(this.vaultUsdcBalance);
130174
+ merge2(this.walletBalance);
130175
+ this.unusedBalance = Array.from(balanceMap.values());
129327
130176
  }
129328
- spendVAWallet(desired) {
129329
- let remaining = Math.max(0, desired);
130177
+ // ── Spend methods (return amount consumed, auto-decrement totalUnused)
130178
+ /**
130179
+ * Spend VA **raw** USD (up to {@link vaRawUsd}). Prefer {@link vaultUsdcBalance} when present, then {@link vaultAssetBalance}.
130180
+ */
130181
+ spendVA(rawDesired) {
130182
+ const capRaw = this._vaRawUsd();
130183
+ const usedRaw = Math.min(capRaw, Math.max(0, rawDesired));
130184
+ if (usedRaw <= CASE_THRESHOLD_USD) return 0;
130185
+ let rem = usedRaw;
130186
+ if (rem > 0 && this.vaultUsdcBalance && this.vaultUsdcBalance.usdValue > 0) {
130187
+ const fromUsdc = Math.min(rem, this.vaultUsdcBalance.usdValue);
130188
+ this._deductUsdFromTokenBalance(this.vaultUsdcBalance, fromUsdc);
130189
+ rem -= fromUsdc;
130190
+ }
130191
+ if (rem > 0 && this.vaultAssetBalance) {
130192
+ this._deductUsdFromTokenBalance(this.vaultAssetBalance, rem);
130193
+ }
130194
+ this._recomputeUnusedBalance();
130195
+ logger2.debug(`SolveBudget::spendVA usedRaw=${usedRaw}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
130196
+ return usedRaw;
130197
+ }
130198
+ /**
130199
+ * Spend nominal/raw USD from VA (e.g. Vesu repay, on-chain USDC). Does not apply the safety buffer to the cap.
130200
+ */
130201
+ spendVaRawUsd(rawUsdDesired) {
130202
+ const capRaw = this._rawTokenUsd(this.vaultUsdcBalance) + this._rawTokenUsd(this.vaultAssetBalance);
130203
+ const usedRaw = Math.min(capRaw, Math.max(0, rawUsdDesired));
130204
+ if (usedRaw <= CASE_THRESHOLD_USD) return 0;
130205
+ let rem = usedRaw;
130206
+ if (rem > 0 && this.vaultUsdcBalance && this.vaultUsdcBalance.usdValue > 0) {
130207
+ const fromUsdc = Math.min(rem, this.vaultUsdcBalance.usdValue);
130208
+ this._deductUsdFromTokenBalance(this.vaultUsdcBalance, fromUsdc);
130209
+ rem -= fromUsdc;
130210
+ }
130211
+ if (rem > 0 && this.vaultAssetBalance) {
130212
+ this._deductUsdFromTokenBalance(this.vaultAssetBalance, rem);
130213
+ }
130214
+ this._recomputeUnusedBalance();
130215
+ logger2.debug(`SolveBudget::spendVaRawUsd usedRaw=${usedRaw}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
130216
+ return usedRaw;
130217
+ }
130218
+ /**
130219
+ * Add **raw nominal USD** to VA (borrow proceeds, wallet→VA in raw USDC, etc.).
130220
+ */
130221
+ addToVA(rawUsd) {
130222
+ assert3(rawUsd >= 0, "SolveBudget::addToVA amount must be positive");
130223
+ if (rawUsd === 0) return;
130224
+ if (this.vaultUsdcBalance) {
130225
+ this._addUsdToTokenBalance(this.vaultUsdcBalance, rawUsd);
130226
+ } else if (this.vaultAssetBalance) {
130227
+ this._addUsdToTokenBalance(this.vaultAssetBalance, rawUsd);
130228
+ }
130229
+ this._recomputeUnusedBalance();
130230
+ logger2.debug(`SolveBudget::addToVA rawUsd=${rawUsd}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
130231
+ }
130232
+ spendWallet(rawDesired) {
130233
+ const capRaw = this._walletRawUsd();
130234
+ const usedRaw = Math.min(capRaw, Math.max(0, rawDesired));
130235
+ if (usedRaw <= CASE_THRESHOLD_USD) return 0;
130236
+ if (this.walletBalance) {
130237
+ this._deductUsdFromTokenBalance(this.walletBalance, usedRaw);
130238
+ }
130239
+ this._recomputeUnusedBalance();
130240
+ logger2.debug(`SolveBudget::spendWallet usedRaw=${usedRaw}, walletUsd=${this.walletUsd}, totalUnused=${this.totalUnused}`);
130241
+ return usedRaw;
130242
+ }
130243
+ /** Add **raw nominal USD** to the operator wallet balance (e.g. Extended→wallet withdrawal). */
130244
+ addToWallet(rawUsd) {
130245
+ assert3(rawUsd >= 0, "SolveBudget::addToWallet amount must be positive");
130246
+ if (rawUsd === 0) return;
130247
+ if (this.walletBalance) {
130248
+ this._addUsdToTokenBalance(this.walletBalance, rawUsd);
130249
+ }
130250
+ this._recomputeUnusedBalance();
130251
+ logger2.debug(`SolveBudget::addToWallet rawUsd=${rawUsd}, walletUsd=${this.walletUsd}, totalUnused=${this.totalUnused}`);
130252
+ }
130253
+ spendVAWallet(rawDesired) {
130254
+ let remaining = Math.max(0, rawDesired);
129330
130255
  const vaSpent = this.spendVA(remaining);
129331
130256
  remaining -= vaSpent;
129332
130257
  const walletSpent = this.spendWallet(remaining);
129333
130258
  return vaSpent + walletSpent;
129334
130259
  }
129335
- _updateExtAvailWithdraw(desired, isSpend) {
129336
- let amount = desired;
129337
- assert3(amount > 0, "SolveBudget::_updateExtAvailWithdraw amount must be positive");
130260
+ _updateExtAvailWithdraw(desiredRaw, isSpend) {
130261
+ assert3(desiredRaw > 0, "SolveBudget::_updateExtAvailWithdraw amount must be positive");
130262
+ let rawDelta;
129338
130263
  if (isSpend) {
129339
- amount = Math.min(this._extAvailWithdraw, Math.max(0, desired));
129340
- amount = -amount;
130264
+ const capRaw = this.extendedBalance?.availableForWithdrawal?.toNumber() ?? 0;
130265
+ const useRaw = Math.min(capRaw, desiredRaw);
130266
+ if (useRaw <= CASE_THRESHOLD_USD) return 0;
130267
+ rawDelta = -useRaw;
130268
+ } else {
130269
+ rawDelta = desiredRaw;
129341
130270
  }
129342
- this._extAvailWithdraw += amount;
129343
- this._totalUnused += amount;
129344
- this._extAvailTrade += amount;
129345
130271
  if (this.extendedBalance) {
129346
- this.extendedBalance.availableForWithdrawal = safeUsdcWeb3Number(this.extendedBalance.availableForWithdrawal.toNumber() + amount);
129347
- this.extendedBalance.availableForTrade = safeUsdcWeb3Number(this.extendedBalance.availableForTrade.toNumber() + amount);
129348
- this.extendedBalance.balance = safeUsdcWeb3Number(this.extendedBalance.balance.toNumber() + amount);
129349
- this.extendedBalance.equity = safeUsdcWeb3Number(this.extendedBalance.equity.toNumber() + amount);
130272
+ this.extendedBalance.availableForWithdrawal = safeUsdcWeb3Number(this.extendedBalance.availableForWithdrawal.toNumber() + rawDelta);
130273
+ this.extendedBalance.availableForTrade = safeUsdcWeb3Number(this.extendedBalance.availableForTrade.toNumber() + rawDelta);
130274
+ this.extendedBalance.balance = safeUsdcWeb3Number(this.extendedBalance.balance.toNumber() + rawDelta);
130275
+ this.extendedBalance.equity = safeUsdcWeb3Number(this.extendedBalance.equity.toNumber() + rawDelta);
129350
130276
  }
129351
- logger2.debug(`SolveBudget::updateExtAvailWithdraw amount=${amount}, extAvailWithdraw=${this._extAvailWithdraw}, totalUnused=${this._totalUnused}`);
129352
- return amount;
130277
+ logger2.debug(`SolveBudget::updateExtAvailWithdraw rawDelta=${rawDelta}, extAvailWithdraw=${this.extAvailWithdraw}, totalUnused=${this.totalUnused}`);
130278
+ return rawDelta;
129353
130279
  }
129354
- _updateExtAvailUpnl(desired, isSpend) {
129355
- let amount = desired;
129356
- assert3(amount > 0, "SolveBudget::_updateExtAvailUpnl amount must be positive");
130280
+ _updateExtAvailUpnl(desiredRaw, isSpend) {
130281
+ assert3(desiredRaw > 0, "SolveBudget::_updateExtAvailUpnl amount must be positive");
130282
+ let rawDelta;
129357
130283
  if (isSpend) {
129358
- amount = Math.min(this._extAvailUpnl, Math.max(0, desired));
129359
- amount = -amount;
130284
+ const capRaw = this.extendedBalance?.unrealisedPnl?.toNumber() ?? 0;
130285
+ const useRaw = Math.min(capRaw, desiredRaw);
130286
+ if (useRaw <= CASE_THRESHOLD_USD) return 0;
130287
+ rawDelta = -useRaw;
130288
+ } else {
130289
+ rawDelta = desiredRaw;
129360
130290
  }
129361
- this._extAvailUpnl += amount;
129362
- this._totalUnused += amount;
129363
130291
  if (this.extendedBalance) {
129364
- this.extendedBalance.unrealisedPnl = safeUsdcWeb3Number(this.extendedBalance.unrealisedPnl.toNumber() + amount);
129365
- this.extendedBalance.balance = safeUsdcWeb3Number(this.extendedBalance.balance.toNumber() + amount);
129366
- this.extendedBalance.equity = safeUsdcWeb3Number(this.extendedBalance.equity.toNumber() + amount);
129367
- this.extendedBalance.availableForTrade = safeUsdcWeb3Number(this.extendedBalance.availableForTrade.toNumber() + amount);
129368
- }
129369
- logger2.debug(`SolveBudget::updateExtAvailUpnl amount=${amount}, extAvailUpnl=${this._extAvailUpnl}, totalUnused=${this._totalUnused}`);
129370
- return amount;
129371
- }
129372
- spendExtAvailTrade(desired) {
129373
- const used = this._updateExtAvailWithdraw(desired, true);
129374
- const usedUpnl = this._updateExtAvailUpnl(desired, true);
129375
- logger2.debug(`SolveBudget::updateExtAvailTrade amount=${used + usedUpnl}, extAvailTrade=${this._extAvailTrade}, totalUnused=${this._totalUnused}`);
130292
+ this.extendedBalance.unrealisedPnl = safeUsdcWeb3Number(this.extendedBalance.unrealisedPnl.toNumber() + rawDelta);
130293
+ this.extendedBalance.balance = safeUsdcWeb3Number(this.extendedBalance.balance.toNumber() + rawDelta);
130294
+ this.extendedBalance.equity = safeUsdcWeb3Number(this.extendedBalance.equity.toNumber() + rawDelta);
130295
+ this.extendedBalance.availableForTrade = safeUsdcWeb3Number(this.extendedBalance.availableForTrade.toNumber() + rawDelta);
130296
+ }
130297
+ logger2.debug(`SolveBudget::updateExtAvailUpnl rawDelta=${rawDelta}, extAvailUpnl=${this.extAvailUpnl}, totalUnused=${this.totalUnused}`);
130298
+ return rawDelta;
130299
+ }
130300
+ spendExtAvailTrade(rawDesired) {
130301
+ const used = this._updateExtAvailWithdraw(rawDesired, true);
130302
+ const usedUpnl = this._updateExtAvailUpnl(rawDesired, true);
130303
+ logger2.debug(`SolveBudget::updateExtAvailTrade rawSum=${used + usedUpnl}, extAvailTrade=${this.extAvailTrade}, totalUnused=${this.totalUnused}`);
130304
+ return used + usedUpnl;
130305
+ }
130306
+ // simply reduces available amounts, but maintains equity and balance.
130307
+ spendExtAvailTradeToEquityOnly(rawDesired) {
130308
+ const used = this._updateExtAvailWithdraw(rawDesired, true);
130309
+ const remaining = rawDesired - Math.abs(used);
130310
+ const usedUpnl = remaining > 0 ? this._updateExtAvailUpnl(remaining, true) : 0;
130311
+ if (this.extendedBalance) {
130312
+ const net = Math.abs(used) + Math.abs(usedUpnl);
130313
+ if (net.toFixed(0) != rawDesired.toFixed(0)) {
130314
+ throw new Error(`SolveBudget::spendExtAvailTradeToEquityOnly net=${net} != rawDesired=${rawDesired}`);
130315
+ }
130316
+ this.extendedBalance.equity = safeUsdcWeb3Number(this.extendedBalance.equity.toNumber() + net);
130317
+ this.extendedBalance.balance = safeUsdcWeb3Number(this.extendedBalance.balance.toNumber() + net);
130318
+ }
130319
+ logger2.debug(`SolveBudget::updateExtAvailTrade rawSum=${used + usedUpnl}, extAvailTrade=${this.extAvailTrade}, totalUnused=${this.totalUnused}`);
129376
130320
  return used + usedUpnl;
129377
130321
  }
129378
- spendExtAvailUpnl(desired) {
129379
- return this._updateExtAvailUpnl(desired, true);
130322
+ spendExtAvailWithdraw(rawDesired) {
130323
+ return this._updateExtAvailWithdraw(rawDesired, true);
129380
130324
  }
129381
- addToExtAvailTrade(amount) {
129382
- this._updateExtAvailWithdraw(amount, false);
129383
- logger2.debug(`SolveBudget::addToExtAvailTrade amount=${amount}, extAvailTrade=${this._extAvailTrade}, totalUnused=${this._totalUnused}`);
130325
+ spendExtAvailUpnl(rawDesired) {
130326
+ return this._updateExtAvailUpnl(rawDesired, true);
130327
+ }
130328
+ /**
130329
+ * Withdraw from Extended **withdrawal bucket only** to operator wallet (planning).
130330
+ * Used when VA must be funded from Extended and withdraw should be exhausted before unrealised PnL.
130331
+ */
130332
+ spendAvailWithdrawToWallet(rawDesired) {
130333
+ const want = Math.max(0, rawDesired);
130334
+ if (want <= CASE_THRESHOLD_USD) return 0;
130335
+ const rawDelta = this._updateExtAvailWithdraw(want, true);
130336
+ if (rawDelta === 0) return 0;
130337
+ const used = -rawDelta;
130338
+ this.addToWallet(used);
130339
+ return used;
130340
+ }
130341
+ /**
130342
+ * Required Extended equity (USD) for current open positions: total notional ÷ strategy leverage.
130343
+ * Same basis as {@link ExtendedSVKVesuStateManager._classifyLtvExtended} margin check.
130344
+ */
130345
+ _extendedMarginRequirementUsd() {
130346
+ const lev = calculateExtendedLevergae();
130347
+ if (lev <= 0 || this.extendedPositions.length === 0) return 0;
130348
+ const totalPosUsd = this.extendedPositions.reduce(
130349
+ (s, p) => s + p.valueUsd.toNumber(),
130350
+ 0
130351
+ );
130352
+ return totalPosUsd / lev;
130353
+ }
130354
+ /** How much more equity is needed before deposits should increase withdraw / trade availability. */
130355
+ _extendedEquityShortfallUsd() {
130356
+ if (!this.extendedBalance) return 0;
130357
+ const req = this._extendedMarginRequirementUsd();
130358
+ const eq = this.extendedBalance.equity.toNumber();
130359
+ return Math.max(0, req - eq);
130360
+ }
130361
+ /**
130362
+ * Credits a USDC inflow on Extended. Fills margin shortfall (balance+equity only) first;
130363
+ * any remainder is credited across balance, equity, availableForWithdrawal, and availableForTrade.
130364
+ */
130365
+ addToExtAvailTrade(rawUsd) {
130366
+ assert3(rawUsd >= 0, "SolveBudget::addToExtAvailTrade amount must be non-negative");
130367
+ if (rawUsd <= CASE_THRESHOLD_USD) return;
130368
+ if (!this.extendedBalance) {
130369
+ logger2.warn("SolveBudget::addToExtAvailTrade skipped \u2014 no extendedBalance");
130370
+ return;
130371
+ }
130372
+ const shortfall = this._extendedEquityShortfallUsd();
130373
+ const toMargin = Math.min(rawUsd, shortfall);
130374
+ const toLiquid = rawUsd - toMargin;
130375
+ if (toMargin > CASE_THRESHOLD_USD) {
130376
+ const b2 = this.extendedBalance.balance.toNumber();
130377
+ const e = this.extendedBalance.equity.toNumber();
130378
+ this.extendedBalance.balance = safeUsdcWeb3Number(b2 + toMargin);
130379
+ this.extendedBalance.equity = safeUsdcWeb3Number(e + toMargin);
130380
+ logger2.debug(
130381
+ `SolveBudget::addToExtAvailTrade margin-first rawUsd=${toMargin} (shortfallBefore=${shortfall}, balance=${b2 + toMargin}, equity=${e + toMargin})`
130382
+ );
130383
+ }
130384
+ if (toLiquid > CASE_THRESHOLD_USD) {
130385
+ this._updateExtAvailWithdraw(toLiquid, false);
130386
+ }
130387
+ logger2.debug(
130388
+ `SolveBudget::addToExtAvailTrade total rawUsd=${rawUsd} toLiquid=${toLiquid} extAvailTrade=${this.extAvailTrade}, totalUnused=${this.totalUnused}`
130389
+ );
129384
130390
  }
129385
130391
  spendVesuBorrowCapacity(desired) {
129386
- const used = Math.min(this._vesuBorrowCapacity, Math.max(0, desired));
129387
- this._vesuBorrowCapacity -= used;
129388
- this._totalUnused -= used;
130392
+ const used = Math.min(this.vesuBorrowCapacity, Math.max(0, desired));
129389
130393
  let spendsByPool = [];
129390
130394
  for (let index = 0; index < this.vesuPerPoolDebtDeltasToBorrow.length; index++) {
129391
130395
  const d = this.vesuPerPoolDebtDeltasToBorrow[index];
@@ -129396,13 +130400,12 @@ spurious results.`);
129396
130400
  this.vesuPoolStates[index].debtUsdValue = this.vesuPoolStates[index].debtAmount.toNumber() * this.vesuPoolStates[index].debtPrice;
129397
130401
  spendsByPool.push({ poolId: this.vesuPoolStates[index].poolId, amount: safeUsdcWeb3Number(borrowed), collateralToken: this.vesuPoolStates[index].collateralToken, debtToken: this.vesuPoolStates[index].debtToken });
129398
130402
  }
129399
- logger2.debug(`SolveBudget::spendVesuBorrowCapacity used=${used}, vesuBorrowCapacity=${this._vesuBorrowCapacity}, totalUnused=${this._totalUnused}`);
130403
+ logger2.debug(`SolveBudget::spendVesuBorrowCapacity used=${used}, vesuBorrowCapacity=${this.vesuBorrowCapacity}, totalUnused=${this.totalUnused}`);
129400
130404
  return { used, spendsByPool };
129401
130405
  }
129402
130406
  repayVesuBorrowCapacity(desired) {
129403
130407
  assert3(desired > 0, "SolveBudget::repayVesuBorrowCapacity desired must be positive");
129404
130408
  const used = desired;
129405
- this._vesuBorrowCapacity += used;
129406
130409
  const spendsByPool = [];
129407
130410
  for (let index = 0; index < this.vesuPerPoolDebtDeltasToBorrow.length; index++) {
129408
130411
  const d = this.vesuPerPoolDebtDeltasToBorrow[index];
@@ -129412,7 +130415,7 @@ spurious results.`);
129412
130415
  this.vesuPoolStates[index].debtAmount = safeUsdcWeb3Number(this.vesuPoolStates[index].debtAmount.toNumber() - repaid);
129413
130416
  spendsByPool.push({ poolId: this.vesuPoolStates[index].poolId, amount: safeUsdcWeb3Number(-repaid), collateralToken: this.vesuPoolStates[index].collateralToken, debtToken: this.vesuPoolStates[index].debtToken });
129414
130417
  }
129415
- logger2.debug(`SolveBudget::repayVesuBorrowCapacity used=${used}, vesuBorrowCapacity=${this._vesuBorrowCapacity}, totalUnused=${this._totalUnused}`);
130418
+ logger2.debug(`SolveBudget::repayVesuBorrowCapacity used=${used}, vesuBorrowCapacity=${this.vesuBorrowCapacity}, totalUnused=${this.totalUnused}`);
129416
130419
  return { used, spendsByPool };
129417
130420
  }
129418
130421
  // ── State mutation ──────────────────────────────────────────────────
@@ -129434,13 +130437,21 @@ spurious results.`);
129434
130437
  pool.debtUsdValue = pool.debtAmount.toNumber() * pool.debtPrice;
129435
130438
  const vesuPerPoolDebtDeltasToBorrow = this._computeperPoolDebtDeltasToBorrow();
129436
130439
  this.vesuPerPoolDebtDeltasToBorrow = vesuPerPoolDebtDeltasToBorrow.map((item) => item.deltaDebt);
129437
- const sum = this.vesuPerPoolDebtDeltasToBorrow.reduce((a, b2) => a.plus(b2), new Web3Number(0, USDC_TOKEN_DECIMALS));
129438
- this._vesuBorrowCapacity = sum.toNumber();
129439
130440
  this.shouldVesuRebalance = vesuPerPoolDebtDeltasToBorrow.map((item) => item.shouldRebalance);
129440
130441
  }
129441
- /** Update an Extended position's size after a lever route. Creates the position if new. */
129442
- applyExtendedExposureDelta(instrument, sizeDelta) {
129443
- const pos = this.extendedPositions.find((p) => p.instrument === instrument);
130442
+ /**
130443
+ * Update Extended position size after a lever route; sync {@link ExtendedPositionState.valueUsd}
130444
+ * and margin buckets (released USD on decrease, locked USD on increase).
130445
+ *
130446
+ * @param collateralPriceUsd BTC collateral price for notional / margin math; if omitted, uses
130447
+ * existing valueUsd / |size| or the first Vesu pool collateral price.
130448
+ */
130449
+ applyExtendedExposureDelta(instrument, sizeDelta, collateralPriceUsd) {
130450
+ const btcEps = 10 ** -COLLATERAL_PRECISION;
130451
+ const lev = calculateExtendedLevergae();
130452
+ let pos = this.extendedPositions.find((p) => p.instrument === instrument);
130453
+ const oldAbs = pos ? Math.abs(pos.size.toNumber()) : 0;
130454
+ const oldValUsd = pos ? pos.valueUsd.toNumber() : 0;
129444
130455
  if (pos) {
129445
130456
  pos.size = new Web3Number(pos.size.plus(sizeDelta).toFixed(8), 8);
129446
130457
  } else if (sizeDelta.toNumber() !== 0) {
@@ -129451,6 +130462,56 @@ spurious results.`);
129451
130462
  valueUsd: new Web3Number(0, USDC_TOKEN_DECIMALS),
129452
130463
  leverage: "0"
129453
130464
  });
130465
+ pos = this.extendedPositions[this.extendedPositions.length - 1];
130466
+ } else {
130467
+ return;
130468
+ }
130469
+ const newAbs = Math.abs(pos.size.toNumber());
130470
+ let price = collateralPriceUsd;
130471
+ if (price === void 0 || price <= 0) {
130472
+ price = oldAbs > btcEps ? oldValUsd / oldAbs : this.vesuPoolStates[0]?.collateralPrice ?? 0;
130473
+ }
130474
+ if (price > 0) {
130475
+ pos.valueUsd = new Web3Number(
130476
+ (newAbs * price).toFixed(USDC_TOKEN_DECIMALS),
130477
+ USDC_TOKEN_DECIMALS
130478
+ );
130479
+ }
130480
+ if (!this.extendedBalance || lev <= 0 || price <= 0) return;
130481
+ const dAbs = newAbs - oldAbs;
130482
+ if (dAbs < -btcEps) {
130483
+ const releasedUsd = -dAbs * price / lev;
130484
+ if (releasedUsd > CASE_THRESHOLD_USD) {
130485
+ this.addToExtAvailTrade(releasedUsd);
130486
+ }
130487
+ } else if (dAbs > btcEps) {
130488
+ const lockedUsd = dAbs * price / lev;
130489
+ if (lockedUsd > CASE_THRESHOLD_USD) {
130490
+ this._lockExtendedMarginUsd(lockedUsd);
130491
+ }
130492
+ }
130493
+ }
130494
+ /** Pull margin for larger Extended exposure from liquid buckets, then balance/equity. */
130495
+ _lockExtendedMarginUsd(lockedUsd) {
130496
+ if (lockedUsd <= CASE_THRESHOLD_USD || !this.extendedBalance) return;
130497
+ let rem = lockedUsd;
130498
+ const uw = Math.min(rem, Math.max(0, this.extendedBalance.availableForWithdrawal.toNumber()));
130499
+ if (uw > 0) {
130500
+ this._updateExtAvailWithdraw(uw, true);
130501
+ rem -= uw;
130502
+ }
130503
+ if (rem > 0) {
130504
+ const uu = Math.min(rem, Math.max(0, this.extendedBalance.unrealisedPnl.toNumber()));
130505
+ if (uu > 0) {
130506
+ this._updateExtAvailUpnl(uu, true);
130507
+ rem -= uu;
130508
+ }
130509
+ }
130510
+ if (rem > 0) {
130511
+ const b2 = this.extendedBalance.balance.toNumber();
130512
+ const e = this.extendedBalance.equity.toNumber();
130513
+ this.extendedBalance.balance = safeUsdcWeb3Number(b2 - rem);
130514
+ this.extendedBalance.equity = safeUsdcWeb3Number(e - rem);
129454
130515
  }
129455
130516
  }
129456
130517
  /**
@@ -129478,6 +130539,23 @@ spurious results.`);
129478
130539
  return output2;
129479
130540
  }
129480
130541
  };
130542
+ function createSolveBudgetFromRawState(params) {
130543
+ const budget = new SolveBudget({
130544
+ assetToken: params.assetToken,
130545
+ usdcToken: params.usdcToken,
130546
+ unusedBalance: params.unusedBalance,
130547
+ walletBalance: params.walletBalance,
130548
+ vaultAssetBalance: params.vaultAssetBalance,
130549
+ vaultUsdcBalance: params.vaultUsdcBalance,
130550
+ extendedPositions: params.extendedPositions,
130551
+ extendedBalance: params.extendedBalance,
130552
+ vesuPoolStates: params.vesuPoolStates
130553
+ });
130554
+ if (params.limitBalanceBufferFactor && params.limitBalanceBufferFactor > 0) {
130555
+ budget.applyBuffer(params.limitBalanceBufferFactor);
130556
+ }
130557
+ return budget;
130558
+ }
129481
130559
  var ExtendedSVKVesuStateManager = class {
129482
130560
  constructor(config3) {
129483
130561
  this._tag = "ExtendedSVKVesuStateManager";
@@ -129513,7 +130591,7 @@ spurious results.`);
129513
130591
  vesuAllocationUsd: safeUsdcWeb3Number(0),
129514
130592
  extendedAllocationUsd: safeUsdcWeb3Number(0),
129515
130593
  bringLiquidityAmount: safeUsdcWeb3Number(0),
129516
- pendingDeposit: this._budget.extendedBalance?.pendingDeposit ?? new Web3Number(0, USDC_TOKEN_DECIMALS)
130594
+ pendingDeposit: safeUsdcWeb3Number(0)
129517
130595
  };
129518
130596
  this._logSolveResult(result2);
129519
130597
  return result2;
@@ -129528,30 +130606,44 @@ spurious results.`);
129528
130606
  async _refresh() {
129529
130607
  logger2.info(`${this._tag}::_refresh starting`);
129530
130608
  const [
129531
- vaultAllocatorBalance,
130609
+ vaultAssetBalance,
130610
+ vaultUsdcBalance,
129532
130611
  walletBalance,
129533
130612
  vesuPoolStates,
129534
130613
  extendedBalance,
129535
130614
  extendedPositions
129536
130615
  ] = await Promise.all([
129537
- this._fetchVaultAllocatorBalance(),
130616
+ this._fetchVaultAllocatorAssetBalance(),
130617
+ this._fetchVaultAllocatorUsdcBalanceIfDistinct(),
129538
130618
  this._fetchWalletBalances(),
129539
130619
  this._fetchAllVesuPoolStates(),
129540
130620
  this._fetchExtendedBalance(),
129541
130621
  this._fetchExtendedPositions()
129542
130622
  ]);
129543
- logger2.verbose(`_refresh vaultAllocatorBalance: ${vaultAllocatorBalance.usdValue}, walletBalance: ${walletBalance.usdValue}`);
130623
+ logger2.verbose(
130624
+ `${this._tag}::_refresh VA asset ${vaultAssetBalance.token.symbol}=$${vaultAssetBalance.usdValue.toFixed(2)}${vaultUsdcBalance ? `, VA USDC=$${vaultUsdcBalance.usdValue.toFixed(2)}` : ""}, wallet=${walletBalance.usdValue}`
130625
+ );
129544
130626
  const unusedBalance = this._computeUnusedBalances(
129545
- vaultAllocatorBalance,
130627
+ vaultAssetBalance,
130628
+ vaultUsdcBalance,
129546
130629
  walletBalance
129547
130630
  );
129548
- this._budget = new SolveBudget({
129549
- limitBalanceBufferFactor: this._config.limitBalanceBufferFactor,
130631
+ this._budget = createSolveBudgetFromRawState({
130632
+ assetToken: this._config.assetToken,
130633
+ usdcToken: this._config.usdcToken,
129550
130634
  unusedBalance,
129551
130635
  walletBalance,
129552
- vaultBalance: vaultAllocatorBalance,
130636
+ vaultAssetBalance,
130637
+ vaultUsdcBalance,
129553
130638
  extendedPositions,
129554
- extendedBalance,
130639
+ extendedBalance: {
130640
+ availableForTrade: extendedBalance?.availableForTrade || new Web3Number(0, USDC_TOKEN_DECIMALS),
130641
+ availableForWithdrawal: extendedBalance?.availableForWithdrawal || new Web3Number(0, USDC_TOKEN_DECIMALS),
130642
+ unrealisedPnl: extendedBalance?.unrealisedPnl || new Web3Number(0, USDC_TOKEN_DECIMALS),
130643
+ balance: extendedBalance?.balance || new Web3Number(0, USDC_TOKEN_DECIMALS),
130644
+ equity: extendedBalance?.equity || new Web3Number(0, USDC_TOKEN_DECIMALS),
130645
+ pendingDeposit: extendedBalance?.pendingDeposit || new Web3Number(0, USDC_TOKEN_DECIMALS)
130646
+ },
129555
130647
  vesuPoolStates
129556
130648
  });
129557
130649
  const totalUnusedUsd = unusedBalance.reduce(
@@ -129563,10 +130655,14 @@ spurious results.`);
129563
130655
  );
129564
130656
  }
129565
130657
  // todo add communication check with python server of extended. if not working, throw error in solve function.
130658
+ /** True when strategy asset and USDC share one token — VA USDC slot is unused (all in asset balance). */
130659
+ _vaultAssetAndUsdcAreSameToken() {
130660
+ return this._config.assetToken.address.eq(this._config.usdcToken.address);
130661
+ }
129566
130662
  /**
129567
- * Reads the asset-token balance sitting idle in the vault allocator contract.
130663
+ * Reads the {@link StateManagerConfig.assetToken} balance idle in the vault allocator.
129568
130664
  */
129569
- async _fetchVaultAllocatorBalance() {
130665
+ async _fetchVaultAllocatorAssetBalance() {
129570
130666
  const { assetToken, vaultAllocator, networkConfig, pricer } = this._config;
129571
130667
  const balance = await new ERC20(networkConfig).balanceOf(
129572
130668
  assetToken.address,
@@ -129578,20 +130674,38 @@ spurious results.`);
129578
130674
  return { token: assetToken, amount: balance, usdValue };
129579
130675
  }
129580
130676
  /**
129581
- * Merges the vault-allocator balance and wallet balances into a
129582
- * deduplicated array of TokenBalance entries keyed by token address.
129583
- *
129584
- * e.g. VA has USDC, wallet has USDC + USDC.e → returns
129585
- * [{ token: USDC, amount: VA+wallet, usdValue: … },
129586
- * { token: USDC.e, amount: wallet, usdValue: }]
130677
+ * Reads {@link StateManagerConfig.usdcToken} in the vault allocator when it differs from
130678
+ * {@link StateManagerConfig.assetToken}. Otherwise returns null (treat VA USDC as 0; stablecoin is only under asset).
130679
+ */
130680
+ async _fetchVaultAllocatorUsdcBalanceIfDistinct() {
130681
+ if (this._vaultAssetAndUsdcAreSameToken()) return null;
130682
+ const { usdcToken, vaultAllocator, networkConfig, pricer } = this._config;
130683
+ const balance = await new ERC20(networkConfig).balanceOf(
130684
+ usdcToken.address,
130685
+ vaultAllocator,
130686
+ usdcToken.decimals
130687
+ );
130688
+ const tokenPrice = await pricer.getPrice(
130689
+ usdcToken.priceProxySymbol || usdcToken.symbol
130690
+ );
130691
+ const usdValue = Number(balance.toFixed(usdcToken.decimals)) * tokenPrice.price;
130692
+ return { token: usdcToken, amount: balance, usdValue };
130693
+ }
130694
+ /**
130695
+ * Merges vault-allocator asset, optional vault-allocator USDC, and operator wallet
130696
+ * balances into entries keyed by token address.
129587
130697
  */
129588
- _computeUnusedBalances(vaultAllocatorBalance, walletBalance) {
130698
+ _computeUnusedBalances(vaultAssetBalance, vaultUsdcBalance, walletBalance) {
129589
130699
  const balanceMap = /* @__PURE__ */ new Map();
129590
- balanceMap.set(vaultAllocatorBalance.token.address.toString(), {
129591
- token: vaultAllocatorBalance.token,
129592
- amount: vaultAllocatorBalance.amount,
129593
- usdValue: vaultAllocatorBalance.usdValue
129594
- });
130700
+ const put = (tb) => {
130701
+ balanceMap.set(tb.token.address.toString(), {
130702
+ token: tb.token,
130703
+ amount: tb.amount,
130704
+ usdValue: tb.usdValue
130705
+ });
130706
+ };
130707
+ put(vaultAssetBalance);
130708
+ if (vaultUsdcBalance) put(vaultUsdcBalance);
129595
130709
  const key = walletBalance.token.address.toString();
129596
130710
  const existing = balanceMap.get(key);
129597
130711
  if (existing) {
@@ -129610,30 +130724,29 @@ spurious results.`);
129610
130724
  return Array.from(balanceMap.values());
129611
130725
  }
129612
130726
  /**
129613
- * Reads the operator wallet's balances for the asset token (USDC.e) and
129614
- * USDC.e (needed for route computation P1 vs P2 decision for Extended deposits).
130727
+ * Reads the operator wallet balance for {@link StateManagerConfig.usdcToken} only
130728
+ * (wallet stablecoin is always USDC, regardless of strategy {@link StateManagerConfig.assetToken}).
129615
130729
  */
129616
130730
  async _fetchWalletBalances() {
129617
130731
  const {
129618
130732
  networkConfig,
129619
130733
  pricer,
129620
130734
  walletAddress,
129621
- assetToken,
129622
- usdceToken
130735
+ usdcToken
129623
130736
  } = this._config;
129624
130737
  const erc20 = new ERC20(networkConfig);
129625
- const [usdceBalance, usdcePrice] = await Promise.all([
130738
+ const [balance, tokenPrice] = await Promise.all([
129626
130739
  erc20.balanceOf(
129627
- usdceToken.address,
130740
+ usdcToken.address,
129628
130741
  walletAddress,
129629
- usdceToken.decimals
130742
+ usdcToken.decimals
129630
130743
  ),
129631
- pricer.getPrice(usdceToken.priceProxySymbol || usdceToken.symbol)
130744
+ pricer.getPrice(usdcToken.priceProxySymbol || usdcToken.symbol)
129632
130745
  ]);
129633
130746
  return {
129634
- token: usdceToken,
129635
- amount: usdceBalance,
129636
- usdValue: Number(usdceBalance.toFixed(usdceToken.decimals)) * usdcePrice.price
130747
+ token: usdcToken,
130748
+ amount: balance,
130749
+ usdValue: Number(balance.toFixed(usdcToken.decimals)) * tokenPrice.price
129637
130750
  };
129638
130751
  }
129639
130752
  /**
@@ -129751,12 +130864,12 @@ spurious results.`);
129751
130864
  * finite, sensible values. Throws on invalid state.
129752
130865
  */
129753
130866
  _validateRefreshedState() {
129754
- if (this._budget.unusedBalance.length === 0) {
130867
+ if (this._budget.unusedBalanceRows.length === 0) {
129755
130868
  throw new Error(
129756
130869
  `${this._tag}: unusedBalance is empty after refresh`
129757
130870
  );
129758
130871
  }
129759
- for (const balance of this._budget.unusedBalance) {
130872
+ for (const balance of this._budget.unusedBalanceRows) {
129760
130873
  this._validateTokenBalanceOrThrow(
129761
130874
  balance,
129762
130875
  `unusedBalance[${balance.token.symbol}]`
@@ -129776,7 +130889,7 @@ spurious results.`);
129776
130889
  }
129777
130890
  }
129778
130891
  _validateVesuPoolPricesOrThrow() {
129779
- for (const pool of this._budget.vesuPoolStates) {
130892
+ for (const pool of this._budget.vesuPools) {
129780
130893
  const poolLabel = pool.poolId.shortString();
129781
130894
  this._assertPositiveFinite(
129782
130895
  pool.collateralPrice,
@@ -129789,8 +130902,8 @@ spurious results.`);
129789
130902
  }
129790
130903
  }
129791
130904
  _validateExtendedBalanceOrThrow() {
129792
- if (!this._budget.extendedBalance) return;
129793
- const { equity, availableForTrade } = this._budget.extendedBalance;
130905
+ if (!this._budget.extendedBalanceView) return;
130906
+ const { equity, availableForTrade } = this._budget.extendedBalanceView;
129794
130907
  if (!Number.isFinite(equity.toNumber()) || !Number.isFinite(availableForTrade.toNumber())) {
129795
130908
  throw new Error(
129796
130909
  `${this._tag}: Extended balance contains non-finite values \u2014 equity: ${equity.toNumber()}, availableForTrade: ${availableForTrade.toNumber()}`
@@ -129829,18 +130942,26 @@ spurious results.`);
129829
130942
  );
129830
130943
  }
129831
130944
  /**
129832
- * Total investable = vault allocator balance + Extended available-for-trade,
129833
- * both reduced by the configured buffer percentage.
130945
+ * Total investable = vault allocator balance + Extended available-for-trade +
130946
+ * buffered unrealised PnL, matching `deposit_cases_extended_vesu.xlsx`:
130947
+ * `(VA + wallet + EXT_WITH_AVL + EXT_UPNL) * (1 − buffer)`.
130948
+ * Positive {@link ExtendedBalanceState.pendingDeposit} stays on the afT leg only (see {@link SolveBudget.extAvailTrade}).
129834
130949
  */
129835
130950
  _computeTotalInvestableAmount() {
129836
- const totalUnusedUsd = this._budget.unusedBalance.reduce(
129837
- (acc, b2) => acc + b2.usdValue,
129838
- 0
129839
- );
130951
+ const totalUnusedUsd = this._budget.unusedBalancesBufferedUsdSum;
129840
130952
  logger2.debug(
129841
- `${this._tag}::_computeTotalInvestableAmount unusedBalances=${JSON.stringify(this._budget.unusedBalance.map((b2) => ({ token: b2.token.symbol, amount: b2.amount.toNumber(), usdValue: b2.usdValue })))}`
130953
+ `${this._tag}::_computeTotalInvestableAmount unusedBalances=${JSON.stringify(this._budget.unusedBalanceRows.map((b2) => ({ token: b2.token.symbol, amount: b2.amount.toNumber(), usdValue: b2.usdValue })))}`
130954
+ );
130955
+ const extBal = this._budget.extendedBalanceView;
130956
+ const rawAft = extBal?.availableForWithdrawal?.toNumber() ?? 0;
130957
+ const rawUpnl = extBal?.unrealisedPnl?.toNumber() ?? 0;
130958
+ let extBuffered = this._budget.bufferedUsd(rawAft) + this._budget.bufferedUsd(rawUpnl);
130959
+ const pd = extBal?.pendingDeposit?.toNumber() ?? 0;
130960
+ if (pd > 0) extBuffered += pd;
130961
+ const extendedAvailable = new Web3Number(
130962
+ extBuffered.toFixed(USDC_TOKEN_DECIMALS),
130963
+ USDC_TOKEN_DECIMALS
129842
130964
  );
129843
- const extendedAvailable = this._budget.extendedBalance?.availableForTrade ?? new Web3Number(0, USDC_TOKEN_DECIMALS);
129844
130965
  logger2.verbose(`_computeTotalInvestableAmount totalUnusedUsd: ${totalUnusedUsd}, extendedAvailable: ${extendedAvailable.toNumber()}`);
129845
130966
  return new Web3Number(totalUnusedUsd.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS).plus(extendedAvailable);
129846
130967
  }
@@ -129871,7 +130992,7 @@ spurious results.`);
129871
130992
  if (denominator === 0) {
129872
130993
  throw new Error(`${this._tag}: Denominator is zero`);
129873
130994
  }
129874
- const collateralPrice = this._budget.vesuPoolStates[0]?.collateralPrice ?? 0;
130995
+ const collateralPrice = this._budget.vesuPools[0]?.collateralPrice ?? 0;
129875
130996
  const totalVesuExposureUsd = this._totalVesuCollateralUsd().plus(new Web3Number((deltaVesuCollateral * collateralPrice).toFixed(6), USDC_TOKEN_DECIMALS));
129876
130997
  const totalExtendedExposureUsd = this._totalExtendedExposureUsd().plus(new Web3Number((deltaExtendedCollateral * collateralPrice).toFixed(6), USDC_TOKEN_DECIMALS));
129877
130998
  const numerator = vesuLeverage * distributableAmount.toNumber() + totalVesuExposureUsd.toNumber() - totalExtendedExposureUsd.toNumber();
@@ -129883,8 +131004,6 @@ spurious results.`);
129883
131004
  distributableAmount.minus(extendedAllocationUsd).toFixed(USDC_TOKEN_DECIMALS),
129884
131005
  USDC_TOKEN_DECIMALS
129885
131006
  );
129886
- const perPoolDebtDeltasToBorrow = this._budget.vesuPerPoolDebtDeltasToBorrow;
129887
- vesuAllocationUsd = vesuAllocationUsd.plus(this._sumDebtDeltas(perPoolDebtDeltasToBorrow).multipliedBy(-1));
129888
131007
  let vesuPositionDelta = Number(new Web3Number((vesuAllocationUsd.toNumber() * vesuLeverage / collateralPrice).toFixed(6), 6).toFixedRoundDown(COLLATERAL_PRECISION));
129889
131008
  let extendedPositionDelta = Number(new Web3Number((extendedAllocationUsd.toNumber() * extendedLeverage / collateralPrice).toFixed(6), 6).toFixedRoundDown(COLLATERAL_PRECISION));
129890
131009
  if (!isRecursive) {
@@ -129903,14 +131022,17 @@ spurious results.`);
129903
131022
  * by existing collateral value, then converts each share to collateral
129904
131023
  * token units.
129905
131024
  */
129906
- _computePerPoolCollateralDeltas(vesuAllocationUsd, perPoolDebtDeltasToBorrow) {
131025
+ _computePerPoolCollateralDeltas(vesuAllocationUsd) {
129907
131026
  const vesuLeverage = calculateVesuLeverage();
129908
- const availableVesuCollateralAllocationUsd = vesuAllocationUsd.plus(this._sumDebtDeltas(perPoolDebtDeltasToBorrow));
131027
+ const availableVesuCollateralAllocationUsd = vesuAllocationUsd;
129909
131028
  const postLeverageAllocationUsd = availableVesuCollateralAllocationUsd.multipliedBy(vesuLeverage);
129910
131029
  const totalCollateralExisting = this._totalVesuCollateral();
129911
- return this._budget.vesuPoolStates.map((pool, index) => {
131030
+ return this._budget.vesuPools.map((pool, index) => {
129912
131031
  const _postLeverageAllocation = postLeverageAllocationUsd.dividedBy(pool.collateralPrice);
129913
- const postLeverageAllocation = new Web3Number(_postLeverageAllocation.plus(totalCollateralExisting).toFixed(COLLATERAL_PRECISION), pool.collateralToken.decimals).minus(totalCollateralExisting);
131032
+ const postLeverageAllocation = new Web3Number(
131033
+ _postLeverageAllocation.plus(totalCollateralExisting).toFixedRoundDown(COLLATERAL_PRECISION),
131034
+ pool.collateralToken.decimals
131035
+ ).minus(totalCollateralExisting);
129914
131036
  const _poolCollateralDelta = this._computePoolCollateralShare(
129915
131037
  pool,
129916
131038
  totalCollateralExisting,
@@ -129920,12 +131042,12 @@ spurious results.`);
129920
131042
  _poolCollateralDelta.toFixed(COLLATERAL_PRECISION),
129921
131043
  pool.collateralToken.decimals
129922
131044
  );
129923
- const newDebt = postLeverageAllocationUsd.minus(availableVesuCollateralAllocationUsd).dividedBy(pool.debtPrice);
131045
+ const newDebt = postLeverageAllocation.multipliedBy(pool.collateralPrice).minus(availableVesuCollateralAllocationUsd).dividedBy(pool.debtPrice);
129924
131046
  return {
129925
131047
  poolId: pool.poolId,
129926
131048
  collateralToken: pool.collateralToken,
129927
131049
  debtToken: pool.debtToken,
129928
- debtDelta: perPoolDebtDeltasToBorrow[index].plus(newDebt),
131050
+ debtDelta: newDebt,
129929
131051
  collateralDelta: poolCollateralDelta,
129930
131052
  collateralPrice: pool.collateralPrice,
129931
131053
  debtPrice: pool.debtPrice
@@ -129938,7 +131060,7 @@ spurious results.`);
129938
131060
  * Multi-pool cases split proportionally by current collateral USD value.
129939
131061
  */
129940
131062
  _computePoolCollateralShare(pool, totalCollateral, totalVesuAllocation) {
129941
- const isSinglePoolOrZeroTotal = this._budget.vesuPoolStates.length === 1 || totalCollateral.toNumber() === 0;
131063
+ const isSinglePoolOrZeroTotal = this._budget.vesuPools.length === 1 || totalCollateral.toNumber() === 0;
129942
131064
  if (isSinglePoolOrZeroTotal) return totalVesuAllocation;
129943
131065
  const poolWeight = pool.collateralAmount.dividedBy(totalCollateral);
129944
131066
  return new Web3Number(
@@ -129975,8 +131097,8 @@ spurious results.`);
129975
131097
  */
129976
131098
  _computeTargetExtendedExposure(vesuDeltas) {
129977
131099
  let totalExposureCollateral = new Web3Number(0, USDC_TOKEN_DECIMALS);
129978
- for (let i = 0; i < this._budget.vesuPoolStates.length; i++) {
129979
- const pool = this._budget.vesuPoolStates[i];
131100
+ for (let i = 0; i < this._budget.vesuPools.length; i++) {
131101
+ const pool = this._budget.vesuPools[i];
129980
131102
  const delta = vesuDeltas[i];
129981
131103
  logger2.debug(
129982
131104
  `${this._tag}::_computeTargetExtendedExposure poolId=${pool.poolId.toString()}, collateralAmount=${pool.collateralAmount.toNumber()}, collateralDelta=${delta.collateralDelta.toNumber()}`
@@ -129994,7 +131116,7 @@ spurious results.`);
129994
131116
  );
129995
131117
  }
129996
131118
  _hasNoExtendedPositions() {
129997
- return this._budget.extendedPositions.length === 0;
131119
+ return this._budget.extendedPositionsView.length === 0;
129998
131120
  }
129999
131121
  /**
130000
131122
  * Creates a single-element delta array for the default instrument
@@ -130005,7 +131127,7 @@ spurious results.`);
130005
131127
  {
130006
131128
  instrument: this._config.extendedAdapter.config.extendedMarketName,
130007
131129
  delta: new Web3Number(
130008
- delta.toFixed(COLLATERAL_PRECISION),
131130
+ delta.toFixedRoundDown(COLLATERAL_PRECISION),
130009
131131
  USDC_TOKEN_DECIMALS
130010
131132
  )
130011
131133
  }
@@ -130017,7 +131139,7 @@ spurious results.`);
130017
131139
  */
130018
131140
  _distributeExposureDeltaAcrossPositions(totalDelta) {
130019
131141
  const totalExposure = this._totalExtendedExposure();
130020
- return this._budget.extendedPositions.map((position) => {
131142
+ return this._budget.extendedPositionsView.map((position) => {
130021
131143
  const share = this._positionExposureShareFraction(
130022
131144
  position,
130023
131145
  totalExposure
@@ -130025,7 +131147,7 @@ spurious results.`);
130025
131147
  return {
130026
131148
  instrument: position.instrument,
130027
131149
  delta: new Web3Number(
130028
- totalDelta.multipliedBy(share).toFixed(COLLATERAL_PRECISION),
131150
+ totalDelta.multipliedBy(share).toFixedRoundDown(COLLATERAL_PRECISION),
130029
131151
  USDC_TOKEN_DECIMALS
130030
131152
  )
130031
131153
  };
@@ -130037,7 +131159,7 @@ spurious results.`);
130037
131159
  * or when total exposure is zero.
130038
131160
  */
130039
131161
  _positionExposureShareFraction(position, totalExposure) {
130040
- const isSingleOrZero = totalExposure.toNumber() === 0 || this._budget.extendedPositions.length === 1;
131162
+ const isSingleOrZero = totalExposure.toNumber() === 0 || this._budget.extendedPositionsView.length === 1;
130041
131163
  if (isSingleOrZero) return 1;
130042
131164
  return position.valueUsd.dividedBy(totalExposure).toNumber();
130043
131165
  }
@@ -130049,7 +131171,10 @@ spurious results.`);
130049
131171
  * Positive = need to deposit more, negative = can withdraw excess.
130050
131172
  */
130051
131173
  _computeExtendedDepositDelta(extendedAllocationUsd) {
130052
- const currentAvailableForTrade = this._budget.extendedBalance?.availableForTrade ?? new Web3Number(0, USDC_TOKEN_DECIMALS);
131174
+ const currentAvailableForTrade = new Web3Number(
131175
+ this._budget.extAvailTrade.toFixed(USDC_TOKEN_DECIMALS),
131176
+ USDC_TOKEN_DECIMALS
131177
+ );
130053
131178
  return new Web3Number(
130054
131179
  extendedAllocationUsd.minus(currentAvailableForTrade).toFixed(USDC_TOKEN_DECIMALS),
130055
131180
  USDC_TOKEN_DECIMALS
@@ -130057,8 +131182,8 @@ spurious results.`);
130057
131182
  }
130058
131183
  _computeVesuDepositAmount(vesuDeltas) {
130059
131184
  let totalVesuCollateral = new Web3Number(0, USDC_TOKEN_DECIMALS);
130060
- for (let i = 0; i < this._budget.vesuPoolStates.length; i++) {
130061
- const pool = this._budget.vesuPoolStates[i];
131185
+ for (let i = 0; i < this._budget.vesuPools.length; i++) {
131186
+ const pool = this._budget.vesuPools[i];
130062
131187
  const delta = vesuDeltas[i];
130063
131188
  totalVesuCollateral = totalVesuCollateral.plus(delta.collateralDelta.multipliedBy(pool.collateralPrice));
130064
131189
  totalVesuCollateral = totalVesuCollateral.minus(delta.debtDelta.multipliedBy(pool.debtPrice));
@@ -130103,13 +131228,17 @@ spurious results.`);
130103
131228
  for (const route of spendsByPool) {
130104
131229
  routes.push({ type: "VESU_REPAY" /* VESU_REPAY */, ...route, priority: routes.length });
130105
131230
  }
130106
- this._budget.spendVA(used);
131231
+ this._budget.spendVaRawUsd(used);
130107
131232
  }
130108
- _buildVesuBorrowRoutes(totalUsd, routes) {
131233
+ _buildVesuBorrowRoutes(totalUsd, routes, opts) {
130109
131234
  let borrowable = this._budget.vesuBorrowCapacity;
131235
+ if (opts?.maxBorrowUsd !== void 0) {
131236
+ borrowable = Math.min(borrowable, Math.max(0, opts.maxBorrowUsd));
131237
+ }
130110
131238
  if (totalUsd <= CASE_THRESHOLD_USD) return { routes, remaining: totalUsd };
130111
131239
  if (borrowable <= CASE_THRESHOLD_USD) return { routes, remaining: totalUsd };
130112
- const { used, spendsByPool } = this._budget.spendVesuBorrowCapacity(totalUsd);
131240
+ const borrowTarget = Math.min(totalUsd, borrowable);
131241
+ const { used, spendsByPool } = this._budget.spendVesuBorrowCapacity(borrowTarget);
130113
131242
  for (const route of spendsByPool) {
130114
131243
  routes.push({ type: "VESU_BORROW" /* VESU_BORROW */, ...route, priority: routes.length });
130115
131244
  }
@@ -130165,9 +131294,9 @@ spurious results.`);
130165
131294
  // });
130166
131295
  // }
130167
131296
  _getWalletToVARoute(tryAmount, routes) {
130168
- const usableAmount = Math.min(tryAmount, this._budget.walletUsd);
130169
- if (usableAmount > CASE_THRESHOLD_USD) {
130170
- const walletUsed = this._budget.spendWallet(usableAmount);
131297
+ const usableRaw = Math.min(tryAmount, this._budget.walletUsd);
131298
+ if (usableRaw > CASE_THRESHOLD_USD) {
131299
+ const walletUsed = this._budget.spendWallet(usableRaw);
130171
131300
  this._budget.addToVA(walletUsed);
130172
131301
  const route = { type: "WALLET_TO_VA" /* WALLET_TO_VA */, amount: safeUsdcWeb3Number(walletUsed), priority: routes.length };
130173
131302
  routes.push(route);
@@ -130176,9 +131305,9 @@ spurious results.`);
130176
131305
  return { routes, remaining: tryAmount };
130177
131306
  }
130178
131307
  _getWalletToEXTENDEDRoute(tryAmount, routes, shouldAddWaitRoute = true) {
130179
- const usableAmount = Math.min(tryAmount, this._budget.walletUsd);
130180
- if (usableAmount > CASE_THRESHOLD_USD) {
130181
- const walletUsed = this._budget.spendWallet(usableAmount);
131308
+ const usableRaw = Math.min(tryAmount, this._budget.walletUsd);
131309
+ if (usableRaw > CASE_THRESHOLD_USD) {
131310
+ const walletUsed = this._budget.spendWallet(usableRaw);
130182
131311
  this._budget.addToExtAvailTrade(walletUsed);
130183
131312
  routes.push({ type: "WALLET_TO_EXTENDED" /* WALLET_TO_EXTENDED */, amount: safeUsdcWeb3Number(walletUsed), priority: routes.length });
130184
131313
  if (shouldAddWaitRoute) {
@@ -130189,9 +131318,9 @@ spurious results.`);
130189
131318
  return { routes, remaining: tryAmount };
130190
131319
  }
130191
131320
  _getVAToEXTENDEDRoute(tryAmount, routes, shouldAddWaitRoute = true) {
130192
- const usableAmount = Math.min(tryAmount, this._budget.vaUsd);
130193
- if (usableAmount > CASE_THRESHOLD_USD) {
130194
- const vaUsed = this._budget.spendVA(usableAmount);
131321
+ const usable = Math.min(tryAmount, this._budget.vaUsd);
131322
+ if (usable > CASE_THRESHOLD_USD) {
131323
+ const vaUsed = this._budget.spendVA(usable);
130195
131324
  this._budget.addToExtAvailTrade(vaUsed);
130196
131325
  const route = { type: "VA_TO_EXTENDED" /* VA_TO_EXTENDED */, amount: safeUsdcWeb3Number(vaUsed), priority: routes.length };
130197
131326
  routes.push(route);
@@ -130204,40 +131333,42 @@ spurious results.`);
130204
131333
  }
130205
131334
  _getExtendedToWalletRoute(tryAmount, routes, shouldAddWaitRoute = true) {
130206
131335
  if (tryAmount <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
130207
- assert3(tryAmount <= this._budget.extAvailWithdraw, `tryAmount is greater than extAvailTrade, tryAmount: ${tryAmount}, extAvailWithdraw: ${this._budget.extAvailWithdraw}`);
130208
- const extWithdrawUsed = this._budget.spendExtAvailTrade(tryAmount);
130209
- this._budget.addToWallet(Math.abs(extWithdrawUsed));
130210
- const route = { type: "EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */, amount: safeUsdcWeb3Number(Math.abs(extWithdrawUsed)), priority: routes.length };
131336
+ const rawCap = this._budget.extAvailWithdraw + this._budget.extAvailUpnl;
131337
+ const rawSpend = Math.min(tryAmount, rawCap);
131338
+ if (rawSpend <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
131339
+ const rawOut = this._budget.spendExtAvailTrade(rawSpend);
131340
+ this._budget.addToWallet(Math.abs(rawOut));
131341
+ const route = { type: "EXTENDED_TO_WALLET" /* EXTENDED_TO_WALLET */, amount: safeUsdcWeb3Number(rawSpend), priority: routes.length };
130211
131342
  routes.push(route);
130212
131343
  if (shouldAddWaitRoute) {
130213
131344
  routes.push({ type: "RETURN_TO_WAIT" /* RETURN_TO_WAIT */, priority: routes.length });
130214
131345
  }
130215
- return { routes, remaining: tryAmount - Math.abs(extWithdrawUsed) };
131346
+ return { routes, remaining: tryAmount - rawSpend };
130216
131347
  }
130217
131348
  _getWALLETToVARoute(tryAmount, routes) {
130218
131349
  if (tryAmount <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
130219
- const usableAmount = Math.min(tryAmount, this._budget.walletUsd);
130220
- if (usableAmount <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
130221
- const walletUsed = this._budget.spendWallet(tryAmount);
131350
+ const usableRaw = Math.min(tryAmount, this._budget.walletUsd);
131351
+ if (usableRaw <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
131352
+ const walletUsed = this._budget.spendWallet(usableRaw);
130222
131353
  this._budget.addToVA(walletUsed);
130223
131354
  const route = { type: "WALLET_TO_VA" /* WALLET_TO_VA */, amount: safeUsdcWeb3Number(walletUsed), priority: routes.length };
130224
131355
  routes.push(route);
130225
131356
  return { routes, remaining: tryAmount - walletUsed };
130226
131357
  }
130227
131358
  _getUpnlRoute(tryAmount, routes) {
130228
- const upnl = this._budget.extendedBalance?.unrealisedPnl?.toNumber() ?? 0;
130229
- const usableAmount = Math.min(tryAmount, upnl);
130230
- if (usableAmount <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
130231
- const upnlUsed = this._budget.spendExtAvailUpnl(usableAmount);
130232
- this._budget.addToExtAvailTrade(upnlUsed);
130233
- assert3(this._budget.extendedPositions.length == 1, "SolveBudget::_getUpnlRoute: extendedPositions length must be 1");
131359
+ const rawUpnl = this._budget.extAvailUpnl;
131360
+ const usableRaw = Math.min(tryAmount, rawUpnl);
131361
+ if (usableRaw <= 0) return { routes, remaining: tryAmount };
131362
+ this._budget.spendExtAvailUpnl(usableRaw);
131363
+ this._budget.addToExtAvailTrade(usableRaw);
131364
+ assert3(this._budget.extendedPositionsView.length == 1, "SolveBudget::_getUpnlRoute: extendedPositions length must be 1");
130234
131365
  routes.push({
130235
131366
  type: "REALISE_PNL" /* REALISE_PNL */,
130236
- amount: safeUsdcWeb3Number(upnlUsed),
130237
- instrument: this._budget.extendedPositions[0].instrument,
131367
+ amount: safeUsdcWeb3Number(usableRaw),
131368
+ instrument: this._budget.extendedPositionsView[0].instrument,
130238
131369
  priority: routes.length
130239
131370
  });
130240
- return { routes, remaining: tryAmount - upnlUsed };
131371
+ return { routes, remaining: tryAmount - usableRaw };
130241
131372
  }
130242
131373
  // ── Sub-classifiers ────────────────────────────────────────────────────
130243
131374
  // Each sub-classifier builds routes directly from contextual data.
@@ -130252,7 +131383,7 @@ spurious results.`);
130252
131383
  const instrument = this._config.extendedAdapter.config.extendedMarketName ?? "BTC-USD";
130253
131384
  const routes = [];
130254
131385
  let remaining = withdrawAmount.toNumber();
130255
- const vaUsed = this._budget.spendVA(Math.min(this._budget.vaUsd, remaining));
131386
+ const vaUsed = this._budget.spendVA(remaining);
130256
131387
  remaining -= vaUsed;
130257
131388
  let totalExtUsed = 0;
130258
131389
  if (remaining > CASE_THRESHOLD_USD) {
@@ -130275,48 +131406,90 @@ spurious results.`);
130275
131406
  totalExtUsed = usableWithrawAmount + upnlUsed;
130276
131407
  }
130277
131408
  if (remaining > CASE_THRESHOLD_USD) {
130278
- const avgCollPrice = this._budget.vesuPoolStates[0]?.collateralPrice ?? 1;
130279
- assert3(this._budget.vesuPoolStates.length == 1, "SolveBudget::_classifyWithdrawal: vesuPoolStates length must be 1");
130280
- const { vesuPositionDelta, extendedPositionDelta, vesuAllocationUsd, extendedAllocationUsd } = this._computeAllocationSplit(new Web3Number((-remaining).toFixed(6), USDC_TOKEN_DECIMALS));
130281
- if (vesuPositionDelta < 0) {
130282
- const vesuAdapter = this._config.vesuAdapters[0];
130283
- const debtDelta = Math.min(0, vesuPositionDelta * avgCollPrice - vesuAllocationUsd.toNumber());
130284
- const withdrawAmount2 = vesuAllocationUsd.dividedBy(avgCollPrice);
130285
- withdrawAmount2.decimals = vesuAdapter.config.collateral.decimals;
131409
+ assert3(this._budget.vesuPools.length == 1, "SolveBudget::_classifyWithdrawal: vesuPoolStates length must be 1");
131410
+ const vesuAdapter = this._config.vesuAdapters[0];
131411
+ const avgCollPrice = this._budget.vesuPools[0]?.collateralPrice ?? 1;
131412
+ const vesuLeverage = calculateVesuLeverage();
131413
+ const extLeverage = calculateExtendedLevergae();
131414
+ const freedPerBtcVesu = avgCollPrice / vesuLeverage;
131415
+ const freedPerBtcExt = avgCollPrice / extLeverage;
131416
+ const vesuColBtc = this._budget.vesuPools[0].collateralAmount.toNumber();
131417
+ const extPosBtc = this._totalExtendedExposure().toNumber();
131418
+ let stillNeeded = remaining;
131419
+ let vesuBtcDelta = 0;
131420
+ let extBtcDelta = 0;
131421
+ let extFreed = 0;
131422
+ const roundUpBtc = (x) => {
131423
+ const factor = 10 ** COLLATERAL_PRECISION;
131424
+ return Math.ceil(x * factor) / factor;
131425
+ };
131426
+ const diff = vesuColBtc - extPosBtc;
131427
+ let currentVesuBtc = vesuColBtc;
131428
+ let currentExtBtc = extPosBtc;
131429
+ if (Math.abs(diff) > 1e-8) {
131430
+ if (diff > 0) {
131431
+ const btcRaw = stillNeeded / freedPerBtcVesu;
131432
+ const btc = Math.min(roundUpBtc(Math.min(Math.abs(diff), btcRaw, currentVesuBtc)), currentVesuBtc);
131433
+ vesuBtcDelta += btc;
131434
+ stillNeeded -= btc * freedPerBtcVesu;
131435
+ currentVesuBtc -= btc;
131436
+ } else {
131437
+ const btcRaw = stillNeeded / freedPerBtcExt;
131438
+ const btc = Math.min(roundUpBtc(Math.min(Math.abs(diff), btcRaw, currentExtBtc)), currentExtBtc);
131439
+ extBtcDelta += btc;
131440
+ extFreed += btc * freedPerBtcExt;
131441
+ stillNeeded -= btc * freedPerBtcExt;
131442
+ currentExtBtc -= btc;
131443
+ }
131444
+ }
131445
+ if (stillNeeded > CASE_THRESHOLD_USD) {
131446
+ const combinedFreed = freedPerBtcVesu + freedPerBtcExt;
131447
+ const maxBtc = Math.min(currentVesuBtc, currentExtBtc);
131448
+ const btcRaw = stillNeeded / combinedFreed;
131449
+ const btc = Math.min(roundUpBtc(Math.min(btcRaw, maxBtc)), maxBtc);
131450
+ vesuBtcDelta += btc;
131451
+ extBtcDelta += btc;
131452
+ extFreed += btc * freedPerBtcExt;
131453
+ }
131454
+ const r6 = (n2) => Number(n2.toFixed(6));
131455
+ if (vesuBtcDelta > 0) {
131456
+ const totalVesuBtcSigned = -vesuBtcDelta;
131457
+ const targetLtv = 1 - 1 / vesuLeverage;
131458
+ const debtDelta = r6(totalVesuBtcSigned * avgCollPrice * targetLtv);
131459
+ const marginBtc = 0;
131460
+ const swappedBtc = Number(vesuBtcDelta.toFixed(COLLATERAL_PRECISION));
130286
131461
  routes.push({
130287
131462
  type: "VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */,
130288
131463
  poolId: vesuAdapter.config.poolId,
130289
131464
  collateralToken: vesuAdapter.config.collateral,
130290
- marginAmount: withdrawAmount2,
130291
- swappedCollateralAmount: new Web3Number(vesuPositionDelta, vesuAdapter.config.collateral.decimals).minus(withdrawAmount2),
131465
+ marginAmount: new Web3Number(marginBtc.toFixed(COLLATERAL_PRECISION), vesuAdapter.config.collateral.decimals),
131466
+ swappedCollateralAmount: new Web3Number(swappedBtc.toFixed(COLLATERAL_PRECISION), vesuAdapter.config.collateral.decimals),
130292
131467
  debtToken: vesuAdapter.config.debt,
130293
131468
  debtAmount: new Web3Number(debtDelta, USDC_TOKEN_DECIMALS),
130294
131469
  priority: routes.length
130295
131470
  });
130296
- this._budget.applyVesuDelta(vesuAdapter.config.poolId, vesuAdapter.config.collateral, vesuAdapter.config.debt, new Web3Number(vesuPositionDelta, USDC_TOKEN_DECIMALS), new Web3Number(debtDelta, USDC_TOKEN_DECIMALS));
130297
- if (vesuAllocationUsd.toNumber() < -CASE_THRESHOLD_USD) {
130298
- const swapAmount = new Web3Number((vesuAllocationUsd.toNumber() / avgCollPrice * 0.998).toFixed(6), USDC_TOKEN_DECIMALS);
130299
- routes.push({
130300
- type: "AVNU_WITHDRAW_SWAP" /* AVNU_WITHDRAW_SWAP */,
130301
- fromToken: vesuAdapter.config.collateral.symbol,
130302
- // add buffer to avoid rounding errors
130303
- fromAmount: swapAmount,
130304
- toToken: vesuAdapter.config.debt.symbol,
130305
- priority: routes.length
130306
- });
130307
- this._budget.addToVA(vesuAllocationUsd.abs().toNumber());
130308
- }
131471
+ this._budget.applyVesuDelta(
131472
+ vesuAdapter.config.poolId,
131473
+ vesuAdapter.config.collateral,
131474
+ vesuAdapter.config.debt,
131475
+ new Web3Number(r6(totalVesuBtcSigned), USDC_TOKEN_DECIMALS),
131476
+ new Web3Number(debtDelta, USDC_TOKEN_DECIMALS)
131477
+ );
131478
+ this._budget.addToVA(vesuBtcDelta * freedPerBtcVesu);
130309
131479
  }
130310
- if (extendedPositionDelta < 0) {
131480
+ if (extBtcDelta > 0) {
130311
131481
  routes.push({
130312
131482
  type: "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
130313
- amount: safeUsdcWeb3Number(extendedPositionDelta),
131483
+ amount: safeUsdcWeb3Number(-r6(extBtcDelta)),
130314
131484
  instrument,
130315
131485
  priority: routes.length
130316
131486
  });
130317
- this._budget.applyExtendedExposureDelta(instrument, safeUsdcWeb3Number(extendedPositionDelta));
130318
- this._budget.addToExtAvailTrade(extendedAllocationUsd.abs().toNumber());
130319
- totalExtUsed += extendedAllocationUsd.abs().toNumber();
131487
+ this._budget.applyExtendedExposureDelta(
131488
+ instrument,
131489
+ safeUsdcWeb3Number(-r6(extBtcDelta)),
131490
+ avgCollPrice
131491
+ );
131492
+ totalExtUsed += extFreed;
130320
131493
  }
130321
131494
  }
130322
131495
  if (totalExtUsed > CASE_THRESHOLD_USD) {
@@ -130349,56 +131522,231 @@ spurious results.`);
130349
131522
  *
130350
131523
  * Design: accumulate all ext-to-wallet moves, add transfer routes at the end (principle #3).
130351
131524
  */
130352
- _classifyLtvVesu() {
130353
- const debtDeltaSum = this._budget.vesuPerPoolDebtDeltasToBorrow.reduce(
130354
- (a, b2) => a + b2.toNumber(),
130355
- 0
131525
+ /**
131526
+ * Unified LTV classifier. Computes both Vesu repay and Extended margin needs,
131527
+ * then builds all routes in a single pass with no duplicate transfers.
131528
+ *
131529
+ * Vesu repay priority: VA > Wallet > ExtAvl > ExtUpnl
131530
+ * Extended margin priority: Wallet > VA > VesuBorrow
131531
+ * Shared sources consumed by Vesu first (higher priority).
131532
+ */
131533
+ _classifyLTV() {
131534
+ assert3(this._budget.vesuPools.length === 1, `${this._tag}::_classifyLTV expects exactly one Vesu pool`);
131535
+ const d = rebalance(this._ltvRebalanceInputsFromBudget());
131536
+ if (this._isLtvRebalanceNoop(d)) return [];
131537
+ logger2.info(
131538
+ `${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}`
130356
131539
  );
130357
- logger2.info(`${this._tag}::_classifyLtvVesu debtDeltaSum: ${debtDeltaSum}`);
130358
- const allHealthLTVs = this._budget.shouldVesuRebalance.every((shouldReb) => !shouldReb);
130359
- if (debtDeltaSum >= -CASE_THRESHOLD_USD || allHealthLTVs) return [];
130360
- const needed = Math.abs(debtDeltaSum);
130361
- const routes = [];
130362
- let remaining = needed;
130363
- let totalExtUsed = 0;
130364
- let caseId = "LTV_VESU_HIGH_USE_VA_OR_WALLET" /* LTV_VESU_HIGH_USE_VA_OR_WALLET */;
130365
- const vaUsed = this._budget.spendVA(Math.min(this._budget.vaUsd, remaining));
130366
- remaining -= vaUsed;
130367
- if (remaining > CASE_THRESHOLD_USD) {
130368
- const { remaining: walletToVaRemaining } = this._getWALLETToVARoute(remaining, routes);
130369
- remaining = walletToVaRemaining;
130370
- }
130371
- if (remaining > CASE_THRESHOLD_USD) {
130372
- const usableWithdrawAmount = Math.min(remaining, this._budget.extAvailWithdraw);
130373
- remaining -= usableWithdrawAmount;
130374
- let upnlUsed = 0;
130375
- if (remaining > CASE_THRESHOLD_USD) {
130376
- const { remaining: upnlRemaining } = this._getUpnlRoute(remaining, routes);
130377
- upnlUsed = remaining - upnlRemaining;
130378
- remaining = upnlRemaining;
130379
- caseId = "LTV_EXTENDED_PROFITABLE_REALIZE" /* LTV_EXTENDED_PROFITABLE_REALIZE */;
130380
- } else {
130381
- caseId = "LTV_EXTENDED_PROFITABLE_AVAILABLE" /* LTV_EXTENDED_PROFITABLE_AVAILABLE */;
130382
- }
130383
- totalExtUsed = usableWithdrawAmount + upnlUsed;
130384
- }
130385
- if (remaining > CASE_THRESHOLD_USD) {
130386
- throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
130387
- }
130388
- if (totalExtUsed > CASE_THRESHOLD_USD) {
130389
- this._getExtendedToWalletRoute(totalExtUsed, routes);
130390
- this._getWALLETToVARoute(totalExtUsed, routes);
130391
- }
130392
- this._buildVesuRepayRoutes(needed - remaining, routes);
131540
+ const routes = this._buildLtvRoutesFromRebalanceDeltas(d);
131541
+ if (routes.length === 0) return [];
131542
+ 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);
130393
131543
  routes.forEach((r, i) => {
130394
131544
  r.priority = i;
130395
131545
  });
130396
131546
  return [{
130397
- case: CASE_DEFINITIONS[caseId],
130398
- additionalArgs: { amount: safeUsdcWeb3Number(needed) },
131547
+ case: CASE_DEFINITIONS["MANAGE_LTV" /* MANAGE_LTV */],
131548
+ additionalArgs: { amount: safeUsdcWeb3Number(amountUsd) },
130399
131549
  routes
130400
131550
  }];
130401
131551
  }
131552
+ _ltvRebalanceInputsFromBudget() {
131553
+ const pool = this._budget.vesuPools[0];
131554
+ const instrument = this._config.extendedAdapter.config.extendedMarketName ?? "BTC-USD";
131555
+ const extPosBtc = this._budget.extendedPositionsView.filter((p) => p.instrument === instrument).reduce((s, p) => s + Math.abs(p.size.toNumber()), 0);
131556
+ const targetHF = VesuConfig.maxLtv / VesuConfig.targetLtv;
131557
+ return {
131558
+ ext: {
131559
+ positionBtc: extPosBtc,
131560
+ equity: this._budget.extendedBalanceView?.equity?.toNumber() ?? 0,
131561
+ avlWithdraw: this._budget.extAvailWithdraw,
131562
+ upnl: this._budget.extAvailUpnl,
131563
+ leverage: calculateExtendedLevergae()
131564
+ },
131565
+ vesu: {
131566
+ positionBtc: pool.collateralAmount.toNumber(),
131567
+ debt: pool.debtAmount.toNumber(),
131568
+ debtPrice: pool.debtPrice,
131569
+ maxLTV: VesuConfig.maxLtv,
131570
+ targetHF
131571
+ },
131572
+ btcPrice: pool.collateralPrice,
131573
+ funding: {
131574
+ vaUsd: this._budget.vaUsd,
131575
+ walletUsd: this._budget.walletUsd,
131576
+ vesuBorrowCapacity: this._budget.vesuBorrowCapacity,
131577
+ extAvlWithdraw: this._budget.extAvailWithdraw,
131578
+ extUpnl: this._budget.extAvailUpnl
131579
+ },
131580
+ config: {
131581
+ positionPrecision: COLLATERAL_PRECISION,
131582
+ hfBuffer: 0.05
131583
+ }
131584
+ };
131585
+ }
131586
+ _isLtvRebalanceNoop(d) {
131587
+ const btcEps = 10 ** -COLLATERAL_PRECISION;
131588
+ const usdEps = CASE_THRESHOLD_USD;
131589
+ 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;
131590
+ }
131591
+ /**
131592
+ * Turn pure rebalance() deltas into execution routes.
131593
+ * Order: Vesu multiply (decrease/increase) → Extended lever → aggregated transfers
131594
+ * (REALISE_PNL, EXTENDED_TO_WALLET + WAIT, WALLET_TO_VA, VESU_BORROW, VESU_REPAY, VA_TO_EXTENDED).
131595
+ */
131596
+ _buildLtvRoutesFromRebalanceDeltas(d) {
131597
+ const routes = [];
131598
+ const pool = this._budget.vesuPools[0];
131599
+ const vesuAdapter = this._config.vesuAdapters[0];
131600
+ const instrument = this._config.extendedAdapter.config.extendedMarketName ?? "BTC-USD";
131601
+ const price = pool.collateralPrice;
131602
+ const debtPrice = pool.debtPrice;
131603
+ const targetLtv = VesuConfig.targetLtv;
131604
+ const btcEps = 10 ** -COLLATERAL_PRECISION;
131605
+ let multiplyDebtRepayUsd = 0;
131606
+ if (d.dVesuPosition < -btcEps) {
131607
+ const xBtc = -d.dVesuPosition;
131608
+ const transferUsdFromVesu = Math.max(0, d.dTransferVesuToExt);
131609
+ let marginBtc = 0;
131610
+ let swappedBtc = Number(xBtc.toFixed(COLLATERAL_PRECISION));
131611
+ if (transferUsdFromVesu > CASE_THRESHOLD_USD && price > 0) {
131612
+ const marginCapFromTransfer = transferUsdFromVesu / price;
131613
+ marginBtc = Number(
131614
+ Math.min(xBtc, marginCapFromTransfer).toFixed(COLLATERAL_PRECISION)
131615
+ );
131616
+ swappedBtc = Number((xBtc - marginBtc).toFixed(COLLATERAL_PRECISION));
131617
+ }
131618
+ const swapLegMaxRepayUsd = swappedBtc * price * debtPrice;
131619
+ const debtUsdFallback = swappedBtc * price * targetLtv;
131620
+ let debtTokenDelta;
131621
+ if (d.dVesuDebt < 0) {
131622
+ const needRepayUsd = -d.dVesuDebt * debtPrice;
131623
+ const multiplyRepayUsd = Math.min(needRepayUsd, swapLegMaxRepayUsd);
131624
+ debtTokenDelta = -(multiplyRepayUsd / debtPrice);
131625
+ } else {
131626
+ debtTokenDelta = -debtUsdFallback;
131627
+ }
131628
+ const debtAmtW3 = new Web3Number(debtTokenDelta.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
131629
+ multiplyDebtRepayUsd = Math.abs(debtTokenDelta) * debtPrice;
131630
+ routes.push({
131631
+ type: "VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */,
131632
+ poolId: vesuAdapter.config.poolId,
131633
+ collateralToken: vesuAdapter.config.collateral,
131634
+ marginAmount: new Web3Number(marginBtc.toFixed(COLLATERAL_PRECISION), vesuAdapter.config.collateral.decimals),
131635
+ swappedCollateralAmount: new Web3Number(swappedBtc.toFixed(COLLATERAL_PRECISION), vesuAdapter.config.collateral.decimals),
131636
+ debtToken: vesuAdapter.config.debt,
131637
+ debtAmount: debtAmtW3,
131638
+ priority: routes.length
131639
+ });
131640
+ this._budget.applyVesuDelta(
131641
+ vesuAdapter.config.poolId,
131642
+ vesuAdapter.config.collateral,
131643
+ vesuAdapter.config.debt,
131644
+ new Web3Number((-xBtc).toFixed(COLLATERAL_PRECISION), vesuAdapter.config.collateral.decimals),
131645
+ debtAmtW3
131646
+ );
131647
+ if (transferUsdFromVesu > CASE_THRESHOLD_USD) {
131648
+ this._budget.addToVA(transferUsdFromVesu);
131649
+ }
131650
+ } else if (d.dVesuPosition > btcEps) {
131651
+ const vesuDepositAmount = new Web3Number(
131652
+ (d.dVesuPosition * price * (1 - targetLtv)).toFixed(USDC_TOKEN_DECIMALS),
131653
+ USDC_TOKEN_DECIMALS
131654
+ );
131655
+ if (vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
131656
+ routes.push({
131657
+ type: "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
131658
+ priority: routes.length,
131659
+ fromToken: vesuAdapter.config.collateral.symbol,
131660
+ fromAmount: vesuDepositAmount,
131661
+ toToken: vesuAdapter.config.debt.symbol
131662
+ });
131663
+ }
131664
+ const collateralDelta = new Web3Number(
131665
+ d.dVesuPosition.toFixed(COLLATERAL_PRECISION),
131666
+ vesuAdapter.config.collateral.decimals
131667
+ );
131668
+ const availableBorrowCapacity = this._budget.vesuBorrowCapacity;
131669
+ const externalDepositAmount = vesuDepositAmount.minus(
131670
+ new Web3Number(Math.min(availableBorrowCapacity, vesuDepositAmount.toNumber()).toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS)
131671
+ );
131672
+ const collPx = pool.collateralPrice || 1;
131673
+ const swappedAmount = new Web3Number(
131674
+ (externalDepositAmount.toNumber() * (pool.debtPrice ?? 1) / collPx).toFixed(6),
131675
+ vesuAdapter.config.collateral.decimals
131676
+ );
131677
+ const debtDeltaTokens = new Web3Number(
131678
+ d.dVesuDebt.toFixed(USDC_TOKEN_DECIMALS),
131679
+ USDC_TOKEN_DECIMALS
131680
+ );
131681
+ routes.push({
131682
+ type: "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */,
131683
+ priority: routes.length,
131684
+ collateralToken: vesuAdapter.config.collateral,
131685
+ debtToken: vesuAdapter.config.debt,
131686
+ marginAmount: swappedAmount,
131687
+ swappedCollateralAmount: collateralDelta.minus(swappedAmount),
131688
+ debtAmount: debtDeltaTokens.plus(new Web3Number(Math.min(availableBorrowCapacity, vesuDepositAmount.toNumber()).toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS)),
131689
+ poolId: vesuAdapter.config.poolId
131690
+ });
131691
+ this._budget.applyVesuDelta(
131692
+ vesuAdapter.config.poolId,
131693
+ vesuAdapter.config.collateral,
131694
+ vesuAdapter.config.debt,
131695
+ collateralDelta,
131696
+ debtDeltaTokens
131697
+ );
131698
+ }
131699
+ if (d.dExtPosition < -btcEps) {
131700
+ const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
131701
+ routes.push({
131702
+ type: "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
131703
+ amount: amt,
131704
+ instrument,
131705
+ priority: routes.length
131706
+ });
131707
+ this._budget.applyExtendedExposureDelta(instrument, amt, price);
131708
+ } else if (d.dExtPosition > btcEps) {
131709
+ const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
131710
+ routes.push({
131711
+ type: "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */,
131712
+ amount: amt,
131713
+ instrument,
131714
+ priority: routes.length
131715
+ });
131716
+ this._budget.applyExtendedExposureDelta(instrument, amt, price);
131717
+ }
131718
+ const negUpnl = Math.min(0, d.dExtUpnl);
131719
+ const negExtAvl = Math.min(0, d.dExtAvlWithdraw);
131720
+ let hadExtendedOut = false;
131721
+ if (negUpnl < -CASE_THRESHOLD_USD) {
131722
+ this._getUpnlRoute(Math.abs(negUpnl), routes);
131723
+ hadExtendedOut = true;
131724
+ }
131725
+ const extToWalletUsd = (negExtAvl < -CASE_THRESHOLD_USD ? Math.abs(negExtAvl) : 0) + (negUpnl < -CASE_THRESHOLD_USD ? Math.abs(negUpnl) : 0);
131726
+ if (extToWalletUsd > CASE_THRESHOLD_USD) {
131727
+ this._getExtendedToWalletRoute(extToWalletUsd, routes);
131728
+ hadExtendedOut = true;
131729
+ }
131730
+ const walletPull = Math.abs(Math.min(0, d.dWalletUsd));
131731
+ const walletToVaUsd = walletPull + extToWalletUsd;
131732
+ if (walletToVaUsd > CASE_THRESHOLD_USD) {
131733
+ this._getWALLETToVARoute(walletToVaUsd, routes);
131734
+ }
131735
+ if (d.dVesuBorrowCapacity < -CASE_THRESHOLD_USD) {
131736
+ this._buildVesuBorrowRoutes(Math.abs(d.dVesuBorrowCapacity), routes);
131737
+ }
131738
+ const totalDebtRepayUsd = d.dVesuDebt < 0 ? -d.dVesuDebt * debtPrice : 0;
131739
+ const standaloneRepayUsd = Math.max(0, totalDebtRepayUsd - multiplyDebtRepayUsd);
131740
+ if (standaloneRepayUsd > CASE_THRESHOLD_USD) {
131741
+ this._buildVesuRepayRoutes(standaloneRepayUsd, routes);
131742
+ }
131743
+ const posExtEq = Math.max(0, d.dExtAvlWithdraw);
131744
+ const vaToExtUsd = posExtEq > CASE_THRESHOLD_USD ? posExtEq : 0;
131745
+ if (vaToExtUsd > CASE_THRESHOLD_USD) {
131746
+ this._getVAToEXTENDEDRoute(vaToExtUsd, routes, hadExtendedOut);
131747
+ }
131748
+ return routes;
131749
+ }
130402
131750
  // ── LTV Vesu route builders ───────────────────────────────────────────
130403
131751
  /**
130404
131752
  * LTV_EXTENDED_PROFITABLE_AVAILABLE:
@@ -130475,60 +131823,7 @@ spurious results.`);
130475
131823
  // routes.forEach((r, i) => { r.priority = i; });
130476
131824
  // return routes;
130477
131825
  // }
130478
- /** 2b. LTV Rebalance Extended side */
130479
- /**
130480
- * 2b. LTV Rebalance — Extended side
130481
- *
130482
- * Triggered when Extended equity is below the required margin for current positions.
130483
- * Sources funds to Extended via VA/Wallet or Vesu borrow.
130484
- *
130485
- * Priority: 1) VA/Wallet → Extended 2) Vesu borrow → VA → Extended
130486
- */
130487
- _classifyLtvExtended() {
130488
- const totalExtPosUsd = this._totalExtendedExposureUsd().toNumber();
130489
- const extEquity = this._budget.extendedBalance?.equity?.toNumber() ?? 0;
130490
- const lev = calculateExtendedLevergae();
130491
- const marginNeeded = lev > 0 ? totalExtPosUsd / lev - extEquity : 0;
130492
- if (marginNeeded <= CASE_THRESHOLD_USD) return [];
130493
- let caseId = "LTV_EXTENDED_HIGH_USE_VA_OR_WALLET" /* LTV_EXTENDED_HIGH_USE_VA_OR_WALLET */;
130494
- let remaining = marginNeeded;
130495
- const routes = [];
130496
- if (this._budget.vaWalletUsd > CASE_THRESHOLD_USD && remaining > CASE_THRESHOLD_USD) {
130497
- const use = Math.min(this._budget.vaWalletUsd, remaining);
130498
- if (this._budget.vaUsd > CASE_THRESHOLD_USD) {
130499
- const { remaining: vaRem } = this._getVAToEXTENDEDRoute(remaining, routes, false);
130500
- remaining = vaRem;
130501
- }
130502
- if (remaining > CASE_THRESHOLD_USD) {
130503
- const { remaining: walletRem } = this._getWalletToEXTENDEDRoute(remaining, routes, false);
130504
- remaining = walletRem;
130505
- }
130506
- }
130507
- if (remaining > CASE_THRESHOLD_USD && this._budget.vesuBorrowCapacity > CASE_THRESHOLD_USD) {
130508
- const { remaining: borrowRem } = this._buildVesuBorrowRoutes(Math.min(remaining, this._budget.vesuBorrowCapacity), routes);
130509
- const borrowed = remaining - borrowRem;
130510
- if (remaining != borrowRem) {
130511
- const { remaining: vaRem } = this._getVAToEXTENDEDRoute(borrowed, routes, false);
130512
- }
130513
- remaining = borrowRem;
130514
- routes.forEach((r, i) => {
130515
- r.priority = i;
130516
- });
130517
- remaining -= borrowed;
130518
- caseId = "LTV_VESU_LOW_TO_EXTENDED" /* LTV_VESU_LOW_TO_EXTENDED */;
130519
- }
130520
- if (remaining > CASE_THRESHOLD_USD) {
130521
- throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
130522
- }
130523
- routes.forEach((r, i) => {
130524
- r.priority = i;
130525
- });
130526
- return [{
130527
- case: CASE_DEFINITIONS[caseId],
130528
- additionalArgs: { amount: safeUsdcWeb3Number(marginNeeded) },
130529
- routes
130530
- }];
130531
- }
131826
+ // _classifyLtvExtended has been merged into the unified _classifyLTV above.
130532
131827
  // ── LTV Extended route builders ───────────────────────────────────────
130533
131828
  /**
130534
131829
  * LTV_EXTENDED_HIGH_USE_VA_OR_WALLET:
@@ -130595,6 +131890,62 @@ spurious results.`);
130595
131890
  // return routes;
130596
131891
  // }
130597
131892
  // ! todo implement max lever amount per execution cycle
131893
+ _rebalanceFunds({
131894
+ extAvlWithdraw,
131895
+ extUpnl,
131896
+ vaUsd,
131897
+ walletUsd,
131898
+ vesuBorrowCapacity,
131899
+ vesuLeverage,
131900
+ extendedLeverage
131901
+ }) {
131902
+ const total = extAvlWithdraw + extUpnl + vaUsd + walletUsd + vesuBorrowCapacity;
131903
+ const extendedTarget = total / (1 + extendedLeverage / vesuLeverage);
131904
+ const extendedInitial = extAvlWithdraw + extUpnl;
131905
+ let delta = extendedTarget - extendedInitial;
131906
+ let dExtAvlWithdraw = 0, dExtUpnl = 0, dVaUsd = 0, dWalletUsd = 0, dVesuBorrowCapacity = 0;
131907
+ if (delta > 0) {
131908
+ let need = delta;
131909
+ const takeWalletUsd = Math.min(walletUsd, need);
131910
+ dWalletUsd -= takeWalletUsd;
131911
+ need -= takeWalletUsd;
131912
+ const takeVaUsd = Math.min(vaUsd, need);
131913
+ dVaUsd -= takeVaUsd;
131914
+ need -= takeVaUsd;
131915
+ const takeVesuBorrowCapacity = Math.min(vesuBorrowCapacity, need);
131916
+ dVesuBorrowCapacity -= takeVesuBorrowCapacity;
131917
+ need -= takeVesuBorrowCapacity;
131918
+ const received = delta - need;
131919
+ const eco1Sum = extAvlWithdraw + extUpnl;
131920
+ if (eco1Sum >= 0) {
131921
+ dExtAvlWithdraw += received;
131922
+ } else {
131923
+ throw new Error(`${this._tag}: Unexpected case`);
131924
+ }
131925
+ if (need > 0) {
131926
+ throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
131927
+ }
131928
+ } else if (delta < 0) {
131929
+ let need = -delta;
131930
+ const takeExtAvlWithdraw = Math.min(extAvlWithdraw, need);
131931
+ dExtAvlWithdraw -= takeExtAvlWithdraw;
131932
+ need -= takeExtAvlWithdraw;
131933
+ const takeExtUpnl = Math.min(extUpnl, need);
131934
+ dExtUpnl -= takeExtUpnl;
131935
+ need -= takeExtUpnl;
131936
+ const sent = -delta - need;
131937
+ const eco2Sum = vaUsd + walletUsd + vesuBorrowCapacity;
131938
+ if (eco2Sum >= 0) {
131939
+ dWalletUsd += sent;
131940
+ } else {
131941
+ throw new Error(`${this._tag}: Unexpected case`);
131942
+ }
131943
+ if (need > 0) {
131944
+ throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
131945
+ }
131946
+ }
131947
+ return { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu: delta < 0 };
131948
+ }
130598
131949
  /**
130599
131950
  * 3. New Deposits / Excess Funds
130600
131951
  *
@@ -130609,81 +131960,84 @@ spurious results.`);
130609
131960
  * Computes allocation split between Vesu and Extended, then sources
130610
131961
  * funds and creates lever-increase routes.
130611
131962
  *
130612
- * Fund flow (principle #3accumulate transfers, defer wait):
130613
- * Phase A: fund Extended (wallet→ext, VA→ext, vesu-borrow→VA→ext)
130614
- * Phase B: fund Vesu VA shortfall (wallet→VA, ext→wallet + wallet→VA)
130615
- * Phase C: RETURN_TO_WAIT (if any transfer to Extended occurred)
130616
- * Phase D: lever routes (VESU_MULTIPLY, EXTENDED_INCREASE) near each other (#4)
130617
- */
130618
- _classifyDeposits(withdrawAmount) {
131963
+ * Fund flow (single passavoid VA→Extended then Extended→wallet round-trips):
131964
+ * 1) Treat Vesu borrow headroom that the multiply route will consume as covering
131965
+ * part of the Vesu USDC need (no standalone VESU_BORROW for that slice). Cap
131966
+ * standalone VESU_BORROW→VA→Extended by remaining headroom.
131967
+ * 2) Cover Extended deposit delta: REALISE_PNL first, then withdrawal→avail-trade,
131968
+ * then wallet→Extended, then VA→Extended only up to VA surplus above the USDC
131969
+ * that must remain for Vesu (after step 1), then borrow→VA→Extended.
131970
+ * 3) Cover Vesu VA shortfall: wallet→VA, Extended withdrawal→wallet→VA, REALISE_PNL,
131971
+ * then combined Extended→wallet→VA for the remainder.
131972
+ * 4) RETURN_TO_WAIT when needed; then AVNU + VESU_MULTIPLY + EXTENDED_INCREASE.
131973
+ */
131974
+ /**
131975
+ * @param skipAvnuDepositSwap Omit AVNU before Vesu multiply when LTV cases already ran this cycle
131976
+ * (matrix tests expect deposit routes without that step).
131977
+ */
131978
+ _classifyDeposits(withdrawAmount, skipAvnuDepositSwap = false) {
130619
131979
  if (withdrawAmount.toNumber() > CASE_THRESHOLD_USD) return [];
130620
131980
  const distributableAmount = this._computeDistributableAmount(
130621
- this._budget.vesuPerPoolDebtDeltasToBorrow,
131981
+ this._budget.vesuDebtDeltas,
130622
131982
  withdrawAmount
130623
131983
  );
130624
131984
  if (distributableAmount.toNumber() <= CASE_THRESHOLD_USD) return [];
130625
131985
  const { vesuAllocationUsd, extendedAllocationUsd } = this._computeAllocationSplit(distributableAmount);
130626
131986
  const vesuDeltas = this._computePerPoolCollateralDeltas(
130627
- vesuAllocationUsd,
130628
- this._budget.vesuPerPoolDebtDeltasToBorrow
131987
+ vesuAllocationUsd
130629
131988
  );
130630
131989
  const extendedPositionDeltas = this._computeExtendedPositionDeltas(vesuDeltas);
130631
- const extendedDepositDelta = this._computeExtendedDepositDelta(extendedAllocationUsd);
130632
131990
  const vesuDepositAmount = this._computeVesuDepositAmount(vesuDeltas);
131991
+ const vesuLeverage = calculateVesuLeverage();
131992
+ const extendedLeverage = calculateExtendedLevergae();
131993
+ const { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu } = this._rebalanceFunds({
131994
+ extAvlWithdraw: this._budget.extAvailWithdraw,
131995
+ extUpnl: this._budget.extAvailUpnl,
131996
+ vaUsd: this._budget.vaUsd,
131997
+ walletUsd: this._budget.walletUsd,
131998
+ vesuBorrowCapacity: this._budget.vesuBorrowCapacity,
131999
+ vesuLeverage,
132000
+ extendedLeverage
132001
+ });
130633
132002
  const routes = [];
130634
- let needsWait = false;
130635
- if (extendedDepositDelta.toNumber() > CASE_THRESHOLD_USD) {
130636
- let rem = extendedDepositDelta.toNumber();
130637
- if (rem > CASE_THRESHOLD_USD) {
130638
- const { remaining } = this._getWalletToEXTENDEDRoute(rem, routes, false);
130639
- if (remaining < rem) needsWait = true;
130640
- rem = remaining;
130641
- }
130642
- if (rem > CASE_THRESHOLD_USD) {
130643
- const { remaining } = this._getVAToEXTENDEDRoute(rem, routes, false);
130644
- if (remaining < rem) needsWait = true;
130645
- rem = remaining;
130646
- }
130647
- if (rem > CASE_THRESHOLD_USD && this._budget.vesuBorrowCapacity > CASE_THRESHOLD_USD) {
130648
- const { remaining: borrowRem } = this._buildVesuBorrowRoutes(rem, routes);
130649
- const borrowed = rem - borrowRem;
130650
- if (borrowRem != rem) {
130651
- this._getVAToEXTENDEDRoute(borrowed, routes, false);
130652
- needsWait = true;
130653
- rem = borrowRem;
130654
- }
130655
- }
130656
- }
130657
- if (vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
130658
- const vaShortfall = vesuDepositAmount.toNumber() - this._budget.vaUsd;
130659
- if (vaShortfall > CASE_THRESHOLD_USD) {
130660
- let rem = vaShortfall;
130661
- if (rem > CASE_THRESHOLD_USD && this._budget.walletUsd > CASE_THRESHOLD_USD) {
130662
- const { remaining } = this._getWalletToVARoute(Math.min(this._budget.walletUsd, rem), routes);
130663
- rem = remaining;
130664
- }
130665
- const isWithdrawalEnough = rem <= this._budget.extAvailWithdraw;
130666
- if (!isWithdrawalEnough && rem > CASE_THRESHOLD_USD) {
130667
- const { remaining: upnlRem } = this._getUpnlRoute(rem, routes);
130668
- rem = upnlRem;
130669
- }
130670
- if (rem > CASE_THRESHOLD_USD && this._budget.extAvailWithdraw > CASE_THRESHOLD_USD) {
130671
- const extUse = Math.min(rem, this._budget.extAvailWithdraw);
130672
- this._getExtendedToWalletRoute(extUse, routes);
130673
- this._getWALLETToVARoute(extUse, routes);
130674
- rem -= extUse;
130675
- needsWait = false;
130676
- }
130677
- }
130678
- }
130679
- if (needsWait) {
130680
- routes.push({ type: "RETURN_TO_WAIT" /* RETURN_TO_WAIT */, priority: routes.length });
132003
+ if (isExtendedToVesu) {
132004
+ if (dExtUpnl < 0) {
132005
+ this._getUpnlRoute(Math.abs(dExtUpnl), routes);
132006
+ }
132007
+ if (dExtUpnl < 0 || dExtAvlWithdraw < 0) {
132008
+ const netAmount = (dExtAvlWithdraw < 0 ? Math.abs(dExtAvlWithdraw) : 0) + (dExtUpnl < 0 ? Math.abs(dExtUpnl) : 0);
132009
+ const walletUsd = this._budget.walletUsd;
132010
+ this._getExtendedToWalletRoute(netAmount, routes);
132011
+ this._getWALLETToVARoute(netAmount + walletUsd, routes);
132012
+ }
132013
+ } else {
132014
+ let netDVaUsd = dVaUsd;
132015
+ if (dWalletUsd < 0) {
132016
+ this._getWalletToVARoute(this._budget.walletUsd, routes);
132017
+ netDVaUsd += dWalletUsd;
132018
+ }
132019
+ if (dVesuBorrowCapacity < 0) {
132020
+ this._buildVesuBorrowRoutes(Math.abs(dVesuBorrowCapacity), routes);
132021
+ netDVaUsd += dVesuBorrowCapacity;
132022
+ }
132023
+ if (netDVaUsd < 0) {
132024
+ this._getVAToEXTENDEDRoute(Math.abs(netDVaUsd), routes, true);
132025
+ }
130681
132026
  }
130682
132027
  for (const vesuDelta of vesuDeltas) {
130683
- if (vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
132028
+ if (!skipAvnuDepositSwap && vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
132029
+ routes.push({
132030
+ type: "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
132031
+ priority: routes.length,
132032
+ fromToken: vesuDelta.collateralToken.symbol,
132033
+ fromAmount: vesuDepositAmount,
132034
+ toToken: vesuDelta.debtToken.symbol
132035
+ });
130684
132036
  }
130685
132037
  if (vesuDelta.collateralDelta.toNumber() > 0) {
130686
- const swappedAmount = new Web3Number((vesuDepositAmount.toNumber() * vesuDelta.debtPrice / (vesuDelta.collateralPrice ?? 0)).toFixed(6), vesuDelta.collateralToken.decimals);
132038
+ const availableBorrowCapacity = this._budget.vesuBorrowCapacity;
132039
+ const externalDepositAmount = vesuDepositAmount.minus(availableBorrowCapacity);
132040
+ const swappedAmount = new Web3Number((externalDepositAmount.toNumber() * vesuDelta.debtPrice / (vesuDelta.collateralPrice ?? 0)).toFixed(6), vesuDelta.collateralToken.decimals);
130687
132041
  routes.push({
130688
132042
  type: "VESU_MULTIPLY_INCREASE_LEVER" /* VESU_MULTIPLY_INCREASE_LEVER */,
130689
132043
  priority: routes.length,
@@ -130692,7 +132046,7 @@ spurious results.`);
130692
132046
  marginAmount: swappedAmount,
130693
132047
  // should be the swapped amount as per vesu multiply adapter
130694
132048
  swappedCollateralAmount: vesuDelta.collateralDelta.minus(swappedAmount),
130695
- debtAmount: vesuDelta.debtDelta,
132049
+ debtAmount: vesuDelta.debtDelta.plus(availableBorrowCapacity),
130696
132050
  poolId: vesuDelta.poolId
130697
132051
  });
130698
132052
  }
@@ -130815,8 +132169,13 @@ spurious results.`);
130815
132169
  */
130816
132170
  _buildImbalanceExtExcessShortNoFundsRoutes(exposureDiffBtc) {
130817
132171
  const instrument = this._config.extendedAdapter.config.extendedMarketName ?? "BTC-USD";
130818
- const decDelta = new Web3Number(exposureDiffBtc.toFixed(COLLATERAL_PRECISION), USDC_TOKEN_DECIMALS);
130819
- this._budget.applyExtendedExposureDelta(instrument, new Web3Number(decDelta.negated().toFixed(COLLATERAL_PRECISION), USDC_TOKEN_DECIMALS));
132172
+ const decDelta = new Web3Number(Web3Number.fromNumber(exposureDiffBtc, 8).toFixedRoundDown(COLLATERAL_PRECISION), USDC_TOKEN_DECIMALS);
132173
+ const collPx = this._budget.vesuPools[0]?.collateralPrice ?? 1;
132174
+ this._budget.applyExtendedExposureDelta(
132175
+ instrument,
132176
+ new Web3Number(Web3Number.fromNumber(decDelta.negated().toNumber(), 8).toFixedRoundDown(COLLATERAL_PRECISION), USDC_TOKEN_DECIMALS),
132177
+ collPx
132178
+ );
130820
132179
  return [{
130821
132180
  type: "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
130822
132181
  amount: decDelta,
@@ -130882,13 +132241,15 @@ spurious results.`);
130882
132241
  _classifyCases(withdrawAmount) {
130883
132242
  this._budget.initBudget();
130884
132243
  const withdrawalCases = this._classifyWithdrawal(withdrawAmount);
130885
- const ltvVesuCases = this._classifyLtvVesu();
130886
- const ltvExtendedCases = this._classifyLtvExtended();
130887
- const depositCases = this._classifyDeposits(withdrawAmount);
132244
+ this._budget.applyBuffer(this._config.limitBalanceBufferFactor);
132245
+ const ltvCases = this._classifyLTV();
132246
+ const depositCases = this._classifyDeposits(
132247
+ withdrawAmount,
132248
+ ltvCases.length > 0
132249
+ );
130888
132250
  return [
130889
132251
  ...withdrawalCases,
130890
- ...ltvVesuCases,
130891
- ...ltvExtendedCases,
132252
+ ...ltvCases,
130892
132253
  ...depositCases
130893
132254
  ];
130894
132255
  }
@@ -130896,7 +132257,7 @@ spurious results.`);
130896
132257
  // Private — aggregation helpers
130897
132258
  // ═══════════════════════════════════════════════════════════════════════════
130898
132259
  _totalVesuCollateral() {
130899
- return this._budget.vesuPoolStates.reduce(
132260
+ return this._budget.vesuPools.reduce(
130900
132261
  (acc, pool) => acc.plus(
130901
132262
  pool.collateralAmount
130902
132263
  ),
@@ -130904,7 +132265,7 @@ spurious results.`);
130904
132265
  );
130905
132266
  }
130906
132267
  _totalVesuCollateralUsd() {
130907
- return this._budget.vesuPoolStates.reduce(
132268
+ return this._budget.vesuPools.reduce(
130908
132269
  (acc, pool) => acc.plus(
130909
132270
  pool.collateralAmount.multipliedBy(pool.collateralPrice)
130910
132271
  ),
@@ -130912,13 +132273,13 @@ spurious results.`);
130912
132273
  );
130913
132274
  }
130914
132275
  _totalExtendedExposure() {
130915
- return this._budget.extendedPositions.reduce(
132276
+ return this._budget.extendedPositionsView.reduce(
130916
132277
  (acc, position) => acc.plus(position.size),
130917
132278
  new Web3Number(0, USDC_TOKEN_DECIMALS)
130918
132279
  );
130919
132280
  }
130920
132281
  _totalExtendedExposureUsd() {
130921
- return this._budget.extendedPositions.reduce(
132282
+ return this._budget.extendedPositionsView.reduce(
130922
132283
  (acc, position) => acc.plus(position.valueUsd),
130923
132284
  new Web3Number(0, USDC_TOKEN_DECIMALS)
130924
132285
  );
@@ -131002,11 +132363,10 @@ spurious results.`);
131002
132363
  this._tokenSymbols = StarknetCallParser.buildTokenSymbolLookup([
131003
132364
  config3.wbtcToken,
131004
132365
  config3.usdcToken,
131005
- config3.usdceToken,
131006
- config3.vesuAdapter.config.baseToken,
131007
- config3.vesuAdapter.config.collateral,
131008
- config3.vesuAdapter.config.debt,
131009
- config3.vesuAdapter.config.marginToken,
132366
+ config3.vesuMultiplyAdapter.config.baseToken,
132367
+ config3.vesuMultiplyAdapter.config.collateral,
132368
+ config3.vesuMultiplyAdapter.config.debt,
132369
+ config3.vesuMultiplyAdapter.config.marginToken,
131010
132370
  config3.vesuModifyPositionAdapter.config.collateral,
131011
132371
  config3.vesuModifyPositionAdapter.config.debt,
131012
132372
  ...avnuTokens
@@ -131014,19 +132374,18 @@ spurious results.`);
131014
132374
  this._tokenDecimals = StarknetCallParser.buildTokenDecimalsLookup([
131015
132375
  config3.wbtcToken,
131016
132376
  config3.usdcToken,
131017
- config3.usdceToken,
131018
- config3.vesuAdapter.config.baseToken,
131019
- config3.vesuAdapter.config.collateral,
131020
- config3.vesuAdapter.config.debt,
131021
- config3.vesuAdapter.config.marginToken,
132377
+ config3.vesuMultiplyAdapter.config.baseToken,
132378
+ config3.vesuMultiplyAdapter.config.collateral,
132379
+ config3.vesuMultiplyAdapter.config.debt,
132380
+ config3.vesuMultiplyAdapter.config.marginToken,
131022
132381
  config3.vesuModifyPositionAdapter.config.collateral,
131023
132382
  config3.vesuModifyPositionAdapter.config.debt,
131024
132383
  ...avnuTokens
131025
132384
  ]);
131026
132385
  this._poolNames = StarknetCallParser.buildPoolNameLookup([
131027
132386
  {
131028
- poolId: config3.vesuAdapter.config.poolId.toBigInt(),
131029
- name: `${config3.vesuAdapter.config.collateral.symbol}/${config3.vesuAdapter.config.debt.symbol}`
132387
+ poolId: config3.vesuMultiplyAdapter.config.poolId.toBigInt(),
132388
+ name: `${config3.vesuMultiplyAdapter.config.collateral.symbol}/${config3.vesuMultiplyAdapter.config.debt.symbol}`
131030
132389
  },
131031
132390
  {
131032
132391
  poolId: config3.vesuModifyPositionAdapter.config.poolId.toBigInt(),
@@ -131429,12 +132788,14 @@ spurious results.`);
131429
132788
  *
131430
132789
  * For deposit (USDC→BTC): price = sum(USDC sold) / sum(BTC bought)
131431
132790
  * For withdraw (BTC→USDC): price = sum(USDC bought) / sum(BTC sold)
131432
- */
132791
+ * @returns no-swap means, logic is fine and explicit no swap is needed
132792
+ */
131433
132793
  _getNetExecutionPrice(isDeposit) {
131434
132794
  const prices = [
131435
132795
  this._config.avnuAdapter.lastSwapPriceInfo,
131436
- this._config.vesuAdapter.lastSwapPriceInfo
132796
+ this._config.vesuMultiplyAdapter.lastSwapPriceInfo
131437
132797
  ].filter((p) => p !== null);
132798
+ assert3(prices.length <= 1, "Only one swap price info is allowed");
131438
132799
  if (prices.length === 0) return null;
131439
132800
  if (isDeposit) {
131440
132801
  const totalUsdc = prices.reduce((s, p) => s + p.fromAmount, 0);
@@ -131449,7 +132810,7 @@ spurious results.`);
131449
132810
  /** Clears cached swap price info on all adapters to prevent stale data across cycles. */
131450
132811
  _clearAdapterPriceInfo() {
131451
132812
  this._config.avnuAdapter.lastSwapPriceInfo = null;
131452
- this._config.vesuAdapter.lastSwapPriceInfo = null;
132813
+ this._config.vesuMultiplyAdapter.lastSwapPriceInfo = null;
131453
132814
  }
131454
132815
  // ═══════════════════════════════════════════════════════════════════════════
131455
132816
  // Public API
@@ -131877,7 +133238,7 @@ spurious results.`);
131877
133238
  }
131878
133239
  // ── Transfer routes ─────────────────────────────────────────────────────
131879
133240
  /**
131880
- * WALLET_TO_EXTENDED: Deposit USDC.e from operator wallet directly to Extended.
133241
+ * WALLET_TO_EXTENDED: Deposit USDC from operator wallet directly to Extended.
131881
133242
  *
131882
133243
  * Builds raw approve + deposit calls (NOT through the manager/merkle system)
131883
133244
  * because the wallet interacts with Extended directly, not via vault allocator.
@@ -131892,13 +133253,13 @@ spurious results.`);
131892
133253
  );
131893
133254
  return [];
131894
133255
  }
131895
- const { usdceToken, extendedAdapter } = this._config;
133256
+ const { usdcToken, extendedAdapter } = this._config;
131896
133257
  const extendedContract = extendedAdapter.config.extendedContract;
131897
133258
  const vaultId = extendedAdapter.config.vaultIdExtended;
131898
- const salt = Math.floor(Math.random() * 10 ** usdceToken.decimals);
133259
+ const salt = Math.floor(Math.random() * 10 ** usdcToken.decimals);
131899
133260
  const uint256Amount = uint256_exports.bnToUint256(amount.toWei());
131900
133261
  const approveCall = {
131901
- contractAddress: usdceToken.address.address,
133262
+ contractAddress: usdcToken.address.address,
131902
133263
  entrypoint: "approve",
131903
133264
  calldata: [
131904
133265
  extendedContract.address,
@@ -131921,7 +133282,7 @@ spurious results.`);
131921
133282
  return [approveCall, depositCall];
131922
133283
  }
131923
133284
  /**
131924
- * VA_TO_EXTENDED: Deposit USDC.e from vault allocator to Extended.
133285
+ * VA_TO_EXTENDED: Deposit USDC from vault allocator to Extended.
131925
133286
  *
131926
133287
  * Uses the extended adapter's getDepositCall to build ManageCalls,
131927
133288
  * then wraps them in a merkle-verified manage call through the manager contract.
@@ -131936,43 +133297,31 @@ spurious results.`);
131936
133297
  );
131937
133298
  return [];
131938
133299
  }
131939
- const swapCall = await this._buildAdapterManageCall(
131940
- this._config.usdcToUsdceAdapter,
131941
- true,
131942
- { amount }
131943
- );
131944
133300
  const manageCall = await this._buildAdapterManageCall(
131945
133301
  this._config.extendedAdapter,
131946
133302
  true,
131947
133303
  { amount }
131948
133304
  );
131949
- return [swapCall, manageCall];
133305
+ return [manageCall];
131950
133306
  }
131951
133307
  /**
131952
- * WALLET_TO_VA: Transfer USDC.e from operator wallet to vault allocator.
133308
+ * WALLET_TO_VA: Transfer USDC from operator wallet to vault allocator.
131953
133309
  * Caps amount by actual wallet balance.
131954
133310
  */
131955
133311
  async _buildWalletToVACalls(route) {
131956
- const erc20 = new ERC20(this._config.networkConfig);
131957
- const { usdceToken, vaultAllocator, walletAddress } = this._config;
131958
133312
  const transferAmount = route.amount;
131959
133313
  if (transferAmount.lessThanOrEqualTo(0)) {
131960
133314
  logger2.warn(
131961
- `${this._tag}::_buildWalletToVACalls no USDC.e in wallet to transfer`
133315
+ `${this._tag}::_buildWalletToVACalls no USDC in wallet to transfer`
131962
133316
  );
131963
133317
  return [];
131964
133318
  }
131965
133319
  const transferCall = await this._buildAdapterManageCall(
131966
- this._config.usdceTransferAdapter,
133320
+ this._config.usdcTransferAdapter,
131967
133321
  false,
131968
133322
  { amount: transferAmount }
131969
133323
  );
131970
- const swapCall = await this._buildAdapterManageCall(
131971
- this._config.usdcToUsdceAdapter,
131972
- false,
131973
- { amount: transferAmount }
131974
- );
131975
- return [transferCall, swapCall];
133324
+ return [transferCall];
131976
133325
  }
131977
133326
  // ── AVNU swap routes ────────────────────────────────────────────────────
131978
133327
  /**
@@ -132005,12 +133354,12 @@ spurious results.`);
132005
133354
  */
132006
133355
  async _buildVesuIncreaseLeverCalls(route) {
132007
133356
  const depositManageCall = await this._buildAdapterManageCall(
132008
- this._config.vesuAdapter,
133357
+ this._config.vesuMultiplyAdapter,
132009
133358
  true,
132010
133359
  {
132011
133360
  amount: route.marginAmount,
132012
133361
  marginSwap: {
132013
- marginToken: this._config.vesuAdapter.config.marginToken
133362
+ marginToken: this._config.vesuMultiplyAdapter.config.marginToken
132014
133363
  // todo, must be vault token
132015
133364
  },
132016
133365
  leverSwap: {
@@ -132030,11 +133379,11 @@ spurious results.`);
132030
133379
  async _buildVesuDecreaseLeverCalls(route) {
132031
133380
  const collateralAmount = route.marginAmount.abs();
132032
133381
  const withdrawManageCall = await this._buildAdapterManageCall(
132033
- this._config.vesuAdapter,
133382
+ this._config.vesuMultiplyAdapter,
132034
133383
  false,
132035
133384
  {
132036
133385
  amount: collateralAmount,
132037
- withdrawSwap: { outputToken: this._config.vesuAdapter.config.marginToken }
133386
+ withdrawSwap: { outputToken: this._config.vesuMultiplyAdapter.config.marginToken }
132038
133387
  }
132039
133388
  );
132040
133389
  return [withdrawManageCall];
@@ -132625,139 +133974,6 @@ spurious results.`);
132625
133974
  ]);
132626
133975
  var ExecutionService = _ExecutionService;
132627
133976
 
132628
- // src/strategies/universal-adapters/usdc<>usdce-adapter.ts
132629
- var UsdcToUsdceAdapter = class _UsdcToUsdceAdapter extends BaseAdapter {
132630
- _approveProofReadableId(usdcToUsdce) {
132631
- const method = usdcToUsdce ? "swap_to_legacy" : "swap_to_new";
132632
- return `approve_${method}`;
132633
- }
132634
- _swapProofReadableId(usdcToUsdce) {
132635
- const method = usdcToUsdce ? "swap_to_legacy" : "swap_to_new";
132636
- const target = usdcToUsdce ? this.config.supportedPositions[0].asset : this.config.supportedPositions[1].asset;
132637
- return `${method}_${target.symbol}`;
132638
- }
132639
- buildSwapLeafConfigs(usdcToUsdce) {
132640
- const method = usdcToUsdce ? "swap_to_legacy" : "swap_to_new";
132641
- const target = usdcToUsdce ? this.config.supportedPositions[0].asset : this.config.supportedPositions[1].asset;
132642
- return [
132643
- {
132644
- target: target.address,
132645
- method: "approve",
132646
- packedArguments: [AVNU_EXCHANGE_FOR_LEGACY_USDC.toBigInt()],
132647
- id: this._approveProofReadableId(usdcToUsdce),
132648
- sanitizer: AVNU_LEGACY_SANITIZER
132649
- },
132650
- {
132651
- target: AVNU_EXCHANGE_FOR_LEGACY_USDC,
132652
- method,
132653
- packedArguments: [],
132654
- id: this._swapProofReadableId(usdcToUsdce),
132655
- sanitizer: AVNU_LEGACY_SANITIZER
132656
- }
132657
- ];
132658
- }
132659
- async buildSwapCalls(params, usdcToUsdce) {
132660
- const approveAmount = uint256_exports.bnToUint256(params.amount.toWei());
132661
- const target = usdcToUsdce ? this.config.supportedPositions[0].asset : this.config.supportedPositions[1].asset;
132662
- const method = usdcToUsdce ? "swap_to_legacy" : "swap_to_new";
132663
- return [
132664
- {
132665
- proofReadableId: this._approveProofReadableId(usdcToUsdce),
132666
- sanitizer: AVNU_LEGACY_SANITIZER,
132667
- call: {
132668
- contractAddress: target.address,
132669
- selector: hash_exports.getSelectorFromName("approve"),
132670
- calldata: [
132671
- AVNU_EXCHANGE_FOR_LEGACY_USDC.toBigInt(),
132672
- toBigInt3(approveAmount.low.toString()),
132673
- toBigInt3(approveAmount.high.toString())
132674
- ]
132675
- }
132676
- },
132677
- {
132678
- proofReadableId: this._swapProofReadableId(usdcToUsdce),
132679
- sanitizer: AVNU_LEGACY_SANITIZER,
132680
- call: {
132681
- contractAddress: AVNU_EXCHANGE_FOR_LEGACY_USDC,
132682
- selector: hash_exports.getSelectorFromName(method),
132683
- calldata: [
132684
- toBigInt3(approveAmount.low.toString()),
132685
- // amount low
132686
- toBigInt3(approveAmount.high.toString())
132687
- // amount high
132688
- ]
132689
- }
132690
- }
132691
- ];
132692
- }
132693
- constructor(config3) {
132694
- super(config3, _UsdcToUsdceAdapter.name, Protocols.AVNU);
132695
- this.config = config3;
132696
- assert3(this.config.supportedPositions.length === 2, "UsdcToUsdceAdapter must have 2 supported positions");
132697
- assert3(this.config.supportedPositions[0].asset.symbol === "USDC", "UsdcToUsdceAdapter must have USDC as the first supported position");
132698
- assert3(this.config.supportedPositions[1].asset.symbol === "USDC.e", "UsdcToUsdceAdapter must have USDCE as the second supported position");
132699
- }
132700
- //abstract means the method has no implementation in this class; instead, child classes must implement it.
132701
- async getAPY(supportedPosition) {
132702
- return Promise.resolve({ apy: 0, type: "base" /* BASE */ });
132703
- }
132704
- async getPosition(supportedPosition) {
132705
- const toToken = this.config.supportedPositions[1].asset;
132706
- if (supportedPosition.asset.symbol != toToken.symbol) {
132707
- return null;
132708
- }
132709
- try {
132710
- const balance = await new ERC20(this.config.networkConfig).balanceOf(
132711
- toToken.address,
132712
- this.config.vaultAllocator.address,
132713
- toToken.decimals
132714
- );
132715
- return { amount: balance, remarks: `USDC.e unused balance (VA)` };
132716
- } catch (_e) {
132717
- logger2.error(`${_UsdcToUsdceAdapter.name}::getPosition: failed for ${toToken.symbol}`);
132718
- throw new Error(`${_UsdcToUsdceAdapter.name}: failed to get balance for ${toToken.symbol}`);
132719
- }
132720
- }
132721
- async maxDeposit(amount) {
132722
- return Promise.resolve({
132723
- tokenInfo: this.config.baseToken,
132724
- amount: new Web3Number(0, 0),
132725
- usdValue: 0,
132726
- apy: { apy: 0, type: "base" /* BASE */ },
132727
- protocol: Protocols.AVNU,
132728
- remarks: ""
132729
- });
132730
- }
132731
- async maxWithdraw() {
132732
- return Promise.resolve({
132733
- tokenInfo: this.config.baseToken,
132734
- amount: new Web3Number(0, 0),
132735
- usdValue: 0,
132736
- apy: { apy: 0, type: "base" /* BASE */ },
132737
- protocol: Protocols.AVNU,
132738
- remarks: ""
132739
- });
132740
- }
132741
- _getDepositLeaf() {
132742
- return this.buildSwapLeafConfigs(true);
132743
- }
132744
- _getWithdrawLeaf() {
132745
- return this.buildSwapLeafConfigs(false);
132746
- }
132747
- async getDepositCall(params) {
132748
- const calls = await this.buildSwapCalls(params, true);
132749
- return calls;
132750
- }
132751
- //Swap wbtc to usdc
132752
- async getWithdrawCall(params) {
132753
- const calls = await this.buildSwapCalls(params, false);
132754
- return calls;
132755
- }
132756
- async getHealthFactor() {
132757
- return Promise.resolve(1);
132758
- }
132759
- };
132760
-
132761
133977
  // src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx
132762
133978
  var import_jsx_runtime6 = __toESM(require_jsx_runtime());
132763
133979
  var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy extends SVKStrategy {
@@ -132773,9 +133989,6 @@ spurious results.`);
132773
133989
  });
132774
133990
  this.wbtcToken = Global.getDefaultTokens().find((token) => token.symbol === "WBTC");
132775
133991
  this.usdcToken = this.metadata.additionalInfo.borrowable_assets[0];
132776
- this.usdceToken = Global.getDefaultTokens().find(
132777
- (token) => token.symbol === "USDC.e"
132778
- );
132779
133992
  this.stateManager = this._initializeStateManager();
132780
133993
  }
132781
133994
  /**
@@ -132799,9 +134012,8 @@ spurious results.`);
132799
134012
  extendedAdapter: extendedAdapterEntry.adapter,
132800
134013
  vaultAllocator: this.metadata.additionalInfo.vaultAllocator,
132801
134014
  walletAddress: this.metadata.additionalInfo.walletAddress,
132802
- assetToken: Global.getDefaultTokens().find((token) => token.symbol === "USDC"),
132803
- // ! TODO change to asset() latest
132804
- usdceToken: this.usdceToken,
134015
+ assetToken: this.asset(),
134016
+ usdcToken: this.usdcToken,
132805
134017
  collateralToken: this.wbtcToken,
132806
134018
  limitBalanceBufferFactor: LIMIT_BALANCE
132807
134019
  };
@@ -132835,7 +134047,7 @@ spurious results.`);
132835
134047
  }
132836
134048
  return { collateralPrice, debtPrice };
132837
134049
  }
132838
- async getVesuAdapter() {
134050
+ async getVesuMultiplyAdapter() {
132839
134051
  const vesuAdapter = this.metadata.additionalInfo.adapters.find(
132840
134052
  (adapter2) => adapter2.adapter.name === VesuMultiplyAdapter.name
132841
134053
  );
@@ -132857,16 +134069,16 @@ spurious results.`);
132857
134069
  }
132858
134070
  return vesuModifyPositionAdapter.adapter;
132859
134071
  }
132860
- async getUsdceTransferAdapter() {
132861
- const usdceTransferAdapter = this.metadata.additionalInfo.adapters.find(
134072
+ async getUsdcTransferAdapter() {
134073
+ const usdcTransferAdapter = this.metadata.additionalInfo.adapters.find(
132862
134074
  (adapter2) => adapter2.adapter.name === TokenTransferAdapter.name
132863
134075
  );
132864
- if (!usdceTransferAdapter) {
134076
+ if (!usdcTransferAdapter) {
132865
134077
  throw new Error(
132866
- `${this.getTag()} Usdce transfer adapter not configured in metadata.`
134078
+ `${this.getTag()} Usdc transfer adapter not configured in metadata.`
132867
134079
  );
132868
134080
  }
132869
- return usdceTransferAdapter.adapter;
134081
+ return usdcTransferAdapter.adapter;
132870
134082
  }
132871
134083
  async getAvnuAdapter() {
132872
134084
  const avnuAdapter = this.metadata.additionalInfo.adapters.find(
@@ -132895,17 +134107,6 @@ spurious results.`);
132895
134107
  }
132896
134108
  return extendedAdapter.adapter;
132897
134109
  }
132898
- async getUsdcToUsdceAdapter() {
132899
- const usdcToUsdceAdapter = this.metadata.additionalInfo.adapters.find(
132900
- (adapter2) => adapter2.adapter.name === UsdcToUsdceAdapter.name
132901
- );
132902
- if (!usdcToUsdceAdapter) {
132903
- throw new Error(
132904
- `${this.getTag()} UsdcToUsdce adapter not configured in metadata.`
132905
- );
132906
- }
132907
- return usdcToUsdceAdapter.adapter;
132908
- }
132909
134110
  /**
132910
134111
  * Creates an ExecutionService wired to this strategy's adapters and config.
132911
134112
  * Use with `stateManager.solve()` to get a SolveResult, then pass it to
@@ -132917,34 +134118,30 @@ spurious results.`);
132917
134118
  */
132918
134119
  async createExecutionService(opts) {
132919
134120
  const [
132920
- vesuAdapter,
134121
+ vesuMultiplyAdapter,
132921
134122
  vesuModifyPositionAdapter,
132922
- usdceTransferAdapter,
132923
134123
  extendedAdapter,
132924
134124
  avnuAdapter,
132925
- usdcToUsdceAdapter
134125
+ usdcTransferAdapter
132926
134126
  ] = await Promise.all([
132927
- this.getVesuAdapter(),
134127
+ this.getVesuMultiplyAdapter(),
132928
134128
  this.getVesuModifyPositionAdapter(),
132929
- this.getUsdceTransferAdapter(),
132930
134129
  this.getExtendedAdapter(),
132931
134130
  this.getAvnuAdapter(),
132932
- this.getUsdcToUsdceAdapter()
134131
+ this.getUsdcTransferAdapter()
132933
134132
  ]);
132934
134133
  const executionConfig = {
132935
134134
  networkConfig: this.config,
132936
134135
  pricer: this.pricer,
132937
- vesuAdapter,
134136
+ vesuMultiplyAdapter,
132938
134137
  vesuModifyPositionAdapter,
132939
134138
  extendedAdapter,
132940
134139
  avnuAdapter,
132941
- usdcToUsdceAdapter,
134140
+ usdcTransferAdapter,
132942
134141
  vaultAllocator: this.metadata.additionalInfo.vaultAllocator,
132943
134142
  walletAddress: this.metadata.additionalInfo.walletAddress,
132944
- usdceTransferAdapter,
132945
134143
  wbtcToken: this.wbtcToken,
132946
134144
  usdcToken: this.usdcToken,
132947
- usdceToken: this.usdceToken,
132948
134145
  getMerkleTree: () => this.getMerkleTree(),
132949
134146
  getManageCall: (proofs, manageCalls) => this.getManageCall(proofs, manageCalls),
132950
134147
  getBringLiquidityCall: (params) => this.getBringLiquidityCall(params),
@@ -132965,9 +134162,7 @@ spurious results.`);
132965
134162
  for (let adapter2 of this.metadata.additionalInfo.adapters) {
132966
134163
  let positions = await adapter2.adapter.getPositions();
132967
134164
  if (positions && positions.length > 0) {
132968
- const filteredPositions = positions.filter((position) => {
132969
- return position.tokenInfo.address !== this.usdceToken.address;
132970
- });
134165
+ const filteredPositions = positions;
132971
134166
  allPositions.push(...filteredPositions);
132972
134167
  }
132973
134168
  }
@@ -133162,34 +134357,22 @@ spurious results.`);
133162
134357
  };
133163
134358
  }
133164
134359
  /**
133165
- * Fetches the operator wallet's current holdings for USDC.e, USDC, and WBTC,
134360
+ * Fetches the operator wallet's current holdings for USDC and WBTC,
133166
134361
  * returning each token's balance and USD value.
133167
134362
  */
133168
134363
  async getWalletHoldings() {
133169
- if (!this.usdceToken || !this.wbtcToken || !this.usdcToken) {
134364
+ if (!this.wbtcToken || !this.usdcToken) {
133170
134365
  return [];
133171
134366
  }
133172
134367
  const walletAddress = this.metadata.additionalInfo.walletAddress;
133173
- const usdceWalletBalance = await new ERC20(this.config).balanceOf(
133174
- this.usdceToken.address,
133175
- walletAddress,
133176
- this.usdceToken.decimals
133177
- );
133178
134368
  const usdcWalletBalance = await new ERC20(this.config).balanceOf(
133179
134369
  this.usdcToken.address,
133180
134370
  walletAddress,
133181
134371
  this.usdcToken.decimals
133182
134372
  );
133183
- const price = await this.pricer.getPrice(this.usdceToken.symbol);
133184
- const wbtcPrice = await this.pricer.getPrice(this.wbtcToken.symbol);
133185
- const usdceUsdValue = Number(usdceWalletBalance.toFixed(this.usdceToken.decimals)) * price.price;
134373
+ const price = await this.pricer.getPrice(this.usdcToken.symbol);
133186
134374
  const usdcUsdValue = Number(usdcWalletBalance.toFixed(this.usdcToken.decimals)) * price.price;
133187
134375
  return [
133188
- {
133189
- tokenInfo: this.usdceToken,
133190
- amount: usdceWalletBalance,
133191
- usdValue: usdceUsdValue
133192
- },
133193
134376
  {
133194
134377
  tokenInfo: this.usdcToken,
133195
134378
  amount: usdcWalletBalance,
@@ -133206,9 +134389,6 @@ spurious results.`);
133206
134389
  const usdcToken = Global.getDefaultTokens().find(
133207
134390
  (token) => token.symbol === underlyingSymbol
133208
134391
  );
133209
- const usdceToken = Global.getDefaultTokens().find(
133210
- (token) => token.symbol === "USDC.e"
133211
- );
133212
134392
  const baseAdapterConfig = {
133213
134393
  baseToken: wbtcToken,
133214
134394
  supportedPositions: [
@@ -133229,17 +134409,10 @@ spurious results.`);
133229
134409
  minimumExtendedPriceDifferenceForSwapOpen,
133230
134410
  maximumExtendedPriceDifferenceForSwapClosing
133231
134411
  });
133232
- const usdcToUsdceAdapter = new UsdcToUsdceAdapter({
133233
- ...baseAdapterConfig,
133234
- supportedPositions: [
133235
- { asset: usdcToken, isDebt: true },
133236
- { asset: usdceToken, isDebt: false }
133237
- ]
133238
- });
133239
134412
  const extendedAdapter = new ExtendedAdapter({
133240
134413
  ...baseAdapterConfig,
133241
134414
  supportedPositions: [
133242
- { asset: usdceToken, isDebt: false }
134415
+ { asset: usdcToken, isDebt: false }
133243
134416
  ],
133244
134417
  vaultIdExtended,
133245
134418
  extendedContract: EXTENDED_CONTRACT,
@@ -133285,10 +134458,10 @@ spurious results.`);
133285
134458
  { asset: usdcToken, isDebt: true }
133286
134459
  ]
133287
134460
  });
133288
- const usdceTransferAdapter = new TokenTransferAdapter({
134461
+ const usdcTransferAdapter = new TokenTransferAdapter({
133289
134462
  ...baseAdapterConfig,
133290
- baseToken: usdceToken,
133291
- supportedPositions: [{ asset: usdceToken, isDebt: false }],
134463
+ baseToken: usdcToken,
134464
+ supportedPositions: [{ asset: usdcToken, isDebt: false }],
133292
134465
  fromAddress: vaultSettings.vaultAllocator,
133293
134466
  toAddress: ContractAddr.from(vaultSettings.walletAddress)
133294
134467
  });
@@ -133301,12 +134474,8 @@ spurious results.`);
133301
134474
  adapter: vesuModifyPositionAdapter
133302
134475
  });
133303
134476
  vaultSettings.adapters.push({
133304
- id: `${usdceTransferAdapter.name}_${usdceToken.symbol}`,
133305
- adapter: usdceTransferAdapter
133306
- });
133307
- vaultSettings.adapters.push({
133308
- id: `${usdcToUsdceAdapter.name}_${usdceToken.symbol}_${usdcToken.symbol}`,
133309
- adapter: usdcToUsdceAdapter
134477
+ id: `${usdcTransferAdapter.name}_${usdcToken.symbol}`,
134478
+ adapter: usdcTransferAdapter
133310
134479
  });
133311
134480
  vaultSettings.adapters.push({
133312
134481
  id: `${extendedAdapter.name}_${wbtcToken.symbol}`,
@@ -133328,12 +134497,10 @@ spurious results.`);
133328
134497
  vaultSettings.leafAdapters.push(() => vesuModifyPositionAdapter.getDepositLeaf());
133329
134498
  vaultSettings.leafAdapters.push(() => vesuModifyPositionAdapter.getWithdrawLeaf());
133330
134499
  vaultSettings.leafAdapters.push(() => extendedAdapter.getDepositLeaf());
133331
- vaultSettings.leafAdapters.push(() => usdcToUsdceAdapter.getDepositLeaf());
133332
- vaultSettings.leafAdapters.push(() => usdcToUsdceAdapter.getWithdrawLeaf());
133333
134500
  vaultSettings.leafAdapters.push(() => avnuAdapter.getDepositLeaf());
133334
134501
  vaultSettings.leafAdapters.push(() => avnuAdapter.getWithdrawLeaf());
133335
- vaultSettings.leafAdapters.push(() => usdceTransferAdapter.getDepositLeaf());
133336
- vaultSettings.leafAdapters.push(() => usdceTransferAdapter.getWithdrawLeaf());
134502
+ vaultSettings.leafAdapters.push(() => usdcTransferAdapter.getDepositLeaf());
134503
+ vaultSettings.leafAdapters.push(() => usdcTransferAdapter.getWithdrawLeaf());
133337
134504
  vaultSettings.leafAdapters.push(
133338
134505
  commonAdapter.getApproveAdapter(
133339
134506
  usdcToken.address,