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