@1delta/margin-fetcher 0.0.3423 → 5.0.0
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/ccip-VK5PCUV6.js +5 -0
- package/dist/{ccip-VFYF2C5F.js.map → ccip-VK5PCUV6.js.map} +1 -1
- package/dist/{chunk-Z3MGRQJR.js → chunk-YILYOOYB.js} +200 -4
- package/dist/chunk-YILYOOYB.js.map +1 -0
- package/dist/index.d.ts +2999 -111
- package/dist/index.js +12415 -2501
- package/dist/index.js.map +1 -1
- package/package.json +10 -8
- package/dist/ccip-VFYF2C5F.js +0 -5
- package/dist/chunk-Z3MGRQJR.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { PublicClient, Address, Hex } from 'viem';
|
|
2
2
|
import { Lender } from '@1delta/lender-registry';
|
|
3
|
-
export { isAaveType, isAaveV2Type, isAaveV32Type, isAaveV3Type, isCompoundV3, isCompoundV3Type, isInit, isMorphoType, isMultiMarket, isYLDR } from '@1delta/lender-registry';
|
|
3
|
+
export { hasCrossMarginRisk, isAaveType, isAaveV2Type, isAaveV32Type, isAaveV3Type, isCompoundV3, isCompoundV3Type, isInit, isMorphoType, isMultiMarket, isYLDR } from '@1delta/lender-registry';
|
|
4
4
|
import { DebitData, LenderDebitData, LstAcceptedInput } from '@1delta/calldata-sdk';
|
|
5
5
|
import { RelayProxyConfig } from '@1delta/proxy-fetch';
|
|
6
|
-
import { TermMarketConfig, LiquityBranchConfig, LiquityConfigChain, RiverMarketConfig, RiverConfigChain, RiverChainData, TellerPoolConfig, MorphoTypeVaultEntry } from '@1delta/data-sdk';
|
|
6
|
+
import { TermMarketConfig, LiquityBranchConfig, LiquityConfigChain, RiverMarketConfig, RiverConfigChain, RiverChainData, InverseMarketConfig, InverseConfigChain, InverseChainData, ResupplyConfigChain, LlamaLendMarketConfig, LlamaLendConfigChain, LlamaLendChainData, UsddMarketConfig, UsddConfigChain, UsddChainData, FrankencoinMarketConfig, FrankencoinConfigChain, FrankencoinChainData, TellerPoolConfig, MorphoTypeVaultEntry } from '@1delta/data-sdk';
|
|
7
7
|
export { MorphoLensAbi } from '@1delta/abis';
|
|
8
8
|
|
|
9
9
|
interface GenericCurrency {
|
|
@@ -329,6 +329,76 @@ interface FixedTermProvider {
|
|
|
329
329
|
/** The single counterparty/venue contract, when there is one (Lista broker, Term servicer). */
|
|
330
330
|
address?: string;
|
|
331
331
|
}
|
|
332
|
+
/**
|
|
333
|
+
* Origination window for a fixed-term market whose terms are only obtainable
|
|
334
|
+
* during a bounded round rather than continuously (`provider.kind: 'auction'`
|
|
335
|
+
* — Term Finance).
|
|
336
|
+
*
|
|
337
|
+
* This is the difference between "the rate card is empty right now" and "this
|
|
338
|
+
* market is dead": between rounds a Term repo still has a maturity, collateral
|
|
339
|
+
* params and a last-cleared rate, but nothing can be borrowed until the next
|
|
340
|
+
* round is listed. Without it every closed repo renders as an ordinary
|
|
341
|
+
* borrowable market whose action silently cannot be built.
|
|
342
|
+
*
|
|
343
|
+
* `status` is a snapshot at fetch time; the timestamps are raw so a consumer
|
|
344
|
+
* can re-derive it live (and drive a countdown) against a cached response.
|
|
345
|
+
*/
|
|
346
|
+
interface FixedTermAuction {
|
|
347
|
+
/**
|
|
348
|
+
* Round lifecycle at fetch time:
|
|
349
|
+
* - `upcoming` — listed but not yet accepting submissions (`now < startTime`)
|
|
350
|
+
* - `open` — accepting sealed bids/offers (`startTime ≤ now < revealTime`)
|
|
351
|
+
* - `revealing` — submissions closed, prices revealing / clearing pending
|
|
352
|
+
* (`revealTime ≤ now < endTime`)
|
|
353
|
+
* - `closed` — no round is currently listed for this market. Borrowing is
|
|
354
|
+
* unavailable until the next one; lending may still be
|
|
355
|
+
* possible on the secondary repo-token book.
|
|
356
|
+
*/
|
|
357
|
+
status: 'upcoming' | 'open' | 'revealing' | 'closed';
|
|
358
|
+
/**
|
|
359
|
+
* Can a NEW borrow be opened right now? True only inside an open round —
|
|
360
|
+
* Term borrow origination is a sealed bid, so there is no other entry point.
|
|
361
|
+
*
|
|
362
|
+
* Consume this rather than re-deriving from `status`: it is the single flag
|
|
363
|
+
* a borrow CTA should gate on, and it stays correct if more statuses appear.
|
|
364
|
+
* It is NOT the same as `canLend` — see below.
|
|
365
|
+
*/
|
|
366
|
+
canBorrow: boolean;
|
|
367
|
+
/**
|
|
368
|
+
* Can a NEW lend position be opened right now? Deliberately decoupled from
|
|
369
|
+
* `canBorrow`: the primary auction is only one of two lend surfaces, and
|
|
370
|
+
* buying repo tokens on the secondary book works between rounds. So a closed
|
|
371
|
+
* round leaves the market lend-only rather than fully inert, and a UI that
|
|
372
|
+
* greys out the whole market would be wrong.
|
|
373
|
+
*/
|
|
374
|
+
canLend: boolean;
|
|
375
|
+
/**
|
|
376
|
+
* Seconds until submissions close (`revealTime − now`), or undefined when no
|
|
377
|
+
* round is open. A snapshot — for a live countdown, derive from `revealTime`.
|
|
378
|
+
*/
|
|
379
|
+
secondsUntilClose?: number;
|
|
380
|
+
/**
|
|
381
|
+
* Ready-to-display consequences of this market's origination model, most
|
|
382
|
+
* important first. Mirrors `params.market.teller.implications`: auction
|
|
383
|
+
* mechanics are unusual enough that a UI showing only a rate misleads.
|
|
384
|
+
*/
|
|
385
|
+
implications?: string[];
|
|
386
|
+
/** Round id. Absent when `status: 'closed'`. */
|
|
387
|
+
id?: string;
|
|
388
|
+
/** Submissions open (unix seconds). Absent when `status: 'closed'`. */
|
|
389
|
+
startTime?: number;
|
|
390
|
+
/** Submissions CLOSE / reveal begins (unix seconds). Absent when closed. */
|
|
391
|
+
revealTime?: number;
|
|
392
|
+
/** Round clears (unix seconds). Absent when closed. */
|
|
393
|
+
endTime?: number;
|
|
394
|
+
/**
|
|
395
|
+
* Minimum submission size in loan-token base units (raw). Term rounds carry a
|
|
396
|
+
* real floor (e.g. 1000 USDC) — an amount below it cannot be submitted at all,
|
|
397
|
+
* so it belongs next to the terms rather than surfacing as a failed action.
|
|
398
|
+
*/
|
|
399
|
+
minBorrowAmount?: string;
|
|
400
|
+
minLendAmount?: string;
|
|
401
|
+
}
|
|
332
402
|
/**
|
|
333
403
|
* Canonical fixed-term market descriptor, emitted on `params.market.fixedTerm`
|
|
334
404
|
* for EVERY fixed-rate / fixed-maturity market (Lista brokered + Morpho
|
|
@@ -338,7 +408,7 @@ interface FixedTermProvider {
|
|
|
338
408
|
*/
|
|
339
409
|
interface FixedTermInfo {
|
|
340
410
|
/** Underlying fixed-term protocol shape. */
|
|
341
|
-
model: 'lista' | 'midnight' | 'term' | 'exactly' | 'teller';
|
|
411
|
+
model: 'lista' | 'midnight' | 'term' | 'exactly' | 'teller' | 'termmax';
|
|
342
412
|
/**
|
|
343
413
|
* Single fixed calendar maturity (unix secs). Undefined for rolling-duration
|
|
344
414
|
* menus (Lista) and multi-maturity markets (Exactly — the maturity menu lives
|
|
@@ -367,17 +437,40 @@ interface FixedTermInfo {
|
|
|
367
437
|
earlyRepay: FixedTermEarlyRepay;
|
|
368
438
|
/** Who offers the term (Lista broker vs Midnight order book). */
|
|
369
439
|
provider?: FixedTermProvider;
|
|
440
|
+
/**
|
|
441
|
+
* Origination window, for `provider.kind: 'auction'` markets only (Term
|
|
442
|
+
* Finance). Absent for lenders whose terms are continuously available — a
|
|
443
|
+
* missing `auction` means "no window applies", NOT "closed".
|
|
444
|
+
*/
|
|
445
|
+
auction?: FixedTermAuction;
|
|
370
446
|
}
|
|
371
|
-
/**
|
|
447
|
+
/**
|
|
448
|
+
* A single fixed-term loan, attached to its own entry in the positions array.
|
|
449
|
+
*
|
|
450
|
+
* Named for Lista (the first producer) but SHARED by every fixed-term lender
|
|
451
|
+
* that emits per-loan rows — Lista, Exactly, TermMax, Teller. Fields are
|
|
452
|
+
* therefore mostly optional and several are lender-specific; see
|
|
453
|
+
* [FIXED_TERM_REPAY_TERMS.md](../../../../FIXED_TERM_REPAY_TERMS.md) for which
|
|
454
|
+
* lender populates what and for the exact repay economics behind each number.
|
|
455
|
+
*
|
|
456
|
+
* `loanId` is the WRITE TARGET and its meaning differs per lender (Lista posId /
|
|
457
|
+
* Exactly maturity-as-string / TermMax gtId / Teller bidId) — check the lender
|
|
458
|
+
* before using it. `termId` is the RATE-MENU id and is NOT interchangeable with
|
|
459
|
+
* it (Exactly is the only lender where the two coincide, both = maturity).
|
|
460
|
+
*/
|
|
372
461
|
interface ListaTermLoan {
|
|
373
462
|
/** loanId — the repay target for the LISTA_BROKER_REPAY composer op. For fixed loans this is the
|
|
374
|
-
* posId; for the dynamic (flexible) loan it is the dynamic sentinel (type(uint128).max).
|
|
463
|
+
* posId; for the dynamic (flexible) loan it is the dynamic sentinel (type(uint128).max).
|
|
464
|
+
* Other lenders reuse the slot: Exactly = String(maturity), TermMax = gtId, Teller = bidId. */
|
|
375
465
|
loanId: string;
|
|
376
466
|
/** true for the flexible (dynamic / variable-rate) loan; fixed loans omit it */
|
|
377
467
|
isDynamic?: boolean;
|
|
378
468
|
/** best-effort term product id (matched from duration vs the current menu); may be undefined */
|
|
379
469
|
termId?: number;
|
|
380
|
-
/** outstanding debt in loan-token units
|
|
470
|
+
/** outstanding debt in loan-token units. Lista: principal + accrued interest.
|
|
471
|
+
* Static-face-value lenders (Exactly / TermMax / Term): the EXIT-NOW cost —
|
|
472
|
+
* for Exactly that is discounted early and penalty-inflated when overdue, so
|
|
473
|
+
* compare against `faceValue` rather than assuming it is the face. */
|
|
381
474
|
debt: string;
|
|
382
475
|
/** locked annualised borrow rate in PERCENT (e.g. 3.857 = 3.857% APR) */
|
|
383
476
|
apr?: number;
|
|
@@ -386,9 +479,27 @@ interface ListaTermLoan {
|
|
|
386
479
|
termDays?: number;
|
|
387
480
|
/** outstanding accrued interest in loan-token units */
|
|
388
481
|
accruedInterest?: string;
|
|
389
|
-
/** early-repayment penalty (loan-token units) to close the loan now; 0 once matured
|
|
482
|
+
/** early-repayment penalty (loan-token units) to close the loan now; 0 once matured.
|
|
483
|
+
* Lista only — the OPPOSITE sign to Exactly's `earlyRepayDiscount` below. */
|
|
390
484
|
earlyRepayPenalty?: string;
|
|
391
485
|
isMatured?: boolean;
|
|
486
|
+
/** amount owed AT maturity (principal + fee). Static — no accrual index; it
|
|
487
|
+
* grows only via a late penalty where the protocol has one. */
|
|
488
|
+
faceValue?: string;
|
|
489
|
+
/** Exactly: rebate for repaying BEFORE maturity (`faceValue − debt`). Exactly
|
|
490
|
+
* never charges an early-repay fee, but this is 0 when the fixed pool has no
|
|
491
|
+
* unassigned earnings left, so it is not a guaranteed saving. */
|
|
492
|
+
earlyRepayDiscount?: string;
|
|
493
|
+
/** Exactly: penalty accrued so far past maturity (`debt − faceValue`). */
|
|
494
|
+
latePenalty?: string;
|
|
495
|
+
/** Exactly: further penalty per additional day overdue — LINEAR on face, not
|
|
496
|
+
* compounding. */
|
|
497
|
+
latePenaltyPerDay?: string;
|
|
498
|
+
/** annualized late-penalty rate in PERCENT (Exactly `penaltyRate`; ~164 %/yr).
|
|
499
|
+
* A mutable market parameter, snapshotted per fetch. */
|
|
500
|
+
latePenaltyApr?: number;
|
|
501
|
+
/** seconds past maturity; 0 until overdue */
|
|
502
|
+
secondsLate?: number;
|
|
392
503
|
}
|
|
393
504
|
interface MorphoLendingPositions extends BaseLendingPositions {
|
|
394
505
|
isWhitelisted?: boolean;
|
|
@@ -592,6 +703,18 @@ declare const getLenderPublicDataViaApi: (chainId: string, lenders: string[], pr
|
|
|
592
703
|
[lender: string]: any;
|
|
593
704
|
}>;
|
|
594
705
|
|
|
706
|
+
/**
|
|
707
|
+
* Returns true when the lender should ONLY use the API path (no on-chain
|
|
708
|
+
* fallback). Currently Morpho-type only — the Morpho GraphQL indexer has
|
|
709
|
+
* been reliable on every chain it supports, so there's no benefit to
|
|
710
|
+
* double-fetching.
|
|
711
|
+
*
|
|
712
|
+
* Exported so the config-consistency test can assert the invariant that makes
|
|
713
|
+
* the Morpho list safe: a chain routed to the on-chain path MUST have a
|
|
714
|
+
* `MORPHO_LENS` entry, otherwise `buildMorphoCall` produces a call with an
|
|
715
|
+
* undefined address and the chain silently yields no markets at all.
|
|
716
|
+
*/
|
|
717
|
+
declare function lenderApiOnly(lender: string, chainId: string): boolean;
|
|
595
718
|
declare const getLenderPublicDataAll: (chainId: string, lenders: string[], prices: {
|
|
596
719
|
[asset: string]: number;
|
|
597
720
|
}, additionalYields: AdditionalYields, multicallRetry: MulticallRetryFunction, tokenList?: () => Promise<GenericTokenList>, includeUnlistedMorphoMarkets?: boolean) => Promise<{
|
|
@@ -664,6 +787,38 @@ declare function createRawRpcCalls(preparedCalls: PreparedCall[], batchSize?: nu
|
|
|
664
787
|
declare function createMulticallRpcCall(preparedCalls: PreparedCall[], multicallAddress: string, batchSize?: number, blockTag?: string, allowFailure?: boolean): MulticallRpcBatch[];
|
|
665
788
|
|
|
666
789
|
type Call = GeneralCall;
|
|
790
|
+
/**
|
|
791
|
+
* Sentinel written into the result array for a call that returned NO data —
|
|
792
|
+
* a revert, an RPC error, or a whole `aggregate3` chunk that was rejected
|
|
793
|
+
* (rate limit, subrequest cap, dropped connection: viem marks every call in a
|
|
794
|
+
* rejected chunk as `status: 'failure'`).
|
|
795
|
+
*
|
|
796
|
+
* It is NOT a zero value. Parsers must skip these slots — coercing one to `0`
|
|
797
|
+
* turns a failed read into a phantom "no balance" position and, worse, makes a
|
|
798
|
+
* real deposit or debt silently disappear from a user's portfolio.
|
|
799
|
+
*/
|
|
800
|
+
declare const MULTICALL_FAILURE = "0x";
|
|
801
|
+
/** True when a multicall slot holds no usable data (see {@link MULTICALL_FAILURE}). */
|
|
802
|
+
declare const isFailedCall: (value: unknown) => boolean;
|
|
803
|
+
/** Reported for each endpoint that failed to serve a batch. */
|
|
804
|
+
interface EndpointFailure {
|
|
805
|
+
chainId: string;
|
|
806
|
+
/** Endpoint URL, or `rpc#<id>` when the transport does not expose one. */
|
|
807
|
+
url: string;
|
|
808
|
+
rpcId: number;
|
|
809
|
+
/** `transport` — the request itself died. `slots` — it answered with nothing but failures. */
|
|
810
|
+
kind: 'transport' | 'slots';
|
|
811
|
+
}
|
|
812
|
+
interface MulticallEndpointOptions {
|
|
813
|
+
/**
|
|
814
|
+
* Endpoint URLs already attempted for this call set. Failover consults it so
|
|
815
|
+
* a retry lands on an endpoint that has NOT already failed — see
|
|
816
|
+
* {@link resolveEndpoint}.
|
|
817
|
+
*/
|
|
818
|
+
tried?: Set<string>;
|
|
819
|
+
/** Invoked for every endpoint that fails, so callers can demote it. */
|
|
820
|
+
onEndpointFailure?: (info: EndpointFailure) => void;
|
|
821
|
+
}
|
|
667
822
|
declare function prepareMulticallInputs(abi: any[], calls: Call[]): PreparedCall[];
|
|
668
823
|
|
|
669
824
|
interface PreparedUserDataRpcCalls {
|
|
@@ -926,33 +1081,106 @@ type UserData = {
|
|
|
926
1081
|
* not as the user's full position.
|
|
927
1082
|
*/
|
|
928
1083
|
incomplete?: boolean;
|
|
1084
|
+
/**
|
|
1085
|
+
* Set when this entry did NOT come from the current read: the live read failed
|
|
1086
|
+
* and a previously COMPLETE snapshot was served in its place. The position was
|
|
1087
|
+
* accurate as of `staleAgeMs` ago; it is not a partial read (those are
|
|
1088
|
+
* `incomplete`) and it is never served for a lender that read successfully.
|
|
1089
|
+
*/
|
|
1090
|
+
stale?: boolean;
|
|
1091
|
+
/** Age of the served snapshot in ms. Only set alongside `stale`. */
|
|
1092
|
+
staleAgeMs?: number;
|
|
929
1093
|
};
|
|
930
1094
|
|
|
931
|
-
/**
|
|
1095
|
+
/**
|
|
1096
|
+
* How a lender's slice reacts to a failed read.
|
|
1097
|
+
*
|
|
1098
|
+
* Note what this is NOT keyed on: "is the position cross-margin". EVERY position
|
|
1099
|
+
* with debt is corrupted by a lost read — an isolated Morpho market computes its
|
|
1100
|
+
* health from a collateral read and a debt read, so losing either fabricates the
|
|
1101
|
+
* same nonsense a lost Aave reserve does. The question here is narrower and
|
|
1102
|
+
* purely mechanical: **what is the smallest thing we can void?** A failed slot
|
|
1103
|
+
* carries no market label, so the only unit we can void is the lender key.
|
|
1104
|
+
*
|
|
1105
|
+
* - `strict` — void the whole slice on any lost read. Correct when the slice
|
|
1106
|
+
* resolves to ONE risk computation, which is true both for a single-market
|
|
1107
|
+
* lender and for a multi-market lender that is nevertheless cross-margin
|
|
1108
|
+
* (Exactly scores every market under one per-chain Auditor). Nothing smaller
|
|
1109
|
+
* can be voided, and publishing the remainder would publish a fiction.
|
|
1110
|
+
* - `lenient` — publish, flag `incomplete`, and let per-record invariant
|
|
1111
|
+
* validation catch the corrupted one. Correct ONLY when the slice fans out to
|
|
1112
|
+
* many INDEPENDENT isolated positions, where voiding the key would discard
|
|
1113
|
+
* hundreds of intact markets to hide one — a cure worse than the disease.
|
|
1114
|
+
*
|
|
1115
|
+
* So leniency requires BOTH properties: many independent positions AND no shared
|
|
1116
|
+
* risk computation across them.
|
|
1117
|
+
*/
|
|
1118
|
+
type ReadFailurePolicy = 'strict' | 'lenient';
|
|
1119
|
+
declare const getReadFailurePolicy: (lender: string) => ReadFailurePolicy;
|
|
1120
|
+
/** Why a lender's slice did not convert cleanly. */
|
|
1121
|
+
type IncompleteReason =
|
|
1122
|
+
/** Every read in the slice failed. */
|
|
1123
|
+
'all-reads-failed'
|
|
1124
|
+
/** Reads failed on a strict lender — the whole risk set was voided. */
|
|
1125
|
+
| 'partial-read-cross-margin'
|
|
1126
|
+
/** Reads failed on a lenient lender — surviving markets were published. */
|
|
1127
|
+
| 'partial-read'
|
|
1128
|
+
/** The converter threw. */
|
|
1129
|
+
| 'converter-error'
|
|
1130
|
+
/** The converted entry asserted something that cannot be true. */
|
|
1131
|
+
| 'invariant-violation';
|
|
1132
|
+
/** Reported per lender whose multicall slice did not convert cleanly. */
|
|
932
1133
|
interface IncompleteLenderRead {
|
|
933
1134
|
lender: string;
|
|
934
1135
|
/** Number of slots in the lender's slice that returned no data. */
|
|
935
1136
|
failedCalls: number;
|
|
1137
|
+
/**
|
|
1138
|
+
* Subset of `failedCalls` that could plausibly succeed on a re-read — i.e.
|
|
1139
|
+
* excluding calls known to have reverted. Zero means re-fetching this lender
|
|
1140
|
+
* is pointless: the markets in question always revert for this account.
|
|
1141
|
+
* Equals `failedCalls` when no `permanentFailures` set was supplied.
|
|
1142
|
+
*/
|
|
1143
|
+
retryableFailedCalls: number;
|
|
936
1144
|
/** Size of the lender's slice. */
|
|
937
1145
|
totalCalls: number;
|
|
938
|
-
/** True when
|
|
1146
|
+
/** True when nothing was published for this lender. */
|
|
939
1147
|
dropped: boolean;
|
|
1148
|
+
/** What went wrong. */
|
|
1149
|
+
reason: IncompleteReason;
|
|
1150
|
+
/** Extra context for logs (converter message, violation details). */
|
|
1151
|
+
detail?: string;
|
|
940
1152
|
}
|
|
941
1153
|
interface ConvertLenderUserDataOptions {
|
|
942
|
-
/** Invoked once per lender
|
|
1154
|
+
/** Invoked once per lender that did not convert cleanly — for logging / surfacing. */
|
|
943
1155
|
onIncomplete?: (info: IncompleteLenderRead) => void;
|
|
1156
|
+
/**
|
|
1157
|
+
* Indices (into `rawResults`) of calls that failed deterministically, as
|
|
1158
|
+
* collected by `getLenderUserDataResult`. Used only to compute
|
|
1159
|
+
* `retryableFailedCalls`.
|
|
1160
|
+
*/
|
|
1161
|
+
permanentFailures?: Set<number>;
|
|
944
1162
|
}
|
|
945
1163
|
/**
|
|
946
1164
|
* Converts the raw results into the desired format
|
|
947
1165
|
*
|
|
948
1166
|
* Slots that hold the multicall failure sentinel are NOT data: a failed read
|
|
949
|
-
* says nothing about the user's position.
|
|
950
|
-
*
|
|
951
|
-
*
|
|
952
|
-
*
|
|
953
|
-
*
|
|
954
|
-
*
|
|
955
|
-
*
|
|
1167
|
+
* says nothing about the user's position. Coercing one to zero is how a
|
|
1168
|
+
* rate-limited RPC ends up rendering phantom $0 rows and understated balances,
|
|
1169
|
+
* so failures are handled explicitly, in three gates:
|
|
1170
|
+
*
|
|
1171
|
+
* 1. **Failure policy** (see {@link ReadFailurePolicy}). Every read failing
|
|
1172
|
+
* drops the lender under either policy. Beyond that, a `strict`
|
|
1173
|
+
* (cross-margin) lender drops on ANY failure because its aggregates are only
|
|
1174
|
+
* meaningful over the complete set, while a `lenient` (multi-market) lender
|
|
1175
|
+
* publishes the markets that did read and is flagged `incomplete`.
|
|
1176
|
+
* 2. **Converter errors** are reported rather than swallowed — a throwing
|
|
1177
|
+
* converter used to leave a lender silently absent, indistinguishable from a
|
|
1178
|
+
* user with no position there.
|
|
1179
|
+
* 3. **Invariant validation** (see `validate.ts`) rejects sub-accounts that
|
|
1180
|
+
* cannot be true regardless of how the reads went — a `NaN` anywhere in the
|
|
1181
|
+
* aggregates, or (alongside failed reads) debt with no collateral behind it.
|
|
1182
|
+
*
|
|
1183
|
+
* Anything published after that is either complete or explicitly marked as not.
|
|
956
1184
|
*
|
|
957
1185
|
* @param chainId - The chain ID
|
|
958
1186
|
* @param queriesRaw - The queries to fetch data for
|
|
@@ -965,6 +1193,53 @@ declare const convertLenderUserDataResult: (chainId: string, queriesRaw: LenderU
|
|
|
965
1193
|
[lender: string]: UserData;
|
|
966
1194
|
};
|
|
967
1195
|
|
|
1196
|
+
/**
|
|
1197
|
+
* Why this exists SEPARATELY from the failure sentinels
|
|
1198
|
+
* -----------------------------------------------------
|
|
1199
|
+
* The sentinel path (`isFailedCall`) catches reads that announced themselves as
|
|
1200
|
+
* failures. This catches the ones that did not: a decodable-but-wrong response,
|
|
1201
|
+
* a market whose metadata went missing so its price resolved to `undefined`, a
|
|
1202
|
+
* converter that divided by a zero it should never have seen. Those produce the
|
|
1203
|
+
* SAME user-visible artefact as a dropped read — a debt with no collateral, a
|
|
1204
|
+
* `NaN` health factor — while every slot reports success.
|
|
1205
|
+
*
|
|
1206
|
+
* So this is the last gate before a position is published: it asserts what must
|
|
1207
|
+
* be true of any real lending position, independent of how the data was
|
|
1208
|
+
* obtained.
|
|
1209
|
+
*/
|
|
1210
|
+
/** One failed assertion about a sub-account's published shape. */
|
|
1211
|
+
interface InvariantViolation {
|
|
1212
|
+
/** Sub-account this fired on (`accountId`). */
|
|
1213
|
+
accountId: string;
|
|
1214
|
+
/** Machine-readable check name. */
|
|
1215
|
+
code: 'non-finite' | 'debt-without-collateral' | 'invalid-mode';
|
|
1216
|
+
/** Human-readable detail for logs. */
|
|
1217
|
+
detail: string;
|
|
1218
|
+
/**
|
|
1219
|
+
* `true` when the violation is only conclusive because the read was also
|
|
1220
|
+
* known-incomplete (see {@link validateUserData}).
|
|
1221
|
+
*/
|
|
1222
|
+
requiresFailedReads: boolean;
|
|
1223
|
+
}
|
|
1224
|
+
interface ValidationResult {
|
|
1225
|
+
/** Sub-accounts that passed. Empty means the whole entry must be dropped. */
|
|
1226
|
+
kept: UserDataForSubAccount[];
|
|
1227
|
+
/** Every violation found, including ones on kept sub-accounts. */
|
|
1228
|
+
violations: InvariantViolation[];
|
|
1229
|
+
/** Sub-account ids dropped as corrupt. */
|
|
1230
|
+
dropped: string[];
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Validates a converted entry and drops the sub-accounts that cannot be true.
|
|
1234
|
+
*
|
|
1235
|
+
* `hadFailedReads` gates the checks whose violation is ambiguous on its own:
|
|
1236
|
+
* with a known-incomplete read, `debt-without-collateral` is the signature of a
|
|
1237
|
+
* dropped collateral slot and the sub-account is corrupt; with a clean read it
|
|
1238
|
+
* is a genuine (if grim) position and is kept. Unconditional checks — anything
|
|
1239
|
+
* non-finite — fire either way, because no read produces those legitimately.
|
|
1240
|
+
*/
|
|
1241
|
+
declare function validateUserData(userData: UserData, hadFailedReads: boolean): ValidationResult;
|
|
1242
|
+
|
|
968
1243
|
interface ExposureInfo {
|
|
969
1244
|
asset: GenericCurrency;
|
|
970
1245
|
collateralFactor: number;
|
|
@@ -1004,9 +1279,17 @@ declare function unflattenLenderData(pools: PoolWithMeta[]): LenderData;
|
|
|
1004
1279
|
* @param logs - show multicall error logs, default is false
|
|
1005
1280
|
* @param concurrency - number of distinct RPC endpoints to shard batches
|
|
1006
1281
|
* across in parallel; <= 1 keeps the legacy single-endpoint path
|
|
1282
|
+
* @param permanentFailures - optional collector filled with the indices of
|
|
1283
|
+
* calls that failed DETERMINISTICALLY (revert / no code / unknown selector)
|
|
1284
|
+
* rather than because of the RPC. Pass it to
|
|
1285
|
+
* {@link convertLenderUserDataResult} so a caller can tell "this market
|
|
1286
|
+
* always reverts" from "this read was lost" and only re-fetch the latter.
|
|
1287
|
+
* @param onEndpointFailure - optional hook invoked for every RPC endpoint that
|
|
1288
|
+
* fails to serve a batch. Only the caller knows where to persist that (KV,
|
|
1289
|
+
* metrics), and without it every request rediscovers the same bad endpoint.
|
|
1007
1290
|
* @returns The raw results from the multicall, "0x" for failures
|
|
1008
1291
|
*/
|
|
1009
|
-
declare const getLenderUserDataResult: (chainId: string, queriesRaw: LenderUserQuery[], getEvmClient: GetEvmClientFunction, allowFailure?: boolean, batchSize?: number, retries?: number, logs?: boolean, concurrency?: number) => Promise<any[]>;
|
|
1292
|
+
declare const getLenderUserDataResult: (chainId: string, queriesRaw: LenderUserQuery[], getEvmClient: GetEvmClientFunction, allowFailure?: boolean, batchSize?: number, retries?: number, logs?: boolean, concurrency?: number, permanentFailures?: Set<number>, onEndpointFailure?: (info: EndpointFailure) => void) => Promise<any[]>;
|
|
1010
1293
|
/**
|
|
1011
1294
|
* Prepares the RPC calls for fetching user data without executing them
|
|
1012
1295
|
* Uses multicall3 aggregate3 to batch all calls into a single RPC call
|
|
@@ -1361,6 +1644,21 @@ interface LenderDataEntry extends Omit<LenderSummary, 'subAccounts'> {
|
|
|
1361
1644
|
account: string;
|
|
1362
1645
|
lenderInfo?: LenderInfo;
|
|
1363
1646
|
data: UserDataForSubAccount[];
|
|
1647
|
+
/**
|
|
1648
|
+
* Set when some of this lender's on-chain reads could not be completed. The
|
|
1649
|
+
* positions listed are real but the set is a LOWER BOUND — anything derived
|
|
1650
|
+
* from the whole picture (NAV, net APR, health factor) is unreliable and must
|
|
1651
|
+
* not be rendered as fact.
|
|
1652
|
+
*/
|
|
1653
|
+
incomplete?: boolean;
|
|
1654
|
+
/**
|
|
1655
|
+
* Set when this entry was served from the last COMPLETE snapshot because the
|
|
1656
|
+
* live read failed. Internally consistent — unlike `incomplete` — but as of
|
|
1657
|
+
* `staleAgeMs` ago rather than now.
|
|
1658
|
+
*/
|
|
1659
|
+
stale?: boolean;
|
|
1660
|
+
/** Age of the served snapshot in ms. Only set alongside `stale`. */
|
|
1661
|
+
staleAgeMs?: number;
|
|
1364
1662
|
}
|
|
1365
1663
|
/**
|
|
1366
1664
|
* Input type for buildSummaries - user data result from convertLenderUserDataResult
|
|
@@ -1472,6 +1770,26 @@ interface MarketBook {
|
|
|
1472
1770
|
bids: PublicBookLevel[];
|
|
1473
1771
|
asks: PublicBookLevel[];
|
|
1474
1772
|
}
|
|
1773
|
+
/**
|
|
1774
|
+
* One entry in a fixed-term rate menu. Lives on `params.market.terms` for
|
|
1775
|
+
* single-borrowable-asset markets, and on `data[*].terms` for cross-margin
|
|
1776
|
+
* multi-asset lenders (Exactly), where each asset has its own fixed pools.
|
|
1777
|
+
*
|
|
1778
|
+
* `termId` semantics are LENDER-SPECIFIC — Exactly/TermMax = the unix maturity,
|
|
1779
|
+
* Teller = duration in seconds, Lista = the broker product id, Midnight/Term =
|
|
1780
|
+
* `0` placeholder. See FIXED_TERM_REPAY_TERMS.md.
|
|
1781
|
+
*/
|
|
1782
|
+
interface MarketTermEntry {
|
|
1783
|
+
termId: number;
|
|
1784
|
+
durationSecs: number;
|
|
1785
|
+
durationDays: number;
|
|
1786
|
+
/** annualised borrow rate in PERCENT (e.g. 3.857 = 3.857% APR) */
|
|
1787
|
+
apr: number;
|
|
1788
|
+
/** annualised fixed LEND rate in PERCENT at this maturity (Exactly only) */
|
|
1789
|
+
depositApr?: number;
|
|
1790
|
+
/** borrowable liquidity at this maturity, loan-token human units (Exactly only) */
|
|
1791
|
+
available?: number;
|
|
1792
|
+
}
|
|
1475
1793
|
interface MorphoMarket {
|
|
1476
1794
|
/** the 1delta lender enum */
|
|
1477
1795
|
lender: string;
|
|
@@ -1502,19 +1820,13 @@ interface MorphoMarket {
|
|
|
1502
1820
|
/** IRM rate floor */
|
|
1503
1821
|
rateFloor?: string;
|
|
1504
1822
|
/** Fixed-term rate menu — available term products (Lista brokered markets,
|
|
1505
|
-
* Term/Midnight single-maturity markets
|
|
1506
|
-
*
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
apr: number;
|
|
1513
|
-
/** annualised fixed LEND rate in PERCENT at this maturity (Exactly only) */
|
|
1514
|
-
depositApr?: number;
|
|
1515
|
-
/** borrowable liquidity at this maturity, loan-token human units (Exactly only) */
|
|
1516
|
-
available?: number;
|
|
1517
|
-
}[];
|
|
1823
|
+
* Term/Midnight single-maturity markets).
|
|
1824
|
+
*
|
|
1825
|
+
* MARKET-LEVEL menu, valid only when the lender key has ONE borrowable asset
|
|
1826
|
+
* (every isolated-market fixed-term lender). CROSS-MARGIN multi-asset
|
|
1827
|
+
* lenders — Exactly — carry a menu PER ASSET on `data[*].terms` instead,
|
|
1828
|
+
* since each asset has its own fixed pools. */
|
|
1829
|
+
terms?: MarketTermEntry[];
|
|
1518
1830
|
/**
|
|
1519
1831
|
* Canonical cross-protocol fixed-term descriptor (Lista brokered + Morpho
|
|
1520
1832
|
* Midnight). Present on fixed-rate/fixed-maturity markets only. See
|
|
@@ -1565,7 +1877,7 @@ interface MorphoGeneralPublicResponse {
|
|
|
1565
1877
|
* - `'zeroInterest'` — NO ongoing rate at all (River/Satoshi). The borrow
|
|
1566
1878
|
* cost is the one-off `originationFee`, not an APR.
|
|
1567
1879
|
*/
|
|
1568
|
-
rateModel?: 'variable' | 'userSet' | 'fixedTerm' | 'zeroInterest';
|
|
1880
|
+
rateModel?: 'variable' | 'userSet' | 'fixedTerm' | 'zeroInterest' | 'dbr' | 'protocolSet';
|
|
1569
1881
|
/**
|
|
1570
1882
|
* One-off fee charged ONCE at borrow time, as a PERCENT of the amount
|
|
1571
1883
|
* borrowed (e.g. `0.5` = 0.5%). Front-loaded cost that is NOT an APR and
|
|
@@ -1575,6 +1887,20 @@ interface MorphoGeneralPublicResponse {
|
|
|
1575
1887
|
* only for an effective-cost-since-open view.
|
|
1576
1888
|
*/
|
|
1577
1889
|
originationFee?: number;
|
|
1890
|
+
/**
|
|
1891
|
+
* PER-ASSET fixed-term rate menu, for CROSS-MARGIN multi-asset fixed-term
|
|
1892
|
+
* lenders (Exactly): one lender key covers every asset, and each asset has
|
|
1893
|
+
* its own fixed pools, so the menu cannot live on `params.market`.
|
|
1894
|
+
* Isolated-market fixed-term lenders (Midnight, Term, Lista broker,
|
|
1895
|
+
* TermMax, Teller) keep using `params.market.terms` — read that as the
|
|
1896
|
+
* fallback when this is absent.
|
|
1897
|
+
*/
|
|
1898
|
+
terms?: MarketTermEntry[];
|
|
1899
|
+
/**
|
|
1900
|
+
* PER-ASSET fixed-term descriptor, same rationale as `terms` above
|
|
1901
|
+
* (Exactly). Falls back to `params.market.fixedTerm` when absent.
|
|
1902
|
+
*/
|
|
1903
|
+
fixedTerm?: FixedTermInfo;
|
|
1578
1904
|
rewards?: RewardsList;
|
|
1579
1905
|
decimals: number;
|
|
1580
1906
|
config: {
|
|
@@ -2065,7 +2391,7 @@ interface MidnightBookSource {
|
|
|
2065
2391
|
|
|
2066
2392
|
/** Default hosted Midnight API (see @morpho-org/midnight-sdk MidnightApi). */
|
|
2067
2393
|
declare const DEFAULT_MIDNIGHT_API = "https://api.morpho.org/v0/midnight";
|
|
2068
|
-
type FetchLike$
|
|
2394
|
+
type FetchLike$2 = typeof fetch;
|
|
2069
2395
|
/**
|
|
2070
2396
|
* Hosted-API book source. Reads `GET {base}/books/{marketId}` and reduces the
|
|
2071
2397
|
* `asks`/`bids` price levels to a {@link MidnightBookTop}. This is the swappable
|
|
@@ -2074,7 +2400,7 @@ type FetchLike$1 = typeof fetch;
|
|
|
2074
2400
|
declare class ApiBookSource implements MidnightBookSource {
|
|
2075
2401
|
private readonly baseUrl;
|
|
2076
2402
|
private readonly fetchImpl;
|
|
2077
|
-
constructor(baseUrl: string, fetchImpl?: FetchLike$
|
|
2403
|
+
constructor(baseUrl: string, fetchImpl?: FetchLike$2);
|
|
2078
2404
|
getBookTop(marketId: string): Promise<MidnightBookTop | null>;
|
|
2079
2405
|
/**
|
|
2080
2406
|
* Full ladder for a market — every level per side, best-first (unlike
|
|
@@ -2105,7 +2431,7 @@ declare class ApiBookSource implements MidnightBookSource {
|
|
|
2105
2431
|
getOfferMakers(marketId: string, side: 'bids' | 'asks', assets: bigint): Promise<Record<string, string[]>>;
|
|
2106
2432
|
}
|
|
2107
2433
|
/** Build the default (hosted-API) book source for a chain. */
|
|
2108
|
-
declare function createMidnightBookSource(chainId: string, fetchImpl?: FetchLike$
|
|
2434
|
+
declare function createMidnightBookSource(chainId: string, fetchImpl?: FetchLike$2): MidnightBookSource;
|
|
2109
2435
|
|
|
2110
2436
|
/**
|
|
2111
2437
|
* Top-of-book snapshot for a single Term repo, already reduced to best
|
|
@@ -2222,14 +2548,48 @@ interface TermBookSource {
|
|
|
2222
2548
|
getTopAndBook?(config: TermMarketConfig, maxLevels?: number): Promise<{
|
|
2223
2549
|
top: TermBookTop;
|
|
2224
2550
|
book: TermBook;
|
|
2551
|
+
/** Live/upcoming auction round; null when none is listed. */
|
|
2552
|
+
auction: TermAuctionWindow | null;
|
|
2225
2553
|
} | null>;
|
|
2226
2554
|
}
|
|
2555
|
+
/**
|
|
2556
|
+
* The repo's CURRENT primary auction round, when one is listed.
|
|
2557
|
+
*
|
|
2558
|
+
* Term borrow origination is a periodic sealed-bid auction, not a continuous
|
|
2559
|
+
* book: outside the submission window there is nothing to bid on, so a repo
|
|
2560
|
+
* whose auction has cleared is lend-only (buy repo tokens on the secondary
|
|
2561
|
+
* book) until the next round is listed. Timestamps are raw so consumers can
|
|
2562
|
+
* derive a live countdown; `status` is a snapshot at fetch time.
|
|
2563
|
+
*/
|
|
2564
|
+
interface TermAuctionWindow {
|
|
2565
|
+
/** Auction round id (the TermAuction entity id). */
|
|
2566
|
+
id: string;
|
|
2567
|
+
/** Submissions open (unix seconds). */
|
|
2568
|
+
startTime: number;
|
|
2569
|
+
/** Submissions CLOSE and the sealed prices start revealing (unix seconds). */
|
|
2570
|
+
revealTime: number;
|
|
2571
|
+
/** Auction clears (unix seconds). Equal to `revealTime` on current deployments. */
|
|
2572
|
+
endTime: number;
|
|
2573
|
+
/** Minimum bid (borrow) size, loan-token base units (raw string; '0' when unset). */
|
|
2574
|
+
minBidAmount: string;
|
|
2575
|
+
/** Minimum offer (lend) size, loan-token base units (raw string; '0' when unset). */
|
|
2576
|
+
minOfferAmount: string;
|
|
2577
|
+
/** Highest accepted bid rate, WAD (raw string; '0' when unset). */
|
|
2578
|
+
maxBidPriceWad: string;
|
|
2579
|
+
/** Highest accepted offer rate, WAD (raw string; '0' when unset). */
|
|
2580
|
+
maxOfferPriceWad: string;
|
|
2581
|
+
}
|
|
2227
2582
|
/** A Term repo paired with its current top-of-book (null when the fetch failed). */
|
|
2228
2583
|
interface TermMarketRaw {
|
|
2229
2584
|
config: TermMarketConfig;
|
|
2230
2585
|
top: TermBookTop | null;
|
|
2231
2586
|
/** Bounded book slice (top-N levels/side); null/absent when unavailable. */
|
|
2232
2587
|
book?: TermBook | null;
|
|
2588
|
+
/**
|
|
2589
|
+
* The live/upcoming auction round, or null when no round is currently listed
|
|
2590
|
+
* (the common case between auctions — the repo is then lend-only).
|
|
2591
|
+
*/
|
|
2592
|
+
auction?: TermAuctionWindow | null;
|
|
2233
2593
|
}
|
|
2234
2594
|
|
|
2235
2595
|
/**
|
|
@@ -2261,7 +2621,7 @@ declare function convertTermMarketsToResponse(raw: TermMarketRaw[], chainId: str
|
|
|
2261
2621
|
[m: string]: MorphoGeneralPublicResponse;
|
|
2262
2622
|
};
|
|
2263
2623
|
|
|
2264
|
-
type FetchLike = typeof fetch;
|
|
2624
|
+
type FetchLike$1 = typeof fetch;
|
|
2265
2625
|
/**
|
|
2266
2626
|
* GraphQL subgraph source. `getBookTop` derives the fixed APR from the repo's
|
|
2267
2627
|
* latest completed auction clearing price and open-order depth; `getListings`
|
|
@@ -2270,20 +2630,29 @@ type FetchLike = typeof fetch;
|
|
|
2270
2630
|
declare class TermSubgraphSource implements TermBookSource {
|
|
2271
2631
|
private readonly url;
|
|
2272
2632
|
private readonly fetchImpl;
|
|
2273
|
-
constructor(url: string, fetchImpl?: FetchLike);
|
|
2633
|
+
constructor(url: string, fetchImpl?: FetchLike$1);
|
|
2274
2634
|
private gql;
|
|
2275
2635
|
getBookTop(config: TermMarketConfig): Promise<TermBookTop | null>;
|
|
2276
2636
|
/**
|
|
2277
2637
|
* ONE query → the aggregate top (best APR + FULL depth) PLUS a bounded book
|
|
2278
|
-
* slice (top `maxLevels` open orders per side)
|
|
2279
|
-
*
|
|
2280
|
-
*
|
|
2281
|
-
*
|
|
2282
|
-
* clearing APR; the levels
|
|
2638
|
+
* slice (top `maxLevels` open orders per side) PLUS the repo's current
|
|
2639
|
+
* auction round. `asks` = orders selling repo tokens (the secondary LEND
|
|
2640
|
+
* book); `bids` = the rest (borrow side, usually empty — Term borrow is
|
|
2641
|
+
* sealed-bid auction, not a continuous book). Term secondary orders carry no
|
|
2642
|
+
* per-order rate, so every level shares the market's clearing APR; the levels
|
|
2643
|
+
* expose per-order SIZE for filtering.
|
|
2644
|
+
*
|
|
2645
|
+
* Two auction reads, deliberately distinct:
|
|
2646
|
+
* - `cleared` — the latest COMPLETE round, whose clearing price IS the
|
|
2647
|
+
* market's fixed APR (and stays the reference rate between auctions).
|
|
2648
|
+
* - `pending` — rounds not yet complete/cancelled. Only one of these is a
|
|
2649
|
+
* real, actionable round; the rest are abandoned listings the subgraph
|
|
2650
|
+
* never marked complete, filtered out below.
|
|
2283
2651
|
*/
|
|
2284
2652
|
getTopAndBook(config: TermMarketConfig, maxLevels?: number): Promise<{
|
|
2285
2653
|
top: TermBookTop;
|
|
2286
2654
|
book: TermBook;
|
|
2655
|
+
auction: TermAuctionWindow | null;
|
|
2287
2656
|
} | null>;
|
|
2288
2657
|
getListings(config: TermMarketConfig): Promise<TermListing[] | null>;
|
|
2289
2658
|
/**
|
|
@@ -2296,7 +2665,7 @@ declare class TermSubgraphSource implements TermBookSource {
|
|
|
2296
2665
|
getAuctionOrders(config: TermMarketConfig, account: string): Promise<TermAuctionOrders | null>;
|
|
2297
2666
|
}
|
|
2298
2667
|
/** Default Term public-data source for a chain (subgraph via resolved URL). */
|
|
2299
|
-
declare function createTermBookSource(chainId: string, fetchImpl?: FetchLike): TermBookSource;
|
|
2668
|
+
declare function createTermBookSource(chainId: string, fetchImpl?: FetchLike$1): TermBookSource;
|
|
2300
2669
|
|
|
2301
2670
|
/**
|
|
2302
2671
|
* Decoded shapes of the Exactly `Previewer.exactly(account)` aggregate view.
|
|
@@ -2390,26 +2759,46 @@ interface ExactlyMarketsRaw {
|
|
|
2390
2759
|
*/
|
|
2391
2760
|
declare function fetchExactlyMarkets(chainId: string): Promise<ExactlyMarketsRaw>;
|
|
2392
2761
|
|
|
2393
|
-
/** Synthesized per-market lender key, e.g. `EXACTLY_<MARKET_ADDRESS_HEX_UPPER>`. */
|
|
2394
|
-
declare function exactlyLenderKey(market: string): string;
|
|
2395
|
-
/** Recover the Market address from an `EXACTLY_<HEX>` lender key (or undefined). */
|
|
2396
|
-
declare function exactlyMarketFromLenderKey(lender: string): string | undefined;
|
|
2397
2762
|
/**
|
|
2398
|
-
*
|
|
2399
|
-
* shape (identical to Midnight/Term), keyed by `EXACTLY_<MARKET_ADDRESS>` — one
|
|
2400
|
-
* key per asset Market (NOT per maturity; the maturity menu is the market's
|
|
2401
|
-
* `params.market.terms[]`, `termId` = maturity timestamp).
|
|
2763
|
+
* The ONE Exactly lender key per chain.
|
|
2402
2764
|
*
|
|
2403
|
-
*
|
|
2404
|
-
*
|
|
2405
|
-
*
|
|
2406
|
-
*
|
|
2407
|
-
*
|
|
2408
|
-
*
|
|
2409
|
-
*
|
|
2410
|
-
*
|
|
2411
|
-
*
|
|
2412
|
-
*
|
|
2765
|
+
* Exactly is a CROSS-MARGIN protocol: a single per-chain `Auditor` (a
|
|
2766
|
+
* Compound-V2-shaped comptroller, NOT a Euler controller) holds one
|
|
2767
|
+
* `enterMarket` bitmap per account, every entered deposit backs debt in ANY
|
|
2768
|
+
* market simultaneously, and health is one global check. The per-asset `Market`
|
|
2769
|
+
* contracts exist because each is the ERC-4626 share token for its asset and
|
|
2770
|
+
* carries that asset's rates / fixed pools — exactly like cUSDC and cETH under
|
|
2771
|
+
* one Comptroller. They are NOT isolated markets.
|
|
2772
|
+
*
|
|
2773
|
+
* So Exactly is modeled like Compound V2: ONE lender key, one entry per asset.
|
|
2774
|
+
* (It was previously split into synthesized `EXACTLY_<MARKET_ADDR>` keys — that
|
|
2775
|
+
* only ever existed because `terms[]` / `fixedTerm` lived on `params.market`,
|
|
2776
|
+
* which assumes one borrowable asset per key. Both now also exist per asset on
|
|
2777
|
+
* `data[*]`, so the split is gone along with the cross-margin collateral
|
|
2778
|
+
* mirroring, the double-count hazard and the optimistic per-key health it
|
|
2779
|
+
* forced. Resolve a Market contract from the ASSET via
|
|
2780
|
+
* `exactlyMarketByAsset(chainId, asset)` — or from the entry's `poolId`.)
|
|
2781
|
+
*/
|
|
2782
|
+
declare const EXACTLY_LENDER_KEY = "EXACTLY";
|
|
2783
|
+
/**
|
|
2784
|
+
* Map the on-chain Previewer batch into the shared `MorphoGeneralPublicResponse`
|
|
2785
|
+
* shape, under the SINGLE cross-margin {@link EXACTLY_LENDER_KEY} — one entry
|
|
2786
|
+
* per ASSET (the Compound V2 shape), never one key per Market.
|
|
2787
|
+
*
|
|
2788
|
+
* Per asset entry:
|
|
2789
|
+
* - FLOATING rates (`depositRate` / `variableBorrowRate`) plus the best live
|
|
2790
|
+
* fixed borrow APR on `stableBorrowRate`;
|
|
2791
|
+
* - its OWN `terms[]` maturity menu (`termId` = the pool's maturity) and its
|
|
2792
|
+
* OWN `fixedTerm` descriptor pointing at that asset's Market — per-asset
|
|
2793
|
+
* because each asset has its own fixed pools;
|
|
2794
|
+
* - risk as `collateralFactor = adjustFactor` + `borrowFactor = 1/adjustFactor`,
|
|
2795
|
+
* which is the Auditor's own formula (their product = the pairwise LTV);
|
|
2796
|
+
* - `poolId` / `exactly.market` = the Market contract (the write target).
|
|
2797
|
+
*
|
|
2798
|
+
* Every asset is simultaneously borrowable AND collateral for every other, so
|
|
2799
|
+
* there are no sibling-collateral rows. `params.market` carries only the
|
|
2800
|
+
* pool-wide descriptor (Auditor as `id`, a market-level `fixedTerm` without a
|
|
2801
|
+
* provider address). See the wrapper README for the repay mechanics.
|
|
2413
2802
|
*/
|
|
2414
2803
|
declare function convertExactlyMarketsToResponse(raw: ExactlyMarketsRaw, chainId: string, prices?: {
|
|
2415
2804
|
[asset: string]: number;
|
|
@@ -2435,7 +2824,15 @@ declare function exactlyPenaltyRateToAprPercent(penaltyRatePerSecond: bigint | u
|
|
|
2435
2824
|
*/
|
|
2436
2825
|
declare function exactlyPairLtv(collateralAdjustFactor: bigint | undefined, borrowAdjustFactor: bigint | undefined): number;
|
|
2437
2826
|
|
|
2438
|
-
/**
|
|
2827
|
+
/**
|
|
2828
|
+
* Per-position fixed-term detail attached to the position row (raw strings).
|
|
2829
|
+
*
|
|
2830
|
+
* Carries the FULL exit economics so a repay/withdraw UI needs no second read:
|
|
2831
|
+
* `faceValue` is what is owed/paid at maturity, `previewValue` is what the exit
|
|
2832
|
+
* actually costs/pays RIGHT NOW, and exactly one of `earlyRepayDiscount` /
|
|
2833
|
+
* `earlyExitCost` / `latePenalty` explains the gap. See the "repay terms"
|
|
2834
|
+
* section of the Exactly README for the source-verified formulas.
|
|
2835
|
+
*/
|
|
2439
2836
|
interface ExactlyUserFixedPosition {
|
|
2440
2837
|
/** unix maturity */
|
|
2441
2838
|
maturity: number;
|
|
@@ -2445,12 +2842,32 @@ interface ExactlyUserFixedPosition {
|
|
|
2445
2842
|
principal: string;
|
|
2446
2843
|
/** face fee locked at trade time (raw asset units) */
|
|
2447
2844
|
fee: string;
|
|
2845
|
+
/** face value at maturity = principal + fee. Static — Exactly fixed debt does
|
|
2846
|
+
* NOT accrue an index; it only grows via the late penalty below. */
|
|
2847
|
+
faceValue: string;
|
|
2448
2848
|
/** live exit value now: withdraw-now / repay-now incl. discount or overdue
|
|
2449
2849
|
* penalty (raw asset units) — from the Previewer */
|
|
2450
2850
|
previewValue: string;
|
|
2451
2851
|
/** true once maturity passed and the position is still open (borrows accrue
|
|
2452
2852
|
* the per-second late penalty until repaid) */
|
|
2453
2853
|
overdue: boolean;
|
|
2854
|
+
/** seconds past maturity (0 until overdue) */
|
|
2855
|
+
secondsLate: number;
|
|
2856
|
+
/** BORROW before maturity: face − repay-now, the REBATE for repaying early
|
|
2857
|
+
* (Exactly never charges an early-repay fee). Absent otherwise. */
|
|
2858
|
+
earlyRepayDiscount?: string;
|
|
2859
|
+
/** DEPOSIT before maturity: face − payout-now, the HAIRCUT for exiting a
|
|
2860
|
+
* fixed deposit early (sold back at the current curve rate). Absent
|
|
2861
|
+
* otherwise. */
|
|
2862
|
+
earlyExitCost?: string;
|
|
2863
|
+
/** BORROW past maturity: repay-now − face, penalty accrued SO FAR. Absent
|
|
2864
|
+
* otherwise. */
|
|
2865
|
+
latePenalty?: string;
|
|
2866
|
+
/** BORROW: penalty this position accrues per further day overdue (raw units,
|
|
2867
|
+
* linear on face — not compounding). Present for borrows only. */
|
|
2868
|
+
latePenaltyPerDay: string;
|
|
2869
|
+
/** market's linear late-penalty rate as an annualized percent (e.g. 164.24) */
|
|
2870
|
+
latePenaltyApr: number;
|
|
2454
2871
|
}
|
|
2455
2872
|
|
|
2456
2873
|
/**
|
|
@@ -2695,6 +3112,682 @@ interface RiverPositionInfo {
|
|
|
2695
3112
|
collateralSurplus: string;
|
|
2696
3113
|
}
|
|
2697
3114
|
|
|
3115
|
+
/**
|
|
3116
|
+
* Per-market snapshot of a FiRM market. Numbers are HUMAN units (the
|
|
3117
|
+
* Inverse API serves human numbers; the on-chain fallback normalizes to
|
|
3118
|
+
* match). `null` marks a value the active source could not provide —
|
|
3119
|
+
* the converter degrades gracefully per field.
|
|
3120
|
+
*/
|
|
3121
|
+
interface InverseMarketRaw {
|
|
3122
|
+
market: InverseMarketConfig;
|
|
3123
|
+
/** Market.totalDebt — DOLA units (human). */
|
|
3124
|
+
totalDebt: number | null;
|
|
3125
|
+
/** DOLA sitting in the Market = instant borrowable ceiling (human). */
|
|
3126
|
+
dolaLiquidity: number | null;
|
|
3127
|
+
/** min(dolaLiquidity, dailyLimit − dailyBorrows) — API only. */
|
|
3128
|
+
leftToBorrow: number | null;
|
|
3129
|
+
/** Collateral price in USD (pessimistic-oracle based). */
|
|
3130
|
+
price: number | null;
|
|
3131
|
+
/** Live borrowPaused (falls back to the metadata snapshot). */
|
|
3132
|
+
borrowPaused: boolean | null;
|
|
3133
|
+
/** Borrows already taken today against `dailyLimit` — API only. */
|
|
3134
|
+
dailyBorrows: number | null;
|
|
3135
|
+
/**
|
|
3136
|
+
* `Market.replenishmentIncentiveBps` (1000 = 10%) — the replenisher
|
|
3137
|
+
* bot's cut of a force-replenish, per market. It is carved OUT of the
|
|
3138
|
+
* `replenishmentPriceBps` cost and paid in DOLA from the market's own
|
|
3139
|
+
* liquidity; the borrower's debt grows by the FULL cost either way, so
|
|
3140
|
+
* this is a protocol/bot split, not an extra borrower charge.
|
|
3141
|
+
*/
|
|
3142
|
+
replenishmentIncentiveBps: number | null;
|
|
3143
|
+
}
|
|
3144
|
+
/** Raw public-data batch for the FiRM deployment (one chain). */
|
|
3145
|
+
interface InverseMarketsRaw {
|
|
3146
|
+
/** The bare lender key, `INVERSE`. */
|
|
3147
|
+
lender: string;
|
|
3148
|
+
config: InverseConfigChain | undefined;
|
|
3149
|
+
chainData: InverseChainData | undefined;
|
|
3150
|
+
/**
|
|
3151
|
+
* DBR price in DOLA — THE fixed borrow APR as a decimal (0.041 =
|
|
3152
|
+
* 4.1%). API-first, metadata snapshot as fallback, `null` if neither
|
|
3153
|
+
* resolves.
|
|
3154
|
+
*/
|
|
3155
|
+
dbrPriceDola: number | null;
|
|
3156
|
+
/** Force-replenish penalty APR in bps (54.75% = 5475) — static read. */
|
|
3157
|
+
replenishmentPriceBps: number | null;
|
|
3158
|
+
markets: InverseMarketRaw[];
|
|
3159
|
+
/** Which source filled the market rows. */
|
|
3160
|
+
source: 'api' | 'chain' | 'none';
|
|
3161
|
+
}
|
|
3162
|
+
|
|
3163
|
+
declare function fetchInverseMarkets(lender: string, chainId: string): Promise<InverseMarketsRaw>;
|
|
3164
|
+
|
|
3165
|
+
/**
|
|
3166
|
+
* Synthesized per-market lender key, e.g.
|
|
3167
|
+
* `INVERSE_63DF5E23DB45A2066508318F172BA45B9CD37035` (= the WETH
|
|
3168
|
+
* market). Address-suffixed (Teller/Exactly convention) — FiRM is
|
|
3169
|
+
* Ethereum-only so the chain id is not part of the key.
|
|
3170
|
+
*/
|
|
3171
|
+
declare function inverseLenderKey(lender: string, market: string): string;
|
|
3172
|
+
/** Recover `{ lender, market }` from a per-market key (or undefined). */
|
|
3173
|
+
declare function inverseKeyParts(key: string): {
|
|
3174
|
+
lender: string;
|
|
3175
|
+
market: string;
|
|
3176
|
+
} | undefined;
|
|
3177
|
+
/**
|
|
3178
|
+
* Map the FiRM batch into the shared `MorphoGeneralPublicResponse`
|
|
3179
|
+
* shape, keyed by `INVERSE_<MARKET_ADDR>` — one key per Market
|
|
3180
|
+
* (collateral).
|
|
3181
|
+
*
|
|
3182
|
+
* Per market:
|
|
3183
|
+
* - COLLATERAL entry: deposit-only; LTV = `collateralFactorBps`;
|
|
3184
|
+
* liquidation penalty = `liquidationIncentiveBps`; the close factor
|
|
3185
|
+
* is `liquidationFactorBps` (a liquidation may only close that
|
|
3186
|
+
* share of the position).
|
|
3187
|
+
* - LOAN entry (DOLA): `totalDebt` = market debt; the borrow rate is
|
|
3188
|
+
* the DBR price (FIXED APR — interest is prepaid in DBR, not
|
|
3189
|
+
* accrued on principal, `rateModel: 'dbr'`); `borrowLiquidity` =
|
|
3190
|
+
* `leftToBorrow` (API: min(dailyLimit headroom, DOLA in market)) or
|
|
3191
|
+
* `dolaLiquidity` in the on-chain fallback. There is NO supply side
|
|
3192
|
+
* — `totalDeposits` on the loan row is always 0 (DOLA is Fed-minted
|
|
3193
|
+
* into markets, not user-deposited).
|
|
3194
|
+
*
|
|
3195
|
+
* The full FiRM descriptor (minDebt, dailyLimit, escrow implementation,
|
|
3196
|
+
* DBR addresses, replenishment penalty) rides in
|
|
3197
|
+
* `params.market.inverse` for the calldata builders + worker-api
|
|
3198
|
+
* resolvers.
|
|
3199
|
+
*/
|
|
3200
|
+
declare function convertInverseMarketsToResponse(raw: InverseMarketsRaw, chainId: string, prices?: {
|
|
3201
|
+
[asset: string]: number;
|
|
3202
|
+
}, _additionalYields?: AdditionalYields, tokens?: GenericTokenList): {
|
|
3203
|
+
[m: string]: MorphoGeneralPublicResponse;
|
|
3204
|
+
};
|
|
3205
|
+
|
|
3206
|
+
/**
|
|
3207
|
+
* The immutable half of a Resupply pair. Cached across refreshes because none
|
|
3208
|
+
* of it can change: `collateral` and `underlying` are set in the pair's
|
|
3209
|
+
* constructor and there is no setter (`setConvexPool` only moves where the
|
|
3210
|
+
* SHARES are staked, never what the collateral token is).
|
|
3211
|
+
*/
|
|
3212
|
+
interface ResupplyPairIdentity {
|
|
3213
|
+
pair: string;
|
|
3214
|
+
/** e.g. `Resupply Pair (CurveLend: crvUSD/sfrxUSD) - 1`. */
|
|
3215
|
+
name: string;
|
|
3216
|
+
/** The ERC-4626 share of the WRAPPED market — the accounting unit. */
|
|
3217
|
+
collateral: string;
|
|
3218
|
+
/** What the user actually deposits and withdraws: crvUSD or frxUSD. */
|
|
3219
|
+
underlying: string;
|
|
3220
|
+
collateralDecimals: number;
|
|
3221
|
+
underlyingDecimals: number;
|
|
3222
|
+
}
|
|
3223
|
+
/**
|
|
3224
|
+
* One Resupply pair after the state batch. Raw bigints; `null` = failed
|
|
3225
|
+
* allowFailure read.
|
|
3226
|
+
*/
|
|
3227
|
+
interface ResupplyPairRaw {
|
|
3228
|
+
identity: ResupplyPairIdentity;
|
|
3229
|
+
/** 1e5-scaled (95000 = 95%). */
|
|
3230
|
+
maxLTV: bigint | null;
|
|
3231
|
+
/** Debt ceiling. ZERO ⇒ paused/retired — this is the liveness signal. */
|
|
3232
|
+
borrowLimit: bigint | null;
|
|
3233
|
+
/** 1e5-scaled penalty on top of the debt at liquidation. */
|
|
3234
|
+
liquidationFee: bigint | null;
|
|
3235
|
+
/** 1e5-scaled fee added to minted debt (0 on every live pair). */
|
|
3236
|
+
mintFee: bigint | null;
|
|
3237
|
+
/** Hard per-position floor (1,000 reUSD). */
|
|
3238
|
+
minimumBorrowAmount: bigint | null;
|
|
3239
|
+
/** Face reUSD debt, interest previewed. */
|
|
3240
|
+
totalBorrowAmount: bigint | null;
|
|
3241
|
+
totalBorrowShares: bigint | null;
|
|
3242
|
+
/** Collateral SHARES held by the pair. */
|
|
3243
|
+
totalCollateral: bigint | null;
|
|
3244
|
+
/** 1e18-scaled per SECOND, from `currentRateInfo` (last checkpoint). */
|
|
3245
|
+
ratePerSec: bigint | null;
|
|
3246
|
+
/** Same, but recomputed live by the Utilities lens — preferred. */
|
|
3247
|
+
liveRatePerSec: bigint | null;
|
|
3248
|
+
/** The WRAPPED market's supply rate per second, 1e18-scaled. */
|
|
3249
|
+
underlyingSupplyRatePerSec: bigint | null;
|
|
3250
|
+
/** `convertToAssets(1e18)` on the collateral vault — UNDERLYING per share.
|
|
3251
|
+
* ~1e15 for Curve Lend vaults, ~1e18 for Fraxlend pairs. */
|
|
3252
|
+
collateralPrice: bigint | null;
|
|
3253
|
+
/** Cached `1e36 / collateralPrice` from the pair (stale between writes). */
|
|
3254
|
+
exchangeRate: bigint | null;
|
|
3255
|
+
}
|
|
3256
|
+
interface ResupplyMarketsRaw {
|
|
3257
|
+
lender: string;
|
|
3258
|
+
config?: ResupplyConfigChain;
|
|
3259
|
+
pairs: ResupplyPairRaw[];
|
|
3260
|
+
}
|
|
3261
|
+
|
|
3262
|
+
/**
|
|
3263
|
+
* Fetch every Resupply market on a chain — FULLY ON-CHAIN, no API and no
|
|
3264
|
+
* published market roster.
|
|
3265
|
+
*
|
|
3266
|
+
* Discovery is `ResupplyRegistry.getAllPairAddresses()`. That is deliberate:
|
|
3267
|
+
* governance adds pairs (7 appeared between the docs' published list and
|
|
3268
|
+
* 2026-08) and retires them by zeroing `borrowLimit`, so a static file would
|
|
3269
|
+
* both miss new markets and advertise frozen ones as borrowable. The registry
|
|
3270
|
+
* is permissionless to read and is the same source the protocol's own
|
|
3271
|
+
* periphery uses.
|
|
3272
|
+
*
|
|
3273
|
+
* Three rounds cold, two warm:
|
|
3274
|
+
* 1. registry → pair addresses (intersected with `pairAllowlist` if set);
|
|
3275
|
+
* 2. identity (`name`/`collateral`/`underlying` + both tokens' decimals) for
|
|
3276
|
+
* pairs not already cached — immutable, so this is once per pair ever;
|
|
3277
|
+
* 3. state: terms, accounting, rates and the collateral vault's share price.
|
|
3278
|
+
*
|
|
3279
|
+
* The rate is read from the `Utilities` lens rather than `currentRateInfo`,
|
|
3280
|
+
* which is only a checkpoint from the last write — on a quiet pair that can be
|
|
3281
|
+
* hours stale, and the off-peg amplifier moves with the reUSD price. The
|
|
3282
|
+
* checkpoint is kept as a fallback.
|
|
3283
|
+
*/
|
|
3284
|
+
declare function fetchResupplyMarkets(lender: string, chainId: string): Promise<ResupplyMarketsRaw>;
|
|
3285
|
+
|
|
3286
|
+
/**
|
|
3287
|
+
* The external lending market a Resupply pair wraps.
|
|
3288
|
+
*
|
|
3289
|
+
* Every Resupply pair's collateral IS another lender's supply position, so a
|
|
3290
|
+
* position here carries that market's risk on top of Resupply's own. This
|
|
3291
|
+
* resolves the link where we can: the collateral vault is matched against the
|
|
3292
|
+
* LlamaLend roster by ADDRESS (LlamaLend indexes markets by Controller, so the
|
|
3293
|
+
* lookup goes through `llamaLendMarketByVault`).
|
|
3294
|
+
*
|
|
3295
|
+
* Verified 2026-08-04: 16 of the 21 registered pairs match a LlamaLend market
|
|
3296
|
+
* exactly, and the generation agrees independently — Resupply's own
|
|
3297
|
+
* `CurveLend:` names map to LlamaLend `version: 1` and `CurveLendV2:` to
|
|
3298
|
+
* `version: 2`. The other 5 are Fraxlend pairs, which we do not integrate as a
|
|
3299
|
+
* lender, so they resolve to `provider: 'fraxlend'` with no market key.
|
|
3300
|
+
*/
|
|
3301
|
+
interface ResupplyWrappedMarket {
|
|
3302
|
+
/** Which protocol the collateral position lives in. */
|
|
3303
|
+
provider: 'llamalend' | 'fraxlend' | 'unknown';
|
|
3304
|
+
/** The ERC-4626 the pair custodies — always known (it IS the collateral). */
|
|
3305
|
+
vault: string;
|
|
3306
|
+
/** `LLAMALEND_<CONTROLLER_ADDR>` when we integrate that market, else absent.
|
|
3307
|
+
* This is the key to look the wrapped market up in our own data. */
|
|
3308
|
+
lender?: string;
|
|
3309
|
+
/** LlamaLend Controller (the borrow surface of the wrapped market). */
|
|
3310
|
+
controller?: string;
|
|
3311
|
+
/** LlamaLend LLAMMA. Carried because a leverage route through Resupply must
|
|
3312
|
+
* never touch it — the wrapped Controller asserts its band state. */
|
|
3313
|
+
amm?: string;
|
|
3314
|
+
/** 1 = `oneway`, 2 = `oneway-v2`. Matches Resupply's CurveLend/CurveLendV2. */
|
|
3315
|
+
version?: 1 | 2;
|
|
3316
|
+
/** What the wrapped market lends against, e.g. `sfrxUSD`. */
|
|
3317
|
+
collateralSymbol?: string;
|
|
3318
|
+
}
|
|
3319
|
+
/**
|
|
3320
|
+
* Synthesized per-pair lender key, e.g. `RESUPPLY_1_C5184CCC…`. The chain id
|
|
3321
|
+
* rides in the key (Fluid/River/Frankencoin convention) even though Resupply
|
|
3322
|
+
* is Ethereum-only today.
|
|
3323
|
+
*/
|
|
3324
|
+
declare function resupplyLenderKey(lender: string, chainId: string | number, pair: string): string;
|
|
3325
|
+
/** Recover `{ lender, chainId, pair }` from a per-pair key. */
|
|
3326
|
+
declare function resupplyKeyParts(key: string): {
|
|
3327
|
+
lender: string;
|
|
3328
|
+
chainId: string;
|
|
3329
|
+
pair: string;
|
|
3330
|
+
} | undefined;
|
|
3331
|
+
/**
|
|
3332
|
+
* Map one Resupply deployment's on-chain batch into the shared
|
|
3333
|
+
* `MorphoGeneralPublicResponse` shape, keyed `RESUPPLY_<chainId>_<PAIR>`.
|
|
3334
|
+
*
|
|
3335
|
+
* Four modelling decisions worth knowing (all from RESUPPLY_PLAN.md):
|
|
3336
|
+
*
|
|
3337
|
+
* - **The collateral we publish is the UNDERLYING (crvUSD / frxUSD), not the
|
|
3338
|
+
* ERC-4626 share.** The share is an internal accounting unit that no token
|
|
3339
|
+
* list carries and no price feed covers, and both user-facing entry points
|
|
3340
|
+
* (`addCollateral` / `removeCollateral`) are denominated in the underlying.
|
|
3341
|
+
* Share amounts are converted with the vault's own
|
|
3342
|
+
* `convertToAssets(1e18)` — the exact number Resupply's oracle uses. NB
|
|
3343
|
+
* that price is ~**1e15** for Curve Lend vaults, so the share count is
|
|
3344
|
+
* ~1000x the underlying; a bare 1e18 divide is wrong by three orders of
|
|
3345
|
+
* magnitude.
|
|
3346
|
+
* - **The collateral carries the wrapped market's yield.** A Resupply deposit
|
|
3347
|
+
* is a Curve Lend / Fraxlend supply position, so `getUnderlyingSupplyRate`
|
|
3348
|
+
* is published as the collateral row's `intrinsicYield`. Without it the
|
|
3349
|
+
* position looks like it pays nothing, when in fact the whole product is
|
|
3350
|
+
* the spread between that and the ~half-of-it borrow rate.
|
|
3351
|
+
* - **`borrowLimit == 0` means PAUSED.** `pause()` zeroes it and there is no
|
|
3352
|
+
* `isPaused`; 9 of 21 pairs sat at zero at integration. Such a pair is
|
|
3353
|
+
* reported frozen and non-borrowable, but deposits/withdrawals stay open so
|
|
3354
|
+
* users can exit.
|
|
3355
|
+
* - **There is no supply side** — reUSD is minted — so `totalDeposits` on the
|
|
3356
|
+
* loan row is 0 and `depositRate` is 0. The earn leg is sreUSD, which
|
|
3357
|
+
* belongs to the savings provider.
|
|
3358
|
+
*/
|
|
3359
|
+
declare function convertResupplyMarketsToResponse(raw: ResupplyMarketsRaw, chainId: string, prices?: {
|
|
3360
|
+
[asset: string]: number;
|
|
3361
|
+
}, _additionalYields?: AdditionalYields, tokens?: GenericTokenList): {
|
|
3362
|
+
[m: string]: MorphoGeneralPublicResponse;
|
|
3363
|
+
};
|
|
3364
|
+
|
|
3365
|
+
/** Per-position Resupply detail attached to the debt row (raw strings). */
|
|
3366
|
+
interface ResupplyPositionInfo {
|
|
3367
|
+
/** Internal borrow shares. NOT an amount — see `debt` for reUSD units. */
|
|
3368
|
+
borrowShares: string;
|
|
3369
|
+
/** Collateral in VAULT SHARES (the pair's own accounting unit). */
|
|
3370
|
+
collateralShares: string;
|
|
3371
|
+
/** `convertToAssets(1e18)` on the collateral vault at read time — the
|
|
3372
|
+
* factor that turned those shares into the reported underlying. ~1e15 for
|
|
3373
|
+
* Curve Lend vaults. */
|
|
3374
|
+
collateralSharePrice: string;
|
|
3375
|
+
/** The pair contract — the market id and every write target. */
|
|
3376
|
+
pair: string;
|
|
3377
|
+
/** The collateral vault (Curve Lend / Fraxlend 4626 share token). */
|
|
3378
|
+
collateralVault: string;
|
|
3379
|
+
}
|
|
3380
|
+
|
|
3381
|
+
/** Test seam — drops the roster + discovery caches. */
|
|
3382
|
+
declare function __resetResupplyUserCaches(): void;
|
|
3383
|
+
|
|
3384
|
+
/** Off-chain `Market.predictEscrow(user)`. */
|
|
3385
|
+
declare function predictInverseEscrow(market: Address, escrowImplementation: Address, user: Address): Address;
|
|
3386
|
+
|
|
3387
|
+
/** Per-position FiRM detail attached to the debt row (raw strings). */
|
|
3388
|
+
interface InversePositionInfo {
|
|
3389
|
+
/** Pessimistic-oracle borrow ceiling for the CURRENT collateral (DOLA raw). */
|
|
3390
|
+
creditLimit: string;
|
|
3391
|
+
/** Max collateral withdrawable right now (collateral raw). */
|
|
3392
|
+
withdrawalLimit: string;
|
|
3393
|
+
/** DBR wallet balance (raw) — the prepaid-interest runway. */
|
|
3394
|
+
dbrBalance: string;
|
|
3395
|
+
/** DBR deficit (raw). > 0 ⇒ force-replenishable at 54.75% APR AND
|
|
3396
|
+
* withdrawals are FROZEN until the user buys DBR. */
|
|
3397
|
+
dbrDeficit: string;
|
|
3398
|
+
/** DBR signed balance (raw, may be negative). */
|
|
3399
|
+
dbrSignedBalance: string;
|
|
3400
|
+
/** `DBR.debts(user)` (raw DOLA) — debt across ALL FiRM markets, which is
|
|
3401
|
+
* what burns DBR at 1 per DOLA-year. Chain-wide, repeated on every row. */
|
|
3402
|
+
dbrTotalDebt: string;
|
|
3403
|
+
/** Seconds until `dbrBalance` is exhausted at that burn ('0' if no debt). */
|
|
3404
|
+
dbrRunwaySeconds: string;
|
|
3405
|
+
/** Unix seconds of the projected depletion — past that point anyone can
|
|
3406
|
+
* force-replenish the account, charging the replenished DBR to its DOLA
|
|
3407
|
+
* debt at `replenishmentPriceBps`, and withdrawals freeze. Absent when
|
|
3408
|
+
* there is no debt. */
|
|
3409
|
+
dbrDepletionTimestamp?: string;
|
|
3410
|
+
}
|
|
3411
|
+
|
|
3412
|
+
/**
|
|
3413
|
+
* Per-market snapshot of a LlamaLend market. Amounts are HUMAN units (the
|
|
3414
|
+
* Curve API serves human numbers; the on-chain fallback normalizes to match).
|
|
3415
|
+
* Rates are DECIMALS (0.0391 = 3.91% APR), nominal — never the compounded
|
|
3416
|
+
* `borrowApy` the API also carries.
|
|
3417
|
+
*
|
|
3418
|
+
* `null` marks a value the active source could not provide; the converter
|
|
3419
|
+
* degrades per field rather than dropping the market.
|
|
3420
|
+
*/
|
|
3421
|
+
interface LlamaLendMarketRaw {
|
|
3422
|
+
market: LlamaLendMarketConfig;
|
|
3423
|
+
/** Controller.total_debt — borrowed-token units (human). */
|
|
3424
|
+
totalDebt: number | null;
|
|
3425
|
+
/** Vault.totalAssets — borrowed-token units (human). */
|
|
3426
|
+
totalSupplied: number | null;
|
|
3427
|
+
/**
|
|
3428
|
+
* Borrowable right now. v2: `Controller.available_balance()`. v1:
|
|
3429
|
+
* `borrowedToken.balanceOf(controller)`. This is also the withdrawal
|
|
3430
|
+
* ceiling for lenders — the vault cannot pay out what is lent.
|
|
3431
|
+
*/
|
|
3432
|
+
availableToBorrow: number | null;
|
|
3433
|
+
/** Nominal borrow APR as a decimal. */
|
|
3434
|
+
borrowApr: number | null;
|
|
3435
|
+
/** Nominal lend APR as a decimal. */
|
|
3436
|
+
lendApr: number | null;
|
|
3437
|
+
/** Collateral price in borrowed-token terms, from the AMM's EMA oracle. */
|
|
3438
|
+
collateralPrice: number | null;
|
|
3439
|
+
/** USD price of the collateral, API only (the chain read has no USD leg). */
|
|
3440
|
+
collateralPriceUsd: number | null;
|
|
3441
|
+
/** USD price of the borrowed token, API only. */
|
|
3442
|
+
borrowedPriceUsd: number | null;
|
|
3443
|
+
/**
|
|
3444
|
+
* Effective collateral factor at the market's `defaultBands`, derived from
|
|
3445
|
+
* `max_borrowable(1 unit, N)` divided by the oracle price.
|
|
3446
|
+
*
|
|
3447
|
+
* There is NO market-constant LTV in LlamaLend — the number moves with the
|
|
3448
|
+
* band count. Measured on sreUSD/crvUSD: 0.991 at N=4 down to 0.886 at
|
|
3449
|
+
* N=50. Reporting the N=4 maximum would flatter every risk comparison
|
|
3450
|
+
* against Aave/Morpho, so the default N is what gets reported and the rest
|
|
3451
|
+
* of the curve travels alongside in `bandLtv`.
|
|
3452
|
+
*/
|
|
3453
|
+
collateralFactor: number | null;
|
|
3454
|
+
/** `{ [N]: collateralFactor }` — the trade-off curve for the UI and sizer. */
|
|
3455
|
+
bandLtv: {
|
|
3456
|
+
[bands: string]: number;
|
|
3457
|
+
} | null;
|
|
3458
|
+
/**
|
|
3459
|
+
* v2 borrow cap in borrowed-token units (human). `0` DISABLES borrowing —
|
|
3460
|
+
* a fresh v2 market looks live but is not. `null` on v1 (uncapped).
|
|
3461
|
+
*/
|
|
3462
|
+
borrowCap: number | null;
|
|
3463
|
+
/** Whether new borrows are possible at all right now. */
|
|
3464
|
+
borrowingEnabled: boolean;
|
|
3465
|
+
/** Vault.maxDeposit — `0` disables deposits (v2 `max_supply`). */
|
|
3466
|
+
maxDeposit: number | null;
|
|
3467
|
+
/** Number of open loans, for the liquidations surface. */
|
|
3468
|
+
nLoans: number | null;
|
|
3469
|
+
/**
|
|
3470
|
+
* Soft-liquidation state of the market as a whole: the AMM's active band.
|
|
3471
|
+
* Not a per-user value, but it tells the UI whether the market is currently
|
|
3472
|
+
* converting anyone's collateral.
|
|
3473
|
+
*/
|
|
3474
|
+
activeBand: number | null;
|
|
3475
|
+
}
|
|
3476
|
+
/** Raw public-data batch for one LlamaLend chain (both generations together). */
|
|
3477
|
+
interface LlamaLendMarketsRaw {
|
|
3478
|
+
/** The bare lender key, `LLAMALEND`. */
|
|
3479
|
+
lender: string;
|
|
3480
|
+
config: LlamaLendConfigChain | undefined;
|
|
3481
|
+
chainData: LlamaLendChainData | undefined;
|
|
3482
|
+
markets: LlamaLendMarketRaw[];
|
|
3483
|
+
/** Which source filled the market rows. */
|
|
3484
|
+
source: 'api' | 'chain' | 'none';
|
|
3485
|
+
}
|
|
3486
|
+
|
|
3487
|
+
declare function fetchLlamaLendMarkets(lender: string, chainId: string): Promise<LlamaLendMarketsRaw>;
|
|
3488
|
+
|
|
3489
|
+
/**
|
|
3490
|
+
* Synthesized per-market lender key, e.g.
|
|
3491
|
+
* `LLAMALEND_4F79FE450A2BAF833E8F50340BD230F5A3ECAFE9` (= the sreUSD/crvUSD
|
|
3492
|
+
* market). Keyed by the CONTROLLER, which is what every write and every user
|
|
3493
|
+
* read targets — the vault is a lookup off it. Address-suffixed
|
|
3494
|
+
* (Teller/Exactly/Inverse convention); the chain id is not part of the key
|
|
3495
|
+
* because chain scoping happens at the marketUid level.
|
|
3496
|
+
*
|
|
3497
|
+
* Both generations share this key space on purpose: to a user they are one
|
|
3498
|
+
* protocol, and the Curve API returns them in one list. The `version` field on
|
|
3499
|
+
* the market row is what encoders branch on.
|
|
3500
|
+
*/
|
|
3501
|
+
declare function llamaLendLenderKey(lender: string, controller: string): string;
|
|
3502
|
+
/** Recover `{ lender, controller }` from a per-market key (or undefined). */
|
|
3503
|
+
declare function llamaLendKeyParts(key: string): {
|
|
3504
|
+
lender: string;
|
|
3505
|
+
controller: string;
|
|
3506
|
+
} | undefined;
|
|
3507
|
+
/**
|
|
3508
|
+
* Map the LlamaLend batch into the shared `MorphoGeneralPublicResponse` shape,
|
|
3509
|
+
* keyed by `LLAMALEND_<CONTROLLER_ADDR>` — one key per market.
|
|
3510
|
+
*
|
|
3511
|
+
* Per market, two entries in the isolated-pair layout:
|
|
3512
|
+
*
|
|
3513
|
+
* - COLLATERAL entry — deposit-only. `collateralFactor` is the LTV AT THE
|
|
3514
|
+
* MARKET'S DEFAULT BAND COUNT, because LlamaLend has no market-constant
|
|
3515
|
+
* LTV: it is a function of `N` and moves 0.886..0.991 on a single market.
|
|
3516
|
+
* The whole curve rides along in `params.market.llamalend.bandLtv` so the
|
|
3517
|
+
* UI can show the trade-off and the leverage sizer can use the real number
|
|
3518
|
+
* for the `N` the user actually picks.
|
|
3519
|
+
* - LOAN entry — the borrowed token. Supply side is the ERC-4626 vault, so
|
|
3520
|
+
* unlike Inverse this one HAS `totalDeposits`.
|
|
3521
|
+
*
|
|
3522
|
+
* SOFT LIQUIDATION is the thing this shape cannot express natively, so it is
|
|
3523
|
+
* carried explicitly in the descriptor. `liquidationPenalty` here is the HARD
|
|
3524
|
+
* liquidation bonus only — it applies below the entire band range. Inside the
|
|
3525
|
+
* range a position is converted gradually through the market's own AMM with no
|
|
3526
|
+
* penalty at all, and a consumer that renders `liquidationPenalty` as "what
|
|
3527
|
+
* you lose when the price hits X" is describing the wrong event.
|
|
3528
|
+
*/
|
|
3529
|
+
declare function convertLlamaLendMarketsToResponse(raw: LlamaLendMarketsRaw, chainId: string, prices?: {
|
|
3530
|
+
[asset: string]: number;
|
|
3531
|
+
}, additionalYields?: AdditionalYields, tokens?: GenericTokenList): {
|
|
3532
|
+
[m: string]: MorphoGeneralPublicResponse;
|
|
3533
|
+
};
|
|
3534
|
+
|
|
3535
|
+
/**
|
|
3536
|
+
* Per-position LlamaLend detail attached to the debt row (raw strings unless
|
|
3537
|
+
* noted).
|
|
3538
|
+
*
|
|
3539
|
+
* The two fields a consumer must not ignore:
|
|
3540
|
+
*
|
|
3541
|
+
* - `softLiquidating` / `priceUpper` / `priceLower` — a LlamaLend position
|
|
3542
|
+
* does not have a liquidation price. It has a band RANGE, and it starts
|
|
3543
|
+
* converting collateral into the borrowed token as soon as the oracle
|
|
3544
|
+
* enters that range. Rendering a single number here misrepresents the
|
|
3545
|
+
* protocol.
|
|
3546
|
+
* - `bandCollateralInBorrowed` — the borrowed-token leg the LLAMMA has
|
|
3547
|
+
* already produced from the user's collateral. It sits inside the position,
|
|
3548
|
+
* offsets debt, and is NOT a wallet balance.
|
|
3549
|
+
*/
|
|
3550
|
+
interface LlamaLendPositionInfo {
|
|
3551
|
+
/** Signed health, WAD. `< 0` ⇒ hard-liquidatable. */
|
|
3552
|
+
health: string;
|
|
3553
|
+
/** Upper bound of the soft-liquidation band range, WAD. */
|
|
3554
|
+
priceUpper: string;
|
|
3555
|
+
/** Lower bound of the soft-liquidation band range, WAD. */
|
|
3556
|
+
priceLower: string;
|
|
3557
|
+
/** Band indices `[n1, n2]` the collateral currently occupies. */
|
|
3558
|
+
bands: [number, number] | undefined;
|
|
3559
|
+
/** Band count `N` chosen at loan creation — IMMUTABLE for the loan's life. */
|
|
3560
|
+
bandCount: number;
|
|
3561
|
+
/**
|
|
3562
|
+
* Borrowed-token amount held inside the user's bands (raw). Non-zero means
|
|
3563
|
+
* the position IS or HAS BEEN in soft liquidation.
|
|
3564
|
+
*/
|
|
3565
|
+
bandCollateralInBorrowed: string;
|
|
3566
|
+
/** True when the LLAMMA currently holds a borrowed-token leg for this user. */
|
|
3567
|
+
softLiquidating: boolean;
|
|
3568
|
+
/**
|
|
3569
|
+
* Whether the probed spender holds the Controller's boolean grant for this
|
|
3570
|
+
* market. FAILS CLOSED — older-blueprint controllers have no `approval`
|
|
3571
|
+
* method, the call fails, and this reads `false`, which is correct.
|
|
3572
|
+
*/
|
|
3573
|
+
delegated: boolean;
|
|
3574
|
+
/** Whether the market's Controller supports delegation at all. */
|
|
3575
|
+
supportsDelegation: boolean;
|
|
3576
|
+
/** Market generation — the leverage/encoder ABIs differ. */
|
|
3577
|
+
version: 1 | 2;
|
|
3578
|
+
}
|
|
3579
|
+
|
|
3580
|
+
/**
|
|
3581
|
+
* One USDD market (= one collateral ilk) after the on-chain batch.
|
|
3582
|
+
* Raw bigints; `null` = failed allowFailure read. Maker fixed-point:
|
|
3583
|
+
* wad 1e18 / ray 1e27 / rad 1e45.
|
|
3584
|
+
*/
|
|
3585
|
+
interface UsddMarketRaw {
|
|
3586
|
+
market: UsddMarketConfig;
|
|
3587
|
+
/** Vat.ilks — total normalised debt (wad). */
|
|
3588
|
+
Art: bigint | null;
|
|
3589
|
+
/** Vat.ilks — debt accumulator (ray); debt = Art × rate (rad). */
|
|
3590
|
+
rate: bigint | null;
|
|
3591
|
+
/** Vat.ilks — liquidation-adjusted price (ray): price / (par × mat). */
|
|
3592
|
+
spot: bigint | null;
|
|
3593
|
+
/** Vat.ilks — ilk debt ceiling (rad). */
|
|
3594
|
+
line: bigint | null;
|
|
3595
|
+
/** Vat.ilks — per-urn debt floor (rad). */
|
|
3596
|
+
dust: bigint | null;
|
|
3597
|
+
/** Jug.ilks — per-second stability fee (ray). */
|
|
3598
|
+
duty: bigint | null;
|
|
3599
|
+
/** Spot.ilks — liquidation ratio (ray). */
|
|
3600
|
+
mat: bigint | null;
|
|
3601
|
+
/** gem.balanceOf(gemJoin) — total collateral custodied by the adapter
|
|
3602
|
+
* (locked ink + unswept gem), gem-native decimals. */
|
|
3603
|
+
joinBalance: bigint | null;
|
|
3604
|
+
}
|
|
3605
|
+
interface UsddMarketsRaw {
|
|
3606
|
+
lender: string;
|
|
3607
|
+
config?: UsddConfigChain;
|
|
3608
|
+
chainData?: UsddChainData;
|
|
3609
|
+
markets: UsddMarketRaw[];
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
/** Ilk string → bytes32 (`'WBTC-A'` → right-padded hex). */
|
|
3613
|
+
declare const usddIlkBytes32: (ilk: string) => `0x${string}`;
|
|
3614
|
+
/**
|
|
3615
|
+
* Fetch all market data of ONE USDD (Maker-fork) deployment — FULLY ON-CHAIN
|
|
3616
|
+
* via one retrying multicall. The ilk roster comes from lender-metadata
|
|
3617
|
+
* (`usddConfig`/`usddMarkets`, discovered + verified by its `update:usdd`
|
|
3618
|
+
* generator); this fetch reads the LIVE Vat/Jug/Spot params per ilk plus the
|
|
3619
|
+
* gem-join balance (total custodied collateral — the Vat keeps no per-ilk
|
|
3620
|
+
* ink total).
|
|
3621
|
+
*
|
|
3622
|
+
* The roster is EMPTY on both EVM chains today (`cdpi() = 0`, no ilk filed —
|
|
3623
|
+
* see USDD_PLAN.md), so this returns zero markets without issuing a
|
|
3624
|
+
* multicall. The code path stays live so the day metadata fills, data flows
|
|
3625
|
+
* with no code change.
|
|
3626
|
+
*/
|
|
3627
|
+
declare function fetchUsddMarkets(lender: string, chainId: string): Promise<UsddMarketsRaw>;
|
|
3628
|
+
|
|
3629
|
+
/**
|
|
3630
|
+
* Synthesized per-ilk lender key, e.g. `USDD_1_WBTC-A`. The CHAIN ID is part
|
|
3631
|
+
* of the key (Fluid/River convention) because Ethereum and BNB run
|
|
3632
|
+
* INDEPENDENT Maker stacks that could file the same ilk string.
|
|
3633
|
+
*/
|
|
3634
|
+
declare function usddLenderKey(lender: string, chainId: string | number, ilk: string): string;
|
|
3635
|
+
/**
|
|
3636
|
+
* Recover `{ lender, chainId, ilk }` from a per-market key (or undefined).
|
|
3637
|
+
* Ilk strings are Maker `<GEM>-<CLASS>` tokens (`WBTC-A`, `PSM-USDT-A`) —
|
|
3638
|
+
* uppercase alphanumerics + dashes; the leading `\d+_` disambiguates from
|
|
3639
|
+
* the bare `USDD` key.
|
|
3640
|
+
*/
|
|
3641
|
+
declare function usddKeyParts(key: string): {
|
|
3642
|
+
lender: string;
|
|
3643
|
+
chainId: string;
|
|
3644
|
+
ilk: string;
|
|
3645
|
+
} | undefined;
|
|
3646
|
+
/**
|
|
3647
|
+
* Map one USDD deployment's on-chain batch into the shared
|
|
3648
|
+
* `MorphoGeneralPublicResponse` shape, keyed `USDD_<chainId>_<ILK>` — one key
|
|
3649
|
+
* per collateral ilk.
|
|
3650
|
+
*
|
|
3651
|
+
* Per market:
|
|
3652
|
+
* - COLLATERAL entry: totals = the gem-join balance (the Vat keeps no
|
|
3653
|
+
* per-ilk ink total; the adapter custodies locked + unswept gems);
|
|
3654
|
+
* LTV = 1/mat; liquidation penalty = chop − 1 (Dog.chop, wad).
|
|
3655
|
+
* - LOAN entry (USDD): `totalDebt` = Art × rate (rad → human);
|
|
3656
|
+
* `variableBorrowRate` = the stability fee as a nominal APR percent —
|
|
3657
|
+
* `(duty − RAY)/RAY × YEAR_SECONDS × 100`, the same annualisation as the
|
|
3658
|
+
* Pot's dsr in the savings fetcher (never `^ seconds − 1`, which is the
|
|
3659
|
+
* APY); `borrowLiquidity` = ceiling headroom `(line − Art × rate)/1e45`.
|
|
3660
|
+
* There is NO protocol supply side (USDD is Vat-minted) — the earn side
|
|
3661
|
+
* is sUSDD, carried by the savings provider, so `totalDeposits` on the
|
|
3662
|
+
* loan row is 0 and `depositRate` 0 here.
|
|
3663
|
+
* - Collateral price: Vat.spot × mat (both ray) recovers the par-adjusted
|
|
3664
|
+
* OSM price without reading the pip (whitelisted `peek` would revert);
|
|
3665
|
+
* shared price map as fallback.
|
|
3666
|
+
*/
|
|
3667
|
+
declare function convertUsddMarketsToResponse(raw: UsddMarketsRaw, chainId: string, prices?: {
|
|
3668
|
+
[asset: string]: number;
|
|
3669
|
+
}, _additionalYields?: AdditionalYields, tokens?: GenericTokenList): {
|
|
3670
|
+
[m: string]: MorphoGeneralPublicResponse;
|
|
3671
|
+
};
|
|
3672
|
+
|
|
3673
|
+
/** Per-CDP position detail attached to the debt row (raw strings). */
|
|
3674
|
+
interface UsddPositionInfo {
|
|
3675
|
+
/** DssCdpManager id — the sub-account id and every write op's target. */
|
|
3676
|
+
cdpId: string;
|
|
3677
|
+
/** Urn handle in the Vat. */
|
|
3678
|
+
urn: string;
|
|
3679
|
+
ilk: string;
|
|
3680
|
+
}
|
|
3681
|
+
|
|
3682
|
+
/**
|
|
3683
|
+
* One Frankencoin market (= one ORIGINAL position) after the on-chain batch.
|
|
3684
|
+
* Raw bigints; `null` = failed allowFailure read.
|
|
3685
|
+
*/
|
|
3686
|
+
interface FrankencoinMarketRaw {
|
|
3687
|
+
market: FrankencoinMarketConfig;
|
|
3688
|
+
/** Owner-declared liquidation price, 36-dec scaled vs collateral decimals. */
|
|
3689
|
+
price: bigint | null;
|
|
3690
|
+
/** FACE debt on the original itself (clones carry their own). */
|
|
3691
|
+
minted: bigint | null;
|
|
3692
|
+
/** ZCHF the original + its clones may still draw — market borrow capacity. */
|
|
3693
|
+
availableForClones: bigint | null;
|
|
3694
|
+
/** Collateral custodied by the ORIGINAL position contract. */
|
|
3695
|
+
collateralBalance: bigint | null;
|
|
3696
|
+
/** Hub lead rate + risk premium, ppm. */
|
|
3697
|
+
annualInterestPPM: bigint | null;
|
|
3698
|
+
/** Upfront fee for minting now (pro-rata to expiry), ppm. */
|
|
3699
|
+
currentFeePPM: bigint | null;
|
|
3700
|
+
/** Withheld into the equity reserve at mint, ppm. */
|
|
3701
|
+
reserveContribution: bigint | null;
|
|
3702
|
+
/** Non-zero while a Dutch-auction challenge is running. */
|
|
3703
|
+
challengedAmount: bigint | null;
|
|
3704
|
+
expiration: bigint | null;
|
|
3705
|
+
isClosed: boolean | null;
|
|
3706
|
+
}
|
|
3707
|
+
interface FrankencoinMarketsRaw {
|
|
3708
|
+
lender: string;
|
|
3709
|
+
config?: FrankencoinConfigChain;
|
|
3710
|
+
chainData?: FrankencoinChainData;
|
|
3711
|
+
markets: FrankencoinMarketRaw[];
|
|
3712
|
+
}
|
|
3713
|
+
|
|
3714
|
+
/**
|
|
3715
|
+
* Fetch all market data of ONE Frankencoin deployment — FULLY ON-CHAIN via
|
|
3716
|
+
* one retrying multicall over the curated ORIGINAL-position roster from
|
|
3717
|
+
* lender-metadata (`frankencoinConfig` / `frankencoinMarkets`, generated by
|
|
3718
|
+
* its `update:frankencoin` job, which filters to V2 + open + a priceable
|
|
3719
|
+
* collateral allowlist).
|
|
3720
|
+
*
|
|
3721
|
+
* Only originals are read here: they carry the terms AND the market-level
|
|
3722
|
+
* borrow capacity (`availableForClones`). Clones are USER positions and are
|
|
3723
|
+
* resolved per-account in the user-data path.
|
|
3724
|
+
*
|
|
3725
|
+
* NB `price` is the owner-DECLARED liquidation price, not an oracle quote —
|
|
3726
|
+
* see the converter for how that is surfaced.
|
|
3727
|
+
*/
|
|
3728
|
+
declare function fetchFrankencoinMarkets(lender: string, chainId: string): Promise<FrankencoinMarketsRaw>;
|
|
3729
|
+
|
|
3730
|
+
/**
|
|
3731
|
+
* Synthesized per-market lender key, e.g.
|
|
3732
|
+
* `FRANKENCOIN_1_5F2C10F7…` — one per ORIGINAL position. The chain id is part
|
|
3733
|
+
* of the key (Fluid/River convention) even though Frankencoin is
|
|
3734
|
+
* Ethereum-only today, so an L2 hub would not collide.
|
|
3735
|
+
*/
|
|
3736
|
+
declare function frankencoinLenderKey(lender: string, chainId: string | number, position: string): string;
|
|
3737
|
+
/** Recover `{ lender, chainId, position }` from a per-market key. */
|
|
3738
|
+
declare function frankencoinKeyParts(key: string): {
|
|
3739
|
+
lender: string;
|
|
3740
|
+
chainId: string;
|
|
3741
|
+
position: string;
|
|
3742
|
+
} | undefined;
|
|
3743
|
+
/**
|
|
3744
|
+
* Map one Frankencoin deployment's on-chain batch into the shared
|
|
3745
|
+
* `MorphoGeneralPublicResponse` shape, keyed `FRANKENCOIN_<chainId>_<ORIGINAL>`.
|
|
3746
|
+
*
|
|
3747
|
+
* Three modelling decisions worth knowing (all from FRANKENCOIN_PLAN.md):
|
|
3748
|
+
*
|
|
3749
|
+
* - **The liquidation price is owner-DECLARED, not an oracle.** `price` is
|
|
3750
|
+
* 36-dec scaled against the collateral's decimals and is policed by a
|
|
3751
|
+
* Dutch-auction challenge game. We surface it as the LIQUIDATION price
|
|
3752
|
+
* (it defines `collateralFactor = 1`, i.e. minting is allowed up to
|
|
3753
|
+
* `coll × price`) but value collateral with OUR price feeds. The
|
|
3754
|
+
* divergence between the two is the risk signal, and it is carried
|
|
3755
|
+
* verbatim in `params.market.frankencoin.declaredPrice` so a consumer can
|
|
3756
|
+
* compute it. Never present the resulting health as a protocol invariant.
|
|
3757
|
+
* - **Debt ≠ proceeds.** `minted` is FACE debt including the withheld
|
|
3758
|
+
* `reserveContribution` (10–40 %) plus the upfront pro-rata fee. Both
|
|
3759
|
+
* ppm figures ride along in the descriptor so a quote layer can convert.
|
|
3760
|
+
* - **Positions expire.** `expiration` is surfaced and an expired market is
|
|
3761
|
+
* reported halted (its collateral is subject to forced sale).
|
|
3762
|
+
*
|
|
3763
|
+
* Per market: a COLLATERAL entry (the original's own collateral) and a LOAN
|
|
3764
|
+
* entry (ZCHF). There is no protocol supply side — ZCHF is minted, and the
|
|
3765
|
+
* earn leg is the separate savings module carried by the savings provider —
|
|
3766
|
+
* so `totalDeposits` on the loan row is 0.
|
|
3767
|
+
*/
|
|
3768
|
+
declare function convertFrankencoinMarketsToResponse(raw: FrankencoinMarketsRaw, chainId: string, prices?: {
|
|
3769
|
+
[asset: string]: number;
|
|
3770
|
+
}, _additionalYields?: AdditionalYields, tokens?: GenericTokenList): {
|
|
3771
|
+
[m: string]: MorphoGeneralPublicResponse;
|
|
3772
|
+
};
|
|
3773
|
+
|
|
3774
|
+
/** Per-position detail attached to the debt row (raw strings). */
|
|
3775
|
+
interface FrankencoinPositionInfo {
|
|
3776
|
+
/** The position contract — the sub-account id and every write op's target. */
|
|
3777
|
+
position: string;
|
|
3778
|
+
/** The ORIGINAL this position was cloned from (its market). */
|
|
3779
|
+
original: string;
|
|
3780
|
+
/** Owner-declared liquidation price (raw, 36-dec scaled). */
|
|
3781
|
+
declaredPrice: string;
|
|
3782
|
+
/** Unix seconds; a position past this is subject to forced sale. */
|
|
3783
|
+
expiration: string;
|
|
3784
|
+
/** Non-zero while a Dutch-auction challenge is running against it. */
|
|
3785
|
+
challengedAmount: string;
|
|
3786
|
+
/** ppm withheld into the equity reserve — needed to quote a close, since
|
|
3787
|
+
* repaying burns with reserve credit and costs LESS than `minted`. */
|
|
3788
|
+
reserveContributionPPM: string;
|
|
3789
|
+
}
|
|
3790
|
+
|
|
2698
3791
|
/**
|
|
2699
3792
|
* Raw on-chain read for ONE Teller `LenderCommitmentGroup` pool. All amounts are
|
|
2700
3793
|
* raw token base units; `minRateBps` is the pool's min borrow APR in BASIS
|
|
@@ -2827,6 +3920,369 @@ interface TellerDiscovery {
|
|
|
2827
3920
|
declare const getCachedTellerBids: (chainId: string, account: string) => TellerDiscovery | undefined;
|
|
2828
3921
|
declare const buildTellerUserCall: (chainId: string, _lender: string, account: string) => Promise<Call[]>;
|
|
2829
3922
|
|
|
3923
|
+
/**
|
|
3924
|
+
* TermMax public-data types.
|
|
3925
|
+
*
|
|
3926
|
+
* ─── SIDE SEMANTICS, READ THIS FIRST ───────────────────────────────────────
|
|
3927
|
+
* TermMax names its two curves from the MAKER's perspective, and they cross
|
|
3928
|
+
* over relative to what a taker (our user) is doing:
|
|
3929
|
+
*
|
|
3930
|
+
* maker's `lendCurveCuts` → consumed by a taker who BORROWS
|
|
3931
|
+
* maker's `borrowCurveCuts` → consumed by a taker who LENDS
|
|
3932
|
+
*
|
|
3933
|
+
* `ITermMaxOrder.apr()` inherits that naming, so its `lendApr` is our user's
|
|
3934
|
+
* BORROW rate and its `borrowApr` is our user's LEND rate — and the API's
|
|
3935
|
+
* `priceInfo.term.lcft` / `bcft` follow the same convention.
|
|
3936
|
+
*
|
|
3937
|
+
* Every field in this file is named for the TAKER action. The crossing happens
|
|
3938
|
+
* exactly once, at the adapter boundary in `apiClient.ts` / `onchain.ts`, and
|
|
3939
|
+
* nowhere else. If a value ever produces lend > borrow on the same order, the
|
|
3940
|
+
* mapping has been applied twice.
|
|
3941
|
+
* ───────────────────────────────────────────────────────────────────────────
|
|
3942
|
+
*/
|
|
3943
|
+
/**
|
|
3944
|
+
* One segment of an order's piecewise pricing curve (TermMax `CurveCut`).
|
|
3945
|
+
*
|
|
3946
|
+
* `xtReserve` is the LEFT EDGE of the segment's validity interval, not a
|
|
3947
|
+
* quantity. Within a segment the swap solves a constant product over
|
|
3948
|
+
* `(xtReserve + offset, liqSquare_eff / (xtReserve + offset))` where
|
|
3949
|
+
* `liqSquare_eff = liqSquare · daysToMaturity · nif / (365 · 1e8)`.
|
|
3950
|
+
*
|
|
3951
|
+
* This is the TermMax analogue of a Midnight book LEVEL, but continuous rather
|
|
3952
|
+
* than discrete — which is why it cannot reuse `MidnightBookLevel`.
|
|
3953
|
+
*/
|
|
3954
|
+
interface TermMaxCurveSegment {
|
|
3955
|
+
xtReserve: bigint;
|
|
3956
|
+
liqSquare: bigint;
|
|
3957
|
+
/** Signed — shifts the virtual reserve. */
|
|
3958
|
+
offset: bigint;
|
|
3959
|
+
}
|
|
3960
|
+
/** Per-order fee ratios, 1e8-scaled (`0.02e8` = 2%). Fees apply to the INTEREST, not principal. */
|
|
3961
|
+
interface TermMaxFeeConfig {
|
|
3962
|
+
lendTakerFeeRatio: bigint;
|
|
3963
|
+
lendMakerFeeRatio: bigint;
|
|
3964
|
+
borrowTakerFeeRatio: bigint;
|
|
3965
|
+
borrowMakerFeeRatio: bigint;
|
|
3966
|
+
mintGtFeeRatio: bigint;
|
|
3967
|
+
mintGtFeeRef: bigint;
|
|
3968
|
+
}
|
|
3969
|
+
/**
|
|
3970
|
+
* Live state of one maker order, already mapped to taker-side semantics.
|
|
3971
|
+
*
|
|
3972
|
+
* An order can source liquidity beyond its own token balance (from an ERC-4626
|
|
3973
|
+
* `pool`, or by minting fresh FT against the maker's own `gtId`), so
|
|
3974
|
+
* balance-derived depth UNDERSTATES what is actually executable. Prefer the
|
|
3975
|
+
* capacity fields on {@link TermMaxBookTop}, or quote.
|
|
3976
|
+
*/
|
|
3977
|
+
interface TermMaxOrderState {
|
|
3978
|
+
orderAddress: string;
|
|
3979
|
+
marketAddress: string;
|
|
3980
|
+
/** The maker; `makerIsVault` when it is a curated ERC-4626 vault. */
|
|
3981
|
+
makerAddress?: string;
|
|
3982
|
+
makerIsVault?: boolean;
|
|
3983
|
+
/** THE pricing state in V2 (V1 used the real XT balance). */
|
|
3984
|
+
virtualXtReserve: bigint;
|
|
3985
|
+
ftReserve: bigint;
|
|
3986
|
+
xtReserve: bigint;
|
|
3987
|
+
maxXtReserve: bigint;
|
|
3988
|
+
/** Maker's own GT, used to mint FT on demand. 0 = none (it is a sentinel). */
|
|
3989
|
+
gtId: bigint;
|
|
3990
|
+
/** Curve a TAKER LENDS against (TermMax's `borrowCurveCuts`). */
|
|
3991
|
+
takerLendCuts: TermMaxCurveSegment[];
|
|
3992
|
+
/** Curve a TAKER BORROWS against (TermMax's `lendCurveCuts`). */
|
|
3993
|
+
takerBorrowCuts: TermMaxCurveSegment[];
|
|
3994
|
+
feeConfig?: TermMaxFeeConfig;
|
|
3995
|
+
/** Optional ERC-4626 base-yield sink; absent/zero when unset. */
|
|
3996
|
+
pool?: string;
|
|
3997
|
+
/** Executable size caps in USD, as reported upstream. */
|
|
3998
|
+
lendCapacityUsd: number;
|
|
3999
|
+
borrowCapacityUsd: number;
|
|
4000
|
+
/** Executable size caps in debt-token units (human, not raw). */
|
|
4001
|
+
lendCapacityAmount: number;
|
|
4002
|
+
borrowCapacityAmount: number;
|
|
4003
|
+
/** Fee-free mid APRs as fractions (0.055 = 5.5%), taker-side. Display only. */
|
|
4004
|
+
takerLendApr?: number;
|
|
4005
|
+
takerBorrowApr?: number;
|
|
4006
|
+
}
|
|
4007
|
+
/**
|
|
4008
|
+
* Best executable rate per side plus aggregate depth for one market — the
|
|
4009
|
+
* direct analogue of `MidnightBookTop`, collapsed across every order.
|
|
4010
|
+
*
|
|
4011
|
+
* "Best" is order-agnostic (we scan all orders): best LEND = highest taker-lend
|
|
4012
|
+
* APR, best BORROW = lowest taker-borrow APR.
|
|
4013
|
+
*/
|
|
4014
|
+
interface TermMaxBookTop {
|
|
4015
|
+
/** Highest taker LEND APR available, as a fraction. Undefined when no order quotes the side. */
|
|
4016
|
+
bestLendApr?: number;
|
|
4017
|
+
/** Lowest taker BORROW APR available, as a fraction. */
|
|
4018
|
+
bestBorrowApr?: number;
|
|
4019
|
+
/** Aggregate executable depth, USD. */
|
|
4020
|
+
lendDepthUsd: number;
|
|
4021
|
+
borrowDepthUsd: number;
|
|
4022
|
+
/** Aggregate executable depth in debt-token units (human). */
|
|
4023
|
+
lendDepthAmount: number;
|
|
4024
|
+
borrowDepthAmount: number;
|
|
4025
|
+
}
|
|
4026
|
+
/**
|
|
4027
|
+
* A TermMax market, i.e. one (debtToken, collateral, maturity) tuple.
|
|
4028
|
+
*
|
|
4029
|
+
* Discovered DYNAMICALLY — never read from a static registry. Matured markets
|
|
4030
|
+
* disappear from upstream entirely rather than lingering with a flag, and ~15%
|
|
4031
|
+
* of the book can roll on a single maturity date.
|
|
4032
|
+
*/
|
|
4033
|
+
interface TermMaxMarketConfig {
|
|
4034
|
+
/** Market contract — also the per-market lender-key body. */
|
|
4035
|
+
market: string;
|
|
4036
|
+
/** FT: the zero-coupon bond ERC-20. THE LEND POSITION. */
|
|
4037
|
+
ft: string;
|
|
4038
|
+
/** XT: the complement (`FT + XT = 1` debt token). */
|
|
4039
|
+
xt: string;
|
|
4040
|
+
/** GT: the ERC-721 loan. THE BORROW POSITION (sub-accounts). */
|
|
4041
|
+
gt: string;
|
|
4042
|
+
/** Debt / loan token. */
|
|
4043
|
+
debtToken: string;
|
|
4044
|
+
debtDecimals: number;
|
|
4045
|
+
collateral: string;
|
|
4046
|
+
collateralDecimals: number;
|
|
4047
|
+
/**
|
|
4048
|
+
* False when decimals could not be resolved from the payload and the 18-dec
|
|
4049
|
+
* default was used. Exists because reading the wrong `assetConfigs` field
|
|
4050
|
+
* names once made EVERY 6-dec stablecoin market silently 10^12 out; a
|
|
4051
|
+
* consumer that cares about exactness should treat `false` as suspect.
|
|
4052
|
+
*/
|
|
4053
|
+
debtDecimalsResolved?: boolean;
|
|
4054
|
+
collateralDecimalsResolved?: boolean;
|
|
4055
|
+
/** Display symbol, e.g. `USDC/PT-sUSDE-13AUG2026@16AUG2026`. */
|
|
4056
|
+
symbol?: string;
|
|
4057
|
+
/** Unix seconds. */
|
|
4058
|
+
maturity: number;
|
|
4059
|
+
/** 1e8-scaled, as strings (as upstream reports them). */
|
|
4060
|
+
maxLtv: string;
|
|
4061
|
+
liquidationLtv: string;
|
|
4062
|
+
/** false ⇒ NO liquidation at all, only post-maturity physical delivery. */
|
|
4063
|
+
liquidatable: boolean;
|
|
4064
|
+
/** Post-maturity liquidation window, seconds (7200 on every live market). */
|
|
4065
|
+
liquidationWindowSeconds?: number;
|
|
4066
|
+
/** Contract `getVersion()`: `v2` = "2.0.0", `v2_01` = "2.0.1". Both are V2. */
|
|
4067
|
+
version?: string;
|
|
4068
|
+
/** Market-level fee config (1e8-scaled). */
|
|
4069
|
+
feeConfig?: TermMaxFeeConfig;
|
|
4070
|
+
/** Oracle the protocol itself prices LTV/liquidation against. */
|
|
4071
|
+
oracle?: string;
|
|
4072
|
+
isMatured?: boolean;
|
|
4073
|
+
isEnabled?: boolean;
|
|
4074
|
+
}
|
|
4075
|
+
/** A market paired with its live book state. `top` is null when the fetch failed. */
|
|
4076
|
+
interface TermMaxMarketRaw {
|
|
4077
|
+
config: TermMaxMarketConfig;
|
|
4078
|
+
top: TermMaxBookTop | null;
|
|
4079
|
+
/** Per-order state, best-first by taker rate. Empty when the market has no live orders. */
|
|
4080
|
+
orders: TermMaxOrderState[];
|
|
4081
|
+
}
|
|
4082
|
+
/**
|
|
4083
|
+
* Pluggable TermMax data source — the hosted API today, a self-hosted indexer
|
|
4084
|
+
* or a pure on-chain reader later. Mirrors `MidnightBookSource`.
|
|
4085
|
+
*/
|
|
4086
|
+
interface TermMaxDataSource {
|
|
4087
|
+
/**
|
|
4088
|
+
* Every live market on a chain with its orders, or null when unavailable.
|
|
4089
|
+
* One upstream call per chain on the happy path.
|
|
4090
|
+
*/
|
|
4091
|
+
getChainMarkets(chainId: string): Promise<TermMaxMarketRaw[] | null>;
|
|
4092
|
+
}
|
|
4093
|
+
|
|
4094
|
+
/**
|
|
4095
|
+
* Resolve a TermMax market from the discovery cache, or undefined when it was
|
|
4096
|
+
* never fetched / has gone stale. Callers that need a guaranteed answer should
|
|
4097
|
+
* `await fetchTermMaxMarkets(chainId)` first (or read the market on-chain).
|
|
4098
|
+
*/
|
|
4099
|
+
declare function getCachedTermMaxMarket(chainId: string | number, market: string): TermMaxMarketConfig | undefined;
|
|
4100
|
+
/** All markets cached for a chain (may be empty before the first fetch). */
|
|
4101
|
+
declare function getCachedTermMaxMarkets(chainId: string | number): TermMaxMarketConfig[];
|
|
4102
|
+
/**
|
|
4103
|
+
* Fetch every live TermMax market on a chain, with its order book collapsed to
|
|
4104
|
+
* a best-rate + depth snapshot.
|
|
4105
|
+
*
|
|
4106
|
+
* Returns `[]` (not an error) when the chain has no TermMax deployment
|
|
4107
|
+
* configured, so callers can fan out across chains unconditionally.
|
|
4108
|
+
*
|
|
4109
|
+
* Markets are DISCOVERED, never read from a static list: matured markets vanish
|
|
4110
|
+
* from upstream entirely rather than lingering with a flag, and ~15% of the
|
|
4111
|
+
* book can roll on a single maturity date.
|
|
4112
|
+
*/
|
|
4113
|
+
declare function fetchTermMaxMarkets(chainId: string, source?: TermMaxDataSource): Promise<TermMaxMarketRaw[]>;
|
|
4114
|
+
|
|
4115
|
+
/**
|
|
4116
|
+
* Map fetched TermMax markets into the shared `MorphoGeneralPublicResponse`
|
|
4117
|
+
* shape, keyed by the synthesized `TERMMAX_<MARKET_ADDR>` lender key.
|
|
4118
|
+
*
|
|
4119
|
+
* Each TermMax market is ONE (debt, collateral, maturity) tuple, so it emits
|
|
4120
|
+
* exactly two entries — unlike Midnight, which has several collateral legs per
|
|
4121
|
+
* market:
|
|
4122
|
+
* - LOAN entry: `depositRate` = best taker LEND APR, `variableBorrowRate` =
|
|
4123
|
+
* best taker BORROW APR (already crossed from TermMax's maker-side names in
|
|
4124
|
+
* `apiClient.parseOrder`), plus order-book depth as the liquidity proxy.
|
|
4125
|
+
* - COLLATERAL entry: maxLtv → collateralFactor, liquidationLtv →
|
|
4126
|
+
* borrowCollateralFactor, fixed 10% liquidation penalty.
|
|
4127
|
+
*
|
|
4128
|
+
* Rates are DISPLAY values from the fee-free mid quote. Anything that actually
|
|
4129
|
+
* executes must price off a live quote at build time.
|
|
4130
|
+
*/
|
|
4131
|
+
declare function convertTermMaxMarketsToResponse(raw: TermMaxMarketRaw[], chainId: string, prices?: {
|
|
4132
|
+
[asset: string]: number;
|
|
4133
|
+
}, _additionalYields?: AdditionalYields, tokens?: GenericTokenList): {
|
|
4134
|
+
[m: string]: MorphoGeneralPublicResponse;
|
|
4135
|
+
};
|
|
4136
|
+
|
|
4137
|
+
/** Hosted TermMax data API (Swagger at `/api-docs`, spec at `/api-docs-json`). */
|
|
4138
|
+
declare const DEFAULT_TERMMAX_API = "https://api.termmax.ts.finance";
|
|
4139
|
+
type FetchLike = typeof fetch;
|
|
4140
|
+
/** Resolve a chain's TermMax API base (config override → hosted default). */
|
|
4141
|
+
declare function termMaxApiBase(chainId: string): string;
|
|
4142
|
+
/**
|
|
4143
|
+
* Hosted-API data source. ONE call per chain — `GET /market/data?chainId=`
|
|
4144
|
+
* returns global config, asset configs, markets, order configs and live order
|
|
4145
|
+
* state together (~240KB on Ethereum).
|
|
4146
|
+
*
|
|
4147
|
+
* This is the swappable seam: a self-hosted indexer or a pure on-chain reader
|
|
4148
|
+
* can implement {@link TermMaxDataSource} later without touching callers.
|
|
4149
|
+
*
|
|
4150
|
+
* Reliability posture: the endpoint is an undocumented app backend (no
|
|
4151
|
+
* versioning or rate-limit statement, `/admin/*` routes on the same host), so
|
|
4152
|
+
* treat it exactly like the Morpho GraphQL API — primary, but the caller keeps
|
|
4153
|
+
* a last-known-good snapshot (see fetchPublic.ts).
|
|
4154
|
+
*/
|
|
4155
|
+
declare class TermMaxApiSource implements TermMaxDataSource {
|
|
4156
|
+
private readonly baseUrl;
|
|
4157
|
+
private readonly fetchImpl;
|
|
4158
|
+
constructor(baseUrl: string, fetchImpl?: FetchLike);
|
|
4159
|
+
getChainMarkets(chainId: string): Promise<TermMaxMarketRaw[] | null>;
|
|
4160
|
+
}
|
|
4161
|
+
/** Default data source for a chain (hosted API, resolved per chain). */
|
|
4162
|
+
declare function createTermMaxDataSource(chainId: string, fetchImpl?: FetchLike): TermMaxDataSource;
|
|
4163
|
+
|
|
4164
|
+
/** TermMax ratio base: `0.01e8` = 1%. */
|
|
4165
|
+
declare const DECIMAL_BASE = 100000000n;
|
|
4166
|
+
/**
|
|
4167
|
+
* Days to maturity, **ceilinged to whole days** — exactly as
|
|
4168
|
+
* `TermMaxOrderV2._daysToMaturity` does it:
|
|
4169
|
+
*
|
|
4170
|
+
* `(maturity - now + SECONDS_IN_DAY - 1) / SECONDS_IN_DAY`
|
|
4171
|
+
*
|
|
4172
|
+
* Reproduce the ceiling or an off-chain quote drifts by up to a full day of
|
|
4173
|
+
* interest against the contract. Returns 0 at/after maturity.
|
|
4174
|
+
*/
|
|
4175
|
+
declare function daysToMaturity(maturity: number, nowSec: number): bigint;
|
|
4176
|
+
/**
|
|
4177
|
+
* Marginal (fee-free) APR implied by a curve at a given reserve, 1e8-scaled —
|
|
4178
|
+
* the same formula `TermMaxOrderV2.apr()` uses:
|
|
4179
|
+
*
|
|
4180
|
+
* `apr = vFt · 1e8 · 365 / (vXt · daysToMaturity)`
|
|
4181
|
+
*
|
|
4182
|
+
* Returns 0n for an empty curve or a matured market. This is a MID PRICE at the
|
|
4183
|
+
* current reserve: it ignores both the taker fee and trade size, so it is for
|
|
4184
|
+
* display and sorting only — never quote against it.
|
|
4185
|
+
*/
|
|
4186
|
+
declare function curveApr(cuts: TermMaxCurveSegment[], virtualXtReserve: bigint, days: bigint): bigint;
|
|
4187
|
+
/** 1e8-scaled ratio → plain fraction (`5_500_000n` → `0.055`). */
|
|
4188
|
+
declare function ratioToNumber(v: bigint): number;
|
|
4189
|
+
/** 1e8-scaled ratio → percent (`5_500_000n` → `5.5`). */
|
|
4190
|
+
declare function ratioToPercent(v: bigint): number;
|
|
4191
|
+
/**
|
|
4192
|
+
* Marginal APR as a fraction for one side, taker-side by construction.
|
|
4193
|
+
*
|
|
4194
|
+
* Pass `takerLendCuts` for the lend rate and `takerBorrowCuts` for the borrow
|
|
4195
|
+
* rate — the maker/taker crossing has already been applied when those fields
|
|
4196
|
+
* were built (see types.ts).
|
|
4197
|
+
*/
|
|
4198
|
+
declare function curveAprNumber(cuts: TermMaxCurveSegment[], virtualXtReserve: bigint, days: bigint): number;
|
|
4199
|
+
/**
|
|
4200
|
+
* Net-interest factor for a taker LEND (`1e8 - lendTakerFeeRatio`).
|
|
4201
|
+
* Guarded so a malformed fee config cannot produce a non-positive factor.
|
|
4202
|
+
*/
|
|
4203
|
+
declare function lendNif(fee?: TermMaxFeeConfig): bigint;
|
|
4204
|
+
/** Net-interest factor for a taker BORROW (`1e8 + borrowTakerFeeRatio`). */
|
|
4205
|
+
declare function borrowNif(fee?: TermMaxFeeConfig): bigint;
|
|
4206
|
+
/**
|
|
4207
|
+
* The GT-mint fee ratio at a given time to maturity, 1e8-scaled — mirrors
|
|
4208
|
+
* `TermMaxMarketV2.mintGtFeeRatio()`:
|
|
4209
|
+
*
|
|
4210
|
+
* `days · feeRatio · feeRef / (365·1e8 + feeRef·days)`
|
|
4211
|
+
*
|
|
4212
|
+
* This is a genuine percentage OF PRINCIPAL charged once at borrow time
|
|
4213
|
+
* (`issueFee = debt · ratio / 1e8`), so it maps onto the cross-protocol
|
|
4214
|
+
* `FixedTermInfo.fees.originationFeePercent`. It is NOT the raw
|
|
4215
|
+
* `feeConfig.mintGtFeeRatio` — prefer reading the contract when you can.
|
|
4216
|
+
*/
|
|
4217
|
+
declare function mintGtFeeRatio(fee: TermMaxFeeConfig | undefined, days: bigint): bigint;
|
|
4218
|
+
/**
|
|
4219
|
+
* TermMax liquidation penalty as a fraction of the repaid debt.
|
|
4220
|
+
*
|
|
4221
|
+
* Protocol constants, not per-market: the liquidator is paid 5% and the
|
|
4222
|
+
* protocol reserve takes 5%, so the borrower loses 10% of the liquidated debt
|
|
4223
|
+
* value. (Loans over $10k can only be liquidated 50% at a time; that is a size
|
|
4224
|
+
* cap, not a penalty, and is modeled as `closeFactor`.)
|
|
4225
|
+
*/
|
|
4226
|
+
declare const TERMMAX_LIQUIDATION_PENALTY = 0.1;
|
|
4227
|
+
declare const TERMMAX_LIQUIDATOR_BONUS = 0.05;
|
|
4228
|
+
/** Partial-liquidation threshold, USD: above it a single call can take at most 50%. */
|
|
4229
|
+
declare const TERMMAX_PARTIAL_LIQUIDATION_THRESHOLD_USD = 10000;
|
|
4230
|
+
declare const TERMMAX_PARTIAL_CLOSE_FACTOR = 0.5;
|
|
4231
|
+
/** Post-maturity liquidation window before physical delivery (`Constants.LIQUIDATION_WINDOW`). */
|
|
4232
|
+
declare const TERMMAX_LIQUIDATION_WINDOW_SECS = 7200;
|
|
4233
|
+
/**
|
|
4234
|
+
* Parse a 1e8-scaled LTV string into a plain fraction (`87500000` → `0.875`).
|
|
4235
|
+
* Returns 0 for missing/garbage input rather than NaN, so a bad config row
|
|
4236
|
+
* degrades to "no collateral value" instead of poisoning the optimizer.
|
|
4237
|
+
*/
|
|
4238
|
+
declare function parseTermMaxLtv(v: string | number | bigint | undefined): number;
|
|
4239
|
+
|
|
4240
|
+
/**
|
|
4241
|
+
* TermMax user data is a DISCOVERY-first, async build — the Teller/Liquity
|
|
4242
|
+
* shape, not the Midnight one.
|
|
4243
|
+
*
|
|
4244
|
+
* Midnight can build synchronously because its positions are keyed
|
|
4245
|
+
* `(marketId, user)`: one fixed slot per user per market, with the market list
|
|
4246
|
+
* coming from a static registry. TermMax has neither property:
|
|
4247
|
+
*
|
|
4248
|
+
* 1. the market list is DYNAMIC (markets churn on every maturity roll and
|
|
4249
|
+
* matured ones vanish upstream), so it must be resolved at call time; and
|
|
4250
|
+
* 2. borrow positions are GT ERC-721 sub-accounts — a user can hold N per
|
|
4251
|
+
* market and the ids are not derivable.
|
|
4252
|
+
*
|
|
4253
|
+
* But it is cheaper than Teller: `TermMaxViewer.getPositionDetails(markets[],
|
|
4254
|
+
* owner)` returns FT/XT/collateral balances AND every GT with its debt and
|
|
4255
|
+
* collateral in ONE call, so discovery and data collapse into a single
|
|
4256
|
+
* multicall entry — no two-phase discovery round-trip. The viewer `try`-guards
|
|
4257
|
+
* each position internally, so one bad loan cannot poison the batch.
|
|
4258
|
+
*
|
|
4259
|
+
* Health is NOT read per GT (`getLiquidationInfo`): it is computed downstream
|
|
4260
|
+
* from LTVs + oracle prices in `createMultiAccountTypeUserState`, exactly as
|
|
4261
|
+
* Midnight does. That keeps this to one call.
|
|
4262
|
+
*/
|
|
4263
|
+
/** Every TermMax market read consumes exactly one viewer call (batched). */
|
|
4264
|
+
declare const TERMMAX_CALLS_PER_ACCOUNT = 1;
|
|
4265
|
+
interface TermMaxDiscovery {
|
|
4266
|
+
/** Markets passed to the viewer, IN ORDER — the parser slices results by index. */
|
|
4267
|
+
markets: TermMaxMarketConfig[];
|
|
4268
|
+
at: number;
|
|
4269
|
+
}
|
|
4270
|
+
/**
|
|
4271
|
+
* The market layout used for the last build on this (chain, account).
|
|
4272
|
+
*
|
|
4273
|
+
* The parser runs as a separate phase, fed only the multicall results, so it
|
|
4274
|
+
* needs the layout the builder chose — the same trick the Teller / Liquity /
|
|
4275
|
+
* Lista-broker caches use.
|
|
4276
|
+
*/
|
|
4277
|
+
declare const getCachedTermMaxDiscovery: (chainId: string, account: string) => TermMaxDiscovery | undefined;
|
|
4278
|
+
/**
|
|
4279
|
+
* Build the user-data call for every live TermMax market on a chain.
|
|
4280
|
+
*
|
|
4281
|
+
* Returns `[]` when the chain has no TermMax deployment, no viewer configured,
|
|
4282
|
+
* or no live markets — callers can fan out unconditionally.
|
|
4283
|
+
*/
|
|
4284
|
+
declare const buildTermMaxUserCall: (chainId: string, _lender: string, account: string) => Promise<Call[]>;
|
|
4285
|
+
|
|
2830
4286
|
type PendleAssetTypes = 'PT' | 'YT' | 'SY' | 'PENDLE_LP';
|
|
2831
4287
|
/**
|
|
2832
4288
|
* Main function to fetch Pendle prices
|
|
@@ -4011,49 +5467,164 @@ interface EulerEarnVault extends VaultClassificationFields {
|
|
|
4011
5467
|
/** Extra rewards APR in percent, on top of `supplyRate`.
|
|
4012
5468
|
* Goldsky doesn't surface rewards directly — defaults to 0. */
|
|
4013
5469
|
rewardsRate: number;
|
|
4014
|
-
/** Sum of `supplyRate + rewardsRate` — what a depositor actually earns. */
|
|
5470
|
+
/** Sum of `supplyRate + rewardsRate` — what a depositor actually earns. */
|
|
5471
|
+
depositRate: number;
|
|
5472
|
+
/** Performance fee in percent (e.g. `5.0` = 5 %). */
|
|
5473
|
+
fee: number;
|
|
5474
|
+
/** Governance timelock in seconds — the delay before owner/curator config
|
|
5475
|
+
* changes take effect (Euler Earn `governance.timelock`). Governance-only,
|
|
5476
|
+
* NOT a per-deposit withdrawal lock. Mirrors `MorphoVault.timelock`. */
|
|
5477
|
+
timelock?: number;
|
|
5478
|
+
/** Owner address, lowercased — may be absent if not set. */
|
|
5479
|
+
owner?: string;
|
|
5480
|
+
/** Curator / fee-recipient address, lowercased. */
|
|
5481
|
+
curator?: string;
|
|
5482
|
+
/** Human-readable curator label for UI, derived best-effort from the
|
|
5483
|
+
* (curator-branded) vault name — the subgraph exposes only the address.
|
|
5484
|
+
* Mirrors `MorphoVault.curatorName` so consumers can write generic UI. */
|
|
5485
|
+
curatorName?: string;
|
|
5486
|
+
/** Guardian address, lowercased — may be absent. */
|
|
5487
|
+
guardian?: string;
|
|
5488
|
+
/** Fee recipient, lowercased — may be absent. */
|
|
5489
|
+
feeRecipient?: string;
|
|
5490
|
+
/** Hydrated asset metadata from the provided token list, if any. */
|
|
5491
|
+
asset?: GenericCurrency;
|
|
5492
|
+
/** USD price of one underlying unit, if prices were supplied. */
|
|
5493
|
+
priceUsd?: number;
|
|
5494
|
+
/** Human-formatted total assets (`totalAssets / 10^assetDecimals`). */
|
|
5495
|
+
totalAssetsFormatted: number;
|
|
5496
|
+
/** Human-formatted total assets in USD. */
|
|
5497
|
+
totalAssetsUsd: number;
|
|
5498
|
+
/** Currently withdrawable underlying, raw integer as string. Computed
|
|
5499
|
+
* as `idle_cash + Σ min(allocated_i, evk_cash_i)` across the Earn
|
|
5500
|
+
* vault's strategies — accounts for per-EVK utilization caps. Falls
|
|
5501
|
+
* back to `totalAssets` (optimistic ceiling) when the EVK index is
|
|
5502
|
+
* unavailable. Never exceeds `totalAssets`. */
|
|
5503
|
+
liquidity: string;
|
|
5504
|
+
/** Human-formatted immediate withdrawable liquidity. */
|
|
5505
|
+
liquidityFormatted: number;
|
|
5506
|
+
/** Human-formatted immediate withdrawable liquidity in USD. */
|
|
5507
|
+
liquidityUsd: number;
|
|
5508
|
+
}
|
|
5509
|
+
/** Full parsed payload: per-vault-address map. */
|
|
5510
|
+
type EulerEarnVaults = {
|
|
5511
|
+
/** Keyed by lowercased vault address. */
|
|
5512
|
+
[vaultAddress: string]: EulerEarnVault;
|
|
5513
|
+
};
|
|
5514
|
+
|
|
5515
|
+
/**
|
|
5516
|
+
* Parsed TermMax curated vault entry.
|
|
5517
|
+
*
|
|
5518
|
+
* TermMax vaults are the CONTINUOUS earn side of a fixed-maturity protocol.
|
|
5519
|
+
* The per-market lend position (buying FT) expires; a vault holds a rolling
|
|
5520
|
+
* book of orders across many maturities and rolls them for the depositor, so
|
|
5521
|
+
* from the outside it behaves like an ordinary ERC-4626 yield vault. Same
|
|
5522
|
+
* relationship Fluid has between its per-vault borrow markets and its fTokens.
|
|
5523
|
+
*
|
|
5524
|
+
* Multiple vaults exist per underlying with different curators (Keyrock, MEV
|
|
5525
|
+
* Capital, Origami and TermMax itself all curate), so the map is keyed by
|
|
5526
|
+
* VAULT ADDRESS —
|
|
5527
|
+
* the `MorphoVaults` / `SiloVaults` / `EulerEarnVaults` convention, not the
|
|
5528
|
+
* key-by-underlying convention Fluid and Gearbox use.
|
|
5529
|
+
*
|
|
5530
|
+
* IMPORTANT — vault assets and lender-side depth are the SAME capital. A
|
|
5531
|
+
* vault's deposits are what appear as lend-side order depth in the TermMax
|
|
5532
|
+
* lender data, so summing "TermMax lender TVL + TermMax vault TVL" double
|
|
5533
|
+
* counts.
|
|
5534
|
+
*/
|
|
5535
|
+
interface TermMaxVault extends VaultClassificationFields {
|
|
5536
|
+
/** Vault (share-token) contract address, lowercased. */
|
|
5537
|
+
address: string;
|
|
5538
|
+
/** Lowercased underlying ERC-20 address (the markets' debt token). */
|
|
5539
|
+
underlying: string;
|
|
5540
|
+
/** Vault share-token symbol, e.g. `TMKR-RLUSD`. */
|
|
5541
|
+
symbol: string;
|
|
5542
|
+
/** Vault share-token name, e.g. `Coinshift rlUSD vault`. */
|
|
5543
|
+
name: string;
|
|
5544
|
+
/** Share-token decimals. */
|
|
5545
|
+
decimals: number;
|
|
5546
|
+
/** Underlying asset decimals — read on-chain, may differ from `decimals`. */
|
|
5547
|
+
assetDecimals: number;
|
|
5548
|
+
/** Total underlying assets held, raw integer as string. */
|
|
5549
|
+
totalAssets: string;
|
|
5550
|
+
/** Total shares minted, raw integer as string. */
|
|
5551
|
+
totalSupply: string;
|
|
5552
|
+
/** Raw `convertToAssets(10**shareDecimals)` — 1 share → X underlying. */
|
|
5553
|
+
convertToAssets: string;
|
|
5554
|
+
/**
|
|
5555
|
+
* Supply APR in percent, **net of the performance fee**.
|
|
5556
|
+
*
|
|
5557
|
+
* TermMax's own formula (`OrderManagerV2`):
|
|
5558
|
+
* `annualizedInterest · (1e8 − performanceFeeRate) / accretingPrincipal`
|
|
5559
|
+
* `annualizedInterest` is GROSS — the fee is taken out of it on accrual
|
|
5560
|
+
* (`_accretingPrincipal += interest − performanceFeeToCurator`), so it must
|
|
5561
|
+
* be netted here to match the README's "net of performance fee" convention.
|
|
5562
|
+
*
|
|
5563
|
+
* Served directly by the API. The on-chain fallback derives it, because
|
|
5564
|
+
* `apr()` AND `accretingPrincipal()` both revert on the deployed 2.0.0
|
|
5565
|
+
* vaults — it substitutes `totalAssets` as the denominator, which is larger
|
|
5566
|
+
* and so understates rather than overstates. The derivation was validated
|
|
5567
|
+
* against the API on both funded Ethereum vaults: 2.579% and 1.449%,
|
|
5568
|
+
* matching to 3 decimals.
|
|
5569
|
+
*/
|
|
5570
|
+
supplyRate: number;
|
|
5571
|
+
/** Extra rewards APR (TMX emissions). 0 on the on-chain fallback, which
|
|
5572
|
+
* cannot see them. */
|
|
5573
|
+
rewardsRate: number;
|
|
5574
|
+
/** `supplyRate + rewardsRate` — what a depositor actually earns. */
|
|
4015
5575
|
depositRate: number;
|
|
4016
|
-
/** Performance fee in percent (e.g. `
|
|
5576
|
+
/** Performance fee in percent (e.g. `10` = 10%). */
|
|
4017
5577
|
fee: number;
|
|
4018
|
-
/** Governance timelock in seconds
|
|
4019
|
-
* changes take effect (Euler Earn `governance.timelock`). Governance-only,
|
|
4020
|
-
* NOT a per-deposit withdrawal lock. Mirrors `MorphoVault.timelock`. */
|
|
5578
|
+
/** Governance timelock in seconds for curator/guardian config changes. */
|
|
4021
5579
|
timelock?: number;
|
|
4022
|
-
/**
|
|
4023
|
-
owner?: string;
|
|
4024
|
-
/** Curator / fee-recipient address, lowercased. */
|
|
5580
|
+
/** Curator address, lowercased. */
|
|
4025
5581
|
curator?: string;
|
|
4026
|
-
/**
|
|
4027
|
-
*
|
|
4028
|
-
*
|
|
5582
|
+
/**
|
|
5583
|
+
* Human-readable curator label.
|
|
5584
|
+
*
|
|
5585
|
+
* ONLY the API carries this — the on-chain surface exposes an address and
|
|
5586
|
+
* nothing else. Do NOT derive it from `name`: the vault literally called
|
|
5587
|
+
* "Coinshift rlUSD vault" is curated by **Keyrock**.
|
|
5588
|
+
*/
|
|
4029
5589
|
curatorName?: string;
|
|
4030
|
-
/** Guardian address, lowercased
|
|
5590
|
+
/** Guardian address, lowercased. */
|
|
4031
5591
|
guardian?: string;
|
|
4032
|
-
/** Fee recipient, lowercased — may be absent. */
|
|
4033
|
-
feeRecipient?: string;
|
|
4034
5592
|
/** Hydrated asset metadata from the provided token list, if any. */
|
|
4035
5593
|
asset?: GenericCurrency;
|
|
4036
5594
|
/** USD price of one underlying unit, if prices were supplied. */
|
|
4037
5595
|
priceUsd?: number;
|
|
4038
|
-
/**
|
|
5596
|
+
/** `totalAssets / 10^assetDecimals`. */
|
|
4039
5597
|
totalAssetsFormatted: number;
|
|
4040
|
-
/**
|
|
5598
|
+
/** `totalAssetsFormatted * priceUsd`. */
|
|
4041
5599
|
totalAssetsUsd: number;
|
|
4042
|
-
/**
|
|
4043
|
-
*
|
|
4044
|
-
*
|
|
4045
|
-
*
|
|
4046
|
-
*
|
|
5600
|
+
/**
|
|
5601
|
+
* Immediately withdrawable underlying, raw integer as string.
|
|
5602
|
+
*
|
|
5603
|
+
* A TermMax vault's capital is committed to maker orders until each order's
|
|
5604
|
+
* maturity, so only part is instantly exitable — the rest needs the curator
|
|
5605
|
+
* to unwind or a maturity to roll. Sourced from the API's `redeemableAmt`
|
|
5606
|
+
* (falling back to `idleFunds`), or the vault's own underlying balance
|
|
5607
|
+
* on-chain. Deliberately NOT `totalAssets`.
|
|
5608
|
+
*/
|
|
4047
5609
|
liquidity: string;
|
|
4048
|
-
/** Human-formatted immediate withdrawable liquidity. */
|
|
4049
5610
|
liquidityFormatted: number;
|
|
4050
|
-
/** Human-formatted immediate withdrawable liquidity in USD. */
|
|
4051
5611
|
liquidityUsd: number;
|
|
5612
|
+
/** Contract `getVersion()` / API `version`, e.g. `"2.0.0"` / `"v2"`. */
|
|
5613
|
+
version?: string;
|
|
5614
|
+
/** Vault is paused — deposits blocked, existing funds still visible. */
|
|
5615
|
+
isPaused?: boolean;
|
|
5616
|
+
/** Deposit cap in underlying base units (API `capacity`). */
|
|
5617
|
+
supplyCap?: string;
|
|
5618
|
+
/**
|
|
5619
|
+
* The vault's ERC-4626 base-yield pool for idle funds (v2_01 "composable
|
|
5620
|
+
* base yield"). Idle capital earns here instead of sitting dead.
|
|
5621
|
+
*/
|
|
5622
|
+
basePool?: string;
|
|
4052
5623
|
}
|
|
4053
5624
|
/** Full parsed payload: per-vault-address map. */
|
|
4054
|
-
type
|
|
5625
|
+
type TermMaxVaults = {
|
|
4055
5626
|
/** Keyed by lowercased vault address. */
|
|
4056
|
-
[vaultAddress: string]:
|
|
5627
|
+
[vaultAddress: string]: TermMaxVault;
|
|
4057
5628
|
};
|
|
4058
5629
|
|
|
4059
5630
|
/**
|
|
@@ -4308,11 +5879,41 @@ interface LstWithdrawalRequest {
|
|
|
4308
5879
|
* ERC-7540 requestId, …). Encoded as a string for cross-protocol
|
|
4309
5880
|
* uniformity. */
|
|
4310
5881
|
requestId: string;
|
|
4311
|
-
/** Raw
|
|
4312
|
-
* integer string. Some protocols
|
|
4313
|
-
* (queue-finalization with floating
|
|
4314
|
-
* surface the **expected** amount at
|
|
5882
|
+
/** Raw amount the request will return on claim, in the token that
|
|
5883
|
+
* escrow actually pays out. Wei-like integer string. Some protocols
|
|
5884
|
+
* only know this at claim time (queue-finalization with a floating
|
|
5885
|
+
* finalization rate) — those surface the **expected** amount at
|
|
5886
|
+
* request time.
|
|
5887
|
+
*
|
|
5888
|
+
* Strata is the one entry where the denomination is not simply the
|
|
5889
|
+
* vault's underlying, and it is easy to get wrong: the escrow is
|
|
5890
|
+
* **keyed** by the collateral token (`finalize(sUSDe, user)`) but the
|
|
5891
|
+
* amount it records is whatever that leg settles in — the tranche's
|
|
5892
|
+
* `asset()` (USDe) on the UnstakeCooldown, which books Ethena's
|
|
5893
|
+
* unstake output, and collateral-token shares on the ERC20Cooldown.
|
|
5894
|
+
* We do not normalize between them; read `withdrawQueue` to tell the
|
|
5895
|
+
* legs apart. Fork-verified for the UnstakeCooldown leg 2026-08-04
|
|
5896
|
+
* (10,000 USDe in → 9,997.5 USDe out at a 2.49 bps exit fee, with
|
|
5897
|
+
* zero sUSDe paid); the ERC20Cooldown denomination is read off the
|
|
5898
|
+
* strategy source, which escrows `sUSDe.previewWithdraw(baseAssets)`
|
|
5899
|
+
* shares. */
|
|
4315
5900
|
amountUnderlying: string;
|
|
5901
|
+
/** Raw share amount of the request, for protocols whose claim call
|
|
5902
|
+
* takes shares (ERC-7540 `redeem`, sUSD3's plain 4626 `redeem`).
|
|
5903
|
+
* Passed back verbatim into the claim builder. */
|
|
5904
|
+
shares?: string;
|
|
5905
|
+
/** The escrow contract this request actually lives on, when the
|
|
5906
|
+
* protocol runs more than one and the registry's default is not
|
|
5907
|
+
* necessarily the right claim target. Strata gives each market both
|
|
5908
|
+
* an `UnstakeCooldown` (base-asset leg) and an `ERC20Cooldown`
|
|
5909
|
+
* (collateral-token leg) — a claim built against the wrong one is a
|
|
5910
|
+
* no-op — so the reader reports which. Passed back verbatim into
|
|
5911
|
+
* the claim builder. */
|
|
5912
|
+
withdrawQueue?: string;
|
|
5913
|
+
/** The token the escrow books this request under, when the claim
|
|
5914
|
+
* call takes it as an argument (Strata's
|
|
5915
|
+
* `finalize(claimToken, user)`). Passed back verbatim. */
|
|
5916
|
+
claimToken?: string;
|
|
4316
5917
|
/** Status discriminator. */
|
|
4317
5918
|
status: LstWithdrawalStatus;
|
|
4318
5919
|
/** Unix seconds when the request becomes claimable. Set for
|
|
@@ -4349,7 +5950,7 @@ type LstWithdrawalStatus =
|
|
|
4349
5950
|
| 'expired';
|
|
4350
5951
|
/** Withdrawal-reader implementation kind — drives which enumeration
|
|
4351
5952
|
* function the user is queried against. */
|
|
4352
|
-
type LstWithdrawalReaderKind = 'lidoQueue' | 'etherfiNft' | 'yieldNestNft' | 'staderEthxQueue' | 'staderMaticXQueue' | 'renzoQueue' | 'kelpQueue' | 'benqiSavaxQueue' | 'beetsStSQueue' | 'hyperbeatBeHype7540' | 'erc7540' | 'ethenaCooldown' | 'swellNft' | 'stakeWiseSubgraph' | 'mantleCallerSuppliedIds' | 'pufferCallerSuppliedIds' | 'trufinCallerSuppliedIds' | 'lairCallerSuppliedIds' | 'stceloAccountQueue' | 'kinetiqQueue' | 'beHypeQueue' | 'valantisBurnQueue' | 'listaQueue' | 'iberaQueue' | 'primeStakingQueue' | 'berapawRedeemQueue' | 'stellaUnbondQueue' | 'eventsOnly' | 'noQueue' | 'unverified';
|
|
5953
|
+
type LstWithdrawalReaderKind = 'lidoQueue' | 'etherfiNft' | 'yieldNestNft' | 'staderEthxQueue' | 'staderMaticXQueue' | 'renzoQueue' | 'kelpQueue' | 'benqiSavaxQueue' | 'beetsStSQueue' | 'hyperbeatBeHype7540' | 'erc7540' | 'ethenaCooldown' | 'susd3Cooldown' | 'strataCooldown' | 'swellNft' | 'stakeWiseSubgraph' | 'mantleCallerSuppliedIds' | 'pufferCallerSuppliedIds' | 'trufinCallerSuppliedIds' | 'lairCallerSuppliedIds' | 'stceloAccountQueue' | 'kinetiqQueue' | 'beHypeQueue' | 'valantisBurnQueue' | 'listaQueue' | 'iberaQueue' | 'primeStakingQueue' | 'berapawRedeemQueue' | 'stellaUnbondQueue' | 'eventsOnly' | 'noQueue' | 'unverified';
|
|
4353
5954
|
/** Map keyed by lowercased LST share-token address. The orchestrator
|
|
4354
5955
|
* fetches all LSTs on a chain in parallel and returns this map
|
|
4355
5956
|
* (possibly with empty arrays for LSTs the user has no requests
|
|
@@ -4395,6 +5996,17 @@ interface LstWithdrawalRegistryEntry {
|
|
|
4395
5996
|
/** Polygon IStakeManager — only for `staderMaticXQueue`. The
|
|
4396
5997
|
* finalization check requires `epoch()` + `withdrawalDelay()`. */
|
|
4397
5998
|
polygonStakeManager?: string;
|
|
5999
|
+
/** Second escrow contract probed with the same reader — only for
|
|
6000
|
+
* `strataCooldown`, where a market runs both an `UnstakeCooldown`
|
|
6001
|
+
* (base-asset leg, in `withdrawalContract`) and an `ERC20Cooldown`
|
|
6002
|
+
* (collateral-token leg). Lowercased. */
|
|
6003
|
+
secondaryWithdrawalContract?: string;
|
|
6004
|
+
/** The token an escrow's requests are booked under — only for
|
|
6005
|
+
* `strataCooldown` (`balanceOf(escrowToken, user)` /
|
|
6006
|
+
* `finalize(escrowToken, user)`). The market's staked collateral
|
|
6007
|
+
* (sUSDe, sNUSD, mHYPER, …), NOT the tranche's `asset()`.
|
|
6008
|
+
* Lowercased. */
|
|
6009
|
+
escrowToken?: string;
|
|
4398
6010
|
}
|
|
4399
6011
|
/** Returns the withdrawal-registry entries for a chain, or `[]`. */
|
|
4400
6012
|
declare const getLstWithdrawalRegistry: (chainId: string, extraEntries?: LstWithdrawalRegistryEntry[]) => LstWithdrawalRegistryEntry[];
|
|
@@ -4529,7 +6141,7 @@ declare const resolveStCeloDepositGroup: (user: Address, requestedGroup?: string
|
|
|
4529
6141
|
* kept separate so the two providers can evolve independently without
|
|
4530
6142
|
* one provider's withdrawal taxonomy creep affecting the other.
|
|
4531
6143
|
*/
|
|
4532
|
-
type SavingsWithdrawalMode = 'instant' | 'fixed-cooldown' | 'queued' | 'request-based' | 'fee-or-queued';
|
|
6144
|
+
type SavingsWithdrawalMode = 'instant' | 'instant-capped' | 'fixed-cooldown' | 'queued' | 'request-based' | 'fee-or-queued';
|
|
4533
6145
|
/**
|
|
4534
6146
|
* Parsed savings-vault entry.
|
|
4535
6147
|
*
|
|
@@ -4560,6 +6172,10 @@ interface SavingsVault extends VaultClassificationFields {
|
|
|
4560
6172
|
displayName: string;
|
|
4561
6173
|
/** Brand label — `Ethena`, `Sky`, `Maker`, `Angle`, `Falcon`, … */
|
|
4562
6174
|
brand: string;
|
|
6175
|
+
/** Curated user-facing explainer (from the registry): what the
|
|
6176
|
+
* underlying is, where the yield comes from, and the exit
|
|
6177
|
+
* mechanics / trust caveat when load-bearing. */
|
|
6178
|
+
description: string;
|
|
4563
6179
|
/** Alias for `brand` to match `curatorName` on the other providers'
|
|
4564
6180
|
* vault types — keeps cross-provider UI code uniform. */
|
|
4565
6181
|
curatorName: string;
|
|
@@ -4591,9 +6207,13 @@ interface SavingsVault extends VaultClassificationFields {
|
|
|
4591
6207
|
/** Sum of `supplyRate + rewardsRate` — what a depositor actually
|
|
4592
6208
|
* earns. */
|
|
4593
6209
|
depositRate: number;
|
|
4594
|
-
/**
|
|
4595
|
-
*
|
|
4596
|
-
|
|
6210
|
+
/** Whether the share token implements ERC-4626. True for every entry
|
|
6211
|
+
* except Native's wNLP, which is a bespoke wrapper (`asset()`,
|
|
6212
|
+
* `totalAssets()` and `convertToAssets()` all revert) — the
|
|
6213
|
+
* `convertTo*` / `exchangeRate` fields below are still populated for
|
|
6214
|
+
* it, derived from its own rate getter. Parity with
|
|
6215
|
+
* `LstShareToken.isErc4626`. */
|
|
6216
|
+
isErc4626: boolean;
|
|
4597
6217
|
/** Whether the share token itself rebases. False for nearly every
|
|
4598
6218
|
* savings vault (they're the non-rebasing wrapper); rebasing
|
|
4599
6219
|
* surfaces sit on the underlying (e.g. USDe inside sUSDe). */
|
|
@@ -4607,12 +6227,51 @@ interface SavingsVault extends VaultClassificationFields {
|
|
|
4607
6227
|
mintContract?: string;
|
|
4608
6228
|
/** Withdrawal mechanism. */
|
|
4609
6229
|
withdrawalMode: SavingsWithdrawalMode;
|
|
4610
|
-
/**
|
|
4611
|
-
*
|
|
4612
|
-
*
|
|
4613
|
-
*
|
|
4614
|
-
*
|
|
6230
|
+
/** Waiting period in seconds before a requested redemption can be
|
|
6231
|
+
* claimed. For `fixed-cooldown` entries (Ethena, Avant) this is the
|
|
6232
|
+
* registry-pinned value. For `fee-or-queued` entries it is the
|
|
6233
|
+
* **live** on-chain queue window, which varies per asset (Native
|
|
6234
|
+
* runs 8 h on some pools and 3 days on most). */
|
|
4615
6235
|
withdrawalCooldownSeconds?: number;
|
|
6236
|
+
/**
|
|
6237
|
+
* Exit fee in basis points (`10` = 0.10 %) — same units and name as
|
|
6238
|
+
* `GearboxV3Pool.withdrawFeeBps`, so consumers read one field across
|
|
6239
|
+
* providers.
|
|
6240
|
+
*
|
|
6241
|
+
* **How it is charged** (Native): it is *not* a deposit fee, a
|
|
6242
|
+
* management fee, or a skim on yield — `exchangeRate` and `supplyRate`
|
|
6243
|
+
* are already net of everything Native takes on the way in. It is a
|
|
6244
|
+
* one-off haircut on the **instant** exit only, taken out of the
|
|
6245
|
+
* underlying paid to the receiver:
|
|
6246
|
+
*
|
|
6247
|
+
* received = shares × exchangeRate × (1 − withdrawFeeBps/10_000)
|
|
6248
|
+
*
|
|
6249
|
+
* so redeeming 9,867.98 wNLP-USDC worth 10,000 USDC returns 9,900 USDC
|
|
6250
|
+
* at 100 bps. The shares burn in full — the fee is deducted from the
|
|
6251
|
+
* payout, never charged as a separate transfer, so a caller does not
|
|
6252
|
+
* need to fund it or approve anything extra.
|
|
6253
|
+
*
|
|
6254
|
+
* The **queued** leg (`withdrawQueue`, after
|
|
6255
|
+
* `withdrawalCooldownSeconds`) pays out at par and does not touch this
|
|
6256
|
+
* field. Its cost is implicit instead: the payout is snapshotted when
|
|
6257
|
+
* the request is made, so yield accruing during the wait goes to the
|
|
6258
|
+
* protocol rather than the requester.
|
|
6259
|
+
*
|
|
6260
|
+
* `0` means the instant leg is free. Absent when the vault has no
|
|
6261
|
+
* instant leg at all.
|
|
6262
|
+
*/
|
|
6263
|
+
withdrawFeeBps?: number;
|
|
6264
|
+
/** Whether the instant leg is enabled at all — some assets are
|
|
6265
|
+
* queue-only. When `false`, `liquidity` is `0` regardless of the
|
|
6266
|
+
* protocol's inventory and `withdrawFeeBps` is unreachable. */
|
|
6267
|
+
instantRedeemEnabled?: boolean;
|
|
6268
|
+
/** Contract the instant leg draws from — Native's per-chain
|
|
6269
|
+
* `CreditVault`. Its underlying balance is what `liquidity`
|
|
6270
|
+
* measures. */
|
|
6271
|
+
inventoryContract?: string;
|
|
6272
|
+
/** Contract a delayed redemption is requested from and claimed
|
|
6273
|
+
* against, when it is not the share token itself. */
|
|
6274
|
+
withdrawQueue?: string;
|
|
4616
6275
|
/** Hydrated asset metadata from the provided token list, if any. */
|
|
4617
6276
|
asset?: GenericCurrency;
|
|
4618
6277
|
/** USD price of one underlying unit, if prices were supplied. */
|
|
@@ -4621,18 +6280,60 @@ interface SavingsVault extends VaultClassificationFields {
|
|
|
4621
6280
|
totalAssetsFormatted: number;
|
|
4622
6281
|
/** Human-formatted total assets in USD. */
|
|
4623
6282
|
totalAssetsUsd: number;
|
|
4624
|
-
/**
|
|
4625
|
-
*
|
|
4626
|
-
*
|
|
4627
|
-
*
|
|
4628
|
-
*
|
|
4629
|
-
*
|
|
4630
|
-
*
|
|
6283
|
+
/** Withdrawable underlying **right now**, raw integer string, per
|
|
6284
|
+
* `withdrawalMode`:
|
|
6285
|
+
* - `instant` — fully liquid (`= totalAssets`).
|
|
6286
|
+
* - `instant-capped` — settles in the same transaction, but only up
|
|
6287
|
+
* to a live inventory that is smaller than the vault: Spark
|
|
6288
|
+
* Savings V1 is capped by the PSM3 pocket's underlying balance
|
|
6289
|
+
* (6–29 % of TVL on the L2 deployments) and Spark Vaults V2 by the
|
|
6290
|
+
* vault's own idle balance (the rest is lent out through the Spark
|
|
6291
|
+
* Liquidity Layer). No fee and no cooldown on this leg — the
|
|
6292
|
+
* difference from `instant` is purely the size cap, and the
|
|
6293
|
+
* difference from `fee-or-queued` is that exceeding it costs
|
|
6294
|
+
* nothing extra, it simply cannot be done this block.
|
|
6295
|
+
* - `fee-or-queued` — the protocol's live instant-exit inventory,
|
|
6296
|
+
* clamped to `totalAssets`; `0` when the instant leg is disabled.
|
|
6297
|
+
* This is a **gross** figure: pulling it out instantly nets
|
|
6298
|
+
* `withdrawFeeBps` less (see that field). The queued leg is not
|
|
6299
|
+
* inventory-capped and pays at par, so `liquidity` is not a cap on
|
|
6300
|
+
* what the vault can ultimately return.
|
|
6301
|
+
* - `fixed-cooldown` / `queued` / `request-based` — `0`; these
|
|
6302
|
+
* require a waiting period.
|
|
6303
|
+
* A cooldown vault's underlying may still be redeemable after the
|
|
6304
|
+
* wait — this field is the *right now* figure, matching the
|
|
6305
|
+
* cross-provider `liquidity` semantic. */
|
|
4631
6306
|
liquidity: string;
|
|
4632
6307
|
/** Human-formatted withdrawable liquidity. */
|
|
4633
6308
|
liquidityFormatted: number;
|
|
4634
6309
|
/** Withdrawable liquidity in USD. */
|
|
4635
6310
|
liquidityUsd: number;
|
|
6311
|
+
/**
|
|
6312
|
+
* `liquidity / totalAssets`, clamped to `0…1` — the share of the vault a
|
|
6313
|
+
* holder could exit **this block**. `1 − instantLiquidityRatio` is the
|
|
6314
|
+
* share that must wait, so this is the vault's **lockup indicator**.
|
|
6315
|
+
*
|
|
6316
|
+
* Deliberately *not* called `utilization`. For a lending vault
|
|
6317
|
+
* utilization is `borrowed / supplied`, read from a debt accumulator;
|
|
6318
|
+
* none of these protocols expose one (Native's CreditVault and NTLP have
|
|
6319
|
+
* no debt getter at all, and the CreditVault commingles market-maker
|
|
6320
|
+
* collateral with pool inventory, so its balance can exceed the pool).
|
|
6321
|
+
* What this measures is exit **coverage**, which is the quantity that
|
|
6322
|
+
* actually predicts lockup — and unlike utilization it stays meaningful
|
|
6323
|
+
* for cooldown vaults that have no borrow side whatsoever.
|
|
6324
|
+
*
|
|
6325
|
+
* Reads per mode:
|
|
6326
|
+
* - `instant` → always `1` (fully liquid by construction).
|
|
6327
|
+
* - `fee-or-queued` → the live CreditVault coverage; the observed spread
|
|
6328
|
+
* across Native pools is the full `0…1` range, so it carries real
|
|
6329
|
+
* information (BNB `wNLP-T4B` sits near `0`, Ethereum `wNLP-USDC` at
|
|
6330
|
+
* `1`). Below `1` the remainder is not lost, just queued.
|
|
6331
|
+
* - `fixed-cooldown` / `queued` / `request-based` → always `0`; nothing
|
|
6332
|
+
* is redeemable without waiting.
|
|
6333
|
+
*
|
|
6334
|
+
* An empty vault reports `1` — there is nothing to be locked up.
|
|
6335
|
+
*/
|
|
6336
|
+
instantLiquidityRatio: number;
|
|
4636
6337
|
}
|
|
4637
6338
|
/**
|
|
4638
6339
|
* Full parsed payload: per-share-token-address map.
|
|
@@ -5852,7 +7553,7 @@ interface VaultLookupEntry {
|
|
|
5852
7553
|
declare function buildVaultLookup(data: VaultPublicDataAll): Map<string, VaultLookupEntry>;
|
|
5853
7554
|
|
|
5854
7555
|
/** Supported ERC-4626 vault providers. */
|
|
5855
|
-
type VaultProvider = 'fluid' | 'gearbox' | 'morpho' | 'lista' | 'silo' | 'euler-earn' | 'lst' | 'savings' | 'lagoon' | 'aave-earn' | 'upshift' | 'yearn' | 'hypercore' | 'gmx';
|
|
7556
|
+
type VaultProvider = 'fluid' | 'gearbox' | 'morpho' | 'lista' | 'silo' | 'euler-earn' | 'termmax' | 'lst' | 'savings' | 'lagoon' | 'aave-earn' | 'upshift' | 'yearn' | 'hypercore' | 'gmx';
|
|
5856
7557
|
/**
|
|
5857
7558
|
* Per-provider payload returned by `getVaultPublicDataAll`. Each entry is
|
|
5858
7559
|
* present only when the matching provider was requested AND its fetch
|
|
@@ -5869,6 +7570,7 @@ interface VaultPublicDataAll {
|
|
|
5869
7570
|
lista?: MorphoVaults;
|
|
5870
7571
|
silo?: SiloVaults;
|
|
5871
7572
|
'euler-earn'?: EulerEarnVaults;
|
|
7573
|
+
termmax?: TermMaxVaults;
|
|
5872
7574
|
lst?: LstShareTokens;
|
|
5873
7575
|
savings?: SavingsVaults;
|
|
5874
7576
|
lagoon?: LagoonVaults;
|
|
@@ -6564,8 +8266,18 @@ declare function parseRawRpcBatchResponses(batches: RawRpcBatch[], batchResponse
|
|
|
6564
8266
|
* Parses multicall3 aggregate3 responses
|
|
6565
8267
|
* The response contains an array of {success, returnData} tuples
|
|
6566
8268
|
* Each returnData needs to be decoded using the original call's ABI
|
|
8269
|
+
*
|
|
8270
|
+
* `permanentFailures`, when supplied, is filled with the indices of calls that
|
|
8271
|
+
* failed DETERMINISTICALLY. This path can tell them apart with certainty, which
|
|
8272
|
+
* the viem path can only infer: if the batch response itself came back, the
|
|
8273
|
+
* transport worked, so a `success: false` entry inside it is a revert — the
|
|
8274
|
+
* chain's answer, not a lost read. Only a batch-level error is a lost read.
|
|
8275
|
+
*
|
|
8276
|
+
* The distinction matters downstream: a cross-margin lender voids its whole set
|
|
8277
|
+
* on a lost read, and without this a single always-reverting market would void
|
|
8278
|
+
* a perfectly good position on every request.
|
|
6567
8279
|
*/
|
|
6568
|
-
declare function parseMulticallRpcResponses(responses: RawRpcResponse[], batches: MulticallRpcBatch[], allowFailure?: boolean): any[];
|
|
8280
|
+
declare function parseMulticallRpcResponses(responses: RawRpcResponse[], batches: MulticallRpcBatch[], allowFailure?: boolean, permanentFailures?: Set<number>): any[];
|
|
6569
8281
|
|
|
6570
8282
|
type TokenEntry = {
|
|
6571
8283
|
chainId: string;
|
|
@@ -6658,4 +8370,1180 @@ interface FetchTokenBalancesOptions {
|
|
|
6658
8370
|
*/
|
|
6659
8371
|
declare function fetchTokenBalances(chainId: string, account: string, tokens: string[], options?: FetchTokenBalancesOptions): Promise<TokenBalanceResult>;
|
|
6660
8372
|
|
|
6661
|
-
|
|
8373
|
+
/**
|
|
8374
|
+
* Term sheets — a structured, per-`marketUid` description of every lend and
|
|
8375
|
+
* borrow offer we serve.
|
|
8376
|
+
*
|
|
8377
|
+
* One shape for pool lenders, fixed-term lenders, CDPs and vaults, so an
|
|
8378
|
+
* integrator reads ONE object instead of ~15 protocol-specific fields plus a
|
|
8379
|
+
* per-lender mental model. See [TERM_SHEET_PLAN.md](../../../../TERM_SHEET_PLAN.md)
|
|
8380
|
+
* for the design rationale and the per-lender coverage matrix.
|
|
8381
|
+
*
|
|
8382
|
+
* ## Conventions that hold everywhere in this file
|
|
8383
|
+
*
|
|
8384
|
+
* - **Rates are nominal APR in PERCENT** (`3.85` = 3.85 %/yr), never a
|
|
8385
|
+
* fraction and never an APY. This is the package-wide convention.
|
|
8386
|
+
* - **Factors are fractions** (`0.85` = 85 % LTV) — matching `LenderConfigData`.
|
|
8387
|
+
* - **Durations are SECONDS**, timestamps are **unix seconds**.
|
|
8388
|
+
* - **Raw amounts are decimal strings** in base units; human amounts are
|
|
8389
|
+
* `number`.
|
|
8390
|
+
* - Every string union is deliberately OPEN (`| (string & {})`) so a new
|
|
8391
|
+
* lender can introduce a member without breaking a consumer's exhaustive
|
|
8392
|
+
* switch. Consumers MUST have a `default` branch and fall back to
|
|
8393
|
+
* `info.headline`.
|
|
8394
|
+
*/
|
|
8395
|
+
/** Nominal APR in percent (`3.85` = 3.85 %/yr). Never a fraction, never APY. */
|
|
8396
|
+
type AprPercent = number;
|
|
8397
|
+
/**
|
|
8398
|
+
* Open-enum helper. `Open<'a' | 'b'>` keeps autocomplete for the known members
|
|
8399
|
+
* while still accepting any string, so adding a member later is an ADDITIVE
|
|
8400
|
+
* change rather than a breaking one.
|
|
8401
|
+
*/
|
|
8402
|
+
type Open<T extends string> = T | (string & {});
|
|
8403
|
+
/** Compact token reference — enough to render without a second lookup. */
|
|
8404
|
+
interface TermAssetRef {
|
|
8405
|
+
chainId: string;
|
|
8406
|
+
/** Lowercased contract address. */
|
|
8407
|
+
address: string;
|
|
8408
|
+
symbol?: string;
|
|
8409
|
+
name?: string;
|
|
8410
|
+
decimals?: number;
|
|
8411
|
+
assetGroup?: string;
|
|
8412
|
+
logoURI?: string;
|
|
8413
|
+
}
|
|
8414
|
+
/**
|
|
8415
|
+
* Machine tags for filtering/faceting. DERIVED from the structured fields in
|
|
8416
|
+
* `tags.ts` — never hand-written per lender, so they cannot drift from the
|
|
8417
|
+
* numbers they summarize.
|
|
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' | 'oracle-flagged' | 'no-oracle'>;
|
|
8420
|
+
/** Human-facing copy for one side of a term sheet. */
|
|
8421
|
+
interface TermInfo {
|
|
8422
|
+
/**
|
|
8423
|
+
* ≤ ~100 chars, templated from live numbers, ready to render.
|
|
8424
|
+
* `"Fixed 4.12 % until 3 Sep 2026 · repay any time at face value"`.
|
|
8425
|
+
*
|
|
8426
|
+
* ALWAYS populated — it is the graceful-degradation path for a consumer
|
|
8427
|
+
* that does not recognise a newer enum member.
|
|
8428
|
+
*/
|
|
8429
|
+
headline: string;
|
|
8430
|
+
/** 1–3 sentences. Invariant prose lives on the profile; this interpolates
|
|
8431
|
+
* the market's own values. */
|
|
8432
|
+
description: string;
|
|
8433
|
+
/** Ready-to-display consequences, most important first. */
|
|
8434
|
+
implications?: string[];
|
|
8435
|
+
tags: TermTag[];
|
|
8436
|
+
}
|
|
8437
|
+
type RateKind = Open<
|
|
8438
|
+
/** Utilization IRM (Aave, Compound, Morpho, Silo, Euler, Fluid…). */
|
|
8439
|
+
'variable-curve'
|
|
8440
|
+
/** Governance-set with no curve (USDD stability fee, Spark `vsr`). */
|
|
8441
|
+
| 'variable-managed'
|
|
8442
|
+
/** Borrower picks the rate (Liquity family). */
|
|
8443
|
+
| 'user-set'
|
|
8444
|
+
/** Locked for a maturity (Exactly, Midnight, Term, TermMax, Teller, Lista). */
|
|
8445
|
+
| 'fixed-term'
|
|
8446
|
+
/** Locked, no maturity. Reserved — nothing uses it today. */
|
|
8447
|
+
| 'fixed-open'
|
|
8448
|
+
/** No ongoing rate at all (River); cost is a one-off fee. */
|
|
8449
|
+
| 'zero-interest'
|
|
8450
|
+
/** Interest prepaid in a separate token (Inverse DBR). */
|
|
8451
|
+
| 'prepaid'
|
|
8452
|
+
/** Share price tracks an attested NAV (Re, Apyx, USPC). */
|
|
8453
|
+
| 'nav-accrual'
|
|
8454
|
+
/** Pure collateral leg — no yield. */
|
|
8455
|
+
| 'none'>;
|
|
8456
|
+
/** One reward program. Identity matters: points are not a bankable APR. */
|
|
8457
|
+
interface RewardTerm {
|
|
8458
|
+
/** Absent for points programs — that absence IS the signal. */
|
|
8459
|
+
asset?: TermAssetRef;
|
|
8460
|
+
kind: Open<'token' | 'points' | 'unknown'>;
|
|
8461
|
+
apr: AprPercent;
|
|
8462
|
+
side: 'supply' | 'borrow';
|
|
8463
|
+
/** How it is realized — decides whether the APR is actually bankable. */
|
|
8464
|
+
claim: Open<'accrual' | 'merkl' | 'manual' | 'none'>;
|
|
8465
|
+
/** Program end, where known. An APR with two weeks left is not an APR. */
|
|
8466
|
+
endsAt?: number;
|
|
8467
|
+
/**
|
|
8468
|
+
* `true` ⇒ not priceable (points). MUST be shown separately and is
|
|
8469
|
+
* deliberately EXCLUDED from `RateTerms.aprTotal`.
|
|
8470
|
+
*/
|
|
8471
|
+
indicative?: boolean;
|
|
8472
|
+
}
|
|
8473
|
+
/** One entry in a fixed-term rate menu. Mirrors `MarketTermEntry`. */
|
|
8474
|
+
interface RateMenuEntry {
|
|
8475
|
+
/** LENDER-SPECIFIC: Exactly/TermMax = unix maturity, Teller = duration
|
|
8476
|
+
* seconds, Lista = broker product id, Midnight/Term = `0`. */
|
|
8477
|
+
termId: number;
|
|
8478
|
+
durationSecs: number;
|
|
8479
|
+
durationDays: number;
|
|
8480
|
+
/** Borrow APR at this term. */
|
|
8481
|
+
apr: AprPercent;
|
|
8482
|
+
/** Lend APR at this term, where the lender quotes both sides. */
|
|
8483
|
+
depositApr?: AprPercent;
|
|
8484
|
+
/** Borrowable liquidity at this term, human units. */
|
|
8485
|
+
available?: number;
|
|
8486
|
+
}
|
|
8487
|
+
interface RateTerms {
|
|
8488
|
+
kind: RateKind;
|
|
8489
|
+
/** Base rate only — no rewards, no intrinsic yield. */
|
|
8490
|
+
apr: AprPercent;
|
|
8491
|
+
components: {
|
|
8492
|
+
base: AprPercent;
|
|
8493
|
+
/** Priceable rewards only. */
|
|
8494
|
+
rewards?: AprPercent;
|
|
8495
|
+
/** Underlying/LST yield the asset earns by itself. */
|
|
8496
|
+
intrinsic?: AprPercent;
|
|
8497
|
+
};
|
|
8498
|
+
/** `base + priceable rewards + intrinsic` — the headline number. */
|
|
8499
|
+
aprTotal: AprPercent;
|
|
8500
|
+
/** Carried explicitly even though it has one value today: a silent
|
|
8501
|
+
* APR→APY change would be the classic undetectable break. */
|
|
8502
|
+
basis: 'apr-nominal';
|
|
8503
|
+
compounding: Open<'per-second' | 'per-block' | 'none' | 'unknown'>;
|
|
8504
|
+
source: Open<'utilization-curve' | 'orderbook' | 'auction' | 'governance' | 'borrower' | 'oracle' | 'api' | 'derived'>;
|
|
8505
|
+
/** Is the rate locked for the life of the position? */
|
|
8506
|
+
isLocked: boolean;
|
|
8507
|
+
/** Protocol-enforced bounds (Liquity min/max, Morpho rateCap/rateFloor). */
|
|
8508
|
+
minApr?: AprPercent;
|
|
8509
|
+
maxApr?: AprPercent;
|
|
8510
|
+
/**
|
|
8511
|
+
* `kind: 'user-set'` only — the contract a RATE SETTER needs.
|
|
8512
|
+
*
|
|
8513
|
+
* On the Liquity family the borrower picks their own rate at open, and the
|
|
8514
|
+
* choice is not cosmetic: it decides where you sit in the redemption queue,
|
|
8515
|
+
* so the cheapest rate is also the one that gets redeemed first. A borrow UI
|
|
8516
|
+
* that offers no input either cannot build the transaction or silently
|
|
8517
|
+
* accepts a default the user never saw.
|
|
8518
|
+
*
|
|
8519
|
+
* `default` is what the protocol applies when the caller omits a rate (the
|
|
8520
|
+
* branch average), so a UI can pre-fill it and stay consistent with what the
|
|
8521
|
+
* action would have done anyway.
|
|
8522
|
+
*/
|
|
8523
|
+
userSet?: {
|
|
8524
|
+
/** Is a rate part of the open call at all? */
|
|
8525
|
+
required: boolean;
|
|
8526
|
+
/** Inclusive bounds, PERCENT. Outside these the open reverts. */
|
|
8527
|
+
min?: AprPercent;
|
|
8528
|
+
max?: AprPercent;
|
|
8529
|
+
/** Applied when the caller omits one — the branch average. */
|
|
8530
|
+
default?: AprPercent;
|
|
8531
|
+
/** Can it be changed after opening (Liquity `set-rate`)? */
|
|
8532
|
+
adjustable: boolean;
|
|
8533
|
+
/**
|
|
8534
|
+
* Changing the rate re-charges the upfront fee — so it is not free, and a
|
|
8535
|
+
* UI that presents it as a slider should say so.
|
|
8536
|
+
*/
|
|
8537
|
+
adjustmentCostNote?: string;
|
|
8538
|
+
/** Seconds before the rate may be adjusted again without penalty. */
|
|
8539
|
+
adjustmentCooldownSecs?: number;
|
|
8540
|
+
};
|
|
8541
|
+
/** Per-program reward detail — token identity, claim path, end date. */
|
|
8542
|
+
rewards?: RewardTerm[];
|
|
8543
|
+
/** Rate menu when the market offers several terms at once. */
|
|
8544
|
+
menu?: RateMenuEntry[];
|
|
8545
|
+
/** Size the quote is valid for, when the rate is depth-dependent. */
|
|
8546
|
+
quote?: {
|
|
8547
|
+
assets: number;
|
|
8548
|
+
basis: Open<'marginal' | 'average'>;
|
|
8549
|
+
};
|
|
8550
|
+
lastChangedAt?: number;
|
|
8551
|
+
}
|
|
8552
|
+
interface MaturityTerms {
|
|
8553
|
+
kind: Open<'perpetual' | 'fixed-date' | 'rolling-duration'>;
|
|
8554
|
+
/** unix seconds; `kind: 'fixed-date'`. */
|
|
8555
|
+
maturity?: number;
|
|
8556
|
+
/** ISO-8601 mirror so consumers need not re-format. */
|
|
8557
|
+
maturityIso?: string;
|
|
8558
|
+
/** Snapshot — derive live from `maturity` for a countdown. */
|
|
8559
|
+
secondsToMaturity?: number;
|
|
8560
|
+
/** `kind: 'rolling-duration'` (Teller, Lista broker). */
|
|
8561
|
+
minDurationSecs?: number;
|
|
8562
|
+
maxDurationSecs?: number;
|
|
8563
|
+
/**
|
|
8564
|
+
* What happens at/after maturity if NOBODY acts. The field that most
|
|
8565
|
+
* surprises users — outcomes range from "interest simply stops" to
|
|
8566
|
+
* "liquidated within five minutes, losing all collateral".
|
|
8567
|
+
*/
|
|
8568
|
+
atMaturity?: Open<'stops-earning' | 'penalty-accrues' | 'liquidatable' | 'default-seizure' | 'physical-delivery' | 'refinanced' | 'auto-roll' | 'none'>;
|
|
8569
|
+
/** Grace window before `atMaturity` bites (Teller ~300 s, TermMax 7200 s). */
|
|
8570
|
+
graceSecs?: number;
|
|
8571
|
+
}
|
|
8572
|
+
type FeeWhen = Open<'entry' | 'ongoing' | 'exit' | 'late' | 'liquidation' | 'performance'>;
|
|
8573
|
+
/**
|
|
8574
|
+
* One charge, in a shape general enough that a NEW fee is data rather than a
|
|
8575
|
+
* schema change. Replaces the current scatter: `originationFee`,
|
|
8576
|
+
* `withdrawFeeBps`, `rates.fee`, `fixedTerm.fees.*`, `river.mintFeeRate`,
|
|
8577
|
+
* `liquity.gasCompensation`.
|
|
8578
|
+
*/
|
|
8579
|
+
interface FeeTerm {
|
|
8580
|
+
/** Stable slug — the join key for UI copy and filtering. */
|
|
8581
|
+
id: Open<'origination' | 'late-penalty' | 'early-repay-penalty' | 'early-repay-discount' | 'continuous' | 'settlement' | 'instant-exit' | 'performance' | 'reserve-factor' | 'gas-compensation' | 'redemption' | 'claim' | 'liquidation-bonus'>;
|
|
8582
|
+
/** Human label — lets an unknown `id` still render correctly. */
|
|
8583
|
+
label: string;
|
|
8584
|
+
when: FeeWhen;
|
|
8585
|
+
unit: Open<'apr-percent' | 'percent' | 'bps' | 'absolute'>;
|
|
8586
|
+
basis: Open<'principal' | 'face-value' | 'yield' | 'collateral' | 'shares' | 'debt-repaid'>;
|
|
8587
|
+
/**
|
|
8588
|
+
* A NEGATIVE value is legal and means a REBATE (Exactly's early-repay
|
|
8589
|
+
* discount). Sign is load-bearing — never take an absolute value.
|
|
8590
|
+
*/
|
|
8591
|
+
value: number;
|
|
8592
|
+
payee?: Open<'protocol' | 'lenders' | 'liquidator' | 'curator' | 'gas-refund'>;
|
|
8593
|
+
/** Governance-mutable ⇒ this is a snapshot; re-verify before quoting. */
|
|
8594
|
+
mutable?: boolean;
|
|
8595
|
+
/** Only resolvable at action time (Exactly discount, TermMax curve price). */
|
|
8596
|
+
indicative?: boolean;
|
|
8597
|
+
/** Decaying/scheduled fees (Apyx: 3.40 % → 0 over 20 days). */
|
|
8598
|
+
schedule?: {
|
|
8599
|
+
afterSecs: number;
|
|
8600
|
+
value: number;
|
|
8601
|
+
}[];
|
|
8602
|
+
description?: string;
|
|
8603
|
+
}
|
|
8604
|
+
/** Superset of `SavingsWithdrawalMode` + `LstWithdrawalMode`, plus the two
|
|
8605
|
+
* lending-side exits neither covers. */
|
|
8606
|
+
type SupplyExitMode = Open<'instant' | 'instant-capped' | 'instant-or-queued' | 'fee-or-queued' | 'fixed-cooldown' | 'queued' | 'request-based'
|
|
8607
|
+
/** Sell the instrument on a book (Term, TermMax, Midnight). */
|
|
8608
|
+
| 'market-sale'
|
|
8609
|
+
/** No early exit at all. */
|
|
8610
|
+
| 'at-maturity' | 'off-chain' | 'dex-only'>;
|
|
8611
|
+
interface SupplyExitTerms {
|
|
8612
|
+
mode: SupplyExitMode;
|
|
8613
|
+
/** Coarse alias — identical semantics to `VaultClassificationFields.redemptionType`. */
|
|
8614
|
+
settlement: Open<'sync' | 'async'>;
|
|
8615
|
+
cooldownSecs?: number;
|
|
8616
|
+
/** Claim-window constraints (Apyx: blocked 3 d, free at 20 d). */
|
|
8617
|
+
claimWindow?: {
|
|
8618
|
+
earliestSecs?: number;
|
|
8619
|
+
freeAfterSecs?: number;
|
|
8620
|
+
};
|
|
8621
|
+
/** What can actually leave right now. */
|
|
8622
|
+
liquidity?: {
|
|
8623
|
+
assets: number;
|
|
8624
|
+
assetsUsd?: number;
|
|
8625
|
+
ratio?: number;
|
|
8626
|
+
};
|
|
8627
|
+
partialAllowed: boolean;
|
|
8628
|
+
/**
|
|
8629
|
+
* Does exiting early cost an UNKNOWN amount?
|
|
8630
|
+
* `none` par · `haircut-formula` deterministic discount ·
|
|
8631
|
+
* `market-price` you sell into a book · `may-be-impossible` the book can be
|
|
8632
|
+
* empty.
|
|
8633
|
+
*/
|
|
8634
|
+
priceRisk: Open<'none' | 'haircut-formula' | 'market-price' | 'may-be-impossible'>;
|
|
8635
|
+
cancellable?: boolean;
|
|
8636
|
+
/** The `when: 'exit' | 'performance'` subset of the side's fees. */
|
|
8637
|
+
fees: FeeTerm[];
|
|
8638
|
+
}
|
|
8639
|
+
interface BorrowExitTerms {
|
|
8640
|
+
/** Three signs exist across our lenders: `discount` is a REBATE (Exactly). */
|
|
8641
|
+
earlyRepay: Open<'free' | 'discount' | 'penalty' | 'market-price' | 'not-allowed'>;
|
|
8642
|
+
atMaturityCost: Open<'face' | 'accrued'>;
|
|
8643
|
+
lateBehaviour: Open<'penalty-accrues' | 'liquidatable' | 'default-seizure' | 'refinanced' | 'none'>;
|
|
8644
|
+
partialAllowed: boolean;
|
|
8645
|
+
/** Dust floor — Liquity `minDebt`, Morpho/Lista `minLoan`. Raw base units. */
|
|
8646
|
+
minDebt?: string;
|
|
8647
|
+
/** Over-repay REVERTS (Midnight `uint128` underflow) — a real footgun. */
|
|
8648
|
+
overRepayReverts?: boolean;
|
|
8649
|
+
fees: FeeTerm[];
|
|
8650
|
+
}
|
|
8651
|
+
/** One named penalty, for models where a single number is not enough. */
|
|
8652
|
+
interface LiquidationPenaltyTerm {
|
|
8653
|
+
/** Stable slug: 'stability-pool' | 'redistribution' | 'hard' | 'protocol-fee'. */
|
|
8654
|
+
id: Open<string>;
|
|
8655
|
+
label: string;
|
|
8656
|
+
/** Fraction of the repaid debt (0.05 = 5 %). */
|
|
8657
|
+
value: number;
|
|
8658
|
+
description?: string;
|
|
8659
|
+
}
|
|
8660
|
+
interface LiquidationTerms {
|
|
8661
|
+
/**
|
|
8662
|
+
* HOW liquidation happens mechanically — distinct from WHAT triggers it.
|
|
8663
|
+
*
|
|
8664
|
+
* Without this, every market reads as the ordinary "a liquidator repays your
|
|
8665
|
+
* debt and seizes collateral plus a bonus" model, which is wrong for four of
|
|
8666
|
+
* the lenders we serve and materially misleading for one:
|
|
8667
|
+
*
|
|
8668
|
+
* - `soft-band` Collateral is converted GRADUALLY inside the market's
|
|
8669
|
+
* own AMM as the price enters a band range — with NO
|
|
8670
|
+
* penalty and REVERSIBLY (Curve LlamaLend). There is no
|
|
8671
|
+
* single liquidation price at all.
|
|
8672
|
+
* - `stability-pool` A pool absorbs the debt first and only falls back to
|
|
8673
|
+
* redistributing it across other borrowers (Liquity,
|
|
8674
|
+
* River) — the two paths carry DIFFERENT penalties.
|
|
8675
|
+
* - `auction` Price is discovered by a Dutch auction rather than an
|
|
8676
|
+
* oracle (Frankencoin challenges).
|
|
8677
|
+
* - `default-seizure` The whole escrow is forfeit on a missed payment
|
|
8678
|
+
* (Teller).
|
|
8679
|
+
* - `delivery` Unpaid collateral is delivered to lenders (TermMax).
|
|
8680
|
+
*/
|
|
8681
|
+
model?: Open<'repay-seize' | 'soft-band' | 'stability-pool' | 'auction' | 'default-seizure' | 'delivery' | 'none'>;
|
|
8682
|
+
/** Who ends up holding the seized collateral. */
|
|
8683
|
+
absorber?: Open<'liquidator' | 'stability-pool' | 'other-borrowers' | 'lenders' | 'amm'>;
|
|
8684
|
+
/**
|
|
8685
|
+
* Can the position come BACK out of liquidation if the price recovers?
|
|
8686
|
+
* True only for `soft-band`: conversion is continuous and reverses, so
|
|
8687
|
+
* "being liquidated" is not terminal the way it is everywhere else.
|
|
8688
|
+
*/
|
|
8689
|
+
reversible?: boolean;
|
|
8690
|
+
/**
|
|
8691
|
+
* Named penalties when `penalty` alone cannot express the model — Liquity
|
|
8692
|
+
* charges a different rate depending on whether the Stability Pool absorbs
|
|
8693
|
+
* the debt or it is redistributed. `penalty` stays the headline (worst or
|
|
8694
|
+
* primary) so a naive consumer is still correct.
|
|
8695
|
+
*/
|
|
8696
|
+
penalties?: LiquidationPenaltyTerm[];
|
|
8697
|
+
/** Window between becoming liquidatable and the terminal outcome (TermMax). */
|
|
8698
|
+
windowSecs?: number;
|
|
8699
|
+
/** Only allowlisted keepers may liquidate. Absent/false ⇒ permissionless. */
|
|
8700
|
+
permissioned?: boolean;
|
|
8701
|
+
/** Where a shortfall goes when the collateral does not cover the debt. */
|
|
8702
|
+
badDebt?: Open<'socialized' | 'redistributed' | 'insurance-fund' | 'protocol-absorbed' | 'unknown'>;
|
|
8703
|
+
/**
|
|
8704
|
+
* `soft-band` only: the collateral factor is a FUNCTION of the band count
|
|
8705
|
+
* chosen at open (`{ [N]: ltv }` — 0.991 at N=4 vs 0.886 at N=50). `ltv`
|
|
8706
|
+
* reports the market's default N; a consumer quoting a different N must read
|
|
8707
|
+
* this curve instead.
|
|
8708
|
+
*/
|
|
8709
|
+
bandLtv?: Record<string, number>;
|
|
8710
|
+
/** Band count `ltv` / `liquidationLtv` were computed at. */
|
|
8711
|
+
defaultBands?: number;
|
|
8712
|
+
/**
|
|
8713
|
+
* Aave-style escalation: the close factor rises to 1 once health falls below
|
|
8714
|
+
* this. Without it, `closeFactor: 0.5` understates the worst case.
|
|
8715
|
+
*/
|
|
8716
|
+
fullCloseBelowHealthFactor?: number;
|
|
8717
|
+
trigger: Open<'price' | 'time' | 'price-and-time' | 'redemption' | 'none'>;
|
|
8718
|
+
/** Max LTV at open. */
|
|
8719
|
+
ltv?: number;
|
|
8720
|
+
/** Threshold at which liquidation becomes possible. */
|
|
8721
|
+
liquidationLtv?: number;
|
|
8722
|
+
/** Fraction of repaid debt paid to the liquidator on top of par. */
|
|
8723
|
+
penalty: number;
|
|
8724
|
+
closeFactor: number;
|
|
8725
|
+
targetHealthFactor?: number;
|
|
8726
|
+
/**
|
|
8727
|
+
* `full-collateral` is the Teller case: the liquidator takes the ENTIRE
|
|
8728
|
+
* escrow, not a proportional slice — ~2× the debt at 50 % LTV.
|
|
8729
|
+
*/
|
|
8730
|
+
seizure: Open<'proportional' | 'full-collateral'>;
|
|
8731
|
+
/** Liquity/River: collateral redeemable at par while perfectly healthy. */
|
|
8732
|
+
redeemable?: boolean;
|
|
8733
|
+
gracePeriodSecs?: number;
|
|
8734
|
+
}
|
|
8735
|
+
interface CounterpartyTerms {
|
|
8736
|
+
kind: Open<'pool' | 'orderbook' | 'auction' | 'broker' | 'cdp' | 'p2p' | 'vault-strategy' | 'off-chain-credit'>;
|
|
8737
|
+
address?: string;
|
|
8738
|
+
/** The trust question, one field. */
|
|
8739
|
+
solvency: Open<'overcollateralized' | 'tranched-senior' | 'tranched-junior' | 'undercollateralized' | 'nav-attested'>;
|
|
8740
|
+
socializedLoss?: boolean;
|
|
8741
|
+
curator?: string;
|
|
8742
|
+
}
|
|
8743
|
+
/** Origination window for auction-gated markets (Term Finance). */
|
|
8744
|
+
interface AuctionWindow {
|
|
8745
|
+
status: Open<'upcoming' | 'open' | 'revealing' | 'closed'>;
|
|
8746
|
+
canBorrow: boolean;
|
|
8747
|
+
canLend: boolean;
|
|
8748
|
+
secondsUntilClose?: number;
|
|
8749
|
+
id?: string;
|
|
8750
|
+
startTime?: number;
|
|
8751
|
+
revealTime?: number;
|
|
8752
|
+
endTime?: number;
|
|
8753
|
+
minBorrowAmount?: string;
|
|
8754
|
+
minLendAmount?: string;
|
|
8755
|
+
}
|
|
8756
|
+
/** What must be granted BEFORE an action can even be built. */
|
|
8757
|
+
type PermissionKind = Open<'token-approval' | 'lender-delegation' | 'manager-authorization' | 'eip712-permit' | 'nft-approval' | 'operator-set'
|
|
8758
|
+
/** Contract callers must be governance-approved (Inverse, Fraxlend). */
|
|
8759
|
+
| 'caller-allowlist'>;
|
|
8760
|
+
interface AvailabilityTerms {
|
|
8761
|
+
/** Gate CTAs on THIS and nothing else — it already folds in caps, freezes,
|
|
8762
|
+
* auction windows and gating. */
|
|
8763
|
+
canOpen: boolean;
|
|
8764
|
+
canClose: boolean;
|
|
8765
|
+
/** Machine-readable reason when `canOpen` is false. */
|
|
8766
|
+
blockedBy?: Open<'frozen' | 'paused' | 'cap-full' | 'auction-closed' | 'no-liquidity' | 'not-whitelisted' | 'shutdown' | 'disabled'>;
|
|
8767
|
+
gating: Open<'permissionless' | 'whitelist' | 'attestation' | 'kyc' | 'allowlist-contract'>;
|
|
8768
|
+
/** Absent ⇒ no window applies. NOT the same as `closed`. */
|
|
8769
|
+
window?: AuctionWindow;
|
|
8770
|
+
/** Raw base units. */
|
|
8771
|
+
minSize?: string;
|
|
8772
|
+
cap?: string;
|
|
8773
|
+
/** 0..1 — how full the cap is. */
|
|
8774
|
+
capUtilization?: number;
|
|
8775
|
+
requires?: PermissionKind[];
|
|
8776
|
+
}
|
|
8777
|
+
interface PositionConstraints {
|
|
8778
|
+
/** Aave isolation mode: capped debt, no collateral mixing. */
|
|
8779
|
+
isolation?: {
|
|
8780
|
+
enabled: boolean;
|
|
8781
|
+
debtCeiling?: string;
|
|
8782
|
+
ceilingUtilization?: number;
|
|
8783
|
+
};
|
|
8784
|
+
/** Borrowing this asset forbids borrowing any other in the same account. */
|
|
8785
|
+
siloedBorrowing?: boolean;
|
|
8786
|
+
crossMargin: boolean;
|
|
8787
|
+
/**
|
|
8788
|
+
* How a position is ADDRESSED. `loanId` already means five different things
|
|
8789
|
+
* across our lenders and `termId` six — making the model explicit is
|
|
8790
|
+
* cheaper than making every integrator rediscover it.
|
|
8791
|
+
*/
|
|
8792
|
+
positionModel: Open<'account' | 'sub-account' | 'nft' | 'cdp-id' | 'loan-id' | 'escrow'>;
|
|
8793
|
+
/** One line saying what the id in `loanId`/`posId` actually IS here. */
|
|
8794
|
+
positionIdMeaning?: string;
|
|
8795
|
+
maxPositions?: number;
|
|
8796
|
+
/**
|
|
8797
|
+
* What this fetch actually SAW, as opposed to what the lender family
|
|
8798
|
+
* implies. Kept separate from `crossMargin` / `positionModel` on purpose:
|
|
8799
|
+
* those are family-level truths from the registry, these are per-fetch
|
|
8800
|
+
* observations, and collapsing the two would let a thin chain (a lender
|
|
8801
|
+
* listing one asset today) masquerade as a structural property.
|
|
8802
|
+
*
|
|
8803
|
+
* Useful precisely where they DISAGREE — e.g. Fluid is registered isolated
|
|
8804
|
+
* because its T1 vaults dominate, but its T2–T4 "smart" vaults genuinely
|
|
8805
|
+
* pool two collaterals; a `collateralAssetCount > 1` on a Fluid row is the
|
|
8806
|
+
* signal that this particular vault is one of them.
|
|
8807
|
+
*/
|
|
8808
|
+
observed?: {
|
|
8809
|
+
/** Distinct collateral assets this market actually accepts, this fetch. */
|
|
8810
|
+
collateralAssetCount: number;
|
|
8811
|
+
/** Markets seen under this lender key on this chain, this fetch. */
|
|
8812
|
+
marketCount: number;
|
|
8813
|
+
/** Does the lender key fan out to many markets (registry answer)? */
|
|
8814
|
+
multiMarketKey: boolean;
|
|
8815
|
+
};
|
|
8816
|
+
}
|
|
8817
|
+
type AdminKind = Open<'EOA' | 'SAFE' | 'TIMELOCK' | 'GOVERNOR' | 'GOVERNANCE' | 'CUSTOM' | 'UNKNOWN'>;
|
|
8818
|
+
type GovernancePower = Open<'pause-deposits' | 'pause-withdrawals' | 'pause-borrows' | 'freeze-market' | 'change-ltv' | 'change-rate' | 'change-fees' | 'set-caps' | 'add-collateral' | 'swap-oracle' | 'upgrade-implementation' | 'blacklist' | 'seize-funds' | 'reprice-pending-redemptions'>;
|
|
8819
|
+
interface GovernanceTerms {
|
|
8820
|
+
mutability: Open<'immutable' | 'governed' | 'unknown'>;
|
|
8821
|
+
/** The governance root, after hopping proxy admins / timelock admins. */
|
|
8822
|
+
controller?: string;
|
|
8823
|
+
controllerKind?: AdminKind;
|
|
8824
|
+
safe?: {
|
|
8825
|
+
threshold: number;
|
|
8826
|
+
owners: number;
|
|
8827
|
+
};
|
|
8828
|
+
/**
|
|
8829
|
+
* Enforced delay in SECONDS between a parameter change being queued and it
|
|
8830
|
+
* taking effect — the holder's NOTICE PERIOD. `0`, or any `controllerKind`
|
|
8831
|
+
* that is not `TIMELOCK`, means a parameter can change in the very next
|
|
8832
|
+
* block with no warning.
|
|
8833
|
+
*
|
|
8834
|
+
* **This is NOT a withdrawal lock.** `SupplyExitTerms.cooldownSecs` is how
|
|
8835
|
+
* long YOUR money is stuck; this is how long you have to react to someone
|
|
8836
|
+
* else changing the deal. Never merge or sum the two.
|
|
8837
|
+
*/
|
|
8838
|
+
timelockSecs?: number;
|
|
8839
|
+
timelockSource?: Open<'on-chain' | 'screened' | 'metadata'>;
|
|
8840
|
+
/**
|
|
8841
|
+
* The controller IS a timelock but its delay could not be read. Distinct
|
|
8842
|
+
* from `timelockSecs: undefined` on a non-timelock root, which genuinely
|
|
8843
|
+
* means "no notice period" — conflating the two would raise a false alarm
|
|
8844
|
+
* on the safest governance shape.
|
|
8845
|
+
*/
|
|
8846
|
+
timelockUnknown?: boolean;
|
|
8847
|
+
tier?: Open<'low' | 'medium' | 'high' | 'unknown'>;
|
|
8848
|
+
score?: number;
|
|
8849
|
+
powers?: GovernancePower[];
|
|
8850
|
+
roles?: {
|
|
8851
|
+
owner?: string;
|
|
8852
|
+
curator?: string;
|
|
8853
|
+
guardian?: string;
|
|
8854
|
+
feeRecipient?: string;
|
|
8855
|
+
};
|
|
8856
|
+
/** Governance screens refresh far slower than rates — own timestamp. */
|
|
8857
|
+
asOfScreen?: number;
|
|
8858
|
+
}
|
|
8859
|
+
type OracleBand = Open<'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'>;
|
|
8860
|
+
interface OracleTerms {
|
|
8861
|
+
/**
|
|
8862
|
+
* `none` is MEANINGFUL, not missing data: Teller liquidates on TIME and has
|
|
8863
|
+
* no oracle and no health factor anywhere in its trigger.
|
|
8864
|
+
*/
|
|
8865
|
+
kind: Open<'price-feed' | 'nav-attested' | 'none'>;
|
|
8866
|
+
/**
|
|
8867
|
+
* THE oracle for this `marketUid` — SINGULAR, lowercased. Verified across
|
|
8868
|
+
* the full oracle classification: 8,993 marketUids, 0 with more than one
|
|
8869
|
+
* address. Singularity holds because `marketUid` granularity is already
|
|
8870
|
+
* per-asset; the array in the legacy `oracleInfo.feeds[]` is an artifact of
|
|
8871
|
+
* hanging off the lender/params level instead.
|
|
8872
|
+
*
|
|
8873
|
+
* For a composite/cross adapter this is the ADAPTER — the address whose
|
|
8874
|
+
* failure or replacement moves this market's price.
|
|
8875
|
+
*/
|
|
8876
|
+
address?: string;
|
|
8877
|
+
/** Underlying feeds when the adapter composes several and the classifier
|
|
8878
|
+
* decomposed them. `address` stays the single source of truth. */
|
|
8879
|
+
components?: string[];
|
|
8880
|
+
provider?: string;
|
|
8881
|
+
/** Decoded reported pair, e.g. `"ETH / USD"`. */
|
|
8882
|
+
priceDescription?: string;
|
|
8883
|
+
/** What it SHOULD report, e.g. `"WETH / USD"`. */
|
|
8884
|
+
intendedPair?: string;
|
|
8885
|
+
correctAsset?: boolean | null;
|
|
8886
|
+
correctNumeraire?: boolean | null;
|
|
8887
|
+
fixedRate?: boolean;
|
|
8888
|
+
score?: number;
|
|
8889
|
+
band?: OracleBand;
|
|
8890
|
+
flags?: string[];
|
|
8891
|
+
/** Can the oracle be swapped/upgraded, and by whom. On an otherwise
|
|
8892
|
+
* IMMUTABLE market this is the ONLY mutable trust vector. */
|
|
8893
|
+
mutability?: {
|
|
8894
|
+
mutable: boolean;
|
|
8895
|
+
kind: Open<'IMMUTABLE' | 'PROXY' | 'AUTHORITY' | 'UNKNOWN'>;
|
|
8896
|
+
controller?: string;
|
|
8897
|
+
controllerKind?: AdminKind;
|
|
8898
|
+
timelockSecs?: number;
|
|
8899
|
+
};
|
|
8900
|
+
heartbeatSecs?: number;
|
|
8901
|
+
lastUpdateAt?: number;
|
|
8902
|
+
}
|
|
8903
|
+
interface AssetQuality {
|
|
8904
|
+
/** 1 (best) … 5 (worst). */
|
|
8905
|
+
riskScore?: number;
|
|
8906
|
+
source?: Open<'whitelist' | 'default' | 'curated'>;
|
|
8907
|
+
/** On-chain USD liquidity available to absorb a liquidation — the number
|
|
8908
|
+
* that decides whether the LLTV is actually enforceable. */
|
|
8909
|
+
liquidityUsd?: number;
|
|
8910
|
+
/** The TOKEN CONTRACT's own governance, distinct from the market's. An
|
|
8911
|
+
* upgradeable, pausable collateral is a supplier risk even on an
|
|
8912
|
+
* immutable market. */
|
|
8913
|
+
governanceScore?: number;
|
|
8914
|
+
governanceLevel?: Open<'green' | 'amber' | 'red'>;
|
|
8915
|
+
upgradeable?: boolean;
|
|
8916
|
+
canPause?: boolean;
|
|
8917
|
+
adminKind?: AdminKind;
|
|
8918
|
+
}
|
|
8919
|
+
interface ExposureEntry {
|
|
8920
|
+
asset: TermAssetRef;
|
|
8921
|
+
/** That asset's OWN row in this lender — join key to its full term sheet. */
|
|
8922
|
+
marketUid?: string;
|
|
8923
|
+
via: Open<'collateral' | 'vault-allocation' | 'strategy' | 'idle'>;
|
|
8924
|
+
assets?: number;
|
|
8925
|
+
assetsUsd?: number;
|
|
8926
|
+
/** 0..100. ABSENT when `weightBasis === 'unweighted'`. */
|
|
8927
|
+
weightPct?: number;
|
|
8928
|
+
ltv?: number;
|
|
8929
|
+
liquidationLtv?: number;
|
|
8930
|
+
liquidationPenalty?: number;
|
|
8931
|
+
/** The collateral's OWN oracle. Your deposit's safety depends on the oracle
|
|
8932
|
+
* pricing SOMEONE ELSE'S collateral. */
|
|
8933
|
+
oracle?: OracleTerms;
|
|
8934
|
+
quality?: AssetQuality;
|
|
8935
|
+
}
|
|
8936
|
+
interface ExposureTerms {
|
|
8937
|
+
count: number;
|
|
8938
|
+
/**
|
|
8939
|
+
* How `weightPct` was obtained, and therefore how much to trust it.
|
|
8940
|
+
* `unweighted` = POOLED lenders: Aave/Compound do not record which
|
|
8941
|
+
* collateral backs which borrow on-chain, so the list is the ACCEPTED SET,
|
|
8942
|
+
* not a measured split. Do not render a pie chart from it.
|
|
8943
|
+
*/
|
|
8944
|
+
weightBasis: Open<'debt' | 'allocation' | 'unweighted'>;
|
|
8945
|
+
worstRiskScore?: number;
|
|
8946
|
+
worstOracleBand?: OracleBand;
|
|
8947
|
+
/** Largest single exposure's `weightPct` — the concentration signal. Only
|
|
8948
|
+
* meaningful when `weightBasis !== 'unweighted'`. */
|
|
8949
|
+
topWeightPct?: number;
|
|
8950
|
+
items: ExposureEntry[];
|
|
8951
|
+
}
|
|
8952
|
+
interface UtilizationTerms {
|
|
8953
|
+
/** borrowed / supplied, 0..1 — the IRM input for this market. */
|
|
8954
|
+
utilization: number;
|
|
8955
|
+
/**
|
|
8956
|
+
* The basis the ratio is computed over. NOT always this row: rates for
|
|
8957
|
+
* shared-liquidity protocols are set on a larger pool, and a simulation
|
|
8958
|
+
* must shift THAT, not the row totals.
|
|
8959
|
+
*/
|
|
8960
|
+
basis: Open<'market' | 'hub' | 'liquidity-layer' | 'pool'>;
|
|
8961
|
+
irmTotalDeposits?: number;
|
|
8962
|
+
irmTotalDebt?: number;
|
|
8963
|
+
/** Where the curve steepens — headroom before the rate jumps. */
|
|
8964
|
+
targetUtilization?: number;
|
|
8965
|
+
kinkUtilization?: number;
|
|
8966
|
+
/** 0..1. `1` = cap full and the side is closed. */
|
|
8967
|
+
supplyCapUtilization?: number;
|
|
8968
|
+
borrowCapUtilization?: number;
|
|
8969
|
+
/** Fluid: share of collateral locked below the withdrawal limit. */
|
|
8970
|
+
lockupRatio?: number;
|
|
8971
|
+
}
|
|
8972
|
+
/**
|
|
8973
|
+
* A non-default risk category, expressed as a DELTA against the resolved
|
|
8974
|
+
* default. `config` is a MAP keyed by category (Aave e-modes, Dolomite
|
|
8975
|
+
* categories, Euler configs, Silo) — a single flat LTV silently reports the
|
|
8976
|
+
* default and hides the rest, which on an Aave ETH-correlated e-mode is the
|
|
8977
|
+
* difference between 80 % and 93 %.
|
|
8978
|
+
*/
|
|
8979
|
+
interface ModeVariant {
|
|
8980
|
+
/** The `config` map key. `'0'` is the default on every lender. */
|
|
8981
|
+
modeId: string;
|
|
8982
|
+
label?: string;
|
|
8983
|
+
isDefault: boolean;
|
|
8984
|
+
entry?: Open<'automatic' | 'user-selected' | 'per-position'>;
|
|
8985
|
+
liquidation?: Partial<LiquidationTerms>;
|
|
8986
|
+
/**
|
|
8987
|
+
* Mode-scoped and usually RESTRICTED: an e-mode typically narrows the
|
|
8988
|
+
* accepted collateral to a correlated basket. Omitting it would make the
|
|
8989
|
+
* headline LTV look obtainable against collateral the mode forbids.
|
|
8990
|
+
*/
|
|
8991
|
+
acceptedCollateral?: ExposureTerms;
|
|
8992
|
+
rate?: Partial<RateTerms>;
|
|
8993
|
+
availability?: Partial<AvailabilityTerms>;
|
|
8994
|
+
}
|
|
8995
|
+
interface SupplyTermSheet {
|
|
8996
|
+
/** Is this position earning, or is it just collateral? */
|
|
8997
|
+
role: Open<'yield' | 'collateral' | 'both'>;
|
|
8998
|
+
rate: RateTerms;
|
|
8999
|
+
maturity: MaturityTerms;
|
|
9000
|
+
exit: SupplyExitTerms;
|
|
9001
|
+
/** ALL fees on this side, including the exit subset. */
|
|
9002
|
+
fees: FeeTerm[];
|
|
9003
|
+
/** What secures the debt drawn against this deposit. */
|
|
9004
|
+
backedBy?: ExposureTerms;
|
|
9005
|
+
modes?: ModeVariant[];
|
|
9006
|
+
counterparty: CounterpartyTerms;
|
|
9007
|
+
availability: AvailabilityTerms;
|
|
9008
|
+
/** Is the supplied principal at risk beyond ordinary credit risk? */
|
|
9009
|
+
principal: {
|
|
9010
|
+
protected: boolean;
|
|
9011
|
+
risks: Open<'bad-debt' | 'physical-delivery' | 'nav-drawdown' | 'first-loss' | 'depeg'>[];
|
|
9012
|
+
};
|
|
9013
|
+
info: TermInfo;
|
|
9014
|
+
/** Namespaced escape hatch — see the promotion rule in TERM_SHEET_PLAN §13.5. */
|
|
9015
|
+
ext?: Record<string, unknown>;
|
|
9016
|
+
}
|
|
9017
|
+
interface BorrowTermSheet {
|
|
9018
|
+
rate: RateTerms;
|
|
9019
|
+
maturity: MaturityTerms;
|
|
9020
|
+
/**
|
|
9021
|
+
* Does the amount owed GROW, or is it a static face value fixed at trade
|
|
9022
|
+
* time? The single biggest departure from variable-rate intuition — four of
|
|
9023
|
+
* six fixed-term lenders are static.
|
|
9024
|
+
*/
|
|
9025
|
+
debtShape: Open<'accruing' | 'static-face' | 'prepaid'>;
|
|
9026
|
+
exit: BorrowExitTerms;
|
|
9027
|
+
/** Fully resolved for the DEFAULT mode. `modes[]` carries the rest. */
|
|
9028
|
+
liquidation: LiquidationTerms;
|
|
9029
|
+
/** What you may post, each with its own LTV, oracle and quality. */
|
|
9030
|
+
acceptedCollateral?: ExposureTerms;
|
|
9031
|
+
modes?: ModeVariant[];
|
|
9032
|
+
fees: FeeTerm[];
|
|
9033
|
+
counterparty: CounterpartyTerms;
|
|
9034
|
+
availability: AvailabilityTerms;
|
|
9035
|
+
info: TermInfo;
|
|
9036
|
+
ext?: Record<string, unknown>;
|
|
9037
|
+
}
|
|
9038
|
+
/**
|
|
9039
|
+
* Distinguishes "not applicable" from "not implemented yet" — the affordance
|
|
9040
|
+
* that lets phases ship incrementally without lying. A missing `oracle` must
|
|
9041
|
+
* never read as "this market has no oracle" when the truth is "we have not
|
|
9042
|
+
* classified it".
|
|
9043
|
+
*/
|
|
9044
|
+
interface CoverageInfo {
|
|
9045
|
+
/** Blocks genuinely computed for this market. */
|
|
9046
|
+
present: string[];
|
|
9047
|
+
/** Blocks that do NOT APPLY here — a positive fact. */
|
|
9048
|
+
notApplicable?: Record<string, string>;
|
|
9049
|
+
/** Blocks that WOULD apply but are not wired yet. */
|
|
9050
|
+
pending?: Record<string, string>;
|
|
9051
|
+
}
|
|
9052
|
+
/** Current schema version. Bumped ONLY for removals/semantic/unit changes;
|
|
9053
|
+
* new optional fields and new enum members are additive. */
|
|
9054
|
+
declare const TERM_SHEET_SCHEMA_VERSION = 1;
|
|
9055
|
+
interface TermSheet {
|
|
9056
|
+
schemaVersion: number;
|
|
9057
|
+
/** unix seconds at fetch — everything here is a snapshot. */
|
|
9058
|
+
asOf: number;
|
|
9059
|
+
/** `<family>.<variant>@v<n>`, e.g. `aave-v3.pool@v1`. Points at the prose
|
|
9060
|
+
* catalogue so the per-market payload stays small. */
|
|
9061
|
+
profileId: string;
|
|
9062
|
+
/** The anchor this sheet describes. */
|
|
9063
|
+
marketUid?: string;
|
|
9064
|
+
lender?: string;
|
|
9065
|
+
chainId?: string;
|
|
9066
|
+
/**
|
|
9067
|
+
* The market's own underlying asset.
|
|
9068
|
+
*
|
|
9069
|
+
* Load-bearing, not decoration: `availability.minSize`, `availability.cap`
|
|
9070
|
+
* and `exit.minDebt` are all RAW base units, and without decimals + symbol a
|
|
9071
|
+
* consumer cannot render any of them. The sheet described a market without
|
|
9072
|
+
* ever saying which asset it was about.
|
|
9073
|
+
*/
|
|
9074
|
+
asset?: TermAssetRef;
|
|
9075
|
+
supply?: SupplyTermSheet;
|
|
9076
|
+
borrow?: BorrowTermSheet;
|
|
9077
|
+
/** Shared — these describe the MARKET, not a side. */
|
|
9078
|
+
governance?: GovernanceTerms;
|
|
9079
|
+
oracle?: OracleTerms;
|
|
9080
|
+
utilization?: UtilizationTerms;
|
|
9081
|
+
constraints?: PositionConstraints;
|
|
9082
|
+
coverage?: CoverageInfo;
|
|
9083
|
+
ext?: Record<string, unknown>;
|
|
9084
|
+
}
|
|
9085
|
+
/** Compact form for list endpoints — `?terms=digest`. */
|
|
9086
|
+
interface TermSheetDigest {
|
|
9087
|
+
schemaVersion: number;
|
|
9088
|
+
profileId: string;
|
|
9089
|
+
marketUid?: string;
|
|
9090
|
+
supply?: {
|
|
9091
|
+
rateKind: RateKind;
|
|
9092
|
+
aprTotal: AprPercent;
|
|
9093
|
+
maturityKind: MaturityTerms['kind'];
|
|
9094
|
+
maturity?: number;
|
|
9095
|
+
exitMode: SupplyExitMode;
|
|
9096
|
+
settlement: SupplyExitTerms['settlement'];
|
|
9097
|
+
canOpen: boolean;
|
|
9098
|
+
headline: string;
|
|
9099
|
+
tags: TermTag[];
|
|
9100
|
+
backedBy?: Omit<ExposureTerms, 'items'>;
|
|
9101
|
+
};
|
|
9102
|
+
borrow?: {
|
|
9103
|
+
rateKind: RateKind;
|
|
9104
|
+
apr: AprPercent;
|
|
9105
|
+
maturityKind: MaturityTerms['kind'];
|
|
9106
|
+
maturity?: number;
|
|
9107
|
+
debtShape: BorrowTermSheet['debtShape'];
|
|
9108
|
+
earlyRepay: BorrowExitTerms['earlyRepay'];
|
|
9109
|
+
liquidationTrigger: LiquidationTerms['trigger'];
|
|
9110
|
+
canOpen: boolean;
|
|
9111
|
+
headline: string;
|
|
9112
|
+
tags: TermTag[];
|
|
9113
|
+
acceptedCollateral?: Omit<ExposureTerms, 'items'>;
|
|
9114
|
+
};
|
|
9115
|
+
oracle?: Pick<OracleTerms, 'kind' | 'address' | 'provider' | 'band'>;
|
|
9116
|
+
governance?: Pick<GovernanceTerms, 'mutability' | 'controllerKind' | 'timelockSecs' | 'tier'>;
|
|
9117
|
+
utilization?: number;
|
|
9118
|
+
}
|
|
9119
|
+
/** A term profile — the invariant prose, one per lender family × variant. */
|
|
9120
|
+
interface TermProfile {
|
|
9121
|
+
id: string;
|
|
9122
|
+
/** Display name, e.g. `Aave V3 pool market`. */
|
|
9123
|
+
name: string;
|
|
9124
|
+
/** Which lender family this covers. */
|
|
9125
|
+
family: string;
|
|
9126
|
+
supply?: {
|
|
9127
|
+
description: string;
|
|
9128
|
+
implications?: string[];
|
|
9129
|
+
};
|
|
9130
|
+
borrow?: {
|
|
9131
|
+
description: string;
|
|
9132
|
+
implications?: string[];
|
|
9133
|
+
};
|
|
9134
|
+
docsUrl?: string;
|
|
9135
|
+
}
|
|
9136
|
+
/** Deep-partial, for adapters that return only what they override. */
|
|
9137
|
+
type DeepPartial<T> = {
|
|
9138
|
+
[K in keyof T]?: T[K] extends (infer U)[] ? U[] : T[K] extends object | undefined ? DeepPartial<NonNullable<T[K]>> : T[K];
|
|
9139
|
+
};
|
|
9140
|
+
|
|
9141
|
+
/**
|
|
9142
|
+
* The normalized input the builder reads.
|
|
9143
|
+
*
|
|
9144
|
+
* Deliberately NOT `PoolData` directly: the same market travels through this
|
|
9145
|
+
* codebase in two casings — the in-package shape (`totalDepositsUSD`,
|
|
9146
|
+
* `variableBorrowRate`) and the API-serialized shape (`totalDepositsUsd`,
|
|
9147
|
+
* nested `caps`/`flags`/`config`). The builder must work on both, because it
|
|
9148
|
+
* runs in-package during a fetch AND at the worker while proxying an origin
|
|
9149
|
+
* response.
|
|
9150
|
+
*
|
|
9151
|
+
* So: one tolerant reader (`toTermSheetInput`) normalizes either shape into
|
|
9152
|
+
* this interface, and the builder itself is pure over the normalized form.
|
|
9153
|
+
*/
|
|
9154
|
+
interface TermConfigEntry {
|
|
9155
|
+
category: number | string;
|
|
9156
|
+
label?: string;
|
|
9157
|
+
borrowCollateralFactor?: number;
|
|
9158
|
+
collateralFactor?: number;
|
|
9159
|
+
borrowFactor?: number;
|
|
9160
|
+
liquidationPenalty?: number;
|
|
9161
|
+
closeFactor?: number;
|
|
9162
|
+
targetHealthFactor?: number;
|
|
9163
|
+
collateralDisabled?: boolean;
|
|
9164
|
+
debtDisabled?: boolean;
|
|
9165
|
+
}
|
|
9166
|
+
interface TermRewardInput {
|
|
9167
|
+
asset?: string;
|
|
9168
|
+
depositRate?: number;
|
|
9169
|
+
variableBorrowRate?: number;
|
|
9170
|
+
stableBorrowRate?: number;
|
|
9171
|
+
/** Merkl / points programs mark themselves; absent ⇒ a normal token. */
|
|
9172
|
+
kind?: string;
|
|
9173
|
+
endsAt?: number;
|
|
9174
|
+
claim?: string;
|
|
9175
|
+
}
|
|
9176
|
+
interface TermMenuInput {
|
|
9177
|
+
termId: number;
|
|
9178
|
+
durationSecs: number;
|
|
9179
|
+
durationDays: number;
|
|
9180
|
+
apr: number;
|
|
9181
|
+
depositApr?: number;
|
|
9182
|
+
available?: number;
|
|
9183
|
+
}
|
|
9184
|
+
/** Normalized market facts the generic builder needs. */
|
|
9185
|
+
interface TermSheetInput {
|
|
9186
|
+
marketUid: string;
|
|
9187
|
+
lender: string;
|
|
9188
|
+
chainId: string;
|
|
9189
|
+
/** Underlying asset of THIS row. */
|
|
9190
|
+
asset?: {
|
|
9191
|
+
chainId?: string;
|
|
9192
|
+
address?: string;
|
|
9193
|
+
symbol?: string;
|
|
9194
|
+
name?: string;
|
|
9195
|
+
decimals?: number;
|
|
9196
|
+
assetGroup?: string;
|
|
9197
|
+
logoURI?: string;
|
|
9198
|
+
};
|
|
9199
|
+
underlying?: string;
|
|
9200
|
+
decimals?: number;
|
|
9201
|
+
depositRate?: number;
|
|
9202
|
+
variableBorrowRate?: number;
|
|
9203
|
+
stableBorrowRate?: number;
|
|
9204
|
+
intrinsicYield?: number;
|
|
9205
|
+
rewards?: TermRewardInput[];
|
|
9206
|
+
rateModel?: string;
|
|
9207
|
+
originationFee?: number;
|
|
9208
|
+
totalDeposits?: number;
|
|
9209
|
+
totalDebt?: number;
|
|
9210
|
+
totalDebtStable?: number;
|
|
9211
|
+
totalLiquidity?: number;
|
|
9212
|
+
borrowLiquidity?: number;
|
|
9213
|
+
totalDepositsUsd?: number;
|
|
9214
|
+
totalDebtUsd?: number;
|
|
9215
|
+
totalLiquidityUsd?: number;
|
|
9216
|
+
utilization?: number;
|
|
9217
|
+
irmTotalDeposits?: number;
|
|
9218
|
+
irmTotalDebt?: number;
|
|
9219
|
+
lockupRatio?: number;
|
|
9220
|
+
supplyCap?: number;
|
|
9221
|
+
borrowCap?: number;
|
|
9222
|
+
debtCeiling?: string | number;
|
|
9223
|
+
/** Minimum borrow in RAW base units (Compound V3 `baseBorrowMin`). */
|
|
9224
|
+
minBorrow?: string;
|
|
9225
|
+
isActive?: boolean;
|
|
9226
|
+
isFrozen?: boolean;
|
|
9227
|
+
borrowingEnabled?: boolean;
|
|
9228
|
+
depositsEnabled?: boolean;
|
|
9229
|
+
collateralActive?: boolean;
|
|
9230
|
+
hasStable?: boolean;
|
|
9231
|
+
variableBorrowDisabled?: boolean;
|
|
9232
|
+
config?: Record<string, TermConfigEntry>;
|
|
9233
|
+
closeFactor?: number;
|
|
9234
|
+
targetHealthFactor?: number;
|
|
9235
|
+
fixedTerm?: {
|
|
9236
|
+
model?: string;
|
|
9237
|
+
maturity?: number;
|
|
9238
|
+
fees?: {
|
|
9239
|
+
continuousFeeApr?: number;
|
|
9240
|
+
settlementFee?: number;
|
|
9241
|
+
latePenaltyApr?: number;
|
|
9242
|
+
originationFeePercent?: number;
|
|
9243
|
+
};
|
|
9244
|
+
earlyRepay?: {
|
|
9245
|
+
kind?: string;
|
|
9246
|
+
};
|
|
9247
|
+
provider?: {
|
|
9248
|
+
kind?: string;
|
|
9249
|
+
address?: string;
|
|
9250
|
+
};
|
|
9251
|
+
auction?: Record<string, unknown>;
|
|
9252
|
+
};
|
|
9253
|
+
terms?: TermMenuInput[];
|
|
9254
|
+
/** Market-level params (`params.market`) when the lender has them. */
|
|
9255
|
+
market?: Record<string, unknown>;
|
|
9256
|
+
}
|
|
9257
|
+
/**
|
|
9258
|
+
* Normalize either the in-package `PoolData`-ish row or an API `LendingMarket`
|
|
9259
|
+
* item into {@link TermSheetInput}. Tolerant by design: a field missing under
|
|
9260
|
+
* one casing is looked up under the other, and nested `caps`/`flags` bundles
|
|
9261
|
+
* are unwrapped.
|
|
9262
|
+
*/
|
|
9263
|
+
declare function toTermSheetInput(row: Record<string, any>, ctx?: {
|
|
9264
|
+
marketUid?: string;
|
|
9265
|
+
lender?: string;
|
|
9266
|
+
chainId?: string;
|
|
9267
|
+
market?: Record<string, any>;
|
|
9268
|
+
/**
|
|
9269
|
+
* Item-level fixed-term descriptor. `/lending/latest` attaches `fixedTerm`
|
|
9270
|
+
* to the LENDER item, not to each market row, so without this every
|
|
9271
|
+
* fixed-term market would silently lose its maturity, its fees and its
|
|
9272
|
+
* auction window. A row-level `fixedTerm` (how `/pools/latest` serializes
|
|
9273
|
+
* it) is more specific and wins.
|
|
9274
|
+
*/
|
|
9275
|
+
fixedTerm?: Record<string, any>;
|
|
9276
|
+
}): TermSheetInput;
|
|
9277
|
+
|
|
9278
|
+
/**
|
|
9279
|
+
* Accepted-collateral / backing set from the SIBLING rows of the same lender.
|
|
9280
|
+
*
|
|
9281
|
+
* Pooled lenders (Aave, Compound) do NOT record on-chain which collateral
|
|
9282
|
+
* backs which borrow, so the result is the ACCEPTED SET with
|
|
9283
|
+
* `weightBasis: 'unweighted'` and NO `weightPct` on any item. Inventing a
|
|
9284
|
+
* TVL-proxy weight would look authoritative and be false — a large idle
|
|
9285
|
+
* collateral market is not a large exposure.
|
|
9286
|
+
*/
|
|
9287
|
+
declare function buildExposures(input: TermSheetInput, siblings: TermSheetInput[], direction: 'backing' | 'accepted'): ExposureTerms | undefined;
|
|
9288
|
+
/** Deep merge an adapter's partial over the generic result. Arrays REPLACE. */
|
|
9289
|
+
declare function mergeDeep<T>(base: T, patch: DeepPartial<T> | undefined): T;
|
|
9290
|
+
/**
|
|
9291
|
+
* Fill `info` (headline / description / tags) LAST, after adapters have run,
|
|
9292
|
+
* so the prose always describes the final values rather than the generic
|
|
9293
|
+
* guess. This is the mechanism that stops copy drifting from numbers.
|
|
9294
|
+
*/
|
|
9295
|
+
declare function finalizeInfo(sheet: TermSheet): TermSheet;
|
|
9296
|
+
interface BuildTermSheetOptions {
|
|
9297
|
+
/** Unix seconds; injected so tests are deterministic. */
|
|
9298
|
+
now?: number;
|
|
9299
|
+
/** Other rows of the SAME lender+chain — used to derive the exposure set. */
|
|
9300
|
+
siblings?: TermSheetInput[];
|
|
9301
|
+
/** Adapter output, merged over the generic result. */
|
|
9302
|
+
patch?: DeepPartial<TermSheet>;
|
|
9303
|
+
profileId?: string;
|
|
9304
|
+
}
|
|
9305
|
+
/** Build one complete term sheet for one market row. */
|
|
9306
|
+
declare function buildTermSheet(input: TermSheetInput, opts?: BuildTermSheetOptions): TermSheet;
|
|
9307
|
+
|
|
9308
|
+
/** Supply-side tags. Market-level tags are folded in by the caller. */
|
|
9309
|
+
declare function deriveSupplyTags(supply: SupplyTermSheet, market?: Pick<TermSheet, 'governance' | 'oracle'>): TermTag[];
|
|
9310
|
+
/** Borrow-side tags. */
|
|
9311
|
+
declare function deriveBorrowTags(borrow: BorrowTermSheet, market?: Pick<TermSheet, 'governance' | 'oracle'>): TermTag[];
|
|
9312
|
+
|
|
9313
|
+
/**
|
|
9314
|
+
* Severity model — PURE, derived only from structured fields.
|
|
9315
|
+
*
|
|
9316
|
+
* Space is the binding constraint at every display depth, so ranking has to be
|
|
9317
|
+
* principled rather than per-lender taste. There is deliberately NO
|
|
9318
|
+
* hand-maintained list of "scary markets": a newly integrated lender is
|
|
9319
|
+
* classified correctly the moment its adapter sets the right fields.
|
|
9320
|
+
*
|
|
9321
|
+
* - `critical` — you can lose MORE than the amount at stake, or lose it
|
|
9322
|
+
* without doing anything wrong. This is the only tier that should gate a
|
|
9323
|
+
* signature.
|
|
9324
|
+
* - `warn` — it costs money, or blocks you.
|
|
9325
|
+
* - `info` — everything else.
|
|
9326
|
+
*/
|
|
9327
|
+
type Severity = 'critical' | 'warn' | 'info';
|
|
9328
|
+
interface SeverityFinding {
|
|
9329
|
+
severity: Severity;
|
|
9330
|
+
/** Stable slug — the join key for UI copy and for tests. */
|
|
9331
|
+
id: string;
|
|
9332
|
+
/** Ready-to-render sentence. */
|
|
9333
|
+
message: string;
|
|
9334
|
+
side: 'supply' | 'borrow' | 'market';
|
|
9335
|
+
}
|
|
9336
|
+
/** Sort findings most-severe-first, stable within a tier. */
|
|
9337
|
+
declare function rankFindings(findings: SeverityFinding[]): SeverityFinding[];
|
|
9338
|
+
declare function supplyFindings(supply: SupplyTermSheet): SeverityFinding[];
|
|
9339
|
+
declare function borrowFindings(borrow: BorrowTermSheet): SeverityFinding[];
|
|
9340
|
+
/**
|
|
9341
|
+
* All findings for one side of a sheet, ranked most-severe-first. Pass
|
|
9342
|
+
* `side: 'supply' | 'borrow'` — market-level findings are always included
|
|
9343
|
+
* because governance and oracle affect both sides.
|
|
9344
|
+
*/
|
|
9345
|
+
declare function findingsFor(sheet: TermSheet, side: 'supply' | 'borrow'): SeverityFinding[];
|
|
9346
|
+
/** Does this side carry anything that should gate a signature? */
|
|
9347
|
+
declare function hasCritical(sheet: TermSheet, side: 'supply' | 'borrow'): boolean;
|
|
9348
|
+
|
|
9349
|
+
/** `4.1234` → `"4.12 %"`; trims to 2dp, drops a trailing `.00`. */
|
|
9350
|
+
/**
|
|
9351
|
+
* Format an already-PERCENT value (`49.88` → `"49.88%"`).
|
|
9352
|
+
*
|
|
9353
|
+
* Never pass a fraction. The convention across the term sheet is **rates are
|
|
9354
|
+
* percent, factors/ratios are fractions**, so `liquidationLtv`, `penalty`,
|
|
9355
|
+
* `utilization` and every `*Ratio` must be multiplied by 100 at the call site.
|
|
9356
|
+
*
|
|
9357
|
+
* Trailing zeros are trimmed only inside the FRACTIONAL part: a naive
|
|
9358
|
+
* `/\.?0+$/` strip eats integer zeros whenever `dp = 0` leaves no decimal
|
|
9359
|
+
* point, turning `100` into `"1"` and `1000` into `"1"`.
|
|
9360
|
+
*
|
|
9361
|
+
* No space before the `%` — the portal formats the same numbers without one,
|
|
9362
|
+
* and a headline reading "49.88 %" next to a table cell reading "49.88%" looks
|
|
9363
|
+
* like two different figures.
|
|
9364
|
+
*/
|
|
9365
|
+
declare function pct(value: number | undefined, dp?: number): string;
|
|
9366
|
+
/** Duration in seconds → the coarsest human unit that stays honest. */
|
|
9367
|
+
declare function duration(secs: number | undefined): string;
|
|
9368
|
+
/** Unix seconds → `"3 Sep 2026"`. Locale-independent so snapshots are stable. */
|
|
9369
|
+
declare function shortDate(unixSecs: number | undefined): string;
|
|
9370
|
+
/** One fee → a self-contained phrase, correct even for an unrecognised `id`. */
|
|
9371
|
+
declare function feePhrase(fee: FeeTerm): string;
|
|
9372
|
+
/** Supply-side headline: ≤ ~100 chars, always populated. */
|
|
9373
|
+
declare function supplyHeadline(s: SupplyTermSheet): string;
|
|
9374
|
+
/** Borrow-side headline. */
|
|
9375
|
+
declare function borrowHeadline(b: BorrowTermSheet): string;
|
|
9376
|
+
/** Supply-side description — 1–3 sentences, market values interpolated. */
|
|
9377
|
+
declare function supplyDescription(s: SupplyTermSheet, sheet?: Pick<TermSheet, 'utilization'>): string;
|
|
9378
|
+
/** Borrow-side description. */
|
|
9379
|
+
declare function borrowDescription(b: BorrowTermSheet): string;
|
|
9380
|
+
|
|
9381
|
+
declare const TERM_PROFILES: TermProfile[];
|
|
9382
|
+
declare function getTermProfile(id: string): TermProfile | undefined;
|
|
9383
|
+
/** Fallback used when a family has no dedicated profile yet. */
|
|
9384
|
+
declare const DEFAULT_PROFILE_ID = "pool.variable@v1";
|
|
9385
|
+
|
|
9386
|
+
/**
|
|
9387
|
+
* Stamping — the single place term sheets are attached.
|
|
9388
|
+
*
|
|
9389
|
+
* Runs ONCE at the end of the public-data pipeline rather than inside each
|
|
9390
|
+
* lender's converter. That is the whole architecture: ~200 Aave/Compound forks
|
|
9391
|
+
* get correct sheets from the generic builder with zero per-fork work, and
|
|
9392
|
+
* only the ~13 exotic families need an adapter.
|
|
9393
|
+
*/
|
|
9394
|
+
interface StampOptions {
|
|
9395
|
+
/** Unix seconds; injected so tests are deterministic. */
|
|
9396
|
+
now?: number;
|
|
9397
|
+
/** Attach ranked `implications[]` from the severity model. Default true. */
|
|
9398
|
+
withImplications?: boolean;
|
|
9399
|
+
/**
|
|
9400
|
+
* Derive `governance` / `oracle` / exposure quality from the rows' own
|
|
9401
|
+
* `oracleInfo` + `risk.breakdown`. Default true — set `false` only to test
|
|
9402
|
+
* the un-enriched builder in isolation.
|
|
9403
|
+
*/
|
|
9404
|
+
enrich?: boolean;
|
|
9405
|
+
}
|
|
9406
|
+
/**
|
|
9407
|
+
* Build sheets for one lender's rows on one chain.
|
|
9408
|
+
*
|
|
9409
|
+
* Siblings matter: the exposure set (`backedBy` / `acceptedCollateral`) is
|
|
9410
|
+
* derived by cross-referencing the OTHER rows of the same lender, so the whole
|
|
9411
|
+
* group has to be built together.
|
|
9412
|
+
*/
|
|
9413
|
+
declare function buildTermSheetsForGroup(rows: Record<string, any>[], ctx?: {
|
|
9414
|
+
lender?: string;
|
|
9415
|
+
chainId?: string;
|
|
9416
|
+
market?: Record<string, any>;
|
|
9417
|
+
/** Item-level `fixedTerm` from `/lending/latest` — see `toTermSheetInput`. */
|
|
9418
|
+
fixedTerm?: Record<string, any>;
|
|
9419
|
+
}, opts?: StampOptions): Map<string, TermSheet>;
|
|
9420
|
+
/**
|
|
9421
|
+
* Fill `info.implications[]` from the severity model, most severe first.
|
|
9422
|
+
*
|
|
9423
|
+
* Derived rather than hand-written, so a newly integrated lender gets correct
|
|
9424
|
+
* warnings the moment its adapter sets the right structured fields — and a
|
|
9425
|
+
* warning can never contradict the numbers next to it.
|
|
9426
|
+
*/
|
|
9427
|
+
declare function attachImplications(sheet: TermSheet): TermSheet;
|
|
9428
|
+
/**
|
|
9429
|
+
* Build an {@link EnrichmentIndex} from the market rows themselves.
|
|
9430
|
+
*
|
|
9431
|
+
* The governance and oracle screens are NOT a separate fetch: the origin
|
|
9432
|
+
* already ships both on every row — `oracleInfo.feeds[]` (the oracle-risk
|
|
9433
|
+
* classification) and `risk.breakdown[]` (the governance screen under
|
|
9434
|
+
* `category: 'governance'`, the asset screen under `category: 'token'`). So
|
|
9435
|
+
* the join is local to the group being stamped, with no extra round-trip and
|
|
9436
|
+
* no cross-service dependency.
|
|
9437
|
+
*
|
|
9438
|
+
* The per-exposure enrichment falls out of the same data: an exposure item
|
|
9439
|
+
* points at a SIBLING row's `marketUid`, and that sibling is already in this
|
|
9440
|
+
* group — so its oracle and its asset quality are right there.
|
|
9441
|
+
*/
|
|
9442
|
+
declare function enrichmentIndexFromRows(rows: Record<string, any>[]): EnrichmentIndex;
|
|
9443
|
+
/**
|
|
9444
|
+
* Collapse a sheet to its digest form (`?terms=digest`).
|
|
9445
|
+
*
|
|
9446
|
+
* Drops `items[]` from the exposure sets and the long prose — an Aave market
|
|
9447
|
+
* with 30 accepted collaterals is several kB on its own, and it is the SAME
|
|
9448
|
+
* accepted set repeated on every row of that lender. Every dropped item is
|
|
9449
|
+
* still reachable: each carries a `marketUid` for the bulk endpoint.
|
|
9450
|
+
*/
|
|
9451
|
+
declare function toDigest(sheet: TermSheet): TermSheetDigest;
|
|
9452
|
+
/** Row shape of `~/risk-data/data/oracles/oracle-risk-flat.json`. */
|
|
9453
|
+
interface OracleRiskRow {
|
|
9454
|
+
marketUid: string;
|
|
9455
|
+
oracle?: string;
|
|
9456
|
+
provider?: string;
|
|
9457
|
+
priceDescription?: string;
|
|
9458
|
+
intendedPair?: string;
|
|
9459
|
+
correctOracle?: boolean | null;
|
|
9460
|
+
denominatorMatch?: boolean | null;
|
|
9461
|
+
fixedRate?: boolean;
|
|
9462
|
+
score?: number;
|
|
9463
|
+
band?: string;
|
|
9464
|
+
flags?: string[];
|
|
9465
|
+
/** Underlying feeds when an adapter composes several. `oracle` stays the
|
|
9466
|
+
* single source of truth — this is for auditability only. */
|
|
9467
|
+
components?: string[];
|
|
9468
|
+
}
|
|
9469
|
+
/** Row shape of `~/risk-data/data/lending/market-governance-flat.json`. */
|
|
9470
|
+
interface GovernanceRow {
|
|
9471
|
+
marketUid: string;
|
|
9472
|
+
tier?: string;
|
|
9473
|
+
score?: number;
|
|
9474
|
+
ownerKind?: string;
|
|
9475
|
+
signerThreshold?: number | null;
|
|
9476
|
+
signerCount?: number | null;
|
|
9477
|
+
mode?: string;
|
|
9478
|
+
/** Present once the flat builder carries it through (see TERM_SHEET_PLAN §5.4.1). */
|
|
9479
|
+
delaySeconds?: number | null;
|
|
9480
|
+
}
|
|
9481
|
+
/** Per-asset quality, keyed `chainId → lowercased address`. */
|
|
9482
|
+
type AssetRiskIndex = Record<string, Record<string, {
|
|
9483
|
+
riskScore?: number;
|
|
9484
|
+
source?: string;
|
|
9485
|
+
liquidityUsd?: number;
|
|
9486
|
+
governanceScore?: number;
|
|
9487
|
+
governanceLevel?: string;
|
|
9488
|
+
upgradeable?: boolean;
|
|
9489
|
+
canPause?: boolean;
|
|
9490
|
+
adminKind?: string;
|
|
9491
|
+
}>>;
|
|
9492
|
+
interface EnrichmentIndex {
|
|
9493
|
+
oracleByMarketUid?: Map<string, OracleRiskRow>;
|
|
9494
|
+
governanceByMarketUid?: Map<string, GovernanceRow>;
|
|
9495
|
+
assetRisk?: AssetRiskIndex;
|
|
9496
|
+
}
|
|
9497
|
+
/**
|
|
9498
|
+
* Merge governance / oracle / asset-quality onto a sheet at the SERVING layer.
|
|
9499
|
+
*
|
|
9500
|
+
* These cannot be computed in-package — they come from the risk-data
|
|
9501
|
+
* screeners, which key on the same `marketUid` grammar (a dictionary lookup,
|
|
9502
|
+
* not a fuzzy match). `margin-fetcher` emits the sheet with these blocks
|
|
9503
|
+
* absent; the worker fills them in, exactly as it already does for
|
|
9504
|
+
* `oracleInfo`.
|
|
9505
|
+
*/
|
|
9506
|
+
declare function enrichTermSheet(sheet: TermSheet, index: EnrichmentIndex): TermSheet;
|
|
9507
|
+
|
|
9508
|
+
/**
|
|
9509
|
+
* Invariant checker — pure, and the mechanism that turns a derivation bug into
|
|
9510
|
+
* a loud failure instead of a plausible-looking wrong answer.
|
|
9511
|
+
*
|
|
9512
|
+
* Run over live fetched data in CI for every lender key and vault provider.
|
|
9513
|
+
* Every rule here encodes something that WOULD otherwise ship silently.
|
|
9514
|
+
*/
|
|
9515
|
+
interface TermSheetViolation {
|
|
9516
|
+
/** Stable slug, so a test can assert on the specific rule. */
|
|
9517
|
+
rule: string;
|
|
9518
|
+
message: string;
|
|
9519
|
+
marketUid?: string;
|
|
9520
|
+
}
|
|
9521
|
+
declare function validateTermSheet(sheet: TermSheet): TermSheetViolation[];
|
|
9522
|
+
/** Validate a batch; returns every violation found, flattened. */
|
|
9523
|
+
declare function validateTermSheets(sheets: TermSheet[]): TermSheetViolation[];
|
|
9524
|
+
|
|
9525
|
+
/**
|
|
9526
|
+
* A term-sheet adapter: returns ONLY what the generic builder cannot derive,
|
|
9527
|
+
* as a `DeepPartial<TermSheet>` merged over the generic result.
|
|
9528
|
+
*
|
|
9529
|
+
* Adding a lender is one adapter plus one profile entry — no core edits. The
|
|
9530
|
+
* completeness test fails when a lender family reaches production without a
|
|
9531
|
+
* profile, so the extension point cannot be silently skipped.
|
|
9532
|
+
*/
|
|
9533
|
+
interface TermAdapter {
|
|
9534
|
+
/** Stable id, for tests and debugging. */
|
|
9535
|
+
id: string;
|
|
9536
|
+
/** Does this adapter handle the given lender key? */
|
|
9537
|
+
matches: (lender: string) => boolean;
|
|
9538
|
+
/** The profile whose prose this market points at. */
|
|
9539
|
+
profileId: (input: TermSheetInput) => string;
|
|
9540
|
+
build: (input: TermSheetInput) => DeepPartial<TermSheet>;
|
|
9541
|
+
}
|
|
9542
|
+
/**
|
|
9543
|
+
* Order matters only where predicates could overlap; today they are disjoint.
|
|
9544
|
+
* The list is walked front-to-back and the first match wins.
|
|
9545
|
+
*/
|
|
9546
|
+
declare const TERM_ADAPTERS: TermAdapter[];
|
|
9547
|
+
declare function resolveAdapter(lender: string): TermAdapter | undefined;
|
|
9548
|
+
|
|
9549
|
+
export { type AaveMetadata, type AaveV2Public, type AaveV2UserReserveResponse, type AaveV3Public, type AaveV3UserReserveResponse, type AdditionalYields, type AdminKind, ApiBookSource, type AprData, type AprPercent, type AssetQuality, type AssetRiskIndex, type AuctionWindow, type AvailabilityTerms, type BalanceData, type BaseLendingPosition, type BasicReserveResponse, type BorrowExitTerms, type BorrowTermSheet, type BuildTermSheetOptions, type ChainDiagnostic, type ChainLinkResponse, type ChainQuery, type ChainSummary, type CompoundV2Metadata, type CompoundV3Public, type CompoundV3UserReserveResponse, type ConfigEntry, type ConvertLenderUserDataOptions, type CoreValidators, type CounterpartyTerms, type CoverageInfo, DEFAULT_MIDNIGHT_API, DEFAULT_OUTLIER_GUARD, DEFAULT_PROFILE_ID, DEFAULT_STALE_REJECT_SECONDS, DEFAULT_TERMMAX_API, type DeepPartial, type Denomination, type DepthMap, EMPTY_BALANCE, type EModeAssets, type EModeData, type EModeResult, EXACTLY_LENDER_KEY, type EndpointFailure, type EnrichmentIndex, type EulerEarnVault, type EulerEarnVaults, type EulerV2Metadata, type ExactlyFixedPool, type ExactlyFixedPosition, type ExactlyMarketAccount, type ExactlyMarketsRaw, type ExactlyUserFixedPosition, type ExposureEntry, type ExposureTerms, type ExtraValidationCall, type FeeTerm, type FeeWhen, type FeedObservation, type FeedStat, type FeedStatsMap, type FeedTimestampMap, type FetchListaVaultsFromChainOptions, type FetchMorphoVaultsFromChainOptions, type FetchOraclePricesOptions, type FetchTokenBalancesOptions, type FlattenPriorityConfig, type FluidFToken, type FluidFTokens, type FrankencoinMarketRaw, type FrankencoinMarketsRaw, type FrankencoinPositionInfo, type FullLenderRewardsMap, GMX_API_HOSTS, GMX_READ_CONTRACTS, GMX_SUPPORTED_CHAINS, type GenericCurrency, type GenericTokenList, type GetVaultPublicDataAllOptions, type GmxExecutionFees, type GmxPendingDeposit, type GmxPendingWithdrawal, type GmxReadContracts, type GmxUserBalance, type GmxUserPositions, type GmxUserPositionsOptions, type GmxVault, type GmxVaultKind, type GmxVaults, type GmxVaultsFetchOptions, type GovernancePower, type GovernanceRow, type GovernanceTerms, type GroupAccumulator, HYPERCORE_VAULT_REGISTRY, type HypercoreLockStatus, type HypercoreUserPositionsOptions, type HypercoreVault, type HypercoreVaultPosition, type HypercoreVaultRegistryEntry, type HypercoreVaults, type HypercoreVaultsFetchOptions, IDLE_MARKET_ID, INTERFACE_IDS, type IncompleteLenderRead, type IncompleteReason, type InitMetadata, type InitPublic, type InitUserReserveResponse, type InterfaceKind, type InvariantViolation, type InverseMarketRaw, type InverseMarketsRaw, type InversePositionInfo, LAGOON_API_URL, LAGOON_CHAIN_IDS, type LagoonApiVault, type LagoonSyncMode, type LagoonVault, type LagoonVaults, type LenderConfigData, type LenderConfigMap, type LenderCrossPoolMeta, type LenderData, type LenderDataEntry, type LenderInfo, type LenderInfoMap, type LenderPublicBase, type LenderRewardsMap, type LenderSummary, type LenderToLenderCrossPoolMeta, type LenderTotalAmounts, type LenderUserQuery, type LenderUserResponse, type LenderYieldComplete, type LenderYields$1 as LenderYields, LendingMode, type LiquidationPenaltyTerm, type LiquidationTerms, type LiquityBranchRaw, type LiquityDiscoveredTrove, type LiquityDiscovery, type LiquityMarketsRaw, type LiquitySpInfo, type LiquityTroveInfo, type ListaMarketOverrides, type LlamaLendMarketRaw, type LlamaLendMarketsRaw, type LlamaLendPositionInfo, type LoopPostTradeMetrics, type LstDelegation, type LstDelegationKind, type LstValidator, type LstWithdrawalFetchOptions, type LstWithdrawalRegistryEntry, type LstWithdrawalRequest, type LstWithdrawalRequestsByLst, type LstWithdrawalStatus, MORPHO_LENS, MULTICALL_FAILURE, type MaturityTerms, MaxParamThresholds, type MergedUserData, type MidnightBook, type MidnightBookLevel, type MidnightBookSource, type ModeBase, type ModeVariant, type MorphoMarketOverrides, type MorphoSubgraphProxyConfig, type MorphoUserMarketBalance, type MorphoUserReserveResponse, type MorphoVault, type MorphoVaults, type MulticallEndpointOptions, type MulticallRpcBatch, type NumberMap, type Open, type OracleBand, type OracleDiagnostics, type OraclePriceEntry, type OraclePricesResult, type OracleRiskRow, type OracleTerms, type OutlierGuardConfig, type ParsedBalanceData, type ParsedResponse, type ParsedUserBalance, type PermissionKind, type PermissionParams, type PoolData, type PoolWithMeta, type PortfolioSummary, type PortfolioTotals, type PositionConstraints, type PostTradeMetrics, type PreparedCall, type PreparedMergedMulticallParams, type PreparedMergedRpcCalls, type PreparedTokenBalanceRpcCalls, type PreparedUserDataRpcCalls, type PriceDerivation, type PriceSelection, type ProtocolParams, type ProviderOptions, type RateKind, type RateMenuEntry, type RateTerms, type RawRpcBatch, type RawRpcCall, type RawRpcResponse, type ReadFailurePolicy, type ResupplyMarketsRaw, type ResupplyPairIdentity, type ResupplyPairRaw, type ResupplyPositionInfo, type ResupplyWrappedMarket, type RewardTerm, type RiverMarketRaw, type RiverMarketsRaw, type RiverPositionInfo, STABLECOIN_SYMBOLS, STCELO_MANAGER_ADDRESS, type SelectPricesOptions, type Severity, type SeverityFinding, type SiloVault, type SiloVaults, type StCeloValidatorGroup, type StaleFeedEntry, type StampOptions, type StructuredOraclePrices, type SubAccountSummary, type SumerPositionInput, type SummaryAprData, type SummaryBalanceData, type SupplyExitMode, type SupplyExitTerms, type SupplyTermSheet, TELLER_CALLS_PER_BID, TERMMAX_CALLS_PER_ACCOUNT, DECIMAL_BASE as TERMMAX_DECIMAL_BASE, TERMMAX_LIQUIDATION_PENALTY, TERMMAX_LIQUIDATION_WINDOW_SECS, TERMMAX_LIQUIDATOR_BONUS, TERMMAX_PARTIAL_CLOSE_FACTOR, TERMMAX_PARTIAL_LIQUIDATION_THRESHOLD_USD, TERM_ADAPTERS, TERM_PROFILES, TERM_SHEET_SCHEMA_VERSION, type TellerDiscoveredBid, type TellerDiscovery, type TellerMarketsRaw, type TellerPoolRaw, type TermAdapter, type TermAssetRef, type TermAuctionOrder, type TermAuctionOrders, type TermBookSource, type TermBookTop, type TermInfo, type TermListing, type TermMarketRaw, TermMaxApiSource, type TermMaxBookTop, type TermMaxCurveSegment, type TermMaxDataSource, type TermMaxDiscovery, type TermMaxFeeConfig, type TermMaxMarketConfig, type TermMaxMarketRaw, type TermMaxOrderState, type TermProfile, type TermSheet, type TermSheetDigest, type TermSheetInput, type TermSheetViolation, TermSubgraphSource, type TermTag, type TokenApprovalMeta, type TokenApprovalParams, type TokenBalanceEntry, type TokenBalanceQuery, type TokenBalanceResult, type TokenEntry, type TokenList, type TokenListInput, type TrackerDiagnostic, UPSHIFT_CHAIN_IDS, UPSHIFT_VAULTS_URL, type USDPriceMap, type UpshiftApiAsset, type UpshiftApiVault, type UpshiftVault, type UpshiftVaults, type UsddMarketRaw, type UsddMarketsRaw, type UsddPositionInfo, type UserApr, type UserConfig, type UserData, type UserDataResult, type UserLendingPosition, type UtilizationTerms, VAULT_SHARE_PRICE_PROBE, VOLATILE_VAULT_OVERRIDES, type ValidationResult, type VaultAprResult, type VaultClassification, type VaultClassificationFields, type VaultLookupEntry, type VaultMarketExposure, type VaultProvider, type VaultPublicDataAll, type VaultPublicDataResult, type VaultYieldSeries, type VaultYieldSnapshot, type YDaemonVault, YEARN_CHAIN_IDS, YEARN_YDAEMON_BASE, type YearnVault, type YearnVaultKind, type YearnVaults, type YieldProfile, __resetResupplyUserCaches, accountDepositListKey, accountWithdrawalListKey, appendSnapshot, applyPositionDelta, attachImplications, attachPricesToFlashLiquidity, borrowDescription, borrowFindings, borrowHeadline, buildExposures, buildFluidFTokensCall, buildLiquityUserCall, buildLoopResult, buildMorphoTypeCall, buildMorphoTypeUserCallWithLens, buildPortfolioTotals, buildSumerAccumulators, buildSummaries, buildTellerUserCall, buildTermMaxUserCall, buildTermSheet, buildTermSheetsForGroup, buildVaultLookup, calculateLeverage, calculateNetApr, calculateOverallNetApr, calculateWeightedAverage, classifyFreshness, classifyReliability, classifyVault, collectFeedObservations, computeBorrowDelta, computeCloseTradeDeltas, computeCollateralSwapDeltas, computeDebtSwapDeltas, computeDepositDelta, computeEModeAnalysis, computeOpenTradeDeltas, computePostTradeMetrics, computeRepayDelta, computeSumerBorrowDelta, computeSumerDepositDelta, computeSumerRepayDelta, computeSumerWaterfall, computeSumerWithdrawDelta, computeVaultApr, computeWithdrawDelta, computeZapTradeDeltas, consensusReference, convertExactlyMarketsToResponse, convertFrankencoinMarketsToResponse, convertInverseMarketsToResponse, convertLenderUserDataResult, convertLiquityMarketsToResponse, convertLlamaLendMarketsToResponse, convertResupplyMarketsToResponse, convertRiverMarketsToResponse, convertTellerMarketsToResponse, convertTermMarketsToResponse, convertTermMaxMarketsToResponse, convertUsddMarketsToResponse, createMarketUid, createMidnightBookSource, createMulticallRpcCall, createRawRpcCalls, createTermBookSource, createTermMaxDataSource, decodeListaMarkets, decodeMarkets, decodePackedListaUserDataset, decodePackedMorphoUserDataset, deriveBorrowTags, deriveSupplyTags, detectInterfaceKinds, duration, encodeBalanceFetcherCalldata, enrichTermSheet, enrichmentIndexFromRows, exactlyPairLtv, exactlyPenaltyRateToAprPercent, exactlyWadRateToPercent, feePhrase, feedKeyOf, feedStatKey, fetchDolomiteAccountNumbers, fetchEulerEarnVaults, fetchEulerEarnVaultsFromSubgraph, fetchEulerSubAccountIndexes, fetchExactlyMarkets, fetchFlashLiquidityForChain, fetchFluidFTokens, fetchFrankencoinMarkets, fetchGeneralYields, fetchGeneralYieldsByMarketUid, fetchGmxExecutionFees, fetchGmxTickerPrices, fetchGmxVaults, fetchHypercoreVaults, fetchInverseMarkets, fetchLagoonApiVaults, fetchLagoonVaults, fetchLiquityMarkets, fetchListaVaultsFromChain, fetchLlamaLendMarkets, fetchMorphoUserBalances, fetchMorphoUserPositionMarkets, fetchMorphoVaults, fetchMorphoVaultsFromApi, fetchMorphoVaultsFromChain, fetchOraclePrices, fetchPendlePrices, fetchResupplyMarkets, fetchRiverMarkets, fetchSiloVaults, fetchTellerMarkets, fetchTermMarkets, fetchTermMaxMarkets, fetchTokenBalances, fetchTokenMetadata, fetchUpshiftApiVaults, fetchUpshiftVaults, fetchUsddMarkets, fetchYearnApiVaults, fetchYearnVaults, filterActiveLenders, filterLendersByProtocol, finalizeInfo, findingsFor, frankencoinKeyParts, frankencoinLenderKey, fuseLenderData, generateLendingPools, getAavesForChain, getAssetConfig, getBalanceForMarketUid, getBorrowCapacity, getCachedLiquityTroves, getCachedTellerBids, getCachedTermMaxDiscovery, getCachedTermMaxMarket, getCachedTermMaxMarkets, getCoreValidators, getFluidFTokensConverter, getGmxApiHost, getGmxReadContracts, getGmxUserPositions, getHealthFactor, getHypercoreUserPositions, getHypercoreVaultRegistry, getLenderAssets, getLenderPublicData, getLenderPublicDataAll, getLenderPublicDataViaApi, getLenderUserDataMulti, getLenderUserDataResult, getLendersForChain, getLstDelegation, getLstValidators, getLstWithdrawalRegistry, getLstWithdrawalRequests, getMaxAmountClose, getMaxAmountCollateralSwap, getMaxAmountDebtSwap, getMaxAmountOpen, getMergedUserData, getMorphoTypeMarketConverter, getReadFailurePolicy, getResolvedDolomiteAccountNumbers, getStCeloValidatorGroups, getSubAccountAddress, getSubAccountIndex, getTermProfile, getVaultPublicDataAll, getVaultWithdrawalRequests, hasCritical, hasEulerEarnVaultSubgraph, hasLagoonVaults, hasMorphoPositionIndex, hasMorphoUserApi, hasMorphoUserSubgraph, hasUpshiftVaults, hasYearnVaults, inverseKeyParts, inverseLenderKey, isFailedCall, isStablecoinSymbol, isYearnV3, keysFromMaps, lenderApiOnly, lenderFamily, liquityCandidateTroveIds, liquityKeyParts, liquityLenderKey, llamaLendKeyParts, llamaLendLenderKey, mergeDeep, multicall3Abi, nanTo, needsLenderApproval, needsTokenApproval, noOpResult, normalizeToBytes, parseBalanceFetcherResult, parseMergedResult, parseMulticallRpcResponses, parseRawRpcBatchResponses, parseRawRpcResponses, parseTermMaxLtv, parseTokenBalanceResult, pct, positivePart, predictInverseEscrow, prepareLenderUserDataRpcCalls, prepareMergedMulticallParams, prepareMergedRpcCalls, prepareMulticallInputs, prepareTokenBalanceRpcCalls, priceGlvVaults, priceGmMarkets, probeAaveFeedTimestamps, pruneFeedStats, rankFindings, readVaultSharePrices, rejectOutliers as rejectPriceOutliers, resolveAdapter, resolveDerivation, resolveStCeloDepositGroup, resupplyKeyParts, resupplyLenderKey, riverKeyParts, riverLenderKey, selectAssetGroupPrices, shortDate, stampVaultClassification, supplyDescription, supplyFindings, supplyHeadline, tellerBpsToPercent, tellerImpliedLtv, tellerLenderKey, tellerPoolFromLenderKey, termLenderKey, termMaxApiBase, borrowNif as termMaxBorrowNif, curveApr as termMaxCurveApr, curveAprNumber as termMaxCurveAprNumber, daysToMaturity as termMaxDaysToMaturity, lendNif as termMaxLendNif, mintGtFeeRatio as termMaxMintGtFeeRatio, ratioToNumber as termMaxRatioToNumber, ratioToPercent as termMaxRatioToPercent, tickToAprNumber, tickToPrice, toDigest, toTermSheetInput, unflattenLenderData, updateFeedStats, usddIlkBytes32, usddKeyParts, usddLenderKey, validateTermSheet, validateTermSheets, validateUserData };
|