@1delta/margin-fetcher 5.0.17 → 5.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +164 -116
- package/dist/index.js +470 -32
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
# @1delta/margin-fetcher
|
|
2
2
|
|
|
3
|
-
Multi-protocol lending data fetcher supporting Morpho Blue, Aave V2/V3, Compound
|
|
3
|
+
Multi-protocol lending data fetcher supporting Morpho Blue, Aave V2/V3, Compound
|
|
4
|
+
V2/V3, Euler, Init, Lista DAO, and Morpho Midnight. Provides public market data
|
|
5
|
+
(rates, TVL, configs) and per-user position data (balances, shares, collateral)
|
|
6
|
+
in a unified format.
|
|
4
7
|
|
|
5
8
|
## How Morpho Blue Works
|
|
6
9
|
|
|
7
10
|
### Protocol overview
|
|
8
11
|
|
|
9
|
-
Morpho Blue is an **isolated-market** lending protocol. Unlike pooled protocols
|
|
12
|
+
Morpho Blue is an **isolated-market** lending protocol. Unlike pooled protocols
|
|
13
|
+
(Aave, Compound) where all assets share a single pool, each Morpho market is a
|
|
14
|
+
**standalone pair** defined by five parameters:
|
|
10
15
|
|
|
11
|
-
| Parameter
|
|
12
|
-
|
|
13
|
-
| `loanToken`
|
|
14
|
-
| `collateralToken` | The asset deposited as collateral
|
|
15
|
-
| `oracle`
|
|
16
|
-
| `irm`
|
|
17
|
-
| `lltv`
|
|
16
|
+
| Parameter | Description |
|
|
17
|
+
| ----------------- | ------------------------------------------------ |
|
|
18
|
+
| `loanToken` | The asset that can be supplied and borrowed |
|
|
19
|
+
| `collateralToken` | The asset deposited as collateral |
|
|
20
|
+
| `oracle` | Price oracle for the pair |
|
|
21
|
+
| `irm` | Interest Rate Model contract |
|
|
22
|
+
| `lltv` | Liquidation Loan-To-Value ratio (18-decimal WAD) |
|
|
18
23
|
|
|
19
|
-
These five parameters are hashed into a `uniqueKey` (bytes32) that identifies
|
|
24
|
+
These five parameters are hashed into a `uniqueKey` (bytes32) that identifies
|
|
25
|
+
the market on-chain.
|
|
20
26
|
|
|
21
27
|
### Interest rate model
|
|
22
28
|
|
|
23
|
-
Morpho uses an **adaptive curve IRM** that adjusts rates based on utilization
|
|
29
|
+
Morpho uses an **adaptive curve IRM** that adjusts rates based on utilization
|
|
30
|
+
relative to a 90% target:
|
|
24
31
|
|
|
25
32
|
```
|
|
26
33
|
utilization = totalBorrowAssets / totalSupplyAssets
|
|
@@ -32,9 +39,12 @@ borrowRate = curve(rateAtTarget, utilization)
|
|
|
32
39
|
supplyRate = borrowRate * utilization * (1 - fee)
|
|
33
40
|
```
|
|
34
41
|
|
|
35
|
-
The curve uses a steepness factor of 4x, meaning at full utilization the rate is
|
|
42
|
+
The curve uses a steepness factor of 4x, meaning at full utilization the rate is
|
|
43
|
+
4x the rate-at-target. Rates are stored as per-second WAD values and compounded
|
|
44
|
+
continuously.
|
|
36
45
|
|
|
37
46
|
Key constants:
|
|
47
|
+
|
|
38
48
|
- **Target utilization**: 90%
|
|
39
49
|
- **Curve steepness**: 4x
|
|
40
50
|
- **Min rate at target**: ~0.1% APY
|
|
@@ -43,7 +53,8 @@ Key constants:
|
|
|
43
53
|
|
|
44
54
|
### Share-based accounting
|
|
45
55
|
|
|
46
|
-
Positions are tracked via **shares** rather than raw assets. This allows
|
|
56
|
+
Positions are tracked via **shares** rather than raw assets. This allows
|
|
57
|
+
interest to accrue without storage updates per-user:
|
|
47
58
|
|
|
48
59
|
```
|
|
49
60
|
assets = shares * (totalAssets + 1) / (totalShares + VIRTUAL_SHARES)
|
|
@@ -67,13 +78,17 @@ Each Morpho market produces **two entries** in the normalized output:
|
|
|
67
78
|
|
|
68
79
|
### Whitelisted vs unlisted markets
|
|
69
80
|
|
|
70
|
-
Morpho markets can be **whitelisted** (curated, visible in the Morpho UI) or
|
|
81
|
+
Morpho markets can be **whitelisted** (curated, visible in the Morpho UI) or
|
|
82
|
+
**unlisted** (removed from curation). The `includeUnlistedMorphoMarkets` flag
|
|
83
|
+
controls whether unlisted markets are fetched from the API. Unlisted markets are
|
|
84
|
+
tagged with `isListed: false` on the `MorphoMarket` params.
|
|
71
85
|
|
|
72
86
|
## Architecture
|
|
73
87
|
|
|
74
88
|
### Data fetching strategy
|
|
75
89
|
|
|
76
|
-
The fetcher uses a **hybrid approach** that routes each lender to either an API
|
|
90
|
+
The fetcher uses a **hybrid approach** that routes each lender to either an API
|
|
91
|
+
or on-chain path:
|
|
77
92
|
|
|
78
93
|
```
|
|
79
94
|
getLenderPublicDataAll(chainId, lenders, ...)
|
|
@@ -83,7 +98,8 @@ getLenderPublicDataAll(chainId, lenders, ...)
|
|
|
83
98
|
└── Promise.all([onChain, api]) → merged result
|
|
84
99
|
```
|
|
85
100
|
|
|
86
|
-
**Morpho Blue uses the API** on most chains. On-chain fallback is used for
|
|
101
|
+
**Morpho Blue uses the API** on most chains. On-chain fallback is used for
|
|
102
|
+
chains without API support (OP Mainnet, Soneium, Hemi, Berachain, Sei).
|
|
87
103
|
|
|
88
104
|
### API path (GraphQL)
|
|
89
105
|
|
|
@@ -97,7 +113,8 @@ fetchMorphoMarkets(chainId, includeUnlisted)
|
|
|
97
113
|
→ { [marketId]: MorphoGeneralPublicResponse }
|
|
98
114
|
```
|
|
99
115
|
|
|
100
|
-
The API provides pre-computed APYs, USD values, and reward data. APY→APR
|
|
116
|
+
The API provides pre-computed APYs, USD values, and reward data. APY→APR
|
|
117
|
+
conversion is applied during normalization.
|
|
101
118
|
|
|
102
119
|
### On-chain path (Morpho Lens)
|
|
103
120
|
|
|
@@ -184,64 +201,77 @@ src/types/lender/
|
|
|
184
201
|
|
|
185
202
|
## Lista DAO extension
|
|
186
203
|
|
|
187
|
-
Lista is a Morpho Blue fork with additional per-market fields. Uses a 357-byte
|
|
204
|
+
Lista is a Morpho Blue fork with additional per-market fields. Uses a 357-byte
|
|
205
|
+
binary record instead of 256:
|
|
188
206
|
|
|
189
|
-
| Extra field
|
|
190
|
-
|
|
191
|
-
| `minLoan`
|
|
192
|
-
| `hasWhitelist`
|
|
193
|
-
| `loanProvider`
|
|
194
|
-
| `collateralProvider` | address | Yield source for collateral
|
|
195
|
-
| `broker`
|
|
207
|
+
| Extra field | Type | Description |
|
|
208
|
+
| -------------------- | ------- | ------------------------------------- |
|
|
209
|
+
| `minLoan` | uint128 | Minimum loan amount |
|
|
210
|
+
| `hasWhitelist` | bool | Whether the market has access control |
|
|
211
|
+
| `loanProvider` | address | Yield source for loan token |
|
|
212
|
+
| `collateralProvider` | address | Yield source for collateral |
|
|
213
|
+
| `broker` | address | Authorized broker contract |
|
|
196
214
|
|
|
197
|
-
User data also includes per-market whitelist flags prepended before the balance
|
|
215
|
+
User data also includes per-market whitelist flags prepended before the balance
|
|
216
|
+
records.
|
|
198
217
|
|
|
199
218
|
### Fixed-term broker (`LendingBroker`)
|
|
200
219
|
|
|
201
|
-
Some Lista markets register a **`LendingBroker`** — the mandatory debt-side
|
|
202
|
-
(Morpho-fork) market. Collateral supply/withdraw still go
|
|
203
|
-
**borrow and repay run through the broker**,
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
220
|
+
Some Lista markets register a **`LendingBroker`** — the mandatory debt-side
|
|
221
|
+
gateway of a Moolah (Morpho-fork) market. Collateral supply/withdraw still go
|
|
222
|
+
through the normal Moolah path, but **borrow and repay run through the broker**,
|
|
223
|
+
which overlays a richer debt model on top of the raw Moolah position. The
|
|
224
|
+
on-chain broker is an ERC1967 proxy (e.g. BNB `0x1Fa2…8b54` → impl
|
|
225
|
+
`0xb680…58bb`), verified source `LendingBroker.sol` / `BrokerMath.sol`. Fetcher
|
|
226
|
+
support lives in
|
|
227
|
+
[`lending/public-data/lista/listaBroker.ts`](src/lending/public-data/lista/listaBroker.ts);
|
|
228
|
+
the composer encoders live in `contracts-delegation` (`ListaBrokerLending.sol`,
|
|
229
|
+
`CalldataLib.encodeListaBroker*`).
|
|
230
|
+
|
|
231
|
+
**Two debt buckets per user.** A brokered market has **no flexible/variable
|
|
232
|
+
borrowing** at the Moolah layer — instead each user holds:
|
|
233
|
+
|
|
234
|
+
- **N fixed-term positions** — each a `{posId, principal, apr, start, end, …}`
|
|
235
|
+
tranche with a _locked_ APR and a maturity. Identified by `posId`.
|
|
236
|
+
- **One dynamic position** — a variable-rate position, the catch-all bucket.
|
|
237
|
+
Targeted on repay by the sentinel `posId == type(uint128).max`
|
|
238
|
+
(`LISTA_BROKER_DYNAMIC_POS`).
|
|
239
|
+
|
|
240
|
+
`broker.getUserTotalDebt(user)` is the authoritative total (fixed + dynamic +
|
|
241
|
+
all interest).
|
|
218
242
|
|
|
219
243
|
#### Borrow
|
|
220
244
|
|
|
221
|
-
The composer calls `borrow(amount, termId, user, receiver)`. `user` (the
|
|
222
|
-
the authenticated caller** and must have authorized
|
|
223
|
-
|
|
245
|
+
The composer calls `borrow(amount, termId, user, receiver)`. `user` (the
|
|
246
|
+
position owner) is **always the authenticated caller** and must have authorized
|
|
247
|
+
the composer in Moolah (`setAuthorization`) — debt can never be opened against
|
|
248
|
+
someone else. The broker borrows from Moolah and forwards the loan token to
|
|
224
249
|
`receiver`.
|
|
225
250
|
|
|
226
|
-
> **Native:** the broker
|
|
227
|
-
> `borrow(amount, termId)` overloads that pay `msg.sender`.
|
|
228
|
-
>
|
|
229
|
-
>
|
|
251
|
+
> **Native:** the broker _can_ unwrap WBNB→BNB, but only on its EOA-direct
|
|
252
|
+
> `borrow(amount)` / `borrow(amount, termId)` overloads that pay `msg.sender`.
|
|
253
|
+
> The `receiver`-variant the composer must use (Moolah requires
|
|
254
|
+
> `receiver == broker` when a broker is registered) **always sends ERC20 WBNB,
|
|
255
|
+
> never native**. To deliver native BNB, chain an unwrap step after the borrow.
|
|
230
256
|
|
|
231
257
|
#### Repay
|
|
232
258
|
|
|
233
259
|
`encodeListaBrokerRepay(loanToken, assets, native, broker, posId, onBehalf)` →
|
|
234
|
-
`repay(amount, posId, onBehalf)` (fixed) or `repay(amount, onBehalf)` (dynamic,
|
|
260
|
+
`repay(amount, posId, onBehalf)` (fixed) or `repay(amount, onBehalf)` (dynamic,
|
|
261
|
+
sentinel `posId`).
|
|
235
262
|
|
|
236
263
|
- **Interest-first, then principal.**
|
|
237
|
-
- **`onBehalf`** is taken straight from calldata (exactly like Morpho's repay
|
|
238
|
-
only ever pays debt down and refunds excess to the
|
|
239
|
-
Must be non-zero (broker
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
264
|
+
- **`onBehalf`** is taken straight from calldata (exactly like Morpho's repay
|
|
265
|
+
`onBehalfOf`) — repaying only ever pays debt down and refunds excess to the
|
|
266
|
+
composer, so **repay-on-behalf is permissionless**. Must be non-zero (broker
|
|
267
|
+
reverts `ZeroAddress`); there is no caller fallback.
|
|
268
|
+
- **`assets == 0`** repays the composer's full balance (`balanceOf` /
|
|
269
|
+
`selfbalance`); the broker refunds any excess. The clean "close it out"
|
|
270
|
+
pattern: over-fund slightly, repay with `assets == 0`, take the dust.
|
|
271
|
+
- **Native:** both repay overloads are `payable` and wrap `msg.value` internally
|
|
272
|
+
(gated on `LOAN_TOKEN == WBNB`, else `NativeNotSupported`). The composer's
|
|
273
|
+
native path forwards the value and skips the ERC20 approve. Excess refunds as
|
|
274
|
+
native BNB to the composer (un-swept on explicit amounts).
|
|
245
275
|
|
|
246
276
|
#### Early-repayment penalty
|
|
247
277
|
|
|
@@ -252,83 +282,101 @@ Repaying a fixed position's **principal before `end`** incurs a penalty
|
|
|
252
282
|
penalty = ceil( ceil(repayPrincipal × aprPerSecond / RATE_SCALE) × timeLeft / 2 )
|
|
253
283
|
```
|
|
254
284
|
|
|
255
|
-
≈ **half the interest that principal would still accrue over the remaining
|
|
256
|
-
principal payment and supplied to the Moolah vault
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
285
|
+
≈ **half the interest that principal would still accrue over the remaining
|
|
286
|
+
term**. It's skimmed off the principal payment and supplied to the Moolah vault
|
|
287
|
+
as revenue, so it is **additive** — on top of `outstanding + accruedInterest`.
|
|
288
|
+
Penalty is **0 once matured** (`block.timestamp > end`). The fetcher surfaces it
|
|
289
|
+
per loan as `earlyRepayPenalty` (computed for the _full outstanding principal_;
|
|
290
|
+
scale linearly for partials). Use the broker's
|
|
291
|
+
`previewRepayFixedLoanPosition(user, amount, posId)` for the exact
|
|
260
292
|
`(interest, penalty, principal)` split.
|
|
261
293
|
|
|
262
294
|
#### Maturity & refinancing
|
|
263
295
|
|
|
264
|
-
A fixed position's **interest freezes at `end`**
|
|
265
|
-
`position.end`); after maturity it
|
|
296
|
+
A fixed position's **interest freezes at `end`**
|
|
297
|
+
(`getAccruedInterestForFixedPosition` caps at `position.end`); after maturity it
|
|
298
|
+
accrues no further fixed interest and carries no penalty.
|
|
266
299
|
|
|
267
|
-
`refinanceMaturedFixedPositions(user, posIds)` is a **bot-only** function
|
|
268
|
-
the composer **cannot** call it, so it is **not**
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
and the
|
|
300
|
+
`refinanceMaturedFixedPositions(user, posIds)` is a **bot-only** function
|
|
301
|
+
(`onlyRole(BOT)`) — users and the composer **cannot** call it, so it is **not**
|
|
302
|
+
exposed as an SDK/composer op. It migrates matured fixed positions into the
|
|
303
|
+
dynamic bucket: net principal + frozen interest are normalized at the current
|
|
304
|
+
dynamic rate and added to the dynamic position, and the fixed position is
|
|
305
|
+
**deleted**. The window between maturity and the bot's call is interest-free
|
|
306
|
+
(the protocol absorbs it).
|
|
272
307
|
|
|
273
308
|
#### Repaying late
|
|
274
309
|
|
|
275
|
-
| When
|
|
276
|
-
|
|
277
|
-
| Before `end`
|
|
278
|
-
| After `end`, before bot refinances | fixed `posId`
|
|
279
|
-
| After bot refinances
|
|
310
|
+
| When | Target | Cost |
|
|
311
|
+
| ---------------------------------- | -------------------- | ---------------------------------------------------------------------------- |
|
|
312
|
+
| Before `end` | fixed `posId` | principal + accrued interest + **early penalty** |
|
|
313
|
+
| After `end`, before bot refinances | fixed `posId` | principal + **frozen** interest, **no penalty** — cheapest |
|
|
314
|
+
| After bot refinances | **dynamic** sentinel | principal + interest captured at refinance + **dynamic-rate interest since** |
|
|
280
315
|
|
|
281
|
-
> **Refinance is a race.** A repay targeting a matured `posId` **reverts**
|
|
282
|
-
> bot refinances first; fall back to the dynamic
|
|
283
|
-
> (`isMatured: true`,
|
|
316
|
+
> **Refinance is a race.** A repay targeting a matured `posId` **reverts**
|
|
317
|
+
> (`position not found`) if the bot refinances first; fall back to the dynamic
|
|
318
|
+
> repay. The matured-but-not-yet-refinanced window (`isMatured: true`,
|
|
319
|
+
> `earlyRepayPenalty: '0'`) is the cheapest time to repay.
|
|
284
320
|
|
|
285
321
|
#### Data shape
|
|
286
322
|
|
|
287
|
-
Brokered markets emit **no variable debt** — the position's `debt` is `0`, the
|
|
288
|
-
`debtStable`, and each fixed loan is itemized in a
|
|
323
|
+
Brokered markets emit **no variable debt** — the position's `debt` is `0`, the
|
|
324
|
+
authoritative total sits in `debtStable`, and each fixed loan is itemized in a
|
|
325
|
+
`terms[]` array (`ListaTermLoan`):
|
|
289
326
|
|
|
290
|
-
| Field
|
|
291
|
-
|
|
292
|
-
| `loanId`
|
|
293
|
-
| `debt`
|
|
294
|
-
| `aprFraction`
|
|
295
|
-
| `maturity` / `termDays` | unix maturity timestamp / term length
|
|
296
|
-
| `accruedInterest`
|
|
297
|
-
| `earlyRepayPenalty`
|
|
298
|
-
| `isMatured`
|
|
327
|
+
| Field | Description |
|
|
328
|
+
| ----------------------- | ------------------------------------------------------------------- |
|
|
329
|
+
| `loanId` | `posId` — the repay target for the `LISTA_BROKER_REPAY` composer op |
|
|
330
|
+
| `debt` | outstanding (principal + accrued interest), loan-token units |
|
|
331
|
+
| `aprFraction` | locked fixed APR as a fraction |
|
|
332
|
+
| `maturity` / `termDays` | unix maturity timestamp / term length |
|
|
333
|
+
| `accruedInterest` | outstanding accrued interest |
|
|
334
|
+
| `earlyRepayPenalty` | penalty to close the loan now; `0` once matured |
|
|
335
|
+
| `isMatured` | `now >= end` |
|
|
299
336
|
|
|
300
|
-
> **Note:** `terms[]` currently itemizes only fixed loans. The dynamic position
|
|
301
|
-
> included in the `debtStable` total but not yet surfaced as
|
|
337
|
+
> **Note:** `terms[]` currently itemizes only fixed loans. The dynamic position
|
|
338
|
+
> (post-refinance) is included in the `debtStable` total but not yet surfaced as
|
|
339
|
+
> its own repayable term.
|
|
302
340
|
|
|
303
341
|
## Morpho Midnight extension
|
|
304
342
|
|
|
305
|
-
Morpho Midnight is a Base-only, **fixed-rate / fixed-maturity** lending protocol
|
|
306
|
-
**order book of off-chain signed offers** (zero-coupon credit/debt
|
|
307
|
-
Blue fork**, so it is modeled as its own
|
|
308
|
-
`isMidnight`), not
|
|
309
|
-
|
|
310
|
-
|
|
343
|
+
Morpho Midnight is a Base-only, **fixed-rate / fixed-maturity** lending protocol
|
|
344
|
+
built on an **order book of off-chain signed offers** (zero-coupon credit/debt
|
|
345
|
+
units) — it is **NOT a Morpho Blue fork**, so it is modeled as its own
|
|
346
|
+
`'midnight'` provider (`Lender.MORPHO_MIDNIGHT`, `isMidnight`), not
|
|
347
|
+
`isMorphoType`. There is no pool and no utilization curve: rates are derived
|
|
348
|
+
from each offer's `tick` → price and its time-to-maturity, knowable only via the
|
|
349
|
+
API. Each maturity is its own isolated market, keyed
|
|
350
|
+
`MORPHO_MIDNIGHT_<marketId>`.
|
|
311
351
|
|
|
312
352
|
### API-first fetching
|
|
313
353
|
|
|
314
|
-
Unlike Morpho Blue's GraphQL-then-on-chain hybrid, Midnight public data is
|
|
315
|
-
is no on-chain public fallback in `fetchLenderAll`. The
|
|
354
|
+
Unlike Morpho Blue's GraphQL-then-on-chain hybrid, Midnight public data is
|
|
355
|
+
**API-only** — there is no on-chain public fallback in `fetchLenderAll`. The
|
|
356
|
+
fetcher lives in
|
|
316
357
|
[`lending/public-data/midnight/`](src/lending/public-data/midnight/):
|
|
317
358
|
|
|
318
|
-
- **`apiClient` / `fetchPublic`** — an `ApiBookSource` reads the order book per
|
|
319
|
-
(`GET /books/{id}`) from the Midnight API. The base URL resolves
|
|
320
|
-
default (`https://api.morpho.org/v0/midnight`); the
|
|
321
|
-
`MIDNIGHT_API_BASE` (see `data-sdk`
|
|
322
|
-
|
|
323
|
-
- **`
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
359
|
+
- **`apiClient` / `fetchPublic`** — an `ApiBookSource` reads the order book per
|
|
360
|
+
market (`GET /books/{id}`) from the Midnight API. The base URL resolves
|
|
361
|
+
override → config → hosted default (`https://api.morpho.org/v0/midnight`); the
|
|
362
|
+
worker can point it at a dev API via `MIDNIGHT_API_BASE` (see `data-sdk`
|
|
363
|
+
`setMidnightApiBase`).
|
|
364
|
+
- **`math.ts`** — ported `TickLib` (`tickToApr` etc.), bit-verified against
|
|
365
|
+
`@morpho-org/midnight-sdk`.
|
|
366
|
+
- **`convertPublic`** — normalizes the book into a `MorphoGeneralPublicResponse`
|
|
367
|
+
keyed by `MORPHO_MIDNIGHT_<id>`, emitting the fixed borrow/supply APR and a
|
|
368
|
+
single-term `params.market.terms[]`
|
|
369
|
+
(`{termId, durationSecs, durationDays, apr}`) carrying the maturity — so the
|
|
370
|
+
maturity flows through the same generic `terms[]` path as Lista's fixed loans.
|
|
371
|
+
Units are in the **loan token's decimals** (not 18):
|
|
372
|
+
`assets = units × price / WAD`, so `units ≈ assets`.
|
|
373
|
+
|
|
374
|
+
User positions (`{credit, debt, collateral[128], collateralBitmap}`) are read
|
|
375
|
+
via multicall in
|
|
376
|
+
[`lending/user-data/midnight/`](src/lending/user-data/midnight/), one `UserData`
|
|
377
|
+
per market.
|
|
378
|
+
|
|
379
|
+
> **Scope:** the fetcher and direct spot actions (supply collateral /
|
|
380
|
+
> borrow-via-`take` / repay / withdraw) are wired. Composer-routed leverage and
|
|
381
|
+
> native ETH are deferred — see
|
|
334
382
|
> [`calldata-sdk/src/evm/generic/midnight/COMPOSER.md`](../calldata-sdk/src/evm/generic/midnight/COMPOSER.md).
|