@1delta/margin-fetcher 5.0.46 → 5.0.48
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.d.ts +22 -3
- package/dist/index.js +376 -78
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -14976,8 +14976,46 @@ var ComptrollerAbi = [
|
|
|
14976
14976
|
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
14977
14977
|
stateMutability: "view",
|
|
14978
14978
|
type: "function"
|
|
14979
|
+
},
|
|
14980
|
+
// Per-market pause guardians. Classic Compound V2 keeps them as public
|
|
14981
|
+
// mappings on the Comptroller — the cToken lenses do NOT expose them, so a
|
|
14982
|
+
// fork on the standard lens path can only learn its pause state from here.
|
|
14983
|
+
// `mintAllowed` / `borrowAllowed` hard-revert ("mint is paused" /
|
|
14984
|
+
// "borrow is paused") when set, which is why they gate `depositsEnabled` /
|
|
14985
|
+
// `borrowingEnabled` rather than merely capping an amount.
|
|
14986
|
+
{
|
|
14987
|
+
inputs: [{ internalType: "address", name: "", type: "address" }],
|
|
14988
|
+
name: "mintGuardianPaused",
|
|
14989
|
+
outputs: [{ internalType: "bool", name: "", type: "bool" }],
|
|
14990
|
+
stateMutability: "view",
|
|
14991
|
+
type: "function"
|
|
14992
|
+
},
|
|
14993
|
+
{
|
|
14994
|
+
inputs: [{ internalType: "address", name: "", type: "address" }],
|
|
14995
|
+
name: "borrowGuardianPaused",
|
|
14996
|
+
outputs: [{ internalType: "bool", name: "", type: "bool" }],
|
|
14997
|
+
stateMutability: "view",
|
|
14998
|
+
type: "function"
|
|
14979
14999
|
}
|
|
14980
15000
|
];
|
|
15001
|
+
var COMPTROLLER_PAUSE_READ_LENDERS = [
|
|
15002
|
+
Lender.COMPOUND_V2,
|
|
15003
|
+
Lender.CREAM_FINANCE,
|
|
15004
|
+
Lender.FLUX_FINANCE,
|
|
15005
|
+
Lender.WE_PIGGY,
|
|
15006
|
+
Lender.GAMMA,
|
|
15007
|
+
Lender.CAPY_FI,
|
|
15008
|
+
Lender.LODESTAR,
|
|
15009
|
+
Lender.TENDER
|
|
15010
|
+
];
|
|
15011
|
+
var usesComptrollerPauseReads = (lender) => COMPTROLLER_PAUSE_READ_LENDERS.includes(lender);
|
|
15012
|
+
var parsePausedFlag = (raw) => typeof raw === "boolean" ? raw : void 0;
|
|
15013
|
+
var resolveMarketPause = (token, live) => {
|
|
15014
|
+
const mintPaused = live?.mintPaused ?? token?.mintPaused;
|
|
15015
|
+
const borrowPaused = live?.borrowPaused ?? token?.borrowPaused;
|
|
15016
|
+
if (mintPaused === void 0 && borrowPaused === void 0) return void 0;
|
|
15017
|
+
return { mintPaused, borrowPaused };
|
|
15018
|
+
};
|
|
14981
15019
|
|
|
14982
15020
|
// src/lending/public-data/compound-v2/publicCallBuild.ts
|
|
14983
15021
|
var buildCompoundV2StyleLenderReserveCall = (chainId, lender) => {
|
|
@@ -15058,6 +15096,18 @@ var buildCompoundV2StyleLenderReserveCall = (chainId, lender) => {
|
|
|
15058
15096
|
name: "liquidationIncentiveMantissa",
|
|
15059
15097
|
params: []
|
|
15060
15098
|
});
|
|
15099
|
+
if (usesComptrollerPauseReads(lender)) {
|
|
15100
|
+
for (const name of ["mintGuardianPaused", "borrowGuardianPaused"]) {
|
|
15101
|
+
for (const tk of tokens) {
|
|
15102
|
+
calls.push({
|
|
15103
|
+
abi: ComptrollerAbi,
|
|
15104
|
+
address: stdComptroller,
|
|
15105
|
+
name,
|
|
15106
|
+
params: [tk.cToken]
|
|
15107
|
+
});
|
|
15108
|
+
}
|
|
15109
|
+
}
|
|
15110
|
+
}
|
|
15061
15111
|
}
|
|
15062
15112
|
return calls;
|
|
15063
15113
|
};
|
|
@@ -15513,7 +15563,10 @@ function convertSingleEntry(opts) {
|
|
|
15513
15563
|
if (perMarketIncentive !== void 0 && Number(perMarketIncentive) > 0) {
|
|
15514
15564
|
liquidationPenalty = Math.max(
|
|
15515
15565
|
Number(
|
|
15516
|
-
parseRawAmount(
|
|
15566
|
+
parseRawAmount(
|
|
15567
|
+
perMarketIncentive.toString(),
|
|
15568
|
+
RESERVE_MANTISSA_DECIMALS
|
|
15569
|
+
)
|
|
15517
15570
|
) - 1,
|
|
15518
15571
|
0
|
|
15519
15572
|
);
|
|
@@ -15534,7 +15587,11 @@ function convertSingleEntry(opts) {
|
|
|
15534
15587
|
);
|
|
15535
15588
|
const liquidity = totalSupplyUnderlying - totalDebt;
|
|
15536
15589
|
const price2 = prices[asset.assetGroup] ?? 0;
|
|
15537
|
-
const pausedActions = currentEntry.pausedActions;
|
|
15590
|
+
const pausedActions = { ...currentEntry.pausedActions };
|
|
15591
|
+
if (opts.pause?.mintPaused !== void 0)
|
|
15592
|
+
pausedActions[0 /* MINT */] = opts.pause.mintPaused;
|
|
15593
|
+
if (opts.pause?.borrowPaused !== void 0)
|
|
15594
|
+
pausedActions[2 /* BORROW */] = opts.pause.borrowPaused;
|
|
15538
15595
|
const allPaused = pausedActions[0 /* MINT */] && pausedActions[2 /* BORROW */] && pausedActions[1 /* REDEEM */] && pausedActions[3 /* REPAY */];
|
|
15539
15596
|
const poolId = asset.address;
|
|
15540
15597
|
const collateralActive = Boolean(currentEntry?.isListed);
|
|
@@ -16366,7 +16423,9 @@ var getCompoundV2DataConverter = (lender, chainId, prices, additionalYields, tok
|
|
|
16366
16423
|
const isSumer = isSumerType(lender);
|
|
16367
16424
|
const stdComptroller = isSumer ? void 0 : getCompoundV2Comptroller(chainId, lender);
|
|
16368
16425
|
const stdComptrollerCalls = stdComptroller ? 2 : 0;
|
|
16369
|
-
const
|
|
16426
|
+
const pauseReads = Boolean(stdComptroller) && usesComptrollerPauseReads(lender);
|
|
16427
|
+
const pauseCalls = pauseReads ? tokenCount * 2 : 0;
|
|
16428
|
+
const expectedNumberOfCalls = isSumer ? tokenCount + SUMER_GROUP_COUNT : tokenCount + stdComptrollerCalls + pauseCalls;
|
|
16370
16429
|
const reader = getReader(lender, chainId);
|
|
16371
16430
|
return [
|
|
16372
16431
|
(data) => {
|
|
@@ -16403,6 +16462,8 @@ var getCompoundV2DataConverter = (lender, chainId, prices, additionalYields, tok
|
|
|
16403
16462
|
globalCloseFactor = closeFactorMantissa;
|
|
16404
16463
|
globalLiquidationPenalty = incentiveMantissa !== void 0 ? Math.max(incentiveMantissa - 1, 0) : void 0;
|
|
16405
16464
|
}
|
|
16465
|
+
const mintPausedBase = tokenCount + stdComptrollerCalls;
|
|
16466
|
+
const borrowPausedBase = mintPausedBase + tokenCount;
|
|
16406
16467
|
for (let i = 0; i < tokenCount; i++) {
|
|
16407
16468
|
const { underlying, cToken } = compoundV2Tokens2[i];
|
|
16408
16469
|
if (!cToken || !underlying) continue;
|
|
@@ -16419,7 +16480,13 @@ var getCompoundV2DataConverter = (lender, chainId, prices, additionalYields, tok
|
|
|
16419
16480
|
additionalYields,
|
|
16420
16481
|
cToken,
|
|
16421
16482
|
closeFactor: globalCloseFactor,
|
|
16422
|
-
liquidationPenalty: globalLiquidationPenalty
|
|
16483
|
+
liquidationPenalty: globalLiquidationPenalty,
|
|
16484
|
+
// live guardian read first, then the static fallback published on
|
|
16485
|
+
// the token entry for forks that expose no getter at all
|
|
16486
|
+
pause: pauseReads ? resolveMarketPause(compoundV2Tokens2[i], {
|
|
16487
|
+
mintPaused: parsePausedFlag(data[mintPausedBase + i]),
|
|
16488
|
+
borrowPaused: parsePausedFlag(data[borrowPausedBase + i])
|
|
16489
|
+
}) : void 0
|
|
16423
16490
|
});
|
|
16424
16491
|
out.data[entry.marketUid] = entry;
|
|
16425
16492
|
}
|
|
@@ -19386,6 +19453,19 @@ function parseVault(vault, chainId, prices, additionalYields, tokenList, liquidi
|
|
|
19386
19453
|
const meta = tokenList[token];
|
|
19387
19454
|
return toOracleKey(meta?.assetGroup) ?? toGenericPriceKey(token, chainId);
|
|
19388
19455
|
};
|
|
19456
|
+
const basketRate = (legs) => {
|
|
19457
|
+
let value = 0;
|
|
19458
|
+
let weighted = 0;
|
|
19459
|
+
for (const leg of legs) {
|
|
19460
|
+
const v = leg.amount * (prices[priceKeyFor(leg.token)] ?? 0);
|
|
19461
|
+
if (v <= 0) continue;
|
|
19462
|
+
value += v;
|
|
19463
|
+
weighted += v * leg.rate;
|
|
19464
|
+
}
|
|
19465
|
+
return value > 0 ? weighted / value : legs[0]?.rate ?? 0;
|
|
19466
|
+
};
|
|
19467
|
+
const basketSupplyRate = isSmartCol ? basketRate(colLegs) : void 0;
|
|
19468
|
+
const basketBorrowRate = isSmartDebt ? basketRate(debtLegs) : void 0;
|
|
19389
19469
|
const irmTotals = (state, decimals) => state ? {
|
|
19390
19470
|
irmTotalDeposits: Number(parseRawAmount(state.totalSupply, decimals)),
|
|
19391
19471
|
irmTotalDebt: Number(parseRawAmount(state.totalBorrow, decimals))
|
|
@@ -19451,8 +19531,49 @@ function parseVault(vault, chainId, prices, additionalYields, tokenList, liquidi
|
|
|
19451
19531
|
* it a smart market is described as an ordinary pool — the generic
|
|
19452
19532
|
* builder cannot tell them apart.
|
|
19453
19533
|
*/
|
|
19534
|
+
/**
|
|
19535
|
+
* THIS ROW'S ASSET IS NOT INDEPENDENTLY HOLDABLE.
|
|
19536
|
+
*
|
|
19537
|
+
* LENDER-AGNOSTIC ON PURPOSE — it sits on the ROW, not inside `fluid`,
|
|
19538
|
+
* because the shape is not Fluid's alone: Lista SmartLP's collateral
|
|
19539
|
+
* receipt, GMX's GM/GLV baskets and Fluid's smart sides are all market
|
|
19540
|
+
* sides whose position unit is a multi-token basket (the same set
|
|
19541
|
+
* LP_ACTIONS_PLAN.md enumerates). Any of them can set this, and a
|
|
19542
|
+
* consumer branches on it once instead of learning each protocol.
|
|
19543
|
+
*
|
|
19544
|
+
* True means: depositing any leg mints a share of a basket over ALL of
|
|
19545
|
+
* them, and the POOL — not the user — sets and continuously re-sets the
|
|
19546
|
+
* ratio. There is no way to "hold the ETH leg" of a USDC+ETH side; the
|
|
19547
|
+
* split drifts with every trade and with the range shifting around its
|
|
19548
|
+
* centre price.
|
|
19549
|
+
*
|
|
19550
|
+
* Three consequences a consumer must handle: "you deposited X" is false
|
|
19551
|
+
* from the first block; a single leg's rate is right per DOLLAR but is
|
|
19552
|
+
* NOT the position's rate (use the basket rate); and an exit has to be
|
|
19553
|
+
* sized in shares rather than in this token.
|
|
19554
|
+
*/
|
|
19555
|
+
...isSmartVault ? { autoBalanced: true } : {},
|
|
19454
19556
|
fluid: isSmartVault ? {
|
|
19455
19557
|
vaultType,
|
|
19558
|
+
/**
|
|
19559
|
+
* The rate of the POSITION, not of this leg.
|
|
19560
|
+
*
|
|
19561
|
+
* A per-leg rate is individually correct — every dollar in the LP
|
|
19562
|
+
* earns the DEX trading yield regardless of which token it sits in,
|
|
19563
|
+
* so `legRate = liquidityRate + tradingRate` is right per dollar.
|
|
19564
|
+
* What it is NOT is the vault's APR: ranking or headlining a single
|
|
19565
|
+
* leg overstates the position whenever the legs differ. On the live
|
|
19566
|
+
* USDC+ETH/USDC+ETH vault the legs read 11.81 % and 8.19 % while
|
|
19567
|
+
* the position earns 10.33 % — and a "best APR" taken as the max
|
|
19568
|
+
* over legs shows 11.81 %.
|
|
19569
|
+
*
|
|
19570
|
+
* Value-weighted by each leg's own share of the side, which is the
|
|
19571
|
+
* pool's composition as this vault holds it. Fluid's own UI
|
|
19572
|
+
* publishes exactly this figure (10.33 % supply / −1.72 % borrow),
|
|
19573
|
+
* and these match it to the basis point.
|
|
19574
|
+
*/
|
|
19575
|
+
basketSupplyRate,
|
|
19576
|
+
basketBorrowRate,
|
|
19456
19577
|
isSmartCol,
|
|
19457
19578
|
isSmartDebt,
|
|
19458
19579
|
/** Both legs of the collateral LP, in token0/token1 order. */
|
|
@@ -27655,7 +27776,31 @@ var maxRetries = 3;
|
|
|
27655
27776
|
var MULTICALL_FAILURE = "0x";
|
|
27656
27777
|
var isFailedCall = (value) => value === void 0 || value === null || value === MULTICALL_FAILURE;
|
|
27657
27778
|
var MULTICALL_REPAIR_ROUNDS = 3;
|
|
27658
|
-
var MAX_CALLS_PER_SHARD =
|
|
27779
|
+
var MAX_CALLS_PER_SHARD = 300;
|
|
27780
|
+
var ONE_REQUEST_BATCH_BYTES = 1e6;
|
|
27781
|
+
var MAX_INFLIGHT_REQUESTS = 6;
|
|
27782
|
+
var HEDGE_DELAY_MS = 1200;
|
|
27783
|
+
var SHARD_TIMEOUT_MS = 4e3;
|
|
27784
|
+
var MIN_ADAPTIVE_SHARD = 25;
|
|
27785
|
+
var ADAPTIVE_CAP_TTL_MS = 6e4;
|
|
27786
|
+
var endpointCallCap = /* @__PURE__ */ new Map();
|
|
27787
|
+
var capFor = (url, requested) => {
|
|
27788
|
+
const hit = endpointCallCap.get(url);
|
|
27789
|
+
if (!hit) return requested;
|
|
27790
|
+
if (Date.now() - hit.at > ADAPTIVE_CAP_TTL_MS) {
|
|
27791
|
+
endpointCallCap.delete(url);
|
|
27792
|
+
return requested;
|
|
27793
|
+
}
|
|
27794
|
+
return Math.min(requested, hit.calls);
|
|
27795
|
+
};
|
|
27796
|
+
var noteEndpointRefusal = (url, calls) => {
|
|
27797
|
+
if (calls <= MIN_ADAPTIVE_SHARD) return;
|
|
27798
|
+
const next = Math.max(MIN_ADAPTIVE_SHARD, Math.floor(calls / 2));
|
|
27799
|
+
const current = endpointCallCap.get(url);
|
|
27800
|
+
if (current && Date.now() - current.at <= ADAPTIVE_CAP_TTL_MS && current.calls <= next)
|
|
27801
|
+
return;
|
|
27802
|
+
endpointCallCap.set(url, { calls: next, at: Date.now() });
|
|
27803
|
+
};
|
|
27659
27804
|
var DETERMINISTIC_ERROR_NAMES = /* @__PURE__ */ new Set([
|
|
27660
27805
|
"ContractFunctionRevertedError",
|
|
27661
27806
|
"RawContractError",
|
|
@@ -27679,13 +27824,13 @@ var REPAIR_BACKOFF_JITTER_MS = 60;
|
|
|
27679
27824
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
27680
27825
|
var backoffForRound = (round) => REPAIR_BACKOFF_BASE_MS * 2 ** round + Math.random() * REPAIR_BACKOFF_JITTER_MS;
|
|
27681
27826
|
var endpointUrl = (client, rpcId) => client?.transport?.url ?? `rpc#${rpcId}`;
|
|
27682
|
-
var resolveEndpoint = (chainId, getEvmClient17, startRpcId, tried, maxProbe = 12) => {
|
|
27827
|
+
var resolveEndpoint = (chainId, getEvmClient17, startRpcId, tried, maxProbe = 12, timeoutMs) => {
|
|
27683
27828
|
let fallback = null;
|
|
27684
27829
|
for (let probe = 0; probe < maxProbe; probe++) {
|
|
27685
27830
|
const rpcId = startRpcId + probe;
|
|
27686
27831
|
let client;
|
|
27687
27832
|
try {
|
|
27688
|
-
client = getEvmClient17(chainId, rpcId);
|
|
27833
|
+
client = getEvmClient17(chainId, rpcId, { timeoutMs });
|
|
27689
27834
|
} catch {
|
|
27690
27835
|
break;
|
|
27691
27836
|
}
|
|
@@ -27894,39 +28039,30 @@ var multicallShardedAbiArray = async (chainId, abi, calls, getEvmClient17, poolS
|
|
|
27894
28039
|
functionName: name,
|
|
27895
28040
|
args: params
|
|
27896
28041
|
}));
|
|
28042
|
+
const requestBytes = batchSize < MULTICALL_DEFAULT_BATCH_SIZE ? batchSize : ONE_REQUEST_BATCH_BYTES;
|
|
28043
|
+
const workerBudget = Math.max(1, Math.floor(MAX_INFLIGHT_REQUESTS / 2));
|
|
28044
|
+
const workers = Math.max(1, Math.min(poolSize, workerBudget));
|
|
27897
28045
|
const shardSize = Math.max(
|
|
27898
28046
|
1,
|
|
27899
|
-
Math.min(MAX_CALLS_PER_SHARD, Math.ceil(contracts.length /
|
|
28047
|
+
Math.min(MAX_CALLS_PER_SHARD, Math.ceil(contracts.length / workers))
|
|
27900
28048
|
);
|
|
27901
28049
|
const batches = [];
|
|
27902
28050
|
for (let i = 0; i < contracts.length; i += shardSize) {
|
|
27903
28051
|
batches.push({ start: i, items: contracts.slice(i, i + shardSize) });
|
|
27904
28052
|
}
|
|
27905
28053
|
const results = new Array(contracts.length);
|
|
27906
|
-
const
|
|
27907
|
-
const endpoint = resolveEndpoint(chainId, getEvmClient17, rpcId, tried);
|
|
27908
|
-
if (!endpoint) {
|
|
27909
|
-
if (logs)
|
|
27910
|
-
console.log(
|
|
27911
|
-
`sharded multicall: no untried endpoint left for chain ${chainId}`
|
|
27912
|
-
);
|
|
27913
|
-
return;
|
|
27914
|
-
}
|
|
27915
|
-
const shardOptions = {
|
|
27916
|
-
tried,
|
|
27917
|
-
onEndpointFailure: options?.onEndpointFailure
|
|
27918
|
-
};
|
|
28054
|
+
const request = async (endpoint, items) => {
|
|
27919
28055
|
try {
|
|
27920
28056
|
const returnData = await endpoint.client.multicall({
|
|
27921
28057
|
allowFailure,
|
|
27922
|
-
batchSize:
|
|
27923
|
-
contracts:
|
|
28058
|
+
batchSize: requestBytes,
|
|
28059
|
+
contracts: items
|
|
27924
28060
|
});
|
|
27925
|
-
|
|
27926
|
-
if (allowFailure &&
|
|
28061
|
+
const slots = toSlots(returnData, allowFailure);
|
|
28062
|
+
if (allowFailure && slots.length > 0 && slots.every((s) => isFailedCall(s.value) && !s.permanent)) {
|
|
27927
28063
|
if (logs)
|
|
27928
28064
|
console.log(
|
|
27929
|
-
`sharded multicall
|
|
28065
|
+
`sharded multicall: rpc ${endpoint.rpcId} answered ${items.length} calls with nothing`
|
|
27930
28066
|
);
|
|
27931
28067
|
options?.onEndpointFailure?.({
|
|
27932
28068
|
chainId,
|
|
@@ -27934,61 +28070,165 @@ var multicallShardedAbiArray = async (chainId, abi, calls, getEvmClient17, poolS
|
|
|
27934
28070
|
rpcId: endpoint.rpcId,
|
|
27935
28071
|
kind: "slots"
|
|
27936
28072
|
});
|
|
27937
|
-
|
|
27938
|
-
return
|
|
27939
|
-
batch,
|
|
27940
|
-
endpoint.rpcId + 1,
|
|
27941
|
-
attemptsLeft - 1,
|
|
27942
|
-
size,
|
|
27943
|
-
tried
|
|
27944
|
-
);
|
|
27945
|
-
}
|
|
27946
|
-
if (allowFailure && retryFailed) {
|
|
27947
|
-
slots = await repairFailedSlots(
|
|
27948
|
-
chainId,
|
|
27949
|
-
batch.items,
|
|
27950
|
-
slots,
|
|
27951
|
-
getEvmClient17,
|
|
27952
|
-
endpoint.rpcId + 1,
|
|
27953
|
-
size,
|
|
27954
|
-
logs,
|
|
27955
|
-
MULTICALL_REPAIR_ROUNDS,
|
|
27956
|
-
shardOptions
|
|
27957
|
-
);
|
|
27958
|
-
}
|
|
27959
|
-
recordPermanentFailures(slots, batch.start, permanentFailures);
|
|
27960
|
-
for (let j = 0; j < slots.length; j++) {
|
|
27961
|
-
results[batch.start + j] = slots[j].value;
|
|
28073
|
+
noteEndpointRefusal(endpoint.url, items.length);
|
|
28074
|
+
return null;
|
|
27962
28075
|
}
|
|
28076
|
+
return slots;
|
|
27963
28077
|
} catch (error) {
|
|
27964
|
-
if (logs) console.log("error in sharded multicall
|
|
28078
|
+
if (logs) console.log("error in sharded multicall request", error);
|
|
27965
28079
|
options?.onEndpointFailure?.({
|
|
27966
28080
|
chainId,
|
|
27967
28081
|
url: endpoint.url,
|
|
27968
28082
|
rpcId: endpoint.rpcId,
|
|
27969
28083
|
kind: "transport"
|
|
27970
28084
|
});
|
|
27971
|
-
|
|
27972
|
-
|
|
27973
|
-
|
|
27974
|
-
|
|
27975
|
-
|
|
27976
|
-
|
|
27977
|
-
|
|
27978
|
-
|
|
28085
|
+
noteEndpointRefusal(endpoint.url, items.length);
|
|
28086
|
+
return null;
|
|
28087
|
+
}
|
|
28088
|
+
};
|
|
28089
|
+
const firstUsable = (attempts) => new Promise((resolve) => {
|
|
28090
|
+
let pending = attempts.length;
|
|
28091
|
+
let settled = false;
|
|
28092
|
+
const lose = () => {
|
|
28093
|
+
if (!settled && --pending === 0) resolve(null);
|
|
28094
|
+
};
|
|
28095
|
+
for (const attempt of attempts) {
|
|
28096
|
+
attempt.then((won) => {
|
|
28097
|
+
if (settled) return;
|
|
28098
|
+
if (won) {
|
|
28099
|
+
settled = true;
|
|
28100
|
+
resolve(won);
|
|
28101
|
+
} else lose();
|
|
28102
|
+
}, lose);
|
|
28103
|
+
}
|
|
28104
|
+
});
|
|
28105
|
+
const attemptHedged = async (items, startRpcId, tried) => {
|
|
28106
|
+
const primary = resolveEndpoint(
|
|
28107
|
+
chainId,
|
|
28108
|
+
getEvmClient17,
|
|
28109
|
+
startRpcId,
|
|
28110
|
+
tried,
|
|
28111
|
+
12,
|
|
28112
|
+
SHARD_TIMEOUT_MS
|
|
28113
|
+
);
|
|
28114
|
+
if (!primary) {
|
|
28115
|
+
if (logs)
|
|
28116
|
+
console.log(
|
|
28117
|
+
`sharded multicall: no untried endpoint left for chain ${chainId}`
|
|
28118
|
+
);
|
|
28119
|
+
return null;
|
|
28120
|
+
}
|
|
28121
|
+
let timer;
|
|
28122
|
+
let hedgeStarted = false;
|
|
28123
|
+
let beginHedge = () => {
|
|
28124
|
+
};
|
|
28125
|
+
const hedged = new Promise((resolve) => {
|
|
28126
|
+
beginHedge = () => {
|
|
28127
|
+
if (hedgeStarted) return;
|
|
28128
|
+
hedgeStarted = true;
|
|
28129
|
+
clearTimeout(timer);
|
|
28130
|
+
const alt = resolveEndpoint(
|
|
28131
|
+
chainId,
|
|
28132
|
+
getEvmClient17,
|
|
28133
|
+
primary.rpcId + 1,
|
|
28134
|
+
tried,
|
|
28135
|
+
12,
|
|
28136
|
+
SHARD_TIMEOUT_MS
|
|
28137
|
+
);
|
|
28138
|
+
if (!alt) return resolve(null);
|
|
28139
|
+
const altItems = items.slice(0, capFor(alt.url, items.length));
|
|
28140
|
+
if (altItems.length < items.length) return resolve(null);
|
|
28141
|
+
request(alt, items).then(
|
|
28142
|
+
(slots) => resolve(slots ? { slots, rpcId: alt.rpcId } : null)
|
|
28143
|
+
);
|
|
28144
|
+
};
|
|
28145
|
+
});
|
|
28146
|
+
timer = setTimeout(() => beginHedge(), HEDGE_DELAY_MS);
|
|
28147
|
+
const direct = request(primary, items).then((slots) => {
|
|
28148
|
+
if (slots) return { slots, rpcId: primary.rpcId };
|
|
28149
|
+
beginHedge();
|
|
28150
|
+
return null;
|
|
28151
|
+
});
|
|
28152
|
+
const won = await firstUsable([direct, hedged]);
|
|
28153
|
+
clearTimeout(timer);
|
|
28154
|
+
return won;
|
|
28155
|
+
};
|
|
28156
|
+
const runBatch = async (batch, rpcId, attemptsLeft, tried) => {
|
|
28157
|
+
if (!batch.items.length) return;
|
|
28158
|
+
const won = await attemptHedged(batch.items, rpcId, tried);
|
|
28159
|
+
if (!won) {
|
|
28160
|
+
if (attemptsLeft > 0 && batch.items.length > 1) {
|
|
28161
|
+
const half = Math.ceil(batch.items.length / 2);
|
|
28162
|
+
await sleep(backoffForRound(0));
|
|
28163
|
+
await runBatch(
|
|
28164
|
+
{ start: batch.start, items: batch.items.slice(0, half) },
|
|
28165
|
+
rpcId,
|
|
28166
|
+
attemptsLeft - 1,
|
|
28167
|
+
tried
|
|
28168
|
+
);
|
|
28169
|
+
await runBatch(
|
|
28170
|
+
{ start: batch.start + half, items: batch.items.slice(half) },
|
|
28171
|
+
rpcId + 1,
|
|
28172
|
+
attemptsLeft - 1,
|
|
28173
|
+
tried
|
|
28174
|
+
);
|
|
28175
|
+
}
|
|
28176
|
+
return;
|
|
28177
|
+
}
|
|
28178
|
+
let slots = won.slots;
|
|
28179
|
+
if (allowFailure && retryFailed) {
|
|
28180
|
+
slots = await repairFailedSlots(
|
|
28181
|
+
chainId,
|
|
28182
|
+
batch.items,
|
|
28183
|
+
slots,
|
|
28184
|
+
getEvmClient17,
|
|
28185
|
+
won.rpcId + 1,
|
|
28186
|
+
requestBytes,
|
|
28187
|
+
logs,
|
|
28188
|
+
MULTICALL_REPAIR_ROUNDS,
|
|
28189
|
+
{ tried, onEndpointFailure: options?.onEndpointFailure }
|
|
27979
28190
|
);
|
|
27980
28191
|
}
|
|
28192
|
+
recordPermanentFailures(slots, batch.start, permanentFailures);
|
|
28193
|
+
for (let j = 0; j < slots.length; j++) {
|
|
28194
|
+
results[batch.start + j] = slots[j].value;
|
|
28195
|
+
}
|
|
27981
28196
|
};
|
|
27982
|
-
const workers = Math.max(1, Math.min(poolSize, batches.length));
|
|
27983
28197
|
let cursor = 0;
|
|
27984
28198
|
const worker = async (workerId) => {
|
|
27985
28199
|
while (true) {
|
|
27986
28200
|
const idx = cursor++;
|
|
27987
28201
|
if (idx >= batches.length) break;
|
|
27988
|
-
|
|
28202
|
+
const batch = batches[idx];
|
|
28203
|
+
const tried = /* @__PURE__ */ new Set();
|
|
28204
|
+
const start = resolveEndpoint(
|
|
28205
|
+
chainId,
|
|
28206
|
+
getEvmClient17,
|
|
28207
|
+
workerId,
|
|
28208
|
+
void 0,
|
|
28209
|
+
12,
|
|
28210
|
+
SHARD_TIMEOUT_MS
|
|
28211
|
+
);
|
|
28212
|
+
const cap = start ? capFor(start.url, batch.items.length) : batch.items.length;
|
|
28213
|
+
for (let off = 0; off < batch.items.length; off += cap) {
|
|
28214
|
+
await runBatch(
|
|
28215
|
+
{
|
|
28216
|
+
start: batch.start + off,
|
|
28217
|
+
items: batch.items.slice(off, off + cap)
|
|
28218
|
+
},
|
|
28219
|
+
workerId,
|
|
28220
|
+
retries,
|
|
28221
|
+
tried
|
|
28222
|
+
);
|
|
28223
|
+
}
|
|
27989
28224
|
}
|
|
27990
28225
|
};
|
|
27991
|
-
await Promise.all(
|
|
28226
|
+
await Promise.all(
|
|
28227
|
+
Array.from(
|
|
28228
|
+
{ length: Math.min(workers, batches.length) },
|
|
28229
|
+
(_3, w) => worker(w)
|
|
28230
|
+
)
|
|
28231
|
+
);
|
|
27992
28232
|
return results;
|
|
27993
28233
|
};
|
|
27994
28234
|
function prepareMulticallInputs(abi, calls) {
|
|
@@ -53819,6 +54059,19 @@ var FlashAbi = [
|
|
|
53819
54059
|
name: "FLASHLOAN_PREMIUM_TOTAL",
|
|
53820
54060
|
inputs: []
|
|
53821
54061
|
},
|
|
54062
|
+
{
|
|
54063
|
+
inputs: [],
|
|
54064
|
+
name: "decimals",
|
|
54065
|
+
outputs: [
|
|
54066
|
+
{
|
|
54067
|
+
internalType: "uint8",
|
|
54068
|
+
name: "",
|
|
54069
|
+
type: "uint8"
|
|
54070
|
+
}
|
|
54071
|
+
],
|
|
54072
|
+
stateMutability: "view",
|
|
54073
|
+
type: "function"
|
|
54074
|
+
},
|
|
53822
54075
|
{
|
|
53823
54076
|
inputs: [
|
|
53824
54077
|
{
|
|
@@ -53841,6 +54094,14 @@ var FlashAbi = [
|
|
|
53841
54094
|
];
|
|
53842
54095
|
var DEFAULT_BATCH_SIZE = 4096;
|
|
53843
54096
|
var isValidResult = (v) => typeof v === "bigint";
|
|
54097
|
+
var NATIVE_DECIMALS = 18;
|
|
54098
|
+
function parseDecimalsResult(v) {
|
|
54099
|
+
const n = typeof v === "bigint" ? Number(v) : v;
|
|
54100
|
+
if (typeof n !== "number" || !Number.isInteger(n) || n < 0 || n > 36) {
|
|
54101
|
+
return void 0;
|
|
54102
|
+
}
|
|
54103
|
+
return n;
|
|
54104
|
+
}
|
|
53844
54105
|
var FLASHLOAN_ENABLED_MASK = BigInt(
|
|
53845
54106
|
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFF"
|
|
53846
54107
|
);
|
|
@@ -53906,6 +54167,20 @@ async function fetchFlashLiquidityForChain(chain, multicallRetry, list = {}) {
|
|
|
53906
54167
|
const balancerV3Calls = buildBalanceCalls(balancerV3s);
|
|
53907
54168
|
const uniswapV4s = RELEVANT_UNISWAP_V4_FORKS[chain] ?? [];
|
|
53908
54169
|
const uniswapV4Calls = buildBalanceCalls(uniswapV4s);
|
|
54170
|
+
const decimalsByAsset = {
|
|
54171
|
+
[zeroAddress]: NATIVE_DECIMALS
|
|
54172
|
+
};
|
|
54173
|
+
for (const asset of unifiedAssets) {
|
|
54174
|
+
const fromList = list[asset]?.decimals;
|
|
54175
|
+
if (typeof fromList === "number") decimalsByAsset[asset] = fromList;
|
|
54176
|
+
}
|
|
54177
|
+
const assetsMissingDecimals = unifiedAssets.filter(
|
|
54178
|
+
(asset) => decimalsByAsset[asset] === void 0
|
|
54179
|
+
);
|
|
54180
|
+
const decimalsCalls = assetsMissingDecimals.map((address) => ({
|
|
54181
|
+
name: "decimals",
|
|
54182
|
+
address
|
|
54183
|
+
}));
|
|
53909
54184
|
const calls = [
|
|
53910
54185
|
...aaveCalls,
|
|
53911
54186
|
...balancerV2Calls,
|
|
@@ -53913,6 +54188,8 @@ async function fetchFlashLiquidityForChain(chain, multicallRetry, list = {}) {
|
|
|
53913
54188
|
...balancerV3Calls,
|
|
53914
54189
|
...uniswapV4Calls
|
|
53915
54190
|
];
|
|
54191
|
+
const decimalsOffset = calls.length;
|
|
54192
|
+
calls.push(...decimalsCalls);
|
|
53916
54193
|
const rawResults = await multicallRetry({
|
|
53917
54194
|
chain,
|
|
53918
54195
|
calls,
|
|
@@ -53922,6 +54199,10 @@ async function fetchFlashLiquidityForChain(chain, multicallRetry, list = {}) {
|
|
|
53922
54199
|
providerId: 0,
|
|
53923
54200
|
allowFailure: true
|
|
53924
54201
|
});
|
|
54202
|
+
assetsMissingDecimals.forEach((asset, i) => {
|
|
54203
|
+
const parsed = parseDecimalsResult(rawResults[decimalsOffset + i]);
|
|
54204
|
+
if (parsed !== void 0) decimalsByAsset[asset] = parsed;
|
|
54205
|
+
});
|
|
53925
54206
|
let liquidity = {};
|
|
53926
54207
|
let currentOffset = 0;
|
|
53927
54208
|
aaveProtocols.forEach((aave) => {
|
|
@@ -53934,7 +54215,7 @@ async function fetchFlashLiquidityForChain(chain, multicallRetry, list = {}) {
|
|
|
53934
54215
|
const rawAmount = data[2 * i];
|
|
53935
54216
|
const config = data[2 * i + 1];
|
|
53936
54217
|
if (typeof rawAmount !== "bigint" || typeof config !== "bigint") return;
|
|
53937
|
-
const decimals =
|
|
54218
|
+
const decimals = decimalsByAsset[asset];
|
|
53938
54219
|
const enabled = !AAVE_V3_LENDERS.includes(aave) || getFlashLoanEnabled(config);
|
|
53939
54220
|
if (enabled && rawAmount > 0n && FLASH_LOAN_IDS[aave] !== void 0) {
|
|
53940
54221
|
if (!liquidity[asset]) liquidity[asset] = [];
|
|
@@ -53945,7 +54226,7 @@ async function fetchFlashLiquidityForChain(chain, multicallRetry, list = {}) {
|
|
|
53945
54226
|
source: getAaveTypePoolAddress(chain, aave),
|
|
53946
54227
|
fee: fee.toString(),
|
|
53947
54228
|
availableRaw: rawAmount.toString(),
|
|
53948
|
-
available: Number(formatUnits(rawAmount, decimals
|
|
54229
|
+
available: decimals === void 0 ? void 0 : Number(formatUnits(rawAmount, decimals)),
|
|
53949
54230
|
decimals
|
|
53950
54231
|
});
|
|
53951
54232
|
}
|
|
@@ -53956,7 +54237,7 @@ async function fetchFlashLiquidityForChain(chain, multicallRetry, list = {}) {
|
|
|
53956
54237
|
const data = rawResults.slice(currentOffset, callLen + currentOffset);
|
|
53957
54238
|
currentOffset += callLen;
|
|
53958
54239
|
unifiedAssets.forEach((asset, i) => {
|
|
53959
|
-
const decimals =
|
|
54240
|
+
const decimals = decimalsByAsset[asset];
|
|
53960
54241
|
const rawAmount = data[i];
|
|
53961
54242
|
if (isValidResult(rawAmount) && rawAmount > 0n) {
|
|
53962
54243
|
if (!liquidity[asset]) liquidity[asset] = [];
|
|
@@ -53967,7 +54248,7 @@ async function fetchFlashLiquidityForChain(chain, multicallRetry, list = {}) {
|
|
|
53967
54248
|
source: balancer.address,
|
|
53968
54249
|
fee: "0",
|
|
53969
54250
|
availableRaw: rawAmount.toString(),
|
|
53970
|
-
available: Number(formatUnits(rawAmount, decimals
|
|
54251
|
+
available: decimals === void 0 ? void 0 : Number(formatUnits(rawAmount, decimals)),
|
|
53971
54252
|
decimals
|
|
53972
54253
|
});
|
|
53973
54254
|
}
|
|
@@ -53981,7 +54262,7 @@ async function fetchFlashLiquidityForChain(chain, multicallRetry, list = {}) {
|
|
|
53981
54262
|
const rawAmount = data[i];
|
|
53982
54263
|
if (isValidResult(rawAmount) && rawAmount > 0n) {
|
|
53983
54264
|
if (!liquidity[asset]) liquidity[asset] = [];
|
|
53984
|
-
const decimals =
|
|
54265
|
+
const decimals = decimalsByAsset[asset];
|
|
53985
54266
|
liquidity[asset].push({
|
|
53986
54267
|
id: FLASH_LOAN_IDS[morpho.pool],
|
|
53987
54268
|
name: morpho.pool,
|
|
@@ -53989,7 +54270,7 @@ async function fetchFlashLiquidityForChain(chain, multicallRetry, list = {}) {
|
|
|
53989
54270
|
source: morpho.address,
|
|
53990
54271
|
fee: "0",
|
|
53991
54272
|
availableRaw: rawAmount.toString(),
|
|
53992
|
-
available: Number(formatUnits(rawAmount, decimals
|
|
54273
|
+
available: decimals === void 0 ? void 0 : Number(formatUnits(rawAmount, decimals)),
|
|
53993
54274
|
decimals
|
|
53994
54275
|
});
|
|
53995
54276
|
}
|
|
@@ -54000,7 +54281,7 @@ async function fetchFlashLiquidityForChain(chain, multicallRetry, list = {}) {
|
|
|
54000
54281
|
const data = rawResults.slice(currentOffset, callLen + currentOffset);
|
|
54001
54282
|
currentOffset += callLen;
|
|
54002
54283
|
unifiedAssets.forEach((asset, i) => {
|
|
54003
|
-
const decimals =
|
|
54284
|
+
const decimals = decimalsByAsset[asset];
|
|
54004
54285
|
const rawAmount = data[i];
|
|
54005
54286
|
if (isValidResult(rawAmount) && rawAmount > 0n) {
|
|
54006
54287
|
if (!liquidity[asset]) liquidity[asset] = [];
|
|
@@ -54011,7 +54292,7 @@ async function fetchFlashLiquidityForChain(chain, multicallRetry, list = {}) {
|
|
|
54011
54292
|
source: balancerV3.address,
|
|
54012
54293
|
fee: "0",
|
|
54013
54294
|
availableRaw: rawAmount.toString(),
|
|
54014
|
-
available: Number(formatUnits(rawAmount, decimals
|
|
54295
|
+
available: decimals === void 0 ? void 0 : Number(formatUnits(rawAmount, decimals)),
|
|
54015
54296
|
decimals
|
|
54016
54297
|
});
|
|
54017
54298
|
}
|
|
@@ -54022,7 +54303,7 @@ async function fetchFlashLiquidityForChain(chain, multicallRetry, list = {}) {
|
|
|
54022
54303
|
const data = rawResults.slice(currentOffset, callLen + currentOffset);
|
|
54023
54304
|
currentOffset += callLen;
|
|
54024
54305
|
unifiedAssets.forEach((asset, i) => {
|
|
54025
|
-
const decimals =
|
|
54306
|
+
const decimals = decimalsByAsset[asset];
|
|
54026
54307
|
const rawAmount = data[i];
|
|
54027
54308
|
if (isValidResult(rawAmount) && rawAmount > 0n) {
|
|
54028
54309
|
if (!liquidity[asset]) liquidity[asset] = [];
|
|
@@ -54033,7 +54314,7 @@ async function fetchFlashLiquidityForChain(chain, multicallRetry, list = {}) {
|
|
|
54033
54314
|
source: uniV4.address,
|
|
54034
54315
|
fee: "0",
|
|
54035
54316
|
availableRaw: rawAmount.toString(),
|
|
54036
|
-
available: Number(formatUnits(rawAmount, decimals
|
|
54317
|
+
available: decimals === void 0 ? void 0 : Number(formatUnits(rawAmount, decimals)),
|
|
54037
54318
|
decimals
|
|
54038
54319
|
});
|
|
54039
54320
|
}
|
|
@@ -54050,7 +54331,9 @@ function attachPricesToFlashLiquidity(chainId, liq, prices, list = {}) {
|
|
|
54050
54331
|
const price2 = prices[priceKey] ?? 0;
|
|
54051
54332
|
liqCopy[asset] = entry.map((e) => ({
|
|
54052
54333
|
...e,
|
|
54053
|
-
|
|
54334
|
+
// absent when the token amount itself is unknown — never 0, which reads
|
|
54335
|
+
// as "no liquidity" rather than "not priced"
|
|
54336
|
+
availableUSD: e.available === void 0 ? void 0 : e.available * price2
|
|
54054
54337
|
}));
|
|
54055
54338
|
});
|
|
54056
54339
|
return liqCopy;
|
|
@@ -65653,9 +65936,8 @@ function borrowDescription(b) {
|
|
|
65653
65936
|
b.rate.minApr != null && b.rate.maxApr != null ? `You choose your own rate between ${pct(b.rate.minApr)} and ${pct(b.rate.maxApr)}; a lower rate is cheaper but makes you redeemed first.` : "You choose your own interest rate; a lower rate is cheaper but makes you redeemed first."
|
|
65654
65937
|
);
|
|
65655
65938
|
} else {
|
|
65656
|
-
|
|
65657
|
-
|
|
65658
|
-
);
|
|
65939
|
+
const how = b.rate.isLocked ? ", locked for the term" : b.rate.kind === "variable-curve" ? ", floating with utilization" : b.rate.kind === "variable-managed" ? ", set by the protocol rather than floating with utilization" : "";
|
|
65940
|
+
parts.push(`You pay ${pct(b.rate.apr)}${how}.`);
|
|
65659
65941
|
}
|
|
65660
65942
|
parts.push(
|
|
65661
65943
|
b.debtShape === "static-face" ? "The amount owed is fixed at trade time and does not accrue." : b.debtShape === "prepaid" ? "The debt principal is static; the cost is the prepaid interest token burning down." : "The amount owed grows continuously as interest accrues."
|
|
@@ -65674,11 +65956,27 @@ function borrowDescription(b) {
|
|
|
65674
65956
|
`It matures on ${shortDate(b.maturity.maturity)}${what ? `; if nothing is done then, ${what}` : ""}.`
|
|
65675
65957
|
);
|
|
65676
65958
|
}
|
|
65959
|
+
const withheld = (b.fees ?? []).filter(
|
|
65960
|
+
(f) => f.when === "entry" && f.basis === "principal" && f.unit === "percent" && f.value > 0
|
|
65961
|
+
);
|
|
65962
|
+
if (withheld.length > 0) {
|
|
65963
|
+
const total = withheld.reduce((a, f) => a + f.value, 0);
|
|
65964
|
+
const named = withheld.map((f) => `${f.label.toLowerCase()} ${pct(f.value)}`).join(" + ");
|
|
65965
|
+
parts.push(
|
|
65966
|
+
`${pct(total)} of anything you borrow is withheld at open` + (withheld.length > 1 ? ` (${named})` : "") + `, so you receive ${pct(100 - total)} of what you owe.`
|
|
65967
|
+
);
|
|
65968
|
+
}
|
|
65677
65969
|
if (b.liquidation.trigger === "time") {
|
|
65678
65970
|
parts.push(
|
|
65679
65971
|
"Liquidation here is triggered by TIME, not price \u2014 being late is the trigger, and being over-collateralised does not protect you."
|
|
65680
65972
|
);
|
|
65681
|
-
} else if (b.liquidation.liquidationLtv
|
|
65973
|
+
} else if (b.liquidation.model === "auction" && !(b.liquidation.liquidationLtv > 0)) {
|
|
65974
|
+
const pen = b.liquidation.penalty;
|
|
65975
|
+
const win = b.liquidation.windowSecs;
|
|
65976
|
+
parts.push(
|
|
65977
|
+
"Liquidation is a public auction rather than a loan-to-value threshold" + (win ? `, open for ${duration(win)} once started` : "") + (pen != null ? `, and the liquidator is paid ${pct(pen * 100)}` : "") + "."
|
|
65978
|
+
);
|
|
65979
|
+
} else if (b.liquidation.liquidationLtv != null && b.liquidation.liquidationLtv > 0) {
|
|
65682
65980
|
const pen = b.liquidation.penalty;
|
|
65683
65981
|
parts.push(
|
|
65684
65982
|
`Liquidation starts at ${pct(b.liquidation.liquidationLtv * 100)} LTV` + (pen != null ? `, with a ${pct(pen * 100)} penalty` : "") + "."
|