@strkfarm/sdk 2.0.0-dev.29 → 2.0.0-dev.30
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 +543 -258
- package/dist/index.browser.mjs +466 -181
- package/dist/index.d.ts +44 -25
- package/dist/index.js +466 -181
- package/dist/index.mjs +466 -181
- package/package.json +1 -1
- package/src/modules/ExtendedWrapperSDk/types.ts +1 -0
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +153 -11
- package/src/modules/ekubo-quoter.ts +2 -0
- package/src/strategies/universal-adapters/extended-adapter.ts +15 -14
- package/src/strategies/universal-adapters/vesu-adapter.ts +4 -1
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +26 -17
- package/src/strategies/vesu-extended-strategy/services/executionService.ts +15 -14
- package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +265 -134
- package/src/strategies/vesu-extended-strategy/services/ltv-imbalance-rebalance-math.ts +73 -20
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +59 -9
- package/src/utils/cacheClass.ts +11 -2
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
calculateVesuLeverage,
|
|
14
14
|
} from "../utils/helper";
|
|
15
15
|
import { VesuConfig } from "../utils/config.runtime";
|
|
16
|
-
import { rebalance, type RebalanceDeltas } from "./ltv-imbalance-rebalance-math";
|
|
16
|
+
import { rebalance, routableDrawAmount, type RebalanceDeltas } from "./ltv-imbalance-rebalance-math";
|
|
17
17
|
|
|
18
18
|
// ─── State types ───────────────────────────────────────────────────────────────
|
|
19
19
|
|
|
@@ -77,6 +77,8 @@ type Deltas = {
|
|
|
77
77
|
dVaUsd: number;
|
|
78
78
|
dWalletUsd: number;
|
|
79
79
|
dVesuBorrowCapacity: number;
|
|
80
|
+
finalVaUsd: number;
|
|
81
|
+
finalExtended: number;
|
|
80
82
|
isExtendedToVesu: boolean;
|
|
81
83
|
};
|
|
82
84
|
|
|
@@ -748,12 +750,12 @@ export class SolveBudget {
|
|
|
748
750
|
// ── Refreshed state (mutable during solve, raw on-chain / API values) ─
|
|
749
751
|
private unusedBalance: TokenBalance[];
|
|
750
752
|
private walletBalance: TokenBalance | null;
|
|
751
|
-
/** Idle {@link StateManagerConfig.assetToken} in the vault allocator */
|
|
752
|
-
private vaultAssetBalance: TokenBalance | null;
|
|
753
753
|
/**
|
|
754
|
-
* Idle {@link StateManagerConfig.
|
|
755
|
-
*
|
|
754
|
+
* Idle non-stable {@link StateManagerConfig.assetToken} in the vault allocator.
|
|
755
|
+
* Null when asset and USDC are the same token (VA idle stablecoin is only in {@link vaultUsdcBalance}).
|
|
756
756
|
*/
|
|
757
|
+
private vaultAssetBalance: TokenBalance | null;
|
|
758
|
+
/** Idle {@link StateManagerConfig.usdcToken} in the vault allocator (including when it is also the strategy asset). */
|
|
757
759
|
private vaultUsdcBalance: TokenBalance | null;
|
|
758
760
|
private extendedPositions: ExtendedPositionState[];
|
|
759
761
|
private extendedBalance: ExtendedBalanceState | null;
|
|
@@ -1037,9 +1039,10 @@ export class SolveBudget {
|
|
|
1037
1039
|
|
|
1038
1040
|
// ── Derived getters (buffered where applicable) ─────────────────────
|
|
1039
1041
|
|
|
1040
|
-
/** Buffered VA USD:
|
|
1042
|
+
/** Buffered VA USD: non-stable asset slot (if any) + USDC slot. */
|
|
1041
1043
|
get vaUsd(): number {
|
|
1042
|
-
return this.bufferedTokenUsd(this.vaultAssetBalance) + this.bufferedTokenUsd(this.vaultUsdcBalance);
|
|
1044
|
+
// return this.bufferedTokenUsd(this.vaultAssetBalance) + this.bufferedTokenUsd(this.vaultUsdcBalance);
|
|
1045
|
+
return this.bufferedTokenUsd(this.vaultUsdcBalance); // we dont have swap routes and is not correct, to consider asset balance directlyt in strategy
|
|
1043
1046
|
}
|
|
1044
1047
|
|
|
1045
1048
|
/** Buffered USD in VA strategy-asset bucket only. */
|
|
@@ -1047,7 +1050,7 @@ export class SolveBudget {
|
|
|
1047
1050
|
return this.bufferedTokenUsd(this.vaultAssetBalance);
|
|
1048
1051
|
}
|
|
1049
1052
|
|
|
1050
|
-
/** Buffered USD in VA USDC bucket (
|
|
1053
|
+
/** Buffered USD in VA USDC bucket (includes full VA idle when asset === USDC). */
|
|
1051
1054
|
get vaUsdcUsd(): number {
|
|
1052
1055
|
return this.bufferedTokenUsd(this.vaultUsdcBalance);
|
|
1053
1056
|
}
|
|
@@ -1146,9 +1149,15 @@ export class SolveBudget {
|
|
|
1146
1149
|
return this.shouldVesuRebalance;
|
|
1147
1150
|
}
|
|
1148
1151
|
|
|
1149
|
-
/** Raw USD in VA (USDC slot + asset slot); spend caps when executing transfers. */
|
|
1152
|
+
/** Raw USD in VA (USDC slot + non-stable asset slot when distinct); spend caps when executing transfers. */
|
|
1150
1153
|
private _vaRawUsd(): number {
|
|
1151
|
-
return this._rawTokenUsd(this.vaultUsdcBalance) + this._rawTokenUsd(this.vaultAssetBalance);
|
|
1154
|
+
// return this._rawTokenUsd(this.vaultUsdcBalance) + this._rawTokenUsd(this.vaultAssetBalance);
|
|
1155
|
+
return this._rawTokenUsd(this.vaultUsdcBalance); // we dont have swap routes and is not correct, to consider asset balance directlyt in strategy
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/** VA liquidity usable for repay / {@link spendVaRawUsd} (matches nominal balances after any {@link applyBuffer} scaling). */
|
|
1159
|
+
get vaRawUsd(): number {
|
|
1160
|
+
return this._vaRawUsd();
|
|
1152
1161
|
}
|
|
1153
1162
|
|
|
1154
1163
|
private _walletRawUsd(): number {
|
|
@@ -1235,7 +1244,8 @@ export class SolveBudget {
|
|
|
1235
1244
|
rem -= fromUsdc;
|
|
1236
1245
|
}
|
|
1237
1246
|
if (rem > 0 && this.vaultAssetBalance) {
|
|
1238
|
-
this._deductUsdFromTokenBalance(this.vaultAssetBalance, rem);
|
|
1247
|
+
// this._deductUsdFromTokenBalance(this.vaultAssetBalance, rem);
|
|
1248
|
+
throw new Error(`Not implemented: spendVA with vaultAssetBalance`);
|
|
1239
1249
|
}
|
|
1240
1250
|
this._recomputeUnusedBalance();
|
|
1241
1251
|
logger.debug(`SolveBudget::spendVA usedRaw=${usedRaw}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
|
|
@@ -1247,7 +1257,8 @@ export class SolveBudget {
|
|
|
1247
1257
|
*/
|
|
1248
1258
|
spendVaRawUsd(rawUsdDesired: number): number {
|
|
1249
1259
|
const capRaw =
|
|
1250
|
-
this._rawTokenUsd(this.vaultUsdcBalance) + this._rawTokenUsd(this.vaultAssetBalance);
|
|
1260
|
+
// this._rawTokenUsd(this.vaultUsdcBalance) + this._rawTokenUsd(this.vaultAssetBalance);
|
|
1261
|
+
this._rawTokenUsd(this.vaultUsdcBalance); // we dont have swap routes and is not correct, to consider asset balance directlyt in strategy
|
|
1251
1262
|
const usedRaw = Math.min(capRaw, Math.max(0, rawUsdDesired));
|
|
1252
1263
|
if (usedRaw <= CASE_THRESHOLD_USD) return 0;
|
|
1253
1264
|
let rem = usedRaw;
|
|
@@ -1257,7 +1268,8 @@ export class SolveBudget {
|
|
|
1257
1268
|
rem -= fromUsdc;
|
|
1258
1269
|
}
|
|
1259
1270
|
if (rem > 0 && this.vaultAssetBalance) {
|
|
1260
|
-
this._deductUsdFromTokenBalance(this.vaultAssetBalance, rem);
|
|
1271
|
+
// this._deductUsdFromTokenBalance(this.vaultAssetBalance, rem);
|
|
1272
|
+
throw new Error(`Not implemented: spendVaRawUsd with vaultAssetBalance`);
|
|
1261
1273
|
}
|
|
1262
1274
|
this._recomputeUnusedBalance();
|
|
1263
1275
|
logger.debug(`SolveBudget::spendVaRawUsd usedRaw=${usedRaw}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
|
|
@@ -1273,7 +1285,8 @@ export class SolveBudget {
|
|
|
1273
1285
|
if (this.vaultUsdcBalance) {
|
|
1274
1286
|
this._addUsdToTokenBalance(this.vaultUsdcBalance, rawUsd);
|
|
1275
1287
|
} else if (this.vaultAssetBalance) {
|
|
1276
|
-
this._addUsdToTokenBalance(this.vaultAssetBalance, rawUsd);
|
|
1288
|
+
// this._addUsdToTokenBalance(this.vaultAssetBalance, rawUsd);
|
|
1289
|
+
throw new Error(`Not implemented: addToVA with vaultAssetBalance`);
|
|
1277
1290
|
}
|
|
1278
1291
|
this._recomputeUnusedBalance();
|
|
1279
1292
|
logger.debug(`SolveBudget::addToVA rawUsd=${rawUsd}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
|
|
@@ -1316,7 +1329,7 @@ export class SolveBudget {
|
|
|
1316
1329
|
if (isSpend) {
|
|
1317
1330
|
const capRaw = this.extendedBalance?.availableForWithdrawal?.toNumber() ?? 0;
|
|
1318
1331
|
const useRaw = Math.min(capRaw, desiredRaw);
|
|
1319
|
-
if (useRaw <=
|
|
1332
|
+
if (useRaw <= 0) return 0;
|
|
1320
1333
|
rawDelta = -useRaw;
|
|
1321
1334
|
} else {
|
|
1322
1335
|
rawDelta = desiredRaw;
|
|
@@ -1338,7 +1351,7 @@ export class SolveBudget {
|
|
|
1338
1351
|
if (isSpend) {
|
|
1339
1352
|
const capRaw = this.extendedBalance?.unrealisedPnl?.toNumber() ?? 0;
|
|
1340
1353
|
const useRaw = Math.min(capRaw, desiredRaw);
|
|
1341
|
-
if (useRaw <=
|
|
1354
|
+
if (useRaw <= 0) return 0;
|
|
1342
1355
|
rawDelta = -useRaw;
|
|
1343
1356
|
} else {
|
|
1344
1357
|
rawDelta = desiredRaw;
|
|
@@ -1355,10 +1368,12 @@ export class SolveBudget {
|
|
|
1355
1368
|
}
|
|
1356
1369
|
|
|
1357
1370
|
spendExtAvailTrade(rawDesired: number): number {
|
|
1358
|
-
const
|
|
1359
|
-
const
|
|
1360
|
-
|
|
1361
|
-
|
|
1371
|
+
const usedWd = this._updateExtAvailWithdraw(rawDesired, true);
|
|
1372
|
+
const tookWd = Math.abs(usedWd);
|
|
1373
|
+
const rem = rawDesired - tookWd;
|
|
1374
|
+
const usedUpnl = rem > 0 ? this._updateExtAvailUpnl(rem, true) : 0;
|
|
1375
|
+
logger.debug(`SolveBudget::updateExtAvailTrade rawSum=${usedWd + usedUpnl}, extAvailTrade=${this.extAvailTrade}, totalUnused=${this.totalUnused}`);
|
|
1376
|
+
return usedWd + usedUpnl;
|
|
1362
1377
|
}
|
|
1363
1378
|
|
|
1364
1379
|
// simply reduces available amounts, but maintains equity and balance.
|
|
@@ -1801,8 +1816,9 @@ export class ExtendedSVKVesuStateManager {
|
|
|
1801
1816
|
]);
|
|
1802
1817
|
|
|
1803
1818
|
logger.verbose(
|
|
1804
|
-
`${this._tag}::_refresh
|
|
1805
|
-
`${
|
|
1819
|
+
`${this._tag}::_refresh ` +
|
|
1820
|
+
`${vaultAssetBalance ? `VA asset ${vaultAssetBalance.token.symbol}=$${vaultAssetBalance.usdValue.toFixed(2)}, ` : ""}` +
|
|
1821
|
+
`VA USDC=${vaultUsdcBalance.usdValue.toFixed(2)}` +
|
|
1806
1822
|
`, wallet=${walletBalance.usdValue}`,
|
|
1807
1823
|
);
|
|
1808
1824
|
const unusedBalance = this._computeUnusedBalances(
|
|
@@ -1839,6 +1855,7 @@ export class ExtendedSVKVesuStateManager {
|
|
|
1839
1855
|
},
|
|
1840
1856
|
vesuPoolStates,
|
|
1841
1857
|
});
|
|
1858
|
+
this._budget.logStateSummary();
|
|
1842
1859
|
|
|
1843
1860
|
const totalUnusedUsd = unusedBalance.reduce(
|
|
1844
1861
|
(acc, b) => acc + b.usdValue,
|
|
@@ -1846,10 +1863,10 @@ export class ExtendedSVKVesuStateManager {
|
|
|
1846
1863
|
);
|
|
1847
1864
|
logger.info(
|
|
1848
1865
|
`${this._tag}::_refresh completed — ` +
|
|
1849
|
-
`unusedBalances: ${unusedBalance.length} tokens, ` +
|
|
1866
|
+
`unusedBalances: ${unusedBalance.length} tokens [${unusedBalance.map((b) => `${b.token.symbol}=$${b.usdValue.toFixed(2)}`).join(', ')}], ` +
|
|
1850
1867
|
`totalUnusedUsd: ${totalUnusedUsd.toFixed(2)}, ` +
|
|
1851
|
-
`extendedPositions: ${extendedPositions.length}, ` +
|
|
1852
|
-
`vesuPools: ${vesuPoolStates.length}
|
|
1868
|
+
`extendedPositions: ${extendedPositions.length} [${extendedPositions.map((p) => `${p.instrument}=${p.size.toFixed(6)} ${p.side}, ${p.valueUsd.toFixed(6)} ${p.instrument}`).join(', ')}], ` +
|
|
1869
|
+
`vesuPools: ${vesuPoolStates.length} [${vesuPoolStates.map((p) => `${p.poolId.shortString()}=${p.debtAmount.toFixed(6)} ${p.debtToken.symbol}, ${p.collateralAmount.toFixed(6)} ${p.collateralToken.symbol}`).join(', ')}], ` +
|
|
1853
1870
|
`availableForTrade: ${extendedBalance?.availableForTrade.toNumber()} - ` +
|
|
1854
1871
|
`availableForWithdrawal: ${extendedBalance?.availableForWithdrawal.toNumber()} - ` +
|
|
1855
1872
|
`unrealisedPnl: ${extendedBalance?.unrealisedPnl.toNumber()} - ` +
|
|
@@ -1861,15 +1878,19 @@ export class ExtendedSVKVesuStateManager {
|
|
|
1861
1878
|
|
|
1862
1879
|
// todo add communication check with python server of extended. if not working, throw error in solve function.
|
|
1863
1880
|
|
|
1864
|
-
/** True when strategy asset and USDC share one token — VA
|
|
1881
|
+
/** True when strategy asset and USDC share one token — VA idle balance is tracked as USDC, not as asset. */
|
|
1865
1882
|
private _vaultAssetAndUsdcAreSameToken(): boolean {
|
|
1866
1883
|
return this._config.assetToken.address.eq(this._config.usdcToken.address);
|
|
1867
1884
|
}
|
|
1868
1885
|
|
|
1869
1886
|
/**
|
|
1870
|
-
* Reads
|
|
1887
|
+
* Reads idle {@link StateManagerConfig.assetToken} in the vault allocator when it differs from USDC.
|
|
1888
|
+
* When asset and USDC are the same token, returns null (that balance is reported via {@link _fetchVaultAllocatorUsdcBalanceIfDistinct} only).
|
|
1871
1889
|
*/
|
|
1872
|
-
private async _fetchVaultAllocatorAssetBalance(): Promise<TokenBalance> {
|
|
1890
|
+
private async _fetchVaultAllocatorAssetBalance(): Promise<TokenBalance | null> {
|
|
1891
|
+
if (this._vaultAssetAndUsdcAreSameToken()) {
|
|
1892
|
+
return null;
|
|
1893
|
+
}
|
|
1873
1894
|
const { assetToken, vaultAllocator, networkConfig, pricer } = this._config;
|
|
1874
1895
|
const balance = await new ERC20(networkConfig).balanceOf(
|
|
1875
1896
|
assetToken.address,
|
|
@@ -1884,12 +1905,9 @@ export class ExtendedSVKVesuStateManager {
|
|
|
1884
1905
|
}
|
|
1885
1906
|
|
|
1886
1907
|
/**
|
|
1887
|
-
* Reads {@link StateManagerConfig.usdcToken} in the vault allocator
|
|
1888
|
-
* {@link StateManagerConfig.assetToken}. Otherwise returns null (treat VA USDC as 0; stablecoin is only under asset).
|
|
1908
|
+
* Reads {@link StateManagerConfig.usdcToken} idle in the vault allocator (always — distinct asset or USDC-as-asset).
|
|
1889
1909
|
*/
|
|
1890
|
-
private async _fetchVaultAllocatorUsdcBalanceIfDistinct(): Promise<TokenBalance
|
|
1891
|
-
if (this._vaultAssetAndUsdcAreSameToken()) return null;
|
|
1892
|
-
|
|
1910
|
+
private async _fetchVaultAllocatorUsdcBalanceIfDistinct(): Promise<TokenBalance> {
|
|
1893
1911
|
const { usdcToken, vaultAllocator, networkConfig, pricer } = this._config;
|
|
1894
1912
|
const balance = await new ERC20(networkConfig).balanceOf(
|
|
1895
1913
|
usdcToken.address,
|
|
@@ -1906,12 +1924,12 @@ export class ExtendedSVKVesuStateManager {
|
|
|
1906
1924
|
}
|
|
1907
1925
|
|
|
1908
1926
|
/**
|
|
1909
|
-
* Merges vault-allocator asset,
|
|
1927
|
+
* Merges vault-allocator asset (if any), vault-allocator USDC, and operator wallet
|
|
1910
1928
|
* balances into entries keyed by token address.
|
|
1911
1929
|
*/
|
|
1912
1930
|
private _computeUnusedBalances(
|
|
1913
|
-
vaultAssetBalance: TokenBalance,
|
|
1914
|
-
vaultUsdcBalance: TokenBalance
|
|
1931
|
+
vaultAssetBalance: TokenBalance | null,
|
|
1932
|
+
vaultUsdcBalance: TokenBalance,
|
|
1915
1933
|
walletBalance: TokenBalance,
|
|
1916
1934
|
): TokenBalance[] {
|
|
1917
1935
|
const balanceMap = new Map<string, TokenBalance>();
|
|
@@ -1924,8 +1942,8 @@ export class ExtendedSVKVesuStateManager {
|
|
|
1924
1942
|
});
|
|
1925
1943
|
};
|
|
1926
1944
|
|
|
1927
|
-
put(vaultAssetBalance);
|
|
1928
|
-
|
|
1945
|
+
if (vaultAssetBalance) put(vaultAssetBalance);
|
|
1946
|
+
put(vaultUsdcBalance);
|
|
1929
1947
|
|
|
1930
1948
|
// Merge wallet balances by token address
|
|
1931
1949
|
const key = walletBalance.token.address.toString();
|
|
@@ -2590,7 +2608,14 @@ export class ExtendedSVKVesuStateManager {
|
|
|
2590
2608
|
// ── Atomic route builders ────────────────────────────────────────────
|
|
2591
2609
|
|
|
2592
2610
|
private _buildVesuRepayRoutes(totalUsd: number, routes: ExecutionRoute[]): void {
|
|
2593
|
-
|
|
2611
|
+
// Repay consumes VA USDC/asset; do not plan more than the budget VA holds after prior
|
|
2612
|
+
// routes (borrow→VA, Vesu→VA, wallet→VA, etc.). Otherwise repayVesuBorrowCapacity would
|
|
2613
|
+
// mutate Vesu debt while spendVaRawUsd could not fund it.
|
|
2614
|
+
const vaCap = this._budget.vaRawUsd;
|
|
2615
|
+
const repayUsd = Math.min(totalUsd, vaCap);
|
|
2616
|
+
if (repayUsd <= CASE_THRESHOLD_USD) return;
|
|
2617
|
+
|
|
2618
|
+
const { used, spendsByPool } = this._budget.repayVesuBorrowCapacity(repayUsd);
|
|
2594
2619
|
for (const route of spendsByPool) {
|
|
2595
2620
|
routes.push({ type: RouteType.VESU_REPAY as const, ...route, priority: routes.length });
|
|
2596
2621
|
}
|
|
@@ -2699,7 +2724,7 @@ export class ExtendedSVKVesuStateManager {
|
|
|
2699
2724
|
}
|
|
2700
2725
|
|
|
2701
2726
|
private _getVAToEXTENDEDRoute(tryAmount: number, routes: ExecutionRoute[], shouldAddWaitRoute = true): { routes: ExecutionRoute[], remaining: number } {
|
|
2702
|
-
const usable = Math.min(tryAmount, this._budget.
|
|
2727
|
+
const usable = Math.min(tryAmount, this._budget.vaUsdcUsd);
|
|
2703
2728
|
if (usable > CASE_THRESHOLD_USD) {
|
|
2704
2729
|
const vaUsed = this._budget.spendVA(usable);
|
|
2705
2730
|
this._budget.addToExtAvailTrade(vaUsed);
|
|
@@ -2719,7 +2744,7 @@ export class ExtendedSVKVesuStateManager {
|
|
|
2719
2744
|
|
|
2720
2745
|
private _getExtendedToWalletRoute(tryAmount: number, routes: ExecutionRoute[], shouldAddWaitRoute = true): { routes: ExecutionRoute[], remaining: number } {
|
|
2721
2746
|
if (tryAmount <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
|
|
2722
|
-
const rawCap = this._budget.extAvailWithdraw + this._budget.extAvailUpnl;
|
|
2747
|
+
const rawCap = this._budget.extAvailWithdraw + Math.max(0, this._budget.extAvailUpnl);
|
|
2723
2748
|
const rawSpend = Math.min(tryAmount, rawCap);
|
|
2724
2749
|
if (rawSpend <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
|
|
2725
2750
|
const rawOut = this._budget.spendExtAvailTrade(rawSpend);
|
|
@@ -2862,6 +2887,10 @@ export class ExtendedSVKVesuStateManager {
|
|
|
2862
2887
|
}
|
|
2863
2888
|
}
|
|
2864
2889
|
|
|
2890
|
+
// todo rereviewe logic on how neg upnl gets handled
|
|
2891
|
+
// esp when equity is below ideal margin, 0 ext withdraw,
|
|
2892
|
+
// and how does closing free capital.
|
|
2893
|
+
|
|
2865
2894
|
// Step 5b: Equal unwind from both sides
|
|
2866
2895
|
if (stillNeeded > CASE_THRESHOLD_USD) {
|
|
2867
2896
|
const combinedFreed = freedPerBtcVesu + freedPerBtcExt;
|
|
@@ -2954,7 +2983,9 @@ export class ExtendedSVKVesuStateManager {
|
|
|
2954
2983
|
* Design: accumulate all ext-to-wallet moves, add transfer routes at the end (principle #3).
|
|
2955
2984
|
*/
|
|
2956
2985
|
/**
|
|
2957
|
-
* Unified LTV classifier
|
|
2986
|
+
* Unified LTV / exposure classifier: `rebalance()` drives both LTV (debt, margin,
|
|
2987
|
+
* funding) and Vesu↔Extended position alignment. There is no separate imbalance pass.
|
|
2988
|
+
* Computes both Vesu repay and Extended margin needs,
|
|
2958
2989
|
* then builds all routes in a single pass with no duplicate transfers.
|
|
2959
2990
|
*
|
|
2960
2991
|
* Vesu repay priority: VA > Wallet > ExtAvl > ExtUpnl
|
|
@@ -3025,6 +3056,7 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3025
3056
|
config: {
|
|
3026
3057
|
positionPrecision: COLLATERAL_PRECISION,
|
|
3027
3058
|
hfBuffer: 0.05,
|
|
3059
|
+
minRoutableUsd: CASE_THRESHOLD_USD,
|
|
3028
3060
|
},
|
|
3029
3061
|
};
|
|
3030
3062
|
}
|
|
@@ -3047,8 +3079,9 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3047
3079
|
|
|
3048
3080
|
/**
|
|
3049
3081
|
* Turn pure rebalance() deltas into execution routes.
|
|
3050
|
-
* Order: Vesu multiply
|
|
3051
|
-
* (REALISE_PNL, EXTENDED_TO_WALLET + WAIT, WALLET_TO_VA, VESU_BORROW, VESU_REPAY, VA_TO_EXTENDED)
|
|
3082
|
+
* Order: Vesu multiply decrease → Extended decrease → aggregated transfers
|
|
3083
|
+
* (REALISE_PNL, EXTENDED_TO_WALLET + WAIT, WALLET_TO_VA, VESU_BORROW, VESU_REPAY, VA_TO_EXTENDED),
|
|
3084
|
+
* then Vesu multiply increase and Extended increase (need VA / Extended funded first).
|
|
3052
3085
|
*/
|
|
3053
3086
|
private _buildLtvRoutesFromRebalanceDeltas(d: RebalanceDeltas): ExecutionRoute[] {
|
|
3054
3087
|
const routes: ExecutionRoute[] = [];
|
|
@@ -3062,7 +3095,9 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3062
3095
|
|
|
3063
3096
|
let multiplyDebtRepayUsd = 0;
|
|
3064
3097
|
|
|
3065
|
-
|
|
3098
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas d=${JSON.stringify(d)}`);
|
|
3099
|
+
|
|
3100
|
+
// ── 1) Vesu multiply decrease (free collateral / repay) ─────────────
|
|
3066
3101
|
if (d.dVesuPosition < -btcEps) {
|
|
3067
3102
|
const xBtc = -d.dVesuPosition;
|
|
3068
3103
|
// When Vesu sends USD to Extended (dTransferVesuToExt > 0), part of the BTC cut must be the
|
|
@@ -3085,12 +3120,29 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3085
3120
|
if (d.dVesuDebt < 0) {
|
|
3086
3121
|
const needRepayUsd = -d.dVesuDebt * debtPrice;
|
|
3087
3122
|
const multiplyRepayUsd = Math.min(needRepayUsd, swapLegMaxRepayUsd);
|
|
3123
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas ${JSON.stringify({
|
|
3124
|
+
needRepayUsd,
|
|
3125
|
+
multiplyRepayUsd,
|
|
3126
|
+
})}`);
|
|
3088
3127
|
debtTokenDelta = -(multiplyRepayUsd / debtPrice);
|
|
3089
3128
|
} else {
|
|
3090
3129
|
debtTokenDelta = -debtUsdFallback;
|
|
3091
3130
|
}
|
|
3131
|
+
|
|
3092
3132
|
const debtAmtW3 = new Web3Number(debtTokenDelta.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
|
|
3093
3133
|
multiplyDebtRepayUsd = Math.abs(debtTokenDelta) * debtPrice;
|
|
3134
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas ${JSON.stringify({
|
|
3135
|
+
debtTokenDelta,
|
|
3136
|
+
debtUsdFallback,
|
|
3137
|
+
swapLegMaxRepayUsd,
|
|
3138
|
+
xBtc,
|
|
3139
|
+
marginBtc,
|
|
3140
|
+
swappedBtc,
|
|
3141
|
+
transferUsdFromVesu,
|
|
3142
|
+
debtPrice,
|
|
3143
|
+
targetLtv,
|
|
3144
|
+
multiplyDebtRepayUsd,
|
|
3145
|
+
})}`);
|
|
3094
3146
|
routes.push({
|
|
3095
3147
|
type: RouteType.VESU_MULTIPLY_DECREASE_LEVER,
|
|
3096
3148
|
poolId: vesuAdapter.config.poolId,
|
|
@@ -3111,57 +3163,9 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3111
3163
|
if (transferUsdFromVesu > CASE_THRESHOLD_USD) {
|
|
3112
3164
|
this._budget.addToVA(transferUsdFromVesu);
|
|
3113
3165
|
}
|
|
3114
|
-
} else if (d.dVesuPosition > btcEps) {
|
|
3115
|
-
const vesuDepositAmount = new Web3Number(
|
|
3116
|
-
(d.dVesuPosition * price * (1 - targetLtv)).toFixed(USDC_TOKEN_DECIMALS),
|
|
3117
|
-
USDC_TOKEN_DECIMALS,
|
|
3118
|
-
);
|
|
3119
|
-
if (vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
|
|
3120
|
-
routes.push({
|
|
3121
|
-
type: RouteType.AVNU_DEPOSIT_SWAP,
|
|
3122
|
-
priority: routes.length,
|
|
3123
|
-
fromToken: vesuAdapter.config.collateral.symbol,
|
|
3124
|
-
fromAmount: vesuDepositAmount,
|
|
3125
|
-
toToken: vesuAdapter.config.debt.symbol,
|
|
3126
|
-
});
|
|
3127
|
-
}
|
|
3128
|
-
const collateralDelta = new Web3Number(
|
|
3129
|
-
d.dVesuPosition.toFixed(COLLATERAL_PRECISION),
|
|
3130
|
-
vesuAdapter.config.collateral.decimals,
|
|
3131
|
-
);
|
|
3132
|
-
const availableBorrowCapacity = this._budget.vesuBorrowCapacity;
|
|
3133
|
-
const externalDepositAmount = vesuDepositAmount.minus(
|
|
3134
|
-
new Web3Number(Math.min(availableBorrowCapacity, vesuDepositAmount.toNumber()).toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS),
|
|
3135
|
-
);
|
|
3136
|
-
const collPx = pool.collateralPrice || 1;
|
|
3137
|
-
const swappedAmount = new Web3Number(
|
|
3138
|
-
((externalDepositAmount.toNumber() * (pool.debtPrice ?? 1) / collPx)).toFixed(6),
|
|
3139
|
-
vesuAdapter.config.collateral.decimals,
|
|
3140
|
-
);
|
|
3141
|
-
const debtDeltaTokens = new Web3Number(
|
|
3142
|
-
(d.dVesuDebt).toFixed(USDC_TOKEN_DECIMALS),
|
|
3143
|
-
USDC_TOKEN_DECIMALS,
|
|
3144
|
-
);
|
|
3145
|
-
routes.push({
|
|
3146
|
-
type: RouteType.VESU_MULTIPLY_INCREASE_LEVER,
|
|
3147
|
-
priority: routes.length,
|
|
3148
|
-
collateralToken: vesuAdapter.config.collateral,
|
|
3149
|
-
debtToken: vesuAdapter.config.debt,
|
|
3150
|
-
marginAmount: swappedAmount,
|
|
3151
|
-
swappedCollateralAmount: collateralDelta.minus(swappedAmount),
|
|
3152
|
-
debtAmount: debtDeltaTokens.plus(new Web3Number(Math.min(availableBorrowCapacity, vesuDepositAmount.toNumber()).toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS)),
|
|
3153
|
-
poolId: vesuAdapter.config.poolId,
|
|
3154
|
-
} as VesuMultiplyRoute);
|
|
3155
|
-
this._budget.applyVesuDelta(
|
|
3156
|
-
vesuAdapter.config.poolId,
|
|
3157
|
-
vesuAdapter.config.collateral,
|
|
3158
|
-
vesuAdapter.config.debt,
|
|
3159
|
-
collateralDelta,
|
|
3160
|
-
debtDeltaTokens,
|
|
3161
|
-
);
|
|
3162
3166
|
}
|
|
3163
3167
|
|
|
3164
|
-
// ── 2) Extended lever
|
|
3168
|
+
// ── 2) Extended decrease lever only (increase after VA→Ext funding) ─
|
|
3165
3169
|
if (d.dExtPosition < -btcEps) {
|
|
3166
3170
|
const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
|
|
3167
3171
|
routes.push({
|
|
@@ -3171,15 +3175,6 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3171
3175
|
priority: routes.length,
|
|
3172
3176
|
} as ExtendedLeverRoute);
|
|
3173
3177
|
this._budget.applyExtendedExposureDelta(instrument, amt, price);
|
|
3174
|
-
} else if (d.dExtPosition > btcEps) {
|
|
3175
|
-
const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
|
|
3176
|
-
routes.push({
|
|
3177
|
-
type: RouteType.EXTENDED_INCREASE_LEVER,
|
|
3178
|
-
amount: amt,
|
|
3179
|
-
instrument,
|
|
3180
|
-
priority: routes.length,
|
|
3181
|
-
} as ExtendedLeverRoute);
|
|
3182
|
-
this._budget.applyExtendedExposureDelta(instrument, amt, price);
|
|
3183
3178
|
}
|
|
3184
3179
|
|
|
3185
3180
|
// ── 3) Aggregated transfers (no WALLET_TO_EXTENDED; Ext only via VA) ─
|
|
@@ -3194,11 +3189,22 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3194
3189
|
|
|
3195
3190
|
const extToWalletUsd = (negExtAvl < -CASE_THRESHOLD_USD ? Math.abs(negExtAvl) : 0)
|
|
3196
3191
|
+ (negUpnl < -CASE_THRESHOLD_USD ? Math.abs(negUpnl) : 0);
|
|
3192
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas extToWalletUsd=${extToWalletUsd}, negExtAvl=${negExtAvl}, negUpnl=${negUpnl}`);
|
|
3197
3193
|
if (extToWalletUsd > CASE_THRESHOLD_USD) {
|
|
3198
3194
|
this._getExtendedToWalletRoute(extToWalletUsd, routes);
|
|
3199
3195
|
hadExtendedOut = true;
|
|
3196
|
+
} else {
|
|
3197
|
+
// too small to withdraw from extended
|
|
3198
|
+
// reduce any vesu debt
|
|
3199
|
+
// else further repay from va will fail
|
|
3200
|
+
if (d.dVesuDebt < 0) {
|
|
3201
|
+
d.dVesuDebt += (negExtAvl < 0 && negExtAvl > -CASE_THRESHOLD_USD ? Math.abs(negExtAvl) : 0)
|
|
3202
|
+
+ (negUpnl < 0 && negUpnl > -CASE_THRESHOLD_USD ? Math.abs(negUpnl) : 0);
|
|
3203
|
+
}
|
|
3200
3204
|
}
|
|
3201
3205
|
|
|
3206
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas d=${JSON.stringify(d)}`);
|
|
3207
|
+
|
|
3202
3208
|
const walletPull = Math.abs(Math.min(0, d.dWalletUsd));
|
|
3203
3209
|
const walletToVaUsd = walletPull + extToWalletUsd;
|
|
3204
3210
|
if (walletToVaUsd > CASE_THRESHOLD_USD) {
|
|
@@ -3210,7 +3216,16 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3210
3216
|
}
|
|
3211
3217
|
|
|
3212
3218
|
const totalDebtRepayUsd = d.dVesuDebt < 0 ? -d.dVesuDebt * debtPrice : 0;
|
|
3213
|
-
|
|
3219
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas totalDebtRepayUsd=${totalDebtRepayUsd}`);
|
|
3220
|
+
let standaloneRepayUsd = Math.max(0, totalDebtRepayUsd - multiplyDebtRepayUsd);
|
|
3221
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas standaloneRepayUsd=${standaloneRepayUsd}`);
|
|
3222
|
+
if (standaloneRepayUsd > this._budget.vaRawUsd) {
|
|
3223
|
+
if (Math.abs(standaloneRepayUsd - this._budget.vaRawUsd) < CASE_THRESHOLD_USD) {
|
|
3224
|
+
standaloneRepayUsd = this._budget.vaRawUsd;
|
|
3225
|
+
} else {
|
|
3226
|
+
throw new Error(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas standaloneRepayUsd=${standaloneRepayUsd} > vaRawUsd=${this._budget.vaRawUsd}`);
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3214
3229
|
if (standaloneRepayUsd > CASE_THRESHOLD_USD) {
|
|
3215
3230
|
this._buildVesuRepayRoutes(standaloneRepayUsd, routes);
|
|
3216
3231
|
}
|
|
@@ -3221,6 +3236,69 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3221
3236
|
this._getVAToEXTENDEDRoute(vaToExtUsd, routes, hadExtendedOut);
|
|
3222
3237
|
}
|
|
3223
3238
|
|
|
3239
|
+
// ── Vesu / Extended increase lever: require prior VA funding & VA→Extended where applicable
|
|
3240
|
+
if (d.dVesuPosition > btcEps) {
|
|
3241
|
+
const vesuDepositAmount = new Web3Number(
|
|
3242
|
+
(d.dVesuPosition * price * (1 - targetLtv)).toFixed(USDC_TOKEN_DECIMALS),
|
|
3243
|
+
USDC_TOKEN_DECIMALS,
|
|
3244
|
+
);
|
|
3245
|
+
if (vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
|
|
3246
|
+
// routes.push({
|
|
3247
|
+
// type: RouteType.AVNU_DEPOSIT_SWAP,
|
|
3248
|
+
// priority: routes.length,
|
|
3249
|
+
// fromToken: vesuAdapter.config.collateral.symbol,
|
|
3250
|
+
// fromAmount: vesuDepositAmount,
|
|
3251
|
+
// toToken: vesuAdapter.config.debt.symbol,
|
|
3252
|
+
// });
|
|
3253
|
+
}
|
|
3254
|
+
const collateralDelta = new Web3Number(
|
|
3255
|
+
d.dVesuPosition.toFixed(COLLATERAL_PRECISION),
|
|
3256
|
+
vesuAdapter.config.collateral.decimals,
|
|
3257
|
+
);
|
|
3258
|
+
const availableBorrowCapacity = this._budget.vesuBorrowCapacity;
|
|
3259
|
+
const externalDepositAmount = vesuDepositAmount.minus(
|
|
3260
|
+
new Web3Number(Math.min(availableBorrowCapacity, vesuDepositAmount.toNumber()).toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS),
|
|
3261
|
+
);
|
|
3262
|
+
const collPx = pool.collateralPrice || 1;
|
|
3263
|
+
const marginUsdAmount = externalDepositAmount.toNumber() * (pool.debtPrice ?? 1);
|
|
3264
|
+
const swappedAmount = new Web3Number(
|
|
3265
|
+
((marginUsdAmount / collPx)).toFixed(6),
|
|
3266
|
+
vesuAdapter.config.collateral.decimals,
|
|
3267
|
+
);
|
|
3268
|
+
const debtDeltaTokens = new Web3Number(
|
|
3269
|
+
(d.dVesuDebt).toFixed(USDC_TOKEN_DECIMALS),
|
|
3270
|
+
USDC_TOKEN_DECIMALS,
|
|
3271
|
+
);
|
|
3272
|
+
routes.push({
|
|
3273
|
+
type: RouteType.VESU_MULTIPLY_INCREASE_LEVER,
|
|
3274
|
+
priority: routes.length,
|
|
3275
|
+
collateralToken: vesuAdapter.config.collateral,
|
|
3276
|
+
debtToken: vesuAdapter.config.debt,
|
|
3277
|
+
marginAmount: swappedAmount,
|
|
3278
|
+
swappedCollateralAmount: collateralDelta.minus(swappedAmount),
|
|
3279
|
+
debtAmount: debtDeltaTokens.plus(new Web3Number(Math.min(availableBorrowCapacity, vesuDepositAmount.toNumber()).toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS)),
|
|
3280
|
+
poolId: vesuAdapter.config.poolId,
|
|
3281
|
+
} as VesuMultiplyRoute);
|
|
3282
|
+
this._budget.applyVesuDelta(
|
|
3283
|
+
vesuAdapter.config.poolId,
|
|
3284
|
+
vesuAdapter.config.collateral,
|
|
3285
|
+
vesuAdapter.config.debt,
|
|
3286
|
+
collateralDelta,
|
|
3287
|
+
debtDeltaTokens,
|
|
3288
|
+
);
|
|
3289
|
+
this._budget.spendVA(marginUsdAmount);
|
|
3290
|
+
}
|
|
3291
|
+
if (d.dExtPosition > btcEps) {
|
|
3292
|
+
const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
|
|
3293
|
+
routes.push({
|
|
3294
|
+
type: RouteType.EXTENDED_INCREASE_LEVER,
|
|
3295
|
+
amount: amt,
|
|
3296
|
+
instrument,
|
|
3297
|
+
priority: routes.length,
|
|
3298
|
+
} as ExtendedLeverRoute);
|
|
3299
|
+
this._budget.applyExtendedExposureDelta(instrument, amt, price);
|
|
3300
|
+
}
|
|
3301
|
+
|
|
3224
3302
|
return routes;
|
|
3225
3303
|
}
|
|
3226
3304
|
|
|
@@ -3428,37 +3506,51 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3428
3506
|
dExtUpnl = 0,
|
|
3429
3507
|
dVaUsd = 0,
|
|
3430
3508
|
dWalletUsd = 0,
|
|
3431
|
-
dVesuBorrowCapacity = 0
|
|
3509
|
+
dVesuBorrowCapacity = 0,
|
|
3510
|
+
finalVaUsd = vaUsd,
|
|
3511
|
+
finalExtended = extAvlWithdraw + (Math.max(extUpnl, 0));
|
|
3432
3512
|
|
|
3433
3513
|
// --- Case 1: eco1 needs MORE funds (pull from eco2)
|
|
3434
3514
|
if (delta > 0) {
|
|
3435
3515
|
let need = delta;
|
|
3516
|
+
const eps = CASE_THRESHOLD_USD;
|
|
3436
3517
|
|
|
3437
|
-
const takeWalletUsd =
|
|
3518
|
+
const takeWalletUsd = routableDrawAmount(walletUsd, need, eps);
|
|
3438
3519
|
dWalletUsd -= takeWalletUsd;
|
|
3439
3520
|
need -= takeWalletUsd;
|
|
3440
3521
|
|
|
3441
|
-
const takeVaUsd =
|
|
3522
|
+
const takeVaUsd = routableDrawAmount(vaUsd, need, eps);
|
|
3442
3523
|
dVaUsd -= takeVaUsd;
|
|
3443
3524
|
need -= takeVaUsd;
|
|
3444
3525
|
|
|
3445
|
-
|
|
3526
|
+
finalVaUsd -= takeVaUsd; // remove, bcz its sent to extended and wont be available for vesu multiply
|
|
3527
|
+
finalVaUsd += walletUsd - takeWalletUsd; // add remaining wallet usd to va
|
|
3528
|
+
// dWallet
|
|
3529
|
+
|
|
3530
|
+
const takeVesuBorrowCapacity = routableDrawAmount(vesuBorrowCapacity, need, eps);
|
|
3446
3531
|
dVesuBorrowCapacity -= takeVesuBorrowCapacity;
|
|
3447
3532
|
need -= takeVesuBorrowCapacity;
|
|
3448
3533
|
|
|
3534
|
+
finalVaUsd += vesuBorrowCapacity - takeVesuBorrowCapacity; // add remaining vesu borrow capacity to va
|
|
3535
|
+
|
|
3449
3536
|
// Received into eco1 → distribute proportionally into E1/E2
|
|
3450
3537
|
const received = delta - need;
|
|
3451
3538
|
const eco1Sum = extAvlWithdraw + extUpnl;
|
|
3539
|
+
finalExtended += received;
|
|
3452
3540
|
|
|
3453
3541
|
if (eco1Sum >= 0) {
|
|
3454
3542
|
// any received amount is always given to extended avl withdaw only. upnl wont change
|
|
3455
3543
|
dExtAvlWithdraw += received;
|
|
3456
3544
|
} else {
|
|
3457
|
-
//
|
|
3458
|
-
|
|
3545
|
+
// Negative Extended "liquid" (withdraw + uPnL): cover uPnL deficit first, then avl withdraw.
|
|
3546
|
+
const hole = -eco1Sum;
|
|
3547
|
+
const fillUpnl = Math.min(received, hole);
|
|
3548
|
+
dExtUpnl += fillUpnl;
|
|
3549
|
+
dExtAvlWithdraw += received - fillUpnl;
|
|
3550
|
+
finalExtended -= fillUpnl;
|
|
3459
3551
|
}
|
|
3460
3552
|
|
|
3461
|
-
if (need >
|
|
3553
|
+
if (need > CASE_THRESHOLD_USD) {
|
|
3462
3554
|
throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
|
|
3463
3555
|
}
|
|
3464
3556
|
}
|
|
@@ -3467,19 +3559,26 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3467
3559
|
else if (delta < 0) {
|
|
3468
3560
|
let need = -delta;
|
|
3469
3561
|
|
|
3470
|
-
const takeExtAvlWithdraw = Math.min(extAvlWithdraw, need);
|
|
3562
|
+
const takeExtAvlWithdraw = Math.min(Math.max(extAvlWithdraw, 0), need);
|
|
3471
3563
|
dExtAvlWithdraw -= takeExtAvlWithdraw;
|
|
3472
|
-
need -= takeExtAvlWithdraw;
|
|
3473
3564
|
|
|
3474
|
-
const takeExtUpnl = Math.min(extUpnl, need);
|
|
3565
|
+
const takeExtUpnl = Math.min(Math.max(extUpnl, 0), need);
|
|
3475
3566
|
dExtUpnl -= takeExtUpnl;
|
|
3476
|
-
|
|
3567
|
+
|
|
3568
|
+
const netDrawableAmount = takeExtAvlWithdraw + takeExtUpnl;
|
|
3569
|
+
if (netDrawableAmount > CASE_THRESHOLD_USD) {
|
|
3570
|
+
need -= netDrawableAmount;
|
|
3571
|
+
finalExtended -= netDrawableAmount;
|
|
3572
|
+
}
|
|
3477
3573
|
|
|
3478
3574
|
const sent = -delta - need;
|
|
3479
3575
|
|
|
3480
3576
|
// Distribute into eco2 proportionally (optional design choice)
|
|
3481
3577
|
const eco2Sum = vaUsd + walletUsd + vesuBorrowCapacity;
|
|
3482
3578
|
|
|
3579
|
+
const netWalletUsd = walletUsd < CASE_THRESHOLD_USD ? 0 : walletUsd;
|
|
3580
|
+
finalVaUsd += sent + netWalletUsd;
|
|
3581
|
+
|
|
3483
3582
|
if (eco2Sum >= 0) {
|
|
3484
3583
|
// all amount is sent to wallet only
|
|
3485
3584
|
dWalletUsd += sent;
|
|
@@ -3488,12 +3587,27 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3488
3587
|
throw new Error(`${this._tag}: Unexpected case`);
|
|
3489
3588
|
}
|
|
3490
3589
|
|
|
3491
|
-
if (need >
|
|
3590
|
+
if (need > CASE_THRESHOLD_USD) {
|
|
3492
3591
|
throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
|
|
3493
3592
|
}
|
|
3494
3593
|
}
|
|
3495
3594
|
|
|
3496
|
-
return { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu: delta < 0 };
|
|
3595
|
+
return { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, finalVaUsd, finalExtended, isExtendedToVesu: delta < 0 };
|
|
3596
|
+
}
|
|
3597
|
+
|
|
3598
|
+
private _scaleVesuPoolDeltasByFactor(deltas: VesuPoolDelta[], scale: number): VesuPoolDelta[] {
|
|
3599
|
+
if (scale >= 1 - 1e-15) return deltas;
|
|
3600
|
+
return deltas.map((d) => ({
|
|
3601
|
+
...d,
|
|
3602
|
+
collateralDelta: new Web3Number(
|
|
3603
|
+
(d.collateralDelta.toNumber() * scale).toFixed(COLLATERAL_PRECISION),
|
|
3604
|
+
d.collateralToken.decimals,
|
|
3605
|
+
),
|
|
3606
|
+
debtDelta: new Web3Number(
|
|
3607
|
+
(d.debtDelta.toNumber() * scale).toFixed(USDC_TOKEN_DECIMALS),
|
|
3608
|
+
USDC_TOKEN_DECIMALS,
|
|
3609
|
+
),
|
|
3610
|
+
}));
|
|
3497
3611
|
}
|
|
3498
3612
|
|
|
3499
3613
|
/**
|
|
@@ -3510,6 +3624,11 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3510
3624
|
* Computes allocation split between Vesu and Extended, then sources
|
|
3511
3625
|
* funds and creates lever-increase routes.
|
|
3512
3626
|
*
|
|
3627
|
+
* Order: {@link _rebalanceFunds} first → project VA / Extended liquid after the same funding
|
|
3628
|
+
* routes (wallet→VA, borrow→VA, VA→Extended, Extended→wallet→VA) → ideal Vesu/Extended deltas
|
|
3629
|
+
* from distributable split → cap common BTC by min(Vesu fundable, Extended fundable) → scale
|
|
3630
|
+
* Vesu deltas and recompute Extended deltas so both sides stay matched.
|
|
3631
|
+
*
|
|
3513
3632
|
* Fund flow (single pass — avoid VA→Extended then Extended→wallet round-trips):
|
|
3514
3633
|
* 1) Treat Vesu borrow headroom that the multiply route will consume as covering
|
|
3515
3634
|
* part of the Vesu USDC need (no standalone VESU_BORROW for that slice). Cap
|
|
@@ -3536,19 +3655,10 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3536
3655
|
);
|
|
3537
3656
|
if (distributableAmount.toNumber() <= CASE_THRESHOLD_USD) return [];
|
|
3538
3657
|
|
|
3539
|
-
const { vesuAllocationUsd, extendedAllocationUsd } =
|
|
3540
|
-
this._computeAllocationSplit(distributableAmount);
|
|
3541
|
-
|
|
3542
|
-
const vesuDeltas = this._computePerPoolCollateralDeltas(
|
|
3543
|
-
vesuAllocationUsd
|
|
3544
|
-
);
|
|
3545
|
-
|
|
3546
|
-
const extendedPositionDeltas = this._computeExtendedPositionDeltas(vesuDeltas);
|
|
3547
|
-
const vesuDepositAmount = this._computeVesuDepositAmount(vesuDeltas);
|
|
3548
3658
|
const vesuLeverage = calculateVesuLeverage();
|
|
3549
3659
|
const extendedLeverage = calculateExtendedLevergae();
|
|
3550
3660
|
|
|
3551
|
-
const { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu } = this._rebalanceFunds({
|
|
3661
|
+
const { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu, finalVaUsd, finalExtended } = this._rebalanceFunds({
|
|
3552
3662
|
extAvlWithdraw: this._budget.extAvailWithdraw,
|
|
3553
3663
|
extUpnl: this._budget.extAvailUpnl,
|
|
3554
3664
|
vaUsd: this._budget.vaUsd,
|
|
@@ -3557,6 +3667,27 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3557
3667
|
vesuLeverage,
|
|
3558
3668
|
extendedLeverage,
|
|
3559
3669
|
});
|
|
3670
|
+
logger.info(`${this._tag}::_classifyDeposits dExtAvlWithdraw=${dExtAvlWithdraw}, dExtUpnl=${dExtUpnl}, dVaUsd=${dVaUsd}, dWalletUsd=${dWalletUsd}, dVesuBorrowCapacity=${dVesuBorrowCapacity}, isExtendedToVesu=${isExtendedToVesu}, finalVaUsd=${finalVaUsd}`);
|
|
3671
|
+
|
|
3672
|
+
// const { vesuAllocationUsd } = this._computeAllocationSplit(distributableAmount);
|
|
3673
|
+
|
|
3674
|
+
let vesuDeltas = this._computePerPoolCollateralDeltas(new Web3Number(finalVaUsd.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS));
|
|
3675
|
+
const collateralPrice = this._budget.vesuPools[0]?.collateralPrice ?? 0;
|
|
3676
|
+
const collateralDecimals = this._budget.vesuPools[0]?.collateralToken.decimals ?? 0;
|
|
3677
|
+
let _extendedPositionDelta = new Web3Number((finalExtended * extendedLeverage / collateralPrice).toFixed(USDC_TOKEN_DECIMALS), collateralDecimals).toFixedRoundDown(COLLATERAL_PRECISION);
|
|
3678
|
+
logger.info(`${this._tag}::_classifyDeposits extendedPositionDelta=${_extendedPositionDelta}`);
|
|
3679
|
+
logger.info(`${this._tag}::_classifyDeposits vesuDeltas=${JSON.stringify(vesuDeltas)}`);
|
|
3680
|
+
assert(vesuDeltas.length == 1, 'vesuDeltas should have only one delta');
|
|
3681
|
+
const minPositionDelta = Math.min(vesuDeltas[0].collateralDelta.toNumber(), Number(_extendedPositionDelta));
|
|
3682
|
+
logger.info(`${this._tag}::_classifyDeposits minPositionDelta=${minPositionDelta}`);
|
|
3683
|
+
vesuDeltas[0].collateralDelta = new Web3Number(minPositionDelta.toFixed(COLLATERAL_PRECISION), vesuDeltas[0].collateralDelta.decimals);
|
|
3684
|
+
const extendedPositionDeltas: ExtendedPositionDelta[] = [{
|
|
3685
|
+
instrument: this._config.extendedAdapter.config.extendedMarketName ?? 'BTC-USD',
|
|
3686
|
+
delta: new Web3Number((minPositionDelta).toFixed(COLLATERAL_PRECISION), collateralDecimals),
|
|
3687
|
+
}];
|
|
3688
|
+
const vesuDepositAmount = this._computeVesuDepositAmount(vesuDeltas);
|
|
3689
|
+
logger.info(`${this._tag}::_classifyDeposits vesuDepositAmount=${vesuDepositAmount}`);
|
|
3690
|
+
|
|
3560
3691
|
const routes: ExecutionRoute[] = [];
|
|
3561
3692
|
|
|
3562
3693
|
if (isExtendedToVesu) {
|
|
@@ -3595,13 +3726,13 @@ export class ExtendedSVKVesuStateManager {
|
|
|
3595
3726
|
!skipAvnuDepositSwap &&
|
|
3596
3727
|
vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD
|
|
3597
3728
|
) {
|
|
3598
|
-
routes.push({
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
});
|
|
3729
|
+
// routes.push({
|
|
3730
|
+
// type: RouteType.AVNU_DEPOSIT_SWAP,
|
|
3731
|
+
// priority: routes.length,
|
|
3732
|
+
// fromToken: vesuDelta.collateralToken.symbol,
|
|
3733
|
+
// fromAmount: vesuDepositAmount,
|
|
3734
|
+
// toToken: vesuDelta.debtToken.symbol,
|
|
3735
|
+
// });
|
|
3605
3736
|
}
|
|
3606
3737
|
if (vesuDelta.collateralDelta.toNumber() > 0) {
|
|
3607
3738
|
// removes borrowing capacity after excluding delta capacity that is being briddged out
|