@1delta/margin-fetcher 5.0.7 → 5.0.9
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 +63 -2
- package/dist/index.js +116 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -2299,11 +2299,45 @@ interface LenderRewards {
|
|
|
2299
2299
|
};
|
|
2300
2300
|
};
|
|
2301
2301
|
}
|
|
2302
|
+
/**
|
|
2303
|
+
* Rewards keyed by the RESERVE TOKEN the campaign pays on — the aToken for a
|
|
2304
|
+
* supply campaign, the variable debt token for a borrow one.
|
|
2305
|
+
*
|
|
2306
|
+
* Why this exists: a protocol's Merkl campaigns cover every deployment under
|
|
2307
|
+
* one `mainProtocolId` ("aave" spans V3 mainnet, Horizon, Prime, Ether.fi and
|
|
2308
|
+
* every V4 hub/spoke), and nothing in the opportunity payload names the
|
|
2309
|
+
* deployment reliably — `depositUrl`'s `marketName` is wrong on at least one
|
|
2310
|
+
* live campaign ("Borrow USDC on Aave" claims `proto_mainnet` while paying on
|
|
2311
|
+
* `variableDebtHorRwaUSDC`, i.e. Horizon). Attributing by (lender, underlying)
|
|
2312
|
+
* therefore collapsed every deployment's campaign onto the base lender key and
|
|
2313
|
+
* SUMMED them: Aave V3 USDC read 5.75% borrow (1.75 + 2 + 2 across three
|
|
2314
|
+
* deployments) and V3 USDG read 12% (two V4 hubs), while Horizon/Prime/V4
|
|
2315
|
+
* markets read 0%.
|
|
2316
|
+
*
|
|
2317
|
+
* The reserve token is unambiguous — it exists in exactly one market of exactly
|
|
2318
|
+
* one deployment. Consumers hold the other half of the join already: Aave-type
|
|
2319
|
+
* market nodes carry `params.metadata.{aToken, vToken}`, so matching on those
|
|
2320
|
+
* lands each campaign on exactly one marketUid, and cross-deployment summing
|
|
2321
|
+
* becomes impossible by construction.
|
|
2322
|
+
*/
|
|
2323
|
+
interface RewardsByReserveToken {
|
|
2324
|
+
/** chainId → lowercased reserve-token address → reward */
|
|
2325
|
+
[chainId: string]: {
|
|
2326
|
+
[reserveToken: string]: LenderAssetReward;
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2302
2329
|
interface YieldDataWithTimestamp {
|
|
2303
2330
|
intrinsicYields: {
|
|
2304
2331
|
[asset: string]: number | undefined;
|
|
2305
2332
|
};
|
|
2306
2333
|
lenderRewards: LenderRewards;
|
|
2334
|
+
/**
|
|
2335
|
+
* Reserve-token-keyed rewards for protocols whose campaigns cannot be
|
|
2336
|
+
* attributed to a deployment from the campaign payload alone (Aave).
|
|
2337
|
+
* Those protocols are absent from `lenderRewards` — better no reward than
|
|
2338
|
+
* one filed against the wrong market.
|
|
2339
|
+
*/
|
|
2340
|
+
rewardsByReserveToken: RewardsByReserveToken;
|
|
2307
2341
|
}
|
|
2308
2342
|
interface LenderRewardsByMarketUid {
|
|
2309
2343
|
[marketUid: string]: LenderAssetReward;
|
|
@@ -2312,7 +2346,25 @@ interface YieldDataByMarketUid {
|
|
|
2312
2346
|
intrinsicYields: {
|
|
2313
2347
|
[asset: string]: number | undefined;
|
|
2314
2348
|
};
|
|
2349
|
+
/**
|
|
2350
|
+
* Flattened to marketUid via `createMarketUid(chain, lender, asset)`.
|
|
2351
|
+
*
|
|
2352
|
+
* CAVEAT: that construction assumes a market's uid ends in its underlying
|
|
2353
|
+
* address, which is not universal — Aave V4 uids end in a numeric RESERVE ID
|
|
2354
|
+
* (`AAVE_V4_94E7A5DC…:1:3` is WBTC), so a reward flattened this way can never
|
|
2355
|
+
* match one. Prefer `rewardsByLenderAsset` when the consumer has the market
|
|
2356
|
+
* nodes to hand; this stays for callers that only have uids.
|
|
2357
|
+
*/
|
|
2315
2358
|
lenderRewards: LenderRewardsByMarketUid;
|
|
2359
|
+
/**
|
|
2360
|
+
* The same lender-keyed rewards UNflattened — (chain → lender → underlying).
|
|
2361
|
+
* Consumers holding market nodes should join on this: it needs no assumption
|
|
2362
|
+
* about how a lender composes its marketUid.
|
|
2363
|
+
*/
|
|
2364
|
+
rewardsByLenderAsset: LenderRewards;
|
|
2365
|
+
/** See {@link RewardsByReserveToken} — joined by the consumer against each
|
|
2366
|
+
* market node's `params.metadata.{aToken, vToken}`. */
|
|
2367
|
+
rewardsByReserveToken: RewardsByReserveToken;
|
|
2316
2368
|
}
|
|
2317
2369
|
|
|
2318
2370
|
declare const fetchGeneralYields: () => Promise<YieldDataWithTimestamp>;
|
|
@@ -8808,8 +8860,17 @@ interface LiquidationTerms {
|
|
|
8808
8860
|
ltv?: number;
|
|
8809
8861
|
/** Threshold at which liquidation becomes possible. */
|
|
8810
8862
|
liquidationLtv?: number;
|
|
8811
|
-
/**
|
|
8812
|
-
|
|
8863
|
+
/**
|
|
8864
|
+
* Fraction of repaid debt paid to the liquidator on top of par.
|
|
8865
|
+
*
|
|
8866
|
+
* OPTIONAL, and the distinction is load-bearing: `0` means "the liquidator
|
|
8867
|
+
* gets no bonus", `undefined` means "we do not know it". The first cut
|
|
8868
|
+
* defaulted the unknown case to `0` and every market on `/lending/latest`
|
|
8869
|
+
* rendered a confident "Liquidator takes debt repaid + 0%" — the origin's
|
|
8870
|
+
* `market_config` had no penalty column at all, so NOTHING was known. A row
|
|
8871
|
+
* we cannot fill must be absent, not zero.
|
|
8872
|
+
*/
|
|
8873
|
+
penalty?: number;
|
|
8813
8874
|
closeFactor: number;
|
|
8814
8875
|
targetHealthFactor?: number;
|
|
8815
8876
|
/**
|
package/dist/index.js
CHANGED
|
@@ -23028,7 +23028,11 @@ async function fetchChainExtras(chainId, markets) {
|
|
|
23028
23028
|
abi: LLAMALEND_READ_ABI,
|
|
23029
23029
|
allowFailure: true
|
|
23030
23030
|
});
|
|
23031
|
-
} catch {
|
|
23031
|
+
} catch (e) {
|
|
23032
|
+
console.error(
|
|
23033
|
+
`[llamalend] chain-extras multicall failed on chain ${chainId} for ${markets.length} market(s) \u2014 LTV/price/band state unavailable this cycle:`,
|
|
23034
|
+
e?.message ?? e
|
|
23035
|
+
);
|
|
23032
23036
|
return {};
|
|
23033
23037
|
}
|
|
23034
23038
|
const out = {};
|
|
@@ -24019,7 +24023,7 @@ function convertLlamaLendMarketsToResponse(raw, chainId, prices = {}, additional
|
|
|
24019
24023
|
const utilization = reserves > 0 ? totalDebt / reserves : 0;
|
|
24020
24024
|
const borrowRatePct = (m.borrowApr ?? 0) * 100;
|
|
24021
24025
|
const depositRatePct = (m.lendApr ?? 0) * 100;
|
|
24022
|
-
const ltv = m.collateralFactor ??
|
|
24026
|
+
const ltv = m.collateralFactor ?? null;
|
|
24023
24027
|
const liqPenalty = Number(market.liquidationDiscount) / 1e18 || 0;
|
|
24024
24028
|
const closeFactor = 1;
|
|
24025
24029
|
const depositsEnabled = (m.maxDeposit ?? 0) > 0;
|
|
@@ -24128,7 +24132,10 @@ function convertLlamaLendMarketsToResponse(raw, chainId, prices = {}, additional
|
|
|
24128
24132
|
collateralDecimals: collDecimals,
|
|
24129
24133
|
// The Controller doubles as the market id.
|
|
24130
24134
|
id: market.controller.toLowerCase(),
|
|
24131
|
-
|
|
24135
|
+
// Mirrors the Morpho shape. Empty string, not "0": a consumer parsing
|
|
24136
|
+
// this as a number gets NaN and can branch, where "0" would silently
|
|
24137
|
+
// become a zero LTV. The real curve is in `llamalend.bandLtv`.
|
|
24138
|
+
lltv: ltv !== null ? String(ltv) : "",
|
|
24132
24139
|
oracle: market.priceOracle ?? market.amm,
|
|
24133
24140
|
irm: market.monetaryPolicy ?? zeroAddress,
|
|
24134
24141
|
collateralAddress: collAddr,
|
|
@@ -45942,6 +45949,12 @@ function emptyReward() {
|
|
|
45942
45949
|
additionalBorrowData: []
|
|
45943
45950
|
};
|
|
45944
45951
|
}
|
|
45952
|
+
function ensureReserveTokenReward(result, chainId, reserveToken) {
|
|
45953
|
+
if (!result[chainId]) result[chainId] = {};
|
|
45954
|
+
const key2 = reserveToken.toLowerCase();
|
|
45955
|
+
if (!result[chainId][key2]) result[chainId][key2] = emptyReward();
|
|
45956
|
+
return result[chainId][key2];
|
|
45957
|
+
}
|
|
45945
45958
|
function ensureReward(result, chainId, lender, asset) {
|
|
45946
45959
|
if (!result[chainId]) result[chainId] = {};
|
|
45947
45960
|
if (!result[chainId][lender]) result[chainId][lender] = {};
|
|
@@ -45989,6 +46002,7 @@ function createMerklRewardFetcher(config) {
|
|
|
45989
46002
|
label: `MERKL_${config.merklProtocolId.toUpperCase()}`,
|
|
45990
46003
|
fetch: async () => {
|
|
45991
46004
|
const result = {};
|
|
46005
|
+
const byReserveToken = {};
|
|
45992
46006
|
const chainResults = config.chainIds ? await Promise.all(
|
|
45993
46007
|
config.chainIds.map(async (chainId) => {
|
|
45994
46008
|
try {
|
|
@@ -46012,8 +46026,17 @@ function createMerklRewardFetcher(config) {
|
|
|
46012
46026
|
for (const resolved2 of resolutions) {
|
|
46013
46027
|
const bucket = resolved2.bucket ?? (opp.action === "LEND" ? "deposit" : opp.action === "BORROW" ? "borrow" : void 0);
|
|
46014
46028
|
if (!bucket) continue;
|
|
46015
|
-
const
|
|
46016
|
-
const reward =
|
|
46029
|
+
const keyBy = resolved2.keyBy ?? (config.keyByReserveToken ? "reserveToken" : "lender");
|
|
46030
|
+
const reward = keyBy === "reserveToken" ? ensureReserveTokenReward(
|
|
46031
|
+
byReserveToken,
|
|
46032
|
+
chainId,
|
|
46033
|
+
resolved2.asset
|
|
46034
|
+
) : ensureReward(
|
|
46035
|
+
result,
|
|
46036
|
+
chainId,
|
|
46037
|
+
resolved2.lender ?? config.lenderKey,
|
|
46038
|
+
resolved2.asset
|
|
46039
|
+
);
|
|
46017
46040
|
const breakdowns = extractRewardBreakdowns(opp);
|
|
46018
46041
|
reward.link = getMerkleUrl(opp);
|
|
46019
46042
|
if (bucket === "deposit") {
|
|
@@ -46026,13 +46049,41 @@ function createMerklRewardFetcher(config) {
|
|
|
46026
46049
|
}
|
|
46027
46050
|
}
|
|
46028
46051
|
}
|
|
46029
|
-
return result;
|
|
46052
|
+
return { byLender: result, byReserveToken };
|
|
46030
46053
|
}
|
|
46031
46054
|
};
|
|
46032
46055
|
}
|
|
46033
46056
|
var aaveMerklRewardFetcher = createMerklRewardFetcher({
|
|
46034
46057
|
merklProtocolId: "aave",
|
|
46035
|
-
lenderKey: "AAVE_V3"
|
|
46058
|
+
lenderKey: "AAVE_V3",
|
|
46059
|
+
// Aave campaigns are keyed by the reserve token they pay on, NOT by lender:
|
|
46060
|
+
// Merkl's `aave` protocol id covers V3 mainnet, Horizon, Prime, Ether.fi and
|
|
46061
|
+
// every V4 hub/spoke, and nothing in the payload identifies the deployment
|
|
46062
|
+
// reliably — "Borrow USDC on Aave" advertises `marketName=proto_mainnet` in
|
|
46063
|
+
// its depositUrl while paying on `variableDebtHorRwaUSDC`, i.e. Horizon.
|
|
46064
|
+
// Filing them all under AAVE_V3 summed unrelated deployments into one number
|
|
46065
|
+
// (V3 USDC borrow read 5.75% = 1.75 + 2 + 2; V3 USDG read 12% = two V4 hubs)
|
|
46066
|
+
// and left the deployments actually running the campaigns at 0%.
|
|
46067
|
+
keyByReserveToken: true,
|
|
46068
|
+
resolveAsset: (opp) => {
|
|
46069
|
+
const explorer = opp.explorerAddress?.toLowerCase();
|
|
46070
|
+
if (opp.type?.startsWith("AAVE_V4")) {
|
|
46071
|
+
const underlying2 = resolveUnderlyingFromOpportunity(opp);
|
|
46072
|
+
if (!explorer || !underlying2) return void 0;
|
|
46073
|
+
return {
|
|
46074
|
+
asset: underlying2,
|
|
46075
|
+
lender: `AAVE_V4_${explorer.slice(2).toUpperCase()}`,
|
|
46076
|
+
keyBy: "lender"
|
|
46077
|
+
};
|
|
46078
|
+
}
|
|
46079
|
+
if (explorer) return { asset: explorer };
|
|
46080
|
+
const underlying = resolveUnderlyingFromOpportunity(opp);
|
|
46081
|
+
const candidates = (opp.tokens ?? []).filter(
|
|
46082
|
+
(t) => t.address.toLowerCase() !== underlying
|
|
46083
|
+
);
|
|
46084
|
+
if (candidates.length !== 1) return void 0;
|
|
46085
|
+
return { asset: candidates[0].address.toLowerCase() };
|
|
46086
|
+
}
|
|
46036
46087
|
});
|
|
46037
46088
|
function parseEulerVaultFromUrl(depositUrl) {
|
|
46038
46089
|
if (!depositUrl) return void 0;
|
|
@@ -46349,6 +46400,8 @@ async function fetchForPool(chainId, lender, pool, nowSeconds) {
|
|
|
46349
46400
|
}
|
|
46350
46401
|
var dtrinityRebateRewardFetcher = {
|
|
46351
46402
|
label: "DTRINITY_REBATE",
|
|
46403
|
+
// On-chain source: the pool (and therefore the lender key) is known up front,
|
|
46404
|
+
// so this stays lender-keyed — no deployment ambiguity to resolve.
|
|
46352
46405
|
fetch: async () => {
|
|
46353
46406
|
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
46354
46407
|
const results = await Promise.all(
|
|
@@ -46371,7 +46424,7 @@ var dtrinityRebateRewardFetcher = {
|
|
|
46371
46424
|
}
|
|
46372
46425
|
}
|
|
46373
46426
|
}
|
|
46374
|
-
return merged;
|
|
46427
|
+
return { byLender: merged };
|
|
46375
46428
|
}
|
|
46376
46429
|
};
|
|
46377
46430
|
|
|
@@ -46391,30 +46444,51 @@ var rewardFetchers = [
|
|
|
46391
46444
|
morphoCollateralMerklRewardFetcher,
|
|
46392
46445
|
dtrinityRebateRewardFetcher
|
|
46393
46446
|
];
|
|
46447
|
+
function mergeReserveTokenRewards(target, source) {
|
|
46448
|
+
for (const [chainId, byToken] of Object.entries(source)) {
|
|
46449
|
+
if (!target[chainId]) target[chainId] = {};
|
|
46450
|
+
Object.assign(target[chainId], byToken);
|
|
46451
|
+
}
|
|
46452
|
+
}
|
|
46394
46453
|
async function fetchLenderRewards() {
|
|
46395
46454
|
const results = await Promise.all(
|
|
46396
46455
|
rewardFetchers.map((f) => safeFetch(f.label, f.fetch))
|
|
46397
46456
|
);
|
|
46398
|
-
const
|
|
46457
|
+
const byLender = {};
|
|
46458
|
+
const byReserveToken = {};
|
|
46399
46459
|
for (const rr of results) {
|
|
46400
|
-
if (rr)
|
|
46460
|
+
if (!rr) continue;
|
|
46461
|
+
if (rr.byLender) mergeRewardResults(byLender, rr.byLender);
|
|
46462
|
+
if (rr.byReserveToken)
|
|
46463
|
+
mergeReserveTokenRewards(byReserveToken, rr.byReserveToken);
|
|
46401
46464
|
}
|
|
46402
|
-
return
|
|
46465
|
+
return { byLender, byReserveToken };
|
|
46403
46466
|
}
|
|
46404
46467
|
|
|
46405
46468
|
// src/yields/fetchGeneralYields.ts
|
|
46406
46469
|
var fetchGeneralYields = async () => {
|
|
46407
|
-
const [intrinsicYields,
|
|
46470
|
+
const [intrinsicYields, rewards] = await Promise.all([
|
|
46408
46471
|
fetchIntrinsicYields(),
|
|
46409
46472
|
fetchLenderRewards()
|
|
46410
46473
|
]);
|
|
46411
|
-
return {
|
|
46474
|
+
return {
|
|
46475
|
+
intrinsicYields,
|
|
46476
|
+
lenderRewards: rewards.byLender,
|
|
46477
|
+
rewardsByReserveToken: rewards.byReserveToken
|
|
46478
|
+
};
|
|
46412
46479
|
};
|
|
46413
46480
|
var fetchGeneralYieldsByMarketUid = async () => {
|
|
46414
|
-
const { intrinsicYields, lenderRewards } = await fetchGeneralYields();
|
|
46481
|
+
const { intrinsicYields, lenderRewards, rewardsByReserveToken } = await fetchGeneralYields();
|
|
46415
46482
|
return {
|
|
46416
46483
|
intrinsicYields,
|
|
46417
|
-
lenderRewards: flattenLenderRewards(lenderRewards)
|
|
46484
|
+
lenderRewards: flattenLenderRewards(lenderRewards),
|
|
46485
|
+
// Same rewards, un-flattened: the flatten above assumes a uid ends in the
|
|
46486
|
+
// underlying address, which Aave V4 (reserve-id uids) breaks. Consumers
|
|
46487
|
+
// with market nodes join (lender, underlying) off this instead.
|
|
46488
|
+
rewardsByLenderAsset: lenderRewards,
|
|
46489
|
+
// Left un-flattened for the same reason: turning a reserve token into a
|
|
46490
|
+
// marketUid needs the market's own metadata, which only the consumer has.
|
|
46491
|
+
rewardsByReserveToken
|
|
46418
46492
|
};
|
|
46419
46493
|
};
|
|
46420
46494
|
var flattenLenderRewards = (input) => {
|
|
@@ -62540,8 +62614,9 @@ function borrowDescription(b) {
|
|
|
62540
62614
|
"Liquidation here is triggered by TIME, not price \u2014 being late is the trigger, and being over-collateralised does not protect you."
|
|
62541
62615
|
);
|
|
62542
62616
|
} else if (b.liquidation.liquidationLtv != null) {
|
|
62617
|
+
const pen = b.liquidation.penalty;
|
|
62543
62618
|
parts.push(
|
|
62544
|
-
`Liquidation starts at ${pct(b.liquidation.liquidationLtv * 100)} LTV
|
|
62619
|
+
`Liquidation starts at ${pct(b.liquidation.liquidationLtv * 100)} LTV` + (pen != null ? `, with a ${pct(pen * 100)} penalty` : "") + "."
|
|
62545
62620
|
);
|
|
62546
62621
|
}
|
|
62547
62622
|
return parts.join(" ");
|
|
@@ -63167,7 +63242,20 @@ function buildConstraints(input, siblings, acceptedCollateral) {
|
|
|
63167
63242
|
}
|
|
63168
63243
|
};
|
|
63169
63244
|
}
|
|
63170
|
-
function
|
|
63245
|
+
function marketLltv(input) {
|
|
63246
|
+
const raw = input.market?.lltv;
|
|
63247
|
+
if (raw == null) return void 0;
|
|
63248
|
+
const n = Number(raw);
|
|
63249
|
+
if (!Number.isFinite(n) || n <= 0) return void 0;
|
|
63250
|
+
return n / 1e18;
|
|
63251
|
+
}
|
|
63252
|
+
function liquidationFrom(cfg, input, acceptedCollateral) {
|
|
63253
|
+
const lltv = marketLltv(input);
|
|
63254
|
+
const soleCollateral = acceptedCollateral?.count === 1 ? acceptedCollateral.items[0] : void 0;
|
|
63255
|
+
const isolated = lltv != null || !hasCrossMarginRisk(input.lender) && soleCollateral != null;
|
|
63256
|
+
const ownLtv = cfg?.borrowCollateralFactor;
|
|
63257
|
+
const ownLiqLtv = cfg?.collateralFactor;
|
|
63258
|
+
const ownPenalty = cfg?.liquidationPenalty;
|
|
63171
63259
|
return {
|
|
63172
63260
|
// The ordinary model: a liquidator repays part of the debt and takes
|
|
63173
63261
|
// collateral plus a bonus. Adapters override for the four lenders where
|
|
@@ -63175,9 +63263,11 @@ function liquidationFrom(cfg, input) {
|
|
|
63175
63263
|
model: "repay-seize",
|
|
63176
63264
|
absorber: "liquidator",
|
|
63177
63265
|
trigger: "price",
|
|
63178
|
-
ltv:
|
|
63179
|
-
liquidationLtv:
|
|
63180
|
-
penalty
|
|
63266
|
+
ltv: ownLtv || (isolated ? soleCollateral?.ltv ?? lltv : void 0) || ownLtv,
|
|
63267
|
+
liquidationLtv: ownLiqLtv || (isolated ? soleCollateral?.liquidationLtv ?? lltv : void 0) || ownLiqLtv,
|
|
63268
|
+
// No `?? 0` — see `LiquidationTerms.penalty`. An unknown penalty is
|
|
63269
|
+
// undefined so the row is omitted rather than rendered as "+0%".
|
|
63270
|
+
penalty: ownPenalty || (isolated ? soleCollateral?.liquidationPenalty : void 0) || ownPenalty,
|
|
63181
63271
|
closeFactor: cfg?.closeFactor ?? input.closeFactor ?? 1,
|
|
63182
63272
|
targetHealthFactor: cfg?.targetHealthFactor ?? input.targetHealthFactor,
|
|
63183
63273
|
seizure: "proportional",
|
|
@@ -63204,7 +63294,8 @@ function buildModes(input) {
|
|
|
63204
63294
|
liquidation: {
|
|
63205
63295
|
ltv: c.borrowCollateralFactor,
|
|
63206
63296
|
liquidationLtv: c.collateralFactor,
|
|
63207
|
-
penalty:
|
|
63297
|
+
// Same rule as the top-level penalty: unknown stays unknown.
|
|
63298
|
+
penalty: c.liquidationPenalty,
|
|
63208
63299
|
closeFactor: c.closeFactor ?? input.closeFactor ?? 1,
|
|
63209
63300
|
targetHealthFactor: c.targetHealthFactor ?? input.targetHealthFactor
|
|
63210
63301
|
},
|
|
@@ -63299,6 +63390,7 @@ function buildBorrow(input, now, siblings) {
|
|
|
63299
63390
|
const rate = buildRate(input, "borrow");
|
|
63300
63391
|
const maturity = buildMaturity(input, now);
|
|
63301
63392
|
const fees = buildFees(input, "borrow");
|
|
63393
|
+
const acceptedCollateral = buildExposures2(input, siblings, "accepted");
|
|
63302
63394
|
const borrow = {
|
|
63303
63395
|
rate,
|
|
63304
63396
|
maturity,
|
|
@@ -63313,8 +63405,8 @@ function buildBorrow(input, now, siblings) {
|
|
|
63313
63405
|
minDebt: resolveMinDebt(input),
|
|
63314
63406
|
fees: fees.filter((f) => f.when === "exit" || f.when === "late")
|
|
63315
63407
|
},
|
|
63316
|
-
liquidation: liquidationFrom(cfg, input),
|
|
63317
|
-
acceptedCollateral
|
|
63408
|
+
liquidation: liquidationFrom(cfg, input, acceptedCollateral),
|
|
63409
|
+
acceptedCollateral,
|
|
63318
63410
|
modes: buildModes(input),
|
|
63319
63411
|
fees,
|
|
63320
63412
|
counterparty: { kind: "pool", solvency: "overcollateralized" },
|
|
@@ -64939,7 +65031,7 @@ function validateTermSheet(sheet) {
|
|
|
64939
65031
|
"full-collateral seizure must surface a tag or an implication"
|
|
64940
65032
|
);
|
|
64941
65033
|
}
|
|
64942
|
-
if (l.penalty < 0 || l.penalty > 1)
|
|
65034
|
+
if (l.penalty != null && (l.penalty < 0 || l.penalty > 1))
|
|
64943
65035
|
fail("penalty-range", `liquidation.penalty ${l.penalty} outside 0..1`);
|
|
64944
65036
|
if (l.closeFactor <= 0 || l.closeFactor > 1)
|
|
64945
65037
|
fail(
|