@1delta/margin-fetcher 5.0.4 → 5.0.6
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/README.md +1 -1
- package/dist/index.d.ts +41 -5
- package/dist/index.js +203 -4
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -331,4 +331,4 @@ User positions (`{credit, debt, collateral[128], collateralBitmap}`) are read vi
|
|
|
331
331
|
|
|
332
332
|
> **Scope:** the fetcher and direct spot actions (supply collateral / borrow-via-`take` / repay /
|
|
333
333
|
> withdraw) are wired. Composer-routed leverage and native ETH are deferred — see
|
|
334
|
-
> [`calldata-sdk/src/evm/generic/midnight/
|
|
334
|
+
> [`calldata-sdk/src/evm/generic/midnight/COMPOSER.md`](../calldata-sdk/src/evm/generic/midnight/COMPOSER.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -3332,7 +3332,7 @@ declare function resupplyKeyParts(key: string): {
|
|
|
3332
3332
|
* Map one Resupply deployment's on-chain batch into the shared
|
|
3333
3333
|
* `MorphoGeneralPublicResponse` shape, keyed `RESUPPLY_<chainId>_<PAIR>`.
|
|
3334
3334
|
*
|
|
3335
|
-
* Four modelling decisions worth knowing (all from
|
|
3335
|
+
* Four modelling decisions worth knowing (all from RESUPPLY.md):
|
|
3336
3336
|
*
|
|
3337
3337
|
* - **The collateral we publish is the UNDERLYING (crvUSD / frxUSD), not the
|
|
3338
3338
|
* ERC-4626 share.** The share is an internal accounting unit that no token
|
|
@@ -3620,7 +3620,7 @@ declare const usddIlkBytes32: (ilk: string) => `0x${string}`;
|
|
|
3620
3620
|
* ink total).
|
|
3621
3621
|
*
|
|
3622
3622
|
* The roster is EMPTY on both EVM chains today (`cdpi() = 0`, no ilk filed —
|
|
3623
|
-
* see
|
|
3623
|
+
* see USDD.md), so this returns zero markets without issuing a
|
|
3624
3624
|
* multicall. The code path stays live so the day metadata fills, data flows
|
|
3625
3625
|
* with no code change.
|
|
3626
3626
|
*/
|
|
@@ -3744,7 +3744,7 @@ declare function frankencoinKeyParts(key: string): {
|
|
|
3744
3744
|
* Map one Frankencoin deployment's on-chain batch into the shared
|
|
3745
3745
|
* `MorphoGeneralPublicResponse` shape, keyed `FRANKENCOIN_<chainId>_<ORIGINAL>`.
|
|
3746
3746
|
*
|
|
3747
|
-
* Three modelling decisions worth knowing (all from
|
|
3747
|
+
* Three modelling decisions worth knowing (all from FRANKENCOIN.md):
|
|
3748
3748
|
*
|
|
3749
3749
|
* - **The liquidation price is owner-DECLARED, not an oracle.** `price` is
|
|
3750
3750
|
* 36-dec scaled against the collateral's decimals and is policed by a
|
|
@@ -8416,7 +8416,28 @@ interface TermAssetRef {
|
|
|
8416
8416
|
* `tags.ts` — never hand-written per lender, so they cannot drift from the
|
|
8417
8417
|
* numbers they summarize.
|
|
8418
8418
|
*/
|
|
8419
|
-
type TermTag = Open<'fixed-rate' | 'variable-rate' | 'user-set-rate' | 'zero-interest' | 'prepaid-interest' | 'nav-accrual' | 'has-maturity' | 'perpetual' | 'rolling-duration' | 'static-debt' | 'accruing-debt' | 'time-liquidation' | 'price-liquidation' | 'redeemable' | 'no-liquidation' | 'full-collateral-seizure' | 'early-exit-free' | 'early-exit-penalty' | 'early-exit-discount' | 'exit-instant' | 'exit-capped' | 'exit-cooldown' | 'exit-queued' | 'exit-market-sale' | 'exit-may-be-impossible' | 'permissioned' | 'capped' | 'cap-full' | 'first-loss' | 'socialized-loss' | 'physical-delivery' | 'undercollateralized' | 'nav-attested' | 'immutable' | 'no-timelock' | 'eoa-controlled' | 'points-rewards'
|
|
8419
|
+
type TermTag = Open<'fixed-rate' | 'variable-rate' | 'user-set-rate' | 'zero-interest' | 'prepaid-interest' | 'nav-accrual' | 'has-maturity' | 'perpetual' | 'rolling-duration' | 'static-debt' | 'accruing-debt' | 'time-liquidation' | 'price-liquidation' | 'redeemable' | 'no-liquidation' | 'full-collateral-seizure' | 'early-exit-free' | 'early-exit-penalty' | 'early-exit-discount' | 'exit-instant' | 'exit-capped' | 'exit-cooldown' | 'exit-queued' | 'exit-market-sale' | 'exit-may-be-impossible' | 'permissioned' | 'capped' | 'cap-full' | 'first-loss' | 'socialized-loss' | 'physical-delivery' | 'undercollateralized' | 'nav-attested' | 'immutable' | 'no-timelock' | 'eoa-controlled' | 'points-rewards'
|
|
8420
|
+
/**
|
|
8421
|
+
* The headline yield is MOSTLY the asset's own (staking / RWA / savings)
|
|
8422
|
+
* yield, not interest this market pays. Set at >= 50 % of `aprTotal`.
|
|
8423
|
+
*
|
|
8424
|
+
* Worth a tag rather than only a detail row, because the two are not
|
|
8425
|
+
* interchangeable and the difference is invisible in a single percentage: a
|
|
8426
|
+
* market rate is paid by borrowers and moves with utilization, while
|
|
8427
|
+
* intrinsic yield you would earn holding the asset in your wallet — you are
|
|
8428
|
+
* taking this market's risk for the DIFFERENCE, not for the headline.
|
|
8429
|
+
* Decisive when looping: a loop only carries if the collateral out-earns the
|
|
8430
|
+
* debt, and intrinsic yield does not scale with leverage the way a lending
|
|
8431
|
+
* spread does.
|
|
8432
|
+
*/
|
|
8433
|
+
| 'intrinsic-yield'
|
|
8434
|
+
/**
|
|
8435
|
+
* This market pays NO interest of its own — the entire headline is intrinsic
|
|
8436
|
+
* yield and/or rewards. Curvance's collateral-only legs are the canonical
|
|
8437
|
+
* case (`debtCap == 0` ⇒ 0 % supply APR by construction), but any market
|
|
8438
|
+
* with no borrowers reads the same way.
|
|
8439
|
+
*/
|
|
8440
|
+
| 'no-market-interest' | 'oracle-flagged' | 'no-oracle'>;
|
|
8420
8441
|
/** Human-facing copy for one side of a term sheet. */
|
|
8421
8442
|
interface TermInfo {
|
|
8422
8443
|
/**
|
|
@@ -8646,6 +8667,22 @@ interface BorrowExitTerms {
|
|
|
8646
8667
|
minDebt?: string;
|
|
8647
8668
|
/** Over-repay REVERTS (Midnight `uint128` underflow) — a real footgun. */
|
|
8648
8669
|
overRepayReverts?: boolean;
|
|
8670
|
+
/**
|
|
8671
|
+
* Seconds after OPENING (or topping up) during which the debt cannot be
|
|
8672
|
+
* repaid at all.
|
|
8673
|
+
*
|
|
8674
|
+
* The mirror of `SupplyExitTerms.cooldownSecs`, and deliberately a separate
|
|
8675
|
+
* field because it is a categorically worse term: a supply cooldown delays
|
|
8676
|
+
* your money, a REPAY cooldown means a position cannot be closed,
|
|
8677
|
+
* deleveraged or migrated out — and, on the one lender that has it, means an
|
|
8678
|
+
* incoming liquidation cannot be averted by repaying either.
|
|
8679
|
+
*
|
|
8680
|
+
* Only Curvance (`MIN_HOLD_PERIOD`, 1200 s) has one today, which is exactly
|
|
8681
|
+
* why it needs to be stated rather than assumed away: every other lender we
|
|
8682
|
+
* carry lets you exit at any block, and the generic builder's
|
|
8683
|
+
* `availability.canClose: true` encodes that assumption.
|
|
8684
|
+
*/
|
|
8685
|
+
cooldownSecs?: number;
|
|
8649
8686
|
fees: FeeTerm[];
|
|
8650
8687
|
}
|
|
8651
8688
|
/** One named penalty, for models where a single number is not enough. */
|
|
@@ -9429,7 +9466,6 @@ declare function duration(secs: number | undefined): string;
|
|
|
9429
9466
|
declare function shortDate(unixSecs: number | undefined): string;
|
|
9430
9467
|
/** One fee → a self-contained phrase, correct even for an unrecognised `id`. */
|
|
9431
9468
|
declare function feePhrase(fee: FeeTerm): string;
|
|
9432
|
-
/** Supply-side headline: ≤ ~100 chars, always populated. */
|
|
9433
9469
|
declare function supplyHeadline(s: SupplyTermSheet): string;
|
|
9434
9470
|
/** Borrow-side headline. */
|
|
9435
9471
|
declare function borrowHeadline(b: BorrowTermSheet): string;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseAbi, keccak256, encodeAbiParameters, formatEther, BaseError, erc20Abi, stringToHex, pad, encodeFunctionData, formatUnits, isAddress, getAddress, decodeFunctionResult, decodeAbiParameters, concat, AbiEncodingLengthMismatchError, concatHex, toBytes, slice, hexToString, InvalidAddressError, boolToHex, integerRegex, numberToHex, bytesRegex, BytesSizeMismatchError, arrayRegex, UnsupportedPackedAbiType } from './chunk-YILYOOYB.js';
|
|
2
2
|
import './chunk-BYTNVMX7.js';
|
|
3
3
|
import './chunk-PR4QN5HX.js';
|
|
4
|
-
import { Lender, isExactly, isMidnight, isTerm, isTermMax, isTeller, isListaDao, isLiquityFamily, isRiver, isInverse, isUsdd, isCompoundV3Type, isFluid, isGearboxV3, isLlamaLend, isFrankencoin, isResupply, LIQUITY_FAMILY_LENDERS, isAaveType, isCompoundV3, isMultiMarket, isSiloV2Type, isSiloV3Type, isDolomite,
|
|
4
|
+
import { Lender, isExactly, isMidnight, isTerm, isTermMax, isTeller, isListaDao, isLiquityFamily, isRiver, isInverse, isUsdd, isCompoundV3Type, isFluid, isGearboxV3, isLlamaLend, isFrankencoin, isResupply, isCurvance, LIQUITY_FAMILY_LENDERS, isAaveType, isCompoundV3, isMultiMarket, isSiloV2Type, isSiloV3Type, isDolomite, hasCrossMarginRisk, isEulerType, isAaveV4Type, isInit, isMorphoType, isCompoundV2Type, isVenusType, isSumerType, AAVE_V3_LENDERS, AAVE_V2_LENDERS, isAaveV2Type, isAaveV32Type, isAaveV3Type, isYLDR, isLista, hasSubAccounts, isTectonicType, isKineticType, isBenqiType } from '@1delta/lender-registry';
|
|
5
5
|
export { hasCrossMarginRisk, isAaveType, isAaveV2Type, isAaveV32Type, isAaveV3Type, isCompoundV3, isCompoundV3Type, isInit, isMorphoType, isMultiMarket, isYLDR } from '@1delta/lender-registry';
|
|
6
6
|
import { fetchTokenLists, fetchTokenList, aavePools, compoundV3Pools, initConfig, morphoPools, compoundV2Pools, eulerVaults, siloMarkets, siloV2LenderKey, siloMarketsV3, siloV3LenderKey, aaveV4Spokes, aaveV4SpokeLenderKey, fluidResolvers, gearboxResolvers, dolomiteConfigs, midnightConfig, termConfig, termMarketsByChain, exactlyConfig, liquityLendersByChain, riverLendersByChain, inverseLendersByChain, llamaLendLendersByChain, usddLendersByChain, frankencoinLendersByChain, resupplyLendersByChain, curvanceLendersByChain, tellerConfigByChain, tellerPoolsByChain, termMaxConfigByChain, aaveReserves, compoundV3Reserves, liquityConfigFor, liquityBranchesByChain, riverConfigFor, riverChainData, termMaxApiBaseUrl, termMaxLenderKey, inverseConfigFor, inverseChainData, llamaLendConfigFor, llamaLendChainData, llamaLendApiBaseUrl, resupplyConfigFor, usddConfigFor, usddChainData, frankencoinConfigFor, frankencoinChainData, aaveOracles, morphoTypeMarkets, resolveMidnightApiBase, resolveTermApiBase, llamaLendMarketByVault, getSiloV2MarketEntry, getSiloV3MarketEntry, dolomiteEmode, curvanceConfigFor, aaveTokens, morphoTypeVaults, eulerConfigs, gearboxMarketCompressor, gearboxMarketConfigurators, gearboxV3LenderKey, midnightMarketsByChain, parseAaveV4SpokeLenderKey, gearboxAccountCompressor, compoundV2Tokens, exactlyMarketsByChain, aaveOraclesConfig, compoundV2Oracles, compoundV2TokenArray, compoundV2OraclesConfig, compoundV3OraclesData, compoundV3BaseData, compoundV2Reserves, aaveV4Oracles } from '@1delta/data-sdk';
|
|
7
7
|
import lodash from 'lodash';
|
|
@@ -23450,6 +23450,30 @@ async function fetchCurvanceMarkets(lender, chainId) {
|
|
|
23450
23450
|
);
|
|
23451
23451
|
return result;
|
|
23452
23452
|
}
|
|
23453
|
+
async function fetchMinLoanSizes(chainId, marketManagers) {
|
|
23454
|
+
const out = /* @__PURE__ */ new Map();
|
|
23455
|
+
if (marketManagers.length === 0) return out;
|
|
23456
|
+
try {
|
|
23457
|
+
const res = await multicallRetryUniversal({
|
|
23458
|
+
chain: chainId,
|
|
23459
|
+
calls: marketManagers.map((mm) => ({
|
|
23460
|
+
address: mm,
|
|
23461
|
+
name: "MIN_LOAN_SIZE",
|
|
23462
|
+
params: []
|
|
23463
|
+
})),
|
|
23464
|
+
abi: marketManagers.map(() => CurvanceMarketManagerAbi),
|
|
23465
|
+
allowFailure: true
|
|
23466
|
+
});
|
|
23467
|
+
marketManagers.forEach((mm, i) => {
|
|
23468
|
+
const v = res?.[i];
|
|
23469
|
+
if (typeof v === "bigint") out.set(mm.toLowerCase(), v);
|
|
23470
|
+
else if (typeof v === "string" && /^\d+$/.test(v))
|
|
23471
|
+
out.set(mm.toLowerCase(), BigInt(v));
|
|
23472
|
+
});
|
|
23473
|
+
} catch {
|
|
23474
|
+
}
|
|
23475
|
+
return out;
|
|
23476
|
+
}
|
|
23453
23477
|
async function fetchViaReader(lender, chainId, config) {
|
|
23454
23478
|
const reader = config.protocolReader;
|
|
23455
23479
|
let staticData = [];
|
|
@@ -23540,9 +23564,18 @@ async function fetchViaReader(lender, chainId, config) {
|
|
|
23540
23564
|
markets.push({
|
|
23541
23565
|
marketManager,
|
|
23542
23566
|
cooldownLength: big2(m?.cooldownLength),
|
|
23567
|
+
// Filled in below — one batched round for every market at once.
|
|
23568
|
+
minLoanSizeUsdWad: 0n,
|
|
23543
23569
|
tokens
|
|
23544
23570
|
});
|
|
23545
23571
|
}
|
|
23572
|
+
const minLoans = await fetchMinLoanSizes(
|
|
23573
|
+
chainId,
|
|
23574
|
+
markets.map((mk) => mk.marketManager)
|
|
23575
|
+
);
|
|
23576
|
+
for (const mk of markets) {
|
|
23577
|
+
mk.minLoanSizeUsdWad = minLoans.get(mk.marketManager.toLowerCase()) ?? 0n;
|
|
23578
|
+
}
|
|
23546
23579
|
return { lender, config, markets };
|
|
23547
23580
|
}
|
|
23548
23581
|
async function fetchViaMulticall(lender, chainId, config) {
|
|
@@ -23729,10 +23762,19 @@ async function fetchViaMulticall(lender, chainId, config) {
|
|
|
23729
23762
|
// `MIN_HOLD_PERIOD` is a constant on every deployment; hardcoding the
|
|
23730
23763
|
// known value here beats an extra round-trip in the fallback path.
|
|
23731
23764
|
cooldownLength: 1200n,
|
|
23765
|
+
minLoanSizeUsdWad: 0n,
|
|
23732
23766
|
tokens: [token]
|
|
23733
23767
|
});
|
|
23734
23768
|
});
|
|
23735
|
-
|
|
23769
|
+
const out = [...byManager.values()];
|
|
23770
|
+
const minLoans = await fetchMinLoanSizes(
|
|
23771
|
+
chainId,
|
|
23772
|
+
out.map((mk) => mk.marketManager)
|
|
23773
|
+
);
|
|
23774
|
+
for (const mk of out) {
|
|
23775
|
+
mk.minLoanSizeUsdWad = minLoans.get(mk.marketManager.toLowerCase()) ?? 0n;
|
|
23776
|
+
}
|
|
23777
|
+
return { lender, config, markets: out };
|
|
23736
23778
|
}
|
|
23737
23779
|
function allowlist(config) {
|
|
23738
23780
|
if (!config.marketAllowlist || config.marketAllowlist.length === 0) {
|
|
@@ -24608,6 +24650,13 @@ function convertCurvanceMarketsToResponse(raw, chainId, prices = {}, _additional
|
|
|
24608
24650
|
* opened or topped up — gate every exit flow on it.
|
|
24609
24651
|
*/
|
|
24610
24652
|
holdPeriodSeconds: Number(market.cooldownLength || 1200n),
|
|
24653
|
+
/**
|
|
24654
|
+
* `MIN_LOAN_SIZE` in WAD **USD** — not base units, unlike every
|
|
24655
|
+
* other lender's floor. Gates BOTH ends: opening below it reverts,
|
|
24656
|
+
* and so does a partial repay leaving less (a full repay always
|
|
24657
|
+
* works). `0` means the read failed, NOT that there is no floor.
|
|
24658
|
+
*/
|
|
24659
|
+
minLoanSizeUsdWad: market.minLoanSizeUsdWad.toString(),
|
|
24611
24660
|
/**
|
|
24612
24661
|
* Native-leverage entry points for this market, or `undefined` when
|
|
24613
24662
|
* none is registered — which is a REAL state, not a gap: 5 of the 25
|
|
@@ -62083,6 +62132,16 @@ var ROW_DESCRIPTOR_KEYS = [
|
|
|
62083
62132
|
"exactly",
|
|
62084
62133
|
"usdd",
|
|
62085
62134
|
"termmax",
|
|
62135
|
+
// These five were missing, and the failure is SILENT: the adapter still
|
|
62136
|
+
// runs, reads an empty descriptor and returns a sheet that looks complete
|
|
62137
|
+
// while omitting the lender's whole reason for having an adapter. Any lender
|
|
62138
|
+
// with an entry in `TERM_ADAPTERS` that reads `input.market.<key>` belongs
|
|
62139
|
+
// here — the adapter list and this list must not drift apart.
|
|
62140
|
+
"llamalend",
|
|
62141
|
+
"frankencoin",
|
|
62142
|
+
"resupply",
|
|
62143
|
+
"gearbox",
|
|
62144
|
+
"curvance",
|
|
62086
62145
|
"broker",
|
|
62087
62146
|
"collateralProvider",
|
|
62088
62147
|
"loanProvider"
|
|
@@ -62148,6 +62207,13 @@ function rateTags(rate) {
|
|
|
62148
62207
|
}
|
|
62149
62208
|
if (rate.rewards?.some((r) => r.indicative || r.kind === "points"))
|
|
62150
62209
|
push(out, "points-rewards");
|
|
62210
|
+
const total = rate.aprTotal;
|
|
62211
|
+
const base = rate.components?.base ?? 0;
|
|
62212
|
+
const intrinsic = rate.components?.intrinsic ?? 0;
|
|
62213
|
+
if (total > 0) {
|
|
62214
|
+
if (intrinsic > 0 && intrinsic >= total / 2) push(out, "intrinsic-yield");
|
|
62215
|
+
if (base <= 1e-4 && intrinsic > 0) push(out, "no-market-interest");
|
|
62216
|
+
}
|
|
62151
62217
|
return [...out];
|
|
62152
62218
|
}
|
|
62153
62219
|
function maturityTags(maturity) {
|
|
@@ -62355,11 +62421,22 @@ var exitPhrase = {
|
|
|
62355
62421
|
"off-chain": "exit off-chain",
|
|
62356
62422
|
"dex-only": "exit only via a DEX"
|
|
62357
62423
|
};
|
|
62424
|
+
function provenance(rate) {
|
|
62425
|
+
const total = rate.aprTotal;
|
|
62426
|
+
const base = rate.components?.base ?? 0;
|
|
62427
|
+
const intrinsic = rate.components?.intrinsic ?? 0;
|
|
62428
|
+
if (!(intrinsic > 0) || !(total > 0)) return "";
|
|
62429
|
+
if (base <= 1e-4) return " (all from the asset itself)";
|
|
62430
|
+
if (intrinsic >= total / 2) {
|
|
62431
|
+
return ` (${pct(intrinsic)} from the asset, ${pct(base)} from the market)`;
|
|
62432
|
+
}
|
|
62433
|
+
return ` (incl. ${pct(intrinsic)} from the asset)`;
|
|
62434
|
+
}
|
|
62358
62435
|
function supplyHeadline(s) {
|
|
62359
62436
|
if (s.role === "collateral") {
|
|
62360
62437
|
return `Collateral only \xB7 ${maturityPhrase(s.maturity)}`;
|
|
62361
62438
|
}
|
|
62362
|
-
const rate = `${rateLabel(s)} ${pct(s.rate.aprTotal)}`;
|
|
62439
|
+
const rate = `${rateLabel(s)} ${pct(s.rate.aprTotal)}${provenance(s.rate)}`;
|
|
62363
62440
|
const exit = exitPhrase[String(s.exit.mode)] ?? (s.exit.settlement === "sync" ? "withdraw any time" : "delayed withdrawal");
|
|
62364
62441
|
const cooldown = s.exit.cooldownSecs ? ` (${duration(s.exit.cooldownSecs)})` : "";
|
|
62365
62442
|
const mat = s.maturity.kind === "perpetual" ? "" : ` ${maturityPhrase(s.maturity)}`;
|
|
@@ -62705,6 +62782,26 @@ var TERM_PROFILES = [
|
|
|
62705
62782
|
]
|
|
62706
62783
|
}
|
|
62707
62784
|
}),
|
|
62785
|
+
P({
|
|
62786
|
+
id: "curvance.dual@v1",
|
|
62787
|
+
name: "Curvance isolated market",
|
|
62788
|
+
family: "curvance",
|
|
62789
|
+
supply: {
|
|
62790
|
+
description: "An isolated two-token market on Monad. Each leg is a cToken; a leg with no debt cap is collateral-only and pays no supply interest \u2014 its return is the underlying asset\u2019s own yield.",
|
|
62791
|
+
implications: [
|
|
62792
|
+
"Posting collateral starts a 20-minute hold during which you cannot redeem or transfer your shares.",
|
|
62793
|
+
"A collateral-only leg pays 0 % \u2014 the yield you earn is whatever the deposited asset earns on its own."
|
|
62794
|
+
]
|
|
62795
|
+
},
|
|
62796
|
+
borrow: {
|
|
62797
|
+
description: "Borrow the market\u2019s other leg against your collateral, with liquidation running from a soft threshold (partial) to a hard one (full).",
|
|
62798
|
+
implications: [
|
|
62799
|
+
"You cannot repay for 20 MINUTES after opening or topping up \u2014 the position cannot be closed, deleveraged or migrated out in that window, and an incoming liquidation cannot be averted by repaying (adding collateral still works).",
|
|
62800
|
+
"There is a $10 minimum debt, and it gates the exit as well: a partial repay that would leave less than $10 of debt reverts. Closing in full is always allowed.",
|
|
62801
|
+
"Liquidation has TWO thresholds \u2014 the headline penalty is the one at the soft threshold, and it scales up continuously to the hard one."
|
|
62802
|
+
]
|
|
62803
|
+
}
|
|
62804
|
+
}),
|
|
62708
62805
|
// ── Vaults ───────────────────────────────────────────────────────────────
|
|
62709
62806
|
P({
|
|
62710
62807
|
id: "vault.lending@v1",
|
|
@@ -64386,6 +64483,107 @@ var resupplyAdapter = {
|
|
|
64386
64483
|
};
|
|
64387
64484
|
}
|
|
64388
64485
|
};
|
|
64486
|
+
function minLoanInAssetUnits(minLoanUsdWad, priceUsdWad, decimals) {
|
|
64487
|
+
const floor = toBig6(minLoanUsdWad);
|
|
64488
|
+
const price2 = toBig6(priceUsdWad);
|
|
64489
|
+
if (floor == null || price2 == null || floor === 0n || price2 === 0n) {
|
|
64490
|
+
return void 0;
|
|
64491
|
+
}
|
|
64492
|
+
const dec = typeof decimals === "number" ? decimals : Number(decimals);
|
|
64493
|
+
if (!Number.isFinite(dec) || dec < 0 || dec > 36) return void 0;
|
|
64494
|
+
const scaled = floor * 10n ** BigInt(dec);
|
|
64495
|
+
const units = (scaled + price2 - 1n) / price2;
|
|
64496
|
+
return units > 0n ? units.toString() : void 0;
|
|
64497
|
+
}
|
|
64498
|
+
function toBig6(v) {
|
|
64499
|
+
if (typeof v === "bigint") return v;
|
|
64500
|
+
if (typeof v === "number" && Number.isFinite(v)) return BigInt(Math.trunc(v));
|
|
64501
|
+
if (typeof v === "string" && /^\d+$/.test(v)) return BigInt(v);
|
|
64502
|
+
return void 0;
|
|
64503
|
+
}
|
|
64504
|
+
var curvanceAdapter = {
|
|
64505
|
+
id: "curvance",
|
|
64506
|
+
matches: isCurvance,
|
|
64507
|
+
profileId: () => "curvance.dual@v1",
|
|
64508
|
+
build: (input) => {
|
|
64509
|
+
const cv = input.market?.curvance ?? {};
|
|
64510
|
+
const holdSecs = typeof cv.holdPeriodSeconds === "number" && cv.holdPeriodSeconds > 0 ? cv.holdPeriodSeconds : void 0;
|
|
64511
|
+
const tokens = Array.isArray(cv.tokens) ? cv.tokens : [];
|
|
64512
|
+
const debtLeg = tokens.find(
|
|
64513
|
+
(t) => String(t?.underlying ?? "").toLowerCase() === String(input.underlying ?? "").toLowerCase()
|
|
64514
|
+
) ?? void 0;
|
|
64515
|
+
const minDebt = minLoanInAssetUnits(
|
|
64516
|
+
cv.minLoanSizeUsdWad,
|
|
64517
|
+
debtLeg?.assetPrice,
|
|
64518
|
+
debtLeg?.assetDecimals ?? debtLeg?.decimals
|
|
64519
|
+
);
|
|
64520
|
+
const softPen = numOrUndef(debtLeg?.liquidationPenaltySoft);
|
|
64521
|
+
const hardPen = numOrUndef(debtLeg?.liquidationPenaltyHard);
|
|
64522
|
+
return {
|
|
64523
|
+
constraints: {
|
|
64524
|
+
// One position per address per market; both legs share one health.
|
|
64525
|
+
positionModel: "account",
|
|
64526
|
+
positionIdMeaning: "the borrower address itself \u2014 Curvance keys a position to the account, there is no loan id or NFT",
|
|
64527
|
+
crossMargin: false
|
|
64528
|
+
},
|
|
64529
|
+
borrow: {
|
|
64530
|
+
debtShape: "accruing",
|
|
64531
|
+
availability: {
|
|
64532
|
+
// A borrow below the floor REVERTS, so the CTA must be gated on it.
|
|
64533
|
+
minSize: minDebt
|
|
64534
|
+
// The hold period blocks CLOSING, which the generic builder assumes
|
|
64535
|
+
// is always possible. Stated on the exit side below; `canClose`
|
|
64536
|
+
// stays true because it is per-ACCOUNT and time-bound, not a market
|
|
64537
|
+
// property — a market-level `false` here would read as "nobody can
|
|
64538
|
+
// ever exit", which is wrong and worse.
|
|
64539
|
+
},
|
|
64540
|
+
exit: {
|
|
64541
|
+
partialAllowed: true,
|
|
64542
|
+
minDebt,
|
|
64543
|
+
cooldownSecs: holdSecs
|
|
64544
|
+
},
|
|
64545
|
+
...softPen != null || hardPen != null ? {
|
|
64546
|
+
liquidation: {
|
|
64547
|
+
// Two thresholds with a continuously scaling penalty and close
|
|
64548
|
+
// factor between them, plus a Fastlane/Atlas auction path that
|
|
64549
|
+
// can set both at runtime.
|
|
64550
|
+
model: "dual-threshold",
|
|
64551
|
+
trigger: "price",
|
|
64552
|
+
penalty: softPen ?? hardPen,
|
|
64553
|
+
penalties: [
|
|
64554
|
+
...softPen != null ? [
|
|
64555
|
+
{
|
|
64556
|
+
id: "soft",
|
|
64557
|
+
label: "Soft threshold (partial)",
|
|
64558
|
+
value: softPen,
|
|
64559
|
+
description: "First touch: a PARTIAL liquidation at the close factor, at the lower penalty."
|
|
64560
|
+
}
|
|
64561
|
+
] : [],
|
|
64562
|
+
...hardPen != null ? [
|
|
64563
|
+
{
|
|
64564
|
+
id: "hard",
|
|
64565
|
+
label: "Hard threshold (full)",
|
|
64566
|
+
value: hardPen,
|
|
64567
|
+
description: "Full liquidation at the higher penalty. The penalty scales continuously between the two."
|
|
64568
|
+
}
|
|
64569
|
+
] : []
|
|
64570
|
+
]
|
|
64571
|
+
}
|
|
64572
|
+
} : {}
|
|
64573
|
+
},
|
|
64574
|
+
supply: {
|
|
64575
|
+
exit: {
|
|
64576
|
+
// The same 20 minutes blocks redemption and share transfers, not
|
|
64577
|
+
// just repayment.
|
|
64578
|
+
cooldownSecs: holdSecs
|
|
64579
|
+
}
|
|
64580
|
+
}
|
|
64581
|
+
};
|
|
64582
|
+
}
|
|
64583
|
+
};
|
|
64584
|
+
function numOrUndef(v) {
|
|
64585
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
64586
|
+
}
|
|
64389
64587
|
|
|
64390
64588
|
// src/terms/adapters/index.ts
|
|
64391
64589
|
var TERM_ADAPTERS = [
|
|
@@ -64404,7 +64602,8 @@ var TERM_ADAPTERS = [
|
|
|
64404
64602
|
gearboxAdapter,
|
|
64405
64603
|
llamaLendAdapter,
|
|
64406
64604
|
frankencoinAdapter,
|
|
64407
|
-
resupplyAdapter
|
|
64605
|
+
resupplyAdapter,
|
|
64606
|
+
curvanceAdapter
|
|
64408
64607
|
];
|
|
64409
64608
|
function resolveAdapter(lender) {
|
|
64410
64609
|
return TERM_ADAPTERS.find((a) => a.matches(lender));
|