@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
package/dist/index.browser.mjs
CHANGED
|
@@ -5800,7 +5800,7 @@ var EkuboQuoter = class _EkuboQuoter {
|
|
|
5800
5800
|
async _callQuoterApi(fromToken, toToken, amount, retry = 0) {
|
|
5801
5801
|
try {
|
|
5802
5802
|
const url = this.ENDPOINT.replace("{{AMOUNT}}", amount.toWei()).replace("{{TOKEN_FROM_ADDRESS}}", fromToken).replace("{{TOKEN_TO_ADDRESS}}", toToken);
|
|
5803
|
-
logger.
|
|
5803
|
+
logger.info(`EkuboQuoter::_callQuoterApi url: ${url}`);
|
|
5804
5804
|
const quote = await axios6.get(url);
|
|
5805
5805
|
return quote.data;
|
|
5806
5806
|
} catch (error) {
|
|
@@ -5973,6 +5973,9 @@ var PricerLST = class extends Pricer {
|
|
|
5973
5973
|
}
|
|
5974
5974
|
};
|
|
5975
5975
|
|
|
5976
|
+
// src/modules/ExtendedWrapperSDk/wrapper.ts
|
|
5977
|
+
import BigNumber3 from "bignumber.js";
|
|
5978
|
+
|
|
5976
5979
|
// src/modules/ExtendedWrapperSDk/types.ts
|
|
5977
5980
|
var OrderSide = /* @__PURE__ */ ((OrderSide2) => {
|
|
5978
5981
|
OrderSide2["BUY"] = "BUY";
|
|
@@ -6057,8 +6060,60 @@ var AssetOperationStatus = /* @__PURE__ */ ((AssetOperationStatus3) => {
|
|
|
6057
6060
|
})(AssetOperationStatus || {});
|
|
6058
6061
|
|
|
6059
6062
|
// src/modules/ExtendedWrapperSDk/wrapper.ts
|
|
6063
|
+
function asRecord(v) {
|
|
6064
|
+
return v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
6065
|
+
}
|
|
6066
|
+
function pickTradingConfig(row) {
|
|
6067
|
+
const tc = row.trading_config ?? row.tradingConfig;
|
|
6068
|
+
return asRecord(tc);
|
|
6069
|
+
}
|
|
6070
|
+
function readTcString(tc, snake, camel) {
|
|
6071
|
+
const v = tc[snake] ?? tc[camel];
|
|
6072
|
+
if (v === void 0 || v === null) return void 0;
|
|
6073
|
+
return String(v).trim();
|
|
6074
|
+
}
|
|
6075
|
+
function tradingRulesFromMarketRow(marketName, row) {
|
|
6076
|
+
const r = asRecord(row);
|
|
6077
|
+
if (!r) {
|
|
6078
|
+
throw new Error(`ExtendedWrapper: invalid market payload for ${marketName}`);
|
|
6079
|
+
}
|
|
6080
|
+
const tc = pickTradingConfig(r);
|
|
6081
|
+
if (!tc) {
|
|
6082
|
+
throw new Error(`ExtendedWrapper: missing tradingConfig for market ${marketName}`);
|
|
6083
|
+
}
|
|
6084
|
+
const minS = readTcString(tc, "min_order_size", "minOrderSize");
|
|
6085
|
+
const qtyStep = readTcString(tc, "min_order_size_change", "minOrderSizeChange");
|
|
6086
|
+
const pxStep = readTcString(tc, "min_price_change", "minPriceChange");
|
|
6087
|
+
if (!minS || !qtyStep || !pxStep) {
|
|
6088
|
+
throw new Error(`ExtendedWrapper: incomplete tradingConfig for market ${marketName}`);
|
|
6089
|
+
}
|
|
6090
|
+
const minOrderSize = new BigNumber3(minS);
|
|
6091
|
+
const qty = new BigNumber3(qtyStep);
|
|
6092
|
+
const px = new BigNumber3(pxStep);
|
|
6093
|
+
if (!minOrderSize.isFinite() || minOrderSize.lte(0)) {
|
|
6094
|
+
throw new Error(`ExtendedWrapper: invalid minOrderSize for ${marketName}`);
|
|
6095
|
+
}
|
|
6096
|
+
if (!qty.isFinite() || qty.lte(0)) {
|
|
6097
|
+
throw new Error(`ExtendedWrapper: invalid minOrderSizeChange for ${marketName}`);
|
|
6098
|
+
}
|
|
6099
|
+
if (!px.isFinite() || px.lte(0)) {
|
|
6100
|
+
throw new Error(`ExtendedWrapper: invalid minPriceChange for ${marketName}`);
|
|
6101
|
+
}
|
|
6102
|
+
return { minOrderSize, qtyStep: qty, priceStep: px };
|
|
6103
|
+
}
|
|
6104
|
+
function roundToStepBn(value, step) {
|
|
6105
|
+
if (step.lte(0)) return value;
|
|
6106
|
+
return value.div(step).round(0, BigNumber3.ROUND_HALF_UP).times(step);
|
|
6107
|
+
}
|
|
6108
|
+
function formatBnForApi(bn, step) {
|
|
6109
|
+
const dp = Math.max(step.decimalPlaces() ?? 0, bn.decimalPlaces() ?? 0, 0);
|
|
6110
|
+
return Number(bn.toFixed(Math.min(80, dp))).toString();
|
|
6111
|
+
}
|
|
6060
6112
|
var ExtendedWrapper = class {
|
|
6061
6113
|
constructor(config) {
|
|
6114
|
+
/** Per-market rules from GET /markets (tradingConfig); retained for process lifetime (no TTL). */
|
|
6115
|
+
this.marketTradingRulesCache = /* @__PURE__ */ new Map();
|
|
6116
|
+
this.marketTradingRulesInflight = /* @__PURE__ */ new Map();
|
|
6062
6117
|
this.apiKey = config.apiKey;
|
|
6063
6118
|
this.timeout = config.timeout || 3e4;
|
|
6064
6119
|
this.retries = config.retries || 3;
|
|
@@ -6122,13 +6177,75 @@ var ExtendedWrapper = class {
|
|
|
6122
6177
|
}
|
|
6123
6178
|
throw lastError || new Error("Request failed after all retries");
|
|
6124
6179
|
}
|
|
6180
|
+
async resolveTradingRules(marketName) {
|
|
6181
|
+
const cached = this.marketTradingRulesCache.get(marketName);
|
|
6182
|
+
if (cached) return cached;
|
|
6183
|
+
const existing = this.marketTradingRulesInflight.get(marketName);
|
|
6184
|
+
if (existing) return existing;
|
|
6185
|
+
const inflight = (async () => {
|
|
6186
|
+
const res = await this.getMarkets(marketName);
|
|
6187
|
+
if (res.status !== "OK") {
|
|
6188
|
+
throw new Error(
|
|
6189
|
+
`ExtendedWrapper: getMarkets failed for ${marketName}: ${res.message}`
|
|
6190
|
+
);
|
|
6191
|
+
}
|
|
6192
|
+
const rows = res.data;
|
|
6193
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
6194
|
+
throw new Error(
|
|
6195
|
+
`ExtendedWrapper: empty markets response for ${marketName}`
|
|
6196
|
+
);
|
|
6197
|
+
}
|
|
6198
|
+
const row = rows.find((m) => asRecord(m)?.name === marketName);
|
|
6199
|
+
if (!row) {
|
|
6200
|
+
throw new Error(
|
|
6201
|
+
`ExtendedWrapper: market ${marketName} not found in markets list`
|
|
6202
|
+
);
|
|
6203
|
+
}
|
|
6204
|
+
const rules = tradingRulesFromMarketRow(marketName, row);
|
|
6205
|
+
this.marketTradingRulesCache.set(marketName, rules);
|
|
6206
|
+
return rules;
|
|
6207
|
+
})();
|
|
6208
|
+
this.marketTradingRulesInflight.set(marketName, inflight);
|
|
6209
|
+
void inflight.finally(() => {
|
|
6210
|
+
if (this.marketTradingRulesInflight.get(marketName) === inflight) {
|
|
6211
|
+
this.marketTradingRulesInflight.delete(marketName);
|
|
6212
|
+
}
|
|
6213
|
+
});
|
|
6214
|
+
return inflight;
|
|
6215
|
+
}
|
|
6125
6216
|
/**
|
|
6126
6217
|
* Create a new order on Extended Exchange
|
|
6127
6218
|
*/
|
|
6128
6219
|
async createOrder(request) {
|
|
6220
|
+
const rules = await this.resolveTradingRules(request.market_name);
|
|
6221
|
+
const amountBn = new BigNumber3(String(request.amount).trim());
|
|
6222
|
+
const priceBn = new BigNumber3(String(request.price).trim());
|
|
6223
|
+
if (!amountBn.isFinite() || amountBn.lte(0)) {
|
|
6224
|
+
throw new Error(`ExtendedWrapper: invalid order amount=${request.amount}`);
|
|
6225
|
+
}
|
|
6226
|
+
if (!priceBn.isFinite() || priceBn.lte(0)) {
|
|
6227
|
+
throw new Error(`ExtendedWrapper: invalid order price=${request.price}`);
|
|
6228
|
+
}
|
|
6229
|
+
if (amountBn.lt(rules.minOrderSize)) {
|
|
6230
|
+
throw new Error(
|
|
6231
|
+
`ExtendedWrapper: order amount ${request.amount} is below minOrderSize ${rules.minOrderSize.toFixed()} for ${request.market_name}`
|
|
6232
|
+
);
|
|
6233
|
+
}
|
|
6234
|
+
const adjAmount = roundToStepBn(amountBn, rules.qtyStep);
|
|
6235
|
+
const adjPrice = roundToStepBn(priceBn, rules.priceStep);
|
|
6236
|
+
if (adjAmount.lt(rules.minOrderSize)) {
|
|
6237
|
+
throw new Error(
|
|
6238
|
+
`ExtendedWrapper: amount after tick rounding ${formatBnForApi(adjAmount, rules.qtyStep)} is below minOrderSize ${rules.minOrderSize.toFixed()} (${request.market_name})`
|
|
6239
|
+
);
|
|
6240
|
+
}
|
|
6241
|
+
const payload = {
|
|
6242
|
+
...request,
|
|
6243
|
+
amount: formatBnForApi(adjAmount, rules.qtyStep),
|
|
6244
|
+
price: formatBnForApi(adjPrice, rules.priceStep)
|
|
6245
|
+
};
|
|
6129
6246
|
return this.makeRequest("/api/v1/orders", false, {
|
|
6130
6247
|
method: "POST",
|
|
6131
|
-
body: JSON.stringify(
|
|
6248
|
+
body: JSON.stringify(payload)
|
|
6132
6249
|
});
|
|
6133
6250
|
}
|
|
6134
6251
|
/**
|
|
@@ -8097,6 +8214,7 @@ var vesu_rebalance_abi_default = [
|
|
|
8097
8214
|
var CacheClass = class {
|
|
8098
8215
|
constructor() {
|
|
8099
8216
|
this.cache = /* @__PURE__ */ new Map();
|
|
8217
|
+
this.isCacheEnabled = true;
|
|
8100
8218
|
}
|
|
8101
8219
|
setCache(key, data, ttl = 6e4) {
|
|
8102
8220
|
const timestamp = Date.now();
|
|
@@ -8104,7 +8222,7 @@ var CacheClass = class {
|
|
|
8104
8222
|
}
|
|
8105
8223
|
getCache(key) {
|
|
8106
8224
|
const cachedData = this.cache.get(key);
|
|
8107
|
-
if (!cachedData || !this.isCacheValid(key)) {
|
|
8225
|
+
if (!cachedData || !this.isCacheValid(key) || !this.isCacheEnabled) {
|
|
8108
8226
|
return null;
|
|
8109
8227
|
}
|
|
8110
8228
|
return cachedData.data;
|
|
@@ -8115,6 +8233,12 @@ var CacheClass = class {
|
|
|
8115
8233
|
const { timestamp, ttl } = cachedData;
|
|
8116
8234
|
return Date.now() - timestamp <= ttl;
|
|
8117
8235
|
}
|
|
8236
|
+
disableCache() {
|
|
8237
|
+
this.isCacheEnabled = false;
|
|
8238
|
+
}
|
|
8239
|
+
enableCache() {
|
|
8240
|
+
this.isCacheEnabled = true;
|
|
8241
|
+
}
|
|
8118
8242
|
};
|
|
8119
8243
|
|
|
8120
8244
|
// src/strategies/base-strategy.ts
|
|
@@ -28905,7 +29029,7 @@ var VesuAdapter = class _VesuAdapter extends CacheClass {
|
|
|
28905
29029
|
throw new Error("LTV is 0");
|
|
28906
29030
|
}
|
|
28907
29031
|
this.setCache(CACHE_KEY, ltv, 3e5);
|
|
28908
|
-
return
|
|
29032
|
+
return ltv;
|
|
28909
29033
|
}
|
|
28910
29034
|
async getPositions(config, blockNumber = "latest") {
|
|
28911
29035
|
if (!this.pricer) {
|
|
@@ -28989,6 +29113,9 @@ var VesuAdapter = class _VesuAdapter extends CacheClass {
|
|
|
28989
29113
|
debtPrice = (await pricer.getPrice(this.config.debt.priceProxySymbol || this.config.debt.symbol)).price;
|
|
28990
29114
|
}
|
|
28991
29115
|
logger.verbose(`VesuAdapter::getAssetPrices collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
|
|
29116
|
+
if (isNaN(collateralPrice) || isNaN(debtPrice) || collateralPrice == 0 || debtPrice == 0) {
|
|
29117
|
+
throw new Error(`VesuAdapter::getAssetPrices collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
|
|
29118
|
+
}
|
|
28992
29119
|
return {
|
|
28993
29120
|
collateralTokenAmount,
|
|
28994
29121
|
collateralUSDAmount,
|
|
@@ -34627,20 +34754,23 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
34627
34754
|
}
|
|
34628
34755
|
_buildZeroAmountSwapsWithWeights(quote, token, reverseRoutes = false) {
|
|
34629
34756
|
const swaps = quote.splits.map((split) => {
|
|
34630
|
-
|
|
34631
|
-
|
|
34632
|
-
|
|
34633
|
-
|
|
34634
|
-
|
|
34635
|
-
|
|
34636
|
-
|
|
34637
|
-
|
|
34638
|
-
|
|
34639
|
-
|
|
34640
|
-
|
|
34641
|
-
|
|
34642
|
-
|
|
34643
|
-
|
|
34757
|
+
let sellToken = token.address;
|
|
34758
|
+
const routeNodes = split.route.map((_route) => {
|
|
34759
|
+
let isSellToken0 = sellToken.eqString(_route.pool_key.token0);
|
|
34760
|
+
isSellToken0 = reverseRoutes ? !isSellToken0 : isSellToken0;
|
|
34761
|
+
sellToken = isSellToken0 ? ContractAddr.from(_route.pool_key.token1) : ContractAddr.from(_route.pool_key.token0);
|
|
34762
|
+
return {
|
|
34763
|
+
pool_key: {
|
|
34764
|
+
token0: ContractAddr.from(_route.pool_key.token0),
|
|
34765
|
+
token1: ContractAddr.from(_route.pool_key.token1),
|
|
34766
|
+
fee: _route.pool_key.fee,
|
|
34767
|
+
tick_spacing: _route.pool_key.tick_spacing.toString(),
|
|
34768
|
+
extension: _route.pool_key.extension
|
|
34769
|
+
},
|
|
34770
|
+
sqrt_ratio_limit: isSellToken0 ? Web3Number.fromWei(MIN_SQRT_RATIO_LIMIT.toString(), 18) : Web3Number.fromWei(MAX_SQRT_RATIO_LIMIT.toString(), 18),
|
|
34771
|
+
skip_ahead: Web3Number.fromWei(_route.skip_ahead, 0)
|
|
34772
|
+
};
|
|
34773
|
+
});
|
|
34644
34774
|
return {
|
|
34645
34775
|
route: routeNodes,
|
|
34646
34776
|
token_amount: {
|
|
@@ -34962,6 +35092,7 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
34962
35092
|
collateralToken.address.address,
|
|
34963
35093
|
requiredAmount
|
|
34964
35094
|
);
|
|
35095
|
+
logger.info(`VesuMultiplyAdapter::_getIncreaseCalldata requiredAmount: ${requiredAmount}`);
|
|
34965
35096
|
if (marginSwapQuote.price_impact > 0.01) {
|
|
34966
35097
|
throw new Error(
|
|
34967
35098
|
`VesuMultiplyAdapter: Margin swap price impact too high (${marginSwapQuote.price_impact})`
|
|
@@ -34988,7 +35119,7 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
34988
35119
|
legLTV,
|
|
34989
35120
|
dexPrice
|
|
34990
35121
|
);
|
|
34991
|
-
logger.
|
|
35122
|
+
logger.info(
|
|
34992
35123
|
`${_VesuMultiplyAdapter.name}::_getIncreaseCalldata debtAmount: ${debtAmount}, addedCollateral: ${addedCollateral}`
|
|
34993
35124
|
);
|
|
34994
35125
|
let leverSwap = [];
|
|
@@ -35006,8 +35137,10 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
35006
35137
|
collateralToken.address.address,
|
|
35007
35138
|
params.leverSwap?.exactOutput?.abs()
|
|
35008
35139
|
);
|
|
35140
|
+
logger.info(`VesuMultiplyAdapter::_getIncreaseCalldata amount: ${params.leverSwap?.exactOutput?.abs()}`);
|
|
35009
35141
|
debtAmount = Web3Number.fromWei(swapQuote.total_calculated, debtToken.decimals).abs();
|
|
35010
35142
|
}
|
|
35143
|
+
logger.info(`VesuMultiplyAdapter::_getIncreaseCalldata debtAmount: ${debtAmount}`);
|
|
35011
35144
|
swapQuote = await ekuboQuoter.getQuoteExactInput(
|
|
35012
35145
|
debtToken.address.address,
|
|
35013
35146
|
collateralToken.address.address,
|
|
@@ -35034,7 +35167,7 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
35034
35167
|
await new Promise((resolve) => setTimeout(resolve, 1e4));
|
|
35035
35168
|
} else {
|
|
35036
35169
|
throw new Error(
|
|
35037
|
-
`VesuMultiplyAdapter: Price impact too high (${swapQuote.price_impact}), skipping swap`
|
|
35170
|
+
`VesuMultiplyAdapter: Price impact too high (${swapQuote.price_impact}), skipping swap, debtAmount=${debtAmount.toNumber()}, collateralPrice=${collateralPrice}, debtPrice=${debtPrice}`
|
|
35038
35171
|
);
|
|
35039
35172
|
}
|
|
35040
35173
|
} catch (error) {
|
|
@@ -36405,14 +36538,6 @@ var ExtendedAdapter = class _ExtendedAdapter extends BaseAdapter {
|
|
|
36405
36538
|
logger.error("error initializing client");
|
|
36406
36539
|
return null;
|
|
36407
36540
|
}
|
|
36408
|
-
const setLeverage = await this.setLeverage(
|
|
36409
|
-
leverage,
|
|
36410
|
-
this.config.extendedMarketName
|
|
36411
|
-
);
|
|
36412
|
-
if (!setLeverage) {
|
|
36413
|
-
logger.error("error depositing or setting leverage");
|
|
36414
|
-
return null;
|
|
36415
|
-
}
|
|
36416
36541
|
const { ask, bid } = await this.fetchOrderBookFromExtended();
|
|
36417
36542
|
if (!ask || !bid || ask.lessThanOrEqualTo(0) || bid.lessThanOrEqualTo(0)) {
|
|
36418
36543
|
logger.error(
|
|
@@ -36552,13 +36677,13 @@ var ExtendedAdapter = class _ExtendedAdapter extends BaseAdapter {
|
|
|
36552
36677
|
async createExtendedPositon(client, marketName, amount, price, side) {
|
|
36553
36678
|
try {
|
|
36554
36679
|
const result = side === "SELL" /* SELL */ ? await client.createSellOrder(marketName, amount, price, {
|
|
36555
|
-
|
|
36556
|
-
|
|
36557
|
-
|
|
36680
|
+
post_only: false,
|
|
36681
|
+
reduce_only: false,
|
|
36682
|
+
time_in_force: "IOC" /* IOC */
|
|
36558
36683
|
}) : await client.createBuyOrder(marketName, amount, price, {
|
|
36559
|
-
|
|
36560
|
-
|
|
36561
|
-
|
|
36684
|
+
post_only: false,
|
|
36685
|
+
reduce_only: true,
|
|
36686
|
+
time_in_force: "IOC" /* IOC */
|
|
36562
36687
|
});
|
|
36563
36688
|
if (result.data.id) {
|
|
36564
36689
|
const position_id = result.data.id.toString();
|
|
@@ -42023,20 +42148,42 @@ function mergeDeltas(a, b) {
|
|
|
42023
42148
|
dTransferVesuToExt: a.dTransferVesuToExt + b.dTransferVesuToExt
|
|
42024
42149
|
};
|
|
42025
42150
|
}
|
|
42026
|
-
function
|
|
42151
|
+
function routableDrawAmount(avail, need, minRoutable) {
|
|
42152
|
+
if (avail <= 0 || need <= 0) return 0;
|
|
42153
|
+
const raw = Math.min(avail, need);
|
|
42154
|
+
if (raw <= 0) return 0;
|
|
42155
|
+
if (minRoutable <= 0) return raw;
|
|
42156
|
+
return raw > minRoutable ? raw : 0;
|
|
42157
|
+
}
|
|
42158
|
+
function drawFunds(need, keys, pool, minRoutable) {
|
|
42027
42159
|
const draws = {};
|
|
42028
42160
|
let unmet = need;
|
|
42029
42161
|
for (const k of keys) {
|
|
42030
42162
|
if (unmet <= 0) break;
|
|
42031
42163
|
const avail = pool[k];
|
|
42032
|
-
|
|
42033
|
-
|
|
42164
|
+
const take = routableDrawAmount(avail, unmet, minRoutable);
|
|
42165
|
+
if (take <= 0) continue;
|
|
42034
42166
|
draws[k] = take;
|
|
42035
42167
|
pool[k] -= take;
|
|
42036
42168
|
unmet -= take;
|
|
42037
42169
|
}
|
|
42038
42170
|
return { draws, filled: need - unmet, unmet };
|
|
42039
42171
|
}
|
|
42172
|
+
function drawExtendedAggregated(need, pool, minRoutable) {
|
|
42173
|
+
const draws = {};
|
|
42174
|
+
if (need <= 0) return { draws, filled: 0, unmet: 0 };
|
|
42175
|
+
const totalCap = Math.max(0, pool.extAvlWithdraw) + Math.max(0, pool.extUpnl);
|
|
42176
|
+
const want = routableDrawAmount(totalCap, need, minRoutable);
|
|
42177
|
+
if (want <= 0) return { draws, filled: 0, unmet: need };
|
|
42178
|
+
const fromAvl = Math.min(Math.max(0, pool.extAvlWithdraw), want);
|
|
42179
|
+
pool.extAvlWithdraw -= fromAvl;
|
|
42180
|
+
const fromUpnl = Math.min(Math.max(0, pool.extUpnl), want - fromAvl);
|
|
42181
|
+
pool.extUpnl -= fromUpnl;
|
|
42182
|
+
if (fromAvl > 0) draws.extAvlWithdraw = fromAvl;
|
|
42183
|
+
if (fromUpnl > 0) draws.extUpnl = fromUpnl;
|
|
42184
|
+
const filled = fromAvl + fromUpnl;
|
|
42185
|
+
return { draws, filled, unmet: need - filled };
|
|
42186
|
+
}
|
|
42040
42187
|
function sumKeys(draws, keys) {
|
|
42041
42188
|
return keys.reduce((s, k) => s + (draws[k] ?? 0), 0);
|
|
42042
42189
|
}
|
|
@@ -42047,33 +42194,39 @@ function applyDrawsToDeltas(d, draws, sign) {
|
|
|
42047
42194
|
d.dExtAvlWithdraw += sign * (draws.extAvlWithdraw ?? 0);
|
|
42048
42195
|
d.dExtUpnl += sign * (draws.extUpnl ?? 0);
|
|
42049
42196
|
}
|
|
42050
|
-
function fixExtMargin(ext, price, pool) {
|
|
42197
|
+
function fixExtMargin(ext, price, pool, config) {
|
|
42051
42198
|
const d = emptyDeltas();
|
|
42052
42199
|
const deficit = computeExtDeficit(ext, price);
|
|
42053
42200
|
if (deficit <= 0) return { d, unmet: 0 };
|
|
42054
|
-
const
|
|
42201
|
+
const minR = config.minRoutableUsd ?? 0;
|
|
42202
|
+
const { draws, filled, unmet } = drawFunds(deficit, ["walletUsd", "vaUsd", "vesuBorrowCapacity"], pool, minR);
|
|
42055
42203
|
applyDrawsToDeltas(d, draws, -1);
|
|
42056
42204
|
d.dExtAvlWithdraw += filled;
|
|
42057
42205
|
const fromVesu = draws.vesuBorrowCapacity ?? 0;
|
|
42058
42206
|
if (fromVesu > 0) d.dTransferVesuToExt += fromVesu;
|
|
42059
42207
|
return { d, unmet };
|
|
42060
42208
|
}
|
|
42061
|
-
function fixVesuMargin(vesu, price, pool, hfBuffer) {
|
|
42209
|
+
function fixVesuMargin(vesu, price, pool, hfBuffer, config) {
|
|
42062
42210
|
const d = emptyDeltas();
|
|
42063
42211
|
const hf = computeVesuHF(vesu, price);
|
|
42064
42212
|
if (hf >= vesu.targetHF - hfBuffer) return { d, unmet: 0 };
|
|
42065
42213
|
const repayTokens = computeVesuDebtRepay(vesu, price);
|
|
42066
42214
|
const repayUsd = repayTokens * vesu.debtPrice;
|
|
42067
|
-
const
|
|
42068
|
-
|
|
42215
|
+
const minR = config.minRoutableUsd ?? 0;
|
|
42216
|
+
const rMain = drawFunds(repayUsd, ["vaUsd", "walletUsd"], pool, minR);
|
|
42217
|
+
applyDrawsToDeltas(d, rMain.draws, -1);
|
|
42218
|
+
const rExt = drawExtendedAggregated(rMain.unmet, pool, minR);
|
|
42219
|
+
applyDrawsToDeltas(d, rExt.draws, -1);
|
|
42220
|
+
const filled = rMain.filled + rExt.filled;
|
|
42221
|
+
const unmet = rExt.unmet;
|
|
42069
42222
|
d.dVesuDebt -= filled / vesu.debtPrice;
|
|
42070
|
-
const fromExt = sumKeys(draws, ["extAvlWithdraw", "extUpnl"]);
|
|
42223
|
+
const fromExt = sumKeys(rExt.draws, ["extAvlWithdraw", "extUpnl"]);
|
|
42071
42224
|
if (fromExt > 0) d.dTransferVesuToExt -= fromExt;
|
|
42072
42225
|
return { d, unmet };
|
|
42073
42226
|
}
|
|
42074
42227
|
function phase1(ext, vesu, price, pool, config) {
|
|
42075
|
-
const extResult = fixExtMargin(ext, price, pool);
|
|
42076
|
-
const vesuResult = fixVesuMargin(vesu, price, pool, config.hfBuffer);
|
|
42228
|
+
const extResult = fixExtMargin(ext, price, pool, config);
|
|
42229
|
+
const vesuResult = fixVesuMargin(vesu, price, pool, config.hfBuffer, config);
|
|
42077
42230
|
return {
|
|
42078
42231
|
deltas: mergeDeltas(extResult.d, vesuResult.d),
|
|
42079
42232
|
extDeficitRemaining: extResult.unmet,
|
|
@@ -42113,26 +42266,31 @@ function roundFinalPosition(extPos, vesuPos, rawF, precision) {
|
|
|
42113
42266
|
}
|
|
42114
42267
|
return Math.max(0, Math.min(fFromExt, fFromVesu));
|
|
42115
42268
|
}
|
|
42116
|
-
function phase2(extPos, vesuPos, extEquity, vesuDebt, vesu, extLev, price, pool,
|
|
42269
|
+
function phase2(extPos, vesuPos, extEquity, vesuDebt, vesu, extLev, price, pool, config) {
|
|
42117
42270
|
const d = emptyDeltas();
|
|
42271
|
+
const precision = config.positionPrecision;
|
|
42272
|
+
const minR = config.minRoutableUsd ?? 0;
|
|
42118
42273
|
const targetLTV = computeVesuTargetLTV(vesu);
|
|
42119
42274
|
const imbalance = extPos - vesuPos;
|
|
42120
42275
|
let fundedGrowthBtc = 0;
|
|
42121
42276
|
if (!isNegligible(imbalance, precision)) {
|
|
42122
42277
|
if (imbalance > 0) {
|
|
42123
42278
|
const equityCostUsd = computeVesuGrowthCost(imbalance, vesu, price);
|
|
42124
|
-
const
|
|
42125
|
-
applyDrawsToDeltas(d, draws, -1);
|
|
42279
|
+
const rMain = drawFunds(equityCostUsd, ["vaUsd", "walletUsd"], pool, minR);
|
|
42280
|
+
applyDrawsToDeltas(d, rMain.draws, -1);
|
|
42281
|
+
const rExt = drawExtendedAggregated(rMain.unmet, pool, minR);
|
|
42282
|
+
applyDrawsToDeltas(d, rExt.draws, -1);
|
|
42283
|
+
const filled = rMain.filled + rExt.filled;
|
|
42126
42284
|
const grownBtc = computeVesuGrowthFromEquity(filled, vesu, price);
|
|
42127
42285
|
d.dVesuPosition += grownBtc;
|
|
42128
42286
|
fundedGrowthBtc = grownBtc;
|
|
42129
42287
|
d.dVesuDebt += computeVesuGrowthDebt(grownBtc, vesu, price);
|
|
42130
|
-
const fromExt = sumKeys(draws, ["extAvlWithdraw", "extUpnl"]);
|
|
42288
|
+
const fromExt = sumKeys(rExt.draws, ["extAvlWithdraw", "extUpnl"]);
|
|
42131
42289
|
if (fromExt > 0) d.dTransferVesuToExt -= fromExt;
|
|
42132
42290
|
} else {
|
|
42133
42291
|
const absImbalance = -imbalance;
|
|
42134
42292
|
const marginCostUsd = absImbalance * price / extLev;
|
|
42135
|
-
const { draws, filled } = drawFunds(marginCostUsd, ["walletUsd", "vaUsd", "vesuBorrowCapacity"], pool);
|
|
42293
|
+
const { draws, filled } = drawFunds(marginCostUsd, ["walletUsd", "vaUsd", "vesuBorrowCapacity"], pool, minR);
|
|
42136
42294
|
applyDrawsToDeltas(d, draws, -1);
|
|
42137
42295
|
const grownBtc = filled * extLev / price;
|
|
42138
42296
|
d.dExtPosition += grownBtc;
|
|
@@ -42173,6 +42331,7 @@ function phase2(extPos, vesuPos, extEquity, vesuDebt, vesu, extLev, price, pool,
|
|
|
42173
42331
|
return d;
|
|
42174
42332
|
}
|
|
42175
42333
|
function rebalance(inputs) {
|
|
42334
|
+
logger.info(`ltv-imbalance-rebalance-math::rebalance inputs=${JSON.stringify(inputs)}`);
|
|
42176
42335
|
const { ext, vesu, btcPrice, config } = inputs;
|
|
42177
42336
|
const pool = { ...inputs.funding };
|
|
42178
42337
|
const p1 = phase1(ext, vesu, btcPrice, pool, config);
|
|
@@ -42189,7 +42348,7 @@ function rebalance(inputs) {
|
|
|
42189
42348
|
ext.leverage,
|
|
42190
42349
|
btcPrice,
|
|
42191
42350
|
pool,
|
|
42192
|
-
config
|
|
42351
|
+
config
|
|
42193
42352
|
);
|
|
42194
42353
|
return mergeDeltas(p1.deltas, p2);
|
|
42195
42354
|
}
|
|
@@ -42770,15 +42929,15 @@ var SolveBudget = class {
|
|
|
42770
42929
|
return this.vesuPoolStates[0];
|
|
42771
42930
|
}
|
|
42772
42931
|
// ── Derived getters (buffered where applicable) ─────────────────────
|
|
42773
|
-
/** Buffered VA USD:
|
|
42932
|
+
/** Buffered VA USD: non-stable asset slot (if any) + USDC slot. */
|
|
42774
42933
|
get vaUsd() {
|
|
42775
|
-
return this.bufferedTokenUsd(this.
|
|
42934
|
+
return this.bufferedTokenUsd(this.vaultUsdcBalance);
|
|
42776
42935
|
}
|
|
42777
42936
|
/** Buffered USD in VA strategy-asset bucket only. */
|
|
42778
42937
|
get vaAssetUsd() {
|
|
42779
42938
|
return this.bufferedTokenUsd(this.vaultAssetBalance);
|
|
42780
42939
|
}
|
|
42781
|
-
/** Buffered USD in VA USDC bucket (
|
|
42940
|
+
/** Buffered USD in VA USDC bucket (includes full VA idle when asset === USDC). */
|
|
42782
42941
|
get vaUsdcUsd() {
|
|
42783
42942
|
return this.bufferedTokenUsd(this.vaultUsdcBalance);
|
|
42784
42943
|
}
|
|
@@ -42860,9 +43019,13 @@ var SolveBudget = class {
|
|
|
42860
43019
|
get vesuRebalanceFlags() {
|
|
42861
43020
|
return this.shouldVesuRebalance;
|
|
42862
43021
|
}
|
|
42863
|
-
/** Raw USD in VA (USDC slot + asset slot); spend caps when executing transfers. */
|
|
43022
|
+
/** Raw USD in VA (USDC slot + non-stable asset slot when distinct); spend caps when executing transfers. */
|
|
42864
43023
|
_vaRawUsd() {
|
|
42865
|
-
return this._rawTokenUsd(this.vaultUsdcBalance)
|
|
43024
|
+
return this._rawTokenUsd(this.vaultUsdcBalance);
|
|
43025
|
+
}
|
|
43026
|
+
/** VA liquidity usable for repay / {@link spendVaRawUsd} (matches nominal balances after any {@link applyBuffer} scaling). */
|
|
43027
|
+
get vaRawUsd() {
|
|
43028
|
+
return this._vaRawUsd();
|
|
42866
43029
|
}
|
|
42867
43030
|
_walletRawUsd() {
|
|
42868
43031
|
return this._rawTokenUsd(this.walletBalance);
|
|
@@ -42938,7 +43101,7 @@ var SolveBudget = class {
|
|
|
42938
43101
|
rem -= fromUsdc;
|
|
42939
43102
|
}
|
|
42940
43103
|
if (rem > 0 && this.vaultAssetBalance) {
|
|
42941
|
-
|
|
43104
|
+
throw new Error(`Not implemented: spendVA with vaultAssetBalance`);
|
|
42942
43105
|
}
|
|
42943
43106
|
this._recomputeUnusedBalance();
|
|
42944
43107
|
logger.debug(`SolveBudget::spendVA usedRaw=${usedRaw}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
|
|
@@ -42948,7 +43111,10 @@ var SolveBudget = class {
|
|
|
42948
43111
|
* Spend nominal/raw USD from VA (e.g. Vesu repay, on-chain USDC). Does not apply the safety buffer to the cap.
|
|
42949
43112
|
*/
|
|
42950
43113
|
spendVaRawUsd(rawUsdDesired) {
|
|
42951
|
-
const capRaw =
|
|
43114
|
+
const capRaw = (
|
|
43115
|
+
// this._rawTokenUsd(this.vaultUsdcBalance) + this._rawTokenUsd(this.vaultAssetBalance);
|
|
43116
|
+
this._rawTokenUsd(this.vaultUsdcBalance)
|
|
43117
|
+
);
|
|
42952
43118
|
const usedRaw = Math.min(capRaw, Math.max(0, rawUsdDesired));
|
|
42953
43119
|
if (usedRaw <= CASE_THRESHOLD_USD) return 0;
|
|
42954
43120
|
let rem = usedRaw;
|
|
@@ -42958,7 +43124,7 @@ var SolveBudget = class {
|
|
|
42958
43124
|
rem -= fromUsdc;
|
|
42959
43125
|
}
|
|
42960
43126
|
if (rem > 0 && this.vaultAssetBalance) {
|
|
42961
|
-
|
|
43127
|
+
throw new Error(`Not implemented: spendVaRawUsd with vaultAssetBalance`);
|
|
42962
43128
|
}
|
|
42963
43129
|
this._recomputeUnusedBalance();
|
|
42964
43130
|
logger.debug(`SolveBudget::spendVaRawUsd usedRaw=${usedRaw}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
|
|
@@ -42973,7 +43139,7 @@ var SolveBudget = class {
|
|
|
42973
43139
|
if (this.vaultUsdcBalance) {
|
|
42974
43140
|
this._addUsdToTokenBalance(this.vaultUsdcBalance, rawUsd);
|
|
42975
43141
|
} else if (this.vaultAssetBalance) {
|
|
42976
|
-
|
|
43142
|
+
throw new Error(`Not implemented: addToVA with vaultAssetBalance`);
|
|
42977
43143
|
}
|
|
42978
43144
|
this._recomputeUnusedBalance();
|
|
42979
43145
|
logger.debug(`SolveBudget::addToVA rawUsd=${rawUsd}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
|
|
@@ -43012,7 +43178,7 @@ var SolveBudget = class {
|
|
|
43012
43178
|
if (isSpend) {
|
|
43013
43179
|
const capRaw = this.extendedBalance?.availableForWithdrawal?.toNumber() ?? 0;
|
|
43014
43180
|
const useRaw = Math.min(capRaw, desiredRaw);
|
|
43015
|
-
if (useRaw <=
|
|
43181
|
+
if (useRaw <= 0) return 0;
|
|
43016
43182
|
rawDelta = -useRaw;
|
|
43017
43183
|
} else {
|
|
43018
43184
|
rawDelta = desiredRaw;
|
|
@@ -43032,7 +43198,7 @@ var SolveBudget = class {
|
|
|
43032
43198
|
if (isSpend) {
|
|
43033
43199
|
const capRaw = this.extendedBalance?.unrealisedPnl?.toNumber() ?? 0;
|
|
43034
43200
|
const useRaw = Math.min(capRaw, desiredRaw);
|
|
43035
|
-
if (useRaw <=
|
|
43201
|
+
if (useRaw <= 0) return 0;
|
|
43036
43202
|
rawDelta = -useRaw;
|
|
43037
43203
|
} else {
|
|
43038
43204
|
rawDelta = desiredRaw;
|
|
@@ -43047,10 +43213,12 @@ var SolveBudget = class {
|
|
|
43047
43213
|
return rawDelta;
|
|
43048
43214
|
}
|
|
43049
43215
|
spendExtAvailTrade(rawDesired) {
|
|
43050
|
-
const
|
|
43051
|
-
const
|
|
43052
|
-
|
|
43053
|
-
|
|
43216
|
+
const usedWd = this._updateExtAvailWithdraw(rawDesired, true);
|
|
43217
|
+
const tookWd = Math.abs(usedWd);
|
|
43218
|
+
const rem = rawDesired - tookWd;
|
|
43219
|
+
const usedUpnl = rem > 0 ? this._updateExtAvailUpnl(rem, true) : 0;
|
|
43220
|
+
logger.debug(`SolveBudget::updateExtAvailTrade rawSum=${usedWd + usedUpnl}, extAvailTrade=${this.extAvailTrade}, totalUnused=${this.totalUnused}`);
|
|
43221
|
+
return usedWd + usedUpnl;
|
|
43054
43222
|
}
|
|
43055
43223
|
// simply reduces available amounts, but maintains equity and balance.
|
|
43056
43224
|
spendExtAvailTradeToEquityOnly(rawDesired) {
|
|
@@ -43370,7 +43538,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43370
43538
|
this._fetchExtendedPositions()
|
|
43371
43539
|
]);
|
|
43372
43540
|
logger.verbose(
|
|
43373
|
-
`${this._tag}::_refresh VA asset ${vaultAssetBalance.token.symbol}=$${vaultAssetBalance.usdValue.toFixed(2)}
|
|
43541
|
+
`${this._tag}::_refresh ${vaultAssetBalance ? `VA asset ${vaultAssetBalance.token.symbol}=$${vaultAssetBalance.usdValue.toFixed(2)}, ` : ""}VA USDC=${vaultUsdcBalance.usdValue.toFixed(2)}, wallet=${walletBalance.usdValue}`
|
|
43374
43542
|
);
|
|
43375
43543
|
const unusedBalance = this._computeUnusedBalances(
|
|
43376
43544
|
vaultAssetBalance,
|
|
@@ -43395,23 +43563,28 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43395
43563
|
},
|
|
43396
43564
|
vesuPoolStates
|
|
43397
43565
|
});
|
|
43566
|
+
this._budget.logStateSummary();
|
|
43398
43567
|
const totalUnusedUsd = unusedBalance.reduce(
|
|
43399
43568
|
(acc, b) => acc + b.usdValue,
|
|
43400
43569
|
0
|
|
43401
43570
|
);
|
|
43402
43571
|
logger.info(
|
|
43403
|
-
`${this._tag}::_refresh completed \u2014 unusedBalances: ${unusedBalance.length} tokens, totalUnusedUsd: ${totalUnusedUsd.toFixed(2)}, extendedPositions: ${extendedPositions.length}, vesuPools: ${vesuPoolStates.length}
|
|
43572
|
+
`${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()}`
|
|
43404
43573
|
);
|
|
43405
43574
|
}
|
|
43406
43575
|
// todo add communication check with python server of extended. if not working, throw error in solve function.
|
|
43407
|
-
/** True when strategy asset and USDC share one token — VA
|
|
43576
|
+
/** True when strategy asset and USDC share one token — VA idle balance is tracked as USDC, not as asset. */
|
|
43408
43577
|
_vaultAssetAndUsdcAreSameToken() {
|
|
43409
43578
|
return this._config.assetToken.address.eq(this._config.usdcToken.address);
|
|
43410
43579
|
}
|
|
43411
43580
|
/**
|
|
43412
|
-
* Reads
|
|
43581
|
+
* Reads idle {@link StateManagerConfig.assetToken} in the vault allocator when it differs from USDC.
|
|
43582
|
+
* When asset and USDC are the same token, returns null (that balance is reported via {@link _fetchVaultAllocatorUsdcBalanceIfDistinct} only).
|
|
43413
43583
|
*/
|
|
43414
43584
|
async _fetchVaultAllocatorAssetBalance() {
|
|
43585
|
+
if (this._vaultAssetAndUsdcAreSameToken()) {
|
|
43586
|
+
return null;
|
|
43587
|
+
}
|
|
43415
43588
|
const { assetToken, vaultAllocator, networkConfig, pricer } = this._config;
|
|
43416
43589
|
const balance = await new ERC20(networkConfig).balanceOf(
|
|
43417
43590
|
assetToken.address,
|
|
@@ -43423,11 +43596,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43423
43596
|
return { token: assetToken, amount: balance, usdValue };
|
|
43424
43597
|
}
|
|
43425
43598
|
/**
|
|
43426
|
-
* Reads {@link StateManagerConfig.usdcToken} in the vault allocator
|
|
43427
|
-
* {@link StateManagerConfig.assetToken}. Otherwise returns null (treat VA USDC as 0; stablecoin is only under asset).
|
|
43599
|
+
* Reads {@link StateManagerConfig.usdcToken} idle in the vault allocator (always — distinct asset or USDC-as-asset).
|
|
43428
43600
|
*/
|
|
43429
43601
|
async _fetchVaultAllocatorUsdcBalanceIfDistinct() {
|
|
43430
|
-
if (this._vaultAssetAndUsdcAreSameToken()) return null;
|
|
43431
43602
|
const { usdcToken, vaultAllocator, networkConfig, pricer } = this._config;
|
|
43432
43603
|
const balance = await new ERC20(networkConfig).balanceOf(
|
|
43433
43604
|
usdcToken.address,
|
|
@@ -43441,7 +43612,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43441
43612
|
return { token: usdcToken, amount: balance, usdValue };
|
|
43442
43613
|
}
|
|
43443
43614
|
/**
|
|
43444
|
-
* Merges vault-allocator asset,
|
|
43615
|
+
* Merges vault-allocator asset (if any), vault-allocator USDC, and operator wallet
|
|
43445
43616
|
* balances into entries keyed by token address.
|
|
43446
43617
|
*/
|
|
43447
43618
|
_computeUnusedBalances(vaultAssetBalance, vaultUsdcBalance, walletBalance) {
|
|
@@ -43453,8 +43624,8 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43453
43624
|
usdValue: tb.usdValue
|
|
43454
43625
|
});
|
|
43455
43626
|
};
|
|
43456
|
-
put(vaultAssetBalance);
|
|
43457
|
-
|
|
43627
|
+
if (vaultAssetBalance) put(vaultAssetBalance);
|
|
43628
|
+
put(vaultUsdcBalance);
|
|
43458
43629
|
const key = walletBalance.token.address.toString();
|
|
43459
43630
|
const existing = balanceMap.get(key);
|
|
43460
43631
|
if (existing) {
|
|
@@ -43973,7 +44144,10 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43973
44144
|
// ── ExecutionRoute-building helpers ─────────────────────────────────────────────
|
|
43974
44145
|
// ── Atomic route builders ────────────────────────────────────────────
|
|
43975
44146
|
_buildVesuRepayRoutes(totalUsd, routes) {
|
|
43976
|
-
const
|
|
44147
|
+
const vaCap = this._budget.vaRawUsd;
|
|
44148
|
+
const repayUsd = Math.min(totalUsd, vaCap);
|
|
44149
|
+
if (repayUsd <= CASE_THRESHOLD_USD) return;
|
|
44150
|
+
const { used, spendsByPool } = this._budget.repayVesuBorrowCapacity(repayUsd);
|
|
43977
44151
|
for (const route of spendsByPool) {
|
|
43978
44152
|
routes.push({ type: "VESU_REPAY" /* VESU_REPAY */, ...route, priority: routes.length });
|
|
43979
44153
|
}
|
|
@@ -44067,7 +44241,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44067
44241
|
return { routes, remaining: tryAmount };
|
|
44068
44242
|
}
|
|
44069
44243
|
_getVAToEXTENDEDRoute(tryAmount, routes, shouldAddWaitRoute = true) {
|
|
44070
|
-
const usable = Math.min(tryAmount, this._budget.
|
|
44244
|
+
const usable = Math.min(tryAmount, this._budget.vaUsdcUsd);
|
|
44071
44245
|
if (usable > CASE_THRESHOLD_USD) {
|
|
44072
44246
|
const vaUsed = this._budget.spendVA(usable);
|
|
44073
44247
|
this._budget.addToExtAvailTrade(vaUsed);
|
|
@@ -44082,7 +44256,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44082
44256
|
}
|
|
44083
44257
|
_getExtendedToWalletRoute(tryAmount, routes, shouldAddWaitRoute = true) {
|
|
44084
44258
|
if (tryAmount <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
|
|
44085
|
-
const rawCap = this._budget.extAvailWithdraw + this._budget.extAvailUpnl;
|
|
44259
|
+
const rawCap = this._budget.extAvailWithdraw + Math.max(0, this._budget.extAvailUpnl);
|
|
44086
44260
|
const rawSpend = Math.min(tryAmount, rawCap);
|
|
44087
44261
|
if (rawSpend <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
|
|
44088
44262
|
const rawOut = this._budget.spendExtAvailTrade(rawSpend);
|
|
@@ -44272,7 +44446,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44272
44446
|
* Design: accumulate all ext-to-wallet moves, add transfer routes at the end (principle #3).
|
|
44273
44447
|
*/
|
|
44274
44448
|
/**
|
|
44275
|
-
* Unified LTV classifier
|
|
44449
|
+
* Unified LTV / exposure classifier: `rebalance()` drives both LTV (debt, margin,
|
|
44450
|
+
* funding) and Vesu↔Extended position alignment. There is no separate imbalance pass.
|
|
44451
|
+
* Computes both Vesu repay and Extended margin needs,
|
|
44276
44452
|
* then builds all routes in a single pass with no duplicate transfers.
|
|
44277
44453
|
*
|
|
44278
44454
|
* Vesu repay priority: VA > Wallet > ExtAvl > ExtUpnl
|
|
@@ -44328,7 +44504,8 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44328
44504
|
},
|
|
44329
44505
|
config: {
|
|
44330
44506
|
positionPrecision: COLLATERAL_PRECISION,
|
|
44331
|
-
hfBuffer: 0.05
|
|
44507
|
+
hfBuffer: 0.05,
|
|
44508
|
+
minRoutableUsd: CASE_THRESHOLD_USD
|
|
44332
44509
|
}
|
|
44333
44510
|
};
|
|
44334
44511
|
}
|
|
@@ -44339,8 +44516,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44339
44516
|
}
|
|
44340
44517
|
/**
|
|
44341
44518
|
* Turn pure rebalance() deltas into execution routes.
|
|
44342
|
-
* Order: Vesu multiply
|
|
44343
|
-
* (REALISE_PNL, EXTENDED_TO_WALLET + WAIT, WALLET_TO_VA, VESU_BORROW, VESU_REPAY, VA_TO_EXTENDED)
|
|
44519
|
+
* Order: Vesu multiply decrease → Extended decrease → aggregated transfers
|
|
44520
|
+
* (REALISE_PNL, EXTENDED_TO_WALLET + WAIT, WALLET_TO_VA, VESU_BORROW, VESU_REPAY, VA_TO_EXTENDED),
|
|
44521
|
+
* then Vesu multiply increase and Extended increase (need VA / Extended funded first).
|
|
44344
44522
|
*/
|
|
44345
44523
|
_buildLtvRoutesFromRebalanceDeltas(d) {
|
|
44346
44524
|
const routes = [];
|
|
@@ -44352,6 +44530,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44352
44530
|
const targetLtv = VesuConfig.targetLtv;
|
|
44353
44531
|
const btcEps = 10 ** -COLLATERAL_PRECISION;
|
|
44354
44532
|
let multiplyDebtRepayUsd = 0;
|
|
44533
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas d=${JSON.stringify(d)}`);
|
|
44355
44534
|
if (d.dVesuPosition < -btcEps) {
|
|
44356
44535
|
const xBtc = -d.dVesuPosition;
|
|
44357
44536
|
const transferUsdFromVesu = Math.max(0, d.dTransferVesuToExt);
|
|
@@ -44370,12 +44549,28 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44370
44549
|
if (d.dVesuDebt < 0) {
|
|
44371
44550
|
const needRepayUsd = -d.dVesuDebt * debtPrice;
|
|
44372
44551
|
const multiplyRepayUsd = Math.min(needRepayUsd, swapLegMaxRepayUsd);
|
|
44552
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas ${JSON.stringify({
|
|
44553
|
+
needRepayUsd,
|
|
44554
|
+
multiplyRepayUsd
|
|
44555
|
+
})}`);
|
|
44373
44556
|
debtTokenDelta = -(multiplyRepayUsd / debtPrice);
|
|
44374
44557
|
} else {
|
|
44375
44558
|
debtTokenDelta = -debtUsdFallback;
|
|
44376
44559
|
}
|
|
44377
44560
|
const debtAmtW3 = new Web3Number(debtTokenDelta.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
|
|
44378
44561
|
multiplyDebtRepayUsd = Math.abs(debtTokenDelta) * debtPrice;
|
|
44562
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas ${JSON.stringify({
|
|
44563
|
+
debtTokenDelta,
|
|
44564
|
+
debtUsdFallback,
|
|
44565
|
+
swapLegMaxRepayUsd,
|
|
44566
|
+
xBtc,
|
|
44567
|
+
marginBtc,
|
|
44568
|
+
swappedBtc,
|
|
44569
|
+
transferUsdFromVesu,
|
|
44570
|
+
debtPrice,
|
|
44571
|
+
targetLtv,
|
|
44572
|
+
multiplyDebtRepayUsd
|
|
44573
|
+
})}`);
|
|
44379
44574
|
routes.push({
|
|
44380
44575
|
type: "VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */,
|
|
44381
44576
|
poolId: vesuAdapter.config.poolId,
|
|
@@ -44396,19 +44591,68 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44396
44591
|
if (transferUsdFromVesu > CASE_THRESHOLD_USD) {
|
|
44397
44592
|
this._budget.addToVA(transferUsdFromVesu);
|
|
44398
44593
|
}
|
|
44399
|
-
}
|
|
44594
|
+
}
|
|
44595
|
+
if (d.dExtPosition < -btcEps) {
|
|
44596
|
+
const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
|
|
44597
|
+
routes.push({
|
|
44598
|
+
type: "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
|
|
44599
|
+
amount: amt,
|
|
44600
|
+
instrument,
|
|
44601
|
+
priority: routes.length
|
|
44602
|
+
});
|
|
44603
|
+
this._budget.applyExtendedExposureDelta(instrument, amt, price);
|
|
44604
|
+
}
|
|
44605
|
+
const negUpnl = Math.min(0, d.dExtUpnl);
|
|
44606
|
+
const negExtAvl = Math.min(0, d.dExtAvlWithdraw);
|
|
44607
|
+
let hadExtendedOut = false;
|
|
44608
|
+
if (negUpnl < -CASE_THRESHOLD_USD) {
|
|
44609
|
+
this._getUpnlRoute(Math.abs(negUpnl), routes);
|
|
44610
|
+
hadExtendedOut = true;
|
|
44611
|
+
}
|
|
44612
|
+
const extToWalletUsd = (negExtAvl < -CASE_THRESHOLD_USD ? Math.abs(negExtAvl) : 0) + (negUpnl < -CASE_THRESHOLD_USD ? Math.abs(negUpnl) : 0);
|
|
44613
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas extToWalletUsd=${extToWalletUsd}, negExtAvl=${negExtAvl}, negUpnl=${negUpnl}`);
|
|
44614
|
+
if (extToWalletUsd > CASE_THRESHOLD_USD) {
|
|
44615
|
+
this._getExtendedToWalletRoute(extToWalletUsd, routes);
|
|
44616
|
+
hadExtendedOut = true;
|
|
44617
|
+
} else {
|
|
44618
|
+
if (d.dVesuDebt < 0) {
|
|
44619
|
+
d.dVesuDebt += (negExtAvl < 0 && negExtAvl > -CASE_THRESHOLD_USD ? Math.abs(negExtAvl) : 0) + (negUpnl < 0 && negUpnl > -CASE_THRESHOLD_USD ? Math.abs(negUpnl) : 0);
|
|
44620
|
+
}
|
|
44621
|
+
}
|
|
44622
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas d=${JSON.stringify(d)}`);
|
|
44623
|
+
const walletPull = Math.abs(Math.min(0, d.dWalletUsd));
|
|
44624
|
+
const walletToVaUsd = walletPull + extToWalletUsd;
|
|
44625
|
+
if (walletToVaUsd > CASE_THRESHOLD_USD) {
|
|
44626
|
+
this._getWALLETToVARoute(walletToVaUsd, routes);
|
|
44627
|
+
}
|
|
44628
|
+
if (d.dVesuBorrowCapacity < -CASE_THRESHOLD_USD) {
|
|
44629
|
+
this._buildVesuBorrowRoutes(Math.abs(d.dVesuBorrowCapacity), routes);
|
|
44630
|
+
}
|
|
44631
|
+
const totalDebtRepayUsd = d.dVesuDebt < 0 ? -d.dVesuDebt * debtPrice : 0;
|
|
44632
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas totalDebtRepayUsd=${totalDebtRepayUsd}`);
|
|
44633
|
+
let standaloneRepayUsd = Math.max(0, totalDebtRepayUsd - multiplyDebtRepayUsd);
|
|
44634
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas standaloneRepayUsd=${standaloneRepayUsd}`);
|
|
44635
|
+
if (standaloneRepayUsd > this._budget.vaRawUsd) {
|
|
44636
|
+
if (Math.abs(standaloneRepayUsd - this._budget.vaRawUsd) < CASE_THRESHOLD_USD) {
|
|
44637
|
+
standaloneRepayUsd = this._budget.vaRawUsd;
|
|
44638
|
+
} else {
|
|
44639
|
+
throw new Error(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas standaloneRepayUsd=${standaloneRepayUsd} > vaRawUsd=${this._budget.vaRawUsd}`);
|
|
44640
|
+
}
|
|
44641
|
+
}
|
|
44642
|
+
if (standaloneRepayUsd > CASE_THRESHOLD_USD) {
|
|
44643
|
+
this._buildVesuRepayRoutes(standaloneRepayUsd, routes);
|
|
44644
|
+
}
|
|
44645
|
+
const posExtEq = Math.max(0, d.dExtAvlWithdraw);
|
|
44646
|
+
const vaToExtUsd = posExtEq > CASE_THRESHOLD_USD ? posExtEq : 0;
|
|
44647
|
+
if (vaToExtUsd > CASE_THRESHOLD_USD) {
|
|
44648
|
+
this._getVAToEXTENDEDRoute(vaToExtUsd, routes, hadExtendedOut);
|
|
44649
|
+
}
|
|
44650
|
+
if (d.dVesuPosition > btcEps) {
|
|
44400
44651
|
const vesuDepositAmount = new Web3Number(
|
|
44401
44652
|
(d.dVesuPosition * price * (1 - targetLtv)).toFixed(USDC_TOKEN_DECIMALS),
|
|
44402
44653
|
USDC_TOKEN_DECIMALS
|
|
44403
44654
|
);
|
|
44404
44655
|
if (vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
|
|
44405
|
-
routes.push({
|
|
44406
|
-
type: "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
|
|
44407
|
-
priority: routes.length,
|
|
44408
|
-
fromToken: vesuAdapter.config.collateral.symbol,
|
|
44409
|
-
fromAmount: vesuDepositAmount,
|
|
44410
|
-
toToken: vesuAdapter.config.debt.symbol
|
|
44411
|
-
});
|
|
44412
44656
|
}
|
|
44413
44657
|
const collateralDelta = new Web3Number(
|
|
44414
44658
|
d.dVesuPosition.toFixed(COLLATERAL_PRECISION),
|
|
@@ -44419,8 +44663,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44419
44663
|
new Web3Number(Math.min(availableBorrowCapacity, vesuDepositAmount.toNumber()).toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS)
|
|
44420
44664
|
);
|
|
44421
44665
|
const collPx = pool.collateralPrice || 1;
|
|
44666
|
+
const marginUsdAmount = externalDepositAmount.toNumber() * (pool.debtPrice ?? 1);
|
|
44422
44667
|
const swappedAmount = new Web3Number(
|
|
44423
|
-
(
|
|
44668
|
+
(marginUsdAmount / collPx).toFixed(6),
|
|
44424
44669
|
vesuAdapter.config.collateral.decimals
|
|
44425
44670
|
);
|
|
44426
44671
|
const debtDeltaTokens = new Web3Number(
|
|
@@ -44444,17 +44689,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44444
44689
|
collateralDelta,
|
|
44445
44690
|
debtDeltaTokens
|
|
44446
44691
|
);
|
|
44692
|
+
this._budget.spendVA(marginUsdAmount);
|
|
44447
44693
|
}
|
|
44448
|
-
if (d.dExtPosition
|
|
44449
|
-
const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
|
|
44450
|
-
routes.push({
|
|
44451
|
-
type: "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
|
|
44452
|
-
amount: amt,
|
|
44453
|
-
instrument,
|
|
44454
|
-
priority: routes.length
|
|
44455
|
-
});
|
|
44456
|
-
this._budget.applyExtendedExposureDelta(instrument, amt, price);
|
|
44457
|
-
} else if (d.dExtPosition > btcEps) {
|
|
44694
|
+
if (d.dExtPosition > btcEps) {
|
|
44458
44695
|
const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
|
|
44459
44696
|
routes.push({
|
|
44460
44697
|
type: "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */,
|
|
@@ -44464,36 +44701,6 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44464
44701
|
});
|
|
44465
44702
|
this._budget.applyExtendedExposureDelta(instrument, amt, price);
|
|
44466
44703
|
}
|
|
44467
|
-
const negUpnl = Math.min(0, d.dExtUpnl);
|
|
44468
|
-
const negExtAvl = Math.min(0, d.dExtAvlWithdraw);
|
|
44469
|
-
let hadExtendedOut = false;
|
|
44470
|
-
if (negUpnl < -CASE_THRESHOLD_USD) {
|
|
44471
|
-
this._getUpnlRoute(Math.abs(negUpnl), routes);
|
|
44472
|
-
hadExtendedOut = true;
|
|
44473
|
-
}
|
|
44474
|
-
const extToWalletUsd = (negExtAvl < -CASE_THRESHOLD_USD ? Math.abs(negExtAvl) : 0) + (negUpnl < -CASE_THRESHOLD_USD ? Math.abs(negUpnl) : 0);
|
|
44475
|
-
if (extToWalletUsd > CASE_THRESHOLD_USD) {
|
|
44476
|
-
this._getExtendedToWalletRoute(extToWalletUsd, routes);
|
|
44477
|
-
hadExtendedOut = true;
|
|
44478
|
-
}
|
|
44479
|
-
const walletPull = Math.abs(Math.min(0, d.dWalletUsd));
|
|
44480
|
-
const walletToVaUsd = walletPull + extToWalletUsd;
|
|
44481
|
-
if (walletToVaUsd > CASE_THRESHOLD_USD) {
|
|
44482
|
-
this._getWALLETToVARoute(walletToVaUsd, routes);
|
|
44483
|
-
}
|
|
44484
|
-
if (d.dVesuBorrowCapacity < -CASE_THRESHOLD_USD) {
|
|
44485
|
-
this._buildVesuBorrowRoutes(Math.abs(d.dVesuBorrowCapacity), routes);
|
|
44486
|
-
}
|
|
44487
|
-
const totalDebtRepayUsd = d.dVesuDebt < 0 ? -d.dVesuDebt * debtPrice : 0;
|
|
44488
|
-
const standaloneRepayUsd = Math.max(0, totalDebtRepayUsd - multiplyDebtRepayUsd);
|
|
44489
|
-
if (standaloneRepayUsd > CASE_THRESHOLD_USD) {
|
|
44490
|
-
this._buildVesuRepayRoutes(standaloneRepayUsd, routes);
|
|
44491
|
-
}
|
|
44492
|
-
const posExtEq = Math.max(0, d.dExtAvlWithdraw);
|
|
44493
|
-
const vaToExtUsd = posExtEq > CASE_THRESHOLD_USD ? posExtEq : 0;
|
|
44494
|
-
if (vaToExtUsd > CASE_THRESHOLD_USD) {
|
|
44495
|
-
this._getVAToEXTENDEDRoute(vaToExtUsd, routes, hadExtendedOut);
|
|
44496
|
-
}
|
|
44497
44704
|
return routes;
|
|
44498
44705
|
}
|
|
44499
44706
|
// ── LTV Vesu route builders ───────────────────────────────────────────
|
|
@@ -44652,48 +44859,76 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44652
44859
|
const extendedTarget = total / (1 + extendedLeverage / vesuLeverage);
|
|
44653
44860
|
const extendedInitial = extAvlWithdraw + extUpnl;
|
|
44654
44861
|
let delta = extendedTarget - extendedInitial;
|
|
44655
|
-
let dExtAvlWithdraw = 0, dExtUpnl = 0, dVaUsd = 0, dWalletUsd = 0, dVesuBorrowCapacity = 0;
|
|
44862
|
+
let dExtAvlWithdraw = 0, dExtUpnl = 0, dVaUsd = 0, dWalletUsd = 0, dVesuBorrowCapacity = 0, finalVaUsd = vaUsd, finalExtended = extAvlWithdraw + Math.max(extUpnl, 0);
|
|
44656
44863
|
if (delta > 0) {
|
|
44657
44864
|
let need = delta;
|
|
44658
|
-
const
|
|
44865
|
+
const eps = CASE_THRESHOLD_USD;
|
|
44866
|
+
const takeWalletUsd = routableDrawAmount(walletUsd, need, eps);
|
|
44659
44867
|
dWalletUsd -= takeWalletUsd;
|
|
44660
44868
|
need -= takeWalletUsd;
|
|
44661
|
-
const takeVaUsd =
|
|
44869
|
+
const takeVaUsd = routableDrawAmount(vaUsd, need, eps);
|
|
44662
44870
|
dVaUsd -= takeVaUsd;
|
|
44663
44871
|
need -= takeVaUsd;
|
|
44664
|
-
|
|
44872
|
+
finalVaUsd -= takeVaUsd;
|
|
44873
|
+
finalVaUsd += walletUsd - takeWalletUsd;
|
|
44874
|
+
const takeVesuBorrowCapacity = routableDrawAmount(vesuBorrowCapacity, need, eps);
|
|
44665
44875
|
dVesuBorrowCapacity -= takeVesuBorrowCapacity;
|
|
44666
44876
|
need -= takeVesuBorrowCapacity;
|
|
44877
|
+
finalVaUsd += vesuBorrowCapacity - takeVesuBorrowCapacity;
|
|
44667
44878
|
const received = delta - need;
|
|
44668
44879
|
const eco1Sum = extAvlWithdraw + extUpnl;
|
|
44880
|
+
finalExtended += received;
|
|
44669
44881
|
if (eco1Sum >= 0) {
|
|
44670
44882
|
dExtAvlWithdraw += received;
|
|
44671
44883
|
} else {
|
|
44672
|
-
|
|
44884
|
+
const hole = -eco1Sum;
|
|
44885
|
+
const fillUpnl = Math.min(received, hole);
|
|
44886
|
+
dExtUpnl += fillUpnl;
|
|
44887
|
+
dExtAvlWithdraw += received - fillUpnl;
|
|
44888
|
+
finalExtended -= fillUpnl;
|
|
44673
44889
|
}
|
|
44674
|
-
if (need >
|
|
44890
|
+
if (need > CASE_THRESHOLD_USD) {
|
|
44675
44891
|
throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
|
|
44676
44892
|
}
|
|
44677
44893
|
} else if (delta < 0) {
|
|
44678
44894
|
let need = -delta;
|
|
44679
|
-
const takeExtAvlWithdraw = Math.min(extAvlWithdraw, need);
|
|
44895
|
+
const takeExtAvlWithdraw = Math.min(Math.max(extAvlWithdraw, 0), need);
|
|
44680
44896
|
dExtAvlWithdraw -= takeExtAvlWithdraw;
|
|
44681
|
-
|
|
44682
|
-
const takeExtUpnl = Math.min(extUpnl, need);
|
|
44897
|
+
const takeExtUpnl = Math.min(Math.max(extUpnl, 0), need);
|
|
44683
44898
|
dExtUpnl -= takeExtUpnl;
|
|
44684
|
-
|
|
44899
|
+
const netDrawableAmount = takeExtAvlWithdraw + takeExtUpnl;
|
|
44900
|
+
if (netDrawableAmount > CASE_THRESHOLD_USD) {
|
|
44901
|
+
need -= netDrawableAmount;
|
|
44902
|
+
finalExtended -= netDrawableAmount;
|
|
44903
|
+
}
|
|
44685
44904
|
const sent = -delta - need;
|
|
44686
44905
|
const eco2Sum = vaUsd + walletUsd + vesuBorrowCapacity;
|
|
44906
|
+
const netWalletUsd = walletUsd < CASE_THRESHOLD_USD ? 0 : walletUsd;
|
|
44907
|
+
finalVaUsd += sent + netWalletUsd;
|
|
44687
44908
|
if (eco2Sum >= 0) {
|
|
44688
44909
|
dWalletUsd += sent;
|
|
44689
44910
|
} else {
|
|
44690
44911
|
throw new Error(`${this._tag}: Unexpected case`);
|
|
44691
44912
|
}
|
|
44692
|
-
if (need >
|
|
44913
|
+
if (need > CASE_THRESHOLD_USD) {
|
|
44693
44914
|
throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
|
|
44694
44915
|
}
|
|
44695
44916
|
}
|
|
44696
|
-
return { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu: delta < 0 };
|
|
44917
|
+
return { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, finalVaUsd, finalExtended, isExtendedToVesu: delta < 0 };
|
|
44918
|
+
}
|
|
44919
|
+
_scaleVesuPoolDeltasByFactor(deltas, scale) {
|
|
44920
|
+
if (scale >= 1 - 1e-15) return deltas;
|
|
44921
|
+
return deltas.map((d) => ({
|
|
44922
|
+
...d,
|
|
44923
|
+
collateralDelta: new Web3Number(
|
|
44924
|
+
(d.collateralDelta.toNumber() * scale).toFixed(COLLATERAL_PRECISION),
|
|
44925
|
+
d.collateralToken.decimals
|
|
44926
|
+
),
|
|
44927
|
+
debtDelta: new Web3Number(
|
|
44928
|
+
(d.debtDelta.toNumber() * scale).toFixed(USDC_TOKEN_DECIMALS),
|
|
44929
|
+
USDC_TOKEN_DECIMALS
|
|
44930
|
+
)
|
|
44931
|
+
}));
|
|
44697
44932
|
}
|
|
44698
44933
|
/**
|
|
44699
44934
|
* 3. New Deposits / Excess Funds
|
|
@@ -44709,6 +44944,11 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44709
44944
|
* Computes allocation split between Vesu and Extended, then sources
|
|
44710
44945
|
* funds and creates lever-increase routes.
|
|
44711
44946
|
*
|
|
44947
|
+
* Order: {@link _rebalanceFunds} first → project VA / Extended liquid after the same funding
|
|
44948
|
+
* routes (wallet→VA, borrow→VA, VA→Extended, Extended→wallet→VA) → ideal Vesu/Extended deltas
|
|
44949
|
+
* from distributable split → cap common BTC by min(Vesu fundable, Extended fundable) → scale
|
|
44950
|
+
* Vesu deltas and recompute Extended deltas so both sides stay matched.
|
|
44951
|
+
*
|
|
44712
44952
|
* Fund flow (single pass — avoid VA→Extended then Extended→wallet round-trips):
|
|
44713
44953
|
* 1) Treat Vesu borrow headroom that the multiply route will consume as covering
|
|
44714
44954
|
* part of the Vesu USDC need (no standalone VESU_BORROW for that slice). Cap
|
|
@@ -44731,15 +44971,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44731
44971
|
withdrawAmount
|
|
44732
44972
|
);
|
|
44733
44973
|
if (distributableAmount.toNumber() <= CASE_THRESHOLD_USD) return [];
|
|
44734
|
-
const { vesuAllocationUsd, extendedAllocationUsd } = this._computeAllocationSplit(distributableAmount);
|
|
44735
|
-
const vesuDeltas = this._computePerPoolCollateralDeltas(
|
|
44736
|
-
vesuAllocationUsd
|
|
44737
|
-
);
|
|
44738
|
-
const extendedPositionDeltas = this._computeExtendedPositionDeltas(vesuDeltas);
|
|
44739
|
-
const vesuDepositAmount = this._computeVesuDepositAmount(vesuDeltas);
|
|
44740
44974
|
const vesuLeverage = calculateVesuLeverage();
|
|
44741
44975
|
const extendedLeverage = calculateExtendedLevergae();
|
|
44742
|
-
const { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu } = this._rebalanceFunds({
|
|
44976
|
+
const { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu, finalVaUsd, finalExtended } = this._rebalanceFunds({
|
|
44743
44977
|
extAvlWithdraw: this._budget.extAvailWithdraw,
|
|
44744
44978
|
extUpnl: this._budget.extAvailUpnl,
|
|
44745
44979
|
vaUsd: this._budget.vaUsd,
|
|
@@ -44748,6 +44982,23 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44748
44982
|
vesuLeverage,
|
|
44749
44983
|
extendedLeverage
|
|
44750
44984
|
});
|
|
44985
|
+
logger.info(`${this._tag}::_classifyDeposits dExtAvlWithdraw=${dExtAvlWithdraw}, dExtUpnl=${dExtUpnl}, dVaUsd=${dVaUsd}, dWalletUsd=${dWalletUsd}, dVesuBorrowCapacity=${dVesuBorrowCapacity}, isExtendedToVesu=${isExtendedToVesu}, finalVaUsd=${finalVaUsd}`);
|
|
44986
|
+
let vesuDeltas = this._computePerPoolCollateralDeltas(new Web3Number(finalVaUsd.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS));
|
|
44987
|
+
const collateralPrice = this._budget.vesuPools[0]?.collateralPrice ?? 0;
|
|
44988
|
+
const collateralDecimals = this._budget.vesuPools[0]?.collateralToken.decimals ?? 0;
|
|
44989
|
+
let _extendedPositionDelta = new Web3Number((finalExtended * extendedLeverage / collateralPrice).toFixed(USDC_TOKEN_DECIMALS), collateralDecimals).toFixedRoundDown(COLLATERAL_PRECISION);
|
|
44990
|
+
logger.info(`${this._tag}::_classifyDeposits extendedPositionDelta=${_extendedPositionDelta}`);
|
|
44991
|
+
logger.info(`${this._tag}::_classifyDeposits vesuDeltas=${JSON.stringify(vesuDeltas)}`);
|
|
44992
|
+
assert(vesuDeltas.length == 1, "vesuDeltas should have only one delta");
|
|
44993
|
+
const minPositionDelta = Math.min(vesuDeltas[0].collateralDelta.toNumber(), Number(_extendedPositionDelta));
|
|
44994
|
+
logger.info(`${this._tag}::_classifyDeposits minPositionDelta=${minPositionDelta}`);
|
|
44995
|
+
vesuDeltas[0].collateralDelta = new Web3Number(minPositionDelta.toFixed(COLLATERAL_PRECISION), vesuDeltas[0].collateralDelta.decimals);
|
|
44996
|
+
const extendedPositionDeltas = [{
|
|
44997
|
+
instrument: this._config.extendedAdapter.config.extendedMarketName ?? "BTC-USD",
|
|
44998
|
+
delta: new Web3Number(minPositionDelta.toFixed(COLLATERAL_PRECISION), collateralDecimals)
|
|
44999
|
+
}];
|
|
45000
|
+
const vesuDepositAmount = this._computeVesuDepositAmount(vesuDeltas);
|
|
45001
|
+
logger.info(`${this._tag}::_classifyDeposits vesuDepositAmount=${vesuDepositAmount}`);
|
|
44751
45002
|
const routes = [];
|
|
44752
45003
|
if (isExtendedToVesu) {
|
|
44753
45004
|
if (dExtUpnl < 0) {
|
|
@@ -44775,13 +45026,6 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44775
45026
|
}
|
|
44776
45027
|
for (const vesuDelta of vesuDeltas) {
|
|
44777
45028
|
if (!skipAvnuDepositSwap && vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
|
|
44778
|
-
routes.push({
|
|
44779
|
-
type: "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
|
|
44780
|
-
priority: routes.length,
|
|
44781
|
-
fromToken: vesuDelta.collateralToken.symbol,
|
|
44782
|
-
fromAmount: vesuDepositAmount,
|
|
44783
|
-
toToken: vesuDelta.debtToken.symbol
|
|
44784
|
-
});
|
|
44785
45029
|
}
|
|
44786
45030
|
if (vesuDelta.collateralDelta.toNumber() > 0) {
|
|
44787
45031
|
const availableBorrowCapacity = this._budget.vesuBorrowCapacity;
|
|
@@ -45761,14 +46005,6 @@ var _ExecutionService = class _ExecutionService {
|
|
|
45761
46005
|
);
|
|
45762
46006
|
return this._executeStandardCase(caseEntry, activeRoutes);
|
|
45763
46007
|
}
|
|
45764
|
-
const setLevResult = await this._config.extendedAdapter.setLeverage(calculateExtendedLevergae().toString(), this._config.extendedAdapter.config.extendedMarketName);
|
|
45765
|
-
if (!setLevResult) {
|
|
45766
|
-
logger.error(
|
|
45767
|
-
`${this._tag}::_executeCoordinatedCase failed to set leverage`
|
|
45768
|
-
);
|
|
45769
|
-
results.push(this._failureResult(extendedRoute));
|
|
45770
|
-
return results;
|
|
45771
|
-
}
|
|
45772
46008
|
const isIncrease = _ExecutionService.INCREASE_EXPOSURE_ROUTES.has(
|
|
45773
46009
|
extendedRoute.type
|
|
45774
46010
|
);
|
|
@@ -45789,8 +46025,9 @@ var _ExecutionService = class _ExecutionService {
|
|
|
45789
46025
|
onChainCalls = callArrays.flat();
|
|
45790
46026
|
} catch (err) {
|
|
45791
46027
|
logger.error(
|
|
45792
|
-
`${this._tag}::_executeCoordinatedCase on-chain call construction failed
|
|
46028
|
+
`${this._tag}::_executeCoordinatedCase on-chain call construction failed:`
|
|
45793
46029
|
);
|
|
46030
|
+
console.error(err);
|
|
45794
46031
|
await this._emitEvent("FAILURE" /* FAILURE */, {
|
|
45795
46032
|
routeSummary: `coordinated on-chain build for case "${caseEntry.case.id}"`,
|
|
45796
46033
|
error: `${err}`
|
|
@@ -45862,7 +46099,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
45862
46099
|
}
|
|
45863
46100
|
const timeoutMs = this._config.extendedFillTimeoutMs ?? DEFAULT_EXTENDED_FILL_TIMEOUT_MS;
|
|
45864
46101
|
const extResult = await this._executeExtendedLimitOrderWithRecovery(
|
|
45865
|
-
btcAmount,
|
|
46102
|
+
Math.abs(btcAmount),
|
|
45866
46103
|
executionPrice,
|
|
45867
46104
|
side,
|
|
45868
46105
|
{
|
|
@@ -46390,7 +46627,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46390
46627
|
return this._failureResult(route);
|
|
46391
46628
|
}
|
|
46392
46629
|
const closeResult = await this._executeExtendedLimitOrderWithRecovery(
|
|
46393
|
-
positionToClose.toNumber(),
|
|
46630
|
+
Math.abs(positionToClose.toNumber()),
|
|
46394
46631
|
midPrice,
|
|
46395
46632
|
"BUY" /* BUY */
|
|
46396
46633
|
);
|
|
@@ -46408,7 +46645,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46408
46645
|
`${this._tag}::_executeRealisePnl reopening ${positionToClose.toNumber()} on ${route.instrument}`
|
|
46409
46646
|
);
|
|
46410
46647
|
const reopenResult = await this._executeExtendedLimitOrderWithRecovery(
|
|
46411
|
-
positionToClose.toNumber(),
|
|
46648
|
+
Math.abs(positionToClose.toNumber()),
|
|
46412
46649
|
midPrice,
|
|
46413
46650
|
"SELL" /* SELL */
|
|
46414
46651
|
);
|
|
@@ -46462,7 +46699,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46462
46699
|
return this._failureResult(route);
|
|
46463
46700
|
}
|
|
46464
46701
|
const result = await this._executeExtendedLimitOrderWithRecovery(
|
|
46465
|
-
route.amount.toNumber(),
|
|
46702
|
+
Math.abs(route.amount.toNumber()),
|
|
46466
46703
|
midPrice,
|
|
46467
46704
|
"SELL" /* SELL */
|
|
46468
46705
|
);
|
|
@@ -46510,7 +46747,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46510
46747
|
return this._failureResult(route);
|
|
46511
46748
|
}
|
|
46512
46749
|
const result = await this._executeExtendedLimitOrderWithRecovery(
|
|
46513
|
-
route.amount.toNumber(),
|
|
46750
|
+
Math.abs(route.amount.toNumber()),
|
|
46514
46751
|
midPrice,
|
|
46515
46752
|
"BUY" /* BUY */
|
|
46516
46753
|
);
|
|
@@ -47133,16 +47370,16 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
|
|
|
47133
47370
|
];
|
|
47134
47371
|
}
|
|
47135
47372
|
};
|
|
47136
|
-
function getLooperSettings3(
|
|
47373
|
+
function getLooperSettings3(collateralSymbol, underlyingSymbol, vaultSettings, pool1, extendedBackendReadUrl, extendedBackendWriteUrl, vaultIdExtended, minimumExtendedMovementAmount, minimumVesuMovementAmount, minimumExtendedRetriesDelayForOrderStatus, minimumExtendedPriceDifferenceForSwapOpen, maximumExtendedPriceDifferenceForSwapClosing) {
|
|
47137
47374
|
vaultSettings.leafAdapters = [];
|
|
47138
47375
|
const wbtcToken = Global.getDefaultTokens().find(
|
|
47139
|
-
(token) => token.symbol ===
|
|
47376
|
+
(token) => token.symbol === collateralSymbol
|
|
47140
47377
|
);
|
|
47141
47378
|
const usdcToken = Global.getDefaultTokens().find(
|
|
47142
47379
|
(token) => token.symbol === underlyingSymbol
|
|
47143
47380
|
);
|
|
47144
47381
|
const baseAdapterConfig = {
|
|
47145
|
-
baseToken:
|
|
47382
|
+
baseToken: usdcToken,
|
|
47146
47383
|
supportedPositions: [
|
|
47147
47384
|
{ asset: usdcToken, isDebt: true },
|
|
47148
47385
|
{ asset: wbtcToken, isDebt: false }
|
|
@@ -47242,7 +47479,7 @@ function getLooperSettings3(lstSymbol, underlyingSymbol, vaultSettings, pool1, e
|
|
|
47242
47479
|
vaultAddress: vaultSettings.vaultAddress,
|
|
47243
47480
|
vaultAllocator: vaultSettings.vaultAllocator,
|
|
47244
47481
|
manager: vaultSettings.manager,
|
|
47245
|
-
asset:
|
|
47482
|
+
asset: usdcToken.address
|
|
47246
47483
|
});
|
|
47247
47484
|
vaultSettings.leafAdapters.push(() => vesuMultiplyAdapter.getDepositLeaf());
|
|
47248
47485
|
vaultSettings.leafAdapters.push(() => vesuMultiplyAdapter.getWithdrawLeaf());
|
|
@@ -47376,6 +47613,39 @@ var re7UsdcPrimeDevansh = {
|
|
|
47376
47613
|
minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
|
|
47377
47614
|
walletAddress: "0x024b563C1C7d41B32BF4EFB9F38828508a65Be2d6e25268E9f63F22C5e9E51c5"
|
|
47378
47615
|
};
|
|
47616
|
+
var pureUsdc = {
|
|
47617
|
+
vaultAddress: ContractAddr.from(
|
|
47618
|
+
"0x745c972db65bdee10022fd875dd328c7f40a90849135b6a0f875a40f3c632ae"
|
|
47619
|
+
),
|
|
47620
|
+
manager: ContractAddr.from(
|
|
47621
|
+
"0x364e0894edefb616ec090f57f5c0274517fcd98ab276ae1f021c5e962fa1deb"
|
|
47622
|
+
),
|
|
47623
|
+
vaultAllocator: ContractAddr.from(
|
|
47624
|
+
"0x6fceed28e03a96091877568893df0dd89b9bb80fec30da2b742dacbd5526179"
|
|
47625
|
+
),
|
|
47626
|
+
redeemRequestNFT: ContractAddr.from(
|
|
47627
|
+
"0x501c2b87728e22c6dfcebe4c0b2b3a9fba5845606e4d59fa7bf591badcbb42"
|
|
47628
|
+
),
|
|
47629
|
+
aumOracle: ContractAddr.from(
|
|
47630
|
+
"0x6ccd95f5765242695d3c75e1440b1d0b30efac8babb864ce15729977b97cb82"
|
|
47631
|
+
),
|
|
47632
|
+
leafAdapters: [],
|
|
47633
|
+
adapters: [],
|
|
47634
|
+
targetHealthFactor: 1.4,
|
|
47635
|
+
minHealthFactor: 1.35,
|
|
47636
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
47637
|
+
(token) => token.symbol === "USDC"
|
|
47638
|
+
),
|
|
47639
|
+
quoteAmountToFetchPrice: new Web3Number(
|
|
47640
|
+
"0.001",
|
|
47641
|
+
Global.getDefaultTokens().find((token) => token.symbol === "USDC").decimals
|
|
47642
|
+
),
|
|
47643
|
+
borrowable_assets: [
|
|
47644
|
+
Global.getDefaultTokens().find((token) => token.symbol === "USDC")
|
|
47645
|
+
],
|
|
47646
|
+
minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
|
|
47647
|
+
walletAddress: "0x058571C23da5FEdd4e36003FAE3fE2fA9782f2692E552f081839142B10770D0B"
|
|
47648
|
+
};
|
|
47379
47649
|
var VesuExtendedTestStrategies = (extendedBackendReadUrl, extendedBackendWriteUrl, vaultIdExtended, minimumExtendedMovementAmount, minimumVesuMovementAmount, minimumExtendedRetriesDelayForOrderStatus, minimumExtendedPriceDifferenceForSwapOpen, maximumExtendedPriceDifferenceForSwapClosing) => {
|
|
47380
47650
|
return [
|
|
47381
47651
|
getStrategySettingsVesuExtended(
|
|
@@ -47392,14 +47662,29 @@ var VesuExtendedTestStrategies = (extendedBackendReadUrl, extendedBackendWriteUr
|
|
|
47392
47662
|
minimumExtendedRetriesDelayForOrderStatus,
|
|
47393
47663
|
minimumExtendedPriceDifferenceForSwapOpen,
|
|
47394
47664
|
maximumExtendedPriceDifferenceForSwapClosing
|
|
47665
|
+
),
|
|
47666
|
+
getStrategySettingsVesuExtended(
|
|
47667
|
+
"WBTC",
|
|
47668
|
+
"USDC",
|
|
47669
|
+
pureUsdc,
|
|
47670
|
+
false,
|
|
47671
|
+
false,
|
|
47672
|
+
extendedBackendReadUrl,
|
|
47673
|
+
extendedBackendWriteUrl,
|
|
47674
|
+
vaultIdExtended,
|
|
47675
|
+
minimumExtendedMovementAmount,
|
|
47676
|
+
minimumVesuMovementAmount,
|
|
47677
|
+
minimumExtendedRetriesDelayForOrderStatus,
|
|
47678
|
+
minimumExtendedPriceDifferenceForSwapOpen,
|
|
47679
|
+
maximumExtendedPriceDifferenceForSwapClosing
|
|
47395
47680
|
)
|
|
47396
47681
|
];
|
|
47397
47682
|
};
|
|
47398
|
-
function getStrategySettingsVesuExtended(
|
|
47683
|
+
function getStrategySettingsVesuExtended(collateralSymbol, underlyingSymbol, addresses, isPreview = false, isLST, extendedBackendReadUrl, extendedBackendWriteUrl, vaultIdExtended, minimumExtendedMovementAmount, minimumVesuMovementAmount, minimumExtendedRetriesDelayForOrderStatus, minimumExtendedPriceDifferenceForSwapOpen, maximumExtendedPriceDifferenceForSwapClosing) {
|
|
47399
47684
|
return {
|
|
47400
47685
|
id: `extended_${underlyingSymbol.toLowerCase()}_test`,
|
|
47401
47686
|
name: `Extended Test ${underlyingSymbol}`,
|
|
47402
|
-
description: getDescription3(
|
|
47687
|
+
description: getDescription3(collateralSymbol, underlyingSymbol),
|
|
47403
47688
|
address: addresses.vaultAddress,
|
|
47404
47689
|
launchBlock: 0,
|
|
47405
47690
|
type: "Other",
|
|
@@ -47413,7 +47698,7 @@ function getStrategySettingsVesuExtended(lstSymbol, underlyingSymbol, addresses,
|
|
|
47413
47698
|
)
|
|
47414
47699
|
],
|
|
47415
47700
|
additionalInfo: getLooperSettings3(
|
|
47416
|
-
|
|
47701
|
+
collateralSymbol,
|
|
47417
47702
|
underlyingSymbol,
|
|
47418
47703
|
addresses,
|
|
47419
47704
|
VesuPools.Re7USDCPrime,
|
|
@@ -47434,8 +47719,8 @@ function getStrategySettingsVesuExtended(lstSymbol, underlyingSymbol, addresses,
|
|
|
47434
47719
|
auditUrl: AUDIT_URL4,
|
|
47435
47720
|
protocols: [Protocols.ENDUR, Protocols.VESU],
|
|
47436
47721
|
contractDetails: getContractDetails(addresses),
|
|
47437
|
-
faqs: getFAQs2(
|
|
47438
|
-
investmentSteps: getInvestmentSteps(
|
|
47722
|
+
faqs: getFAQs2(collateralSymbol, underlyingSymbol, isLST),
|
|
47723
|
+
investmentSteps: getInvestmentSteps(collateralSymbol, underlyingSymbol),
|
|
47439
47724
|
isPreview,
|
|
47440
47725
|
apyMethodology: isLST ? "Current annualized APY in terms of base asset of the LST. There is no additional fee taken by Troves on LST APY. We charge a 10% performance fee on the additional gain which is already accounted in the APY shown." : "Current annualized APY in terms of base asset of the Yield Token. There is no additional fee taken by Troves on yield token APY. We charge a 10% performance fee on the additional gain which is already accounted in the APY shown.",
|
|
47441
47726
|
security: {
|