@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.mjs
CHANGED
|
@@ -6013,7 +6013,7 @@ var EkuboQuoter = class _EkuboQuoter {
|
|
|
6013
6013
|
async _callQuoterApi(fromToken, toToken, amount, retry = 0) {
|
|
6014
6014
|
try {
|
|
6015
6015
|
const url = this.ENDPOINT.replace("{{AMOUNT}}", amount.toWei()).replace("{{TOKEN_FROM_ADDRESS}}", fromToken).replace("{{TOKEN_TO_ADDRESS}}", toToken);
|
|
6016
|
-
logger.
|
|
6016
|
+
logger.info(`EkuboQuoter::_callQuoterApi url: ${url}`);
|
|
6017
6017
|
const quote = await axios6.get(url);
|
|
6018
6018
|
return quote.data;
|
|
6019
6019
|
} catch (error) {
|
|
@@ -7883,6 +7883,7 @@ var vesu_rebalance_abi_default = [
|
|
|
7883
7883
|
var CacheClass = class {
|
|
7884
7884
|
constructor() {
|
|
7885
7885
|
this.cache = /* @__PURE__ */ new Map();
|
|
7886
|
+
this.isCacheEnabled = true;
|
|
7886
7887
|
}
|
|
7887
7888
|
setCache(key, data, ttl = 6e4) {
|
|
7888
7889
|
const timestamp = Date.now();
|
|
@@ -7890,7 +7891,7 @@ var CacheClass = class {
|
|
|
7890
7891
|
}
|
|
7891
7892
|
getCache(key) {
|
|
7892
7893
|
const cachedData = this.cache.get(key);
|
|
7893
|
-
if (!cachedData || !this.isCacheValid(key)) {
|
|
7894
|
+
if (!cachedData || !this.isCacheValid(key) || !this.isCacheEnabled) {
|
|
7894
7895
|
return null;
|
|
7895
7896
|
}
|
|
7896
7897
|
return cachedData.data;
|
|
@@ -7901,6 +7902,12 @@ var CacheClass = class {
|
|
|
7901
7902
|
const { timestamp, ttl } = cachedData;
|
|
7902
7903
|
return Date.now() - timestamp <= ttl;
|
|
7903
7904
|
}
|
|
7905
|
+
disableCache() {
|
|
7906
|
+
this.isCacheEnabled = false;
|
|
7907
|
+
}
|
|
7908
|
+
enableCache() {
|
|
7909
|
+
this.isCacheEnabled = true;
|
|
7910
|
+
}
|
|
7904
7911
|
};
|
|
7905
7912
|
|
|
7906
7913
|
// src/strategies/base-strategy.ts
|
|
@@ -28691,7 +28698,7 @@ var VesuAdapter = class _VesuAdapter extends CacheClass {
|
|
|
28691
28698
|
throw new Error("LTV is 0");
|
|
28692
28699
|
}
|
|
28693
28700
|
this.setCache(CACHE_KEY, ltv, 3e5);
|
|
28694
|
-
return
|
|
28701
|
+
return ltv;
|
|
28695
28702
|
}
|
|
28696
28703
|
async getPositions(config, blockNumber = "latest") {
|
|
28697
28704
|
if (!this.pricer) {
|
|
@@ -28775,6 +28782,9 @@ var VesuAdapter = class _VesuAdapter extends CacheClass {
|
|
|
28775
28782
|
debtPrice = (await pricer.getPrice(this.config.debt.priceProxySymbol || this.config.debt.symbol)).price;
|
|
28776
28783
|
}
|
|
28777
28784
|
logger.verbose(`VesuAdapter::getAssetPrices collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
|
|
28785
|
+
if (isNaN(collateralPrice) || isNaN(debtPrice) || collateralPrice == 0 || debtPrice == 0) {
|
|
28786
|
+
throw new Error(`VesuAdapter::getAssetPrices collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
|
|
28787
|
+
}
|
|
28778
28788
|
return {
|
|
28779
28789
|
collateralTokenAmount,
|
|
28780
28790
|
collateralUSDAmount,
|
|
@@ -34413,20 +34423,23 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
34413
34423
|
}
|
|
34414
34424
|
_buildZeroAmountSwapsWithWeights(quote, token, reverseRoutes = false) {
|
|
34415
34425
|
const swaps = quote.splits.map((split) => {
|
|
34416
|
-
|
|
34417
|
-
|
|
34418
|
-
|
|
34419
|
-
|
|
34420
|
-
|
|
34421
|
-
|
|
34422
|
-
|
|
34423
|
-
|
|
34424
|
-
|
|
34425
|
-
|
|
34426
|
-
|
|
34427
|
-
|
|
34428
|
-
|
|
34429
|
-
|
|
34426
|
+
let sellToken = token.address;
|
|
34427
|
+
const routeNodes = split.route.map((_route) => {
|
|
34428
|
+
let isSellToken0 = sellToken.eqString(_route.pool_key.token0);
|
|
34429
|
+
isSellToken0 = reverseRoutes ? !isSellToken0 : isSellToken0;
|
|
34430
|
+
sellToken = isSellToken0 ? ContractAddr.from(_route.pool_key.token1) : ContractAddr.from(_route.pool_key.token0);
|
|
34431
|
+
return {
|
|
34432
|
+
pool_key: {
|
|
34433
|
+
token0: ContractAddr.from(_route.pool_key.token0),
|
|
34434
|
+
token1: ContractAddr.from(_route.pool_key.token1),
|
|
34435
|
+
fee: _route.pool_key.fee,
|
|
34436
|
+
tick_spacing: _route.pool_key.tick_spacing.toString(),
|
|
34437
|
+
extension: _route.pool_key.extension
|
|
34438
|
+
},
|
|
34439
|
+
sqrt_ratio_limit: isSellToken0 ? Web3Number.fromWei(MIN_SQRT_RATIO_LIMIT.toString(), 18) : Web3Number.fromWei(MAX_SQRT_RATIO_LIMIT.toString(), 18),
|
|
34440
|
+
skip_ahead: Web3Number.fromWei(_route.skip_ahead, 0)
|
|
34441
|
+
};
|
|
34442
|
+
});
|
|
34430
34443
|
return {
|
|
34431
34444
|
route: routeNodes,
|
|
34432
34445
|
token_amount: {
|
|
@@ -34748,6 +34761,7 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
34748
34761
|
collateralToken.address.address,
|
|
34749
34762
|
requiredAmount
|
|
34750
34763
|
);
|
|
34764
|
+
logger.info(`VesuMultiplyAdapter::_getIncreaseCalldata requiredAmount: ${requiredAmount}`);
|
|
34751
34765
|
if (marginSwapQuote.price_impact > 0.01) {
|
|
34752
34766
|
throw new Error(
|
|
34753
34767
|
`VesuMultiplyAdapter: Margin swap price impact too high (${marginSwapQuote.price_impact})`
|
|
@@ -34774,7 +34788,7 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
34774
34788
|
legLTV,
|
|
34775
34789
|
dexPrice
|
|
34776
34790
|
);
|
|
34777
|
-
logger.
|
|
34791
|
+
logger.info(
|
|
34778
34792
|
`${_VesuMultiplyAdapter.name}::_getIncreaseCalldata debtAmount: ${debtAmount}, addedCollateral: ${addedCollateral}`
|
|
34779
34793
|
);
|
|
34780
34794
|
let leverSwap = [];
|
|
@@ -34792,8 +34806,10 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
34792
34806
|
collateralToken.address.address,
|
|
34793
34807
|
params.leverSwap?.exactOutput?.abs()
|
|
34794
34808
|
);
|
|
34809
|
+
logger.info(`VesuMultiplyAdapter::_getIncreaseCalldata amount: ${params.leverSwap?.exactOutput?.abs()}`);
|
|
34795
34810
|
debtAmount = Web3Number.fromWei(swapQuote.total_calculated, debtToken.decimals).abs();
|
|
34796
34811
|
}
|
|
34812
|
+
logger.info(`VesuMultiplyAdapter::_getIncreaseCalldata debtAmount: ${debtAmount}`);
|
|
34797
34813
|
swapQuote = await ekuboQuoter.getQuoteExactInput(
|
|
34798
34814
|
debtToken.address.address,
|
|
34799
34815
|
collateralToken.address.address,
|
|
@@ -34820,7 +34836,7 @@ var VesuMultiplyAdapter = class _VesuMultiplyAdapter extends BaseAdapter {
|
|
|
34820
34836
|
await new Promise((resolve) => setTimeout(resolve, 1e4));
|
|
34821
34837
|
} else {
|
|
34822
34838
|
throw new Error(
|
|
34823
|
-
`VesuMultiplyAdapter: Price impact too high (${swapQuote.price_impact}), skipping swap`
|
|
34839
|
+
`VesuMultiplyAdapter: Price impact too high (${swapQuote.price_impact}), skipping swap, debtAmount=${debtAmount.toNumber()}, collateralPrice=${collateralPrice}, debtPrice=${debtPrice}`
|
|
34824
34840
|
);
|
|
34825
34841
|
}
|
|
34826
34842
|
} catch (error) {
|
|
@@ -35805,6 +35821,9 @@ var VesuModifyPositionAdapter = class _VesuModifyPositionAdapter extends BaseAda
|
|
|
35805
35821
|
// src/strategies/universal-adapters/extended-adapter.ts
|
|
35806
35822
|
import { hash as hash8, uint256 as uint25616 } from "starknet";
|
|
35807
35823
|
|
|
35824
|
+
// src/modules/ExtendedWrapperSDk/wrapper.ts
|
|
35825
|
+
import BigNumber3 from "bignumber.js";
|
|
35826
|
+
|
|
35808
35827
|
// src/modules/ExtendedWrapperSDk/types.ts
|
|
35809
35828
|
var OrderSide = /* @__PURE__ */ ((OrderSide2) => {
|
|
35810
35829
|
OrderSide2["BUY"] = "BUY";
|
|
@@ -35889,8 +35908,60 @@ var AssetOperationStatus = /* @__PURE__ */ ((AssetOperationStatus3) => {
|
|
|
35889
35908
|
})(AssetOperationStatus || {});
|
|
35890
35909
|
|
|
35891
35910
|
// src/modules/ExtendedWrapperSDk/wrapper.ts
|
|
35911
|
+
function asRecord(v) {
|
|
35912
|
+
return v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
35913
|
+
}
|
|
35914
|
+
function pickTradingConfig(row) {
|
|
35915
|
+
const tc = row.trading_config ?? row.tradingConfig;
|
|
35916
|
+
return asRecord(tc);
|
|
35917
|
+
}
|
|
35918
|
+
function readTcString(tc, snake, camel) {
|
|
35919
|
+
const v = tc[snake] ?? tc[camel];
|
|
35920
|
+
if (v === void 0 || v === null) return void 0;
|
|
35921
|
+
return String(v).trim();
|
|
35922
|
+
}
|
|
35923
|
+
function tradingRulesFromMarketRow(marketName, row) {
|
|
35924
|
+
const r = asRecord(row);
|
|
35925
|
+
if (!r) {
|
|
35926
|
+
throw new Error(`ExtendedWrapper: invalid market payload for ${marketName}`);
|
|
35927
|
+
}
|
|
35928
|
+
const tc = pickTradingConfig(r);
|
|
35929
|
+
if (!tc) {
|
|
35930
|
+
throw new Error(`ExtendedWrapper: missing tradingConfig for market ${marketName}`);
|
|
35931
|
+
}
|
|
35932
|
+
const minS = readTcString(tc, "min_order_size", "minOrderSize");
|
|
35933
|
+
const qtyStep = readTcString(tc, "min_order_size_change", "minOrderSizeChange");
|
|
35934
|
+
const pxStep = readTcString(tc, "min_price_change", "minPriceChange");
|
|
35935
|
+
if (!minS || !qtyStep || !pxStep) {
|
|
35936
|
+
throw new Error(`ExtendedWrapper: incomplete tradingConfig for market ${marketName}`);
|
|
35937
|
+
}
|
|
35938
|
+
const minOrderSize = new BigNumber3(minS);
|
|
35939
|
+
const qty = new BigNumber3(qtyStep);
|
|
35940
|
+
const px = new BigNumber3(pxStep);
|
|
35941
|
+
if (!minOrderSize.isFinite() || minOrderSize.lte(0)) {
|
|
35942
|
+
throw new Error(`ExtendedWrapper: invalid minOrderSize for ${marketName}`);
|
|
35943
|
+
}
|
|
35944
|
+
if (!qty.isFinite() || qty.lte(0)) {
|
|
35945
|
+
throw new Error(`ExtendedWrapper: invalid minOrderSizeChange for ${marketName}`);
|
|
35946
|
+
}
|
|
35947
|
+
if (!px.isFinite() || px.lte(0)) {
|
|
35948
|
+
throw new Error(`ExtendedWrapper: invalid minPriceChange for ${marketName}`);
|
|
35949
|
+
}
|
|
35950
|
+
return { minOrderSize, qtyStep: qty, priceStep: px };
|
|
35951
|
+
}
|
|
35952
|
+
function roundToStepBn(value, step) {
|
|
35953
|
+
if (step.lte(0)) return value;
|
|
35954
|
+
return value.div(step).round(0, BigNumber3.ROUND_HALF_UP).times(step);
|
|
35955
|
+
}
|
|
35956
|
+
function formatBnForApi(bn, step) {
|
|
35957
|
+
const dp = Math.max(step.decimalPlaces() ?? 0, bn.decimalPlaces() ?? 0, 0);
|
|
35958
|
+
return Number(bn.toFixed(Math.min(80, dp))).toString();
|
|
35959
|
+
}
|
|
35892
35960
|
var ExtendedWrapper = class {
|
|
35893
35961
|
constructor(config) {
|
|
35962
|
+
/** Per-market rules from GET /markets (tradingConfig); retained for process lifetime (no TTL). */
|
|
35963
|
+
this.marketTradingRulesCache = /* @__PURE__ */ new Map();
|
|
35964
|
+
this.marketTradingRulesInflight = /* @__PURE__ */ new Map();
|
|
35894
35965
|
this.apiKey = config.apiKey;
|
|
35895
35966
|
this.timeout = config.timeout || 3e4;
|
|
35896
35967
|
this.retries = config.retries || 3;
|
|
@@ -35954,13 +36025,75 @@ var ExtendedWrapper = class {
|
|
|
35954
36025
|
}
|
|
35955
36026
|
throw lastError || new Error("Request failed after all retries");
|
|
35956
36027
|
}
|
|
36028
|
+
async resolveTradingRules(marketName) {
|
|
36029
|
+
const cached = this.marketTradingRulesCache.get(marketName);
|
|
36030
|
+
if (cached) return cached;
|
|
36031
|
+
const existing = this.marketTradingRulesInflight.get(marketName);
|
|
36032
|
+
if (existing) return existing;
|
|
36033
|
+
const inflight = (async () => {
|
|
36034
|
+
const res = await this.getMarkets(marketName);
|
|
36035
|
+
if (res.status !== "OK") {
|
|
36036
|
+
throw new Error(
|
|
36037
|
+
`ExtendedWrapper: getMarkets failed for ${marketName}: ${res.message}`
|
|
36038
|
+
);
|
|
36039
|
+
}
|
|
36040
|
+
const rows = res.data;
|
|
36041
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
36042
|
+
throw new Error(
|
|
36043
|
+
`ExtendedWrapper: empty markets response for ${marketName}`
|
|
36044
|
+
);
|
|
36045
|
+
}
|
|
36046
|
+
const row = rows.find((m) => asRecord(m)?.name === marketName);
|
|
36047
|
+
if (!row) {
|
|
36048
|
+
throw new Error(
|
|
36049
|
+
`ExtendedWrapper: market ${marketName} not found in markets list`
|
|
36050
|
+
);
|
|
36051
|
+
}
|
|
36052
|
+
const rules = tradingRulesFromMarketRow(marketName, row);
|
|
36053
|
+
this.marketTradingRulesCache.set(marketName, rules);
|
|
36054
|
+
return rules;
|
|
36055
|
+
})();
|
|
36056
|
+
this.marketTradingRulesInflight.set(marketName, inflight);
|
|
36057
|
+
void inflight.finally(() => {
|
|
36058
|
+
if (this.marketTradingRulesInflight.get(marketName) === inflight) {
|
|
36059
|
+
this.marketTradingRulesInflight.delete(marketName);
|
|
36060
|
+
}
|
|
36061
|
+
});
|
|
36062
|
+
return inflight;
|
|
36063
|
+
}
|
|
35957
36064
|
/**
|
|
35958
36065
|
* Create a new order on Extended Exchange
|
|
35959
36066
|
*/
|
|
35960
36067
|
async createOrder(request) {
|
|
36068
|
+
const rules = await this.resolveTradingRules(request.market_name);
|
|
36069
|
+
const amountBn = new BigNumber3(String(request.amount).trim());
|
|
36070
|
+
const priceBn = new BigNumber3(String(request.price).trim());
|
|
36071
|
+
if (!amountBn.isFinite() || amountBn.lte(0)) {
|
|
36072
|
+
throw new Error(`ExtendedWrapper: invalid order amount=${request.amount}`);
|
|
36073
|
+
}
|
|
36074
|
+
if (!priceBn.isFinite() || priceBn.lte(0)) {
|
|
36075
|
+
throw new Error(`ExtendedWrapper: invalid order price=${request.price}`);
|
|
36076
|
+
}
|
|
36077
|
+
if (amountBn.lt(rules.minOrderSize)) {
|
|
36078
|
+
throw new Error(
|
|
36079
|
+
`ExtendedWrapper: order amount ${request.amount} is below minOrderSize ${rules.minOrderSize.toFixed()} for ${request.market_name}`
|
|
36080
|
+
);
|
|
36081
|
+
}
|
|
36082
|
+
const adjAmount = roundToStepBn(amountBn, rules.qtyStep);
|
|
36083
|
+
const adjPrice = roundToStepBn(priceBn, rules.priceStep);
|
|
36084
|
+
if (adjAmount.lt(rules.minOrderSize)) {
|
|
36085
|
+
throw new Error(
|
|
36086
|
+
`ExtendedWrapper: amount after tick rounding ${formatBnForApi(adjAmount, rules.qtyStep)} is below minOrderSize ${rules.minOrderSize.toFixed()} (${request.market_name})`
|
|
36087
|
+
);
|
|
36088
|
+
}
|
|
36089
|
+
const payload = {
|
|
36090
|
+
...request,
|
|
36091
|
+
amount: formatBnForApi(adjAmount, rules.qtyStep),
|
|
36092
|
+
price: formatBnForApi(adjPrice, rules.priceStep)
|
|
36093
|
+
};
|
|
35961
36094
|
return this.makeRequest("/api/v1/orders", false, {
|
|
35962
36095
|
method: "POST",
|
|
35963
|
-
body: JSON.stringify(
|
|
36096
|
+
body: JSON.stringify(payload)
|
|
35964
36097
|
});
|
|
35965
36098
|
}
|
|
35966
36099
|
/**
|
|
@@ -36570,14 +36703,6 @@ var ExtendedAdapter = class _ExtendedAdapter extends BaseAdapter {
|
|
|
36570
36703
|
logger.error("error initializing client");
|
|
36571
36704
|
return null;
|
|
36572
36705
|
}
|
|
36573
|
-
const setLeverage = await this.setLeverage(
|
|
36574
|
-
leverage,
|
|
36575
|
-
this.config.extendedMarketName
|
|
36576
|
-
);
|
|
36577
|
-
if (!setLeverage) {
|
|
36578
|
-
logger.error("error depositing or setting leverage");
|
|
36579
|
-
return null;
|
|
36580
|
-
}
|
|
36581
36706
|
const { ask, bid } = await this.fetchOrderBookFromExtended();
|
|
36582
36707
|
if (!ask || !bid || ask.lessThanOrEqualTo(0) || bid.lessThanOrEqualTo(0)) {
|
|
36583
36708
|
logger.error(
|
|
@@ -36717,13 +36842,13 @@ var ExtendedAdapter = class _ExtendedAdapter extends BaseAdapter {
|
|
|
36717
36842
|
async createExtendedPositon(client, marketName, amount, price, side) {
|
|
36718
36843
|
try {
|
|
36719
36844
|
const result = side === "SELL" /* SELL */ ? await client.createSellOrder(marketName, amount, price, {
|
|
36720
|
-
|
|
36721
|
-
|
|
36722
|
-
|
|
36845
|
+
post_only: false,
|
|
36846
|
+
reduce_only: false,
|
|
36847
|
+
time_in_force: "IOC" /* IOC */
|
|
36723
36848
|
}) : await client.createBuyOrder(marketName, amount, price, {
|
|
36724
|
-
|
|
36725
|
-
|
|
36726
|
-
|
|
36849
|
+
post_only: false,
|
|
36850
|
+
reduce_only: true,
|
|
36851
|
+
time_in_force: "IOC" /* IOC */
|
|
36727
36852
|
});
|
|
36728
36853
|
if (result.data.id) {
|
|
36729
36854
|
const position_id = result.data.id.toString();
|
|
@@ -42188,20 +42313,42 @@ function mergeDeltas(a, b) {
|
|
|
42188
42313
|
dTransferVesuToExt: a.dTransferVesuToExt + b.dTransferVesuToExt
|
|
42189
42314
|
};
|
|
42190
42315
|
}
|
|
42191
|
-
function
|
|
42316
|
+
function routableDrawAmount(avail, need, minRoutable) {
|
|
42317
|
+
if (avail <= 0 || need <= 0) return 0;
|
|
42318
|
+
const raw = Math.min(avail, need);
|
|
42319
|
+
if (raw <= 0) return 0;
|
|
42320
|
+
if (minRoutable <= 0) return raw;
|
|
42321
|
+
return raw > minRoutable ? raw : 0;
|
|
42322
|
+
}
|
|
42323
|
+
function drawFunds(need, keys, pool, minRoutable) {
|
|
42192
42324
|
const draws = {};
|
|
42193
42325
|
let unmet = need;
|
|
42194
42326
|
for (const k of keys) {
|
|
42195
42327
|
if (unmet <= 0) break;
|
|
42196
42328
|
const avail = pool[k];
|
|
42197
|
-
|
|
42198
|
-
|
|
42329
|
+
const take = routableDrawAmount(avail, unmet, minRoutable);
|
|
42330
|
+
if (take <= 0) continue;
|
|
42199
42331
|
draws[k] = take;
|
|
42200
42332
|
pool[k] -= take;
|
|
42201
42333
|
unmet -= take;
|
|
42202
42334
|
}
|
|
42203
42335
|
return { draws, filled: need - unmet, unmet };
|
|
42204
42336
|
}
|
|
42337
|
+
function drawExtendedAggregated(need, pool, minRoutable) {
|
|
42338
|
+
const draws = {};
|
|
42339
|
+
if (need <= 0) return { draws, filled: 0, unmet: 0 };
|
|
42340
|
+
const totalCap = Math.max(0, pool.extAvlWithdraw) + Math.max(0, pool.extUpnl);
|
|
42341
|
+
const want = routableDrawAmount(totalCap, need, minRoutable);
|
|
42342
|
+
if (want <= 0) return { draws, filled: 0, unmet: need };
|
|
42343
|
+
const fromAvl = Math.min(Math.max(0, pool.extAvlWithdraw), want);
|
|
42344
|
+
pool.extAvlWithdraw -= fromAvl;
|
|
42345
|
+
const fromUpnl = Math.min(Math.max(0, pool.extUpnl), want - fromAvl);
|
|
42346
|
+
pool.extUpnl -= fromUpnl;
|
|
42347
|
+
if (fromAvl > 0) draws.extAvlWithdraw = fromAvl;
|
|
42348
|
+
if (fromUpnl > 0) draws.extUpnl = fromUpnl;
|
|
42349
|
+
const filled = fromAvl + fromUpnl;
|
|
42350
|
+
return { draws, filled, unmet: need - filled };
|
|
42351
|
+
}
|
|
42205
42352
|
function sumKeys(draws, keys) {
|
|
42206
42353
|
return keys.reduce((s, k) => s + (draws[k] ?? 0), 0);
|
|
42207
42354
|
}
|
|
@@ -42212,33 +42359,39 @@ function applyDrawsToDeltas(d, draws, sign) {
|
|
|
42212
42359
|
d.dExtAvlWithdraw += sign * (draws.extAvlWithdraw ?? 0);
|
|
42213
42360
|
d.dExtUpnl += sign * (draws.extUpnl ?? 0);
|
|
42214
42361
|
}
|
|
42215
|
-
function fixExtMargin(ext, price, pool) {
|
|
42362
|
+
function fixExtMargin(ext, price, pool, config) {
|
|
42216
42363
|
const d = emptyDeltas();
|
|
42217
42364
|
const deficit = computeExtDeficit(ext, price);
|
|
42218
42365
|
if (deficit <= 0) return { d, unmet: 0 };
|
|
42219
|
-
const
|
|
42366
|
+
const minR = config.minRoutableUsd ?? 0;
|
|
42367
|
+
const { draws, filled, unmet } = drawFunds(deficit, ["walletUsd", "vaUsd", "vesuBorrowCapacity"], pool, minR);
|
|
42220
42368
|
applyDrawsToDeltas(d, draws, -1);
|
|
42221
42369
|
d.dExtAvlWithdraw += filled;
|
|
42222
42370
|
const fromVesu = draws.vesuBorrowCapacity ?? 0;
|
|
42223
42371
|
if (fromVesu > 0) d.dTransferVesuToExt += fromVesu;
|
|
42224
42372
|
return { d, unmet };
|
|
42225
42373
|
}
|
|
42226
|
-
function fixVesuMargin(vesu, price, pool, hfBuffer) {
|
|
42374
|
+
function fixVesuMargin(vesu, price, pool, hfBuffer, config) {
|
|
42227
42375
|
const d = emptyDeltas();
|
|
42228
42376
|
const hf = computeVesuHF(vesu, price);
|
|
42229
42377
|
if (hf >= vesu.targetHF - hfBuffer) return { d, unmet: 0 };
|
|
42230
42378
|
const repayTokens = computeVesuDebtRepay(vesu, price);
|
|
42231
42379
|
const repayUsd = repayTokens * vesu.debtPrice;
|
|
42232
|
-
const
|
|
42233
|
-
|
|
42380
|
+
const minR = config.minRoutableUsd ?? 0;
|
|
42381
|
+
const rMain = drawFunds(repayUsd, ["vaUsd", "walletUsd"], pool, minR);
|
|
42382
|
+
applyDrawsToDeltas(d, rMain.draws, -1);
|
|
42383
|
+
const rExt = drawExtendedAggregated(rMain.unmet, pool, minR);
|
|
42384
|
+
applyDrawsToDeltas(d, rExt.draws, -1);
|
|
42385
|
+
const filled = rMain.filled + rExt.filled;
|
|
42386
|
+
const unmet = rExt.unmet;
|
|
42234
42387
|
d.dVesuDebt -= filled / vesu.debtPrice;
|
|
42235
|
-
const fromExt = sumKeys(draws, ["extAvlWithdraw", "extUpnl"]);
|
|
42388
|
+
const fromExt = sumKeys(rExt.draws, ["extAvlWithdraw", "extUpnl"]);
|
|
42236
42389
|
if (fromExt > 0) d.dTransferVesuToExt -= fromExt;
|
|
42237
42390
|
return { d, unmet };
|
|
42238
42391
|
}
|
|
42239
42392
|
function phase1(ext, vesu, price, pool, config) {
|
|
42240
|
-
const extResult = fixExtMargin(ext, price, pool);
|
|
42241
|
-
const vesuResult = fixVesuMargin(vesu, price, pool, config.hfBuffer);
|
|
42393
|
+
const extResult = fixExtMargin(ext, price, pool, config);
|
|
42394
|
+
const vesuResult = fixVesuMargin(vesu, price, pool, config.hfBuffer, config);
|
|
42242
42395
|
return {
|
|
42243
42396
|
deltas: mergeDeltas(extResult.d, vesuResult.d),
|
|
42244
42397
|
extDeficitRemaining: extResult.unmet,
|
|
@@ -42278,26 +42431,31 @@ function roundFinalPosition(extPos, vesuPos, rawF, precision) {
|
|
|
42278
42431
|
}
|
|
42279
42432
|
return Math.max(0, Math.min(fFromExt, fFromVesu));
|
|
42280
42433
|
}
|
|
42281
|
-
function phase2(extPos, vesuPos, extEquity, vesuDebt, vesu, extLev, price, pool,
|
|
42434
|
+
function phase2(extPos, vesuPos, extEquity, vesuDebt, vesu, extLev, price, pool, config) {
|
|
42282
42435
|
const d = emptyDeltas();
|
|
42436
|
+
const precision = config.positionPrecision;
|
|
42437
|
+
const minR = config.minRoutableUsd ?? 0;
|
|
42283
42438
|
const targetLTV = computeVesuTargetLTV(vesu);
|
|
42284
42439
|
const imbalance = extPos - vesuPos;
|
|
42285
42440
|
let fundedGrowthBtc = 0;
|
|
42286
42441
|
if (!isNegligible(imbalance, precision)) {
|
|
42287
42442
|
if (imbalance > 0) {
|
|
42288
42443
|
const equityCostUsd = computeVesuGrowthCost(imbalance, vesu, price);
|
|
42289
|
-
const
|
|
42290
|
-
applyDrawsToDeltas(d, draws, -1);
|
|
42444
|
+
const rMain = drawFunds(equityCostUsd, ["vaUsd", "walletUsd"], pool, minR);
|
|
42445
|
+
applyDrawsToDeltas(d, rMain.draws, -1);
|
|
42446
|
+
const rExt = drawExtendedAggregated(rMain.unmet, pool, minR);
|
|
42447
|
+
applyDrawsToDeltas(d, rExt.draws, -1);
|
|
42448
|
+
const filled = rMain.filled + rExt.filled;
|
|
42291
42449
|
const grownBtc = computeVesuGrowthFromEquity(filled, vesu, price);
|
|
42292
42450
|
d.dVesuPosition += grownBtc;
|
|
42293
42451
|
fundedGrowthBtc = grownBtc;
|
|
42294
42452
|
d.dVesuDebt += computeVesuGrowthDebt(grownBtc, vesu, price);
|
|
42295
|
-
const fromExt = sumKeys(draws, ["extAvlWithdraw", "extUpnl"]);
|
|
42453
|
+
const fromExt = sumKeys(rExt.draws, ["extAvlWithdraw", "extUpnl"]);
|
|
42296
42454
|
if (fromExt > 0) d.dTransferVesuToExt -= fromExt;
|
|
42297
42455
|
} else {
|
|
42298
42456
|
const absImbalance = -imbalance;
|
|
42299
42457
|
const marginCostUsd = absImbalance * price / extLev;
|
|
42300
|
-
const { draws, filled } = drawFunds(marginCostUsd, ["walletUsd", "vaUsd", "vesuBorrowCapacity"], pool);
|
|
42458
|
+
const { draws, filled } = drawFunds(marginCostUsd, ["walletUsd", "vaUsd", "vesuBorrowCapacity"], pool, minR);
|
|
42301
42459
|
applyDrawsToDeltas(d, draws, -1);
|
|
42302
42460
|
const grownBtc = filled * extLev / price;
|
|
42303
42461
|
d.dExtPosition += grownBtc;
|
|
@@ -42338,6 +42496,7 @@ function phase2(extPos, vesuPos, extEquity, vesuDebt, vesu, extLev, price, pool,
|
|
|
42338
42496
|
return d;
|
|
42339
42497
|
}
|
|
42340
42498
|
function rebalance(inputs) {
|
|
42499
|
+
logger.info(`ltv-imbalance-rebalance-math::rebalance inputs=${JSON.stringify(inputs)}`);
|
|
42341
42500
|
const { ext, vesu, btcPrice, config } = inputs;
|
|
42342
42501
|
const pool = { ...inputs.funding };
|
|
42343
42502
|
const p1 = phase1(ext, vesu, btcPrice, pool, config);
|
|
@@ -42354,7 +42513,7 @@ function rebalance(inputs) {
|
|
|
42354
42513
|
ext.leverage,
|
|
42355
42514
|
btcPrice,
|
|
42356
42515
|
pool,
|
|
42357
|
-
config
|
|
42516
|
+
config
|
|
42358
42517
|
);
|
|
42359
42518
|
return mergeDeltas(p1.deltas, p2);
|
|
42360
42519
|
}
|
|
@@ -42935,15 +43094,15 @@ var SolveBudget = class {
|
|
|
42935
43094
|
return this.vesuPoolStates[0];
|
|
42936
43095
|
}
|
|
42937
43096
|
// ── Derived getters (buffered where applicable) ─────────────────────
|
|
42938
|
-
/** Buffered VA USD:
|
|
43097
|
+
/** Buffered VA USD: non-stable asset slot (if any) + USDC slot. */
|
|
42939
43098
|
get vaUsd() {
|
|
42940
|
-
return this.bufferedTokenUsd(this.
|
|
43099
|
+
return this.bufferedTokenUsd(this.vaultUsdcBalance);
|
|
42941
43100
|
}
|
|
42942
43101
|
/** Buffered USD in VA strategy-asset bucket only. */
|
|
42943
43102
|
get vaAssetUsd() {
|
|
42944
43103
|
return this.bufferedTokenUsd(this.vaultAssetBalance);
|
|
42945
43104
|
}
|
|
42946
|
-
/** Buffered USD in VA USDC bucket (
|
|
43105
|
+
/** Buffered USD in VA USDC bucket (includes full VA idle when asset === USDC). */
|
|
42947
43106
|
get vaUsdcUsd() {
|
|
42948
43107
|
return this.bufferedTokenUsd(this.vaultUsdcBalance);
|
|
42949
43108
|
}
|
|
@@ -43025,9 +43184,13 @@ var SolveBudget = class {
|
|
|
43025
43184
|
get vesuRebalanceFlags() {
|
|
43026
43185
|
return this.shouldVesuRebalance;
|
|
43027
43186
|
}
|
|
43028
|
-
/** Raw USD in VA (USDC slot + asset slot); spend caps when executing transfers. */
|
|
43187
|
+
/** Raw USD in VA (USDC slot + non-stable asset slot when distinct); spend caps when executing transfers. */
|
|
43029
43188
|
_vaRawUsd() {
|
|
43030
|
-
return this._rawTokenUsd(this.vaultUsdcBalance)
|
|
43189
|
+
return this._rawTokenUsd(this.vaultUsdcBalance);
|
|
43190
|
+
}
|
|
43191
|
+
/** VA liquidity usable for repay / {@link spendVaRawUsd} (matches nominal balances after any {@link applyBuffer} scaling). */
|
|
43192
|
+
get vaRawUsd() {
|
|
43193
|
+
return this._vaRawUsd();
|
|
43031
43194
|
}
|
|
43032
43195
|
_walletRawUsd() {
|
|
43033
43196
|
return this._rawTokenUsd(this.walletBalance);
|
|
@@ -43103,7 +43266,7 @@ var SolveBudget = class {
|
|
|
43103
43266
|
rem -= fromUsdc;
|
|
43104
43267
|
}
|
|
43105
43268
|
if (rem > 0 && this.vaultAssetBalance) {
|
|
43106
|
-
|
|
43269
|
+
throw new Error(`Not implemented: spendVA with vaultAssetBalance`);
|
|
43107
43270
|
}
|
|
43108
43271
|
this._recomputeUnusedBalance();
|
|
43109
43272
|
logger.debug(`SolveBudget::spendVA usedRaw=${usedRaw}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
|
|
@@ -43113,7 +43276,10 @@ var SolveBudget = class {
|
|
|
43113
43276
|
* Spend nominal/raw USD from VA (e.g. Vesu repay, on-chain USDC). Does not apply the safety buffer to the cap.
|
|
43114
43277
|
*/
|
|
43115
43278
|
spendVaRawUsd(rawUsdDesired) {
|
|
43116
|
-
const capRaw =
|
|
43279
|
+
const capRaw = (
|
|
43280
|
+
// this._rawTokenUsd(this.vaultUsdcBalance) + this._rawTokenUsd(this.vaultAssetBalance);
|
|
43281
|
+
this._rawTokenUsd(this.vaultUsdcBalance)
|
|
43282
|
+
);
|
|
43117
43283
|
const usedRaw = Math.min(capRaw, Math.max(0, rawUsdDesired));
|
|
43118
43284
|
if (usedRaw <= CASE_THRESHOLD_USD) return 0;
|
|
43119
43285
|
let rem = usedRaw;
|
|
@@ -43123,7 +43289,7 @@ var SolveBudget = class {
|
|
|
43123
43289
|
rem -= fromUsdc;
|
|
43124
43290
|
}
|
|
43125
43291
|
if (rem > 0 && this.vaultAssetBalance) {
|
|
43126
|
-
|
|
43292
|
+
throw new Error(`Not implemented: spendVaRawUsd with vaultAssetBalance`);
|
|
43127
43293
|
}
|
|
43128
43294
|
this._recomputeUnusedBalance();
|
|
43129
43295
|
logger.debug(`SolveBudget::spendVaRawUsd usedRaw=${usedRaw}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
|
|
@@ -43138,7 +43304,7 @@ var SolveBudget = class {
|
|
|
43138
43304
|
if (this.vaultUsdcBalance) {
|
|
43139
43305
|
this._addUsdToTokenBalance(this.vaultUsdcBalance, rawUsd);
|
|
43140
43306
|
} else if (this.vaultAssetBalance) {
|
|
43141
|
-
|
|
43307
|
+
throw new Error(`Not implemented: addToVA with vaultAssetBalance`);
|
|
43142
43308
|
}
|
|
43143
43309
|
this._recomputeUnusedBalance();
|
|
43144
43310
|
logger.debug(`SolveBudget::addToVA rawUsd=${rawUsd}, vaUsd=${this.vaUsd}, totalUnused=${this.totalUnused}`);
|
|
@@ -43177,7 +43343,7 @@ var SolveBudget = class {
|
|
|
43177
43343
|
if (isSpend) {
|
|
43178
43344
|
const capRaw = this.extendedBalance?.availableForWithdrawal?.toNumber() ?? 0;
|
|
43179
43345
|
const useRaw = Math.min(capRaw, desiredRaw);
|
|
43180
|
-
if (useRaw <=
|
|
43346
|
+
if (useRaw <= 0) return 0;
|
|
43181
43347
|
rawDelta = -useRaw;
|
|
43182
43348
|
} else {
|
|
43183
43349
|
rawDelta = desiredRaw;
|
|
@@ -43197,7 +43363,7 @@ var SolveBudget = class {
|
|
|
43197
43363
|
if (isSpend) {
|
|
43198
43364
|
const capRaw = this.extendedBalance?.unrealisedPnl?.toNumber() ?? 0;
|
|
43199
43365
|
const useRaw = Math.min(capRaw, desiredRaw);
|
|
43200
|
-
if (useRaw <=
|
|
43366
|
+
if (useRaw <= 0) return 0;
|
|
43201
43367
|
rawDelta = -useRaw;
|
|
43202
43368
|
} else {
|
|
43203
43369
|
rawDelta = desiredRaw;
|
|
@@ -43212,10 +43378,12 @@ var SolveBudget = class {
|
|
|
43212
43378
|
return rawDelta;
|
|
43213
43379
|
}
|
|
43214
43380
|
spendExtAvailTrade(rawDesired) {
|
|
43215
|
-
const
|
|
43216
|
-
const
|
|
43217
|
-
|
|
43218
|
-
|
|
43381
|
+
const usedWd = this._updateExtAvailWithdraw(rawDesired, true);
|
|
43382
|
+
const tookWd = Math.abs(usedWd);
|
|
43383
|
+
const rem = rawDesired - tookWd;
|
|
43384
|
+
const usedUpnl = rem > 0 ? this._updateExtAvailUpnl(rem, true) : 0;
|
|
43385
|
+
logger.debug(`SolveBudget::updateExtAvailTrade rawSum=${usedWd + usedUpnl}, extAvailTrade=${this.extAvailTrade}, totalUnused=${this.totalUnused}`);
|
|
43386
|
+
return usedWd + usedUpnl;
|
|
43219
43387
|
}
|
|
43220
43388
|
// simply reduces available amounts, but maintains equity and balance.
|
|
43221
43389
|
spendExtAvailTradeToEquityOnly(rawDesired) {
|
|
@@ -43535,7 +43703,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43535
43703
|
this._fetchExtendedPositions()
|
|
43536
43704
|
]);
|
|
43537
43705
|
logger.verbose(
|
|
43538
|
-
`${this._tag}::_refresh VA asset ${vaultAssetBalance.token.symbol}=$${vaultAssetBalance.usdValue.toFixed(2)}
|
|
43706
|
+
`${this._tag}::_refresh ${vaultAssetBalance ? `VA asset ${vaultAssetBalance.token.symbol}=$${vaultAssetBalance.usdValue.toFixed(2)}, ` : ""}VA USDC=${vaultUsdcBalance.usdValue.toFixed(2)}, wallet=${walletBalance.usdValue}`
|
|
43539
43707
|
);
|
|
43540
43708
|
const unusedBalance = this._computeUnusedBalances(
|
|
43541
43709
|
vaultAssetBalance,
|
|
@@ -43560,23 +43728,28 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43560
43728
|
},
|
|
43561
43729
|
vesuPoolStates
|
|
43562
43730
|
});
|
|
43731
|
+
this._budget.logStateSummary();
|
|
43563
43732
|
const totalUnusedUsd = unusedBalance.reduce(
|
|
43564
43733
|
(acc, b) => acc + b.usdValue,
|
|
43565
43734
|
0
|
|
43566
43735
|
);
|
|
43567
43736
|
logger.info(
|
|
43568
|
-
`${this._tag}::_refresh completed \u2014 unusedBalances: ${unusedBalance.length} tokens, totalUnusedUsd: ${totalUnusedUsd.toFixed(2)}, extendedPositions: ${extendedPositions.length}, vesuPools: ${vesuPoolStates.length}
|
|
43737
|
+
`${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()}`
|
|
43569
43738
|
);
|
|
43570
43739
|
}
|
|
43571
43740
|
// todo add communication check with python server of extended. if not working, throw error in solve function.
|
|
43572
|
-
/** True when strategy asset and USDC share one token — VA
|
|
43741
|
+
/** True when strategy asset and USDC share one token — VA idle balance is tracked as USDC, not as asset. */
|
|
43573
43742
|
_vaultAssetAndUsdcAreSameToken() {
|
|
43574
43743
|
return this._config.assetToken.address.eq(this._config.usdcToken.address);
|
|
43575
43744
|
}
|
|
43576
43745
|
/**
|
|
43577
|
-
* Reads
|
|
43746
|
+
* Reads idle {@link StateManagerConfig.assetToken} in the vault allocator when it differs from USDC.
|
|
43747
|
+
* When asset and USDC are the same token, returns null (that balance is reported via {@link _fetchVaultAllocatorUsdcBalanceIfDistinct} only).
|
|
43578
43748
|
*/
|
|
43579
43749
|
async _fetchVaultAllocatorAssetBalance() {
|
|
43750
|
+
if (this._vaultAssetAndUsdcAreSameToken()) {
|
|
43751
|
+
return null;
|
|
43752
|
+
}
|
|
43580
43753
|
const { assetToken, vaultAllocator, networkConfig, pricer } = this._config;
|
|
43581
43754
|
const balance = await new ERC20(networkConfig).balanceOf(
|
|
43582
43755
|
assetToken.address,
|
|
@@ -43588,11 +43761,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43588
43761
|
return { token: assetToken, amount: balance, usdValue };
|
|
43589
43762
|
}
|
|
43590
43763
|
/**
|
|
43591
|
-
* Reads {@link StateManagerConfig.usdcToken} in the vault allocator
|
|
43592
|
-
* {@link StateManagerConfig.assetToken}. Otherwise returns null (treat VA USDC as 0; stablecoin is only under asset).
|
|
43764
|
+
* Reads {@link StateManagerConfig.usdcToken} idle in the vault allocator (always — distinct asset or USDC-as-asset).
|
|
43593
43765
|
*/
|
|
43594
43766
|
async _fetchVaultAllocatorUsdcBalanceIfDistinct() {
|
|
43595
|
-
if (this._vaultAssetAndUsdcAreSameToken()) return null;
|
|
43596
43767
|
const { usdcToken, vaultAllocator, networkConfig, pricer } = this._config;
|
|
43597
43768
|
const balance = await new ERC20(networkConfig).balanceOf(
|
|
43598
43769
|
usdcToken.address,
|
|
@@ -43606,7 +43777,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43606
43777
|
return { token: usdcToken, amount: balance, usdValue };
|
|
43607
43778
|
}
|
|
43608
43779
|
/**
|
|
43609
|
-
* Merges vault-allocator asset,
|
|
43780
|
+
* Merges vault-allocator asset (if any), vault-allocator USDC, and operator wallet
|
|
43610
43781
|
* balances into entries keyed by token address.
|
|
43611
43782
|
*/
|
|
43612
43783
|
_computeUnusedBalances(vaultAssetBalance, vaultUsdcBalance, walletBalance) {
|
|
@@ -43618,8 +43789,8 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
43618
43789
|
usdValue: tb.usdValue
|
|
43619
43790
|
});
|
|
43620
43791
|
};
|
|
43621
|
-
put(vaultAssetBalance);
|
|
43622
|
-
|
|
43792
|
+
if (vaultAssetBalance) put(vaultAssetBalance);
|
|
43793
|
+
put(vaultUsdcBalance);
|
|
43623
43794
|
const key = walletBalance.token.address.toString();
|
|
43624
43795
|
const existing = balanceMap.get(key);
|
|
43625
43796
|
if (existing) {
|
|
@@ -44138,7 +44309,10 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44138
44309
|
// ── ExecutionRoute-building helpers ─────────────────────────────────────────────
|
|
44139
44310
|
// ── Atomic route builders ────────────────────────────────────────────
|
|
44140
44311
|
_buildVesuRepayRoutes(totalUsd, routes) {
|
|
44141
|
-
const
|
|
44312
|
+
const vaCap = this._budget.vaRawUsd;
|
|
44313
|
+
const repayUsd = Math.min(totalUsd, vaCap);
|
|
44314
|
+
if (repayUsd <= CASE_THRESHOLD_USD) return;
|
|
44315
|
+
const { used, spendsByPool } = this._budget.repayVesuBorrowCapacity(repayUsd);
|
|
44142
44316
|
for (const route of spendsByPool) {
|
|
44143
44317
|
routes.push({ type: "VESU_REPAY" /* VESU_REPAY */, ...route, priority: routes.length });
|
|
44144
44318
|
}
|
|
@@ -44232,7 +44406,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44232
44406
|
return { routes, remaining: tryAmount };
|
|
44233
44407
|
}
|
|
44234
44408
|
_getVAToEXTENDEDRoute(tryAmount, routes, shouldAddWaitRoute = true) {
|
|
44235
|
-
const usable = Math.min(tryAmount, this._budget.
|
|
44409
|
+
const usable = Math.min(tryAmount, this._budget.vaUsdcUsd);
|
|
44236
44410
|
if (usable > CASE_THRESHOLD_USD) {
|
|
44237
44411
|
const vaUsed = this._budget.spendVA(usable);
|
|
44238
44412
|
this._budget.addToExtAvailTrade(vaUsed);
|
|
@@ -44247,7 +44421,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44247
44421
|
}
|
|
44248
44422
|
_getExtendedToWalletRoute(tryAmount, routes, shouldAddWaitRoute = true) {
|
|
44249
44423
|
if (tryAmount <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
|
|
44250
|
-
const rawCap = this._budget.extAvailWithdraw + this._budget.extAvailUpnl;
|
|
44424
|
+
const rawCap = this._budget.extAvailWithdraw + Math.max(0, this._budget.extAvailUpnl);
|
|
44251
44425
|
const rawSpend = Math.min(tryAmount, rawCap);
|
|
44252
44426
|
if (rawSpend <= CASE_THRESHOLD_USD) return { routes, remaining: tryAmount };
|
|
44253
44427
|
const rawOut = this._budget.spendExtAvailTrade(rawSpend);
|
|
@@ -44437,7 +44611,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44437
44611
|
* Design: accumulate all ext-to-wallet moves, add transfer routes at the end (principle #3).
|
|
44438
44612
|
*/
|
|
44439
44613
|
/**
|
|
44440
|
-
* Unified LTV classifier
|
|
44614
|
+
* Unified LTV / exposure classifier: `rebalance()` drives both LTV (debt, margin,
|
|
44615
|
+
* funding) and Vesu↔Extended position alignment. There is no separate imbalance pass.
|
|
44616
|
+
* Computes both Vesu repay and Extended margin needs,
|
|
44441
44617
|
* then builds all routes in a single pass with no duplicate transfers.
|
|
44442
44618
|
*
|
|
44443
44619
|
* Vesu repay priority: VA > Wallet > ExtAvl > ExtUpnl
|
|
@@ -44493,7 +44669,8 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44493
44669
|
},
|
|
44494
44670
|
config: {
|
|
44495
44671
|
positionPrecision: COLLATERAL_PRECISION,
|
|
44496
|
-
hfBuffer: 0.05
|
|
44672
|
+
hfBuffer: 0.05,
|
|
44673
|
+
minRoutableUsd: CASE_THRESHOLD_USD
|
|
44497
44674
|
}
|
|
44498
44675
|
};
|
|
44499
44676
|
}
|
|
@@ -44504,8 +44681,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44504
44681
|
}
|
|
44505
44682
|
/**
|
|
44506
44683
|
* Turn pure rebalance() deltas into execution routes.
|
|
44507
|
-
* Order: Vesu multiply
|
|
44508
|
-
* (REALISE_PNL, EXTENDED_TO_WALLET + WAIT, WALLET_TO_VA, VESU_BORROW, VESU_REPAY, VA_TO_EXTENDED)
|
|
44684
|
+
* Order: Vesu multiply decrease → Extended decrease → aggregated transfers
|
|
44685
|
+
* (REALISE_PNL, EXTENDED_TO_WALLET + WAIT, WALLET_TO_VA, VESU_BORROW, VESU_REPAY, VA_TO_EXTENDED),
|
|
44686
|
+
* then Vesu multiply increase and Extended increase (need VA / Extended funded first).
|
|
44509
44687
|
*/
|
|
44510
44688
|
_buildLtvRoutesFromRebalanceDeltas(d) {
|
|
44511
44689
|
const routes = [];
|
|
@@ -44517,6 +44695,7 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44517
44695
|
const targetLtv = VesuConfig.targetLtv;
|
|
44518
44696
|
const btcEps = 10 ** -COLLATERAL_PRECISION;
|
|
44519
44697
|
let multiplyDebtRepayUsd = 0;
|
|
44698
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas d=${JSON.stringify(d)}`);
|
|
44520
44699
|
if (d.dVesuPosition < -btcEps) {
|
|
44521
44700
|
const xBtc = -d.dVesuPosition;
|
|
44522
44701
|
const transferUsdFromVesu = Math.max(0, d.dTransferVesuToExt);
|
|
@@ -44535,12 +44714,28 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44535
44714
|
if (d.dVesuDebt < 0) {
|
|
44536
44715
|
const needRepayUsd = -d.dVesuDebt * debtPrice;
|
|
44537
44716
|
const multiplyRepayUsd = Math.min(needRepayUsd, swapLegMaxRepayUsd);
|
|
44717
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas ${JSON.stringify({
|
|
44718
|
+
needRepayUsd,
|
|
44719
|
+
multiplyRepayUsd
|
|
44720
|
+
})}`);
|
|
44538
44721
|
debtTokenDelta = -(multiplyRepayUsd / debtPrice);
|
|
44539
44722
|
} else {
|
|
44540
44723
|
debtTokenDelta = -debtUsdFallback;
|
|
44541
44724
|
}
|
|
44542
44725
|
const debtAmtW3 = new Web3Number(debtTokenDelta.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
|
|
44543
44726
|
multiplyDebtRepayUsd = Math.abs(debtTokenDelta) * debtPrice;
|
|
44727
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas ${JSON.stringify({
|
|
44728
|
+
debtTokenDelta,
|
|
44729
|
+
debtUsdFallback,
|
|
44730
|
+
swapLegMaxRepayUsd,
|
|
44731
|
+
xBtc,
|
|
44732
|
+
marginBtc,
|
|
44733
|
+
swappedBtc,
|
|
44734
|
+
transferUsdFromVesu,
|
|
44735
|
+
debtPrice,
|
|
44736
|
+
targetLtv,
|
|
44737
|
+
multiplyDebtRepayUsd
|
|
44738
|
+
})}`);
|
|
44544
44739
|
routes.push({
|
|
44545
44740
|
type: "VESU_MULTIPLY_DECREASE_LEVER" /* VESU_MULTIPLY_DECREASE_LEVER */,
|
|
44546
44741
|
poolId: vesuAdapter.config.poolId,
|
|
@@ -44561,19 +44756,68 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44561
44756
|
if (transferUsdFromVesu > CASE_THRESHOLD_USD) {
|
|
44562
44757
|
this._budget.addToVA(transferUsdFromVesu);
|
|
44563
44758
|
}
|
|
44564
|
-
}
|
|
44759
|
+
}
|
|
44760
|
+
if (d.dExtPosition < -btcEps) {
|
|
44761
|
+
const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
|
|
44762
|
+
routes.push({
|
|
44763
|
+
type: "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
|
|
44764
|
+
amount: amt,
|
|
44765
|
+
instrument,
|
|
44766
|
+
priority: routes.length
|
|
44767
|
+
});
|
|
44768
|
+
this._budget.applyExtendedExposureDelta(instrument, amt, price);
|
|
44769
|
+
}
|
|
44770
|
+
const negUpnl = Math.min(0, d.dExtUpnl);
|
|
44771
|
+
const negExtAvl = Math.min(0, d.dExtAvlWithdraw);
|
|
44772
|
+
let hadExtendedOut = false;
|
|
44773
|
+
if (negUpnl < -CASE_THRESHOLD_USD) {
|
|
44774
|
+
this._getUpnlRoute(Math.abs(negUpnl), routes);
|
|
44775
|
+
hadExtendedOut = true;
|
|
44776
|
+
}
|
|
44777
|
+
const extToWalletUsd = (negExtAvl < -CASE_THRESHOLD_USD ? Math.abs(negExtAvl) : 0) + (negUpnl < -CASE_THRESHOLD_USD ? Math.abs(negUpnl) : 0);
|
|
44778
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas extToWalletUsd=${extToWalletUsd}, negExtAvl=${negExtAvl}, negUpnl=${negUpnl}`);
|
|
44779
|
+
if (extToWalletUsd > CASE_THRESHOLD_USD) {
|
|
44780
|
+
this._getExtendedToWalletRoute(extToWalletUsd, routes);
|
|
44781
|
+
hadExtendedOut = true;
|
|
44782
|
+
} else {
|
|
44783
|
+
if (d.dVesuDebt < 0) {
|
|
44784
|
+
d.dVesuDebt += (negExtAvl < 0 && negExtAvl > -CASE_THRESHOLD_USD ? Math.abs(negExtAvl) : 0) + (negUpnl < 0 && negUpnl > -CASE_THRESHOLD_USD ? Math.abs(negUpnl) : 0);
|
|
44785
|
+
}
|
|
44786
|
+
}
|
|
44787
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas d=${JSON.stringify(d)}`);
|
|
44788
|
+
const walletPull = Math.abs(Math.min(0, d.dWalletUsd));
|
|
44789
|
+
const walletToVaUsd = walletPull + extToWalletUsd;
|
|
44790
|
+
if (walletToVaUsd > CASE_THRESHOLD_USD) {
|
|
44791
|
+
this._getWALLETToVARoute(walletToVaUsd, routes);
|
|
44792
|
+
}
|
|
44793
|
+
if (d.dVesuBorrowCapacity < -CASE_THRESHOLD_USD) {
|
|
44794
|
+
this._buildVesuBorrowRoutes(Math.abs(d.dVesuBorrowCapacity), routes);
|
|
44795
|
+
}
|
|
44796
|
+
const totalDebtRepayUsd = d.dVesuDebt < 0 ? -d.dVesuDebt * debtPrice : 0;
|
|
44797
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas totalDebtRepayUsd=${totalDebtRepayUsd}`);
|
|
44798
|
+
let standaloneRepayUsd = Math.max(0, totalDebtRepayUsd - multiplyDebtRepayUsd);
|
|
44799
|
+
logger.info(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas standaloneRepayUsd=${standaloneRepayUsd}`);
|
|
44800
|
+
if (standaloneRepayUsd > this._budget.vaRawUsd) {
|
|
44801
|
+
if (Math.abs(standaloneRepayUsd - this._budget.vaRawUsd) < CASE_THRESHOLD_USD) {
|
|
44802
|
+
standaloneRepayUsd = this._budget.vaRawUsd;
|
|
44803
|
+
} else {
|
|
44804
|
+
throw new Error(`${this._tag}::_buildLtvRoutesFromRebalanceDeltas standaloneRepayUsd=${standaloneRepayUsd} > vaRawUsd=${this._budget.vaRawUsd}`);
|
|
44805
|
+
}
|
|
44806
|
+
}
|
|
44807
|
+
if (standaloneRepayUsd > CASE_THRESHOLD_USD) {
|
|
44808
|
+
this._buildVesuRepayRoutes(standaloneRepayUsd, routes);
|
|
44809
|
+
}
|
|
44810
|
+
const posExtEq = Math.max(0, d.dExtAvlWithdraw);
|
|
44811
|
+
const vaToExtUsd = posExtEq > CASE_THRESHOLD_USD ? posExtEq : 0;
|
|
44812
|
+
if (vaToExtUsd > CASE_THRESHOLD_USD) {
|
|
44813
|
+
this._getVAToEXTENDEDRoute(vaToExtUsd, routes, hadExtendedOut);
|
|
44814
|
+
}
|
|
44815
|
+
if (d.dVesuPosition > btcEps) {
|
|
44565
44816
|
const vesuDepositAmount = new Web3Number(
|
|
44566
44817
|
(d.dVesuPosition * price * (1 - targetLtv)).toFixed(USDC_TOKEN_DECIMALS),
|
|
44567
44818
|
USDC_TOKEN_DECIMALS
|
|
44568
44819
|
);
|
|
44569
44820
|
if (vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
|
|
44570
|
-
routes.push({
|
|
44571
|
-
type: "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
|
|
44572
|
-
priority: routes.length,
|
|
44573
|
-
fromToken: vesuAdapter.config.collateral.symbol,
|
|
44574
|
-
fromAmount: vesuDepositAmount,
|
|
44575
|
-
toToken: vesuAdapter.config.debt.symbol
|
|
44576
|
-
});
|
|
44577
44821
|
}
|
|
44578
44822
|
const collateralDelta = new Web3Number(
|
|
44579
44823
|
d.dVesuPosition.toFixed(COLLATERAL_PRECISION),
|
|
@@ -44584,8 +44828,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44584
44828
|
new Web3Number(Math.min(availableBorrowCapacity, vesuDepositAmount.toNumber()).toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS)
|
|
44585
44829
|
);
|
|
44586
44830
|
const collPx = pool.collateralPrice || 1;
|
|
44831
|
+
const marginUsdAmount = externalDepositAmount.toNumber() * (pool.debtPrice ?? 1);
|
|
44587
44832
|
const swappedAmount = new Web3Number(
|
|
44588
|
-
(
|
|
44833
|
+
(marginUsdAmount / collPx).toFixed(6),
|
|
44589
44834
|
vesuAdapter.config.collateral.decimals
|
|
44590
44835
|
);
|
|
44591
44836
|
const debtDeltaTokens = new Web3Number(
|
|
@@ -44609,17 +44854,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44609
44854
|
collateralDelta,
|
|
44610
44855
|
debtDeltaTokens
|
|
44611
44856
|
);
|
|
44857
|
+
this._budget.spendVA(marginUsdAmount);
|
|
44612
44858
|
}
|
|
44613
|
-
if (d.dExtPosition
|
|
44614
|
-
const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
|
|
44615
|
-
routes.push({
|
|
44616
|
-
type: "EXTENDED_DECREASE_LEVER" /* EXTENDED_DECREASE_LEVER */,
|
|
44617
|
-
amount: amt,
|
|
44618
|
-
instrument,
|
|
44619
|
-
priority: routes.length
|
|
44620
|
-
});
|
|
44621
|
-
this._budget.applyExtendedExposureDelta(instrument, amt, price);
|
|
44622
|
-
} else if (d.dExtPosition > btcEps) {
|
|
44859
|
+
if (d.dExtPosition > btcEps) {
|
|
44623
44860
|
const amt = new Web3Number(d.dExtPosition.toFixed(COLLATERAL_PRECISION), 8);
|
|
44624
44861
|
routes.push({
|
|
44625
44862
|
type: "EXTENDED_INCREASE_LEVER" /* EXTENDED_INCREASE_LEVER */,
|
|
@@ -44629,36 +44866,6 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44629
44866
|
});
|
|
44630
44867
|
this._budget.applyExtendedExposureDelta(instrument, amt, price);
|
|
44631
44868
|
}
|
|
44632
|
-
const negUpnl = Math.min(0, d.dExtUpnl);
|
|
44633
|
-
const negExtAvl = Math.min(0, d.dExtAvlWithdraw);
|
|
44634
|
-
let hadExtendedOut = false;
|
|
44635
|
-
if (negUpnl < -CASE_THRESHOLD_USD) {
|
|
44636
|
-
this._getUpnlRoute(Math.abs(negUpnl), routes);
|
|
44637
|
-
hadExtendedOut = true;
|
|
44638
|
-
}
|
|
44639
|
-
const extToWalletUsd = (negExtAvl < -CASE_THRESHOLD_USD ? Math.abs(negExtAvl) : 0) + (negUpnl < -CASE_THRESHOLD_USD ? Math.abs(negUpnl) : 0);
|
|
44640
|
-
if (extToWalletUsd > CASE_THRESHOLD_USD) {
|
|
44641
|
-
this._getExtendedToWalletRoute(extToWalletUsd, routes);
|
|
44642
|
-
hadExtendedOut = true;
|
|
44643
|
-
}
|
|
44644
|
-
const walletPull = Math.abs(Math.min(0, d.dWalletUsd));
|
|
44645
|
-
const walletToVaUsd = walletPull + extToWalletUsd;
|
|
44646
|
-
if (walletToVaUsd > CASE_THRESHOLD_USD) {
|
|
44647
|
-
this._getWALLETToVARoute(walletToVaUsd, routes);
|
|
44648
|
-
}
|
|
44649
|
-
if (d.dVesuBorrowCapacity < -CASE_THRESHOLD_USD) {
|
|
44650
|
-
this._buildVesuBorrowRoutes(Math.abs(d.dVesuBorrowCapacity), routes);
|
|
44651
|
-
}
|
|
44652
|
-
const totalDebtRepayUsd = d.dVesuDebt < 0 ? -d.dVesuDebt * debtPrice : 0;
|
|
44653
|
-
const standaloneRepayUsd = Math.max(0, totalDebtRepayUsd - multiplyDebtRepayUsd);
|
|
44654
|
-
if (standaloneRepayUsd > CASE_THRESHOLD_USD) {
|
|
44655
|
-
this._buildVesuRepayRoutes(standaloneRepayUsd, routes);
|
|
44656
|
-
}
|
|
44657
|
-
const posExtEq = Math.max(0, d.dExtAvlWithdraw);
|
|
44658
|
-
const vaToExtUsd = posExtEq > CASE_THRESHOLD_USD ? posExtEq : 0;
|
|
44659
|
-
if (vaToExtUsd > CASE_THRESHOLD_USD) {
|
|
44660
|
-
this._getVAToEXTENDEDRoute(vaToExtUsd, routes, hadExtendedOut);
|
|
44661
|
-
}
|
|
44662
44869
|
return routes;
|
|
44663
44870
|
}
|
|
44664
44871
|
// ── LTV Vesu route builders ───────────────────────────────────────────
|
|
@@ -44817,48 +45024,76 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44817
45024
|
const extendedTarget = total / (1 + extendedLeverage / vesuLeverage);
|
|
44818
45025
|
const extendedInitial = extAvlWithdraw + extUpnl;
|
|
44819
45026
|
let delta = extendedTarget - extendedInitial;
|
|
44820
|
-
let dExtAvlWithdraw = 0, dExtUpnl = 0, dVaUsd = 0, dWalletUsd = 0, dVesuBorrowCapacity = 0;
|
|
45027
|
+
let dExtAvlWithdraw = 0, dExtUpnl = 0, dVaUsd = 0, dWalletUsd = 0, dVesuBorrowCapacity = 0, finalVaUsd = vaUsd, finalExtended = extAvlWithdraw + Math.max(extUpnl, 0);
|
|
44821
45028
|
if (delta > 0) {
|
|
44822
45029
|
let need = delta;
|
|
44823
|
-
const
|
|
45030
|
+
const eps = CASE_THRESHOLD_USD;
|
|
45031
|
+
const takeWalletUsd = routableDrawAmount(walletUsd, need, eps);
|
|
44824
45032
|
dWalletUsd -= takeWalletUsd;
|
|
44825
45033
|
need -= takeWalletUsd;
|
|
44826
|
-
const takeVaUsd =
|
|
45034
|
+
const takeVaUsd = routableDrawAmount(vaUsd, need, eps);
|
|
44827
45035
|
dVaUsd -= takeVaUsd;
|
|
44828
45036
|
need -= takeVaUsd;
|
|
44829
|
-
|
|
45037
|
+
finalVaUsd -= takeVaUsd;
|
|
45038
|
+
finalVaUsd += walletUsd - takeWalletUsd;
|
|
45039
|
+
const takeVesuBorrowCapacity = routableDrawAmount(vesuBorrowCapacity, need, eps);
|
|
44830
45040
|
dVesuBorrowCapacity -= takeVesuBorrowCapacity;
|
|
44831
45041
|
need -= takeVesuBorrowCapacity;
|
|
45042
|
+
finalVaUsd += vesuBorrowCapacity - takeVesuBorrowCapacity;
|
|
44832
45043
|
const received = delta - need;
|
|
44833
45044
|
const eco1Sum = extAvlWithdraw + extUpnl;
|
|
45045
|
+
finalExtended += received;
|
|
44834
45046
|
if (eco1Sum >= 0) {
|
|
44835
45047
|
dExtAvlWithdraw += received;
|
|
44836
45048
|
} else {
|
|
44837
|
-
|
|
45049
|
+
const hole = -eco1Sum;
|
|
45050
|
+
const fillUpnl = Math.min(received, hole);
|
|
45051
|
+
dExtUpnl += fillUpnl;
|
|
45052
|
+
dExtAvlWithdraw += received - fillUpnl;
|
|
45053
|
+
finalExtended -= fillUpnl;
|
|
44838
45054
|
}
|
|
44839
|
-
if (need >
|
|
45055
|
+
if (need > CASE_THRESHOLD_USD) {
|
|
44840
45056
|
throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
|
|
44841
45057
|
}
|
|
44842
45058
|
} else if (delta < 0) {
|
|
44843
45059
|
let need = -delta;
|
|
44844
|
-
const takeExtAvlWithdraw = Math.min(extAvlWithdraw, need);
|
|
45060
|
+
const takeExtAvlWithdraw = Math.min(Math.max(extAvlWithdraw, 0), need);
|
|
44845
45061
|
dExtAvlWithdraw -= takeExtAvlWithdraw;
|
|
44846
|
-
|
|
44847
|
-
const takeExtUpnl = Math.min(extUpnl, need);
|
|
45062
|
+
const takeExtUpnl = Math.min(Math.max(extUpnl, 0), need);
|
|
44848
45063
|
dExtUpnl -= takeExtUpnl;
|
|
44849
|
-
|
|
45064
|
+
const netDrawableAmount = takeExtAvlWithdraw + takeExtUpnl;
|
|
45065
|
+
if (netDrawableAmount > CASE_THRESHOLD_USD) {
|
|
45066
|
+
need -= netDrawableAmount;
|
|
45067
|
+
finalExtended -= netDrawableAmount;
|
|
45068
|
+
}
|
|
44850
45069
|
const sent = -delta - need;
|
|
44851
45070
|
const eco2Sum = vaUsd + walletUsd + vesuBorrowCapacity;
|
|
45071
|
+
const netWalletUsd = walletUsd < CASE_THRESHOLD_USD ? 0 : walletUsd;
|
|
45072
|
+
finalVaUsd += sent + netWalletUsd;
|
|
44852
45073
|
if (eco2Sum >= 0) {
|
|
44853
45074
|
dWalletUsd += sent;
|
|
44854
45075
|
} else {
|
|
44855
45076
|
throw new Error(`${this._tag}: Unexpected case`);
|
|
44856
45077
|
}
|
|
44857
|
-
if (need >
|
|
45078
|
+
if (need > CASE_THRESHOLD_USD) {
|
|
44858
45079
|
throw new Error(`${this._tag}: Insufficient funds to cover margin needs`);
|
|
44859
45080
|
}
|
|
44860
45081
|
}
|
|
44861
|
-
return { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu: delta < 0 };
|
|
45082
|
+
return { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, finalVaUsd, finalExtended, isExtendedToVesu: delta < 0 };
|
|
45083
|
+
}
|
|
45084
|
+
_scaleVesuPoolDeltasByFactor(deltas, scale) {
|
|
45085
|
+
if (scale >= 1 - 1e-15) return deltas;
|
|
45086
|
+
return deltas.map((d) => ({
|
|
45087
|
+
...d,
|
|
45088
|
+
collateralDelta: new Web3Number(
|
|
45089
|
+
(d.collateralDelta.toNumber() * scale).toFixed(COLLATERAL_PRECISION),
|
|
45090
|
+
d.collateralToken.decimals
|
|
45091
|
+
),
|
|
45092
|
+
debtDelta: new Web3Number(
|
|
45093
|
+
(d.debtDelta.toNumber() * scale).toFixed(USDC_TOKEN_DECIMALS),
|
|
45094
|
+
USDC_TOKEN_DECIMALS
|
|
45095
|
+
)
|
|
45096
|
+
}));
|
|
44862
45097
|
}
|
|
44863
45098
|
/**
|
|
44864
45099
|
* 3. New Deposits / Excess Funds
|
|
@@ -44874,6 +45109,11 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44874
45109
|
* Computes allocation split between Vesu and Extended, then sources
|
|
44875
45110
|
* funds and creates lever-increase routes.
|
|
44876
45111
|
*
|
|
45112
|
+
* Order: {@link _rebalanceFunds} first → project VA / Extended liquid after the same funding
|
|
45113
|
+
* routes (wallet→VA, borrow→VA, VA→Extended, Extended→wallet→VA) → ideal Vesu/Extended deltas
|
|
45114
|
+
* from distributable split → cap common BTC by min(Vesu fundable, Extended fundable) → scale
|
|
45115
|
+
* Vesu deltas and recompute Extended deltas so both sides stay matched.
|
|
45116
|
+
*
|
|
44877
45117
|
* Fund flow (single pass — avoid VA→Extended then Extended→wallet round-trips):
|
|
44878
45118
|
* 1) Treat Vesu borrow headroom that the multiply route will consume as covering
|
|
44879
45119
|
* part of the Vesu USDC need (no standalone VESU_BORROW for that slice). Cap
|
|
@@ -44896,15 +45136,9 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44896
45136
|
withdrawAmount
|
|
44897
45137
|
);
|
|
44898
45138
|
if (distributableAmount.toNumber() <= CASE_THRESHOLD_USD) return [];
|
|
44899
|
-
const { vesuAllocationUsd, extendedAllocationUsd } = this._computeAllocationSplit(distributableAmount);
|
|
44900
|
-
const vesuDeltas = this._computePerPoolCollateralDeltas(
|
|
44901
|
-
vesuAllocationUsd
|
|
44902
|
-
);
|
|
44903
|
-
const extendedPositionDeltas = this._computeExtendedPositionDeltas(vesuDeltas);
|
|
44904
|
-
const vesuDepositAmount = this._computeVesuDepositAmount(vesuDeltas);
|
|
44905
45139
|
const vesuLeverage = calculateVesuLeverage();
|
|
44906
45140
|
const extendedLeverage = calculateExtendedLevergae();
|
|
44907
|
-
const { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu } = this._rebalanceFunds({
|
|
45141
|
+
const { dExtAvlWithdraw, dExtUpnl, dVaUsd, dWalletUsd, dVesuBorrowCapacity, isExtendedToVesu, finalVaUsd, finalExtended } = this._rebalanceFunds({
|
|
44908
45142
|
extAvlWithdraw: this._budget.extAvailWithdraw,
|
|
44909
45143
|
extUpnl: this._budget.extAvailUpnl,
|
|
44910
45144
|
vaUsd: this._budget.vaUsd,
|
|
@@ -44913,6 +45147,23 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44913
45147
|
vesuLeverage,
|
|
44914
45148
|
extendedLeverage
|
|
44915
45149
|
});
|
|
45150
|
+
logger.info(`${this._tag}::_classifyDeposits dExtAvlWithdraw=${dExtAvlWithdraw}, dExtUpnl=${dExtUpnl}, dVaUsd=${dVaUsd}, dWalletUsd=${dWalletUsd}, dVesuBorrowCapacity=${dVesuBorrowCapacity}, isExtendedToVesu=${isExtendedToVesu}, finalVaUsd=${finalVaUsd}`);
|
|
45151
|
+
let vesuDeltas = this._computePerPoolCollateralDeltas(new Web3Number(finalVaUsd.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS));
|
|
45152
|
+
const collateralPrice = this._budget.vesuPools[0]?.collateralPrice ?? 0;
|
|
45153
|
+
const collateralDecimals = this._budget.vesuPools[0]?.collateralToken.decimals ?? 0;
|
|
45154
|
+
let _extendedPositionDelta = new Web3Number((finalExtended * extendedLeverage / collateralPrice).toFixed(USDC_TOKEN_DECIMALS), collateralDecimals).toFixedRoundDown(COLLATERAL_PRECISION);
|
|
45155
|
+
logger.info(`${this._tag}::_classifyDeposits extendedPositionDelta=${_extendedPositionDelta}`);
|
|
45156
|
+
logger.info(`${this._tag}::_classifyDeposits vesuDeltas=${JSON.stringify(vesuDeltas)}`);
|
|
45157
|
+
assert(vesuDeltas.length == 1, "vesuDeltas should have only one delta");
|
|
45158
|
+
const minPositionDelta = Math.min(vesuDeltas[0].collateralDelta.toNumber(), Number(_extendedPositionDelta));
|
|
45159
|
+
logger.info(`${this._tag}::_classifyDeposits minPositionDelta=${minPositionDelta}`);
|
|
45160
|
+
vesuDeltas[0].collateralDelta = new Web3Number(minPositionDelta.toFixed(COLLATERAL_PRECISION), vesuDeltas[0].collateralDelta.decimals);
|
|
45161
|
+
const extendedPositionDeltas = [{
|
|
45162
|
+
instrument: this._config.extendedAdapter.config.extendedMarketName ?? "BTC-USD",
|
|
45163
|
+
delta: new Web3Number(minPositionDelta.toFixed(COLLATERAL_PRECISION), collateralDecimals)
|
|
45164
|
+
}];
|
|
45165
|
+
const vesuDepositAmount = this._computeVesuDepositAmount(vesuDeltas);
|
|
45166
|
+
logger.info(`${this._tag}::_classifyDeposits vesuDepositAmount=${vesuDepositAmount}`);
|
|
44916
45167
|
const routes = [];
|
|
44917
45168
|
if (isExtendedToVesu) {
|
|
44918
45169
|
if (dExtUpnl < 0) {
|
|
@@ -44940,13 +45191,6 @@ var ExtendedSVKVesuStateManager = class {
|
|
|
44940
45191
|
}
|
|
44941
45192
|
for (const vesuDelta of vesuDeltas) {
|
|
44942
45193
|
if (!skipAvnuDepositSwap && vesuDepositAmount.toNumber() > CASE_THRESHOLD_USD) {
|
|
44943
|
-
routes.push({
|
|
44944
|
-
type: "AVNU_DEPOSIT_SWAP" /* AVNU_DEPOSIT_SWAP */,
|
|
44945
|
-
priority: routes.length,
|
|
44946
|
-
fromToken: vesuDelta.collateralToken.symbol,
|
|
44947
|
-
fromAmount: vesuDepositAmount,
|
|
44948
|
-
toToken: vesuDelta.debtToken.symbol
|
|
44949
|
-
});
|
|
44950
45194
|
}
|
|
44951
45195
|
if (vesuDelta.collateralDelta.toNumber() > 0) {
|
|
44952
45196
|
const availableBorrowCapacity = this._budget.vesuBorrowCapacity;
|
|
@@ -45926,14 +46170,6 @@ var _ExecutionService = class _ExecutionService {
|
|
|
45926
46170
|
);
|
|
45927
46171
|
return this._executeStandardCase(caseEntry, activeRoutes);
|
|
45928
46172
|
}
|
|
45929
|
-
const setLevResult = await this._config.extendedAdapter.setLeverage(calculateExtendedLevergae().toString(), this._config.extendedAdapter.config.extendedMarketName);
|
|
45930
|
-
if (!setLevResult) {
|
|
45931
|
-
logger.error(
|
|
45932
|
-
`${this._tag}::_executeCoordinatedCase failed to set leverage`
|
|
45933
|
-
);
|
|
45934
|
-
results.push(this._failureResult(extendedRoute));
|
|
45935
|
-
return results;
|
|
45936
|
-
}
|
|
45937
46173
|
const isIncrease = _ExecutionService.INCREASE_EXPOSURE_ROUTES.has(
|
|
45938
46174
|
extendedRoute.type
|
|
45939
46175
|
);
|
|
@@ -45954,8 +46190,9 @@ var _ExecutionService = class _ExecutionService {
|
|
|
45954
46190
|
onChainCalls = callArrays.flat();
|
|
45955
46191
|
} catch (err) {
|
|
45956
46192
|
logger.error(
|
|
45957
|
-
`${this._tag}::_executeCoordinatedCase on-chain call construction failed
|
|
46193
|
+
`${this._tag}::_executeCoordinatedCase on-chain call construction failed:`
|
|
45958
46194
|
);
|
|
46195
|
+
console.error(err);
|
|
45959
46196
|
await this._emitEvent("FAILURE" /* FAILURE */, {
|
|
45960
46197
|
routeSummary: `coordinated on-chain build for case "${caseEntry.case.id}"`,
|
|
45961
46198
|
error: `${err}`
|
|
@@ -46027,7 +46264,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46027
46264
|
}
|
|
46028
46265
|
const timeoutMs = this._config.extendedFillTimeoutMs ?? DEFAULT_EXTENDED_FILL_TIMEOUT_MS;
|
|
46029
46266
|
const extResult = await this._executeExtendedLimitOrderWithRecovery(
|
|
46030
|
-
btcAmount,
|
|
46267
|
+
Math.abs(btcAmount),
|
|
46031
46268
|
executionPrice,
|
|
46032
46269
|
side,
|
|
46033
46270
|
{
|
|
@@ -46555,7 +46792,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46555
46792
|
return this._failureResult(route);
|
|
46556
46793
|
}
|
|
46557
46794
|
const closeResult = await this._executeExtendedLimitOrderWithRecovery(
|
|
46558
|
-
positionToClose.toNumber(),
|
|
46795
|
+
Math.abs(positionToClose.toNumber()),
|
|
46559
46796
|
midPrice,
|
|
46560
46797
|
"BUY" /* BUY */
|
|
46561
46798
|
);
|
|
@@ -46573,7 +46810,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46573
46810
|
`${this._tag}::_executeRealisePnl reopening ${positionToClose.toNumber()} on ${route.instrument}`
|
|
46574
46811
|
);
|
|
46575
46812
|
const reopenResult = await this._executeExtendedLimitOrderWithRecovery(
|
|
46576
|
-
positionToClose.toNumber(),
|
|
46813
|
+
Math.abs(positionToClose.toNumber()),
|
|
46577
46814
|
midPrice,
|
|
46578
46815
|
"SELL" /* SELL */
|
|
46579
46816
|
);
|
|
@@ -46627,7 +46864,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46627
46864
|
return this._failureResult(route);
|
|
46628
46865
|
}
|
|
46629
46866
|
const result = await this._executeExtendedLimitOrderWithRecovery(
|
|
46630
|
-
route.amount.toNumber(),
|
|
46867
|
+
Math.abs(route.amount.toNumber()),
|
|
46631
46868
|
midPrice,
|
|
46632
46869
|
"SELL" /* SELL */
|
|
46633
46870
|
);
|
|
@@ -46675,7 +46912,7 @@ var _ExecutionService = class _ExecutionService {
|
|
|
46675
46912
|
return this._failureResult(route);
|
|
46676
46913
|
}
|
|
46677
46914
|
const result = await this._executeExtendedLimitOrderWithRecovery(
|
|
46678
|
-
route.amount.toNumber(),
|
|
46915
|
+
Math.abs(route.amount.toNumber()),
|
|
46679
46916
|
midPrice,
|
|
46680
46917
|
"BUY" /* BUY */
|
|
46681
46918
|
);
|
|
@@ -47298,16 +47535,16 @@ var VesuExtendedMultiplierStrategy = class _VesuExtendedMultiplierStrategy exten
|
|
|
47298
47535
|
];
|
|
47299
47536
|
}
|
|
47300
47537
|
};
|
|
47301
|
-
function getLooperSettings3(
|
|
47538
|
+
function getLooperSettings3(collateralSymbol, underlyingSymbol, vaultSettings, pool1, extendedBackendReadUrl, extendedBackendWriteUrl, vaultIdExtended, minimumExtendedMovementAmount, minimumVesuMovementAmount, minimumExtendedRetriesDelayForOrderStatus, minimumExtendedPriceDifferenceForSwapOpen, maximumExtendedPriceDifferenceForSwapClosing) {
|
|
47302
47539
|
vaultSettings.leafAdapters = [];
|
|
47303
47540
|
const wbtcToken = Global.getDefaultTokens().find(
|
|
47304
|
-
(token) => token.symbol ===
|
|
47541
|
+
(token) => token.symbol === collateralSymbol
|
|
47305
47542
|
);
|
|
47306
47543
|
const usdcToken = Global.getDefaultTokens().find(
|
|
47307
47544
|
(token) => token.symbol === underlyingSymbol
|
|
47308
47545
|
);
|
|
47309
47546
|
const baseAdapterConfig = {
|
|
47310
|
-
baseToken:
|
|
47547
|
+
baseToken: usdcToken,
|
|
47311
47548
|
supportedPositions: [
|
|
47312
47549
|
{ asset: usdcToken, isDebt: true },
|
|
47313
47550
|
{ asset: wbtcToken, isDebt: false }
|
|
@@ -47407,7 +47644,7 @@ function getLooperSettings3(lstSymbol, underlyingSymbol, vaultSettings, pool1, e
|
|
|
47407
47644
|
vaultAddress: vaultSettings.vaultAddress,
|
|
47408
47645
|
vaultAllocator: vaultSettings.vaultAllocator,
|
|
47409
47646
|
manager: vaultSettings.manager,
|
|
47410
|
-
asset:
|
|
47647
|
+
asset: usdcToken.address
|
|
47411
47648
|
});
|
|
47412
47649
|
vaultSettings.leafAdapters.push(() => vesuMultiplyAdapter.getDepositLeaf());
|
|
47413
47650
|
vaultSettings.leafAdapters.push(() => vesuMultiplyAdapter.getWithdrawLeaf());
|
|
@@ -47541,6 +47778,39 @@ var re7UsdcPrimeDevansh = {
|
|
|
47541
47778
|
minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
|
|
47542
47779
|
walletAddress: "0x024b563C1C7d41B32BF4EFB9F38828508a65Be2d6e25268E9f63F22C5e9E51c5"
|
|
47543
47780
|
};
|
|
47781
|
+
var pureUsdc = {
|
|
47782
|
+
vaultAddress: ContractAddr.from(
|
|
47783
|
+
"0x745c972db65bdee10022fd875dd328c7f40a90849135b6a0f875a40f3c632ae"
|
|
47784
|
+
),
|
|
47785
|
+
manager: ContractAddr.from(
|
|
47786
|
+
"0x364e0894edefb616ec090f57f5c0274517fcd98ab276ae1f021c5e962fa1deb"
|
|
47787
|
+
),
|
|
47788
|
+
vaultAllocator: ContractAddr.from(
|
|
47789
|
+
"0x6fceed28e03a96091877568893df0dd89b9bb80fec30da2b742dacbd5526179"
|
|
47790
|
+
),
|
|
47791
|
+
redeemRequestNFT: ContractAddr.from(
|
|
47792
|
+
"0x501c2b87728e22c6dfcebe4c0b2b3a9fba5845606e4d59fa7bf591badcbb42"
|
|
47793
|
+
),
|
|
47794
|
+
aumOracle: ContractAddr.from(
|
|
47795
|
+
"0x6ccd95f5765242695d3c75e1440b1d0b30efac8babb864ce15729977b97cb82"
|
|
47796
|
+
),
|
|
47797
|
+
leafAdapters: [],
|
|
47798
|
+
adapters: [],
|
|
47799
|
+
targetHealthFactor: 1.4,
|
|
47800
|
+
minHealthFactor: 1.35,
|
|
47801
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
47802
|
+
(token) => token.symbol === "USDC"
|
|
47803
|
+
),
|
|
47804
|
+
quoteAmountToFetchPrice: new Web3Number(
|
|
47805
|
+
"0.001",
|
|
47806
|
+
Global.getDefaultTokens().find((token) => token.symbol === "USDC").decimals
|
|
47807
|
+
),
|
|
47808
|
+
borrowable_assets: [
|
|
47809
|
+
Global.getDefaultTokens().find((token) => token.symbol === "USDC")
|
|
47810
|
+
],
|
|
47811
|
+
minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
|
|
47812
|
+
walletAddress: "0x058571C23da5FEdd4e36003FAE3fE2fA9782f2692E552f081839142B10770D0B"
|
|
47813
|
+
};
|
|
47544
47814
|
var VesuExtendedTestStrategies = (extendedBackendReadUrl, extendedBackendWriteUrl, vaultIdExtended, minimumExtendedMovementAmount, minimumVesuMovementAmount, minimumExtendedRetriesDelayForOrderStatus, minimumExtendedPriceDifferenceForSwapOpen, maximumExtendedPriceDifferenceForSwapClosing) => {
|
|
47545
47815
|
return [
|
|
47546
47816
|
getStrategySettingsVesuExtended(
|
|
@@ -47557,14 +47827,29 @@ var VesuExtendedTestStrategies = (extendedBackendReadUrl, extendedBackendWriteUr
|
|
|
47557
47827
|
minimumExtendedRetriesDelayForOrderStatus,
|
|
47558
47828
|
minimumExtendedPriceDifferenceForSwapOpen,
|
|
47559
47829
|
maximumExtendedPriceDifferenceForSwapClosing
|
|
47830
|
+
),
|
|
47831
|
+
getStrategySettingsVesuExtended(
|
|
47832
|
+
"WBTC",
|
|
47833
|
+
"USDC",
|
|
47834
|
+
pureUsdc,
|
|
47835
|
+
false,
|
|
47836
|
+
false,
|
|
47837
|
+
extendedBackendReadUrl,
|
|
47838
|
+
extendedBackendWriteUrl,
|
|
47839
|
+
vaultIdExtended,
|
|
47840
|
+
minimumExtendedMovementAmount,
|
|
47841
|
+
minimumVesuMovementAmount,
|
|
47842
|
+
minimumExtendedRetriesDelayForOrderStatus,
|
|
47843
|
+
minimumExtendedPriceDifferenceForSwapOpen,
|
|
47844
|
+
maximumExtendedPriceDifferenceForSwapClosing
|
|
47560
47845
|
)
|
|
47561
47846
|
];
|
|
47562
47847
|
};
|
|
47563
|
-
function getStrategySettingsVesuExtended(
|
|
47848
|
+
function getStrategySettingsVesuExtended(collateralSymbol, underlyingSymbol, addresses, isPreview = false, isLST, extendedBackendReadUrl, extendedBackendWriteUrl, vaultIdExtended, minimumExtendedMovementAmount, minimumVesuMovementAmount, minimumExtendedRetriesDelayForOrderStatus, minimumExtendedPriceDifferenceForSwapOpen, maximumExtendedPriceDifferenceForSwapClosing) {
|
|
47564
47849
|
return {
|
|
47565
47850
|
id: `extended_${underlyingSymbol.toLowerCase()}_test`,
|
|
47566
47851
|
name: `Extended Test ${underlyingSymbol}`,
|
|
47567
|
-
description: getDescription3(
|
|
47852
|
+
description: getDescription3(collateralSymbol, underlyingSymbol),
|
|
47568
47853
|
address: addresses.vaultAddress,
|
|
47569
47854
|
launchBlock: 0,
|
|
47570
47855
|
type: "Other",
|
|
@@ -47578,7 +47863,7 @@ function getStrategySettingsVesuExtended(lstSymbol, underlyingSymbol, addresses,
|
|
|
47578
47863
|
)
|
|
47579
47864
|
],
|
|
47580
47865
|
additionalInfo: getLooperSettings3(
|
|
47581
|
-
|
|
47866
|
+
collateralSymbol,
|
|
47582
47867
|
underlyingSymbol,
|
|
47583
47868
|
addresses,
|
|
47584
47869
|
VesuPools.Re7USDCPrime,
|
|
@@ -47599,8 +47884,8 @@ function getStrategySettingsVesuExtended(lstSymbol, underlyingSymbol, addresses,
|
|
|
47599
47884
|
auditUrl: AUDIT_URL4,
|
|
47600
47885
|
protocols: [Protocols.ENDUR, Protocols.VESU],
|
|
47601
47886
|
contractDetails: getContractDetails(addresses),
|
|
47602
|
-
faqs: getFAQs2(
|
|
47603
|
-
investmentSteps: getInvestmentSteps(
|
|
47887
|
+
faqs: getFAQs2(collateralSymbol, underlyingSymbol, isLST),
|
|
47888
|
+
investmentSteps: getInvestmentSteps(collateralSymbol, underlyingSymbol),
|
|
47604
47889
|
isPreview,
|
|
47605
47890
|
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.",
|
|
47606
47891
|
security: {
|