@strkfarm/sdk 2.0.0-dev.28 → 2.0.0-dev.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.browser.global.js +1938 -771
- package/dist/index.browser.mjs +1960 -791
- package/dist/index.d.ts +343 -124
- package/dist/index.js +1971 -794
- package/dist/index.mjs +1963 -791
- package/package.json +1 -1
- package/src/dataTypes/bignumber.browser.ts +6 -1
- package/src/dataTypes/bignumber.node.ts +5 -1
- package/src/interfaces/common.tsx +8 -1
- package/src/strategies/universal-adapters/baseAdapter.ts +1 -1
- package/src/strategies/universal-adapters/extended-adapter.ts +7 -7
- package/src/strategies/universal-adapters/index.ts +2 -1
- package/src/strategies/universal-adapters/svk-troves-adapter.ts +364 -0
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +88 -34
- package/src/strategies/vesu-extended-strategy/services/executionService.ts +36 -55
- package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +1553 -538
- package/src/strategies/vesu-extended-strategy/services/ltv-imbalance-rebalance-math.ts +730 -0
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +4 -3
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +27 -83
- package/src/utils/index.ts +1 -0
|
@@ -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
|
-
|
|
122276
|
-
|
|
122277
|
-
|
|
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
|
-
|
|
122387
|
-
|
|
122388
|
-
|
|
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.
|
|
123301
|
+
this.usdcToken = this.config.supportedPositions[0].asset;
|
|
123244
123302
|
}
|
|
123245
123303
|
_depositApproveProofReadableId() {
|
|
123246
|
-
return `extended_approve_${this.
|
|
123304
|
+
return `extended_approve_${this.usdcToken.symbol}`;
|
|
123247
123305
|
}
|
|
123248
123306
|
_depositCallProofReadableId() {
|
|
123249
|
-
return `extended_deposit_${this.
|
|
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.
|
|
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.
|
|
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.
|
|
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 —
|
|
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
|
-
|
|
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
|
-
|
|
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 */]: [
|
|
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
|
-
|
|
129197
|
-
|
|
129198
|
-
|
|
129199
|
-
|
|
129200
|
-
|
|
129201
|
-
|
|
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.
|
|
129215
|
-
|
|
129216
|
-
|
|
129217
|
-
|
|
129218
|
-
|
|
129219
|
-
|
|
129220
|
-
|
|
129221
|
-
|
|
129222
|
-
|
|
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
|
-
|
|
129227
|
-
|
|
129228
|
-
|
|
129229
|
-
|
|
129230
|
-
|
|
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
|
-
*
|
|
129240
|
-
*
|
|
129241
|
-
|
|
129242
|
-
|
|
129243
|
-
|
|
129244
|
-
|
|
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
|
|
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.
|
|
129269
|
-
|
|
129270
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
130037
|
+
return this.bufferedUsd(this._rawTokenUsd(this.walletBalance));
|
|
129279
130038
|
}
|
|
129280
130039
|
get vaWalletUsd() {
|
|
129281
|
-
return this.
|
|
130040
|
+
return this.vaUsd + this.walletUsd;
|
|
129282
130041
|
}
|
|
129283
130042
|
get extAvailWithdraw() {
|
|
129284
|
-
|
|
130043
|
+
const raw = this.extendedBalance?.availableForWithdrawal?.toNumber() ?? 0;
|
|
130044
|
+
return this.bufferedUsd(raw);
|
|
129285
130045
|
}
|
|
129286
130046
|
get extAvailUpnl() {
|
|
129287
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
129297
|
-
|
|
129298
|
-
|
|
129299
|
-
|
|
129300
|
-
|
|
129301
|
-
|
|
129302
|
-
|
|
129303
|
-
|
|
129304
|
-
|
|
129305
|
-
|
|
129306
|
-
|
|
129307
|
-
|
|
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
|
-
|
|
129310
|
-
|
|
129311
|
-
|
|
129312
|
-
|
|
129313
|
-
|
|
129314
|
-
|
|
129315
|
-
|
|
129316
|
-
|
|
129317
|
-
|
|
129318
|
-
|
|
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
|
-
|
|
129323
|
-
|
|
129324
|
-
|
|
129325
|
-
|
|
129326
|
-
|
|
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
|
-
|
|
129329
|
-
|
|
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(
|
|
129336
|
-
|
|
129337
|
-
|
|
130260
|
+
_updateExtAvailWithdraw(desiredRaw, isSpend) {
|
|
130261
|
+
assert3(desiredRaw > 0, "SolveBudget::_updateExtAvailWithdraw amount must be positive");
|
|
130262
|
+
let rawDelta;
|
|
129338
130263
|
if (isSpend) {
|
|
129339
|
-
|
|
129340
|
-
|
|
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() +
|
|
129347
|
-
this.extendedBalance.availableForTrade = safeUsdcWeb3Number(this.extendedBalance.availableForTrade.toNumber() +
|
|
129348
|
-
this.extendedBalance.balance = safeUsdcWeb3Number(this.extendedBalance.balance.toNumber() +
|
|
129349
|
-
this.extendedBalance.equity = safeUsdcWeb3Number(this.extendedBalance.equity.toNumber() +
|
|
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
|
|
129352
|
-
return
|
|
130277
|
+
logger2.debug(`SolveBudget::updateExtAvailWithdraw rawDelta=${rawDelta}, extAvailWithdraw=${this.extAvailWithdraw}, totalUnused=${this.totalUnused}`);
|
|
130278
|
+
return rawDelta;
|
|
129353
130279
|
}
|
|
129354
|
-
_updateExtAvailUpnl(
|
|
129355
|
-
|
|
129356
|
-
|
|
130280
|
+
_updateExtAvailUpnl(desiredRaw, isSpend) {
|
|
130281
|
+
assert3(desiredRaw > 0, "SolveBudget::_updateExtAvailUpnl amount must be positive");
|
|
130282
|
+
let rawDelta;
|
|
129357
130283
|
if (isSpend) {
|
|
129358
|
-
|
|
129359
|
-
|
|
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() +
|
|
129365
|
-
this.extendedBalance.balance = safeUsdcWeb3Number(this.extendedBalance.balance.toNumber() +
|
|
129366
|
-
this.extendedBalance.equity = safeUsdcWeb3Number(this.extendedBalance.equity.toNumber() +
|
|
129367
|
-
this.extendedBalance.availableForTrade = safeUsdcWeb3Number(this.extendedBalance.availableForTrade.toNumber() +
|
|
129368
|
-
}
|
|
129369
|
-
logger2.debug(`SolveBudget::updateExtAvailUpnl
|
|
129370
|
-
return
|
|
129371
|
-
}
|
|
129372
|
-
spendExtAvailTrade(
|
|
129373
|
-
const used = this._updateExtAvailWithdraw(
|
|
129374
|
-
const usedUpnl = this._updateExtAvailUpnl(
|
|
129375
|
-
logger2.debug(`SolveBudget::updateExtAvailTrade
|
|
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
|
-
|
|
129379
|
-
return this.
|
|
130322
|
+
spendExtAvailWithdraw(rawDesired) {
|
|
130323
|
+
return this._updateExtAvailWithdraw(rawDesired, true);
|
|
129380
130324
|
}
|
|
129381
|
-
|
|
129382
|
-
this.
|
|
129383
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
/**
|
|
129442
|
-
|
|
129443
|
-
|
|
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:
|
|
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
|
-
|
|
130609
|
+
vaultAssetBalance,
|
|
130610
|
+
vaultUsdcBalance,
|
|
129532
130611
|
walletBalance,
|
|
129533
130612
|
vesuPoolStates,
|
|
129534
130613
|
extendedBalance,
|
|
129535
130614
|
extendedPositions
|
|
129536
130615
|
] = await Promise.all([
|
|
129537
|
-
this.
|
|
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(
|
|
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
|
-
|
|
130627
|
+
vaultAssetBalance,
|
|
130628
|
+
vaultUsdcBalance,
|
|
129546
130629
|
walletBalance
|
|
129547
130630
|
);
|
|
129548
|
-
this._budget =
|
|
129549
|
-
|
|
130631
|
+
this._budget = createSolveBudgetFromRawState({
|
|
130632
|
+
assetToken: this._config.assetToken,
|
|
130633
|
+
usdcToken: this._config.usdcToken,
|
|
129550
130634
|
unusedBalance,
|
|
129551
130635
|
walletBalance,
|
|
129552
|
-
|
|
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
|
|
130663
|
+
* Reads the {@link StateManagerConfig.assetToken} balance idle in the vault allocator.
|
|
129568
130664
|
*/
|
|
129569
|
-
async
|
|
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
|
-
*
|
|
129582
|
-
*
|
|
129583
|
-
|
|
129584
|
-
|
|
129585
|
-
|
|
129586
|
-
|
|
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(
|
|
130698
|
+
_computeUnusedBalances(vaultAssetBalance, vaultUsdcBalance, walletBalance) {
|
|
129589
130699
|
const balanceMap = /* @__PURE__ */ new Map();
|
|
129590
|
-
|
|
129591
|
-
token
|
|
129592
|
-
|
|
129593
|
-
|
|
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
|
|
129614
|
-
*
|
|
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
|
-
|
|
129622
|
-
usdceToken
|
|
130735
|
+
usdcToken
|
|
129623
130736
|
} = this._config;
|
|
129624
130737
|
const erc20 = new ERC20(networkConfig);
|
|
129625
|
-
const [
|
|
130738
|
+
const [balance, tokenPrice] = await Promise.all([
|
|
129626
130739
|
erc20.balanceOf(
|
|
129627
|
-
|
|
130740
|
+
usdcToken.address,
|
|
129628
130741
|
walletAddress,
|
|
129629
|
-
|
|
130742
|
+
usdcToken.decimals
|
|
129630
130743
|
),
|
|
129631
|
-
pricer.getPrice(
|
|
130744
|
+
pricer.getPrice(usdcToken.priceProxySymbol || usdcToken.symbol)
|
|
129632
130745
|
]);
|
|
129633
130746
|
return {
|
|
129634
|
-
token:
|
|
129635
|
-
amount:
|
|
129636
|
-
usdValue: Number(
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
129793
|
-
const { equity, availableForTrade } = this._budget.
|
|
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
|
-
*
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
131025
|
+
_computePerPoolCollateralDeltas(vesuAllocationUsd) {
|
|
129907
131026
|
const vesuLeverage = calculateVesuLeverage();
|
|
129908
|
-
const availableVesuCollateralAllocationUsd = vesuAllocationUsd
|
|
131027
|
+
const availableVesuCollateralAllocationUsd = vesuAllocationUsd;
|
|
129909
131028
|
const postLeverageAllocationUsd = availableVesuCollateralAllocationUsd.multipliedBy(vesuLeverage);
|
|
129910
131029
|
const totalCollateralExisting = this._totalVesuCollateral();
|
|
129911
|
-
return this._budget.
|
|
131030
|
+
return this._budget.vesuPools.map((pool, index) => {
|
|
129912
131031
|
const _postLeverageAllocation = postLeverageAllocationUsd.dividedBy(pool.collateralPrice);
|
|
129913
|
-
const postLeverageAllocation = new Web3Number(
|
|
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 =
|
|
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:
|
|
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.
|
|
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.
|
|
129979
|
-
const pool = this._budget.
|
|
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.
|
|
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.
|
|
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.
|
|
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).
|
|
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.
|
|
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 =
|
|
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.
|
|
130061
|
-
const pool = this._budget.
|
|
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.
|
|
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
|
|
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
|
|
130169
|
-
if (
|
|
130170
|
-
const walletUsed = this._budget.spendWallet(
|
|
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
|
|
130180
|
-
if (
|
|
130181
|
-
const walletUsed = this._budget.spendWallet(
|
|
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
|
|
130193
|
-
if (
|
|
130194
|
-
const vaUsed = this._budget.spendVA(
|
|
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
|
-
|
|
130208
|
-
const
|
|
130209
|
-
|
|
130210
|
-
const
|
|
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 -
|
|
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
|
|
130220
|
-
if (
|
|
130221
|
-
const walletUsed = this._budget.spendWallet(
|
|
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
|
|
130229
|
-
const
|
|
130230
|
-
if (
|
|
130231
|
-
|
|
130232
|
-
this._budget.addToExtAvailTrade(
|
|
130233
|
-
assert3(this._budget.
|
|
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(
|
|
130237
|
-
instrument: this._budget.
|
|
131367
|
+
amount: safeUsdcWeb3Number(usableRaw),
|
|
131368
|
+
instrument: this._budget.extendedPositionsView[0].instrument,
|
|
130238
131369
|
priority: routes.length
|
|
130239
131370
|
});
|
|
130240
|
-
return { routes, remaining: tryAmount -
|
|
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(
|
|
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
|
-
|
|
130279
|
-
|
|
130280
|
-
const
|
|
130281
|
-
|
|
130282
|
-
|
|
130283
|
-
|
|
130284
|
-
|
|
130285
|
-
|
|
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:
|
|
130291
|
-
swappedCollateralAmount: new Web3Number(
|
|
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(
|
|
130297
|
-
|
|
130298
|
-
|
|
130299
|
-
|
|
130300
|
-
|
|
130301
|
-
|
|
130302
|
-
|
|
130303
|
-
|
|
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 (
|
|
131480
|
+
if (extBtcDelta > 0) {
|
|
130311
131481
|
routes.push({
|
|
130312
131482
|
type: "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
|
|
130313
|
-
amount: safeUsdcWeb3Number(
|
|
131483
|
+
amount: safeUsdcWeb3Number(-r6(extBtcDelta)),
|
|
130314
131484
|
instrument,
|
|
130315
131485
|
priority: routes.length
|
|
130316
131486
|
});
|
|
130317
|
-
this._budget.applyExtendedExposureDelta(
|
|
130318
|
-
|
|
130319
|
-
|
|
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
|
-
|
|
130353
|
-
|
|
130354
|
-
|
|
130355
|
-
|
|
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
|
-
|
|
130358
|
-
|
|
130359
|
-
|
|
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[
|
|
130398
|
-
additionalArgs: { amount: safeUsdcWeb3Number(
|
|
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
|
-
|
|
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 (
|
|
130613
|
-
*
|
|
130614
|
-
*
|
|
130615
|
-
*
|
|
130616
|
-
*
|
|
130617
|
-
|
|
130618
|
-
|
|
131963
|
+
* Fund flow (single pass — avoid 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.
|
|
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
|
-
|
|
130635
|
-
|
|
130636
|
-
|
|
130637
|
-
|
|
130638
|
-
|
|
130639
|
-
|
|
130640
|
-
|
|
130641
|
-
|
|
130642
|
-
|
|
130643
|
-
|
|
130644
|
-
|
|
130645
|
-
|
|
130646
|
-
|
|
130647
|
-
|
|
130648
|
-
|
|
130649
|
-
|
|
130650
|
-
|
|
130651
|
-
|
|
130652
|
-
|
|
130653
|
-
|
|
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
|
|
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.
|
|
130819
|
-
this._budget.
|
|
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
|
-
|
|
130886
|
-
const
|
|
130887
|
-
const depositCases = this._classifyDeposits(
|
|
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
|
-
...
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
131006
|
-
config3.
|
|
131007
|
-
config3.
|
|
131008
|
-
config3.
|
|
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.
|
|
131018
|
-
config3.
|
|
131019
|
-
config3.
|
|
131020
|
-
config3.
|
|
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.
|
|
131029
|
-
name: `${config3.
|
|
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.
|
|
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.
|
|
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
|
|
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 {
|
|
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 **
|
|
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:
|
|
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
|
|
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 [
|
|
133305
|
+
return [manageCall];
|
|
131950
133306
|
}
|
|
131951
133307
|
/**
|
|
131952
|
-
* WALLET_TO_VA: Transfer USDC
|
|
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
|
|
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.
|
|
133320
|
+
this._config.usdcTransferAdapter,
|
|
131967
133321
|
false,
|
|
131968
133322
|
{ amount: transferAmount }
|
|
131969
133323
|
);
|
|
131970
|
-
|
|
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.
|
|
133357
|
+
this._config.vesuMultiplyAdapter,
|
|
132009
133358
|
true,
|
|
132010
133359
|
{
|
|
132011
133360
|
amount: route.marginAmount,
|
|
132012
133361
|
marginSwap: {
|
|
132013
|
-
marginToken: this._config.
|
|
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.
|
|
133382
|
+
this._config.vesuMultiplyAdapter,
|
|
132034
133383
|
false,
|
|
132035
133384
|
{
|
|
132036
133385
|
amount: collateralAmount,
|
|
132037
|
-
withdrawSwap: { outputToken: this._config.
|
|
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:
|
|
132803
|
-
|
|
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
|
|
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
|
|
132861
|
-
const
|
|
134072
|
+
async getUsdcTransferAdapter() {
|
|
134073
|
+
const usdcTransferAdapter = this.metadata.additionalInfo.adapters.find(
|
|
132862
134074
|
(adapter2) => adapter2.adapter.name === TokenTransferAdapter.name
|
|
132863
134075
|
);
|
|
132864
|
-
if (!
|
|
134076
|
+
if (!usdcTransferAdapter) {
|
|
132865
134077
|
throw new Error(
|
|
132866
|
-
`${this.getTag()}
|
|
134078
|
+
`${this.getTag()} Usdc transfer adapter not configured in metadata.`
|
|
132867
134079
|
);
|
|
132868
134080
|
}
|
|
132869
|
-
return
|
|
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
|
-
|
|
134121
|
+
vesuMultiplyAdapter,
|
|
132921
134122
|
vesuModifyPositionAdapter,
|
|
132922
|
-
usdceTransferAdapter,
|
|
132923
134123
|
extendedAdapter,
|
|
132924
134124
|
avnuAdapter,
|
|
132925
|
-
|
|
134125
|
+
usdcTransferAdapter
|
|
132926
134126
|
] = await Promise.all([
|
|
132927
|
-
this.
|
|
134127
|
+
this.getVesuMultiplyAdapter(),
|
|
132928
134128
|
this.getVesuModifyPositionAdapter(),
|
|
132929
|
-
this.getUsdceTransferAdapter(),
|
|
132930
134129
|
this.getExtendedAdapter(),
|
|
132931
134130
|
this.getAvnuAdapter(),
|
|
132932
|
-
this.
|
|
134131
|
+
this.getUsdcTransferAdapter()
|
|
132933
134132
|
]);
|
|
132934
134133
|
const executionConfig = {
|
|
132935
134134
|
networkConfig: this.config,
|
|
132936
134135
|
pricer: this.pricer,
|
|
132937
|
-
|
|
134136
|
+
vesuMultiplyAdapter,
|
|
132938
134137
|
vesuModifyPositionAdapter,
|
|
132939
134138
|
extendedAdapter,
|
|
132940
134139
|
avnuAdapter,
|
|
132941
|
-
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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:
|
|
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
|
|
134461
|
+
const usdcTransferAdapter = new TokenTransferAdapter({
|
|
133289
134462
|
...baseAdapterConfig,
|
|
133290
|
-
baseToken:
|
|
133291
|
-
supportedPositions: [{ asset:
|
|
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: `${
|
|
133305
|
-
adapter:
|
|
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(() =>
|
|
133336
|
-
vaultSettings.leafAdapters.push(() =>
|
|
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,
|