@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.js
CHANGED
|
@@ -6215,7 +6215,7 @@ var EkuboQuoter = class _EkuboQuoter {
|
|
|
6215
6215
|
async _callQuoterApi(fromToken, toToken, amount, retry = 0) {
|
|
6216
6216
|
try {
|
|
6217
6217
|
const url = this.ENDPOINT.replace("{{AMOUNT}}", amount.toWei()).replace("{{TOKEN_FROM_ADDRESS}}", fromToken).replace("{{TOKEN_TO_ADDRESS}}", toToken);
|
|
6218
|
-
logger.
|
|
6218
|
+
logger.info(`EkuboQuoter::_callQuoterApi url: ${url}`);
|
|
6219
6219
|
const quote = await import_axios6.default.get(url);
|
|
6220
6220
|
return quote.data;
|
|
6221
6221
|
} catch (error) {
|
|
@@ -8085,6 +8085,7 @@ var vesu_rebalance_abi_default = [
|
|
|
8085
8085
|
var CacheClass = class {
|
|
8086
8086
|
constructor() {
|
|
8087
8087
|
this.cache = /* @__PURE__ */ new Map();
|
|
8088
|
+
this.isCacheEnabled = true;
|
|
8088
8089
|
}
|
|
8089
8090
|
setCache(key, data, ttl = 6e4) {
|
|
8090
8091
|
const timestamp = Date.now();
|
|
@@ -8092,7 +8093,7 @@ var CacheClass = class {
|
|
|
8092
8093
|
}
|
|
8093
8094
|
getCache(key) {
|
|
8094
8095
|
const cachedData = this.cache.get(key);
|
|
8095
|
-
if (!cachedData || !this.isCacheValid(key)) {
|
|
8096
|
+
if (!cachedData || !this.isCacheValid(key) || !this.isCacheEnabled) {
|
|
8096
8097
|
return null;
|
|
8097
8098
|
}
|
|
8098
8099
|
return cachedData.data;
|
|
@@ -8103,6 +8104,12 @@ var CacheClass = class {
|
|
|
8103
8104
|
const { timestamp, ttl } = cachedData;
|
|
8104
8105
|
return Date.now() - timestamp <= ttl;
|
|
8105
8106
|
}
|
|
8107
|
+
disableCache() {
|
|
8108
|
+
this.isCacheEnabled = false;
|
|
8109
|
+
}
|
|
8110
|
+
enableCache() {
|
|
8111
|
+
this.isCacheEnabled = true;
|
|
8112
|
+
}
|
|
8106
8113
|
};
|
|
8107
8114
|
|
|
8108
8115
|
// src/strategies/base-strategy.ts
|
|
@@ -28889,7 +28896,7 @@ var VesuAdapter = class _VesuAdapter extends CacheClass {
|
|
|
28889
28896
|
throw new Error("LTV is 0");
|
|
28890
28897
|
}
|
|
28891
28898
|
this.setCache(CACHE_KEY, ltv, 3e5);
|
|
28892
|
-
return
|
|
28899
|
+
return ltv;
|
|
28893
28900
|
}
|
|
28894
28901
|
async getPositions(config, blockNumber = "latest") {
|
|
28895
28902
|
if (!this.pricer) {
|
|
@@ -28973,6 +28980,9 @@ var VesuAdapter = class _VesuAdapter extends CacheClass {
|
|
|
28973
28980
|
debtPrice = (await pricer.getPrice(this.config.debt.priceProxySymbol || this.config.debt.symbol)).price;
|
|
28974
28981
|
}
|
|
28975
28982
|
logger.verbose(`VesuAdapter::getAssetPrices collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
|
|
28983
|
+
if (isNaN(collateralPrice) || isNaN(debtPrice) || collateralPrice == 0 || debtPrice == 0) {
|
|
28984
|
+
throw new Error(`VesuAdapter::getAssetPrices collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
|
|
28985
|
+
}
|
|
28976
28986
|
return {
|
|
28977
28987
|
collateralTokenAmount,
|
|
28978
28988
|
collateralUSDAmount,
|
|
@@ -34611,20 +34621,23 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
34611
34621
|
}
|
|
34612
34622
|
_buildZeroAmountSwapsWithWeights(quote, token, reverseRoutes = false) {
|
|
34613
34623
|
const swaps = quote.splits.map((split) => {
|
|
34614
|
-
|
|
34615
|
-
|
|
34616
|
-
|
|
34617
|
-
|
|
34618
|
-
|
|
34619
|
-
|
|
34620
|
-
|
|
34621
|
-
|
|
34622
|
-
|
|
34623
|
-
|
|
34624
|
-
|
|
34625
|
-
|
|
34626
|
-
|
|
34627
|
-
|
|
34624
|
+
let sellToken = token.address;
|
|
34625
|
+
const routeNodes = split.route.map((_route) => {
|
|
34626
|
+
let isSellToken0 = sellToken.eqString(_route.pool_key.token0);
|
|
34627
|
+
isSellToken0 = reverseRoutes ? !isSellToken0 : isSellToken0;
|
|
34628
|
+
sellToken = isSellToken0 ? ContractAddr.from(_route.pool_key.token1) : ContractAddr.from(_route.pool_key.token0);
|
|
34629
|
+
return {
|
|
34630
|
+
pool_key: {
|
|
34631
|
+
token0: ContractAddr.from(_route.pool_key.token0),
|
|
34632
|
+
token1: ContractAddr.from(_route.pool_key.token1),
|
|
34633
|
+
fee: _route.pool_key.fee,
|
|
34634
|
+
tick_spacing: _route.pool_key.tick_spacing.toString(),
|
|
34635
|
+
extension: _route.pool_key.extension
|
|
34636
|
+
},
|
|
34637
|
+
sqrt_ratio_limit: isSellToken0 ? Web3Number.fromWei(MIN_SQRT_RATIO_LIMIT.toString(), 18) : Web3Number.fromWei(MAX_SQRT_RATIO_LIMIT.toString(), 18),
|
|
34638
|
+
skip_ahead: Web3Number.fromWei(_route.skip_ahead, 0)
|
|
34639
|
+
};
|
|
34640
|
+
});
|
|
34628
34641
|
return {
|
|
34629
34642
|
route: routeNodes,
|
|
34630
34643
|
token_amount: {
|
|
@@ -34946,6 +34959,7 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
34946
34959
|
collateralToken.address.address,
|
|
34947
34960
|
requiredAmount
|
|
34948
34961
|
);
|
|
34962
|
+
logger.info(`VesuMultiplyAdapter::_getIncreaseCalldata requiredAmount: ${requiredAmount}`);
|
|
34949
34963
|
if (marginSwapQuote.price_impact > 0.01) {
|
|
34950
34964
|
throw new Error(
|
|
34951
34965
|
`VesuMultiplyAdapter: Margin swap price impact too high (${marginSwapQuote.price_impact})`
|
|
@@ -34972,7 +34986,7 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
34972
34986
|
legLTV,
|
|
34973
34987
|
dexPrice
|
|
34974
34988
|
);
|
|
34975
|
-
logger.
|
|
34989
|
+
logger.info(
|
|
34976
34990
|
`${_VesuMultiplyAdapter.name}::_getIncreaseCalldata debtAmount: ${debtAmount}, addedCollateral: ${addedCollateral}`
|
|
34977
34991
|
);
|
|
34978
34992
|
let leverSwap = [];
|
|
@@ -34990,8 +35004,10 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
34990
35004
|
collateralToken.address.address,
|
|
34991
35005
|
params.leverSwap?.exactOutput?.abs()
|
|
34992
35006
|
);
|
|
35007
|
+
logger.info(`VesuMultiplyAdapter::_getIncreaseCalldata amount: ${params.leverSwap?.exactOutput?.abs()}`);
|
|
34993
35008
|
debtAmount = Web3Number.fromWei(swapQuote.total_calculated, debtToken.decimals).abs();
|
|
34994
35009
|
}
|
|
35010
|
+
logger.info(`VesuMultiplyAdapter::_getIncreaseCalldata debtAmount: ${debtAmount}`);
|
|
34995
35011
|
swapQuote = await ekuboQuoter.getQuoteExactInput(
|
|
34996
35012
|
debtToken.address.address,
|
|
34997
35013
|
collateralToken.address.address,
|
|
@@ -35018,7 +35034,7 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
35018
35034
|
await new Promise((resolve) => setTimeout(resolve, 1e4));
|
|
35019
35035
|
} else {
|
|
35020
35036
|
throw new Error(
|
|
35021
|
-
`VesuMultiplyAdapter: Price impact too high (${swapQuote.price_impact}), skipping swap`
|
|
35037
|
+
`VesuMultiplyAdapter: Price impact too high (${swapQuote.price_impact}), skipping swap, debtAmount=${debtAmount.toNumber()}, collateralPrice=${collateralPrice}, debtPrice=${debtPrice}`
|
|
35022
35038
|
);
|
|
35023
35039
|
}
|
|
35024
35040
|
} catch (error) {
|
|
@@ -36003,6 +36019,9 @@ var VesuModifyPositionAdapter = class _VesuModifyPositionAdapter extends BaseAda
|
|
|
36003
36019
|
// src/strategies/universal-adapters/extended-adapter.ts
|
|
36004
36020
|
var import_starknet27 = require("starknet");
|
|
36005
36021
|
|
|
36022
|
+
// src/modules/ExtendedWrapperSDk/wrapper.ts
|
|
36023
|
+
var import_bignumber6 = __toESM(require("bignumber.js"));
|
|
36024
|
+
|
|
36006
36025
|
// src/modules/ExtendedWrapperSDk/types.ts
|
|
36007
36026
|
var OrderSide = /* @__PURE__ */ ((OrderSide2) => {
|
|
36008
36027
|
OrderSide2["BUY"] = "BUY";
|
|
@@ -36087,8 +36106,60 @@ var AssetOperationStatus = /* @__PURE__ */ ((AssetOperationStatus3) => {
|
|
|
36087
36106
|
})(AssetOperationStatus || {});
|
|
36088
36107
|
|
|
36089
36108
|
// src/modules/ExtendedWrapperSDk/wrapper.ts
|
|
36109
|
+
function asRecord(v) {
|
|
36110
|
+
return v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
36111
|
+
}
|
|
36112
|
+
function pickTradingConfig(row) {
|
|
36113
|
+
const tc = row.trading_config ?? row.tradingConfig;
|
|
36114
|
+
return asRecord(tc);
|
|
36115
|
+
}
|
|
36116
|
+
function readTcString(tc, snake, camel) {
|
|
36117
|
+
const v = tc[snake] ?? tc[camel];
|
|
36118
|
+
if (v === void 0 || v === null) return void 0;
|
|
36119
|
+
return String(v).trim();
|
|
36120
|
+
}
|
|
36121
|
+
function tradingRulesFromMarketRow(marketName, row) {
|
|
36122
|
+
const r = asRecord(row);
|
|
36123
|
+
if (!r) {
|
|
36124
|
+
throw new Error(`ExtendedWrapper: invalid market payload for ${marketName}`);
|
|
36125
|
+
}
|
|
36126
|
+
const tc = pickTradingConfig(r);
|
|
36127
|
+
if (!tc) {
|
|
36128
|
+
throw new Error(`ExtendedWrapper: missing tradingConfig for market ${marketName}`);
|
|
36129
|
+
}
|
|
36130
|
+
const minS = readTcString(tc, "min_order_size", "minOrderSize");
|
|
36131
|
+
const qtyStep = readTcString(tc, "min_order_size_change", "minOrderSizeChange");
|
|
36132
|
+
const pxStep = readTcString(tc, "min_price_change", "minPriceChange");
|
|
36133
|
+
if (!minS || !qtyStep || !pxStep) {
|
|
36134
|
+
throw new Error(`ExtendedWrapper: incomplete tradingConfig for market ${marketName}`);
|
|
36135
|
+
}
|
|
36136
|
+
const minOrderSize = new import_bignumber6.default(minS);
|
|
36137
|
+
const qty = new import_bignumber6.default(qtyStep);
|
|
36138
|
+
const px = new import_bignumber6.default(pxStep);
|
|
36139
|
+
if (!minOrderSize.isFinite() || minOrderSize.lte(0)) {
|
|
36140
|
+
throw new Error(`ExtendedWrapper: invalid minOrderSize for ${marketName}`);
|
|
36141
|
+
}
|
|
36142
|
+
if (!qty.isFinite() || qty.lte(0)) {
|
|
36143
|
+
throw new Error(`ExtendedWrapper: invalid minOrderSizeChange for ${marketName}`);
|
|
36144
|
+
}
|
|
36145
|
+
if (!px.isFinite() || px.lte(0)) {
|
|
36146
|
+
throw new Error(`ExtendedWrapper: invalid minPriceChange for ${marketName}`);
|
|
36147
|
+
}
|
|
36148
|
+
return { minOrderSize, qtyStep: qty, priceStep: px };
|
|
36149
|
+
}
|
|
36150
|
+
function roundToStepBn(value, step) {
|
|
36151
|
+
if (step.lte(0)) return value;
|
|
36152
|
+
return value.div(step).round(0, import_bignumber6.default.ROUND_HALF_UP).times(step);
|
|
36153
|
+
}
|
|
36154
|
+
function formatBnForApi(bn, step) {
|
|
36155
|
+
const dp = Math.max(step.decimalPlaces() ?? 0, bn.decimalPlaces() ?? 0, 0);
|
|
36156
|
+
return Number(bn.toFixed(Math.min(80, dp))).toString();
|
|
36157
|
+
}
|
|
36090
36158
|
var ExtendedWrapper = class {
|
|
36091
36159
|
constructor(config) {
|
|
36160
|
+
/** Per-market rules from GET /markets (tradingConfig); retained for process lifetime (no TTL). */
|
|
36161
|
+
this.marketTradingRulesCache = /* @__PURE__ */ new Map();
|
|
36162
|
+
this.marketTradingRulesInflight = /* @__PURE__ */ new Map();
|
|
36092
36163
|
this.apiKey = config.apiKey;
|
|
36093
36164
|
this.timeout = config.timeout || 3e4;
|
|
36094
36165
|
this.retries = config.retries || 3;
|
|
@@ -36152,13 +36223,75 @@ var ExtendedWrapper = class {
|
|
|
36152
36223
|
}
|
|
36153
36224
|
throw lastError || new Error("Request failed after all retries");
|
|
36154
36225
|
}
|
|
36226
|
+
async resolveTradingRules(marketName) {
|
|
36227
|
+
const cached = this.marketTradingRulesCache.get(marketName);
|
|
36228
|
+
if (cached) return cached;
|
|
36229
|
+
const existing = this.marketTradingRulesInflight.get(marketName);
|
|
36230
|
+
if (existing) return existing;
|
|
36231
|
+
const inflight = (async () => {
|
|
36232
|
+
const res = await this.getMarkets(marketName);
|
|
36233
|
+
if (res.status !== "OK") {
|
|
36234
|
+
throw new Error(
|
|
36235
|
+
`ExtendedWrapper: getMarkets failed for ${marketName}: ${res.message}`
|
|
36236
|
+
);
|
|
36237
|
+
}
|
|
36238
|
+
const rows = res.data;
|
|
36239
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
36240
|
+
throw new Error(
|
|
36241
|
+
`ExtendedWrapper: empty markets response for ${marketName}`
|
|
36242
|
+
);
|
|
36243
|
+
}
|
|
36244
|
+
const row = rows.find((m) => asRecord(m)?.name === marketName);
|
|
36245
|
+
if (!row) {
|
|
36246
|
+
throw new Error(
|
|
36247
|
+
`ExtendedWrapper: market ${marketName} not found in markets list`
|
|
36248
|
+
);
|
|
36249
|
+
}
|
|
36250
|
+
const rules = tradingRulesFromMarketRow(marketName, row);
|
|
36251
|
+
this.marketTradingRulesCache.set(marketName, rules);
|
|
36252
|
+
return rules;
|
|
36253
|
+
})();
|
|
36254
|
+
this.marketTradingRulesInflight.set(marketName, inflight);
|
|
36255
|
+
void inflight.finally(() => {
|
|
36256
|
+
if (this.marketTradingRulesInflight.get(marketName) === inflight) {
|
|
36257
|
+
this.marketTradingRulesInflight.delete(marketName);
|
|
36258
|
+
}
|
|
36259
|
+
});
|
|
36260
|
+
return inflight;
|
|
36261
|
+
}
|
|
36155
36262
|
/**
|
|
36156
36263
|
* Create a new order on Extended Exchange
|
|
36157
36264
|
*/
|
|
36158
36265
|
async createOrder(request) {
|
|
36266
|
+
const rules = await this.resolveTradingRules(request.market_name);
|
|
36267
|
+
const amountBn = new import_bignumber6.default(String(request.amount).trim());
|
|
36268
|
+
const priceBn = new import_bignumber6.default(String(request.price).trim());
|
|
36269
|
+
if (!amountBn.isFinite() || amountBn.lte(0)) {
|
|
36270
|
+
throw new Error(`ExtendedWrapper: invalid order amount=${request.amount}`);
|
|
36271
|
+
}
|
|
36272
|
+
if (!priceBn.isFinite() || priceBn.lte(0)) {
|
|
36273
|
+
throw new Error(`ExtendedWrapper: invalid order price=${request.price}`);
|
|
36274
|
+
}
|
|
36275
|
+
if (amountBn.lt(rules.minOrderSize)) {
|
|
36276
|
+
throw new Error(
|
|
36277
|
+
`ExtendedWrapper: order amount ${request.amount} is below minOrderSize ${rules.minOrderSize.toFixed()} for ${request.market_name}`
|
|
36278
|
+
);
|
|
36279
|
+
}
|
|
36280
|
+
const adjAmount = roundToStepBn(amountBn, rules.qtyStep);
|
|
36281
|
+
const adjPrice = roundToStepBn(priceBn, rules.priceStep);
|
|
36282
|
+
if (adjAmount.lt(rules.minOrderSize)) {
|
|
36283
|
+
throw new Error(
|
|
36284
|
+
`ExtendedWrapper: amount after tick rounding ${formatBnForApi(adjAmount, rules.qtyStep)} is below minOrderSize ${rules.minOrderSize.toFixed()} (${request.market_name})`
|
|
36285
|
+
);
|
|
36286
|
+
}
|
|
36287
|
+
const payload = {
|
|
36288
|
+
...request,
|
|
36289
|
+
amount: formatBnForApi(adjAmount, rules.qtyStep),
|
|
36290
|
+
price: formatBnForApi(adjPrice, rules.priceStep)
|
|
36291
|
+
};
|
|
36159
36292
|
return this.makeRequest("/api/v1/orders", false, {
|
|
36160
36293
|
method: "POST",
|
|
36161
|
-
body: JSON.stringify(
|
|
36294
|
+
body: JSON.stringify(payload)
|
|
36162
36295
|
});
|
|
36163
36296
|
}
|
|
36164
36297
|
/**
|
|
@@ -36768,14 +36901,6 @@ var ExtendedAdapter = class _ExtendedAdapter extends BaseAdapter {
|
|
|
36768
36901
|
logger.error("error initializing client");
|
|
36769
36902
|
return null;
|
|
36770
36903
|
}
|
|
36771
|
-
const setLeverage = await this.setLeverage(
|
|
36772
|
-
leverage,
|
|
36773
|
-
this.config.extendedMarketName
|
|
36774
|
-
);
|
|
36775
|
-
if (!setLeverage) {
|
|
36776
|
-
logger.error("error depositing or setting leverage");
|
|
36777
|
-
return null;
|
|
36778
|
-
}
|
|
36779
36904
|
const { ask, bid } = await this.fetchOrderBookFromExtended();
|
|
36780
36905
|
if (!ask || !bid || ask.lessThanOrEqualTo(0) || bid.lessThanOrEqualTo(0)) {
|
|
36781
36906
|
logger.error(
|
|
@@ -36915,13 +37040,13 @@ var ExtendedAdapter = class _ExtendedAdapter extends BaseAdapter {
|
|
|
36915
37040
|
async createExtendedPositon(client, marketName, amount, price, side) {
|
|
36916
37041
|
try {
|
|
36917
37042
|
const result = side === "SELL" /* SELL */ ? await client.createSellOrder(marketName, amount, price, {
|
|
36918
|
-
|
|
36919
|
-
|
|
36920
|
-
|
|
37043
|
+
post_only: false,
|
|
37044
|
+
reduce_only: false,
|
|
37045
|
+
time_in_force: "IOC" /* IOC */
|
|
36921
37046
|
}) : await client.createBuyOrder(marketName, amount, price, {
|
|
36922
|
-
|
|
36923
|
-
|
|
36924
|
-
|
|
37047
|
+
post_only: false,
|
|
37048
|
+
reduce_only: true,
|
|
37049
|
+
time_in_force: "IOC" /* IOC */
|
|
36925
37050
|
});
|
|
36926
37051
|
if (result.data.id) {
|
|
36927
37052
|
const position_id = result.data.id.toString();
|
|
@@ -42386,20 +42511,42 @@ function mergeDeltas(a, b) {
|
|
|
42386
42511
|
dTransferVesuToExt: a.dTransferVesuToExt + b.dTransferVesuToExt
|
|
42387
42512
|
};
|
|
42388
42513
|
}
|
|
42389
|
-
function
|
|
42514
|
+
function routableDrawAmount(avail, need, minRoutable) {
|
|
42515
|
+
if (avail <= 0 || need <= 0) return 0;
|
|
42516
|
+
const raw = Math.min(avail, need);
|
|
42517
|
+
if (raw <= 0) return 0;
|
|
42518
|
+
if (minRoutable <= 0) return raw;
|
|
42519
|
+
return raw > minRoutable ? raw : 0;
|
|
42520
|
+
}
|
|
42521
|
+
function drawFunds(need, keys, pool, minRoutable) {
|
|
42390
42522
|
const draws = {};
|
|
42391
42523
|
let unmet = need;
|
|
42392
42524
|
for (const k of keys) {
|
|
42393
42525
|
if (unmet <= 0) break;
|
|
42394
42526
|
const avail = pool[k];
|
|
42395
|
-
|
|
42396
|
-
|
|
42527
|
+
const take = routableDrawAmount(avail, unmet, minRoutable);
|
|
42528
|
+
if (take <= 0) continue;
|
|
42397
42529
|
draws[k] = take;
|
|
42398
42530
|
pool[k] -= take;
|
|
42399
42531
|
unmet -= take;
|
|
42400
42532
|
}
|
|
42401
42533
|
return { draws, filled: need - unmet, unmet };
|
|
42402
42534
|
}
|
|
42535
|
+
function drawExtendedAggregated(need, pool, minRoutable) {
|
|
42536
|
+
const draws = {};
|
|
42537
|
+
if (need <= 0) return { draws, filled: 0, unmet: 0 };
|
|
42538
|
+
const totalCap = Math.max(0, pool.extAvlWithdraw) + Math.max(0, pool.extUpnl);
|
|
42539
|
+
const want = routableDrawAmount(totalCap, need, minRoutable);
|
|
42540
|
+
if (want <= 0) return { draws, filled: 0, unmet: need };
|
|
42541
|
+
const fromAvl = Math.min(Math.max(0, pool.extAvlWithdraw), want);
|
|
42542
|
+
pool.extAvlWithdraw -= fromAvl;
|
|
42543
|
+
const fromUpnl = Math.min(Math.max(0, pool.extUpnl), want - fromAvl);
|
|
42544
|
+
pool.extUpnl -= fromUpnl;
|
|
42545
|
+
if (fromAvl > 0) draws.extAvlWithdraw = fromAvl;
|
|
42546
|
+
if (fromUpnl > 0) draws.extUpnl = fromUpnl;
|
|
42547
|
+
const filled = fromAvl + fromUpnl;
|
|
42548
|
+
return { draws, filled, unmet: need - filled };
|
|
42549
|
+
}
|
|
42403
42550
|
function sumKeys(draws, keys) {
|
|
42404
42551
|
return keys.reduce((s, k) => s + (draws[k] ?? 0), 0);
|
|
42405
42552
|
}
|
|
@@ -42410,33 +42557,39 @@ function applyDrawsToDeltas(d, draws, sign) {
|
|
|
42410
42557
|
d.dExtAvlWithdraw += sign * (draws.extAvlWithdraw ?? 0);
|
|
42411
42558
|
d.dExtUpnl += sign * (draws.extUpnl ?? 0);
|
|
42412
42559
|
}
|
|
42413
|
-
function fixExtMargin(ext, price, pool) {
|
|
42560
|
+
function fixExtMargin(ext, price, pool, config) {
|
|
42414
42561
|
const d = emptyDeltas();
|
|
42415
42562
|
const deficit = computeExtDeficit(ext, price);
|
|
42416
42563
|
if (deficit <= 0) return { d, unmet: 0 };
|
|
42417
|
-
const
|
|
42564
|
+
const minR = config.minRoutableUsd ?? 0;
|
|
42565
|
+
const { draws, filled, unmet } = drawFunds(deficit, ["walletUsd", "vaUsd", "vesuBorrowCapacity"], pool, minR);
|
|
42418
42566
|
applyDrawsToDeltas(d, draws, -1);
|
|
42419
42567
|
d.dExtAvlWithdraw += filled;
|
|
42420
42568
|
const fromVesu = draws.vesuBorrowCapacity ?? 0;
|
|
42421
42569
|
if (fromVesu > 0) d.dTransferVesuToExt += fromVesu;
|
|
42422
42570
|
return { d, unmet };
|
|
42423
42571
|
}
|
|
42424
|
-
function fixVesuMargin(vesu, price, pool, hfBuffer) {
|
|
42572
|
+
function fixVesuMargin(vesu, price, pool, hfBuffer, config) {
|
|
42425
42573
|
const d = emptyDeltas();
|
|
42426
42574
|
const hf = computeVesuHF(vesu, price);
|
|
42427
42575
|
if (hf >= vesu.targetHF - hfBuffer) return { d, unmet: 0 };
|
|
42428
42576
|
const repayTokens = computeVesuDebtRepay(vesu, price);
|
|
42429
42577
|
const repayUsd = repayTokens * vesu.debtPrice;
|
|
42430
|
-
const
|
|
42431
|
-
|
|
42578
|
+
const minR = config.minRoutableUsd ?? 0;
|
|
42579
|
+
const rMain = drawFunds(repayUsd, ["vaUsd", "walletUsd"], pool, minR);
|
|
42580
|
+
applyDrawsToDeltas(d, rMain.draws, -1);
|
|
42581
|
+
const rExt = drawExtendedAggregated(rMain.unmet, pool, minR);
|
|
42582
|
+
applyDrawsToDeltas(d, rExt.draws, -1);
|
|
42583
|
+
const filled = rMain.filled + rExt.filled;
|
|
42584
|
+
const unmet = rExt.unmet;
|
|
42432
42585
|
d.dVesuDebt -= filled / vesu.debtPrice;
|
|
42433
|
-
const fromExt = sumKeys(draws, ["extAvlWithdraw", "extUpnl"]);
|
|
42586
|
+
const fromExt = sumKeys(rExt.draws, ["extAvlWithdraw", "extUpnl"]);
|
|
42434
42587
|
if (fromExt > 0) d.dTransferVesuToExt -= fromExt;
|
|
42435
42588
|
return { d, unmet };
|
|
42436
42589
|
}
|
|
42437
42590
|
function phase1(ext, vesu, price, pool, config) {
|
|
42438
|
-
const extResult = fixExtMargin(ext, price, pool);
|
|
42439
|
-
const vesuResult = fixVesuMargin(vesu, price, pool, config.hfBuffer);
|
|
42591
|
+
const extResult = fixExtMargin(ext, price, pool, config);
|
|
42592
|
+
const vesuResult = fixVesuMargin(vesu, price, pool, config.hfBuffer, config);
|
|
42440
42593
|
return {
|
|
42441
42594
|
deltas: mergeDeltas(extResult.d, vesuResult.d),
|
|
42442
42595
|
extDeficitRemaining: extResult.unmet,
|
|
@@ -42476,26 +42629,31 @@ function roundFinalPosition(extPos, vesuPos, rawF, precision) {
|
|
|
42476
42629
|
}
|
|
42477
42630
|
return Math.max(0, Math.min(fFromExt, fFromVesu));
|
|
42478
42631
|
}
|
|
42479
|
-
function phase2(extPos, vesuPos, extEquity, vesuDebt, vesu, extLev, price, pool,
|
|
42632
|
+
function phase2(extPos, vesuPos, extEquity, vesuDebt, vesu, extLev, price, pool, config) {
|
|
42480
42633
|
const d = emptyDeltas();
|
|
42634
|
+
const precision = config.positionPrecision;
|
|
42635
|
+
const minR = config.minRoutableUsd ?? 0;
|
|
42481
42636
|
const targetLTV = computeVesuTargetLTV(vesu);
|
|
42482
42637
|
const imbalance = extPos - vesuPos;
|
|
42483
42638
|
let fundedGrowthBtc = 0;
|
|
42484
42639
|
if (!isNegligible(imbalance, precision)) {
|
|
42485
42640
|
if (imbalance > 0) {
|
|
42486
42641
|
const equityCostUsd = computeVesuGrowthCost(imbalance, vesu, price);
|
|
42487
|
-
const
|
|
42488
|
-
applyDrawsToDeltas(d, draws, -1);
|
|
42642
|
+
const rMain = drawFunds(equityCostUsd, ["vaUsd", "walletUsd"], pool, minR);
|
|
42643
|
+
applyDrawsToDeltas(d, rMain.draws, -1);
|
|
42644
|
+
const rExt = drawExtendedAggregated(rMain.unmet, pool, minR);
|
|
42645
|
+
applyDrawsToDeltas(d, rExt.draws, -1);
|
|
42646
|
+
const filled = rMain.filled + rExt.filled;
|
|
42489
42647
|
const grownBtc = computeVesuGrowthFromEquity(filled, vesu, price);
|
|
42490
42648
|
d.dVesuPosition += grownBtc;
|
|
42491
42649
|
fundedGrowthBtc = grownBtc;
|
|
42492
42650
|
d.dVesuDebt += computeVesuGrowthDebt(grownBtc, vesu, price);
|
|
42493
|
-
const fromExt = sumKeys(draws, ["extAvlWithdraw", "extUpnl"]);
|
|
42651
|
+
const fromExt = sumKeys(rExt.draws, ["extAvlWithdraw", "extUpnl"]);
|
|
42494
42652
|
if (fromExt > 0) d.dTransferVesuToExt -= fromExt;
|
|
42495
42653
|
} else {
|
|
42496
42654
|
const absImbalance = -imbalance;
|
|
42497
42655
|
const marginCostUsd = absImbalance * price / extLev;
|
|
42498
|
-
const { draws, filled } = drawFunds(marginCostUsd, ["walletUsd", "vaUsd", "vesuBorrowCapacity"], pool);
|
|
42656
|
+
const { draws, filled } = drawFunds(marginCostUsd, ["walletUsd", "vaUsd", "vesuBorrowCapacity"], pool, minR);
|
|
42499
42657
|
applyDrawsToDeltas(d, draws, -1);
|
|
42500
42658
|
const grownBtc = filled * extLev / price;
|
|
42501
42659
|
d.dExtPosition += grownBtc;
|
|
@@ -42536,6 +42694,7 @@ function phase2(extPos, vesuPos, extEquity, vesuDebt, vesu, extLev, price, pool,
|
|
|
42536
42694
|
return d;
|
|
42537
42695
|
}
|
|
42538
42696
|
function rebalance(inputs) {
|
|
42697
|
+
logger.info(`ltv-imbalance-rebalance-math::rebalance inputs=${JSON.stringify(inputs)}`);
|
|
42539
42698
|
const { ext, vesu, btcPrice, config } = inputs;
|
|
42540
42699
|
const pool = { ...inputs.funding };
|
|
42541
42700
|
const p1 = phase1(ext, vesu, btcPrice, pool, config);
|
|
@@ -42552,7 +42711,7 @@ function rebalance(inputs) {
|
|
|
42552
42711
|
ext.leverage,
|
|
42553
42712
|
btcPrice,
|
|
42554
42713
|
pool,
|
|
42555
|
-
config
|
|
42714
|
+
config
|
|
42556
42715
|
);
|
|
42557
42716
|
return mergeDeltas(p1.deltas, p2);
|
|
42558
42717
|
}
|
|
@@ -43133,15 +43292,15 @@ var SolveBudget = class {
|
|
|
43133
43292
|
return this.vesuPoolStates[0];
|
|
43134
43293
|
}
|
|
43135
43294
|
// ── Derived getters (buffered where applicable) ─────────────────────
|
|
43136
|
-
/** Buffered VA USD:
|
|
43295
|
+
/** Buffered VA USD: non-stable asset slot (if any) + USDC slot. */
|
|
43137
43296
|
get vaUsd() {
|
|
43138
|
-
return this.bufferedTokenUsd(this.
|
|
43297
|
+
return this.bufferedTokenUsd(this.vaultUsdcBalance);
|
|
43139
43298
|
}
|
|
43140
43299
|
/** Buffered USD in VA strategy-asset bucket only. */
|
|
43141
43300
|
get vaAssetUsd() {
|
|
43142
43301
|
return this.bufferedTokenUsd(this.vaultAssetBalance);
|
|
43143
43302
|
}
|
|
43144
|
-
/** Buffered USD in VA USDC bucket (
|
|
43303
|
+
/** Buffered USD in VA USDC bucket (includes full VA idle when asset === USDC). */
|
|
43145
43304
|
get vaUsdcUsd() {
|
|
43146
43305
|
return this.bufferedTokenUsd(this.vaultUsdcBalance);
|
|
43147
43306
|
}
|
|
@@ -43223,9 +43382,13 @@ var SolveBudget = class {
|
|
|
43223
43382
|
get vesuRebalanceFlags() {
|
|
43224
43383
|
return this.shouldVesuRebalance;
|
|
43225
43384
|
}
|
|
43226
|
-
/** Raw USD in VA (USDC slot + asset slot); spend caps when executing transfers. */
|
|
43385
|
+
/** Raw USD in VA (USDC slot + non-stable asset slot when distinct); spend caps when executing transfers. */
|
|
43227
43386
|
_vaRawUsd() {
|
|
43228
|
-
return this._rawTokenUsd(this.vaultUsdcBalance)
|
|
43387
|
+
return this._rawTokenUsd(this.vaultUsdcBalance);
|
|
43388
|
+
}
|
|
43389
|
+
/** VA liquidity usable for repay / {@link spendVaRawUsd} (matches nominal balances after any {@link applyBuffer} scaling). */
|
|
43390
|
+
get vaRawUsd() {
|
|
43391
|
+
return this._vaRawUsd();
|
|
43229
43392
|
}
|
|
43230
43393
|
_walletRawUsd() {
|
|
43231
43394
|
return this._rawTokenUsd(this.walletBalance);
|
|
@@ -43301,7 +43464,7 @@ var SolveBudget = class {
|
|
|
43301
43464
|
rem -= fromUsdc;
|
|
43302
43465
|
}
|
|
43303
43466
|
if (rem > 0 && this.vaultAssetBalance) {
|
|
43304
|
-
|
|
43467
|
+
throw new Error(`Not implemented: spendVA with vaultAssetBalance`);
|
|
43305
43468
|
}
|
|
43306
43469
|
this._recomputeUnusedBalance();
|
|
43307
43470
|
logger.debug(`SolveBudget::spendVA usedRaw=${usedRaw}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
|
|
@@ -43311,7 +43474,10 @@ var SolveBudget = class {
|
|
|
43311
43474
|
* Spend nominal/raw USD from VA (e.g. Vesu repay, on-chain USDC). Does not apply the safety buffer to the cap.
|
|
43312
43475
|
*/
|
|
43313
43476
|
spendVaRawUsd(rawUsdDesired) {
|
|
43314
|
-
const capRaw =
|
|
43477
|
+
const capRaw = (
|
|
43478
|
+
// this._rawTokenUsd(this.vaultUsdcBalance) + this._rawTokenUsd(this.vaultAssetBalance);
|
|
43479
|
+
this._rawTokenUsd(this.vaultUsdcBalance)
|
|
43480
|
+
);
|
|
43315
43481
|
const usedRaw = Math.min(capRaw, Math.max(0, rawUsdDesired));
|
|
43316
43482
|
if (usedRaw <= CASE_THRESHOLD_USD) return 0;
|
|
43317
43483
|
let rem = usedRaw;
|
|
@@ -43321,7 +43487,7 @@ var SolveBudget = class {
|
|
|
43321
43487
|
rem -= fromUsdc;
|
|
43322
43488
|
}
|
|
43323
43489
|
if (rem > 0 && this.vaultAssetBalance) {
|
|
43324
|
-
|
|
43490
|
+
throw new Error(`Not implemented: spendVaRawUsd with vaultAssetBalance`);
|
|
43325
43491
|
}
|
|
43326
43492
|
this._recomputeUnusedBalance();
|
|
43327
43493
|
logger.debug(`SolveBudget::spendVaRawUsd usedRaw=${usedRaw}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
|
|
@@ -43336,7 +43502,7 @@ var SolveBudget = class {
|
|
|
43336
43502
|
if (this.vaultUsdcBalance) {
|
|
43337
43503
|
this._addUsdToTokenBalance(this.vaultUsdcBalance, rawUsd);
|
|
43338
43504
|
} else if (this.vaultAssetBalance) {
|
|
43339
|
-
|
|
43505
|
+
throw new Error(`Not implemented: addToVA with vaultAssetBalance`);
|
|
43340
43506
|
}
|
|
43341
43507
|
this._recomputeUnusedBalance();
|
|
43342
43508
|
logger.debug(`SolveBudget::addToVA rawUsd=${rawUsd}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
|
|
@@ -43375,7 +43541,7 @@ var SolveBudget = class {
|
|
|
43375
43541
|
if (isSpend) {
|
|
43376
43542
|
const capRaw = this.extendedBalance?.availableForWithdrawal?.toNumber() ?? 0;
|
|
43377
43543
|
const useRaw = Math.min(capRaw, desiredRaw);
|
|
43378
|
-
if (useRaw <=
|
|
43544
|
+
if (useRaw <= 0) return 0;
|
|
43379
43545
|
rawDelta = -useRaw;
|
|
43380
43546
|
} else {
|
|
43381
43547
|
rawDelta = desiredRaw;
|
|
@@ -43395,7 +43561,7 @@ var SolveBudget = class {
|
|
|
43395
43561
|
if (isSpend) {
|
|
43396
43562
|
const capRaw = this.extendedBalance?.unrealisedPnl?.toNumber() ?? 0;
|
|
43397
43563
|
const useRaw = Math.min(capRaw, desiredRaw);
|
|
43398
|
-
if (useRaw <=
|
|
43564
|
+
if (useRaw <= 0) return 0;
|
|
43399
43565
|
rawDelta = -useRaw;
|
|
43400
43566
|
} else {
|
|
43401
43567
|
rawDelta = desiredRaw;
|
|
@@ -43410,10 +43576,12 @@ var SolveBudget = class {
|
|
|
43410
43576
|
return rawDelta;
|
|
43411
43577
|
}
|
|
43412
43578
|
spendExtAvailTrade(rawDesired) {
|
|
43413
|
-
const
|
|
43414
|
-
const
|
|
43415
|
-
|
|
43416
|
-
|
|
43579
|
+
const usedWd = this._updateExtAvailWithdraw(rawDesired, true);
|
|
43580
|
+
const tookWd = Math.abs(usedWd);
|
|
43581
|
+
const rem = rawDesired - tookWd;
|
|
43582
|
+
const usedUpnl = rem > 0 ? this._updateExtAvailUpnl(rem, true) : 0;
|
|
43583
|
+
logger.debug(`SolveBudget::updateExtAvailTrade rawSum=${usedWd + usedUpnl}, extAvailTrade=${this.extAvailTrade}, totalUnused=${this.totalUnused}`);
|
|
43584
|
+
return usedWd + usedUpnl;
|
|
43417
43585
|
}
|
|
43418
43586
|
// simply reduces available amounts, but maintains equity and balance.
|
|
43419
43587
|
spendExtAvailTradeToEquityOnly(rawDesired) {
|
|
@@ -43733,7 +43901,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43733
43901
|
this._fetchExtendedPositions()
|
|
43734
43902
|
]);
|
|
43735
43903
|
logger.verbose(
|
|
43736
|
-
`${this._tag}::_refresh VA asset ${vaultAssetBalance.token.symbol}=$${vaultAssetBalance.usdValue.toFixed(2)}
|
|
43904
|
+
`${this._tag}::_refresh ${vaultAssetBalance ? `VA asset ${vaultAssetBalance.token.symbol}=$${vaultAssetBalance.usdValue.toFixed(2)}, ` : ""}VA USDC=${vaultUsdcBalance.usdValue.toFixed(2)}, wallet=${walletBalance.usdValue}`
|
|
43737
43905
|
);
|
|
43738
43906
|
const unusedBalance = this._computeUnusedBalances(
|
|
43739
43907
|
vaultAssetBalance,
|
|
@@ -43758,23 +43926,28 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43758
43926
|
},
|
|
43759
43927
|
vesuPoolStates
|
|
43760
43928
|
});
|
|
43929
|
+
this._budget.logStateSummary();
|
|
43761
43930
|
const totalUnusedUsd = unusedBalance.reduce(
|
|
43762
43931
|
(acc, b) => acc + b.usdValue,
|
|
43763
43932
|
0
|
|
43764
43933
|
);
|
|
43765
43934
|
logger.info(
|
|
43766
|
-
`${this._tag}::_refresh completed \u2014 unusedBalances: ${unusedBalance.length} tokens, totalUnusedUsd: ${totalUnusedUsd.toFixed(2)}, extendedPositions: ${extendedPositions.length}, vesuPools: ${vesuPoolStates.length}
|
|
43935
|
+
`${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()}`
|
|
43767
43936
|
);
|
|
43768
43937
|
}
|
|
43769
43938
|
// todo add communication check with python server of extended. if not working, throw error in solve function.
|
|
43770
|
-
/** True when strategy asset and USDC share one token — VA
|
|
43939
|
+
/** True when strategy asset and USDC share one token — VA idle balance is tracked as USDC, not as asset. */
|
|
43771
43940
|
_vaultAssetAndUsdcAreSameToken() {
|
|
43772
43941
|
return this._config.assetToken.address.eq(this._config.usdcToken.address);
|
|
43773
43942
|
}
|
|
43774
43943
|
/**
|
|
43775
|
-
* Reads
|
|
43944
|
+
* Reads idle {@link StateManagerConfig.assetToken} in the vault allocator when it differs from USDC.
|
|
43945
|
+
* When asset and USDC are the same token, returns null (that balance is reported via {@link _fetchVaultAllocatorUsdcBalanceIfDistinct} only).
|
|
43776
43946
|
*/
|
|
43777
43947
|
async _fetchVaultAllocatorAssetBalance() {
|
|
43948
|
+
if (this._vaultAssetAndUsdcAreSameToken()) {
|
|
43949
|
+
return null;
|
|
43950
|
+
}
|
|
43778
43951
|
const { assetToken, vaultAllocator, networkConfig, pricer } = this._config;
|
|
43779
43952
|
const balance = await new ERC20(networkConfig).balanceOf(
|
|
43780
43953
|
assetToken.address,
|
|
@@ -43786,11 +43959,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43786
43959
|
return { token: assetToken, amount: balance, usdValue };
|
|
43787
43960
|
}
|
|
43788
43961
|
/**
|
|
43789
|
-
* Reads {@link StateManagerConfig.usdcToken} in the vault allocator
|
|
43790
|
-
* {@link StateManagerConfig.assetToken}. Otherwise returns null (treat VA USDC as 0; stablecoin is only under asset).
|
|
43962
|
+
* Reads {@link StateManagerConfig.usdcToken} idle in the vault allocator (always — distinct asset or USDC-as-asset).
|
|
43791
43963
|
*/
|
|
43792
43964
|
async _fetchVaultAllocatorUsdcBalanceIfDistinct() {
|
|
43793
|
-
if (this._vaultAssetAndUsdcAreSameToken()) return null;
|
|
43794
43965
|
const { usdcToken, vaultAllocator, networkConfig, pricer } = this._config;
|
|
43795
43966
|
const balance = await new ERC20(networkConfig).balanceOf(
|
|
43796
43967
|
usdcToken.address,
|
|
@@ -43804,7 +43975,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43804
43975
|
return { token: usdcToken, amount: balance, usdValue };
|
|
43805
43976
|
}
|
|
43806
43977
|
/**
|
|
43807
|
-
* Merges vault-allocator asset,
|
|
43978
|
+
* Merges vault-allocator asset (if any), vault-allocator USDC, and operator wallet
|
|
43808
43979
|
* balances into entries keyed by token address.
|
|
43809
43980
|
*/
|
|
43810
43981
|
_computeUnusedBalances(vaultAssetBalance, vaultUsdcBalance, walletBalance) {
|
|
@@ -43816,8 +43987,8 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43816
43987
|
usdValue: tb.usdValue
|
|
43817
43988
|
});
|
|
43818
43989
|
};
|
|
43819
|
-
put(vaultAssetBalance);
|
|
43820
|
-
|
|
43990
|
+
if (vaultAssetBalance) put(vaultAssetBalance);
|
|
43991
|
+
put(vaultUsdcBalance);
|
|
43821
43992
|
const key = walletBalance.token.address.toString();
|
|
43822
43993
|
const existing = balanceMap.get(key);
|
|
43823
43994
|
if (existing) {
|
|
@@ -44336,7 +44507,10 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44336
44507
|
// ── ExecutionRoute-building helpers ─────────────────────────────────────────────
|
|
44337
44508
|
// ── Atomic route builders ────────────────────────────────────────────
|
|
44338
44509
|
_buildVesuRepayRoutes(totalUsd, routes) {
|
|
44339
|
-
const
|
|
44510
|
+
const vaCap = this._budget.vaRawUsd;
|
|
44511
|
+
const repayUsd = Math.min(totalUsd, vaCap);
|
|
44512
|
+
if (repayUsd <= CASE_THRESHOLD_USD) return;
|
|
44513
|
+
const { used, spendsByPool } = this._budget.repayVesuBorrowCapacity(repayUsd);
|
|
44340
44514
|
for (const route of spendsByPool) {
|
|
44341
44515
|
routes.push({ type: "VESU_REPAY" /* VESU_REPAY */, ...route, priority: routes.length });
|
|
44342
44516
|
}
|
|
@@ -44430,7 +44604,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44430
44604
|
return { routes, remaining: tryAmount };
|
|
44431
44605
|
}
|
|
44432
44606
|
_getVAToEXTENDEDRoute(tryAmount, routes, shouldAddWaitRoute = true) {
|
|
44433
|
-
const usable = Math.min(tryAmount, this._budget.
|
|
44607
|
+
const usable = Math.min(tryAmount, this._budget.vaUsdcUsd);
|
|
44434
44608
|
if (usable > CASE_THRESHOLD_USD) {
|
|
44435
44609
|
const vaUsed = this._budget.spendVA(usable);
|
|
44436
44610
|
this._budget.addToExtAvailTrade(vaUsed);
|
|
@@ -44445,7 +44619,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44445
44619
|
}
|
|
44446
44620
|
_getExtendedToWalletRoute(tryAmount, routes, shouldAddWaitRoute = true) {
|
|
44447
44621
|
if (tryAmount <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
|
|
44448
|
-
const rawCap = this._budget.extAvailWithdraw + this._budget.extAvailUpnl;
|
|
44622
|
+
const rawCap = this._budget.extAvailWithdraw + Math.max(0, this._budget.extAvailUpnl);
|
|
44449
44623
|
const rawSpend = Math.min(tryAmount, rawCap);
|
|
44450
44624
|
if (rawSpend <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
|
|
44451
44625
|
const rawOut = this._budget.spendExtAvailTrade(rawSpend);
|
|
@@ -44635,7 +44809,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44635
44809
|
* Design: accumulate all ext-to-wallet moves, add transfer routes at the end (principle #3).
|
|
44636
44810
|
*/
|
|
44637
44811
|
/**
|
|
44638
|
-
* Unified LTV classifier
|
|
44812
|
+
* Unified LTV / exposure classifier: `rebalance()` drives both LTV (debt, margin,
|
|
44813
|
+
* funding) and Vesu↔Extended position alignment. There is no separate imbalance pass.
|
|
44814
|
+
* Computes both Vesu repay and Extended margin needs,
|
|
44639
44815
|
* then builds all routes in a single pass with no duplicate transfers.
|
|
44640
44816
|
*
|
|
44641
44817
|
* Vesu repay priority: VA > Wallet > ExtAvl > ExtUpnl
|
|
@@ -44691,7 +44867,8 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44691
44867
|
},
|
|
44692
44868
|
config: {
|
|
44693
44869
|
positionPrecision: COLLATERAL_PRECISION,
|
|
44694
|
-
hfBuffer: 0.05
|
|
44870
|
+
hfBuffer: 0.05,
|
|
44871
|
+
minRoutableUsd: CASE_THRESHOLD_USD
|
|
44695
44872
|
}
|
|
44696
44873
|
};
|
|
44697
44874
|
}
|
|
@@ -44702,8 +44879,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44702
44879
|
}
|
|
44703
44880
|
/**
|
|
44704
44881
|
* Turn pure rebalance() deltas into execution routes.
|
|
44705
|
-
* Order: Vesu multiply
|
|
44706
|
-
* (REALISE_PNL, EXTENDED_TO_WALLET + WAIT, WALLET_TO_VA, VESU_BORROW, VESU_REPAY, VA_TO_EXTENDED)
|
|
44882
|
+
* Order: Vesu multiply decrease → Extended decrease → aggregated transfers
|
|
44883
|
+
* (REALISE_PNL, EXTENDED_TO_WALLET + WAIT, WALLET_TO_VA, VESU_BORROW, VESU_REPAY, VA_TO_EXTENDED),
|
|
44884
|
+
* then Vesu multiply increase and Extended increase (need VA / Extended funded first).
|
|
44707
44885
|
*/
|
|
44708
44886
|
_buildLtvRoutesFromRebalanceDeltas(d) {
|
|
44709
44887
|
const routes = [];
|
|
@@ -44715,6 +44893,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44715
44893
|
const targetLtv = VesuConfig.targetLtv;
|
|
44716
44894
|
const btcEps = 10 ** -COLLATERAL_PRECISION;
|
|
44717
44895
|
let multiplyDebtRepayUsd = 0;
|
|
44896
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas d=${JSON.stringify(d)}`);
|
|
44718
44897
|
if (d.dVesuPosition < -btcEps) {
|
|
44719
44898
|
const xBtc = -d.dVesuPosition;
|
|
44720
44899
|
const transferUsdFromVesu = Math.max(0, d.dTransferVesuToExt);
|
|
@@ -44733,12 +44912,28 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44733
44912
|
if (d.dVesuDebt < 0) {
|
|
44734
44913
|
const needRepayUsd = -d.dVesuDebt * debtPrice;
|
|
44735
44914
|
const multiplyRepayUsd = Math.min(needRepayUsd, swapLegMaxRepayUsd);
|
|
44915
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas ${JSON.stringify({
|
|
44916
|
+
needRepayUsd,
|
|
44917
|
+
multiplyRepayUsd
|
|
44918
|
+
})}`);
|
|
44736
44919
|
debtTokenDelta = -(multiplyRepayUsd / debtPrice);
|
|
44737
44920
|
} else {
|
|
44738
44921
|
debtTokenDelta = -debtUsdFallback;
|
|
44739
44922
|
}
|
|
44740
44923
|
const debtAmtW3 = new Web3Number(debtTokenDelta.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
|
|
44741
44924
|
multiplyDebtRepayUsd = Math.abs(debtTokenDelta) * debtPrice;
|
|
44925
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas ${JSON.stringify({
|
|
44926
|
+
debtTokenDelta,
|
|
44927
|
+
debtUsdFallback,
|
|
44928
|
+
swapLegMaxRepayUsd,
|
|
44929
|
+
xBtc,
|
|
44930
|
+
marginBtc,
|
|
44931
|
+
swappedBtc,
|
|
44932
|
+
transferUsdFromVesu,
|
|
44933
|
+
debtPrice,
|
|
44934
|
+
targetLtv,
|
|
44935
|
+
multiplyDebtRepayUsd
|
|
44936
|
+
})}`);
|
|
44742
44937
|
routes.push({
|
|
44743
44938
|
type: "VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */,
|
|
44744
44939
|
poolId: vesuAdapter.config.poolId,
|
|
@@ -44759,19 +44954,68 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44759
44954
|
if (transferUsdFromVesu > CASE_THRESHOLD_USD) {
|
|
44760
44955
|
this._budget.addToVA(transferUsdFromVesu);
|
|
44761
44956
|
}
|
|
44762
|
-
}
|
|
44957
|
+
}
|
|
44958
|
+
if (d.dExtPosition < -btcEps) {
|
|
44959
|
+
const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
|
|
44960
|
+
routes.push({
|
|
44961
|
+
type: "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
|
|
44962
|
+
amount: amt,
|
|
44963
|
+
instrument,
|
|
44964
|
+
priority: routes.length
|
|
44965
|
+
});
|
|
44966
|
+
this._budget.applyExtendedExposureDelta(instrument, amt, price);
|
|
44967
|
+
}
|
|
44968
|
+
const negUpnl = Math.min(0, d.dExtUpnl);
|
|
44969
|
+
const negExtAvl = Math.min(0, d.dExtAvlWithdraw);
|
|
44970
|
+
let hadExtendedOut = false;
|
|
44971
|
+
if (negUpnl < -CASE_THRESHOLD_USD) {
|
|
44972
|
+
this._getUpnlRoute(Math.abs(negUpnl), routes);
|
|
44973
|
+
hadExtendedOut = true;
|
|
44974
|
+
}
|
|
44975
|
+
const extToWalletUsd = (negExtAvl < -CASE_THRESHOLD_USD ? Math.abs(negExtAvl) : 0) + (negUpnl < -CASE_THRESHOLD_USD ? Math.abs(negUpnl) : 0);
|
|
44976
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas extToWalletUsd=${extToWalletUsd}, negExtAvl=${negExtAvl}, negUpnl=${negUpnl}`);
|
|
44977
|
+
if (extToWalletUsd > CASE_THRESHOLD_USD) {
|
|
44978
|
+
this._getExtendedToWalletRoute(extToWalletUsd, routes);
|
|
44979
|
+
hadExtendedOut = true;
|
|
44980
|
+
} else {
|
|
44981
|
+
if (d.dVesuDebt < 0) {
|
|
44982
|
+
d.dVesuDebt += (negExtAvl < 0 && negExtAvl > -CASE_THRESHOLD_USD ? Math.abs(negExtAvl) : 0) + (negUpnl < 0 && negUpnl > -CASE_THRESHOLD_USD ? Math.abs(negUpnl) : 0);
|
|
44983
|
+
}
|
|
44984
|
+
}
|
|
44985
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas d=${JSON.stringify(d)}`);
|
|
44986
|
+
const walletPull = Math.abs(Math.min(0, d.dWalletUsd));
|
|
44987
|
+
const walletToVaUsd = walletPull + extToWalletUsd;
|
|
44988
|
+
if (walletToVaUsd > CASE_THRESHOLD_USD) {
|
|
44989
|
+
this._getWALLETToVARoute(walletToVaUsd, routes);
|
|
44990
|
+
}
|
|
44991
|
+
if (d.dVesuBorrowCapacity < -CASE_THRESHOLD_USD) {
|
|
44992
|
+
this._buildVesuBorrowRoutes(Math.abs(d.dVesuBorrowCapacity), routes);
|
|
44993
|
+
}
|
|
44994
|
+
const totalDebtRepayUsd = d.dVesuDebt < 0 ? -d.dVesuDebt * debtPrice : 0;
|
|
44995
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas totalDebtRepayUsd=${totalDebtRepayUsd}`);
|
|
44996
|
+
let standaloneRepayUsd = Math.max(0, totalDebtRepayUsd - multiplyDebtRepayUsd);
|
|
44997
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas standaloneRepayUsd=${standaloneRepayUsd}`);
|
|
44998
|
+
if (standaloneRepayUsd > this._budget.vaRawUsd) {
|
|
44999
|
+
if (Math.abs(standaloneRepayUsd - this._budget.vaRawUsd) < CASE_THRESHOLD_USD) {
|
|
45000
|
+
standaloneRepayUsd = this._budget.vaRawUsd;
|
|
45001
|
+
} else {
|
|
45002
|
+
throw new Error(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas standaloneRepayUsd=${standaloneRepayUsd} > vaRawUsd=${this._budget.vaRawUsd}`);
|
|
45003
|
+
}
|
|
45004
|
+
}
|
|
45005
|
+
if (standaloneRepayUsd > CASE_THRESHOLD_USD) {
|
|
45006
|
+
this._buildVesuRepayRoutes(standaloneRepayUsd, routes);
|
|
45007
|
+
}
|
|
45008
|
+
const posExtEq = Math.max(0, d.dExtAvlWithdraw);
|
|
45009
|
+
const vaToExtUsd = posExtEq > CASE_THRESHOLD_USD ? posExtEq : 0;
|
|
45010
|
+
if (vaToExtUsd > CASE_THRESHOLD_USD) {
|
|
45011
|
+
this._getVAToEXTENDEDRoute(vaToExtUsd, routes, hadExtendedOut);
|
|
45012
|
+
}
|
|
45013
|
+
if (d.dVesuPosition > btcEps) {
|
|
44763
45014
|
const vesuDepositAmount = new Web3Number(
|
|
44764
45015
|
(d.dVesuPosition * price * (1 - targetLtv)).toFixed(USDC_TOKEN_DECIMALS),
|
|
44765
45016
|
USDC_TOKEN_DECIMALS
|
|
44766
45017
|
);
|
|
44767
45018
|
if (vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
|
|
44768
|
-
routes.push({
|
|
44769
|
-
type: "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
|
|
44770
|
-
priority: routes.length,
|
|
44771
|
-
fromToken: vesuAdapter.config.collateral.symbol,
|
|
44772
|
-
fromAmount: vesuDepositAmount,
|
|
44773
|
-
toToken: vesuAdapter.config.debt.symbol
|
|
44774
|
-
});
|
|
44775
45019
|
}
|
|
44776
45020
|
const collateralDelta = new Web3Number(
|
|
44777
45021
|
d.dVesuPosition.toFixed(COLLATERAL_PRECISION),
|
|
@@ -44782,8 +45026,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44782
45026
|
new Web3Number(Math.min(availableBorrowCapacity, vesuDepositAmount.toNumber()).toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS)
|
|
44783
45027
|
);
|
|
44784
45028
|
const collPx = pool.collateralPrice || 1;
|
|
45029
|
+
const marginUsdAmount = externalDepositAmount.toNumber() * (pool.debtPrice ?? 1);
|
|
44785
45030
|
const swappedAmount = new Web3Number(
|
|
44786
|
-
(
|
|
45031
|
+
(marginUsdAmount / collPx).toFixed(6),
|
|
44787
45032
|
vesuAdapter.config.collateral.decimals
|
|
44788
45033
|
);
|
|
44789
45034
|
const debtDeltaTokens = new Web3Number(
|
|
@@ -44807,17 +45052,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44807
45052
|
collateralDelta,
|
|
44808
45053
|
debtDeltaTokens
|
|
44809
45054
|
);
|
|
45055
|
+
this._budget.spendVA(marginUsdAmount);
|
|
44810
45056
|
}
|
|
44811
|
-
if (d.dExtPosition
|
|
44812
|
-
const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
|
|
44813
|
-
routes.push({
|
|
44814
|
-
type: "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
|
|
44815
|
-
amount: amt,
|
|
44816
|
-
instrument,
|
|
44817
|
-
priority: routes.length
|
|
44818
|
-
});
|
|
44819
|
-
this._budget.applyExtendedExposureDelta(instrument, amt, price);
|
|
44820
|
-
} else if (d.dExtPosition > btcEps) {
|
|
45057
|
+
if (d.dExtPosition > btcEps) {
|
|
44821
45058
|
const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
|
|
44822
45059
|
routes.push({
|
|
44823
45060
|
type: "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */,
|
|
@@ -44827,36 +45064,6 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44827
45064
|
});
|
|
44828
45065
|
this._budget.applyExtendedExposureDelta(instrument, amt, price);
|
|
44829
45066
|
}
|
|
44830
|
-
const negUpnl = Math.min(0, d.dExtUpnl);
|
|
44831
|
-
const negExtAvl = Math.min(0, d.dExtAvlWithdraw);
|
|
44832
|
-
let hadExtendedOut = false;
|
|
44833
|
-
if (negUpnl < -CASE_THRESHOLD_USD) {
|
|
44834
|
-
this._getUpnlRoute(Math.abs(negUpnl), routes);
|
|
44835
|
-
hadExtendedOut = true;
|
|
44836
|
-
}
|
|
44837
|
-
const extToWalletUsd = (negExtAvl < -CASE_THRESHOLD_USD ? Math.abs(negExtAvl) : 0) + (negUpnl < -CASE_THRESHOLD_USD ? Math.abs(negUpnl) : 0);
|
|
44838
|
-
if (extToWalletUsd > CASE_THRESHOLD_USD) {
|
|
44839
|
-
this._getExtendedToWalletRoute(extToWalletUsd, routes);
|
|
44840
|
-
hadExtendedOut = true;
|
|
44841
|
-
}
|
|
44842
|
-
const walletPull = Math.abs(Math.min(0, d.dWalletUsd));
|
|
44843
|
-
const walletToVaUsd = walletPull + extToWalletUsd;
|
|
44844
|
-
if (walletToVaUsd > CASE_THRESHOLD_USD) {
|
|
44845
|
-
this._getWALLETToVARoute(walletToVaUsd, routes);
|
|
44846
|
-
}
|
|
44847
|
-
if (d.dVesuBorrowCapacity < -CASE_THRESHOLD_USD) {
|
|
44848
|
-
this._buildVesuBorrowRoutes(Math.abs(d.dVesuBorrowCapacity), routes);
|
|
44849
|
-
}
|
|
44850
|
-
const totalDebtRepayUsd = d.dVesuDebt < 0 ? -d.dVesuDebt * debtPrice : 0;
|
|
44851
|
-
const standaloneRepayUsd = Math.max(0, totalDebtRepayUsd - multiplyDebtRepayUsd);
|
|
44852
|
-
if (standaloneRepayUsd > CASE_THRESHOLD_USD) {
|
|
44853
|
-
this._buildVesuRepayRoutes(standaloneRepayUsd, routes);
|
|
44854
|
-
}
|
|
44855
|
-
const posExtEq = Math.max(0, d.dExtAvlWithdraw);
|
|
44856
|
-
const vaToExtUsd = posExtEq > CASE_THRESHOLD_USD ? posExtEq : 0;
|
|
44857
|
-
if (vaToExtUsd > CASE_THRESHOLD_USD) {
|
|
44858
|
-
this._getVAToEXTENDEDRoute(vaToExtUsd, routes, hadExtendedOut);
|
|
44859
|
-
}
|
|
44860
45067
|
return routes;
|
|
44861
45068
|
}
|
|
44862
45069
|
// ── LTV Vesu route builders ───────────────────────────────────────────
|
|
@@ -45015,48 +45222,76 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
45015
45222
|
const extendedTarget = total / (1 + extendedLeverage / vesuLeverage);
|
|
45016
45223
|
const extendedInitial = extAvlWithdraw + extUpnl;
|
|
45017
45224
|
let delta = extendedTarget - extendedInitial;
|
|
45018
|
-
let dExtAvlWithdraw = 0, dExtUpnl = 0, dVaUsd = 0, dWalletUsd = 0, dVesuBorrowCapacity = 0;
|
|
45225
|
+
let dExtAvlWithdraw = 0, dExtUpnl = 0, dVaUsd = 0, dWalletUsd = 0, dVesuBorrowCapacity = 0, finalVaUsd = vaUsd, finalExtended = extAvlWithdraw + Math.max(extUpnl, 0);
|
|
45019
45226
|
if (delta > 0) {
|
|
45020
45227
|
let need = delta;
|
|
45021
|
-
const
|
|
45228
|
+
const eps = CASE_THRESHOLD_USD;
|
|
45229
|
+
const takeWalletUsd = routableDrawAmount(walletUsd, need, eps);
|
|
45022
45230
|
dWalletUsd -= takeWalletUsd;
|
|
45023
45231
|
need -= takeWalletUsd;
|
|
45024
|
-
const takeVaUsd =
|
|
45232
|
+
const takeVaUsd = routableDrawAmount(vaUsd, need, eps);
|
|
45025
45233
|
dVaUsd -= takeVaUsd;
|
|
45026
45234
|
need -= takeVaUsd;
|
|
45027
|
-
|
|
45235
|
+
finalVaUsd -= takeVaUsd;
|
|
45236
|
+
finalVaUsd += walletUsd - takeWalletUsd;
|
|
45237
|
+
const takeVesuBorrowCapacity = routableDrawAmount(vesuBorrowCapacity, need, eps);
|
|
45028
45238
|
dVesuBorrowCapacity -= takeVesuBorrowCapacity;
|
|
45029
45239
|
need -= takeVesuBorrowCapacity;
|
|
45240
|
+
finalVaUsd += vesuBorrowCapacity - takeVesuBorrowCapacity;
|
|
45030
45241
|
const received = delta - need;
|
|
45031
45242
|
const eco1Sum = extAvlWithdraw + extUpnl;
|
|
45243
|
+
finalExtended += received;
|
|
45032
45244
|
if (eco1Sum >= 0) {
|
|
45033
45245
|
dExtAvlWithdraw += received;
|
|
45034
45246
|
} else {
|
|
45035
|
-
|
|
45247
|
+
const hole = -eco1Sum;
|
|
45248
|
+
const fillUpnl = Math.min(received, hole);
|
|
45249
|
+
dExtUpnl += fillUpnl;
|
|
45250
|
+
dExtAvlWithdraw += received - fillUpnl;
|
|
45251
|
+
finalExtended -= fillUpnl;
|
|
45036
45252
|
}
|
|
45037
|
-
if (need >
|
|
45253
|
+
if (need > CASE_THRESHOLD_USD) {
|
|
45038
45254
|
throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
|
|
45039
45255
|
}
|
|
45040
45256
|
} else if (delta < 0) {
|
|
45041
45257
|
let need = -delta;
|
|
45042
|
-
const takeExtAvlWithdraw = Math.min(extAvlWithdraw, need);
|
|
45258
|
+
const takeExtAvlWithdraw = Math.min(Math.max(extAvlWithdraw, 0), need);
|
|
45043
45259
|
dExtAvlWithdraw -= takeExtAvlWithdraw;
|
|
45044
|
-
|
|
45045
|
-
const takeExtUpnl = Math.min(extUpnl, need);
|
|
45260
|
+
const takeExtUpnl = Math.min(Math.max(extUpnl, 0), need);
|
|
45046
45261
|
dExtUpnl -= takeExtUpnl;
|
|
45047
|
-
|
|
45262
|
+
const netDrawableAmount = takeExtAvlWithdraw + takeExtUpnl;
|
|
45263
|
+
if (netDrawableAmount > CASE_THRESHOLD_USD) {
|
|
45264
|
+
need -= netDrawableAmount;
|
|
45265
|
+
finalExtended -= netDrawableAmount;
|
|
45266
|
+
}
|
|
45048
45267
|
const sent = -delta - need;
|
|
45049
45268
|
const eco2Sum = vaUsd + walletUsd + vesuBorrowCapacity;
|
|
45269
|
+
const netWalletUsd = walletUsd < CASE_THRESHOLD_USD ? 0 : walletUsd;
|
|
45270
|
+
finalVaUsd += sent + netWalletUsd;
|
|
45050
45271
|
if (eco2Sum >= 0) {
|
|
45051
45272
|
dWalletUsd += sent;
|
|
45052
45273
|
} else {
|
|
45053
45274
|
throw new Error(`${this._tag}: Unexpected case`);
|
|
45054
45275
|
}
|
|
45055
|
-
if (need >
|
|
45276
|
+
if (need > CASE_THRESHOLD_USD) {
|
|
45056
45277
|
throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
|
|
45057
45278
|
}
|
|
45058
45279
|
}
|
|
45059
|
-
return { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu: delta < 0 };
|
|
45280
|
+
return { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, finalVaUsd, finalExtended, isExtendedToVesu: delta < 0 };
|
|
45281
|
+
}
|
|
45282
|
+
_scaleVesuPoolDeltasByFactor(deltas, scale) {
|
|
45283
|
+
if (scale >= 1 - 1e-15) return deltas;
|
|
45284
|
+
return deltas.map((d) => ({
|
|
45285
|
+
...d,
|
|
45286
|
+
collateralDelta: new Web3Number(
|
|
45287
|
+
(d.collateralDelta.toNumber() * scale).toFixed(COLLATERAL_PRECISION),
|
|
45288
|
+
d.collateralToken.decimals
|
|
45289
|
+
),
|
|
45290
|
+
debtDelta: new Web3Number(
|
|
45291
|
+
(d.debtDelta.toNumber() * scale).toFixed(USDC_TOKEN_DECIMALS),
|
|
45292
|
+
USDC_TOKEN_DECIMALS
|
|
45293
|
+
)
|
|
45294
|
+
}));
|
|
45060
45295
|
}
|
|
45061
45296
|
/**
|
|
45062
45297
|
* 3. New Deposits / Excess Funds
|
|
@@ -45072,6 +45307,11 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
45072
45307
|
* Computes allocation split between Vesu and Extended, then sources
|
|
45073
45308
|
* funds and creates lever-increase routes.
|
|
45074
45309
|
*
|
|
45310
|
+
* Order: {@link _rebalanceFunds} first → project VA / Extended liquid after the same funding
|
|
45311
|
+
* routes (wallet→VA, borrow→VA, VA→Extended, Extended→wallet→VA) → ideal Vesu/Extended deltas
|
|
45312
|
+
* from distributable split → cap common BTC by min(Vesu fundable, Extended fundable) → scale
|
|
45313
|
+
* Vesu deltas and recompute Extended deltas so both sides stay matched.
|
|
45314
|
+
*
|
|
45075
45315
|
* Fund flow (single pass — avoid VA→Extended then Extended→wallet round-trips):
|
|
45076
45316
|
* 1) Treat Vesu borrow headroom that the multiply route will consume as covering
|
|
45077
45317
|
* part of the Vesu USDC need (no standalone VESU_BORROW for that slice). Cap
|
|
@@ -45094,15 +45334,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
45094
45334
|
withdrawAmount
|
|
45095
45335
|
);
|
|
45096
45336
|
if (distributableAmount.toNumber() <= CASE_THRESHOLD_USD) return [];
|
|
45097
|
-
const { vesuAllocationUsd, extendedAllocationUsd } = this._computeAllocationSplit(distributableAmount);
|
|
45098
|
-
const vesuDeltas = this._computePerPoolCollateralDeltas(
|
|
45099
|
-
vesuAllocationUsd
|
|
45100
|
-
);
|
|
45101
|
-
const extendedPositionDeltas = this._computeExtendedPositionDeltas(vesuDeltas);
|
|
45102
|
-
const vesuDepositAmount = this._computeVesuDepositAmount(vesuDeltas);
|
|
45103
45337
|
const vesuLeverage = calculateVesuLeverage();
|
|
45104
45338
|
const extendedLeverage = calculateExtendedLevergae();
|
|
45105
|
-
const { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu } = this._rebalanceFunds({
|
|
45339
|
+
const { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu, finalVaUsd, finalExtended } = this._rebalanceFunds({
|
|
45106
45340
|
extAvlWithdraw: this._budget.extAvailWithdraw,
|
|
45107
45341
|
extUpnl: this._budget.extAvailUpnl,
|
|
45108
45342
|
vaUsd: this._budget.vaUsd,
|
|
@@ -45111,6 +45345,23 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
45111
45345
|
vesuLeverage,
|
|
45112
45346
|
extendedLeverage
|
|
45113
45347
|
});
|
|
45348
|
+
logger.info(`${this._tag}::_classifyDeposits dExtAvlWithdraw=${dExtAvlWithdraw}, dExtUpnl=${dExtUpnl}, dVaUsd=${dVaUsd}, dWalletUsd=${dWalletUsd}, dVesuBorrowCapacity=${dVesuBorrowCapacity}, isExtendedToVesu=${isExtendedToVesu}, finalVaUsd=${finalVaUsd}`);
|
|
45349
|
+
let vesuDeltas = this._computePerPoolCollateralDeltas(new Web3Number(finalVaUsd.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS));
|
|
45350
|
+
const collateralPrice = this._budget.vesuPools[0]?.collateralPrice ?? 0;
|
|
45351
|
+
const collateralDecimals = this._budget.vesuPools[0]?.collateralToken.decimals ?? 0;
|
|
45352
|
+
let _extendedPositionDelta = new Web3Number((finalExtended * extendedLeverage / collateralPrice).toFixed(USDC_TOKEN_DECIMALS), collateralDecimals).toFixedRoundDown(COLLATERAL_PRECISION);
|
|
45353
|
+
logger.info(`${this._tag}::_classifyDeposits extendedPositionDelta=${_extendedPositionDelta}`);
|
|
45354
|
+
logger.info(`${this._tag}::_classifyDeposits vesuDeltas=${JSON.stringify(vesuDeltas)}`);
|
|
45355
|
+
assert(vesuDeltas.length == 1, "vesuDeltas should have only one delta");
|
|
45356
|
+
const minPositionDelta = Math.min(vesuDeltas[0].collateralDelta.toNumber(), Number(_extendedPositionDelta));
|
|
45357
|
+
logger.info(`${this._tag}::_classifyDeposits minPositionDelta=${minPositionDelta}`);
|
|
45358
|
+
vesuDeltas[0].collateralDelta = new Web3Number(minPositionDelta.toFixed(COLLATERAL_PRECISION), vesuDeltas[0].collateralDelta.decimals);
|
|
45359
|
+
const extendedPositionDeltas = [{
|
|
45360
|
+
instrument: this._config.extendedAdapter.config.extendedMarketName ?? "BTC-USD",
|
|
45361
|
+
delta: new Web3Number(minPositionDelta.toFixed(COLLATERAL_PRECISION), collateralDecimals)
|
|
45362
|
+
}];
|
|
45363
|
+
const vesuDepositAmount = this._computeVesuDepositAmount(vesuDeltas);
|
|
45364
|
+
logger.info(`${this._tag}::_classifyDeposits vesuDepositAmount=${vesuDepositAmount}`);
|
|
45114
45365
|
const routes = [];
|
|
45115
45366
|
if (isExtendedToVesu) {
|
|
45116
45367
|
if (dExtUpnl < 0) {
|
|
@@ -45138,13 +45389,6 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
45138
45389
|
}
|
|
45139
45390
|
for (const vesuDelta of vesuDeltas) {
|
|
45140
45391
|
if (!skipAvnuDepositSwap && vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
|
|
45141
|
-
routes.push({
|
|
45142
|
-
type: "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
|
|
45143
|
-
priority: routes.length,
|
|
45144
|
-
fromToken: vesuDelta.collateralToken.symbol,
|
|
45145
|
-
fromAmount: vesuDepositAmount,
|
|
45146
|
-
toToken: vesuDelta.debtToken.symbol
|
|
45147
|
-
});
|
|
45148
45392
|
}
|
|
45149
45393
|
if (vesuDelta.collateralDelta.toNumber() > 0) {
|
|
45150
45394
|
const availableBorrowCapacity = this._budget.vesuBorrowCapacity;
|
|
@@ -46124,14 +46368,6 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46124
46368
|
);
|
|
46125
46369
|
return this._executeStandardCase(caseEntry, activeRoutes);
|
|
46126
46370
|
}
|
|
46127
|
-
const setLevResult = await this._config.extendedAdapter.setLeverage(calculateExtendedLevergae().toString(), this._config.extendedAdapter.config.extendedMarketName);
|
|
46128
|
-
if (!setLevResult) {
|
|
46129
|
-
logger.error(
|
|
46130
|
-
`${this._tag}::_executeCoordinatedCase failed to set leverage`
|
|
46131
|
-
);
|
|
46132
|
-
results.push(this._failureResult(extendedRoute));
|
|
46133
|
-
return results;
|
|
46134
|
-
}
|
|
46135
46371
|
const isIncrease = _ExecutionService.INCREASE_EXPOSURE_ROUTES.has(
|
|
46136
46372
|
extendedRoute.type
|
|
46137
46373
|
);
|
|
@@ -46152,8 +46388,9 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46152
46388
|
onChainCalls = callArrays.flat();
|
|
46153
46389
|
} catch (err) {
|
|
46154
46390
|
logger.error(
|
|
46155
|
-
`${this._tag}::_executeCoordinatedCase on-chain call construction failed
|
|
46391
|
+
`${this._tag}::_executeCoordinatedCase on-chain call construction failed:`
|
|
46156
46392
|
);
|
|
46393
|
+
console.error(err);
|
|
46157
46394
|
await this._emitEvent("FAILURE" /* FAILURE */, {
|
|
46158
46395
|
routeSummary: `coordinated on-chain build for case "${caseEntry.case.id}"`,
|
|
46159
46396
|
error: `${err}`
|
|
@@ -46225,7 +46462,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46225
46462
|
}
|
|
46226
46463
|
const timeoutMs = this._config.extendedFillTimeoutMs ?? DEFAULT_EXTENDED_FILL_TIMEOUT_MS;
|
|
46227
46464
|
const extResult = await this._executeExtendedLimitOrderWithRecovery(
|
|
46228
|
-
btcAmount,
|
|
46465
|
+
Math.abs(btcAmount),
|
|
46229
46466
|
executionPrice,
|
|
46230
46467
|
side,
|
|
46231
46468
|
{
|
|
@@ -46753,7 +46990,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46753
46990
|
return this._failureResult(route);
|
|
46754
46991
|
}
|
|
46755
46992
|
const closeResult = await this._executeExtendedLimitOrderWithRecovery(
|
|
46756
|
-
positionToClose.toNumber(),
|
|
46993
|
+
Math.abs(positionToClose.toNumber()),
|
|
46757
46994
|
midPrice,
|
|
46758
46995
|
"BUY" /* BUY */
|
|
46759
46996
|
);
|
|
@@ -46771,7 +47008,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46771
47008
|
`${this._tag}::_executeRealisePnl reopening ${positionToClose.toNumber()} on ${route.instrument}`
|
|
46772
47009
|
);
|
|
46773
47010
|
const reopenResult = await this._executeExtendedLimitOrderWithRecovery(
|
|
46774
|
-
positionToClose.toNumber(),
|
|
47011
|
+
Math.abs(positionToClose.toNumber()),
|
|
46775
47012
|
midPrice,
|
|
46776
47013
|
"SELL" /* SELL */
|
|
46777
47014
|
);
|
|
@@ -46825,7 +47062,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46825
47062
|
return this._failureResult(route);
|
|
46826
47063
|
}
|
|
46827
47064
|
const result = await this._executeExtendedLimitOrderWithRecovery(
|
|
46828
|
-
route.amount.toNumber(),
|
|
47065
|
+
Math.abs(route.amount.toNumber()),
|
|
46829
47066
|
midPrice,
|
|
46830
47067
|
"SELL" /* SELL */
|
|
46831
47068
|
);
|
|
@@ -46873,7 +47110,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46873
47110
|
return this._failureResult(route);
|
|
46874
47111
|
}
|
|
46875
47112
|
const result = await this._executeExtendedLimitOrderWithRecovery(
|
|
46876
|
-
route.amount.toNumber(),
|
|
47113
|
+
Math.abs(route.amount.toNumber()),
|
|
46877
47114
|
midPrice,
|
|
46878
47115
|
"BUY" /* BUY */
|
|
46879
47116
|
);
|
|
@@ -47496,16 +47733,16 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
|
|
|
47496
47733
|
];
|
|
47497
47734
|
}
|
|
47498
47735
|
};
|
|
47499
|
-
function getLooperSettings3(
|
|
47736
|
+
function getLooperSettings3(collateralSymbol, underlyingSymbol, vaultSettings, pool1, extendedBackendReadUrl, extendedBackendWriteUrl, vaultIdExtended, minimumExtendedMovementAmount, minimumVesuMovementAmount, minimumExtendedRetriesDelayForOrderStatus, minimumExtendedPriceDifferenceForSwapOpen, maximumExtendedPriceDifferenceForSwapClosing) {
|
|
47500
47737
|
vaultSettings.leafAdapters = [];
|
|
47501
47738
|
const wbtcToken = Global.getDefaultTokens().find(
|
|
47502
|
-
(token) => token.symbol ===
|
|
47739
|
+
(token) => token.symbol === collateralSymbol
|
|
47503
47740
|
);
|
|
47504
47741
|
const usdcToken = Global.getDefaultTokens().find(
|
|
47505
47742
|
(token) => token.symbol === underlyingSymbol
|
|
47506
47743
|
);
|
|
47507
47744
|
const baseAdapterConfig = {
|
|
47508
|
-
baseToken:
|
|
47745
|
+
baseToken: usdcToken,
|
|
47509
47746
|
supportedPositions: [
|
|
47510
47747
|
{ asset: usdcToken, isDebt: true },
|
|
47511
47748
|
{ asset: wbtcToken, isDebt: false }
|
|
@@ -47605,7 +47842,7 @@ function getLooperSettings3(lstSymbol, underlyingSymbol, vaultSettings, pool1, e
|
|
|
47605
47842
|
vaultAddress: vaultSettings.vaultAddress,
|
|
47606
47843
|
vaultAllocator: vaultSettings.vaultAllocator,
|
|
47607
47844
|
manager: vaultSettings.manager,
|
|
47608
|
-
asset:
|
|
47845
|
+
asset: usdcToken.address
|
|
47609
47846
|
});
|
|
47610
47847
|
vaultSettings.leafAdapters.push(() => vesuMultiplyAdapter.getDepositLeaf());
|
|
47611
47848
|
vaultSettings.leafAdapters.push(() => vesuMultiplyAdapter.getWithdrawLeaf());
|
|
@@ -47739,6 +47976,39 @@ var re7UsdcPrimeDevansh = {
|
|
|
47739
47976
|
minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
|
|
47740
47977
|
walletAddress: "0x024b563C1C7d41B32BF4EFB9F38828508a65Be2d6e25268E9f63F22C5e9E51c5"
|
|
47741
47978
|
};
|
|
47979
|
+
var pureUsdc = {
|
|
47980
|
+
vaultAddress: ContractAddr.from(
|
|
47981
|
+
"0x745c972db65bdee10022fd875dd328c7f40a90849135b6a0f875a40f3c632ae"
|
|
47982
|
+
),
|
|
47983
|
+
manager: ContractAddr.from(
|
|
47984
|
+
"0x364e0894edefb616ec090f57f5c0274517fcd98ab276ae1f021c5e962fa1deb"
|
|
47985
|
+
),
|
|
47986
|
+
vaultAllocator: ContractAddr.from(
|
|
47987
|
+
"0x6fceed28e03a96091877568893df0dd89b9bb80fec30da2b742dacbd5526179"
|
|
47988
|
+
),
|
|
47989
|
+
redeemRequestNFT: ContractAddr.from(
|
|
47990
|
+
"0x501c2b87728e22c6dfcebe4c0b2b3a9fba5845606e4d59fa7bf591badcbb42"
|
|
47991
|
+
),
|
|
47992
|
+
aumOracle: ContractAddr.from(
|
|
47993
|
+
"0x6ccd95f5765242695d3c75e1440b1d0b30efac8babb864ce15729977b97cb82"
|
|
47994
|
+
),
|
|
47995
|
+
leafAdapters: [],
|
|
47996
|
+
adapters: [],
|
|
47997
|
+
targetHealthFactor: 1.4,
|
|
47998
|
+
minHealthFactor: 1.35,
|
|
47999
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
48000
|
+
(token) => token.symbol === "USDC"
|
|
48001
|
+
),
|
|
48002
|
+
quoteAmountToFetchPrice: new Web3Number(
|
|
48003
|
+
"0.001",
|
|
48004
|
+
Global.getDefaultTokens().find((token) => token.symbol === "USDC").decimals
|
|
48005
|
+
),
|
|
48006
|
+
borrowable_assets: [
|
|
48007
|
+
Global.getDefaultTokens().find((token) => token.symbol === "USDC")
|
|
48008
|
+
],
|
|
48009
|
+
minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
|
|
48010
|
+
walletAddress: "0x058571C23da5FEdd4e36003FAE3fE2fA9782f2692E552f081839142B10770D0B"
|
|
48011
|
+
};
|
|
47742
48012
|
var VesuExtendedTestStrategies = (extendedBackendReadUrl, extendedBackendWriteUrl, vaultIdExtended, minimumExtendedMovementAmount, minimumVesuMovementAmount, minimumExtendedRetriesDelayForOrderStatus, minimumExtendedPriceDifferenceForSwapOpen, maximumExtendedPriceDifferenceForSwapClosing) => {
|
|
47743
48013
|
return [
|
|
47744
48014
|
getStrategySettingsVesuExtended(
|
|
@@ -47755,14 +48025,29 @@ var VesuExtendedTestStrategies = (extendedBackendReadUrl, extendedBackendWriteUr
|
|
|
47755
48025
|
minimumExtendedRetriesDelayForOrderStatus,
|
|
47756
48026
|
minimumExtendedPriceDifferenceForSwapOpen,
|
|
47757
48027
|
maximumExtendedPriceDifferenceForSwapClosing
|
|
48028
|
+
),
|
|
48029
|
+
getStrategySettingsVesuExtended(
|
|
48030
|
+
"WBTC",
|
|
48031
|
+
"USDC",
|
|
48032
|
+
pureUsdc,
|
|
48033
|
+
false,
|
|
48034
|
+
false,
|
|
48035
|
+
extendedBackendReadUrl,
|
|
48036
|
+
extendedBackendWriteUrl,
|
|
48037
|
+
vaultIdExtended,
|
|
48038
|
+
minimumExtendedMovementAmount,
|
|
48039
|
+
minimumVesuMovementAmount,
|
|
48040
|
+
minimumExtendedRetriesDelayForOrderStatus,
|
|
48041
|
+
minimumExtendedPriceDifferenceForSwapOpen,
|
|
48042
|
+
maximumExtendedPriceDifferenceForSwapClosing
|
|
47758
48043
|
)
|
|
47759
48044
|
];
|
|
47760
48045
|
};
|
|
47761
|
-
function getStrategySettingsVesuExtended(
|
|
48046
|
+
function getStrategySettingsVesuExtended(collateralSymbol, underlyingSymbol, addresses, isPreview = false, isLST, extendedBackendReadUrl, extendedBackendWriteUrl, vaultIdExtended, minimumExtendedMovementAmount, minimumVesuMovementAmount, minimumExtendedRetriesDelayForOrderStatus, minimumExtendedPriceDifferenceForSwapOpen, maximumExtendedPriceDifferenceForSwapClosing) {
|
|
47762
48047
|
return {
|
|
47763
48048
|
id: `extended_${underlyingSymbol.toLowerCase()}_test`,
|
|
47764
48049
|
name: `Extended Test ${underlyingSymbol}`,
|
|
47765
|
-
description: getDescription3(
|
|
48050
|
+
description: getDescription3(collateralSymbol, underlyingSymbol),
|
|
47766
48051
|
address: addresses.vaultAddress,
|
|
47767
48052
|
launchBlock: 0,
|
|
47768
48053
|
type: "Other",
|
|
@@ -47776,7 +48061,7 @@ function getStrategySettingsVesuExtended(lstSymbol, underlyingSymbol, addresses,
|
|
|
47776
48061
|
)
|
|
47777
48062
|
],
|
|
47778
48063
|
additionalInfo: getLooperSettings3(
|
|
47779
|
-
|
|
48064
|
+
collateralSymbol,
|
|
47780
48065
|
underlyingSymbol,
|
|
47781
48066
|
addresses,
|
|
47782
48067
|
VesuPools.Re7USDCPrime,
|
|
@@ -47797,8 +48082,8 @@ function getStrategySettingsVesuExtended(lstSymbol, underlyingSymbol, addresses,
|
|
|
47797
48082
|
auditUrl: AUDIT_URL4,
|
|
47798
48083
|
protocols: [Protocols.ENDUR, Protocols.VESU],
|
|
47799
48084
|
contractDetails: getContractDetails(addresses),
|
|
47800
|
-
faqs: getFAQs2(
|
|
47801
|
-
investmentSteps: getInvestmentSteps(
|
|
48085
|
+
faqs: getFAQs2(collateralSymbol, underlyingSymbol, isLST),
|
|
48086
|
+
investmentSteps: getInvestmentSteps(collateralSymbol, underlyingSymbol),
|
|
47802
48087
|
isPreview,
|
|
47803
48088
|
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.",
|
|
47804
48089
|
security: {
|