@1delta/margin-fetcher 5.0.8 → 5.0.10
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 +134 -22
- package/dist/index.js +318 -28
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { fetchTokenLists, fetchTokenList, aavePools, compoundV3Pools, initConfig
|
|
|
7
7
|
import lodash from 'lodash';
|
|
8
8
|
import { Chain } from '@1delta/chain-registry';
|
|
9
9
|
import { multicallRetryUniversal, getEvmClient, getEvmChain, getEvmClientUniversal } from '@1delta/providers';
|
|
10
|
-
import { LiquityTroveManagerAbi, LiquityActivePoolAbi, LiquityStabilityPoolAbi, LiquityPriceFeedAbi, LiquitySortedTrovesAbi, RiverTroveManagerAbi, RiverStabilityPoolAbi, TellerMarketRegistryAbi, TellerV2Abi, InverseMarketAbi, InverseOracleAbi, InverseDbrAbi, Erc20Abi, LlamaLendControllerAbi, LlamaLendControllerV1Abi, LlamaLendControllerV2Abi, LlamaLendVaultAbi, LlamaLendAmmAbi, MetaMorphoAbi, ExactlyPreviewerAbi, ExactlyAuditorAbi, LenderCommitmentGroupAbi, ResupplyRegistryAbi, ResupplyPairAbi, UsddVatAbi, UsddJugAbi, UsddSpotAbi, FrankencoinPositionAbi, FluidLendingResolverAbi, FluidVaultResolverAbi, FluidLiquidityResolverAbi, MoolahVaultAbi,
|
|
10
|
+
import { LiquityTroveManagerAbi, LiquityActivePoolAbi, LiquityStabilityPoolAbi, LiquityPriceFeedAbi, LiquitySortedTrovesAbi, RiverTroveManagerAbi, RiverStabilityPoolAbi, TellerMarketRegistryAbi, TellerV2Abi, InverseMarketAbi, InverseOracleAbi, InverseDbrAbi, Erc20Abi, LlamaLendControllerAbi, LlamaLendControllerV1Abi, LlamaLendControllerV2Abi, LlamaLendVaultAbi, LlamaLendAmmAbi, MetaMorphoAbi, ExactlyPreviewerAbi, ExactlyAuditorAbi, LenderCommitmentGroupAbi, ResupplyRegistryAbi, ResupplyPairAbi, ResupplyUtilitiesAbi, ResupplyRewardHandlerAbi, ResupplyPairEmissionsAbi, ConvexPoolUtilAbi, UsddVatAbi, UsddJugAbi, UsddSpotAbi, FrankencoinPositionAbi, FluidLendingResolverAbi, FluidVaultResolverAbi, FluidLiquidityResolverAbi, MoolahVaultAbi, MorphoLensAbi, AaveV4SpokeAbi, AaveV4OracleAbi, AaveV4HubAbi, DolomiteMarginAbi, GearboxMarketCompressorV310Abi, MorphoBlueAbi, MidnightAbi, TermRepoTokenAbi, TermRepoServicerAbi, TermRepoCollateralManagerAbi, LiquityTroveNFTAbi, LiquityCollSurplusPoolAbi, TellerCollateralManagerAbi, TermMaxViewerAbi, InverseEscrowAbi, CurvanceMarketManagerAbi, CurvanceCTokenAbi, GearboxCreditAccountCompressorV310Abi, UsddCdpManagerAbi, UsddProxyRegistryAbi, CurvanceProtocolReaderAbi, CurvanceCentralRegistryAbi, TermPriceConsumerAbi, CurvanceOracleManagerAbi, TermMaxOracleAggregatorV2Abi } from '@1delta/abis';
|
|
11
11
|
export { MorphoLensAbi } from '@1delta/abis';
|
|
12
12
|
import { prepareDebitDataMulticall, prepareLenderDebitMulticall, parseDebitDataResult, parseLenderDebitResult, getPermit2ContractAddress, getCompoundV3CometAddress as getCompoundV3CometAddress$1, getMorphoAddress, getAaveCollateralTokenAddress, getSiloHalfForUnderlying, InitMarginAddresses, getLstAcceptedInputs } from '@1delta/calldata-sdk';
|
|
13
13
|
import { proxyNativeFetch } from '@1delta/proxy-fetch';
|
|
@@ -21401,6 +21401,19 @@ function convertLiquityMarketsToResponse(raw, chainId, prices = {}, _additionalY
|
|
|
21401
21401
|
borrowLiquidity = Math.min(borrowLiquidity, Math.max(0, cap - entireDebt));
|
|
21402
21402
|
}
|
|
21403
21403
|
if (shutdown) borrowLiquidity = 0;
|
|
21404
|
+
const rateParameter = {
|
|
21405
|
+
kind: "interest-rate",
|
|
21406
|
+
dimension: "rate",
|
|
21407
|
+
domain: {
|
|
21408
|
+
min: Number(cfg.minAnnualInterestRate ?? 0) / 1e18,
|
|
21409
|
+
max: Number(cfg.maxAnnualInterestRate ?? 0) / 1e18
|
|
21410
|
+
},
|
|
21411
|
+
// No protocol default — the borrower must choose. The low end is the
|
|
21412
|
+
// cheapest but the first to be redeemed against.
|
|
21413
|
+
default: Number(cfg.minAnnualInterestRate ?? 0) / 1e18,
|
|
21414
|
+
immutableAfterOpen: false,
|
|
21415
|
+
adjustCooldownSeconds: cfg.interestRateAdjCooldownSeconds ? Number(cfg.interestRateAdjCooldownSeconds) : void 0
|
|
21416
|
+
};
|
|
21404
21417
|
const entry = { data: {} };
|
|
21405
21418
|
const collUid = createMarketUid(chainId, lenderKey, collAddr);
|
|
21406
21419
|
entry.data[collUid] = {
|
|
@@ -21435,7 +21448,18 @@ function convertLiquityMarketsToResponse(raw, chainId, prices = {}, _additionalY
|
|
|
21435
21448
|
liquidationPenalty: liqPenalty,
|
|
21436
21449
|
closeFactor: 1,
|
|
21437
21450
|
collateralDisabled: false,
|
|
21438
|
-
debtDisabled: true
|
|
21451
|
+
debtDisabled: true,
|
|
21452
|
+
/**
|
|
21453
|
+
* Liquity's borrower picks their own interest rate at open, and it
|
|
21454
|
+
* sets redemption priority as well as cost — so it is a real
|
|
21455
|
+
* parameter, not a quoted rate.
|
|
21456
|
+
*
|
|
21457
|
+
* The MUTABLE case: adjustable in place, but rate-limited by
|
|
21458
|
+
* `interestRateAdjCooldownSeconds` and fee-charged if adjusted
|
|
21459
|
+
* early. The per-trove value lives in `modes[troveId]`, because one
|
|
21460
|
+
* address can hold several troves at different rates at once.
|
|
21461
|
+
*/
|
|
21462
|
+
openParameter: rateParameter
|
|
21439
21463
|
}
|
|
21440
21464
|
},
|
|
21441
21465
|
closeFactor: 1,
|
|
@@ -23028,7 +23052,11 @@ async function fetchChainExtras(chainId, markets) {
|
|
|
23028
23052
|
abi: LLAMALEND_READ_ABI,
|
|
23029
23053
|
allowFailure: true
|
|
23030
23054
|
});
|
|
23031
|
-
} catch {
|
|
23055
|
+
} catch (e) {
|
|
23056
|
+
console.error(
|
|
23057
|
+
`[llamalend] chain-extras multicall failed on chain ${chainId} for ${markets.length} market(s) \u2014 LTV/price/band state unavailable this cycle:`,
|
|
23058
|
+
e?.message ?? e
|
|
23059
|
+
);
|
|
23032
23060
|
return {};
|
|
23033
23061
|
}
|
|
23034
23062
|
const out = {};
|
|
@@ -23201,7 +23229,7 @@ var VAULT_PRICE_ABI = [
|
|
|
23201
23229
|
}
|
|
23202
23230
|
];
|
|
23203
23231
|
var IDENTITY_READS = 3;
|
|
23204
|
-
var STATE_READS =
|
|
23232
|
+
var STATE_READS = 14;
|
|
23205
23233
|
var ONE = 10n ** 18n;
|
|
23206
23234
|
var identityCache = /* @__PURE__ */ new Map();
|
|
23207
23235
|
var identityKey = (chainId, pair) => `${chainId}:${pair.toLowerCase()}`;
|
|
@@ -23308,6 +23336,11 @@ async function fetchResupplyMarkets(lender, chainId) {
|
|
|
23308
23336
|
}
|
|
23309
23337
|
const identities = discovered.map((p) => identityCache.get(identityKey(chainId, p))).filter((x) => !!x);
|
|
23310
23338
|
if (identities.length === 0) return { lender, config, pairs: [] };
|
|
23339
|
+
const { rsup, pairEmissions, convexPoolUtil } = await readRewardContext(
|
|
23340
|
+
chainId,
|
|
23341
|
+
config.registry,
|
|
23342
|
+
config.utilities
|
|
23343
|
+
);
|
|
23311
23344
|
const utilities = config.utilities;
|
|
23312
23345
|
const calls = [];
|
|
23313
23346
|
const abis = [];
|
|
@@ -23324,7 +23357,16 @@ async function fetchResupplyMarkets(lender, chainId) {
|
|
|
23324
23357
|
// The collateral vault's own share price — the exact input Resupply's
|
|
23325
23358
|
// `BasicVaultOracle` uses. Read LIVE rather than inverting the pair's
|
|
23326
23359
|
// cached `exchangeRate`, which only moves when someone writes.
|
|
23327
|
-
{ address: id.collateral, name: "convertToAssets", params: [ONE] }
|
|
23360
|
+
{ address: id.collateral, name: "convertToAssets", params: [ONE] },
|
|
23361
|
+
// Rewards. `convexPid == 0` means the collateral is not staked, so there
|
|
23362
|
+
// is nothing to report; the weight is this pair's slice of the RSUP
|
|
23363
|
+
// stream. Both are governance-mutable, so neither is cached.
|
|
23364
|
+
{ address: id.pair, name: "convexPid", params: [] },
|
|
23365
|
+
{
|
|
23366
|
+
address: pairEmissions ?? id.pair,
|
|
23367
|
+
name: pairEmissions ? "balanceOf" : "convexPid",
|
|
23368
|
+
params: pairEmissions ? [id.pair] : []
|
|
23369
|
+
}
|
|
23328
23370
|
);
|
|
23329
23371
|
abis.push(
|
|
23330
23372
|
ResupplyPairAbi,
|
|
@@ -23335,7 +23377,9 @@ async function fetchResupplyMarkets(lender, chainId) {
|
|
|
23335
23377
|
ResupplyPairAbi,
|
|
23336
23378
|
ResupplyPairAbi,
|
|
23337
23379
|
ResupplyPairAbi,
|
|
23338
|
-
VAULT_PRICE_ABI
|
|
23380
|
+
VAULT_PRICE_ABI,
|
|
23381
|
+
ResupplyPairAbi,
|
|
23382
|
+
pairEmissions ? ResupplyPairEmissionsAbi : ResupplyPairAbi
|
|
23339
23383
|
);
|
|
23340
23384
|
if (utilities) {
|
|
23341
23385
|
calls.push(
|
|
@@ -23393,11 +23437,118 @@ async function fetchResupplyMarkets(lender, chainId) {
|
|
|
23393
23437
|
ratePerSec: tuple(rateInfo, 1),
|
|
23394
23438
|
exchangeRate: tuple(exchangeInfo, 2),
|
|
23395
23439
|
collateralPrice: big(results[base + 8]),
|
|
23396
|
-
|
|
23397
|
-
|
|
23440
|
+
// Indices follow the PUSH ORDER above exactly: the two reward reads sit
|
|
23441
|
+
// at +9/+10, BEFORE the lens block, so the lens reads are +11/+12.
|
|
23442
|
+
// (Getting this wrong is silent — it produced a 3-billion-percent APR
|
|
23443
|
+
// before the numbers were checked against a hand computation.)
|
|
23444
|
+
convexPid: big(results[base + 9]),
|
|
23445
|
+
rsupWeight: pairEmissions ? big(results[base + 10]) : null,
|
|
23446
|
+
liveRatePerSec: utilities ? big(results[base + 11]) : null,
|
|
23447
|
+
underlyingSupplyRatePerSec: utilities ? big(results[base + 12]) : null,
|
|
23448
|
+
collateralRewards: []
|
|
23398
23449
|
};
|
|
23399
23450
|
});
|
|
23400
|
-
|
|
23451
|
+
await attachCollateralRewards(chainId, convexPoolUtil, pairs);
|
|
23452
|
+
return { lender, config, pairs, rsup };
|
|
23453
|
+
}
|
|
23454
|
+
async function readRewardContext(chainId, registry, utilities) {
|
|
23455
|
+
const addr2 = (v) => typeof v === "string" && v.startsWith("0x") && !/^0x0+$/.test(v) ? v : void 0;
|
|
23456
|
+
try {
|
|
23457
|
+
const first = await multicallRetryUniversal({
|
|
23458
|
+
chain: chainId,
|
|
23459
|
+
calls: [
|
|
23460
|
+
{ address: registry, name: "rewardHandler", params: [] },
|
|
23461
|
+
{ address: registry, name: "govToken", params: [] },
|
|
23462
|
+
...utilities ? [{ address: utilities, name: "convexPoolUtil", params: [] }] : []
|
|
23463
|
+
],
|
|
23464
|
+
abi: [
|
|
23465
|
+
ResupplyRegistryAbi,
|
|
23466
|
+
ResupplyRegistryAbi,
|
|
23467
|
+
...utilities ? [ResupplyUtilitiesAbi] : []
|
|
23468
|
+
],
|
|
23469
|
+
allowFailure: true
|
|
23470
|
+
});
|
|
23471
|
+
const rewardHandler = addr2(first[0]);
|
|
23472
|
+
const govToken = addr2(first[1]);
|
|
23473
|
+
const convexPoolUtil = utilities ? addr2(first[2]) : void 0;
|
|
23474
|
+
if (!rewardHandler || !govToken) return { convexPoolUtil };
|
|
23475
|
+
const [emissions] = await multicallRetryUniversal({
|
|
23476
|
+
chain: chainId,
|
|
23477
|
+
calls: [{ address: rewardHandler, name: "pairEmissions", params: [] }],
|
|
23478
|
+
abi: ResupplyRewardHandlerAbi,
|
|
23479
|
+
allowFailure: true
|
|
23480
|
+
});
|
|
23481
|
+
const pairEmissions = addr2(emissions);
|
|
23482
|
+
if (!pairEmissions) return { convexPoolUtil };
|
|
23483
|
+
const stream = await multicallRetryUniversal({
|
|
23484
|
+
chain: chainId,
|
|
23485
|
+
calls: [
|
|
23486
|
+
{ address: pairEmissions, name: "periodFinish", params: [] },
|
|
23487
|
+
{ address: pairEmissions, name: "rewardRate", params: [] },
|
|
23488
|
+
{ address: pairEmissions, name: "totalSupply", params: [] }
|
|
23489
|
+
],
|
|
23490
|
+
abi: [
|
|
23491
|
+
ResupplyPairEmissionsAbi,
|
|
23492
|
+
ResupplyPairEmissionsAbi,
|
|
23493
|
+
ResupplyPairEmissionsAbi
|
|
23494
|
+
],
|
|
23495
|
+
allowFailure: true
|
|
23496
|
+
});
|
|
23497
|
+
const periodFinish = big(stream[0]);
|
|
23498
|
+
const rewardRate = big(stream[1]);
|
|
23499
|
+
const totalWeight = big(stream[2]);
|
|
23500
|
+
if (periodFinish === null || rewardRate === null || totalWeight === null || totalWeight === 0n) {
|
|
23501
|
+
return { pairEmissions, convexPoolUtil };
|
|
23502
|
+
}
|
|
23503
|
+
if (periodFinish <= BigInt(Math.floor(Date.now() / 1e3))) {
|
|
23504
|
+
return { pairEmissions, convexPoolUtil };
|
|
23505
|
+
}
|
|
23506
|
+
return {
|
|
23507
|
+
rsup: { govToken, rewardRate, totalWeight, periodFinish },
|
|
23508
|
+
pairEmissions,
|
|
23509
|
+
convexPoolUtil
|
|
23510
|
+
};
|
|
23511
|
+
} catch {
|
|
23512
|
+
return {};
|
|
23513
|
+
}
|
|
23514
|
+
}
|
|
23515
|
+
async function attachCollateralRewards(chainId, convexPoolUtil, pairs) {
|
|
23516
|
+
if (!convexPoolUtil) return;
|
|
23517
|
+
const staked = pairs.filter((p) => (p.convexPid ?? 0n) > 0n);
|
|
23518
|
+
if (staked.length === 0) return;
|
|
23519
|
+
let res = [];
|
|
23520
|
+
try {
|
|
23521
|
+
res = await multicallRetryUniversal({
|
|
23522
|
+
chain: chainId,
|
|
23523
|
+
calls: staked.map((p) => ({
|
|
23524
|
+
address: convexPoolUtil,
|
|
23525
|
+
name: "rewardRates",
|
|
23526
|
+
params: [p.convexPid]
|
|
23527
|
+
})),
|
|
23528
|
+
abi: staked.map(() => ConvexPoolUtilAbi),
|
|
23529
|
+
allowFailure: true
|
|
23530
|
+
});
|
|
23531
|
+
} catch {
|
|
23532
|
+
return;
|
|
23533
|
+
}
|
|
23534
|
+
staked.forEach((p, i) => {
|
|
23535
|
+
const entry = res[i];
|
|
23536
|
+
if (!Array.isArray(entry)) return;
|
|
23537
|
+
const [tokens, rates] = entry;
|
|
23538
|
+
if (!Array.isArray(tokens) || !Array.isArray(rates)) return;
|
|
23539
|
+
const byToken = /* @__PURE__ */ new Map();
|
|
23540
|
+
tokens.forEach((t, j) => {
|
|
23541
|
+
const rate = rates[j];
|
|
23542
|
+
if (typeof t !== "string" || typeof rate !== "bigint" || rate === 0n) {
|
|
23543
|
+
return;
|
|
23544
|
+
}
|
|
23545
|
+
const key2 = t.toLowerCase();
|
|
23546
|
+
byToken.set(key2, (byToken.get(key2) ?? 0n) + rate);
|
|
23547
|
+
});
|
|
23548
|
+
p.collateralRewards = [...byToken.entries()].map(
|
|
23549
|
+
([token, ratePerSecPerShare]) => ({ token, ratePerSecPerShare })
|
|
23550
|
+
);
|
|
23551
|
+
});
|
|
23401
23552
|
}
|
|
23402
23553
|
|
|
23403
23554
|
// src/prices/oracle-prices/fetchers/curvanceRoster.ts
|
|
@@ -23987,6 +24138,8 @@ function currencyFor9(address, decimals, symbol, tokens) {
|
|
|
23987
24138
|
const lower3 = address.toLowerCase();
|
|
23988
24139
|
return tokens[lower3] ?? { address: lower3, symbol, name: symbol, decimals };
|
|
23989
24140
|
}
|
|
24141
|
+
var LLAMALEND_MIN_BANDS = 4;
|
|
24142
|
+
var LLAMALEND_MAX_BANDS = 50;
|
|
23990
24143
|
function convertLlamaLendMarketsToResponse(raw, chainId, prices = {}, additionalYields = {
|
|
23991
24144
|
intrinsicYields: {},
|
|
23992
24145
|
lenderRewards: {},
|
|
@@ -24019,10 +24172,17 @@ function convertLlamaLendMarketsToResponse(raw, chainId, prices = {}, additional
|
|
|
24019
24172
|
const utilization = reserves > 0 ? totalDebt / reserves : 0;
|
|
24020
24173
|
const borrowRatePct = (m.borrowApr ?? 0) * 100;
|
|
24021
24174
|
const depositRatePct = (m.lendApr ?? 0) * 100;
|
|
24022
|
-
const ltv = m.collateralFactor ??
|
|
24175
|
+
const ltv = m.collateralFactor ?? null;
|
|
24023
24176
|
const liqPenalty = Number(market.liquidationDiscount) / 1e18 || 0;
|
|
24024
24177
|
const closeFactor = 1;
|
|
24025
24178
|
const depositsEnabled = (m.maxDeposit ?? 0) > 0;
|
|
24179
|
+
const bandParameter = {
|
|
24180
|
+
kind: "llamalend-bands",
|
|
24181
|
+
dimension: "collateralFactor",
|
|
24182
|
+
domain: { min: LLAMALEND_MIN_BANDS, max: LLAMALEND_MAX_BANDS },
|
|
24183
|
+
default: market.defaultBands ?? 10,
|
|
24184
|
+
immutableAfterOpen: true
|
|
24185
|
+
};
|
|
24026
24186
|
const entry = { data: {} };
|
|
24027
24187
|
const collUid = createMarketUid(chainId, lenderKey, collAddr);
|
|
24028
24188
|
entry.data[collUid] = {
|
|
@@ -24060,7 +24220,8 @@ function convertLlamaLendMarketsToResponse(raw, chainId, prices = {}, additional
|
|
|
24060
24220
|
liquidationPenalty: liqPenalty,
|
|
24061
24221
|
closeFactor,
|
|
24062
24222
|
collateralDisabled: false,
|
|
24063
|
-
debtDisabled: true
|
|
24223
|
+
debtDisabled: true,
|
|
24224
|
+
openParameter: bandParameter
|
|
24064
24225
|
}
|
|
24065
24226
|
},
|
|
24066
24227
|
closeFactor,
|
|
@@ -24128,7 +24289,10 @@ function convertLlamaLendMarketsToResponse(raw, chainId, prices = {}, additional
|
|
|
24128
24289
|
collateralDecimals: collDecimals,
|
|
24129
24290
|
// The Controller doubles as the market id.
|
|
24130
24291
|
id: market.controller.toLowerCase(),
|
|
24131
|
-
|
|
24292
|
+
// Mirrors the Morpho shape. Empty string, not "0": a consumer parsing
|
|
24293
|
+
// this as a number gets NaN and can branch, where "0" would silently
|
|
24294
|
+
// become a zero LTV. The real curve is in `llamalend.bandLtv`.
|
|
24295
|
+
lltv: ltv !== null ? String(ltv) : "",
|
|
24132
24296
|
oracle: market.priceOracle ?? market.amm,
|
|
24133
24297
|
irm: market.monetaryPolicy ?? zeroAddress,
|
|
24134
24298
|
collateralAddress: collAddr,
|
|
@@ -24256,6 +24420,52 @@ function ratePerSecToApr(rate) {
|
|
|
24256
24420
|
if (rate === null) return 0;
|
|
24257
24421
|
return Number(rate) / 1e18 * SECONDS_PER_YEAR9 * 100;
|
|
24258
24422
|
}
|
|
24423
|
+
function buildRewardEntries(p, rsup, chainId, prices, tokens, collateralUsd, debtUsd) {
|
|
24424
|
+
const priceOf = (address) => {
|
|
24425
|
+
const lower3 = address.toLowerCase();
|
|
24426
|
+
const token = tokens[lower3];
|
|
24427
|
+
const key2 = toOracleKey(token?.assetGroup) || toGenericPriceKey(lower3, chainId);
|
|
24428
|
+
return prices[key2] ?? 0;
|
|
24429
|
+
};
|
|
24430
|
+
const collateral = [];
|
|
24431
|
+
if (collateralUsd > 0) {
|
|
24432
|
+
for (const r of p.collateralRewards ?? []) {
|
|
24433
|
+
const price2 = priceOf(r.token);
|
|
24434
|
+
if (price2 <= 0) continue;
|
|
24435
|
+
const decimals = tokens[r.token.toLowerCase()]?.decimals ?? 18;
|
|
24436
|
+
const perSecond = Number(r.ratePerSecPerShare) * Number(p.totalCollateral ?? 0n) / 1e18;
|
|
24437
|
+
const usdPerYear = perSecond / 10 ** decimals * SECONDS_PER_YEAR9 * price2;
|
|
24438
|
+
const apr = usdPerYear / collateralUsd * 100;
|
|
24439
|
+
if (!Number.isFinite(apr) || apr <= 0) continue;
|
|
24440
|
+
collateral.push({
|
|
24441
|
+
asset: r.token,
|
|
24442
|
+
depositRate: apr,
|
|
24443
|
+
variableBorrowRate: 0,
|
|
24444
|
+
stableBorrowRate: 0
|
|
24445
|
+
});
|
|
24446
|
+
}
|
|
24447
|
+
}
|
|
24448
|
+
const borrow = [];
|
|
24449
|
+
if (rsup && debtUsd > 0 && (p.rsupWeight ?? 0n) > 0n) {
|
|
24450
|
+
const price2 = priceOf(rsup.govToken);
|
|
24451
|
+
if (price2 > 0) {
|
|
24452
|
+
const decimals = tokens[rsup.govToken.toLowerCase()]?.decimals ?? 18;
|
|
24453
|
+
const perSecond = Number(rsup.rewardRate) * Number(p.rsupWeight) / Number(rsup.totalWeight);
|
|
24454
|
+
const usdPerYear = perSecond / 10 ** decimals * SECONDS_PER_YEAR9 * price2;
|
|
24455
|
+
const apr = usdPerYear / debtUsd * 100;
|
|
24456
|
+
if (Number.isFinite(apr) && apr > 0) {
|
|
24457
|
+
borrow.push({
|
|
24458
|
+
asset: rsup.govToken,
|
|
24459
|
+
depositRate: 0,
|
|
24460
|
+
// A REBATE on the borrow cost, not a charge.
|
|
24461
|
+
variableBorrowRate: apr,
|
|
24462
|
+
stableBorrowRate: 0
|
|
24463
|
+
});
|
|
24464
|
+
}
|
|
24465
|
+
}
|
|
24466
|
+
}
|
|
24467
|
+
return { collateral, borrow };
|
|
24468
|
+
}
|
|
24259
24469
|
function convertResupplyMarketsToResponse(raw, chainId, prices = {}, _additionalYields = {
|
|
24260
24470
|
intrinsicYields: {},
|
|
24261
24471
|
lenderRewards: {},
|
|
@@ -24291,6 +24501,15 @@ function convertResupplyMarketsToResponse(raw, chainId, prices = {}, _additional
|
|
|
24291
24501
|
const borrowLiquidity = Math.max(borrowLimit - totalDebt, 0);
|
|
24292
24502
|
const halted = (p.borrowLimit ?? 0n) === 0n;
|
|
24293
24503
|
const wrappedMarket = resolveWrappedMarket(chainId, id.collateral, id.name);
|
|
24504
|
+
const rewardEntries = buildRewardEntries(
|
|
24505
|
+
p,
|
|
24506
|
+
raw.rsup,
|
|
24507
|
+
chainId,
|
|
24508
|
+
prices,
|
|
24509
|
+
tokens,
|
|
24510
|
+
totalColl * collPrice,
|
|
24511
|
+
totalDebt * debtPrice
|
|
24512
|
+
);
|
|
24294
24513
|
const maxLtv = p.maxLTV !== null ? Number(p.maxLTV) / LTV_PRECISION : 0;
|
|
24295
24514
|
const liquidationPenalty = p.liquidationFee !== null ? Number(p.liquidationFee) / LTV_PRECISION : 0;
|
|
24296
24515
|
const mintFeePct = p.mintFee !== null ? Number(p.mintFee) / LTV_PRECISION * 100 : 0;
|
|
@@ -24321,7 +24540,9 @@ function convertResupplyMarketsToResponse(raw, chainId, prices = {}, _additional
|
|
|
24321
24540
|
// The wrapped Curve Lend / Fraxlend supply rate — this collateral is a
|
|
24322
24541
|
// lending position and earns while it sits here.
|
|
24323
24542
|
intrinsicYield: collateralIntrinsicApr,
|
|
24324
|
-
|
|
24543
|
+
// CRV/CVX from the Convex position the pair stakes the collateral into.
|
|
24544
|
+
// Empty (not undefined) when the pair does not stake — `convexPid == 0`.
|
|
24545
|
+
rewards: rewardEntries.collateral,
|
|
24325
24546
|
decimals: collDecimals,
|
|
24326
24547
|
config: {
|
|
24327
24548
|
0: {
|
|
@@ -24378,7 +24599,9 @@ function convertResupplyMarketsToResponse(raw, chainId, prices = {}, _additional
|
|
|
24378
24599
|
// but it is governance-settable.
|
|
24379
24600
|
originationFee: mintFeePct,
|
|
24380
24601
|
intrinsicYield: 0,
|
|
24381
|
-
|
|
24602
|
+
// RSUP emissions to BORROWERS — a rebate on `variableBorrowRate`, not a
|
|
24603
|
+
// deposit yield. Empty when the emission stream has expired.
|
|
24604
|
+
rewards: rewardEntries.borrow,
|
|
24382
24605
|
decimals: debtDecimals,
|
|
24383
24606
|
config: {
|
|
24384
24607
|
0: {
|
|
@@ -31134,7 +31357,7 @@ var getLiquityUserDataConverter = (lender, chainId, account, meta) => {
|
|
|
31134
31357
|
troveInfo
|
|
31135
31358
|
}
|
|
31136
31359
|
};
|
|
31137
|
-
modes[t.troveId] =
|
|
31360
|
+
modes[t.troveId] = Number(troveInfo.annualInterestRate) / 1e18;
|
|
31138
31361
|
hist[t.troveId] = {
|
|
31139
31362
|
totalDeposits24h: collNum * collHist,
|
|
31140
31363
|
totalDebt24h: debtNum * loanHist
|
|
@@ -31594,7 +31817,7 @@ var getLlamaLendUserDataConverter = (lender, chainId, account, meta) => {
|
|
|
31594
31817
|
}
|
|
31595
31818
|
}
|
|
31596
31819
|
};
|
|
31597
|
-
const modes = { "0":
|
|
31820
|
+
const modes = { "0": bandCount };
|
|
31598
31821
|
const hist = {
|
|
31599
31822
|
"0": {
|
|
31600
31823
|
totalDeposits24h: collNum * collHist + bandBorrowedNum * loanHist,
|
|
@@ -45942,6 +46165,12 @@ function emptyReward() {
|
|
|
45942
46165
|
additionalBorrowData: []
|
|
45943
46166
|
};
|
|
45944
46167
|
}
|
|
46168
|
+
function ensureReserveTokenReward(result, chainId, reserveToken) {
|
|
46169
|
+
if (!result[chainId]) result[chainId] = {};
|
|
46170
|
+
const key2 = reserveToken.toLowerCase();
|
|
46171
|
+
if (!result[chainId][key2]) result[chainId][key2] = emptyReward();
|
|
46172
|
+
return result[chainId][key2];
|
|
46173
|
+
}
|
|
45945
46174
|
function ensureReward(result, chainId, lender, asset) {
|
|
45946
46175
|
if (!result[chainId]) result[chainId] = {};
|
|
45947
46176
|
if (!result[chainId][lender]) result[chainId][lender] = {};
|
|
@@ -45989,6 +46218,7 @@ function createMerklRewardFetcher(config) {
|
|
|
45989
46218
|
label: `MERKL_${config.merklProtocolId.toUpperCase()}`,
|
|
45990
46219
|
fetch: async () => {
|
|
45991
46220
|
const result = {};
|
|
46221
|
+
const byReserveToken = {};
|
|
45992
46222
|
const chainResults = config.chainIds ? await Promise.all(
|
|
45993
46223
|
config.chainIds.map(async (chainId) => {
|
|
45994
46224
|
try {
|
|
@@ -46012,8 +46242,17 @@ function createMerklRewardFetcher(config) {
|
|
|
46012
46242
|
for (const resolved2 of resolutions) {
|
|
46013
46243
|
const bucket = resolved2.bucket ?? (opp.action === "LEND" ? "deposit" : opp.action === "BORROW" ? "borrow" : void 0);
|
|
46014
46244
|
if (!bucket) continue;
|
|
46015
|
-
const
|
|
46016
|
-
const reward =
|
|
46245
|
+
const keyBy = resolved2.keyBy ?? (config.keyByReserveToken ? "reserveToken" : "lender");
|
|
46246
|
+
const reward = keyBy === "reserveToken" ? ensureReserveTokenReward(
|
|
46247
|
+
byReserveToken,
|
|
46248
|
+
chainId,
|
|
46249
|
+
resolved2.asset
|
|
46250
|
+
) : ensureReward(
|
|
46251
|
+
result,
|
|
46252
|
+
chainId,
|
|
46253
|
+
resolved2.lender ?? config.lenderKey,
|
|
46254
|
+
resolved2.asset
|
|
46255
|
+
);
|
|
46017
46256
|
const breakdowns = extractRewardBreakdowns(opp);
|
|
46018
46257
|
reward.link = getMerkleUrl(opp);
|
|
46019
46258
|
if (bucket === "deposit") {
|
|
@@ -46026,13 +46265,41 @@ function createMerklRewardFetcher(config) {
|
|
|
46026
46265
|
}
|
|
46027
46266
|
}
|
|
46028
46267
|
}
|
|
46029
|
-
return result;
|
|
46268
|
+
return { byLender: result, byReserveToken };
|
|
46030
46269
|
}
|
|
46031
46270
|
};
|
|
46032
46271
|
}
|
|
46033
46272
|
var aaveMerklRewardFetcher = createMerklRewardFetcher({
|
|
46034
46273
|
merklProtocolId: "aave",
|
|
46035
|
-
lenderKey: "AAVE_V3"
|
|
46274
|
+
lenderKey: "AAVE_V3",
|
|
46275
|
+
// Aave campaigns are keyed by the reserve token they pay on, NOT by lender:
|
|
46276
|
+
// Merkl's `aave` protocol id covers V3 mainnet, Horizon, Prime, Ether.fi and
|
|
46277
|
+
// every V4 hub/spoke, and nothing in the payload identifies the deployment
|
|
46278
|
+
// reliably — "Borrow USDC on Aave" advertises `marketName=proto_mainnet` in
|
|
46279
|
+
// its depositUrl while paying on `variableDebtHorRwaUSDC`, i.e. Horizon.
|
|
46280
|
+
// Filing them all under AAVE_V3 summed unrelated deployments into one number
|
|
46281
|
+
// (V3 USDC borrow read 5.75% = 1.75 + 2 + 2; V3 USDG read 12% = two V4 hubs)
|
|
46282
|
+
// and left the deployments actually running the campaigns at 0%.
|
|
46283
|
+
keyByReserveToken: true,
|
|
46284
|
+
resolveAsset: (opp) => {
|
|
46285
|
+
const explorer = opp.explorerAddress?.toLowerCase();
|
|
46286
|
+
if (opp.type?.startsWith("AAVE_V4")) {
|
|
46287
|
+
const underlying2 = resolveUnderlyingFromOpportunity(opp);
|
|
46288
|
+
if (!explorer || !underlying2) return void 0;
|
|
46289
|
+
return {
|
|
46290
|
+
asset: underlying2,
|
|
46291
|
+
lender: `AAVE_V4_${explorer.slice(2).toUpperCase()}`,
|
|
46292
|
+
keyBy: "lender"
|
|
46293
|
+
};
|
|
46294
|
+
}
|
|
46295
|
+
if (explorer) return { asset: explorer };
|
|
46296
|
+
const underlying = resolveUnderlyingFromOpportunity(opp);
|
|
46297
|
+
const candidates = (opp.tokens ?? []).filter(
|
|
46298
|
+
(t) => t.address.toLowerCase() !== underlying
|
|
46299
|
+
);
|
|
46300
|
+
if (candidates.length !== 1) return void 0;
|
|
46301
|
+
return { asset: candidates[0].address.toLowerCase() };
|
|
46302
|
+
}
|
|
46036
46303
|
});
|
|
46037
46304
|
function parseEulerVaultFromUrl(depositUrl) {
|
|
46038
46305
|
if (!depositUrl) return void 0;
|
|
@@ -46349,6 +46616,8 @@ async function fetchForPool(chainId, lender, pool, nowSeconds) {
|
|
|
46349
46616
|
}
|
|
46350
46617
|
var dtrinityRebateRewardFetcher = {
|
|
46351
46618
|
label: "DTRINITY_REBATE",
|
|
46619
|
+
// On-chain source: the pool (and therefore the lender key) is known up front,
|
|
46620
|
+
// so this stays lender-keyed — no deployment ambiguity to resolve.
|
|
46352
46621
|
fetch: async () => {
|
|
46353
46622
|
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
46354
46623
|
const results = await Promise.all(
|
|
@@ -46371,7 +46640,7 @@ var dtrinityRebateRewardFetcher = {
|
|
|
46371
46640
|
}
|
|
46372
46641
|
}
|
|
46373
46642
|
}
|
|
46374
|
-
return merged;
|
|
46643
|
+
return { byLender: merged };
|
|
46375
46644
|
}
|
|
46376
46645
|
};
|
|
46377
46646
|
|
|
@@ -46391,30 +46660,51 @@ var rewardFetchers = [
|
|
|
46391
46660
|
morphoCollateralMerklRewardFetcher,
|
|
46392
46661
|
dtrinityRebateRewardFetcher
|
|
46393
46662
|
];
|
|
46663
|
+
function mergeReserveTokenRewards(target, source) {
|
|
46664
|
+
for (const [chainId, byToken] of Object.entries(source)) {
|
|
46665
|
+
if (!target[chainId]) target[chainId] = {};
|
|
46666
|
+
Object.assign(target[chainId], byToken);
|
|
46667
|
+
}
|
|
46668
|
+
}
|
|
46394
46669
|
async function fetchLenderRewards() {
|
|
46395
46670
|
const results = await Promise.all(
|
|
46396
46671
|
rewardFetchers.map((f) => safeFetch(f.label, f.fetch))
|
|
46397
46672
|
);
|
|
46398
|
-
const
|
|
46673
|
+
const byLender = {};
|
|
46674
|
+
const byReserveToken = {};
|
|
46399
46675
|
for (const rr of results) {
|
|
46400
|
-
if (rr)
|
|
46676
|
+
if (!rr) continue;
|
|
46677
|
+
if (rr.byLender) mergeRewardResults(byLender, rr.byLender);
|
|
46678
|
+
if (rr.byReserveToken)
|
|
46679
|
+
mergeReserveTokenRewards(byReserveToken, rr.byReserveToken);
|
|
46401
46680
|
}
|
|
46402
|
-
return
|
|
46681
|
+
return { byLender, byReserveToken };
|
|
46403
46682
|
}
|
|
46404
46683
|
|
|
46405
46684
|
// src/yields/fetchGeneralYields.ts
|
|
46406
46685
|
var fetchGeneralYields = async () => {
|
|
46407
|
-
const [intrinsicYields,
|
|
46686
|
+
const [intrinsicYields, rewards] = await Promise.all([
|
|
46408
46687
|
fetchIntrinsicYields(),
|
|
46409
46688
|
fetchLenderRewards()
|
|
46410
46689
|
]);
|
|
46411
|
-
return {
|
|
46690
|
+
return {
|
|
46691
|
+
intrinsicYields,
|
|
46692
|
+
lenderRewards: rewards.byLender,
|
|
46693
|
+
rewardsByReserveToken: rewards.byReserveToken
|
|
46694
|
+
};
|
|
46412
46695
|
};
|
|
46413
46696
|
var fetchGeneralYieldsByMarketUid = async () => {
|
|
46414
|
-
const { intrinsicYields, lenderRewards } = await fetchGeneralYields();
|
|
46697
|
+
const { intrinsicYields, lenderRewards, rewardsByReserveToken } = await fetchGeneralYields();
|
|
46415
46698
|
return {
|
|
46416
46699
|
intrinsicYields,
|
|
46417
|
-
lenderRewards: flattenLenderRewards(lenderRewards)
|
|
46700
|
+
lenderRewards: flattenLenderRewards(lenderRewards),
|
|
46701
|
+
// Same rewards, un-flattened: the flatten above assumes a uid ends in the
|
|
46702
|
+
// underlying address, which Aave V4 (reserve-id uids) breaks. Consumers
|
|
46703
|
+
// with market nodes join (lender, underlying) off this instead.
|
|
46704
|
+
rewardsByLenderAsset: lenderRewards,
|
|
46705
|
+
// Left un-flattened for the same reason: turning a reserve token into a
|
|
46706
|
+
// marketUid needs the market's own metadata, which only the consumer has.
|
|
46707
|
+
rewardsByReserveToken
|
|
46418
46708
|
};
|
|
46419
46709
|
};
|
|
46420
46710
|
var flattenLenderRewards = (input) => {
|